Commits:
-
5c903fac
by Pier Angelo Vendrame at 2026-07-01T08:31:43+02:00
BB 45086: Replace omni.ja URI parsing in GetApkURI with a direct JNI call
-
63c810a9
by Beatriz Rizental at 2026-07-01T08:31:43+02:00
TB 45086: [android] Repack omni.ja file with LZMA
16 changed files:
Changes:
mobile/android/fenix/app/build.gradle
| ... |
... |
@@ -197,7 +197,7 @@ android { |
|
197
|
197
|
|
|
198
|
198
|
// manifest.template.json is converted to manifest.json at build time.
|
|
199
|
199
|
// No need to package the template in the APK.
|
|
200
|
|
- ignoreAssetsPattern = "manifest.template.json"
|
|
|
200
|
+ ignoreAssetsPattern = "manifest.template.json:omni.ja"
|
|
201
|
201
|
}
|
|
202
|
202
|
|
|
203
|
203
|
testOptions {
|
mobile/android/geckoview/build.gradle
| ... |
... |
@@ -82,10 +82,20 @@ android { |
|
82
|
82
|
buildConfigField 'boolean', 'MOZ_ANDROID_CONTENT_SERVICE_ISOLATED_PROCESS', mozconfig.substs.MOZ_ANDROID_CONTENT_SERVICE_ISOLATED_PROCESS ? 'true' : 'false';
|
|
83
|
83
|
}
|
|
84
|
84
|
|
|
|
85
|
+ aaptOptions {
|
|
|
86
|
+ noCompress 'xz'
|
|
|
87
|
+ }
|
|
|
88
|
+
|
|
85
|
89
|
lintOptions {
|
|
86
|
90
|
abortOnError = false
|
|
87
|
91
|
}
|
|
88
|
92
|
|
|
|
93
|
+ testOptions {
|
|
|
94
|
+ unitTests {
|
|
|
95
|
+ includeAndroidResources = true
|
|
|
96
|
+ }
|
|
|
97
|
+ }
|
|
|
98
|
+
|
|
89
|
99
|
sourceSets {
|
|
90
|
100
|
main {
|
|
91
|
101
|
java {
|
| ... |
... |
@@ -194,6 +204,7 @@ dependencies { |
|
194
|
204
|
implementation libs.androidx.lifecycle.common
|
|
195
|
205
|
implementation libs.androidx.lifecycle.process
|
|
196
|
206
|
implementation libs.play.services.fido
|
|
|
207
|
+ implementation "org.tukaani:xz:1.12"
|
|
197
|
208
|
implementation "org.yaml:snakeyaml:2.2"
|
|
198
|
209
|
|
|
199
|
210
|
if (mozconfig.substs.MOZ_ANDROID_HLS_SUPPORT) {
|
mobile/android/geckoview/omnijar_repack.sh
|
|
1
|
+#!/bin/sh
|
|
|
2
|
+# Repack a .ja (ZIP) stored uncompressed, compress with XZ, write SHA-256 sidecar.
|
|
|
3
|
+# Usage: omnijar_repack.sh <src> <dst-file> <dst-tag>
|
|
|
4
|
+set -e
|
|
|
5
|
+src="$1" dst_file="$2" dst_tag="$3"
|
|
|
6
|
+tmp=$(mktemp -d)
|
|
|
7
|
+trap 'rm -rf "$tmp"' EXIT
|
|
|
8
|
+cp "$src" "$tmp/omni.ja"
|
|
|
9
|
+# Repack all entries as ZIP_STORED (strip per-entry deflate)
|
|
|
10
|
+( cd "$tmp" && unzip -q omni.ja -d unpacked && cd unpacked && zip -q -0 -r ../repacked.ja . )
|
|
|
11
|
+xz -9e -k -c "$tmp/repacked.ja" > "$dst_file"
|
|
|
12
|
+sha256sum -b "$tmp/repacked.ja" | awk '{print $1}' > "$dst_tag" |
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
| ... |
... |
@@ -1761,6 +1761,11 @@ public class GeckoAppShell { |
|
1761
|
1761
|
return id == 0 ? info.nonLocalizedLabel.toString() : context.getString(id);
|
|
1762
|
1762
|
}
|
|
1763
|
1763
|
|
|
|
1764
|
+ @WrapForJNI
|
|
|
1765
|
+ public static String getApkPath() {
|
|
|
1766
|
+ return getApplicationContext().getPackageResourcePath();
|
|
|
1767
|
+ }
|
|
|
1768
|
+
|
|
1764
|
1769
|
@WrapForJNI(calledFrom = "gecko")
|
|
1765
|
1770
|
private static int getMemoryUsage(final String stateName) {
|
|
1766
|
1771
|
final Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
|
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
| ... |
... |
@@ -19,6 +19,8 @@ import android.util.Log; |
|
19
|
19
|
import androidx.annotation.NonNull;
|
|
20
|
20
|
import androidx.annotation.Nullable;
|
|
21
|
21
|
import androidx.annotation.UiThread;
|
|
|
22
|
+import java.io.File;
|
|
|
23
|
+import java.io.IOException;
|
|
22
|
24
|
import java.util.ArrayList;
|
|
23
|
25
|
import java.util.Arrays;
|
|
24
|
26
|
import java.util.LinkedList;
|
| ... |
... |
@@ -31,6 +33,7 @@ import org.mozilla.gecko.annotation.WrapForJNI; |
|
31
|
33
|
import org.mozilla.gecko.mozglue.GeckoLoader;
|
|
32
|
34
|
import org.mozilla.gecko.process.MemoryController;
|
|
33
|
35
|
import org.mozilla.gecko.util.GeckoBundle;
|
|
|
36
|
+import org.mozilla.gecko.util.XzExtractor;
|
|
34
|
37
|
import org.mozilla.gecko.util.ThreadUtils;
|
|
35
|
38
|
import org.mozilla.geckoview.BuildConfig;
|
|
36
|
39
|
import org.mozilla.geckoview.GeckoResult;
|
| ... |
... |
@@ -329,6 +332,15 @@ public class GeckoThread extends Thread { |
|
329
|
332
|
loadGeckoLibs(context);
|
|
330
|
333
|
}
|
|
331
|
334
|
|
|
|
335
|
+ private static File extractOmnijar(final Context context) throws IOException {
|
|
|
336
|
+ return new XzExtractor(
|
|
|
337
|
+ context,
|
|
|
338
|
+ "omni.ja",
|
|
|
339
|
+ context.getNoBackupFilesDir(),
|
|
|
340
|
+ "omni.ja")
|
|
|
341
|
+ .extract();
|
|
|
342
|
+ }
|
|
|
343
|
+
|
|
332
|
344
|
private String[] getMainProcessArgs() {
|
|
333
|
345
|
final Context context = GeckoAppShell.getApplicationContext();
|
|
334
|
346
|
final ArrayList<String> args = new ArrayList<>();
|
| ... |
... |
@@ -337,8 +349,13 @@ public class GeckoThread extends Thread { |
|
337
|
349
|
args.add(context.getPackageName());
|
|
338
|
350
|
|
|
339
|
351
|
if (!mInitInfo.xpcshell) {
|
|
340
|
|
- args.add("-greomni");
|
|
341
|
|
- args.add(context.getPackageResourcePath());
|
|
|
352
|
+ try {
|
|
|
353
|
+ args.add("-greomni");
|
|
|
354
|
+ String greomni = extractOmnijar(context).getAbsolutePath();
|
|
|
355
|
+ args.add(greomni);
|
|
|
356
|
+ } catch (final IOException e) {
|
|
|
357
|
+ throw new RuntimeException("Failed to extract omnijar", e);
|
|
|
358
|
+ }
|
|
342
|
359
|
}
|
|
343
|
360
|
|
|
344
|
361
|
if (mInitInfo.args != null) {
|
mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XzExtractor.java
|
|
1
|
+package org.mozilla.gecko.util;
|
|
|
2
|
+
|
|
|
3
|
+import android.content.Context;
|
|
|
4
|
+import android.content.res.AssetManager;
|
|
|
5
|
+import android.util.Log;
|
|
|
6
|
+import java.io.BufferedReader;
|
|
|
7
|
+import java.io.File;
|
|
|
8
|
+import java.io.IOException;
|
|
|
9
|
+import java.io.InputStream;
|
|
|
10
|
+import java.io.InputStreamReader;
|
|
|
11
|
+import java.nio.charset.StandardCharsets;
|
|
|
12
|
+import java.nio.file.Files;
|
|
|
13
|
+import java.nio.file.StandardCopyOption;
|
|
|
14
|
+import org.tukaani.xz.XZInputStream;
|
|
|
15
|
+
|
|
|
16
|
+public class XzExtractor {
|
|
|
17
|
+ private static final String TAG = "XzExtractor";
|
|
|
18
|
+ private final Context mContext;
|
|
|
19
|
+ private final String mSource;
|
|
|
20
|
+ private String mSourceHash;
|
|
|
21
|
+ private final File mTarget;
|
|
|
22
|
+ private final File mTargetHash;
|
|
|
23
|
+
|
|
|
24
|
+ public XzExtractor(Context context, String source, File targetDir, String destination) {
|
|
|
25
|
+ mContext = context;
|
|
|
26
|
+ mSource = source;
|
|
|
27
|
+ mTarget = new File(targetDir, destination);
|
|
|
28
|
+ mTargetHash = new File(targetDir, destination + ".sha256");
|
|
|
29
|
+ }
|
|
|
30
|
+
|
|
|
31
|
+ public File extract() throws IOException {
|
|
|
32
|
+ Log.d(TAG, "Extracting " + mSource + " -> " + mTarget.getAbsolutePath());
|
|
|
33
|
+ readSourceTag();
|
|
|
34
|
+ if (checkExisting()) {
|
|
|
35
|
+ Log.d(TAG, mSource + ": up to date, skipping extraction");
|
|
|
36
|
+ } else {
|
|
|
37
|
+ Log.d(TAG, mSource + ": extracting from assets");
|
|
|
38
|
+ extractFromAssets();
|
|
|
39
|
+ Log.d(TAG, mSource + ": extraction complete");
|
|
|
40
|
+ }
|
|
|
41
|
+ return mTarget;
|
|
|
42
|
+ }
|
|
|
43
|
+
|
|
|
44
|
+ private void readSourceTag() throws IOException {
|
|
|
45
|
+ // We use sha256 because we need a unique deterministic string
|
|
|
46
|
+ // to verify if current resources are up to date or need to be
|
|
|
47
|
+ // re-extracted. THIS IS NOT AN INTEGRITY CHECK.
|
|
|
48
|
+ final String asset = mSource + ".sha256";
|
|
|
49
|
+ final Context context = mContext;
|
|
|
50
|
+ try (InputStream in = context.getAssets().open(asset, AssetManager.ACCESS_BUFFER);
|
|
|
51
|
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
|
|
|
52
|
+ mSourceHash = reader.readLine();
|
|
|
53
|
+ }
|
|
|
54
|
+ if (mSourceHash == null || mSourceHash.isEmpty()) {
|
|
|
55
|
+ throw new IOException(asset + " is empty");
|
|
|
56
|
+ }
|
|
|
57
|
+ }
|
|
|
58
|
+
|
|
|
59
|
+ private boolean checkExisting() throws IOException {
|
|
|
60
|
+ if (!mTarget.exists() || !mTargetHash.exists()) {
|
|
|
61
|
+ return false;
|
|
|
62
|
+ }
|
|
|
63
|
+ String destHash = new String(Files.readAllBytes(mTargetHash.toPath()), StandardCharsets.UTF_8).trim();
|
|
|
64
|
+ return destHash.equals(mSourceHash);
|
|
|
65
|
+ }
|
|
|
66
|
+
|
|
|
67
|
+ private void extractFromAssets() throws IOException {
|
|
|
68
|
+ final Context context = mContext;
|
|
|
69
|
+ try (InputStream assetIn = context.getAssets().open(mSource + ".xz");
|
|
|
70
|
+ XZInputStream xzIn = new XZInputStream(assetIn)) {
|
|
|
71
|
+ Files.copy(xzIn, mTarget.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
|
|
72
|
+ }
|
|
|
73
|
+ Files.write(mTargetHash.toPath(), mSourceHash.getBytes(StandardCharsets.UTF_8));
|
|
|
74
|
+ }
|
|
|
75
|
+} |
mobile/android/geckoview/src/test/assets/empty-hash.sha256
mobile/android/geckoview/src/test/assets/empty-hash.xz
No preview for this file type
mobile/android/geckoview/src/test/assets/no-hash.xz
No preview for this file type
mobile/android/geckoview/src/test/assets/test.sha256
|
|
1
|
+d05557c2592a0b2f4d5553ff10a810168755dd98adfabae60fc055273819fd06 |
mobile/android/geckoview/src/test/assets/test.xz
No preview for this file type
mobile/android/geckoview/src/test/java/org/mozilla/gecko/util/XzExtractorTest.java
|
|
1
|
+package org.mozilla.gecko.util;
|
|
|
2
|
+
|
|
|
3
|
+import static org.junit.Assert.assertEquals;
|
|
|
4
|
+import static org.junit.Assert.assertThrows;
|
|
|
5
|
+import static org.junit.Assert.assertTrue;
|
|
|
6
|
+
|
|
|
7
|
+import android.content.Context;
|
|
|
8
|
+import android.test.suitebuilder.annotation.SmallTest;
|
|
|
9
|
+import java.io.File;
|
|
|
10
|
+import java.io.IOException;
|
|
|
11
|
+import java.nio.charset.StandardCharsets;
|
|
|
12
|
+import java.nio.file.Files;
|
|
|
13
|
+import org.junit.After;
|
|
|
14
|
+import org.junit.Before;
|
|
|
15
|
+import org.junit.Test;
|
|
|
16
|
+import org.junit.runner.RunWith;
|
|
|
17
|
+import org.robolectric.RobolectricTestRunner;
|
|
|
18
|
+import org.robolectric.RuntimeEnvironment;
|
|
|
19
|
+
|
|
|
20
|
+@RunWith(RobolectricTestRunner.class)
|
|
|
21
|
+@SmallTest
|
|
|
22
|
+public class XzExtractorTest {
|
|
|
23
|
+ private Context mContext;
|
|
|
24
|
+ private File mTargetDir;
|
|
|
25
|
+
|
|
|
26
|
+ private String mTestFileContents = "#!/bin/sh\necho hello world";
|
|
|
27
|
+
|
|
|
28
|
+ @Before
|
|
|
29
|
+ public void setUp() throws IOException {
|
|
|
30
|
+ mContext = RuntimeEnvironment.getApplication();
|
|
|
31
|
+ mTargetDir = Files.createTempDirectory("xz-extractor-test").toFile();
|
|
|
32
|
+ }
|
|
|
33
|
+
|
|
|
34
|
+ @After
|
|
|
35
|
+ public void tearDown() {
|
|
|
36
|
+ deleteRecursively(mTargetDir);
|
|
|
37
|
+ }
|
|
|
38
|
+
|
|
|
39
|
+ @Test
|
|
|
40
|
+ public void freshExtraction() throws IOException, InterruptedException {
|
|
|
41
|
+ XzExtractor extractor = new XzExtractor(mContext, "test", mTargetDir, "test");
|
|
|
42
|
+ File extracted = extractor.extract();
|
|
|
43
|
+
|
|
|
44
|
+ assertTrue(extracted.exists());
|
|
|
45
|
+ assertTrue(new File(mTargetDir, "test.sha256").exists());
|
|
|
46
|
+ }
|
|
|
47
|
+
|
|
|
48
|
+ @Test
|
|
|
49
|
+ public void upToDateSkipsExtraction() throws IOException {
|
|
|
50
|
+ XzExtractor extractor = new XzExtractor(mContext, "test", mTargetDir, "test");
|
|
|
51
|
+ File extracted = extractor.extract();
|
|
|
52
|
+ assertEquals(
|
|
|
53
|
+ mTestFileContents,
|
|
|
54
|
+ new String(Files.readAllBytes(extracted.toPath()), StandardCharsets.UTF_8).trim());
|
|
|
55
|
+
|
|
|
56
|
+ Files.write(extracted.toPath(), "tampered".getBytes(StandardCharsets.UTF_8));
|
|
|
57
|
+ extractor.extract();
|
|
|
58
|
+
|
|
|
59
|
+ assertEquals(
|
|
|
60
|
+ "tampered",
|
|
|
61
|
+ new String(Files.readAllBytes(extracted.toPath()), StandardCharsets.UTF_8));
|
|
|
62
|
+ }
|
|
|
63
|
+
|
|
|
64
|
+ @Test
|
|
|
65
|
+ public void staleHashTriggersReExtraction() throws IOException, InterruptedException {
|
|
|
66
|
+ XzExtractor extractor = new XzExtractor(mContext, "test", mTargetDir, "test");
|
|
|
67
|
+ File extracted = extractor.extract();
|
|
|
68
|
+ File hashFile = new File(mTargetDir, "test.sha256");
|
|
|
69
|
+
|
|
|
70
|
+ assertEquals(
|
|
|
71
|
+ mTestFileContents,
|
|
|
72
|
+ new String(Files.readAllBytes(extracted.toPath()), StandardCharsets.UTF_8).trim());
|
|
|
73
|
+
|
|
|
74
|
+ Files.write(hashFile.toPath(), "new-hash".getBytes(StandardCharsets.UTF_8));
|
|
|
75
|
+ Files.write(extracted.toPath(), "tampered".getBytes(StandardCharsets.UTF_8));
|
|
|
76
|
+
|
|
|
77
|
+ extractor.extract();
|
|
|
78
|
+ assertEquals(
|
|
|
79
|
+ mTestFileContents,
|
|
|
80
|
+ new String(Files.readAllBytes(extracted.toPath()), StandardCharsets.UTF_8).trim());
|
|
|
81
|
+ }
|
|
|
82
|
+
|
|
|
83
|
+ @Test
|
|
|
84
|
+ public void missingAssetThrows() {
|
|
|
85
|
+ XzExtractor extractor = new XzExtractor(mContext, "nonexistent", mTargetDir, "test");
|
|
|
86
|
+ assertThrows(IOException.class, extractor::extract);
|
|
|
87
|
+ }
|
|
|
88
|
+
|
|
|
89
|
+ @Test
|
|
|
90
|
+ public void missingHashFileThrows() {
|
|
|
91
|
+ XzExtractor extractor = new XzExtractor(mContext, "no-hash", mTargetDir, "test");
|
|
|
92
|
+ assertThrows(IOException.class, extractor::extract);
|
|
|
93
|
+ }
|
|
|
94
|
+
|
|
|
95
|
+ @Test
|
|
|
96
|
+ public void emptyHashFileThrows() {
|
|
|
97
|
+ XzExtractor extractor = new XzExtractor(mContext, "empty-hash", mTargetDir, "test");
|
|
|
98
|
+ assertThrows(IOException.class, extractor::extract);
|
|
|
99
|
+ }
|
|
|
100
|
+
|
|
|
101
|
+ private void deleteRecursively(final File file) {
|
|
|
102
|
+ if (file.isDirectory()) {
|
|
|
103
|
+ final File[] children = file.listFiles();
|
|
|
104
|
+ if (children != null) {
|
|
|
105
|
+ for (final File child : children) {
|
|
|
106
|
+ deleteRecursively(child);
|
|
|
107
|
+ }
|
|
|
108
|
+ }
|
|
|
109
|
+ }
|
|
|
110
|
+ file.delete();
|
|
|
111
|
+ }
|
|
|
112
|
+} |
mobile/android/gradle/with_gecko_binaries.gradle
| ... |
... |
@@ -23,10 +23,27 @@ ext.configureVariantWithGeckoBinaries = { variant -> |
|
23
|
23
|
// the moz.build system and should never be re-entrant in this way.
|
|
24
|
24
|
def assetGenTask = tasks.findByName("generate${variant.name.capitalize()}Assets")
|
|
25
|
25
|
def jniLibFoldersTask = tasks.findByName("merge${variant.name.capitalize()}JniLibFolders")
|
|
|
26
|
+
|
|
|
27
|
+ def omnijarName = "omni.ja"
|
|
|
28
|
+ def assetsDir = "${topobjdir}/dist/geckoview/assets"
|
|
|
29
|
+ def compressOmnijarTask = tasks.register("compressOmnijar${variant.name.capitalize()}", Exec) {
|
|
|
30
|
+ inputs.file("${assetsDir}/${omnijarName}")
|
|
|
31
|
+ outputs.files(
|
|
|
32
|
+ "${assetsDir}/${omnijarName}.xz",
|
|
|
33
|
+ "${assetsDir}/${omnijarName}.sha256"
|
|
|
34
|
+ )
|
|
|
35
|
+ commandLine "sh",
|
|
|
36
|
+ "${topsrcdir}/mobile/android/geckoview/omnijar_repack.sh",
|
|
|
37
|
+ "${assetsDir}/${omnijarName}",
|
|
|
38
|
+ "${assetsDir}/${omnijarName}.xz",
|
|
|
39
|
+ "${assetsDir}/${omnijarName}.sha256"
|
|
|
40
|
+ }
|
|
|
41
|
+
|
|
26
|
42
|
if (!mozconfig.substs.MOZILLA_OFFICIAL && !mozconfig.substs.ENABLE_MOZSEARCH_PLUGIN) {
|
|
27
|
|
- assetGenTask.dependsOn rootProject.machStagePackage
|
|
|
43
|
+ compressOmnijarTask.configure { dependsOn rootProject.machStagePackage }
|
|
28
|
44
|
jniLibFoldersTask.dependsOn rootProject.machStagePackage
|
|
29
|
45
|
}
|
|
|
46
|
+ assetGenTask.dependsOn compressOmnijarTask
|
|
30
|
47
|
}
|
|
31
|
48
|
}
|
|
32
|
49
|
|
netwerk/protocol/res/nsResProtocolHandler.cpp
| ... |
... |
@@ -14,6 +14,10 @@ |
|
14
|
14
|
|
|
15
|
15
|
#include "mozilla/Omnijar.h"
|
|
16
|
16
|
|
|
|
17
|
+#ifdef MOZ_WIDGET_ANDROID
|
|
|
18
|
+# include "mozilla/java/GeckoAppShellWrappers.h"
|
|
|
19
|
+#endif
|
|
|
20
|
+
|
|
17
|
21
|
using mozilla::LogLevel;
|
|
18
|
22
|
using mozilla::dom::ContentParent;
|
|
19
|
23
|
|
| ... |
... |
@@ -68,29 +72,12 @@ nsresult nsResProtocolHandler::Init() { |
|
68
|
72
|
|
|
69
|
73
|
#ifdef ANDROID
|
|
70
|
74
|
nsresult nsResProtocolHandler::GetApkURI(nsACString& aResult) {
|
|
71
|
|
- nsCString::const_iterator start, iter;
|
|
72
|
|
- mGREURI.BeginReading(start);
|
|
73
|
|
- mGREURI.EndReading(iter);
|
|
74
|
|
- nsCString::const_iterator start_iter = start;
|
|
75
|
|
-
|
|
76
|
|
- // This is like jar:jar:file://path/to/apk/base.apk!/path/to/omni.ja!/
|
|
77
|
|
- bool found = FindInReadable("!/"_ns, start_iter, iter);
|
|
78
|
|
- NS_ENSURE_TRUE(found, NS_ERROR_UNEXPECTED);
|
|
79
|
|
-
|
|
80
|
|
- // like jar:jar:file://path/to/apk/base.apk!/
|
|
81
|
|
- const nsDependentCSubstring& withoutPath = Substring(start, iter);
|
|
82
|
|
- NS_ENSURE_TRUE(withoutPath.Length() >= 4, NS_ERROR_UNEXPECTED);
|
|
83
|
|
-
|
|
84
|
|
- // Let's make sure we're removing what we expect to remove
|
|
85
|
|
- NS_ENSURE_TRUE(Substring(withoutPath, 0, 4).EqualsLiteral("jar:"),
|
|
86
|
|
- NS_ERROR_UNEXPECTED);
|
|
87
|
|
-
|
|
88
|
|
- // like jar:file://path/to/apk/base.apk!/
|
|
89
|
|
- aResult = ToNewCString(Substring(withoutPath, 4));
|
|
90
|
|
-
|
|
91
|
|
- // Remove the trailing /
|
|
92
|
|
- NS_ENSURE_TRUE(aResult.Length() >= 1, NS_ERROR_UNEXPECTED);
|
|
93
|
|
- aResult.Truncate(aResult.Length() - 1);
|
|
|
75
|
+ mozilla::jni::String::LocalRef path =
|
|
|
76
|
+ mozilla::java::GeckoAppShell::GetApkPath();
|
|
|
77
|
+ if (!path) {
|
|
|
78
|
+ return NS_ERROR_UNEXPECTED;
|
|
|
79
|
+ }
|
|
|
80
|
+ aResult = "jar:file://"_ns + path->ToCString() + "!"_ns;
|
|
94
|
81
|
return NS_OK;
|
|
95
|
82
|
}
|
|
96
|
83
|
#endif
|
python/mozbuild/mozbuild/action/fat_aar.py
| ... |
... |
@@ -90,7 +90,7 @@ def fat_aar(distdir, zip_paths, no_process=False, no_compatibility_check=False): |
|
90
|
90
|
jar_finder = JarFinder(
|
|
91
|
91
|
aar_file.file.filename, JarReader(fileobj=aar_file.open())
|
|
92
|
92
|
)
|
|
93
|
|
- for path, fileobj in UnpackFinder(jar_finder):
|
|
|
93
|
+ for path, fileobj in UnpackFinder(jar_finder, omnijar_name="assets/omni.ja.xz"):
|
|
94
|
94
|
# Native libraries go straight through.
|
|
95
|
95
|
if mozpath.match(path, "jni/**"):
|
|
96
|
96
|
copier.add(path, fileobj)
|
| ... |
... |
@@ -130,6 +130,7 @@ def fat_aar(distdir, zip_paths, no_process=False, no_compatibility_check=False): |
|
130
|
130
|
"**/*.ftl",
|
|
131
|
131
|
"**/*.dtd",
|
|
132
|
132
|
"**/*.properties",
|
|
|
133
|
+ "assets/omni.ja.sha256",
|
|
133
|
134
|
}
|
|
134
|
135
|
|
|
135
|
136
|
not_allowed = OrderedDict()
|
python/mozbuild/mozpack/packager/unpack.py
| ... |
... |
@@ -3,6 +3,7 @@ |
|
3
|
3
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
4
|
4
|
|
|
5
|
5
|
import codecs
|
|
|
6
|
+import lzma
|
|
6
|
7
|
from urllib.parse import urlparse
|
|
7
|
8
|
|
|
8
|
9
|
import mozpack.path as mozpath
|
| ... |
... |
@@ -143,7 +144,11 @@ class UnpackFinder(BaseFinder): |
|
143
|
144
|
Return a JarReader for the given BaseFile instance, keeping a log of
|
|
144
|
145
|
the preloaded entries it has.
|
|
145
|
146
|
"""
|
|
146
|
|
- jar = JarReader(fileobj=file.open())
|
|
|
147
|
+ if path.endswith(".xz"):
|
|
|
148
|
+ f = lzma.open(file.open())
|
|
|
149
|
+ else:
|
|
|
150
|
+ f = file.open()
|
|
|
151
|
+ jar = JarReader(fileobj=f)
|
|
147
|
152
|
self.compressed = max(self.compressed, jar.compression)
|
|
148
|
153
|
if jar.last_preloaded:
|
|
149
|
154
|
jarlog = list(jar.entries.keys())
|
| ... |
... |
@@ -158,8 +163,17 @@ class UnpackFinder(BaseFinder): |
|
158
|
163
|
"""
|
|
159
|
164
|
Return whether the given BaseFile looks like a ZIP/Jar.
|
|
160
|
165
|
"""
|
|
|
166
|
+
|
|
|
167
|
+ def check_header(header):
|
|
|
168
|
+ return len(header) == 8 and (header[0:2] == b"PK" or header[4:6] == b"PK")
|
|
|
169
|
+
|
|
161
|
170
|
header = file.open().read(8)
|
|
162
|
|
- return len(header) == 8 and (header[0:2] == b"PK" or header[4:6] == b"PK")
|
|
|
171
|
+ if check_header(header):
|
|
|
172
|
+ return True
|
|
|
173
|
+ if header[0:6] == b"\xfd7zXZ\x00":
|
|
|
174
|
+ with lzma.open(file.open()) as f:
|
|
|
175
|
+ return check_header(f.read(8))
|
|
|
176
|
+ return False
|
|
163
|
177
|
|
|
164
|
178
|
def _unjarize(self, entry, relpath):
|
|
165
|
179
|
"""
|
|