Initial commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.DS_Store
|
||||||
|
eazyeuicc
|
||||||
|
out
|
||||||
115
build+install.sh
Executable file
115
build+install.sh
Executable file
@@ -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"
|
||||||
BIN
eazyeuicc.apk
Normal file
BIN
eazyeuicc.apk
Normal file
Binary file not shown.
253
src/im/angry/openeuicc/bridge/LpaBridgeProvider.java
Normal file
253
src/im/angry/openeuicc/bridge/LpaBridgeProvider.java
Normal file
@@ -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<String, String> 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<String, String> 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<String, String> getArgsFromUri(Uri uri)
|
||||||
|
{
|
||||||
|
var out = new HashMap<String, String>();
|
||||||
|
|
||||||
|
for (String name : uri.getQueryParameterNames())
|
||||||
|
{
|
||||||
|
out.put(name, uri.getQueryParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean tryGet(Map<String, String> 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<String, String> 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<String, String> 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<String>();
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
1
tools/baksmali-standalone.jar
Normal file
1
tools/baksmali-standalone.jar
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Not Found
|
||||||
BIN
tools/baksmali.jar
Normal file
BIN
tools/baksmali.jar
Normal file
Binary file not shown.
BIN
tools/dexlib2.jar
Normal file
BIN
tools/dexlib2.jar
Normal file
Binary file not shown.
BIN
tools/smali.jar
Normal file
BIN
tools/smali.jar
Normal file
Binary file not shown.
BIN
tools/util.jar
Normal file
BIN
tools/util.jar
Normal file
Binary file not shown.
Reference in New Issue
Block a user