Merge "Combine isSatelliteEnabled and isSatelliteBeingEnabled" into main
diff --git a/flags/Android.bp b/flags/Android.bp
index 1885032..edcfc3f 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -33,6 +33,7 @@
"subscription.aconfig",
"uicc.aconfig",
"satellite.aconfig",
- "iwlan.aconfig"
+ "iwlan.aconfig",
+ "carrier.aconfig",
],
}
diff --git a/flags/carrier.aconfig b/flags/carrier.aconfig
new file mode 100644
index 0000000..dca2ad3
--- /dev/null
+++ b/flags/carrier.aconfig
@@ -0,0 +1,11 @@
+package: "com.android.internal.telephony.flags"
+container: "system"
+
+# OWNER=nharold TARGET=24Q4
+flag {
+ name: "async_init_carrier_privileges_tracker"
+ is_exported: true
+ namespace: "telephony"
+ description: "Offload the heavyweight initialization of CarrierPrivilegesTracker to a worker thread"
+ bug:"357096337"
+}
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 0fd094d..ccd5db4 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -132,3 +132,10 @@
}
}
+# OWNER=TBD TARGET=TBD
+flag {
+ name: "oem_paid_private"
+ namespace: "telephony"
+ description: "Support OEM_PAID and OEM_PRIVATE networks"
+ bug: "366194627"
+}
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
index 522cf77..6326d6c 100644
--- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -46,6 +46,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
@@ -68,7 +69,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.telephony.flags.FeatureFlags;
-import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccPort;
import com.android.internal.telephony.uicc.UiccProfile;
import com.android.telephony.Rlog;
@@ -80,7 +80,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -179,6 +178,8 @@
private static final int ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE = 11;
private final Context mContext;
+ @NonNull
+ private final FeatureFlags mFeatureFlags;
private final Phone mPhone;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
@@ -198,7 +199,7 @@
@Nullable private List<UiccAccessRule> mTestOverrideRules = null;
@Nullable private String mTestOverrideCarrierServicePackage = null;
// Map of PackageName -> Certificate hashes for that Package
- @NonNull private final Map<String, Set<String>> mInstalledPackageCerts = new ArrayMap<>();
+ @NonNull private final Map<String, Set<Integer>> mInstalledPackageCertHashes = new ArrayMap<>();
// Map of PackageName -> UIDs for that Package
@NonNull private final Map<String, Set<Integer>> mCachedUids = new ArrayMap<>();
@@ -226,8 +227,7 @@
"mPrivilegedPackageInfoLock.writeLock()"})
private boolean mSimIsReadyButNotLoaded = false;
- @NonNull
- private final FeatureFlags mFeatureFlags;
+ private volatile Handler mCurrentHandler;
/** Small snapshot to hold package names and UIDs of privileged packages. */
private static final class PrivilegedPackageInfo {
@@ -303,7 +303,9 @@
return;
}
- sendMessage(obtainMessage(ACTION_SIM_STATE_UPDATED, slotId, simState));
+ mCurrentHandler.sendMessage(
+ mCurrentHandler.obtainMessage(
+ ACTION_SIM_STATE_UPDATED, slotId, simState));
break;
}
case Intent.ACTION_PACKAGE_ADDED: // fall through
@@ -335,19 +337,21 @@
? ACTION_PACKAGE_REMOVED_OR_DISABLED_BY_USER
: ACTION_PACKAGE_ADDED_REPLACED_OR_CHANGED;
- sendMessage(obtainMessage(what, pkgName));
+ mCurrentHandler.sendMessage(
+ mCurrentHandler.obtainMessage(what, pkgName));
break;
}
}
}
};
- public CarrierPrivilegesTracker(@NonNull Looper looper, @NonNull Phone phone,
- @NonNull Context context, @NonNull FeatureFlags flags) {
+ public CarrierPrivilegesTracker(
+ @NonNull Looper looper, @NonNull Phone phone,
+ @NonNull Context context, @NonNull FeatureFlags featureFlags) {
super(looper);
- mContext = context;
- mFeatureFlags = flags;
mPhone = phone;
+ mContext = context;
+ mFeatureFlags = featureFlags;
mPackageManager = mContext.getPackageManager();
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mCarrierConfigManager =
@@ -363,6 +367,56 @@
(TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (mFeatureFlags.asyncInitCarrierPrivilegesTracker()) {
+ final Object localLock = new Object();
+ HandlerThread initializerThread =
+ new HandlerThread("CarrierPrivilegesTracker Initializer") {
+ @Override
+ protected void onLooperPrepared() {
+ synchronized (localLock) {
+ localLock.notifyAll();
+ }
+ }
+ };
+ synchronized (localLock) {
+ initializerThread.start();
+ while (true) {
+ try {
+ localLock.wait();
+ break;
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+ mCurrentHandler = new Handler(initializerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case ACTION_INITIALIZE_TRACKER:
+ handleInitializeTracker();
+ if (!hasMessagesOrCallbacks()) {
+ mCurrentHandler = CarrierPrivilegesTracker.this;
+ initializerThread.quitSafely();
+ }
+ break;
+ default:
+ Message m = CarrierPrivilegesTracker.this.obtainMessage();
+ m.copyFrom(msg);
+ m.sendToTarget();
+ if (!hasMessagesOrCallbacks()) {
+ mCurrentHandler = CarrierPrivilegesTracker.this;
+ initializerThread.quitSafely();
+ }
+ break;
+ }
+ }
+ };
+ } else {
+ mCurrentHandler = this;
+ }
+
+ mCurrentHandler.sendMessage(obtainMessage(ACTION_INITIALIZE_TRACKER));
+
IntentFilter certFilter = new IntentFilter();
certFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
certFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
@@ -391,7 +445,6 @@
mContext.registerReceiver(mIntentReceiver, packageFilter);
}
- sendMessage(obtainMessage(ACTION_INITIALIZE_TRACKER));
}
@Override
@@ -499,7 +552,7 @@
&& mClearUiccRulesUptimeMillis == CLEAR_UICC_RULE_NOT_SCHEDULED) {
mClearUiccRulesUptimeMillis =
SystemClock.uptimeMillis() + CLEAR_UICC_RULES_DELAY_MILLIS;
- sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES),
+ mCurrentHandler.sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES),
mClearUiccRulesUptimeMillis);
mLocalLog.log("SIM is gone, simState=" + TelephonyManager.simStateToString(simState)
+ ". Delay " + TimeUnit.MILLISECONDS.toSeconds(
@@ -556,25 +609,29 @@
return uiccProfile.getCarrierPrivilegeAccessRules();
}
- private void handlePackageAddedReplacedOrChanged(@Nullable String pkgName) {
- if (pkgName == null) return;
+ private PackageInfo getPackageInfoForPackage(@Nullable String pkgName) {
+ if (pkgName == null) return null;
PackageInfo pkg;
try {
- pkg =
- mFeatureFlags.supportCarrierServicesForHsum()
- ? mPackageManager.getPackageInfoAsUser(
- pkgName,
- INSTALLED_PACKAGES_QUERY_FLAGS,
- ActivityManager.getCurrentUser())
- : mPackageManager.getPackageInfo(
- pkgName, INSTALLED_PACKAGES_QUERY_FLAGS);
+ return mFeatureFlags.supportCarrierServicesForHsum()
+ ? mPackageManager.getPackageInfoAsUser(
+ pkgName,
+ INSTALLED_PACKAGES_QUERY_FLAGS,
+ ActivityManager.getCurrentUser())
+ : mPackageManager.getPackageInfo(
+ pkgName, INSTALLED_PACKAGES_QUERY_FLAGS);
} catch (NameNotFoundException e) {
Rlog.e(TAG, "Error getting installed package: " + pkgName, e);
- return;
+ return null;
}
+ }
- updateCertsForPackage(pkg);
+ private void handlePackageAddedReplacedOrChanged(@Nullable String pkgName) {
+ PackageInfo pkg = getPackageInfoForPackage(pkgName);
+ if (pkg == null) return;
+
+ updateCertHashHashesForPackage(pkg);
// Invalidate cache because this may be a package already on the device but getting
// installed for a user it wasn't installed in before, which means there will be an
// additional UID.
@@ -582,30 +639,46 @@
if (VDBG) {
Rlog.d(TAG, "Package added/replaced/changed:"
+ " pkg=" + Rlog.pii(TAG, pkgName)
- + " cert hashes=" + mInstalledPackageCerts.get(pkgName));
+ + " cert hashes=" + mInstalledPackageCertHashes.get(pkgName));
}
maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
}
- private void updateCertsForPackage(@NonNull PackageInfo pkg) {
- Set<String> certs = new ArraySet<>(1);
+ private void updateCertHashHashesForPackage(@NonNull PackageInfo pkg) {
+ Set<Integer> certs = new ArraySet<>(2);
List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
for (Signature signature : signatures) {
byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1);
- certs.add(IccUtils.bytesToHexString(sha1).toUpperCase(Locale.ROOT));
+ certs.add(UiccAccessRule.getCertificateHashHashCode(sha1));
byte[] sha256 = UiccAccessRule.getCertHash(signature, SHA_256);
- certs.add(IccUtils.bytesToHexString(sha256).toUpperCase(Locale.ROOT));
+ certs.add(UiccAccessRule.getCertificateHashHashCode(sha256));
}
- mInstalledPackageCerts.put(pkg.packageName, certs);
+ mInstalledPackageCertHashes.put(pkg.packageName, certs);
+ }
+
+ private Set<byte[]> getCertsForPackage(@NonNull String pkgName) {
+ PackageInfo pkg = getPackageInfoForPackage(pkgName);
+ if (pkg == null) return Collections.emptySet();
+
+ List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
+
+ ArraySet<byte[]> certs = new ArraySet<>(2);
+ for (Signature signature : signatures) {
+ certs.add(UiccAccessRule.getCertHash(signature, SHA_1));
+ certs.add(UiccAccessRule.getCertHash(signature, SHA_256));
+ }
+
+ return certs;
}
private void handlePackageRemovedOrDisabledByUser(@Nullable String pkgName) {
if (pkgName == null) return;
- if (mInstalledPackageCerts.remove(pkgName) == null || mCachedUids.remove(pkgName) == null) {
+ if (mInstalledPackageCertHashes.remove(pkgName) == null
+ || mCachedUids.remove(pkgName) == null) {
Rlog.e(TAG, "Unknown package was uninstalled or disabled by user: " + pkgName);
return;
}
@@ -637,7 +710,7 @@
msg +=
" installed pkgs="
+ getObfuscatedPackages(
- mInstalledPackageCerts.entrySet(),
+ mInstalledPackageCertHashes.entrySet(),
e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue());
}
mLocalLog.log(msg);
@@ -651,7 +724,7 @@
? ActivityManager.getCurrentUser()
: UserHandle.SYSTEM.getIdentifier());
for (PackageInfo pkg : installedPackages) {
- updateCertsForPackage(pkg);
+ updateCertHashHashesForPackage(pkg);
// This may be unnecessary before initialization, but invalidate the cache all the time
// just in case to ensure consistency.
getUidsForPackage(pkg.packageName, /* invalidateCache= */ true);
@@ -740,8 +813,12 @@
Set<String> carrierServiceEligiblePackages = new ArraySet<>();
Set<String> privilegedPackageNames = new ArraySet<>();
Set<Integer> privilegedUids = new ArraySet<>();
- for (Map.Entry<String, Set<String>> e : mInstalledPackageCerts.entrySet()) {
- final int priv = getPackagePrivilegedStatus(e.getKey(), e.getValue());
+ for (Map.Entry<String, Set<Integer>> e : mInstalledPackageCertHashes.entrySet()) {
+ if (!isPackageMaybePrivileged(e.getKey(), e.getValue())) continue;
+
+ Set<byte[]> fullCerts = getCertsForPackage(e.getKey());
+
+ final int priv = getPackagePrivilegedStatus(e.getKey(), fullCerts);
switch (priv) {
case PACKAGE_PRIVILEGED_FROM_SIM:
case PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE: // fallthrough
@@ -760,32 +837,58 @@
getCarrierService(carrierServiceEligiblePackages));
}
+ private boolean isPackageMaybePrivileged(
+ @NonNull String pkgName, @NonNull Set<Integer> hashHashes) {
+ for (Integer hashHash : hashHashes) {
+ // Non-null (whether empty or not) test override rule will ignore the UICC and CC rules
+ if (mTestOverrideRules != null) {
+ for (UiccAccessRule rule : mTestOverrideRules) {
+ if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
+ return true;
+ }
+ }
+ } else {
+ for (UiccAccessRule rule : mUiccRules) {
+ if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
+ return true;
+ }
+ }
+ for (UiccAccessRule rule : mCarrierConfigRules) {
+ if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Returns the privilege status of the provided package.
*
* <p>Returned privilege status depends on whether a package matches the certificates from
* carrier config, from test overrides or from certificates stored on the SIM.
*/
- private int getPackagePrivilegedStatus(@NonNull String pkgName, @NonNull Set<String> certs) {
+ private int getPackagePrivilegedStatus(@NonNull String pkgName, @NonNull Set<byte[]> certs) {
// Double-nested for loops, but each collection should contain at most 2 elements in nearly
// every case.
// TODO(b/184382310) find a way to speed this up
- for (String cert : certs) {
+ for (byte[] cert : certs) {
// Non-null (whether empty or not) test override rule will ignore the UICC and CC rules
if (mTestOverrideRules != null) {
for (UiccAccessRule rule : mTestOverrideRules) {
- if (rule.matches(cert, pkgName)) {
+ if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
return PACKAGE_PRIVILEGED_FROM_SIM;
}
}
} else {
for (UiccAccessRule rule : mUiccRules) {
- if (rule.matches(cert, pkgName)) {
+ if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
return PACKAGE_PRIVILEGED_FROM_SIM;
}
}
for (UiccAccessRule rule : mCarrierConfigRules) {
- if (rule.matches(cert, pkgName)) {
+ if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
return pkgName.equals(mTestOverrideCarrierServicePackage)
? PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE
: PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG;
@@ -856,7 +959,7 @@
pw.println(
"CarrierPrivilegesTracker - Obfuscated Pkgs + Certs: "
+ getObfuscatedPackages(
- mInstalledPackageCerts.entrySet(),
+ mInstalledPackageCertHashes.entrySet(),
e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue()));
}
pw.println("mClearUiccRulesUptimeMillis: " + mClearUiccRulesUptimeMillis);
@@ -872,7 +975,8 @@
* @see TelephonyManager#setCarrierTestOverride
*/
public void setTestOverrideCarrierPrivilegeRules(@Nullable String carrierPrivilegeRules) {
- sendMessage(obtainMessage(ACTION_SET_TEST_OVERRIDE_RULE, carrierPrivilegeRules));
+ mCurrentHandler.sendMessage(
+ obtainMessage(ACTION_SET_TEST_OVERRIDE_RULE, carrierPrivilegeRules));
}
/**
@@ -888,7 +992,7 @@
* @see TelephonyManager#setCarrierServicePackageOverride
*/
public void setTestOverrideCarrierServicePackage(@Nullable String carrierServicePackage) {
- sendMessage(obtainMessage(
+ mCurrentHandler.sendMessage(obtainMessage(
ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE, carrierServicePackage));
}
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index 55baecc..4f9d84d 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -161,7 +162,11 @@
};
public CarrierServiceBindHelper(Context context) {
- mContext = context.createContextAsUser(Process.myUserHandle(), 0);
+ mContext =
+ context.createContextAsUser(
+ Flags.supportCarrierServicesForHsum()
+ ? UserHandle.of(ActivityManager.getCurrentUser())
+ : Process.myUserHandle(), 0);
updateBindingsAndSimStates();
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 5d59327..2e1a89f 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -391,8 +391,8 @@
mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName())
.makeCarrierResolver(this, featureFlags);
- mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(Looper.myLooper(), this, context,
- featureFlags);
+ mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(
+ Looper.myLooper(), this, context, featureFlags);
getCarrierActionAgent().registerForCarrierAction(
CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, this,
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java
index 20da97f..c88e0b3 100644
--- a/src/java/com/android/internal/telephony/data/DataUtils.java
+++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -287,6 +287,8 @@
case NetworkCapabilities.NET_CAPABILITY_VSIM -> ApnSetting.TYPE_VSIM;
case NetworkCapabilities.NET_CAPABILITY_BIP -> ApnSetting.TYPE_BIP;
case NetworkCapabilities.NET_CAPABILITY_RCS -> ApnSetting.TYPE_RCS;
+ case NetworkCapabilities.NET_CAPABILITY_OEM_PAID -> ApnSetting.TYPE_OEM_PAID;
+ case NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE -> ApnSetting.TYPE_OEM_PRIVATE;
default -> ApnSetting.TYPE_NONE;
};
}
@@ -315,6 +317,8 @@
case ApnSetting.TYPE_VSIM -> NetworkCapabilities.NET_CAPABILITY_VSIM;
case ApnSetting.TYPE_ENTERPRISE -> NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
case ApnSetting.TYPE_RCS -> NetworkCapabilities.NET_CAPABILITY_RCS;
+ case ApnSetting.TYPE_OEM_PAID -> NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
+ case ApnSetting.TYPE_OEM_PRIVATE -> NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
default -> -1;
};
}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index daae3f2..be96c0e 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -2943,6 +2943,21 @@
}
/**
+ * This API can be used by only CTS to control ingoring cellular service state event.
+ *
+ * @param enabled Whether to enable boolean config.
+ * @return {@code true} if the value is set successfully, {@code false} otherwise.
+ */
+ public boolean setSatelliteIgnoreCellularServiceState(boolean enabled) {
+ plogd("setSatelliteIgnoreCellularServiceState - " + enabled);
+ if (mSatelliteSessionController == null) {
+ ploge("setSatelliteIgnoreCellularServiceState is not initialized yet");
+ return false;
+ }
+ return mSatelliteSessionController.setSatelliteIgnoreCellularServiceState(enabled);
+ }
+
+ /**
* This API can be used by only CTS to override timeout durations used by DatagramController
* module.
*
@@ -4838,6 +4853,17 @@
return getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL);
}
+ /**
+ * Return whether the device allows to turn off satellite session for emergency call.
+ *
+ * @param subId Associated subscription ID
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean turnOffSatelliteSessionForEmergencyCall(int subId) {
+ return getConfigForSubId(subId).getBoolean(
+ KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL);
+ }
+
private int getCarrierRoamingNtnConnectType(int subId) {
return getConfigForSubId(subId).getInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT);
}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index cca973f..23dcefc 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -285,7 +285,7 @@
private void handleSatelliteProvisionStateChangedEvent(boolean provisioned) {
if (!provisioned) {
- cleanUpResources();
+ cleanUpResources(false);
}
}
@@ -336,8 +336,7 @@
+ ", isCellularAvailable=" + isCellularAvailable
+ ", isSatelliteAllowed=" + isSatelliteAllowed()
+ ", shouldTrackCall=" + shouldTrackCall(mEmergencyConnection.getState()));
- reportESosRecommenderDecision(isDialerNotified);
- cleanUpResources();
+ cleanUpResources(isDialerNotified);
}
}
@@ -388,13 +387,12 @@
* we're not tracking. There must be some unexpected things happened in
* TelephonyConnectionService. Thus, we need to clean up the resources.
*/
- cleanUpResources();
+ cleanUpResources(false);
return;
}
if (!shouldTrackCall(state)) {
- reportESosRecommenderDecision(false);
- cleanUpResources();
+ cleanUpResources(false);
} else {
// Location service will enter emergency mode only when connection state changes to
// STATE_DIALING
@@ -405,7 +403,8 @@
}
}
- private void reportESosRecommenderDecision(boolean isDialerNotified) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected void reportESosRecommenderDecision(boolean isDialerNotified) {
SatelliteStats.getInstance().onSatelliteSosMessageRecommender(
new SatelliteStats.SatelliteSosMessageRecommenderParams.Builder()
.setDisplaySosMessageSent(isDialerNotified)
@@ -419,8 +418,9 @@
.setCarrierId(getAvailableNtnCarrierID()).build());
}
- private void cleanUpResources() {
+ private void cleanUpResources(boolean isDialerNotified) {
plogd("cleanUpResources");
+ reportESosRecommenderDecision(isDialerNotified);
synchronized (mLock) {
stopTimer();
if (mEmergencyConnection != null) {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index f4208f7..540d106 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -52,6 +52,7 @@
import android.os.SystemProperties;
import android.telephony.DropBoxManagerLoggerBackend;
import android.telephony.PersistentLogger;
+import android.telephony.ServiceState;
import android.telephony.satellite.ISatelliteModemStateCallback;
import android.telephony.satellite.SatelliteManager;
import android.telephony.satellite.stub.ISatelliteGateway;
@@ -66,6 +67,7 @@
import com.android.internal.telephony.ExponentialBackoff;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
import com.android.internal.util.State;
@@ -124,6 +126,8 @@
private static final int EVENT_SCREEN_STATE_CHANGED = 9;
protected static final int EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT = 10;
protected static final int EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT = 11;
+ private static final int EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE = 12;
+ private static final int EVENT_SERVICE_STATE_CHANGED = 13;
private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds
private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute
@@ -160,6 +164,7 @@
private long mSatelliteStayAtListeningFromSendingMillis;
private long mSatelliteStayAtListeningFromReceivingMillis;
private long mSatelliteNbIotInactivityTimeoutMillis;
+ private boolean mIgnoreCellularServiceState = false;
private final ConcurrentHashMap<IBinder, ISatelliteModemStateCallback> mListeners;
@SatelliteManager.SatelliteModemState private int mCurrentState;
@SatelliteManager.SatelliteModemState private int mPreviousState;
@@ -205,8 +210,10 @@
boolean isSatelliteSupported) {
if (sInstance == null || isSatelliteSupported != sInstance.mIsSatelliteSupported) {
ConcurrentHashMap<IBinder, ISatelliteModemStateCallback> existingListeners = null;
+ boolean existIgnoreCellularServiceState = false;
if (sInstance != null) {
existingListeners = sInstance.mListeners;
+ existIgnoreCellularServiceState = sInstance.mIgnoreCellularServiceState;
sInstance.cleanUpResource();
}
@@ -220,6 +227,10 @@
Log.d(TAG, "make() existingListeners: " + existingListeners.size());
sInstance.mListeners.putAll(existingListeners);
}
+ if (existIgnoreCellularServiceState) {
+ Log.d(TAG, "make() existIgnoreCellularServiceState is true");
+ sInstance.mIgnoreCellularServiceState = true;
+ }
}
return sInstance;
}
@@ -277,13 +288,27 @@
bindService();
});
- Phone phone = mSatelliteController.getSatellitePhone();
- if (phone == null) {
- phone = SatelliteServiceUtils.getPhone();
+ Phone satellitePhone = mSatelliteController.getSatellitePhone();
+ if (satellitePhone == null) {
+ satellitePhone = SatelliteServiceUtils.getPhone();
}
- mDeviceStateMonitor = phone.getDeviceStateMonitor();
+ mDeviceStateMonitor = satellitePhone.getDeviceStateMonitor();
mSessionMetricsStats = SessionMetricsStats.getInstance();
+ if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+ // Register to received Cellular service state
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (phone == null) continue;
+
+ phone.registerForServiceStateChanged(
+ getHandler(), EVENT_SERVICE_STATE_CHANGED, null);
+ if (DBG) {
+ plogd("SatelliteSessionController: registerForServiceStateChanged phoneId "
+ + phone.getPhoneId());
+ }
+ }
+ }
+
addState(mUnavailableState);
addState(mPowerOffState);
addState(mEnablingState);
@@ -450,6 +475,23 @@
}
/**
+ * This API can be used by only CTS to control ingoring cellular service state event.
+ *
+ * @param enabled Whether to enable boolean config.
+ * @return {@code true} if the value is set successfully, {@code false} otherwise.
+ */
+ public boolean setSatelliteIgnoreCellularServiceState(boolean enabled) {
+ plogd("setSatelliteIgnoreCellularServiceState : "
+ + "old = " + mIgnoreCellularServiceState + " new : " + enabled);
+ if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+ return false;
+ }
+
+ mIgnoreCellularServiceState = enabled;
+ return true;
+ }
+
+ /**
* This API can be used by only CTS to update satellite gateway service package name.
*
* @param servicePackageName The package name of the satellite gateway service.
@@ -547,9 +589,33 @@
plogd("cleanUpResource");
mIsDeviceAlignedWithSatellite = false;
unregisterForScreenStateChanged();
+
+ if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+ // Register to received Cellular service state
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (phone == null) continue;
+
+ phone.unregisterForServiceStateChanged(getHandler());
+ if (DBG) {
+ plogd("cleanUpResource: unregisterForServiceStateChanged phoneId "
+ + phone.getPhoneId());
+ }
+ }
+ }
+
quitNow();
}
+ /**
+ * Uses this function to notify that cellular service state has changed
+ *
+ * @param serviceState The state of the cellular service.
+ */
+ @VisibleForTesting
+ public void onCellularServiceStateChanged(ServiceState serviceState) {
+ sendMessage(EVENT_SERVICE_STATE_CHANGED, new AsyncResult(null, serviceState, null));
+ }
+
private boolean isDemoMode() {
return mIsDemoMode;
}
@@ -790,8 +856,11 @@
mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_IDLE;
mIsSendingTriggeredDuringTransferringState.set(false);
stopNbIotInactivityTimer();
+
//Enable Cellular Modem scanning
- mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, null);
+ Message onCompleted =
+ obtainMessage(EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE);
+ mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, onCompleted);
notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
}
@@ -821,6 +890,27 @@
case EVENT_SATELLITE_MODEM_STATE_CHANGED:
handleSatelliteModemStateChanged(msg);
break;
+ case EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE:
+ if (!mIgnoreCellularServiceState) {
+ handleEventEnableCellularModemWhileSatelliteModeIsOnDone();
+ } else {
+ plogd("IdleState: processing: ignore "
+ + "EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE");
+ }
+ break;
+ case EVENT_SERVICE_STATE_CHANGED:
+ if (!mIgnoreCellularServiceState) {
+ AsyncResult ar = (msg.obj != null) ? (AsyncResult) msg.obj : null;
+ if (ar == null || ar.result == null) {
+ plogd("IdleState: processing: can't access ServiceState");
+ } else {
+ ServiceState newServiceState = (ServiceState) ar.result;
+ handleEventServiceStateChanged(newServiceState);
+ }
+ } else {
+ plogd("IdleState: processing: ignore EVENT_SERVICE_STATE_CHANGED");
+ }
+ break;
}
// Ignore all unexpected events.
return HANDLED;
@@ -848,6 +938,58 @@
}
}
+ private void handleEventEnableCellularModemWhileSatelliteModeIsOnDone() {
+ if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+ Rlog.d(TAG, "handleEventEnableCellularModemWhileSatelliteModeIsOnDone: "
+ + "carrierRoamingNbIotNtn is disabled");
+ return;
+ }
+
+ ServiceState serviceState = mSatelliteController.getSatellitePhone().getServiceState();
+ if (serviceState == null) {
+ plogd("handleEventEnableCellularModemWhileSatelliteModeIsOnDone: "
+ + "can't access ServiceState");
+ return;
+ }
+ handleEventServiceStateChanged(serviceState);
+ }
+
+ private void handleEventServiceStateChanged(ServiceState serviceState) {
+ boolean isInServiceOrEmergency =
+ serviceState.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
+ || serviceState.getDataRegState() == ServiceState.STATE_IN_SERVICE
+ || serviceState.isEmergencyOnly();
+ if (!isInServiceOrEmergency) {
+ plogd("handleEventServiceStateChanged: is not IN_SERVICE or EMERGENCY_ONLY");
+ return;
+ }
+
+ // In emergency
+ boolean isEmergency = mSatelliteController.getRequestIsEmergency();
+ if (isEmergency) {
+ boolean isEmergencyCommunicationEstablished = (mDatagramController == null)
+ ? false : mDatagramController.isEmergencyCommunicationEstablished();
+ boolean isTurnOffAllowed =
+ mSatelliteController.turnOffSatelliteSessionForEmergencyCall(getSubId());
+ if (isEmergencyCommunicationEstablished || !isTurnOffAllowed) {
+ logd("handleEventServiceStateChanged: "
+ + "can't disable emergency satellite session");
+ return;
+ }
+ }
+
+ mSatelliteController.requestSatelliteEnabled(
+ false /*enableSatellite*/,
+ false /*enableDemoMode*/,
+ isEmergency /*isEmergency*/,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ plogd("requestSatelliteEnabled result=" + result);
+ }
+ });
+ }
+
private void handleEventDisableCellularModemWhileSatelliteModeIsOnDone(
@NonNull AsyncResult result) {
synchronized (mLock) {
@@ -1232,6 +1374,12 @@
case EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
whatString = "EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT";
break;
+ case EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE:
+ whatString = "EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE";
+ break;
+ case EVENT_SERVICE_STATE_CHANGED:
+ whatString = "EVENT_SERVICE_STATE_CHANGED";
+ break;
default:
whatString = "UNKNOWN EVENT " + what;
}
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
index 7bdec47..3bd66f8 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
@@ -133,8 +133,7 @@
UiccCard card, MultipleEnabledProfilesMode supportedMepMode) {
super(c, ci, ics, phoneId, lock, card);
// TODO: Set supportExtendedApdu based on ATR.
- mApduSender = new ApduSender(c, phoneId, ci, ISD_R_AID,
- false /* supportExtendedApdu */);
+ mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */);
if (TextUtils.isEmpty(ics.eid)) {
loge("no eid given in constructor for phone " + phoneId);
} else {
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
index b17a0fd..0a56b2b 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
@@ -17,17 +17,11 @@
package com.android.internal.telephony.uicc.euicc.apdu;
import android.annotation.Nullable;
-import android.content.Context;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.preference.PreferenceManager;
import android.telephony.IccOpenLogicalChannelResponse;
-import android.util.Base64;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.euicc.EuiccSession;
import com.android.internal.telephony.uicc.IccIoResult;
import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
import com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper;
@@ -42,14 +36,7 @@
* before sending and closed after all APDU commands are sent. The complete response of the last
* APDU command will be returned. If any APDU command returns an error status (other than
* {@link #STATUS_NO_ERROR}) or causing an exception, an {@link ApduException} will be returned
- * immediately without sending the rest of commands.
- *
- * <p>If {@link EuiccSession} indicates ongoing session(s), the behavior changes: 1) before
- * sending, check if a channel is opened already. If yes, reuse the channel and send APDU commands
- * directly. If no, open a channel before sending. 2) The channel is closed when EuiccSession
- * class ends all sessions, independent of APDU sending.
- *
- * <p>This class is thread-safe.
+ * immediately without sending the rest of commands. This class is thread-safe.
*
* @hide
*/
@@ -63,12 +50,8 @@
// Status code of APDU response
private static final int STATUS_NO_ERROR = 0x9000;
private static final int SW1_NO_ERROR = 0x91;
- private static final int STATUS_CHANNEL_CLOSED = 0x6881; // b/359336875
private static final int WAIT_TIME_MS = 2000;
- private static final String CHANNEL_ID_PRE = "esim-channel";
- static final String ISD_R_AID = "A0000005591010FFFFFFFF8900000100";
- private static final String CHANNEL_RESPONSE_ID_PRE = "esim-res-id";
private static void logv(String msg) {
Rlog.v(LOG_TAG, msg);
@@ -78,50 +61,26 @@
Rlog.d(LOG_TAG, msg);
}
- private static void loge(String msg) {
- Rlog.e(LOG_TAG, msg);
- }
-
private final String mAid;
private final boolean mSupportExtendedApdu;
private final OpenLogicalChannelInvocation mOpenChannel;
private final CloseLogicalChannelInvocation mCloseChannel;
private final TransmitApduLogicalChannelInvocation mTransmitApdu;
- private final Context mContext;
- private final String mChannelKey;
- private final String mChannelResponseKey;
- // closeAnyOpenChannel() needs a handler for its async callbacks.
- private final Handler mHandler;
- // Lock for accessing mChannelInUse. We only allow to open a single logical
- // channel at any time for an AID and to invoke one command at any time.
- // Only the thread (and its async callbacks) that sets mChannelInUse
- // can open/close/send, and update mChannelOpened.
- private final Object mChannelInUseLock = new Object();
- @GuardedBy("mChannelInUseLock")
- private boolean mChannelInUse;
+ // Lock for accessing mChannelOpened. We only allow to open a single logical channel at any
+ // time for an AID.
+ private final Object mChannelLock = new Object();
private boolean mChannelOpened;
/**
* @param aid The AID that will be used to open a logical channel to.
*/
- public ApduSender(Context context, int phoneId, CommandsInterface ci, String aid,
- boolean supportExtendedApdu) {
- if (!aid.equals(ISD_R_AID) && !"user".equals(Build.TYPE)) {
- throw new IllegalArgumentException("Only ISD-R AID is supported.");
- }
+ public ApduSender(CommandsInterface ci, String aid, boolean supportExtendedApdu) {
mAid = aid;
- mContext = context;
mSupportExtendedApdu = supportExtendedApdu;
mOpenChannel = new OpenLogicalChannelInvocation(ci);
mCloseChannel = new CloseLogicalChannelInvocation(ci);
mTransmitApdu = new TransmitApduLogicalChannelInvocation(ci);
- mChannelKey = CHANNEL_ID_PRE + "_" + phoneId;
- mChannelResponseKey = CHANNEL_RESPONSE_ID_PRE + "_" + phoneId;
- mHandler = new Handler();
-
- mChannelInUse = false;
- closeAnyOpenChannel();
}
/**
@@ -140,125 +99,79 @@
RequestProvider requestProvider,
ApduSenderResultCallback resultCallback,
Handler handler) {
- if (!acquireChannelLock()) {
- AsyncResultHelper.throwException(
- new ApduException("The logical channel is still in use."),
- resultCallback,
- handler);
- return;
- }
-
- boolean euiccSession = EuiccSession.get().hasSession();
- // Case 1, channel was already opened AND EuiccSession is ongoing.
- // sendCommand directly. Do not immediately close channel after sendCommand.
- // Case 2, channel was already opened AND EuiccSession is not ongoing. This means
- // EuiccSession#endSession is already called but closeAnyOpenChannel() is not
- // yet executed because of waiting to acquire lock hold by this thread.
- // sendCommand directly. Close channel immediately anyways after sendCommand.
- // Case 3, channel is not open AND EuiccSession is ongoing. Open channel
- // before sendCommand. Do not immediately close channel after sendCommand.
- // Case 4, channel is not open AND EuiccSession is not ongoing. Open channel
- // before sendCommand. Close channel immediately after sendCommand.
- if (mChannelOpened) { // Case 1 or 2
- if (euiccSession) {
- EuiccSession.get().noteChannelOpen(this);
- }
- RequestBuilder builder = getRequestBuilderWithOpenedChannel(requestProvider,
- !euiccSession /* closeChannelImmediately */, resultCallback, handler);
- if (builder == null) {
- return;
- }
- sendCommand(builder.getCommands(), 0 /* index */,
- !euiccSession /* closeChannelImmediately */, resultCallback, handler);
- } else { // Case 3 or 4
- if (euiccSession) {
- EuiccSession.get().noteChannelOpen(this);
- }
- openChannel(requestProvider,
- !euiccSession /* closeChannelImmediately */, resultCallback, handler);
- }
- }
-
- private RequestBuilder getRequestBuilderWithOpenedChannel(
- RequestProvider requestProvider,
- boolean closeChannelImmediately,
- ApduSenderResultCallback resultCallback,
- Handler handler) {
- Throwable requestException = null;
- int channel =
- PreferenceManager.getDefaultSharedPreferences(mContext)
- .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
- String storedResponse =
- PreferenceManager.getDefaultSharedPreferences(mContext)
- .getString(mChannelResponseKey, "");
- byte[] selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
- RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
- try {
- requestProvider.buildRequest(selectResponse, builder);
- } catch (Throwable e) {
- requestException = e;
- }
- if (builder.getCommands().isEmpty() || requestException != null) {
- logd("Release as commands are empty or exception occurred");
- returnRespnseOrException(channel, closeChannelImmediately,
- null /* response */, requestException, resultCallback, handler);
- return null;
- }
- return builder;
- }
-
- private void openChannel(
- RequestProvider requestProvider,
- boolean closeChannelImmediately,
- ApduSenderResultCallback resultCallback,
- Handler handler) {
- mOpenChannel.invoke(mAid, new AsyncResultCallback<IccOpenLogicalChannelResponse>() {
- @Override
- public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
- int channel = openChannelResponse.getChannel();
- int status = openChannelResponse.getStatus();
- byte[] selectResponse = openChannelResponse.getSelectResponse();
- if (status == IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT) {
- channel = PreferenceManager.getDefaultSharedPreferences(mContext)
- .getInt(mChannelKey,
- IccOpenLogicalChannelResponse.INVALID_CHANNEL);
- if (channel != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
- logv("Try to use already opened channel: " + channel);
- status = IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
- String storedResponse = PreferenceManager
- .getDefaultSharedPreferences(mContext)
- .getString(mChannelResponseKey, "");
- selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
- }
- }
-
- if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
- || status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
- mChannelOpened = false;
- resultCallback.onException(
- new ApduException("Failed to open logical channel for AID: "
- + mAid + ", with status: " + status));
- return;
- }
- PreferenceManager.getDefaultSharedPreferences(mContext)
- .edit()
- .putInt(mChannelKey, channel)
- .putString(mChannelResponseKey,
- Base64.encodeToString(selectResponse, Base64.DEFAULT)).apply();
- mChannelOpened = true;
-
- RequestBuilder builder =
- getRequestBuilderWithOpenedChannel(requestProvider,
- closeChannelImmediately, resultCallback, handler);
- if (builder == null) {
- return;
- }
-
- sendCommand(builder.getCommands(), 0 /* index */,
- closeChannelImmediately, resultCallback, handler);
+ synchronized (mChannelLock) {
+ if (mChannelOpened) {
+ if (!Looper.getMainLooper().equals(Looper.myLooper())) {
+ logd("Logical channel has already been opened. Wait.");
+ try {
+ mChannelLock.wait(WAIT_TIME_MS);
+ } catch (InterruptedException e) {
+ // nothing to do
}
- },
- handler);
+ if (mChannelOpened) {
+ AsyncResultHelper.throwException(
+ new ApduException("The logical channel is still in use."),
+ resultCallback, handler);
+ return;
+ }
+ } else {
+ AsyncResultHelper.throwException(
+ new ApduException("The logical channel is in use."),
+ resultCallback, handler);
+ return;
+ }
+ }
+ mChannelOpened = true;
+ }
+
+ mOpenChannel.invoke(mAid, new AsyncResultCallback<IccOpenLogicalChannelResponse>() {
+ @Override
+ public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
+ int channel = openChannelResponse.getChannel();
+ int status = openChannelResponse.getStatus();
+ if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
+ || status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
+ synchronized (mChannelLock) {
+ mChannelOpened = false;
+ mChannelLock.notify();
+ }
+ resultCallback.onException(
+ new ApduException("Failed to open logical channel opened for AID: "
+ + mAid + ", with status: " + status));
+ return;
+ }
+
+ RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
+ Throwable requestException = null;
+ try {
+ requestProvider.buildRequest(openChannelResponse.getSelectResponse(), builder);
+ } catch (Throwable e) {
+ requestException = e;
+ }
+ if (builder.getCommands().isEmpty() || requestException != null) {
+ // Just close the channel if we don't have commands to send or an error
+ // was encountered.
+ closeAndReturn(channel, null /* response */, requestException, resultCallback,
+ handler);
+ return;
+ }
+ sendCommand(builder.getCommands(), 0 /* index */, resultCallback, handler);
+ }
+ }, handler);
+ }
+
+ /**
+ * Closes any open channel.
+ *
+ * <p>Used by EuiccSession#endSession.
+ */
+ public void closeAnyOpenChannel() {
+ // TODO: implement this. Different from existing closeExistingChannelIfExists()
+ // which is only used in constructor and don't worry about multi-thread racing.
+ // 1. Acquire channel lock
+ // 2. Check sharedpref for existing open channel
+ // 3. Close any open channel
+ // 4. Release channel lock
}
/**
@@ -271,7 +184,6 @@
private void sendCommand(
List<ApduCommand> commands,
int index,
- boolean closeChannelImmediately,
ApduSenderResultCallback resultCallback,
Handler handler) {
ApduCommand command = commands.get(index);
@@ -286,21 +198,9 @@
public void onResult(IccIoResult fullResponse) {
logv("Full APDU response: " + fullResponse);
int status = (fullResponse.sw1 << 8) | fullResponse.sw2;
- if (status != STATUS_NO_ERROR
- && fullResponse.sw1 != SW1_NO_ERROR) {
- if (status == STATUS_CHANNEL_CLOSED) {
- // Channel is closed by EUICC e.g. REFRESH.
- tearDownPreferences();
- mChannelOpened = false;
- // TODO: add retry
- }
- returnRespnseOrException(
- command.channel,
- closeChannelImmediately,
- null /* response */,
- new ApduException(status),
- resultCallback,
- handler);
+ if (status != STATUS_NO_ERROR && fullResponse.sw1 != SW1_NO_ERROR) {
+ closeAndReturn(command.channel, null /* response */,
+ new ApduException(status), resultCallback, handler);
return;
}
@@ -310,17 +210,11 @@
fullResponse);
if (continueSendCommand) {
// Sends the next command
- sendCommand(commands, index + 1,
- closeChannelImmediately, resultCallback, handler);
+ sendCommand(commands, index + 1, resultCallback, handler);
} else {
// Returns the result of the last command
- returnRespnseOrException(
- command.channel,
- closeChannelImmediately,
- fullResponse.payload,
- null /* exception */,
- resultCallback,
- handler);
+ closeAndReturn(command.channel, fullResponse.payload,
+ null /* exception */, resultCallback, handler);
}
}
}, handler);
@@ -343,7 +237,7 @@
AsyncResultCallback<IccIoResult> resultCallback,
Handler handler) {
ByteArrayOutputStream resultBuilder =
- responseBuilder == null ? new ByteArrayOutputStream() : responseBuilder;
+ responseBuilder == null ? new ByteArrayOutputStream() : responseBuilder;
if (lastResponse.payload != null) {
try {
resultBuilder.write(lastResponse.payload);
@@ -369,41 +263,6 @@
}, handler);
}
- private void tearDownPreferences() {
- PreferenceManager.getDefaultSharedPreferences(mContext)
- .edit()
- .remove(mChannelKey)
- .remove(mChannelResponseKey)
- .apply();
- }
-
- /**
- * Fires the {@code resultCallback} to return a response or exception. Also
- * closes the open logical channel if {@code closeChannelImmediately} is {@code true}.
- */
- private void returnRespnseOrException(
- int channel,
- boolean closeChannelImmediately,
- @Nullable byte[] response,
- @Nullable Throwable exception,
- ApduSenderResultCallback resultCallback,
- Handler handler) {
- if (closeChannelImmediately) {
- closeAndReturn(
- channel,
- response,
- exception,
- resultCallback,
- handler);
- } else {
- releaseChannelLockAndReturn(
- response,
- exception,
- resultCallback,
- handler);
- }
- }
-
/**
* Closes the opened logical channel.
*
@@ -421,9 +280,10 @@
mCloseChannel.invoke(channel, new AsyncResultCallback<Boolean>() {
@Override
public void onResult(Boolean aBoolean) {
- tearDownPreferences();
- mChannelOpened = false;
- releaseChannelLock();
+ synchronized (mChannelLock) {
+ mChannelOpened = false;
+ mChannelLock.notify();
+ }
if (exception == null) {
resultCallback.onResult(response);
@@ -433,99 +293,4 @@
}
}, handler);
}
-
- /**
- * Cleanup the existing opened channel which remained opened earlier due
- * to:
- *
- * <p> 1) onging EuiccSession. This will be called by {@link EuiccSession#endSession()}
- * from non-main-thread. Or,
- *
- * <p> 2) telephony crash. This will be called by constructor from main-thread.
- */
- public void closeAnyOpenChannel() {
- if (!acquireChannelLock()) {
- // This cannot happen for case 2) when called by constructor
- loge("[closeAnyOpenChannel] failed to acquire channel lock");
- return;
- }
- int channelId = PreferenceManager.getDefaultSharedPreferences(mContext)
- .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
- if (channelId == IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
- releaseChannelLock();
- return;
- }
- logv("[closeAnyOpenChannel] closing the open channel : " + channelId);
- mCloseChannel.invoke(channelId, new AsyncResultCallback<Boolean>() {
- @Override
- public void onResult(Boolean isSuccess) {
- if (isSuccess) {
- logv("[closeAnyOpenChannel] Channel closed successfully: " + channelId);
- tearDownPreferences();
- }
- // Even if CloseChannel failed, pretend that the channel is closed.
- // So next send() will try open the channel again. If the channel is
- // indeed still open, we use the channelId saved in sharedPref.
- mChannelOpened = false;
- releaseChannelLock();
- }
- }, mHandler);
- }
-
- // releases channel and callback
- private void releaseChannelLockAndReturn(
- @Nullable byte[] response,
- @Nullable Throwable exception,
- ApduSenderResultCallback resultCallback,
- Handler handler) {
- handler.post(
- () -> {
- releaseChannelLock();
- if (exception == null) {
- resultCallback.onResult(response);
- } else {
- resultCallback.onException(exception);
- }
- });
- }
-
- private void releaseChannelLock() {
- synchronized (mChannelInUseLock) {
- logd("Channel lock released.");
- mChannelInUse = false;
- mChannelInUseLock.notify();
- }
- }
-
- /**
- * Acquires channel lock and returns {@code true} if successful.
- *
- * <p>It fails and returns {@code false} when:
- * <ul>
- * <li>Called from main thread, and mChannelInUse=true, fails immediately.
- * <li>Called from non main thread, and mChannelInUse=true after 2 seconds waiting, fails.
- * </ul>
- */
- private boolean acquireChannelLock() {
- synchronized (mChannelInUseLock) {
- if (mChannelInUse) {
- if (!Looper.getMainLooper().equals(Looper.myLooper())) {
- logd("Logical channel is in use. Wait.");
- try {
- mChannelInUseLock.wait(WAIT_TIME_MS);
- } catch (InterruptedException e) {
- // nothing to do
- }
- if (mChannelInUse) {
- return false;
- }
- } else {
- return false;
- }
- }
- mChannelInUse = true;
- logd("Channel lock acquired.");
- return true;
- }
- }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
index a860dff..1c58ef2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
@@ -220,8 +220,9 @@
// Capture CarrierConfigChangeListener to emulate the carrier config change notification
ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
- CarrierPrivilegesTracker cpt = new CarrierPrivilegesTracker(mTestableLooper.getLooper(),
- mPhone, mContext, mFeatureFlags);
+ CarrierPrivilegesTracker cpt =
+ new CarrierPrivilegesTracker(
+ mTestableLooper.getLooper(), mPhone, mContext, mFeatureFlags);
verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
listenerArgumentCaptor.capture());
mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index a645439..fae5e8b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -981,6 +981,8 @@
mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(anyString(), anyInt());
+ doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfoAsUser(
+ anyString(), anyInt(), any(UserHandle.class));
mContextFixture.addCallingOrSelfPermission("");
mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
mContextFixture.addCallingOrSelfPermission(
@@ -1077,6 +1079,8 @@
mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(anyString(), anyInt());
+ doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfoAsUser(
+ anyString(), anyInt(), any(UserHandle.class));
mContextFixture.addCallingOrSelfPermission("");
mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
mContextFixture.addCallingOrSelfPermission(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
index 230a9b1..541cee2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -29,6 +29,9 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -67,6 +70,7 @@
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.metrics.SatelliteStats;
import org.junit.After;
import org.junit.Before;
@@ -118,6 +122,8 @@
private Uri mTestConnectionAddress = Uri.parse("tel:1234");
private TestSOSMessageRecommender mTestSOSMessageRecommender;
private ServiceState mServiceState2;
+ @Mock
+ private SatelliteStats mMockSatelliteStats;
@Before
public void setUp() throws Exception {
@@ -153,6 +159,10 @@
when(mServiceState2.getState()).thenReturn(STATE_OUT_OF_SERVICE);
when(mPhone.isImsRegistered()).thenReturn(false);
when(mPhone2.isImsRegistered()).thenReturn(false);
+ replaceInstance(SatelliteStats.class, "sInstance", null,
+ mMockSatelliteStats);
+ doNothing().when(mMockSatelliteStats).onSatelliteSosMessageRecommender(
+ any(SatelliteStats.SatelliteSosMessageRecommenderParams.class));
}
@After
@@ -165,6 +175,8 @@
testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS,
DEFAULT_T911_HANDOVER_INTENT_ACTION);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -177,6 +189,8 @@
testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS,
"android.com.vendor.message", "android.com.vendor.message.SmsApp",
DEFAULT_HANDOVER_INTENT_ACTION);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -188,6 +202,8 @@
mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS, "", "",
DEFAULT_HANDOVER_INTENT_ACTION);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -197,6 +213,8 @@
mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS, "", "",
DEFAULT_HANDOVER_INTENT_ACTION);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -208,6 +226,7 @@
processAllMessages();
assertFalse(mTestSOSMessageRecommender.isTimerStarted());
assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
}
private void testTimeoutBeforeEmergencyCallEnd(int expectedHandoverType,
@@ -239,6 +258,8 @@
}
assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -266,6 +287,8 @@
assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE));
assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertFalse(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -302,11 +325,15 @@
@Test
public void testStopTrackingCallBeforeTimeout_ConnectionActive() {
testStopTrackingCallBeforeTimeout(Connection.STATE_ACTIVE);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertFalse(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
public void testStopTrackingCallBeforeTimeout_ConnectionDisconnected() {
testStopTrackingCallBeforeTimeout(Connection.STATE_DISCONNECTED);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertFalse(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -337,6 +364,8 @@
assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -357,6 +386,9 @@
assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertFalse(mTestSOSMessageRecommender.isDialerNotified());
+ reset(mMockSatelliteStats);
mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
processAllMessages();
@@ -387,6 +419,8 @@
assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
assertUnregisterForStateChangedEventsTriggered(mPhone, 2, 2);
assertUnregisterForStateChangedEventsTriggered(mPhone2, 2, 2);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -426,30 +460,40 @@
assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
public void testCellularServiceStateChangedBeforeTimeout_InServiceToOutOfService() {
testCellularServiceStateChangedBeforeTimeout(
ServiceState.STATE_IN_SERVICE, STATE_OUT_OF_SERVICE);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
public void testCellularServiceStateChangedBeforeTimeout_InServiceToPowerOff() {
testCellularServiceStateChangedBeforeTimeout(
ServiceState.STATE_IN_SERVICE, ServiceState.STATE_POWER_OFF);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
public void testCellularServiceStateChangedBeforeTimeout_EmergencyOnlyToOutOfService() {
testCellularServiceStateChangedBeforeTimeout(
ServiceState.STATE_EMERGENCY_ONLY, STATE_OUT_OF_SERVICE);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
public void testCellularServiceStateChangedBeforeTimeout_EmergencyOnlyToPowerOff() {
testCellularServiceStateChangedBeforeTimeout(
ServiceState.STATE_EMERGENCY_ONLY, ServiceState.STATE_POWER_OFF);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertTrue(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -471,11 +515,14 @@
assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertFalse(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
public void testSatelliteNotAllowedInCurrentLocation() {
mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
+ mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
processAllMessages();
assertNull(mTestSOSMessageRecommender.isSatelliteAllowedCallback);
@@ -498,6 +545,8 @@
assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 1);
assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+ verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+ assertFalse(mTestSOSMessageRecommender.isDialerNotified());
}
@Test
@@ -513,6 +562,7 @@
assertFalse(testSOSMessageRecommender.isTimerStarted());
assertEquals(0, testSOSMessageRecommender.getCountOfTimerStarted());
+ verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
}
@Test
@@ -548,6 +598,7 @@
processAllMessages();
assertEquals(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS,
mTestSOSMessageRecommender.getTimeOutMillis());
+ verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
// Both OEM and carrier support satellite, but device is not connected to carrier satellite
// within hysteresis time. Thus, OEM timer will be used.
@@ -560,6 +611,7 @@
processAllMessages();
assertEquals(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS,
mTestSOSMessageRecommender.getTimeOutMillis());
+ verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
// Both OEM and carrier support satellite, and device is connected to carrier satellite
// within hysteresis time. Thus, carrier timer will be used.
@@ -570,6 +622,7 @@
mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
processAllMessages();
assertEquals(carrierTimeoutMillis, mTestSOSMessageRecommender.getTimeOutMillis());
+ verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
}
@Test
@@ -581,6 +634,7 @@
mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+ verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
}
@@ -594,6 +648,7 @@
mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+ verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
}
@@ -613,6 +668,7 @@
mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS,
mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+ verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
}
@@ -896,6 +952,7 @@
isSatelliteAllowedCallback = null;
private ComponentName mSmsAppComponent = new ComponentName(
DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS);
+ private boolean mIsDialerNotified;
/**
* Create an instance of SatelliteSOSMessageRecommender.
@@ -924,6 +981,12 @@
isSatelliteAllowedCallback = callback;
}
+ @Override
+ protected void reportESosRecommenderDecision(boolean isDialerNotified) {
+ super.reportESosRecommenderDecision(isDialerNotified);
+ mIsDialerNotified = isDialerNotified;
+ }
+
public boolean isTimerStarted() {
return hasMessages(EVENT_TIME_OUT);
}
@@ -939,6 +1002,10 @@
public long getTimeOutMillis() {
return mTimeoutMillis;
}
+
+ public boolean isDialerNotified() {
+ return mIsDialerNotified;
+ }
}
private static class TestConnection extends Connection {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
index b4af458..a617182 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -49,6 +49,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
+import android.telephony.ServiceState;
import android.telephony.satellite.ISatelliteModemStateCallback;
import android.telephony.satellite.SatelliteManager;
import android.testing.AndroidTestingRunner;
@@ -104,6 +105,7 @@
@Mock private DatagramReceiver mMockDatagramReceiver;
@Mock private DatagramDispatcher mMockDatagramDispatcher;
@Mock private DatagramController mMockDatagramController;
+ @Mock private ServiceState mMockServiceState;
@Captor ArgumentCaptor<Handler> mHandlerCaptor;
@Captor ArgumentCaptor<Integer> mMsgCaptor;
@@ -216,6 +218,7 @@
// Notify Screen off
sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), false);
processAllMessages();
+ clearInvocations(mMockSatelliteController);
// Verify that the screen off inactivity timer is started.
assertTrue(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted());
@@ -283,6 +286,8 @@
doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+ when(mMockSatelliteController.turnOffSatelliteSessionForEmergencyCall(
+ anyInt())).thenReturn(false);
when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
@@ -295,6 +300,7 @@
// Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
assertNotNull(mTestSatelliteSessionController);
+ mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
setupDatagramTransferringState(true);
@@ -328,6 +334,8 @@
doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+ when(mMockSatelliteController.turnOffSatelliteSessionForEmergencyCall(
+ anyInt())).thenReturn(false);
when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true);
when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true);
@@ -338,6 +346,7 @@
// Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
assertNotNull(mTestSatelliteSessionController);
+ mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
setupDatagramTransferringState(true);
@@ -366,6 +375,76 @@
}
@Test
+ public void testDisableSatelliteWhenCellularModemEnabledInIdleMode() {
+ when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+ doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
+ eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
+ when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(false);
+ when(mPhone.getServiceState()).thenReturn(mMockServiceState);
+
+ // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+ assertNotNull(mTestSatelliteSessionController);
+ mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
+ assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+ // Conditions for operation
+ boolean isEmergency = true;
+ // Cellular network is not IN_SERVICE and emergency only.
+ // Satellite request is emergency and emergency communication was established.
+ // Disabling satellite was not allowed
+ when(mMockServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mMockServiceState.getDataRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mMockServiceState.isEmergencyOnly()).thenReturn(false);
+ when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(isEmergency);
+ when(mMockDatagramController.isEmergencyCommunicationEstablished()).thenReturn(true);
+ when(mMockSatelliteController.turnOffSatelliteSessionForEmergencyCall(
+ anyInt())).thenReturn(false);
+
+ moveToIdleState();
+
+ // Cellular network is not in STATE_IN_SERVICE or emergency only.
+ // Should not disable satellite
+ verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+ eq(false), eq(false), eq(isEmergency), any(IIntegerConsumer.Stub.class));
+
+ // Notify cellular service is in STATE_IN_SERVICE.
+ ServiceState serviceState = new ServiceState();
+ serviceState.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+ serviceState.setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
+ serviceState.setEmergencyOnly(false);
+ mTestSatelliteSessionController.onCellularServiceStateChanged(serviceState);
+ processAllMessages();
+
+ // Satellite is in emergency mode and emergency communication was established.
+ // Should not disable satellite
+ verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+ eq(false), eq(false), eq(isEmergency), any(IIntegerConsumer.Stub.class));
+
+ // Satellite is in emergency mode but emergency communication was not established.
+ // Disabling satellite was not allowed
+ when(mMockDatagramController.isEmergencyCommunicationEstablished()).thenReturn(false);
+ when(mMockSatelliteController.turnOffSatelliteSessionForEmergencyCall(
+ anyInt())).thenReturn(false);
+ mTestSatelliteSessionController.onCellularServiceStateChanged(serviceState);
+ processAllMessages();
+
+ // Should not disable satellite
+ verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+ eq(false), eq(false), eq(isEmergency), any(IIntegerConsumer.Stub.class));
+
+ // Satellite is in emergency mode but emergency communication was not established.
+ // Disabling satellite was allowed
+ when(mMockSatelliteController.turnOffSatelliteSessionForEmergencyCall(
+ anyInt())).thenReturn(true);
+ mTestSatelliteSessionController.onCellularServiceStateChanged(serviceState);
+ processAllMessages();
+
+ // Should disable satellite
+ verify(mMockSatelliteController).requestSatelliteEnabled(
+ eq(false), eq(false), eq(isEmergency), any(IIntegerConsumer.Stub.class));
+ }
+
+ @Test
public void testStateTransition() {
/**
* Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
@@ -1419,6 +1498,8 @@
}
private static class TestSatelliteSessionController extends SatelliteSessionController {
+ boolean mSatelliteEnabledForNtnOnlySubscription = true;
+
TestSatelliteSessionController(Context context, Looper looper, FeatureFlags featureFlags,
boolean isSatelliteSupported,
SatelliteModemInterface satelliteModemInterface) {
@@ -1450,7 +1531,11 @@
}
protected boolean isSatelliteEnabledForNtnOnlySubscription() {
- return true;
+ return mSatelliteEnabledForNtnOnlySubscription;
+ }
+
+ void setSatelliteEnabledForNtnOnlySubscription(boolean enabled) {
+ mSatelliteEnabledForNtnOnlySubscription = false;
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
index 172211c..a65814e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
@@ -16,52 +16,36 @@
package com.android.internal.telephony.uicc.euicc.apdu;
-import static com.android.internal.telephony.CommandException.Error.RADIO_NOT_AVAILABLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.Handler;
import android.os.Looper;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.preference.PreferenceManager;
-import android.telephony.IccOpenLogicalChannelResponse;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import androidx.test.InstrumentationRegistry;
-
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.euicc.EuiccSession;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.uicc.IccIoResult;
import com.android.internal.telephony.uicc.IccUtils;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.InOrder;
-import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class ApduSenderTest {
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static class ResponseCaptor extends ApduSenderResultCallback {
public byte[] response;
@@ -91,13 +75,6 @@
}
}
- private static final int PHONE_ID = 0;
- private static final String SESSION_ID = "TEST";
- // keep in sync with ApduSender.mChannelKey
- private static final String SHARED_PREFS_KEY_CHANNEL_ID = "esim-channel_0";
- // keep in sync with ApduSender.mChannelResponseKey
- private static final String SHARED_PREFS_KEY_CHANNEL_RESPONSE = "esim-res-id_0";
-
// Mocked classes
private CommandsInterface mMockCi;
@@ -105,20 +82,19 @@
private Handler mHandler;
private ResponseCaptor mResponseCaptor;
private byte[] mSelectResponse;
+ private static final String AID = "B2C3D4";
private ApduSender mSender;
@Before
public void setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER);
-
mMockCi = mock(CommandsInterface.class);
- mLooper = TestableLooper.get(this);
- mHandler = new Handler(mLooper.getLooper());
+ mHandler = new Handler(Looper.myLooper());
+
mResponseCaptor = new ResponseCaptor();
mSelectResponse = null;
- mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
- mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
+ mSender = new ApduSender(mMockCi, AID, false /* supportExtendedApdu */);
+ mLooper = TestableLooper.get(this);
}
@After
@@ -129,19 +105,6 @@
mResponseCaptor = null;
mSelectResponse = null;
mSender = null;
-
- EuiccSession.get().endSession(SESSION_ID);
- clearSharedPreferences();
- }
-
- @Test
- public void testWrongAid_throwsIllegalArgumentException() {
- String wrongAid = "-1";
-
- assertThrows(IllegalArgumentException.class, () -> {
- new ApduSender(InstrumentationRegistry.getContext(), 0 /* phoneId= */,
- mMockCi, wrongAid, false /* supportExtendedApdu */);
- });
}
@Test
@@ -156,7 +119,7 @@
assertEquals("A1A1A19000", IccUtils.bytesToHexString(mSelectResponse));
assertNull(mResponseCaptor.response);
assertNull(mResponseCaptor.exception);
- verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+ verify(mMockCi).iccOpenLogicalChannel(eq(AID), anyInt(), any());
verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
}
@@ -172,7 +135,7 @@
assertNull("Request provider should not be called when failed to open channel.",
mSelectResponse);
assertTrue(mResponseCaptor.exception instanceof ApduException);
- verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+ verify(mMockCi).iccOpenLogicalChannel(eq(AID), anyInt(), any());
}
@Test
@@ -186,11 +149,8 @@
mLooper.processAllMessages();
assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
- InOrder inOrder = inOrder(mMockCi);
- inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
- inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
- eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
- inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
+ eq(3), eq(0), eq("a"), anyBoolean(), any());
}
@Test
@@ -209,17 +169,14 @@
mLooper.processAllMessages();
assertEquals("A4", IccUtils.bytesToHexString(mResponseCaptor.response));
- InOrder inOrder = inOrder(mMockCi);
- inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
- inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
- eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
- inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
- eq(1), eq(2), eq(3), eq(1), eq("ab"), anyBoolean(), any());
- inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
- eq(1), eq(2), eq(3), eq(0), eq(""), anyBoolean(), any());
- inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81),
- eq(0xE2), eq(0x91), eq(0), eq(2), eq("abcd"), anyBoolean(), any());
- inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
+ eq(3), eq(0), eq("a"), anyBoolean(), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
+ eq(3), eq(1), eq("ab"), anyBoolean(), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
+ eq(3), eq(0), eq(""), anyBoolean(), any());
+ verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
+ eq(0), eq(2), eq("abcd"), anyBoolean(), any());
}
@Test
@@ -382,157 +339,6 @@
assertNull("Should not open channel when another one is already opened.", mSelectResponse);
assertTrue(mResponseCaptor.exception instanceof ApduException);
- verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
- }
-
- @Test
- public void testConstructor_closeOpenChannelInSharedPreference() throws InterruptedException {
- // Open a channel and not close it, by making CI.iccTransmitApduLogicalChannel throw.
- int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
- doThrow(new RuntimeException()).when(mMockCi).iccTransmitApduLogicalChannel(
- eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
- anyBoolean(), any());
- mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
- 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
- mLooper.processAllMessages();
- // Stub close channel
- reset(mMockCi);
- LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
-
- // Call constructor
- mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
- mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
- mLooper.processAllMessages();
-
- // The constructor should have closed channel
- verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
- assertEquals(-1, getChannelIdFromSharedPreferences());
- }
-
- @Test
- public void testSend_OpenChannelFailedNoSuchElement_useChannelInSharedPreference() {
- // Open a channel but not close, by making CI.iccTransmitApduLogicalChannel throw.
- int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
- doThrow(new RuntimeException()).when(mMockCi).iccTransmitApduLogicalChannel(
- eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
- anyBoolean(), any());
- mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
- 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
- mLooper.processAllMessages();
- reset(mMockCi);
- // Constructor fails to close channel
- LogicalChannelMocker.mockCloseLogicalChannel(
- mMockCi, channel, new CommandException(RADIO_NOT_AVAILABLE));
- mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
- mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
- mLooper.processAllMessages();
- reset(mMockCi);
- // Stub open channel failure NO_SUCH_ELEMENT
- LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
- new CommandException(CommandException.Error.NO_SUCH_ELEMENT));
- LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000");
- LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
-
- mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
- 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
- mLooper.processAllMessages();
-
- // open channel would fail, and send/close would succeed because of
- // previous open response saved in sharedPref
- InOrder inOrder = inOrder(mMockCi);
- inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
- inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel),
- eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
- inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testSend_euiccSession_shouldNotCloseChannel()
- throws InterruptedException {
- int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
- LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000");
- LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
- EuiccSession.get().startSession(SESSION_ID);
-
- mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
- 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
- mLooper.processAllMessages();
-
- assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
- InOrder inOrder = inOrder(mMockCi);
- inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
- inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
- eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
- // No iccCloseLogicalChannel
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testSendTwice_euiccSession_shouldOpenChannelOnceNotCloseChannel()
- throws InterruptedException {
- int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
- LogicalChannelMocker.mockSendToLogicalChannel(
- mMockCi, channel, "A1A1A19000", "A1A1A19000");
- LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
- EuiccSession.get().startSession(SESSION_ID);
-
- mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
- 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
- mLooper.processAllMessages();
- mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
- 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
- mLooper.processAllMessages();
-
- assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
- InOrder inOrder = inOrder(mMockCi);
- // iccOpenLogicalChannel once
- inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
- // iccTransmitApduLogicalChannel twice
- inOrder.verify(mMockCi, times(2)).iccTransmitApduLogicalChannel(eq(channel),
- eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
- // No iccCloseLogicalChannel
- inOrder.verifyNoMoreInteractions();
- }
-
- @Test
- public void testSendTwice_thenEndSession() throws InterruptedException {
- int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
- LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel,
- "A1A1A19000", "A1A1A19000");
- LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
- EuiccSession.get().startSession(SESSION_ID);
-
- mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
- 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
- mLooper.processAllMessages();
- mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
- 10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
- mLooper.processAllMessages();
- EuiccSession.get().endSession(SESSION_ID);
- mLooper.processAllMessages();
-
- assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
- InOrder inOrder = inOrder(mMockCi);
- // iccOpenLogicalChannel once
- inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
- // iccTransmitApduLogicalChannel twice
- inOrder.verify(mMockCi, times(2)).iccTransmitApduLogicalChannel(eq(channel),
- eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
- // iccCloseLogicalChannel once
- inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
- }
-
- private int getChannelIdFromSharedPreferences() {
- return PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getContext())
- .getInt(SHARED_PREFS_KEY_CHANNEL_ID, -1);
- }
-
- private void clearSharedPreferences() {
- PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getContext())
- .edit()
- .remove(SHARED_PREFS_KEY_CHANNEL_ID)
- .remove(SHARED_PREFS_KEY_CHANNEL_RESPONSE)
- .apply();
+ verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(AID), anyInt(), any());
}
}