Add /preferences + /setPreference
This commit is contained in:
@@ -26,6 +26,7 @@ import kotlin.jvm.functions.Function2;
|
|||||||
import kotlin.coroutines.Continuation;
|
import kotlin.coroutines.Continuation;
|
||||||
import kotlin.coroutines.EmptyCoroutineContext;
|
import kotlin.coroutines.EmptyCoroutineContext;
|
||||||
import kotlinx.coroutines.BuildersKt;
|
import kotlinx.coroutines.BuildersKt;
|
||||||
|
import kotlinx.coroutines.flow.FlowKt;
|
||||||
import kotlinx.coroutines.sync.Mutex;
|
import kotlinx.coroutines.sync.Mutex;
|
||||||
import kotlinx.coroutines.sync.MutexKt;
|
import kotlinx.coroutines.sync.MutexKt;
|
||||||
|
|
||||||
@@ -39,12 +40,16 @@ import com.google.gson.GsonBuilder;
|
|||||||
|
|
||||||
import im.angry.openeuicc.OpenEuiccApplication;
|
import im.angry.openeuicc.OpenEuiccApplication;
|
||||||
import im.angry.openeuicc.di.AppContainer;
|
import im.angry.openeuicc.di.AppContainer;
|
||||||
|
import im.angry.openeuicc.di.UnprivilegedAppContainer;
|
||||||
import im.angry.openeuicc.core.EuiccChannel;
|
import im.angry.openeuicc.core.EuiccChannel;
|
||||||
|
import im.angry.openeuicc.core.EuiccChannelManager;
|
||||||
import im.angry.openeuicc.core.DefaultEuiccChannelManager;
|
import im.angry.openeuicc.core.DefaultEuiccChannelManager;
|
||||||
import im.angry.openeuicc.util.UiccCardInfoCompat;
|
import im.angry.openeuicc.util.UiccCardInfoCompat;
|
||||||
import im.angry.openeuicc.util.UiccPortInfoCompat;
|
import im.angry.openeuicc.util.UiccPortInfoCompat;
|
||||||
import im.angry.openeuicc.util.LPAUtilsKt;
|
import im.angry.openeuicc.util.LPAUtilsKt;
|
||||||
import im.angry.openeuicc.util.ActivationCode;
|
import im.angry.openeuicc.util.ActivationCode;
|
||||||
|
import im.angry.openeuicc.util.PreferenceUtilsKt;
|
||||||
|
import im.angry.openeuicc.util.PreferenceFlowWrapper;
|
||||||
import net.typeblog.lpac_jni.LocalProfileInfo;
|
import net.typeblog.lpac_jni.LocalProfileInfo;
|
||||||
import net.typeblog.lpac_jni.ProfileDownloadCallback;
|
import net.typeblog.lpac_jni.ProfileDownloadCallback;
|
||||||
|
|
||||||
@@ -61,7 +66,6 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
MatrixCursor rows;
|
MatrixCursor rows;
|
||||||
@@ -93,65 +97,46 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
switch (path)
|
rows = switch (path)
|
||||||
{
|
{
|
||||||
case "ping":
|
// out: string ping=pong
|
||||||
// out: string ping=pong
|
case "ping" -> handlePing(args);
|
||||||
rows = handlePing(args);
|
// out (many): string name, bool enabled
|
||||||
break;
|
case "preferences" -> handleGetPreferences(args);
|
||||||
case "cards":
|
// in: string name, bool enabled
|
||||||
// out (many, can be empty): int slotId, int portId
|
// out: bool success
|
||||||
rows = handleGetCards(args);
|
case "setPreference" -> handleSetPreference(args);
|
||||||
break;
|
// out (many, can be empty): int slotId, int portId
|
||||||
case "profiles":
|
case "cards" -> handleGetCards(args);
|
||||||
// in: int slotId, int portId
|
// in: int slotId, int portId
|
||||||
// out (many, can be empty): string iccid, bool isEnabled, string name, string nickname
|
// out (many, can be empty): string iccid, bool isEnabled, string name, string nickname
|
||||||
rows = handleGetProfiles(args);
|
case "profiles" -> handleGetProfiles(args);
|
||||||
break;
|
// in: int slotId, int portId
|
||||||
case "activeProfile":
|
// out (single, can be empty): string iccid, bool isEnabled, string name, string nickname
|
||||||
// in: int slotId, int portId
|
case "activeProfile" -> handleGetActiveProfile(args);
|
||||||
// out (single, can be empty): string iccid, bool isEnabled, string name, string nickname
|
// in: int slotId, int portId, (either {string activationCode} or {string address, string? matchingId}), string? confirmationCode, string? imei
|
||||||
rows = handleGetActiveProfile(args);
|
// out (single, can be empty): string iccid, bool isEnabled, string name, string nickname
|
||||||
break;
|
case "downloadProfile" -> handleDownloadProfile(args);
|
||||||
case "downloadProfile":
|
// in: int slotId, int portId, string iccid
|
||||||
// in: int slotId, int portId, (either {string activationCode} or {string address, string? matchingId}), string? confirmationCode, string? imei
|
// out: bool success
|
||||||
// out (single, can be empty): string iccid, bool isEnabled, string name, string nickname
|
case "deleteProfile" -> handleDeleteProfile(args);
|
||||||
rows = handleDownloadProfile(args);
|
// in: int slotId, int portId, string iccid, bool refresh=true
|
||||||
break;
|
// out: bool success
|
||||||
case "deleteProfile":
|
case "enableProfile" -> handleEnableProfile(args);
|
||||||
// in: int slotId, int portId, string iccid
|
// in: int slotId, int portId, string iccid, bool refresh=true
|
||||||
// out: bool success
|
// out: bool success
|
||||||
rows = handleDeleteProfile(args);
|
case "disableProfile" -> handleDisableProfile(args);
|
||||||
break;
|
// in: int slotId, int portId, bool refresh=true
|
||||||
case "enableProfile":
|
// out: bool success
|
||||||
// in: int slotId, int portId, string iccid, bool refresh=true
|
case "disableActiveProfile" -> handleDisableActiveProfile(args);
|
||||||
// out: bool success
|
// in: int slotId, int portId, string iccid, bool enable=true, bool refresh=true
|
||||||
rows = handleEnableProfile(args);
|
// out: bool success
|
||||||
break;
|
case "switchProfile" -> handleSwitchProfile(args);
|
||||||
case "disableProfile":
|
// in: int slotId, int portId, string iccid, string nickname
|
||||||
// in: int slotId, int portId, string iccid, bool refresh=true
|
// out: bool success
|
||||||
// out: bool success
|
case "setNickname" -> handleSetNickname(args);
|
||||||
rows = handleDisableProfile(args);
|
default -> error("unknown_path");
|
||||||
break;
|
};
|
||||||
case "disableActiveProfile":
|
|
||||||
// in: int slotId, int portId, bool refresh=true
|
|
||||||
// out: bool success
|
|
||||||
rows = handleDisableActiveProfile(args);
|
|
||||||
break;
|
|
||||||
case "switchProfile":
|
|
||||||
// in: int slotId, int portId, string iccid, bool enable=true, bool refresh=true
|
|
||||||
// out: bool success
|
|
||||||
rows = handleSwitchProfile(args);
|
|
||||||
break;
|
|
||||||
case "setNickname":
|
|
||||||
// in: int slotId, int portId, string iccid, string nickname
|
|
||||||
// out: bool success
|
|
||||||
rows = handleSetNickname(args);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
rows = error("unknown_path");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -172,9 +157,9 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
rows = projectColumns(rows, projection, new String[] { "error" });
|
rows = projectColumns(rows, projection, new String[] { "error" });
|
||||||
|
// return rows;
|
||||||
|
|
||||||
String rowsJson = rowsToJson(rows);
|
String rowsJson = rowsToJson(rows);
|
||||||
|
|
||||||
return row("rows", rowsJson);
|
return row("rows", rowsJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +186,52 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
return row("ping", "pong");
|
return row("ping", "pong");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MatrixCursor handleGetPreferences(Map<String, String> args) throws Exception
|
||||||
|
{
|
||||||
|
var preferences = List.of
|
||||||
|
(
|
||||||
|
"verboseLogging",
|
||||||
|
"safeguardActiveProfile",
|
||||||
|
"filterProfileList",
|
||||||
|
"ignoreTlsCertificate",
|
||||||
|
"notificationsDownload",
|
||||||
|
"notificationsDelete",
|
||||||
|
"notificationsSwitch"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!(appContainer instanceof UnprivilegedAppContainer))
|
||||||
|
preferences.add(1, "forceUseTelephonyManager");
|
||||||
|
|
||||||
|
var columns = new String[] { "name", "enabled" };
|
||||||
|
var values = new Object[preferences.size()][2];
|
||||||
|
|
||||||
|
for (int i = 0; i < preferences.size(); i++)
|
||||||
|
{
|
||||||
|
String name = preferences.get(i);
|
||||||
|
|
||||||
|
values[i][0] = name;
|
||||||
|
values[i][1] = getPreference(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows(columns, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MatrixCursor handleSetPreference(Map<String, String> args) throws Exception
|
||||||
|
{
|
||||||
|
String[] name = new String[1];
|
||||||
|
boolean[] enabled = new boolean[1];
|
||||||
|
|
||||||
|
if (!tryGetArgAsString(args, "name", name))
|
||||||
|
return missingArgError("name");
|
||||||
|
|
||||||
|
if (!tryGetArgAsBoolean(args, "enabled", enabled))
|
||||||
|
return missingArgError("enabled");
|
||||||
|
|
||||||
|
setPreference(name[0], enabled[0]);
|
||||||
|
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
||||||
private MatrixCursor handleGetCards(Map<String, String> args) throws Exception
|
private MatrixCursor handleGetCards(Map<String, String> args) throws Exception
|
||||||
{
|
{
|
||||||
var euiccChannelManager = (DefaultEuiccChannelManager) appContainer.getEuiccChannelManager();
|
var euiccChannelManager = (DefaultEuiccChannelManager) appContainer.getEuiccChannelManager();
|
||||||
@@ -392,6 +423,8 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
if (!tryGetArgAsString(args, "iccid", iccid))
|
if (!tryGetArgAsString(args, "iccid", iccid))
|
||||||
return missingArgError("iccid");
|
return missingArgError("iccid");
|
||||||
|
|
||||||
|
safeguardActiveProfile(args, iccid[0]);
|
||||||
|
|
||||||
boolean success = withEuiccChannel
|
boolean success = withEuiccChannel
|
||||||
(
|
(
|
||||||
args,
|
args,
|
||||||
@@ -432,6 +465,8 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
if (!tryGetArgAsBoolean(args, "refresh", refresh))
|
if (!tryGetArgAsBoolean(args, "refresh", refresh))
|
||||||
refresh[0] = true;
|
refresh[0] = true;
|
||||||
|
|
||||||
|
safeguardActiveProfile(args, iccid[0]);
|
||||||
|
|
||||||
boolean success = withEuiccChannel
|
boolean success = withEuiccChannel
|
||||||
(
|
(
|
||||||
args,
|
args,
|
||||||
@@ -448,6 +483,8 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
if (!tryGetArgAsBoolean(args, "refresh", refresh))
|
if (!tryGetArgAsBoolean(args, "refresh", refresh))
|
||||||
refresh[0] = true;
|
refresh[0] = true;
|
||||||
|
|
||||||
|
safeguardActiveProfile(args, null);
|
||||||
|
|
||||||
String iccid = withEuiccChannel
|
String iccid = withEuiccChannel
|
||||||
(
|
(
|
||||||
args,
|
args,
|
||||||
@@ -524,7 +561,6 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
|
|
||||||
// region LPA Helpers
|
// region LPA Helpers
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private EuiccChannel findEuiccChannel(DefaultEuiccChannelManager euiccChannelManager, int slotId, int portId) throws Exception
|
private EuiccChannel findEuiccChannel(DefaultEuiccChannelManager euiccChannelManager, int slotId, int portId) throws Exception
|
||||||
{
|
{
|
||||||
var findEuiccChannelByPortMethod = DefaultEuiccChannelManager.class.getDeclaredMethod("findEuiccChannelByPort", int.class, int.class, Continuation.class);
|
var findEuiccChannelByPortMethod = DefaultEuiccChannelManager.class.getDeclaredMethod("findEuiccChannelByPort", int.class, int.class, Continuation.class);
|
||||||
@@ -565,11 +601,110 @@ public class LpaBridgeProvider extends ContentProvider
|
|||||||
|
|
||||||
private List<LocalProfileInfo> getProfiles(Map<String, String> args) throws Exception
|
private List<LocalProfileInfo> getProfiles(Map<String, String> args) throws Exception
|
||||||
{
|
{
|
||||||
return withEuiccChannel
|
@SuppressWarnings("unchecked")
|
||||||
|
var profiles = (List<LocalProfileInfo>) withEuiccChannel
|
||||||
(
|
(
|
||||||
args,
|
args,
|
||||||
(channel, _) -> channel.getLpa().getProfiles()
|
(channel, _) -> channel.getLpa().getProfiles()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
boolean filterProfileList = getPreference("filterProfileList");
|
||||||
|
|
||||||
|
if (filterProfileList)
|
||||||
|
return LPAUtilsKt.getOperational(profiles);
|
||||||
|
|
||||||
|
return profiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Preference Helpers
|
||||||
|
|
||||||
|
private List<String> invertedPreferences = List.of
|
||||||
|
(
|
||||||
|
"safeguardActiveProfile",
|
||||||
|
"filterProfileList"
|
||||||
|
);
|
||||||
|
|
||||||
|
private PreferenceFlowWrapper<Boolean> getPreferenceFlow(String name) throws Exception
|
||||||
|
{
|
||||||
|
var preferenceRepository = PreferenceUtilsKt.getPreferenceRepository(getContext());
|
||||||
|
|
||||||
|
return switch (name)
|
||||||
|
{
|
||||||
|
case "verboseLogging" -> preferenceRepository.getVerboseLoggingFlow();
|
||||||
|
case "forceUseTelephonyManager" -> preferenceRepository.getForceUseTMAPIFlow();
|
||||||
|
case "safeguardActiveProfile" -> preferenceRepository.getDisableSafeguardFlow();
|
||||||
|
case "filterProfileList" -> preferenceRepository.getUnfilteredProfileListFlow();
|
||||||
|
case "ignoreTlsCertificate" -> preferenceRepository.getIgnoreTLSCertificateFlow();
|
||||||
|
case "notificationsDownload" -> preferenceRepository.getNotificationDownloadFlow();
|
||||||
|
case "notificationsDelete" -> preferenceRepository.getNotificationDeleteFlow();
|
||||||
|
case "notificationsSwitch" -> preferenceRepository.getNotificationSwitchFlow();
|
||||||
|
default -> throw new Exception("unknown_name");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean getPreference(String name) throws Exception
|
||||||
|
{
|
||||||
|
var preferenceFlow = getPreferenceFlow(name);
|
||||||
|
|
||||||
|
boolean enabled = BuildersKt.runBlocking
|
||||||
|
(
|
||||||
|
EmptyCoroutineContext.INSTANCE,
|
||||||
|
(_, continuation) -> FlowKt.first(preferenceFlow, continuation)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (invertedPreferences.contains(name))
|
||||||
|
enabled = !enabled;
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPreference(String name, boolean enabled) throws Exception
|
||||||
|
{
|
||||||
|
var preferenceFlow = getPreferenceFlow(name);
|
||||||
|
|
||||||
|
if (invertedPreferences.contains(name))
|
||||||
|
enabled = !enabled;
|
||||||
|
|
||||||
|
final boolean enabledFinal = enabled;
|
||||||
|
|
||||||
|
BuildersKt.runBlocking
|
||||||
|
(
|
||||||
|
EmptyCoroutineContext.INSTANCE,
|
||||||
|
(_, continuation) -> preferenceFlow.updatePreference(enabledFinal, continuation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void safeguardActiveProfile(Map<String, String> args, String iccid) throws Exception
|
||||||
|
{
|
||||||
|
int[] slotId = new int[1];
|
||||||
|
int[] portId = new int[1];
|
||||||
|
requireSlotAndPort(args, slotId, portId);
|
||||||
|
|
||||||
|
if (slotId[0] == EuiccChannelManager.USB_CHANNEL_ID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
boolean safeguardEnabled = getPreference("safeguardActiveProfile");
|
||||||
|
|
||||||
|
if (!safeguardEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
boolean isTargetActive = iccid == null;
|
||||||
|
|
||||||
|
if (!isTargetActive)
|
||||||
|
{
|
||||||
|
var profiles = getProfiles(args);
|
||||||
|
var activeProfile = LPAUtilsKt.getEnabled(profiles);
|
||||||
|
|
||||||
|
if (activeProfile == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isTargetActive = iccid.equals(activeProfile.getIccid());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTargetActive)
|
||||||
|
throw new Exception("safeguard_active_profile");
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|||||||
Reference in New Issue
Block a user