Commit 63c810a9 authored by brizental's avatar brizental Committed by Pier Angelo Vendrame
Browse files

TB 45086: [android] Repack omni.ja file with LZMA

parent 5c903fac
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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 {
+11 −0
Original line number Diff line number Diff line
@@ -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 {
@@ -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) {
+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"
+19 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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<>();
@@ -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) {
+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