Add withEuiccChannel + getProfiles
This commit is contained in:
@@ -7,6 +7,8 @@ JAVA_PKG="im.angry.openeuicc.bridge"
|
||||
APKTOOL_TREE="eazyeuicc"
|
||||
OUT_DIR="out"
|
||||
SRC_ROOT="src"
|
||||
ORIG_APK="eazyeuicc.apk"
|
||||
APP_JAR="deps/eazyeuicc.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 "$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
|
||||
JAVA_SOURCES=()
|
||||
while IFS= read -r -d '' f; do
|
||||
@@ -58,12 +105,20 @@ rm -rf "$OUT_DIR"
|
||||
mkdir -p "$OUT_DIR"/{classes,dex,smali}
|
||||
|
||||
# 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" \
|
||||
"${JAVA_SOURCES[@]}"
|
||||
|
||||
|
||||
# DEX
|
||||
"$BT/d8" --lib "$ANDROID_JAR" \
|
||||
"$BT/d8" \
|
||||
--lib "$ANDROID_JAR" \
|
||||
--classpath "$APP_JAR" \
|
||||
--output "$OUT_DIR/dex" \
|
||||
$(find "$OUT_DIR/classes" -type f -name '*.class')
|
||||
|
||||
|
||||
BIN
deps/eazyeuicc.jar
vendored
Normal file
BIN
deps/eazyeuicc.jar
vendored
Normal file
Binary file not shown.
@@ -1,20 +1,44 @@
|
||||
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.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;
|
||||
import im.angry.openeuicc.OpenEuiccApplication;
|
||||
import im.angry.openeuicc.core.EuiccChannel;
|
||||
import im.angry.openeuicc.core.EuiccChannelManager;
|
||||
import im.angry.openeuicc.util.LPAUtilsKt;
|
||||
import im.angry.openeuicc.di.AppContainer;
|
||||
import net.typeblog.lpac_jni.LocalProfileInfo;
|
||||
|
||||
public class LpaBridgeProvider extends ContentProvider
|
||||
{
|
||||
public static final String AUTHORITY = "lpa.bridge";
|
||||
|
||||
private AppContainer appContainer;
|
||||
|
||||
@Override
|
||||
public boolean onCreate()
|
||||
{
|
||||
appContainer = ((OpenEuiccApplication)getContext().getApplicationContext()).getAppContainer();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
|
||||
{
|
||||
@@ -28,16 +52,26 @@ public class LpaBridgeProvider extends ContentProvider
|
||||
}
|
||||
else
|
||||
{
|
||||
final Map<String, String> args = getArgsFromUri(uri);
|
||||
|
||||
switch (path)
|
||||
try
|
||||
{
|
||||
case "test":
|
||||
rows = handleTest(args);
|
||||
break;
|
||||
default:
|
||||
rows = error("unknown_path");
|
||||
break;
|
||||
final Map<String, String> args = getArgsFromUri(uri);
|
||||
|
||||
switch (path)
|
||||
{
|
||||
case "ping":
|
||||
rows = handlePing(args);
|
||||
break;
|
||||
case "profiles":
|
||||
rows = handleGetProfiles(args);
|
||||
break;
|
||||
default:
|
||||
rows = error("unknown_path");
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
rows = error(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +80,6 @@ public class LpaBridgeProvider extends ContentProvider
|
||||
|
||||
// region Mandatory Overrides
|
||||
|
||||
@Override
|
||||
public boolean onCreate() { return true; }
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) { return null; }
|
||||
|
||||
@@ -65,15 +96,74 @@ public class LpaBridgeProvider extends ContentProvider
|
||||
|
||||
// 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 portId = new int[1];
|
||||
|
||||
var error = requireSlotAndPort(args, slotId, portId);
|
||||
if (error != null) return error;
|
||||
if (!tryGetInt(args, slotIdArg, slotId))
|
||||
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
|
||||
@@ -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
|
||||
|
||||
// region Row Helpers
|
||||
|
||||
Reference in New Issue
Block a user