commit 44f93088cd074f35cf490a5d8eee687b73aebb5c Author: Laiteux Date: Mon Oct 27 16:22:29 2025 +0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc7108e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +eazyeuicc +out \ No newline at end of file diff --git a/build+install.sh b/build+install.sh new file mode 100755 index 0000000..068baf1 --- /dev/null +++ b/build+install.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================ +APP_PKG="uk.simlink.lpa" +JAVA_PKG="im.angry.openeuicc.bridge" +APKTOOL_TREE="eazyeuicc" +OUT_DIR="out" +SRC_ROOT="src" +BAKSMALI_JAR="tools/baksmali.jar" +# ============================================================================ + +pick_smali_bucket() { + local base="$APKTOOL_TREE" + local n=2 + [[ -d "$base/smali" ]] && echo "$base/smali" && return 0 + while [[ -d "$base/smali_classes$n" ]]; do n=$((n+1)); done + echo "$base/smali_classes$n" +} + +# Derived paths/names +SMALI_PKG_DIR="${JAVA_PKG//.//}" +SRC_PKG_DIR="${SRC_ROOT}/${SMALI_PKG_DIR}" +TARGET_SMALI_ROOT="$(pick_smali_bucket)" +TARGET_SMALI_DIR="${TARGET_SMALI_ROOT}/${SMALI_PKG_DIR}" + +# SDK paths +ANDROID_SDK="$HOME/Library/Android/sdk" +ANDROID_JAR="$ANDROID_SDK/platforms/android-33/android.jar" +BT="$ANDROID_SDK/build-tools/$(ls "$ANDROID_SDK/build-tools" | sort -V | tail -1)" + +# Sanity: tools present +command -v apktool >/dev/null || { echo "ERROR: apktool not found"; exit 1; } +command -v adb >/dev/null || { echo "ERROR: adb not found"; exit 1; } +command -v javac >/dev/null || { echo "ERROR: javac not found"; exit 1; } +command -v java >/dev/null || { echo "ERROR: java not found"; exit 1; } +command -v keytool >/dev/null || { echo "ERROR: keytool not found"; exit 1; } + +# Sanity: files/dirs +[[ -f "$ANDROID_JAR" ]] || { echo "ERROR: android.jar not found: $ANDROID_JAR"; exit 1; } +[[ -x "$BT/d8" ]] || { echo "ERROR: d8 not found: $BT/d8"; exit 1; } +[[ -x "$BT/zipalign" ]] || { echo "ERROR: zipalign not found: $BT/zipalign"; exit 1; } +[[ -x "$BT/apksigner" ]] || { echo "ERROR: apksigner not found: $BT/apksigner"; exit 1; } +[[ -f "$BAKSMALI_JAR" ]] || { echo "ERROR: baksmali jar missing: $BAKSMALI_JAR"; exit 1; } +[[ -d "$APKTOOL_TREE" ]] || { echo "ERROR: apktool tree missing: $APKTOOL_TREE"; exit 1; } +[[ -d "$SRC_PKG_DIR" ]] || { echo "ERROR: source package dir missing: $SRC_PKG_DIR"; exit 1; } + +# Gather sources +JAVA_SOURCES=() +while IFS= read -r -d '' f; do + JAVA_SOURCES+=("$f") +done < <(find "$SRC_PKG_DIR" -type f -name '*.java' -print0) +[[ ${#JAVA_SOURCES[@]} -gt 0 ]] || { echo "ERROR: no .java files found under $SRC_PKG_DIR"; exit 1; } + +# Clean + mkdirs +rm -rf "$APKTOOL_TREE/build" +rm -rf "$OUT_DIR" +mkdir -p "$OUT_DIR"/{classes,dex,smali} + +# Compile +javac --release 23 -cp "$ANDROID_JAR" \ + -d "$OUT_DIR/classes" \ + "${JAVA_SOURCES[@]}" + +# DEX +"$BT/d8" --lib "$ANDROID_JAR" \ + --output "$OUT_DIR/dex" \ + $(find "$OUT_DIR/classes" -type f -name '*.class') + +# Smali +found_dex=0 +for dex in "$OUT_DIR"/dex/*.dex; do + [[ -f "$dex" ]] || continue + found_dex=1 + java -jar "$BAKSMALI_JAR" d "$dex" -o "$OUT_DIR/smali" +done +[[ $found_dex -eq 1 ]] || { echo "ERROR: no dex files produced"; exit 1; } +mkdir -p "$TARGET_SMALI_DIR" +if [[ -d "$OUT_DIR/smali/$SMALI_PKG_DIR" ]]; then + rm -rf "$TARGET_SMALI_DIR" + mkdir -p "$TARGET_SMALI_DIR" + cp -R "$OUT_DIR/smali/$SMALI_PKG_DIR/." "$TARGET_SMALI_DIR/" +else + echo "ERROR: expected smali package dir not found: $OUT_DIR/smali/$SMALI_PKG_DIR" + exit 1 +fi + +# Rebuild +apktool b "$APKTOOL_TREE" -o "$OUT_DIR/build-unsigned.apk" + +# Align +"$BT/zipalign" -p -f 4 "$OUT_DIR/build-unsigned.apk" "$OUT_DIR/build-aligned.apk" + +# Ensure keystore +if [ ! -f "$HOME/.android/debug.keystore" ]; then + mkdir -p "$HOME/.android" + keytool -genkeypair \ + -keystore "$HOME/.android/debug.keystore" \ + -storepass android -keypass android \ + -alias androiddebugkey \ + -keyalg RSA -keysize 2048 -validity 10000 \ + -dname "CN=Android Debug,O=Android,C=US" +fi + +# Sign +java --enable-native-access=ALL-UNNAMED \ + -jar "$BT/lib/apksigner.jar" sign \ + --ks "$HOME/.android/debug.keystore" \ + --ks-key-alias androiddebugkey \ + --ks-pass pass:android --key-pass pass:android \ + --out "$OUT_DIR/build-signed.apk" \ + "$OUT_DIR/build-aligned.apk" + +# Install +adb install -r "$OUT_DIR/build-signed.apk" diff --git a/eazyeuicc.apk b/eazyeuicc.apk new file mode 100644 index 0000000..7dd461f Binary files /dev/null and b/eazyeuicc.apk differ diff --git a/src/im/angry/openeuicc/bridge/LpaBridgeProvider.java b/src/im/angry/openeuicc/bridge/LpaBridgeProvider.java new file mode 100644 index 0000000..88d8e37 --- /dev/null +++ b/src/im/angry/openeuicc/bridge/LpaBridgeProvider.java @@ -0,0 +1,253 @@ +package im.angry.openeuicc.bridge; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.HashMap; + +public class LpaBridgeProvider extends ContentProvider +{ + public static final String AUTHORITY = "lpa.bridge"; + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) + { + MatrixCursor rows; + + final String path = uri.getLastPathSegment(); + + if (path == null) + { + rows = error("no_path"); + } + else + { + final Map args = getArgsFromUri(uri); + + switch (path) + { + case "test": + rows = handleTest(args); + break; + default: + rows = error("unknown_path"); + break; + } + } + + return projectColumns(rows, projection, new String[] { "error" }); + } + + // region Mandatory Overrides + + @Override + public boolean onCreate() { return true; } + + @Override + public Uri insert(Uri uri, ContentValues values) { return null; } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } + + @Override + public String getType(Uri uri) { return null; } + + // endregion + + // region Handlers + + private MatrixCursor handleTest(Map args) + { + var slotId = new int[1]; + var portId = new int[1]; + + var error = requireSlotAndPort(args, slotId, portId); + if (error != null) return error; + + return row("ok", "true"); + } + + // endregion + + // region Arg Helpers + + private static Map getArgsFromUri(Uri uri) + { + var out = new HashMap(); + + for (String name : uri.getQueryParameterNames()) + { + out.put(name, uri.getQueryParameter(name)); + } + + return out; + } + + private static boolean tryGet(Map args, String key, String[] out) + { + String arg = args.get(key); + + if (arg == null || arg.isEmpty()) + return false; + + out[0] = arg; + return true; + } + + private static boolean tryGetInt(Map args, String key, int[] out) + { + String[] tmp = new String[1]; + + if (!tryGet(args, key, tmp)) + return false; + + try + { + out[0] = Integer.parseInt(tmp[0]); + return true; + } + catch (NumberFormatException ex) + { + return false; + } + } + + private MatrixCursor requireSlotAndPort(Map args, int[] slotIdOut, int[] portIdOut) + { + final String slotIdArg = "slotId"; + final String portIdArg = "portId"; + + if (!tryGetInt(args, slotIdArg, slotIdOut)) + return error("missing_arg_" + slotIdArg); + + if (!tryGetInt(args, portIdArg, portIdOut)) + return error("missing_arg_" + portIdArg); + + return null; + } + + // endregion + + // region Row Helpers + + private static MatrixCursor empty() + { + return new MatrixCursor(new String[0]); + } + + private static MatrixCursor error(String message) + { + return row("error", message); + } + + private static MatrixCursor row(String key, String value) + { + return row(new String[] { key }, value); + } + + private static MatrixCursor row(String[] columns, Object... values) + { + var rows = new MatrixCursor(columns); + rows.addRow(values); + return rows; + } + + private static MatrixCursor projectColumns(MatrixCursor source, String[] projection) + { + return projectColumns(source, projection, null); + } + + private static MatrixCursor projectColumns(MatrixCursor source, String[] projection, String[] preserve) + { + if (source == null) + return empty(); + + String[] sourceCols = source.getColumnNames(); + var cols = new LinkedHashSet(); + + if (projection != null && projection.length > 0) + Collections.addAll(cols, projection); + else + Collections.addAll(cols, sourceCols); + + if (preserve != null && preserve.length > 0) + { + for (String col : preserve) + { + if (col == null) + continue; + + boolean exists = false; + + for (String sourceCol : sourceCols) + { + if (col.equals(sourceCol)) + { + exists = true; + break; + } + } + + if (exists) + cols.add(col); + } + } + + if (cols.isEmpty()) + return source; + + var outCols = cols.toArray(new String[0]); + var out = new MatrixCursor(outCols); + + while (source.moveToNext()) + { + var row = new Object[outCols.length]; + + for (int i = 0; i < outCols.length; i++) + { + int index = source.getColumnIndex(outCols[i]); + + if (index < 0) + { + row[i] = null; + continue; + } + + switch (source.getType(index)) + { + case Cursor.FIELD_TYPE_NULL: + row[i] = null; + break; + case Cursor.FIELD_TYPE_INTEGER: + row[i] = source.getLong(index); + break; + case Cursor.FIELD_TYPE_FLOAT: + row[i] = source.getDouble(index); + break; + case Cursor.FIELD_TYPE_BLOB: + row[i] = source.getBlob(index); + break; + case Cursor.FIELD_TYPE_STRING: + default: + row[i] = source.getString(index); + break; + } + } + + out.addRow(row); + } + + return out; + } + + // endregion +} diff --git a/tools/baksmali-standalone.jar b/tools/baksmali-standalone.jar new file mode 100644 index 0000000..8537307 --- /dev/null +++ b/tools/baksmali-standalone.jar @@ -0,0 +1 @@ +Not Found \ No newline at end of file diff --git a/tools/baksmali.jar b/tools/baksmali.jar new file mode 100644 index 0000000..b548bfc Binary files /dev/null and b/tools/baksmali.jar differ diff --git a/tools/dexlib2.jar b/tools/dexlib2.jar new file mode 100644 index 0000000..392b3fc Binary files /dev/null and b/tools/dexlib2.jar differ diff --git a/tools/smali.jar b/tools/smali.jar new file mode 100644 index 0000000..2aa9752 Binary files /dev/null and b/tools/smali.jar differ diff --git a/tools/util.jar b/tools/util.jar new file mode 100644 index 0000000..51e993e Binary files /dev/null and b/tools/util.jar differ