Loading mobile/android/fenix/app/build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -197,7 +197,7 @@ android { // manifest.template.json is converted to manifest.json at build time. // No need to package the template in the APK. ignoreAssetsPattern = "manifest.template.json" ignoreAssetsPattern = "manifest.template.json:omni.ja" } testOptions { Loading mobile/android/geckoview/build.gradle +11 −0 Original line number Diff line number Diff line Loading @@ -82,10 +82,20 @@ android { buildConfigField 'boolean', 'MOZ_ANDROID_CONTENT_SERVICE_ISOLATED_PROCESS', mozconfig.substs.MOZ_ANDROID_CONTENT_SERVICE_ISOLATED_PROCESS ? 'true' : 'false'; } aaptOptions { noCompress 'xz' } lintOptions { abortOnError = false } testOptions { unitTests { includeAndroidResources = true } } sourceSets { main { java { Loading Loading @@ -194,6 +204,7 @@ dependencies { implementation libs.androidx.lifecycle.common implementation libs.androidx.lifecycle.process implementation libs.play.services.fido implementation "org.tukaani:xz:1.12" implementation "org.yaml:snakeyaml:2.2" if (mozconfig.substs.MOZ_ANDROID_HLS_SUPPORT) { Loading mobile/android/geckoview/omnijar_repack.sh 0 → 100755 +12 −0 Original line number Diff line number Diff line #!/bin/sh # Repack a .ja (ZIP) stored uncompressed, compress with XZ, write SHA-256 sidecar. # Usage: omnijar_repack.sh <src> <dst-file> <dst-tag> set -e src="$1" dst_file="$2" dst_tag="$3" tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT cp "$src" "$tmp/omni.ja" # Repack all entries as ZIP_STORED (strip per-entry deflate) ( cd "$tmp" && unzip -q omni.ja -d unpacked && cd unpacked && zip -q -0 -r ../repacked.ja . ) xz -9e -k -c "$tmp/repacked.ja" > "$dst_file" sha256sum -b "$tmp/repacked.ja" | awk '{print $1}' > "$dst_tag" mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java +19 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; Loading @@ -31,6 +33,7 @@ import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.mozglue.GeckoLoader; import org.mozilla.gecko.process.MemoryController; import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.XzExtractor; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.BuildConfig; import org.mozilla.geckoview.GeckoResult; Loading Loading @@ -329,6 +332,15 @@ public class GeckoThread extends Thread { loadGeckoLibs(context); } private static File extractOmnijar(final Context context) throws IOException { return new XzExtractor( context, "omni.ja", context.getNoBackupFilesDir(), "omni.ja") .extract(); } private String[] getMainProcessArgs() { final Context context = GeckoAppShell.getApplicationContext(); final ArrayList<String> args = new ArrayList<>(); Loading @@ -337,8 +349,13 @@ public class GeckoThread extends Thread { args.add(context.getPackageName()); if (!mInitInfo.xpcshell) { try { args.add("-greomni"); args.add(context.getPackageResourcePath()); String greomni = extractOmnijar(context).getAbsolutePath(); args.add(greomni); } catch (final IOException e) { throw new RuntimeException("Failed to extract omnijar", e); } } if (mInitInfo.args != null) { Loading mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XzExtractor.java 0 → 100644 +75 −0 Original line number Diff line number Diff line package org.mozilla.gecko.util; import android.content.Context; import android.content.res.AssetManager; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import org.tukaani.xz.XZInputStream; public class XzExtractor { private static final String TAG = "XzExtractor"; private final Context mContext; private final String mSource; private String mSourceHash; private final File mTarget; private final File mTargetHash; public XzExtractor(Context context, String source, File targetDir, String destination) { mContext = context; mSource = source; mTarget = new File(targetDir, destination); mTargetHash = new File(targetDir, destination + ".sha256"); } public File extract() throws IOException { Log.d(TAG, "Extracting " + mSource + " -> " + mTarget.getAbsolutePath()); readSourceTag(); if (checkExisting()) { Log.d(TAG, mSource + ": up to date, skipping extraction"); } else { Log.d(TAG, mSource + ": extracting from assets"); extractFromAssets(); Log.d(TAG, mSource + ": extraction complete"); } return mTarget; } private void readSourceTag() throws IOException { // We use sha256 because we need a unique deterministic string // to verify if current resources are up to date or need to be // re-extracted. THIS IS NOT AN INTEGRITY CHECK. final String asset = mSource + ".sha256"; final Context context = mContext; try (InputStream in = context.getAssets().open(asset, AssetManager.ACCESS_BUFFER); BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { mSourceHash = reader.readLine(); } if (mSourceHash == null || mSourceHash.isEmpty()) { throw new IOException(asset + " is empty"); } } private boolean checkExisting() throws IOException { if (!mTarget.exists() || !mTargetHash.exists()) { return false; } String destHash = new String(Files.readAllBytes(mTargetHash.toPath()), StandardCharsets.UTF_8).trim(); return destHash.equals(mSourceHash); } private void extractFromAssets() throws IOException { final Context context = mContext; try (InputStream assetIn = context.getAssets().open(mSource + ".xz"); XZInputStream xzIn = new XZInputStream(assetIn)) { Files.copy(xzIn, mTarget.toPath(), StandardCopyOption.REPLACE_EXISTING); } Files.write(mTargetHash.toPath(), mSourceHash.getBytes(StandardCharsets.UTF_8)); } } Loading
mobile/android/fenix/app/build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -197,7 +197,7 @@ android { // manifest.template.json is converted to manifest.json at build time. // No need to package the template in the APK. ignoreAssetsPattern = "manifest.template.json" ignoreAssetsPattern = "manifest.template.json:omni.ja" } testOptions { Loading
mobile/android/geckoview/build.gradle +11 −0 Original line number Diff line number Diff line Loading @@ -82,10 +82,20 @@ android { buildConfigField 'boolean', 'MOZ_ANDROID_CONTENT_SERVICE_ISOLATED_PROCESS', mozconfig.substs.MOZ_ANDROID_CONTENT_SERVICE_ISOLATED_PROCESS ? 'true' : 'false'; } aaptOptions { noCompress 'xz' } lintOptions { abortOnError = false } testOptions { unitTests { includeAndroidResources = true } } sourceSets { main { java { Loading Loading @@ -194,6 +204,7 @@ dependencies { implementation libs.androidx.lifecycle.common implementation libs.androidx.lifecycle.process implementation libs.play.services.fido implementation "org.tukaani:xz:1.12" implementation "org.yaml:snakeyaml:2.2" if (mozconfig.substs.MOZ_ANDROID_HLS_SUPPORT) { Loading
mobile/android/geckoview/omnijar_repack.sh 0 → 100755 +12 −0 Original line number Diff line number Diff line #!/bin/sh # Repack a .ja (ZIP) stored uncompressed, compress with XZ, write SHA-256 sidecar. # Usage: omnijar_repack.sh <src> <dst-file> <dst-tag> set -e src="$1" dst_file="$2" dst_tag="$3" tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT cp "$src" "$tmp/omni.ja" # Repack all entries as ZIP_STORED (strip per-entry deflate) ( cd "$tmp" && unzip -q omni.ja -d unpacked && cd unpacked && zip -q -0 -r ../repacked.ja . ) xz -9e -k -c "$tmp/repacked.ja" > "$dst_file" sha256sum -b "$tmp/repacked.ja" | awk '{print $1}' > "$dst_tag"
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java +19 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; Loading @@ -31,6 +33,7 @@ import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.mozglue.GeckoLoader; import org.mozilla.gecko.process.MemoryController; import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.XzExtractor; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.BuildConfig; import org.mozilla.geckoview.GeckoResult; Loading Loading @@ -329,6 +332,15 @@ public class GeckoThread extends Thread { loadGeckoLibs(context); } private static File extractOmnijar(final Context context) throws IOException { return new XzExtractor( context, "omni.ja", context.getNoBackupFilesDir(), "omni.ja") .extract(); } private String[] getMainProcessArgs() { final Context context = GeckoAppShell.getApplicationContext(); final ArrayList<String> args = new ArrayList<>(); Loading @@ -337,8 +349,13 @@ public class GeckoThread extends Thread { args.add(context.getPackageName()); if (!mInitInfo.xpcshell) { try { args.add("-greomni"); args.add(context.getPackageResourcePath()); String greomni = extractOmnijar(context).getAbsolutePath(); args.add(greomni); } catch (final IOException e) { throw new RuntimeException("Failed to extract omnijar", e); } } if (mInitInfo.args != null) { Loading
mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/XzExtractor.java 0 → 100644 +75 −0 Original line number Diff line number Diff line package org.mozilla.gecko.util; import android.content.Context; import android.content.res.AssetManager; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import org.tukaani.xz.XZInputStream; public class XzExtractor { private static final String TAG = "XzExtractor"; private final Context mContext; private final String mSource; private String mSourceHash; private final File mTarget; private final File mTargetHash; public XzExtractor(Context context, String source, File targetDir, String destination) { mContext = context; mSource = source; mTarget = new File(targetDir, destination); mTargetHash = new File(targetDir, destination + ".sha256"); } public File extract() throws IOException { Log.d(TAG, "Extracting " + mSource + " -> " + mTarget.getAbsolutePath()); readSourceTag(); if (checkExisting()) { Log.d(TAG, mSource + ": up to date, skipping extraction"); } else { Log.d(TAG, mSource + ": extracting from assets"); extractFromAssets(); Log.d(TAG, mSource + ": extraction complete"); } return mTarget; } private void readSourceTag() throws IOException { // We use sha256 because we need a unique deterministic string // to verify if current resources are up to date or need to be // re-extracted. THIS IS NOT AN INTEGRITY CHECK. final String asset = mSource + ".sha256"; final Context context = mContext; try (InputStream in = context.getAssets().open(asset, AssetManager.ACCESS_BUFFER); BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { mSourceHash = reader.readLine(); } if (mSourceHash == null || mSourceHash.isEmpty()) { throw new IOException(asset + " is empty"); } } private boolean checkExisting() throws IOException { if (!mTarget.exists() || !mTargetHash.exists()) { return false; } String destHash = new String(Files.readAllBytes(mTargetHash.toPath()), StandardCharsets.UTF_8).trim(); return destHash.equals(mSourceHash); } private void extractFromAssets() throws IOException { final Context context = mContext; try (InputStream assetIn = context.getAssets().open(mSource + ".xz"); XZInputStream xzIn = new XZInputStream(assetIn)) { Files.copy(xzIn, mTarget.toPath(), StandardCopyOption.REPLACE_EXISTING); } Files.write(mTargetHash.toPath(), mSourceHash.getBytes(StandardCharsets.UTF_8)); } }