Add withEuiccChannel + getProfiles

This commit is contained in:
Laiteux
2025-10-28 03:19:13 +04:00
parent 44f93088cd
commit 735d370ff4
3 changed files with 167 additions and 36 deletions

View File

@@ -7,6 +7,8 @@ JAVA_PKG="im.angry.openeuicc.bridge"
APKTOOL_TREE="eazyeuicc" APKTOOL_TREE="eazyeuicc"
OUT_DIR="out" OUT_DIR="out"
SRC_ROOT="src" SRC_ROOT="src"
ORIG_APK="eazyeuicc.apk"
APP_JAR="deps/eazyeuicc.jar"
BAKSMALI_JAR="tools/baksmali.jar" BAKSMALI_JAR="tools/baksmali.jar"
# ============================================================================ # ============================================================================
@@ -45,6 +47,51 @@ command -v keytool >/dev/null || { echo "ERROR: keytool not found"; exit 1; }
[[ -d "$APKTOOL_TREE" ]] || { echo "ERROR: apktool tree missing: $APKTOOL_TREE"; 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; } [[ -d "$SRC_PKG_DIR" ]] || { echo "ERROR: source package dir missing: $SRC_PKG_DIR"; exit 1; }
# Ensure we have a complete classpath jar containing ALL dexes (kotlin, coroutines, lpac_jni, etc.)
rebuild_cp_jar() {
local apk="$1"
local dexdir="deps/_dex"
local mergedir="deps/_merge"
rm -rf "$dexdir" "$mergedir"
mkdir -p "$dexdir" "$mergedir" deps
echo "[dex] extracting classes*.dex from $apk"
unzip -q -j "$apk" 'classes*.dex' -d "$dexdir"
local any=0
for d in "$dexdir"/classes*.dex; do
[[ -f "$d" ]] || continue
any=1
local j="${d%.dex}.jar"
echo "[dex2jar] $d -> $j"
d2j-dex2jar "$d" -o "$j" >/dev/null
unzip -q -o "$j" -d "$mergedir"
done
[[ $any -eq 1 ]] || { echo "ERROR: no classes*.dex found in $apk"; exit 1; }
(cd "$mergedir" && jar cf ../eazyeuicc.jar .)
rm -rf "$dexdir" "$mergedir"
}
# (Re)build classpath jar if missing or incomplete
need_rebuild=0
if [[ ! -f "$APP_JAR" ]]; then
need_rebuild=1
else
jar tf "$APP_JAR" | grep -q '^kotlin/' || need_rebuild=1
jar tf "$APP_JAR" | grep -q '^kotlinx/coroutines/' || need_rebuild=1
jar tf "$APP_JAR" | grep -q '^net/typeblog/lpac_jni/' || need_rebuild=1
fi
if [[ $need_rebuild -eq 1 ]]; then
[[ -f "$ORIG_APK" ]] || { echo "ERROR: ORIG_APK not found: $ORIG_APK"; exit 1; }
rebuild_cp_jar "$ORIG_APK"
fi
# Verify again (fail fast with helpful messages)
jar tf "$APP_JAR" | grep -q '^kotlin/' || { echo "ERROR: Kotlin stdlib missing in $APP_JAR"; exit 1; }
jar tf "$APP_JAR" | grep -q '^kotlinx/coroutines/' || { echo "ERROR: kotlinx coroutines missing in $APP_JAR"; exit 1; }
jar tf "$APP_JAR" | grep -q '^net/typeblog/lpac_jni/' || { echo "ERROR: net.typeblog.lpac_jni missing in $APP_JAR"; exit 1; }
# Gather sources # Gather sources
JAVA_SOURCES=() JAVA_SOURCES=()
while IFS= read -r -d '' f; do while IFS= read -r -d '' f; do
@@ -58,12 +105,20 @@ rm -rf "$OUT_DIR"
mkdir -p "$OUT_DIR"/{classes,dex,smali} mkdir -p "$OUT_DIR"/{classes,dex,smali}
# Compile # Compile
javac --release 23 -cp "$ANDROID_JAR" \ CP="$ANDROID_JAR:$APP_JAR"
javac \
-source 23 -target 23 \
-classpath "$CP" \
-Xlint:-options \
-encoding UTF-8 \
-d "$OUT_DIR/classes" \ -d "$OUT_DIR/classes" \
"${JAVA_SOURCES[@]}" "${JAVA_SOURCES[@]}"
# DEX # DEX
"$BT/d8" --lib "$ANDROID_JAR" \ "$BT/d8" \
--lib "$ANDROID_JAR" \
--classpath "$APP_JAR" \
--output "$OUT_DIR/dex" \ --output "$OUT_DIR/dex" \
$(find "$OUT_DIR/classes" -type f -name '*.class') $(find "$OUT_DIR/classes" -type f -name '*.class')

BIN
deps/eazyeuicc.jar vendored Normal file

Binary file not shown.

View File

@@ -1,20 +1,44 @@
package im.angry.openeuicc.bridge; package im.angry.openeuicc.bridge;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.EmptyCoroutineContext;
import kotlin.jvm.functions.Function2;
import kotlinx.coroutines.BuildersKt;
import kotlinx.coroutines.CoroutineScope;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.database.MatrixCursor; import android.database.MatrixCursor;
import android.net.Uri; import android.net.Uri;
import java.util.Collections; import im.angry.openeuicc.OpenEuiccApplication;
import java.util.LinkedHashSet; import im.angry.openeuicc.core.EuiccChannel;
import java.util.Map; import im.angry.openeuicc.core.EuiccChannelManager;
import java.util.HashMap; import im.angry.openeuicc.util.LPAUtilsKt;
import im.angry.openeuicc.di.AppContainer;
import net.typeblog.lpac_jni.LocalProfileInfo;
public class LpaBridgeProvider extends ContentProvider public class LpaBridgeProvider extends ContentProvider
{ {
public static final String AUTHORITY = "lpa.bridge"; public static final String AUTHORITY = "lpa.bridge";
private AppContainer appContainer;
@Override
public boolean onCreate()
{
appContainer = ((OpenEuiccApplication)getContext().getApplicationContext()).getAppContainer();
return true;
}
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{ {
@@ -27,28 +51,35 @@ public class LpaBridgeProvider extends ContentProvider
rows = error("no_path"); rows = error("no_path");
} }
else else
{
try
{ {
final Map<String, String> args = getArgsFromUri(uri); final Map<String, String> args = getArgsFromUri(uri);
switch (path) switch (path)
{ {
case "test": case "ping":
rows = handleTest(args); rows = handlePing(args);
break;
case "profiles":
rows = handleGetProfiles(args);
break; break;
default: default:
rows = error("unknown_path"); rows = error("unknown_path");
break; break;
} }
} }
catch (Exception ex)
{
rows = error(ex.getMessage());
}
}
return projectColumns(rows, projection, new String[] { "error" }); return projectColumns(rows, projection, new String[] { "error" });
} }
// region Mandatory Overrides // region Mandatory Overrides
@Override
public boolean onCreate() { return true; }
@Override @Override
public Uri insert(Uri uri, ContentValues values) { return null; } public Uri insert(Uri uri, ContentValues values) { return null; }
@@ -65,15 +96,74 @@ public class LpaBridgeProvider extends ContentProvider
// region Handlers // region Handlers
private MatrixCursor handleTest(Map<String, String> args) private MatrixCursor handlePing(Map<String, String> args)
{ {
return row("ping", "pong");
}
private MatrixCursor handleGetProfiles(Map<String, String> args) throws Exception
{
List<LocalProfileInfo> profiles = withEuiccChannel(
args,
(channel, _) -> channel.getLpa().getProfiles()
);
if (profiles == null || profiles.isEmpty())
return empty();
var rows = new MatrixCursor(new String[]
{
"iccid",
"state",
"name",
"nickname"
});
for (LocalProfileInfo profile : profiles)
{
rows.addRow(new Object[] {
profile.getIccid(),
profile.getState().toString(), // TODO: replace by LPAUtilsKt.isEnabled(profile)?
profile.getName(),
LPAUtilsKt.getDisplayName(profile)
});
}
return rows;
}
// endregion
// region LPA Helpers
@SuppressWarnings("unchecked")
private <T> T withEuiccChannel(Map<String, String> args, Function2<EuiccChannel, Continuation<? super T>, ?> operation) throws Exception
{
final String slotIdArg = "slotId";
final String portIdArg = "portId";
var slotId = new int[1]; var slotId = new int[1];
var portId = new int[1]; var portId = new int[1];
var error = requireSlotAndPort(args, slotId, portId); if (!tryGetInt(args, slotIdArg, slotId))
if (error != null) return error; throw new Exception("missing_arg_" + slotIdArg);
return row("ok", "true"); if (!tryGetInt(args, portIdArg, portId))
throw new Exception("missing_arg_" + portIdArg);
EuiccChannelManager channelManager = appContainer.getEuiccChannelManager();
return (T)BuildersKt.runBlocking(
EmptyCoroutineContext.INSTANCE,
(scope, continuation) -> {
return channelManager.withEuiccChannel(
slotId[0],
portId[0],
operation,
continuation
);
}
);
} }
// endregion // endregion
@@ -121,20 +211,6 @@ public class LpaBridgeProvider extends ContentProvider
} }
} }
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 // endregion
// region Row Helpers // region Row Helpers