Merge 25Q1 (ab/12770256) to aosp-main-future Bug: 385190204 Merged-In: I1c5acde9514207aba7cabceeaca9ccbd8c7f461a Change-Id: I6a1d965a56f4f370c3dc9971999fe34a32219e75
diff --git a/Android.bp b/Android.bp index da32208..a6e526c 100644 --- a/Android.bp +++ b/Android.bp
@@ -82,14 +82,14 @@ "android.hardware.radio-V1.4-java", "android.hardware.radio-V1.5-java", "android.hardware.radio-V1.6-java", - "android.hardware.radio.config-V3-java", - "android.hardware.radio.data-V3-java", - "android.hardware.radio.ims-V2-java", - "android.hardware.radio.messaging-V3-java", - "android.hardware.radio.modem-V3-java", - "android.hardware.radio.network-V3-java", - "android.hardware.radio.sim-V3-java", - "android.hardware.radio.voice-V3-java", + "android.hardware.radio.config-V4-java", + "android.hardware.radio.data-V4-java", + "android.hardware.radio.ims-V3-java", + "android.hardware.radio.messaging-V4-java", + "android.hardware.radio.modem-V4-java", + "android.hardware.radio.network-V4-java", + "android.hardware.radio.sim-V4-java", + "android.hardware.radio.voice-V4-java", "voip-common", "ims-common", "unsupportedappusage",
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/calling.aconfig b/flags/calling.aconfig index a5757d8..27d3518 100644 --- a/flags/calling.aconfig +++ b/flags/calling.aconfig
@@ -62,3 +62,23 @@ purpose: PURPOSE_BUGFIX } } + +# OWNER=yongnamcha TARGET=25Q2 +flag { + name: "emergency_callback_mode_notification" + namespace: "telephony" + description: "Used to notify the emergency callback mode for call/SMS to other applications." + bug:"359064059" + is_exported: true +} + +# OWNER=sewookseo TARGET=25Q2 +flag { + name: "pass_copied_call_state_list" + namespace: "telephony" + description: "To prevent passing the TelephonyRegistry's original instance to listeners in the same process" + bug:"379126049" + metadata { + purpose: PURPOSE_BUGFIX + } +} \ No newline at end of file
diff --git a/flags/carrier.aconfig b/flags/carrier.aconfig new file mode 100644 index 0000000..14aedc6 --- /dev/null +++ b/flags/carrier.aconfig
@@ -0,0 +1,32 @@ +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" +} + +# OWNER=melhuishj TARGET=25Q1 +flag { + name: "cleanup_carrier_app_update_enabled_state_logic" + is_exported: true + namespace: "telephony" + description: "Improve readability of update state logic" + bug:"232141900" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +# OWNER=melhuishj TARGET=25Q2 +flag { + name: "temporary_failures_in_carrier_messaging_service" + is_exported: true + namespace: "telephony" + description: "Enable temporary failures in CarrierMessagingService" + bug:"326610112" +}
diff --git a/flags/data.aconfig b/flags/data.aconfig index 0fd094d..4a90c10 100644 --- a/flags/data.aconfig +++ b/flags/data.aconfig
@@ -121,14 +121,11 @@ } } -# OWNER=jackyu TARGET=25Q1 +# OWNER=jackyu TARGET=25Q2 flag { - name: "sim_disabled_graceful_tear_down" + name: "oem_paid_private" namespace: "telephony" - description: "Gracefully tear down the networks when SIM is disabled." - bug: "362372940" - metadata { - purpose: PURPOSE_BUGFIX - } + description: "Support OEM_PAID and OEM_PRIVATE networks" + bug: "366194627" + is_exported: true } -
diff --git a/flags/ims.aconfig b/flags/ims.aconfig index d2401fe..703440f 100644 --- a/flags/ims.aconfig +++ b/flags/ims.aconfig
@@ -137,3 +137,32 @@ purpose: PURPOSE_BUGFIX } } + +# OWNER=breadley TARGET=25Q1 +flag { + name: "ims_resolver_user_aware" + namespace: "telephony" + description: "When enabled, it makes ImsResolver mult-user aware for configurations like HSUM." + bug:"371272669" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +# OWNER=meghapatil TARGET=25Q2 +flag { + name: "support_sms_over_ims_apis" + namespace: "telephony" + description: "Used to expose SMS related hidden APIs for SMS over IMS to public API." + bug:"359721349" + is_exported: true +} + +# OWNER=jhyoon TARGET=25Q2 +flag { + name: "support_ims_mmtel_interface" + namespace: "telephony" + description: "This flag controls the type of API regarding MmTelFeature, either hidden or system type." + bug:"359721349" + is_exported: true +}
diff --git a/flags/misc.aconfig b/flags/misc.aconfig index 860c864..ec7b8fa 100644 --- a/flags/misc.aconfig +++ b/flags/misc.aconfig
@@ -80,15 +80,6 @@ # OWNER=rambowang TARGET=24Q3 flag { - name: "show_call_id_and_call_waiting_in_additional_settings_menu" - is_exported: true - namespace: "telephony" - description: "Expose carrier config KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL and KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL." - bug: "310264981" -} - -# OWNER=rambowang TARGET=24Q3 -flag { name: "reset_mobile_network_settings" is_exported: true namespace: "telephony" @@ -229,3 +220,29 @@ } } +# OWNER=jackyu TARGET=25Q2 +flag { + name: "power_down_race_fix" + namespace: "telephony" + description: "Fixed race condition while powering down" + bug:"378616116" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +# OWNER=jackyu TARGET=25Q2 +flag { + name: "deprecate_cdma" + namespace: "telephony" + description: "Deprecate CDMA and NV APIS" + bug: "379356026" +} + +# OWNER=jackyu TARGET=25Q2 +flag { + name: "cleanup_cdma" + namespace: "telephony" + description: "Disable CDMA and NV backing code" + bug: "379356026" +}
diff --git a/flags/network.aconfig b/flags/network.aconfig index 7c09ba3..be599ea 100644 --- a/flags/network.aconfig +++ b/flags/network.aconfig
@@ -93,3 +93,20 @@ } } +# OWNER=yomna TARGET=25Q2 +flag { + name: "security_algorithms_update_indications" + is_exported: true + namespace: "telephony" + description: "guard system API onSecurityAlgorithmsChanged" + bug: "355062720" +} + +# OWNER=yomna TARGET=25Q2 +flag { + name: "cellular_identifier_disclosure_indications" + is_exported: true + namespace: "telephony" + description: "guard system API onCellularIdentifierDisclosedChanged" + bug: "355062720" +}
diff --git a/flags/satellite.aconfig b/flags/satellite.aconfig index 4806789..ee4570e 100644 --- a/flags/satellite.aconfig +++ b/flags/satellite.aconfig
@@ -41,6 +41,7 @@ namespace: "telephony" description: "This flag enables satellite carrier roaming to nb iot ntn." bug:"348253735" + is_exported: true } # OWNER=tnd TARGET=24Q4 @@ -61,4 +62,22 @@ metadata { purpose: PURPOSE_BUGFIX } -} \ No newline at end of file +} + +# OWNER=amallampati TARGET=25Q2 +flag { + name: "satellite_system_apis" + is_exported: true + namespace: "telephony" + description: "Convert hidden SatelliteManager APIs to system APIs." + bug:"373436320" +} + +# OWNER=rambowang TARGET=25Q2 +flag { + name: "satellite_state_change_listener" + namespace: "telephony" + description: "Introduce SatelliteManager APIs for carrier apps to monitor satellite state change" + bug: "357638490" + is_exported: true +}
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig index 87c7408..0522a3c 100644 --- a/flags/subscription.aconfig +++ b/flags/subscription.aconfig
@@ -58,14 +58,6 @@ } } -# OWNER=jmattis TARGET=25Q2 -flag { - name: "subscription_plan_allow_status_and_end_date" - namespace: "telephony" - description: "Provide APIs to retrieve the status and recurrence rule info on a subscription plan" - bug: "357272015" -} - # OWNER=songferngwang TARGET=24Q3 flag { name: "reset_primary_sim_default_values" @@ -89,3 +81,12 @@ purpose: PURPOSE_BUGFIX } } + +# OWNER=jmattis TARGET=25Q2 +flag { + name: "subscription_plan_allow_status_and_end_date" + namespace: "telephony" + description: "Provide APIs to retrieve the status and recurrence rule info on a subscription plan" + bug: "357272015" + is_exported: true +}
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig index 14341d9..8561dac 100644 --- a/flags/uicc.aconfig +++ b/flags/uicc.aconfig
@@ -49,6 +49,7 @@ namespace: "telephony" description: "This flag controls the visibility of the setCarrierRestrictionStatus API in carrierRestrictionRules class." bug:"342411308" + is_exported: true } # OWNER=arunvoddu TARGET=24Q4 @@ -69,3 +70,59 @@ description: "This flag controls optimization of apdu sender class." bug:"335257880" } + +# OWNER=arunvoddu TARGET=24Q4 +flag { + name: "ignore_carrierid_reset_for_sim_removal" + namespace: "telephony" + description: "This flag controls the carrierId reset while imsi key deletion time upon sim ejection." + bug:"366178705" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +# OWNER=jhyoon TARGET=25Q2 +flag { + name: "support_isim_record" + namespace: "telephony" + description: "This flag controls the type of API that retrieves ISIM records, either hidden or system type." + bug:"359721349" + is_exported: true +} + +# OWNER=jinjeong TARGET=25Q2 +flag { + name: "carrier_id_from_carrier_identifier" + namespace: "telephony" + description: "This flag controls to get a carrier id using a carrier identifier." + bug:"378778278" + is_exported: true +} + +# OWNER=arunvoddu TARGET=25Q2 +flag { + name: "force_imsi_certificate_delete" + namespace: "telephony" + description: "This flag controls the IMSI certificate delete with out any condition." + bug:"235296888" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +# OWNER=jinjeong TARGET=25Q2 +flag { + name: "get_group_id_level2" + namespace: "telephony" + description: "This flag controls to get a group id level2." + bug:"381171540" +} + +# OWNER=jinjeong TARGET=25Q2 +flag { + name: "action_sim_preference_settings" + namespace: "telephony" + description: "This flag controls to launch sim preference page in Setting" + bug:"381319469" +}
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java index 8364c0a..206770d 100644 --- a/src/java/com/android/internal/telephony/CarrierInfoManager.java +++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -273,21 +273,25 @@ /** * Resets the Carrier Keys in the database. This involves 2 steps: - * 1. Delete the keys from the database. - * 2. Send an intent to download new Certificates. - * @param context Context - * @param mPhoneId phoneId + * 1. Delete the keys from the database. + * 2. Send an intent to download new Certificates. * + * @param context Context + * @param mPhoneId phoneId + * @param forceResetAll to skip the check of the RESET_CARRIER_KEY_RATE_LIMIT. */ - public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId) { - Log.i(LOG_TAG, "resetting carrier key"); + public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId, + boolean forceResetAll) { + Log.i(LOG_TAG, "resetting carrier key, forceResetAll = " +forceResetAll); // Check rate limit. long now = System.currentTimeMillis(); - if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) { - Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded"); - return; + if (!forceResetAll) { + if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) { + Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded"); + return; + } + mLastAccessResetCarrierKey = now; } - mLastAccessResetCarrierKey = now; int subId = SubscriptionManager.getSubscriptionId(mPhoneId); if (!SubscriptionManager.isValidSubscriptionId(subId)) {
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java index 6f84521..1667b7d 100644 --- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java +++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -146,9 +146,9 @@ .createForSubscriptionId(mPhone.getSubId()); if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { mKeyguardManager = mContext.getSystemService(KeyguardManager.class); - } else { - mUserManager = mContext.getSystemService(UserManager.class); } + mUserManager = mContext.getSystemService(UserManager.class); + CarrierConfigManager carrierConfigManager = mContext.getSystemService( CarrierConfigManager.class); // Callback which directly handle config change should be executed on handler thread @@ -170,17 +170,29 @@ TelephonyManager.class) .createForSubscriptionId(subId); } - mCarrierId = carrierId; + if (Flags.ignoreCarrieridResetForSimRemoval()) { + if (carrierId > 0) { + mCarrierId = carrierId; + } + } else { + mCarrierId = carrierId; + } updateSimOperator(); // If device is screen locked do not proceed to handle // EVENT_ALARM_OR_CONFIG_CHANGE - if (mKeyguardManager.isDeviceLocked()) { - logd("Device is Locked"); + printDeviceLockStatus(); + if (Flags.ignoreCarrieridResetForSimRemoval()) { + if (!mUserManager.isUserUnlocked()) { + mIsRequiredToHandleUnlock = true; + return; + } + } else if (mKeyguardManager.isDeviceLocked()) { mIsRequiredToHandleUnlock = true; - } else { - logd("Carrier Config changed: slotIndex=" + slotIndex); - sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); + return; } + logd("Carrier Config changed: slotIndex=" + slotIndex); + sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); + } } else { boolean isUserUnlocked = mUserManager.isUserUnlocked(); @@ -199,6 +211,11 @@ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); } + private void printDeviceLockStatus() { + logd(" Device Status: isDeviceLocked = " + mKeyguardManager.isDeviceLocked() + + " iss User unlocked = " + mUserManager.isUserUnlocked()); + } + // TODO remove this method upon imsiKeyRetryDownloadOnPhoneUnlock enabled. private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() { @Override @@ -307,11 +324,16 @@ if (downloadStartedSuccessfully) { unregisterDefaultNetworkCb(slotIndex); } else { - // If download fails due to the device lock, we will reattempt once the - // device is unlocked. - mIsRequiredToHandleUnlock = mKeyguardManager.isDeviceLocked(); + // If download fails due to the device user lock, we will reattempt once + // the device is unlocked. + if (Flags.ignoreCarrieridResetForSimRemoval()) { + mIsRequiredToHandleUnlock = !mUserManager.isUserUnlocked(); + } else { + mIsRequiredToHandleUnlock = mKeyguardManager.isDeviceLocked(); + } + loge("hasActiveDataConnection = " + hasActiveDataNetwork - + " isDeviceLocked = " + mIsRequiredToHandleUnlock); + + " isDeviceUserLocked = " + mIsRequiredToHandleUnlock); if (!hasActiveDataNetwork) { registerDefaultNetworkCb(slotIndex); } @@ -538,14 +560,19 @@ carrierKeyDownloadIdentifier); } parseJsonAndPersistKey(jsonStr, mccMnc, carrierId); + logd("Completed downloading keys"); } catch (Exception e) { loge( "Error in download:" + carrierKeyDownloadIdentifier + ". " + e); } finally { mDownloadManager.remove(carrierKeyDownloadIdentifier); } + } else { + loge("Download Failed reason = " + cursor.getInt(columnIndex) + + "Failed Status reason" + cursor.getInt( + cursor.getColumnIndex(DownloadManager.COLUMN_REASON))); + printDeviceLockStatus(); } - logd("Completed downloading keys"); } cursor.close(); } @@ -796,6 +823,7 @@ } catch (Exception e) { loge( "exception trying to download key from url: " + mURL + ", Exception = " + e.getMessage()); + printDeviceLockStatus(); return false; } return true;
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/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java index d47c2c4..b470e2e 100644 --- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java +++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -75,7 +75,8 @@ @VisibleForTesting - public static final String ACTION_NEVER_ASK_AGAIN = "SilenceNoWifiEmrgCallingNotification"; + public static final String ACTION_NEVER_ASK_AGAIN = + "com.android.internal.telephony.action.SILENCE_WIFI_CALLING_NOTIFICATION"; public final NotificationActionReceiver mActionReceiver = new NotificationActionReceiver(); @VisibleForTesting @@ -733,6 +734,7 @@ public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ACTION_NEVER_ASK_AGAIN)) { Rlog.i(LOG_TAG, "NotificationActionReceiver: ACTION_NEVER_ASK_AGAIN"); + dismissEmergencyCallingNotification(); // insert a key to silence future notifications SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); @@ -743,5 +745,22 @@ context.unregisterReceiver(mActionReceiver); } } + + /** + * Dismiss the notification when the "Do Not Ask Again" button is clicked + */ + private void dismissEmergencyCallingNotification() { + if (!mFeatureFlags.stopSpammingEmergencyNotification()) { + return; + } + try { + NotificationType t = mNotificationTypeMap.get(NOTIFICATION_EMERGENCY_NETWORK); + if (t != null) { + cancelNotification(t); + } + } catch (Exception e) { + Rlog.e(LOG_TAG, "dismissEmergencyCallingNotification", e); + } + } } }
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java index ee7447c..1b19c99 100644 --- a/src/java/com/android/internal/telephony/CommandsInterface.java +++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -2952,4 +2952,39 @@ * @param h Handler to be removed from the registrant list. */ default void unregisterForSecurityAlgorithmUpdates(Handler h) {} + + /** + * Set the non-terrestrial PLMN with lower priority than terrestrial networks. + * + * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use + * this information to determine the relevant carrier. + * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks + * supported by user subscription. + * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite + * PLMNs that are not supported by the carrier and make sure not to + * attach to them. + * @param result Callback message to receive the result. + */ + default void setSatellitePlmn(int simSlot, + @NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList, + Message result) {} + + /** + * Enable or disable satellite in the cellular modem associated with a carrier. + * + * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use + * this information to determine the relevant carrier. + * @param satelliteEnabled {@code true} to enable satellite, {@code false} to disable satellite. + * @param result Callback message to receive the result. + */ + default void setSatelliteEnabledForCarrier(int simSlot, boolean satelliteEnabled, + Message result) {} + + /** + * Check whether satellite is enabled in the cellular modem associated with a carrier. + * + * @param simSlot Indicates the SIM slot to which this API will be applied. + * @param result Callback message to receive the result. + */ + default void isSatelliteEnabledForCarrier(int simSlot, Message result) {} }
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java index da17f60..dc05608 100644 --- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -25,11 +25,14 @@ import android.telephony.CallQuality; import android.telephony.CellIdentity; import android.telephony.CellInfo; +import android.telephony.CellularIdentifierDisclosure; import android.telephony.LinkCapacityEstimate; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; +import android.telephony.SecurityAlgorithmUpdate; import android.telephony.ServiceState; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager.DataEnabledReason; @@ -40,6 +43,7 @@ import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.MediaQualityStatus; +import android.telephony.satellite.NtnSignalStrength; import com.android.internal.telephony.flags.FeatureFlags; import com.android.telephony.Rlog; @@ -303,14 +307,28 @@ } @Override - public void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type) { - mTelephonyRegistryMgr.notifyCallBackModeStarted(sender.getPhoneId(), - sender.getSubId(), type); + public void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type, + long durationMillis) { + if (!mFeatureFlags.emergencyCallbackModeNotification()) return; + + mTelephonyRegistryMgr.notifyCallbackModeStarted(sender.getPhoneId(), + sender.getSubId(), type, durationMillis); + } + + @Override + public void notifyCallbackModeRestarted(Phone sender, @EmergencyCallbackModeType int type, + long durationMillis) { + if (!mFeatureFlags.emergencyCallbackModeNotification()) return; + + mTelephonyRegistryMgr.notifyCallbackModeRestarted(sender.getPhoneId(), + sender.getSubId(), type, durationMillis); } @Override public void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type, @EmergencyCallbackModeStopReason int reason) { + if (!mFeatureFlags.emergencyCallbackModeNotification()) return; + mTelephonyRegistryMgr.notifyCallbackModeStopped(sender.getPhoneId(), sender.getSubId(), type, reason); } @@ -326,6 +344,37 @@ sender.getSubId(), eligible); } + @Override + public void notifyCarrierRoamingNtnAvailableServicesChanged( + Phone sender, @NetworkRegistrationInfo.ServiceType int[] availableServices) { + mTelephonyRegistryMgr.notifyCarrierRoamingNtnAvailableServicesChanged( + sender.getSubId(), availableServices); + } + + @Override + public void notifyCarrierRoamingNtnSignalStrengthChanged(Phone sender, + @NonNull NtnSignalStrength ntnSignalStrength) { + mTelephonyRegistryMgr.notifyCarrierRoamingNtnSignalStrengthChanged( + sender.getSubId(), ntnSignalStrength); + } + + @Override + public void notifySecurityAlgorithmsChanged(Phone sender, SecurityAlgorithmUpdate update) { + if (!mFeatureFlags.securityAlgorithmsUpdateIndications()) return; + + mTelephonyRegistryMgr.notifySecurityAlgorithmsChanged(sender.getPhoneId(), + sender.getSubId(), update); + } + + @Override + public void notifyCellularIdentifierDisclosedChanged(Phone sender, + CellularIdentifierDisclosure disclosure) { + if (!mFeatureFlags.cellularIdentifierDisclosureIndications()) return; + + mTelephonyRegistryMgr.notifyCellularIdentifierDisclosedChanged(sender.getPhoneId(), + sender.getSubId(), disclosure); + } + /** * Convert the {@link Call.State} enum into the PreciseCallState.PRECISE_CALL_STATE_* constants * for the public API.
diff --git a/src/java/com/android/internal/telephony/DisplayInfoController.java b/src/java/com/android/internal/telephony/DisplayInfoController.java index d3f0264..bb19a31 100644 --- a/src/java/com/android/internal/telephony/DisplayInfoController.java +++ b/src/java/com/android/internal/telephony/DisplayInfoController.java
@@ -110,7 +110,7 @@ mTelephonyDisplayInfo = new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_UNKNOWN, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, - false); + false, false, false); mNetworkTypeController = new NetworkTypeController(phone, this, featureFlags); // EVENT_UPDATE will transition from DefaultState to the current state // and update the TelephonyDisplayInfo based on the current state. @@ -132,7 +132,9 @@ TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo( mNetworkTypeController.getDataNetworkType(), mNetworkTypeController.getOverrideNetworkType(), - isRoaming()); + isRoaming(), + mPhone.getServiceStateTracker().getServiceState().isUsingNonTerrestrialNetwork(), + mNetworkTypeController.getSatelliteConstrainedData()); if (!newDisplayInfo.equals(mTelephonyDisplayInfo)) { logl("TelephonyDisplayInfo changed from " + mTelephonyDisplayInfo + " to " + newDisplayInfo);
diff --git a/src/java/com/android/internal/telephony/GbaManager.java b/src/java/com/android/internal/telephony/GbaManager.java index 7c5f636..047d5d5 100644 --- a/src/java/com/android/internal/telephony/GbaManager.java +++ b/src/java/com/android/internal/telephony/GbaManager.java
@@ -16,6 +16,7 @@ package com.android.internal.telephony; +import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -27,6 +28,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.UserHandle; import android.telephony.IBootstrapAuthenticationCallback; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -75,7 +77,8 @@ private Handler mHandler; private String mServicePackageName; - private String mServicePackageNameOverride; + @UserIdInt + private int mUserId = UserHandle.USER_SYSTEM; private int mReleaseTime; private int mRetryTimes = 0; @@ -426,8 +429,9 @@ try { logv("Trying to bind " + servicePackage); mServiceConnection = new GbaServiceConnection(); - if (!mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE)) { + if (!mContext.bindServiceAsUser(intent, mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + UserHandle.of(mUserId))) { logd("Cannot bind to the service."); retryBind(); return; @@ -462,12 +466,13 @@ } /** override GBA service package name to be connected */ - public boolean overrideServicePackage(String packageName) { + public boolean overrideServicePackage(String packageName, @UserIdInt int userId) { synchronized (this) { - if (!TextUtils.equals(mServicePackageName, packageName)) { + if (!TextUtils.equals(mServicePackageName, packageName) || userId != mUserId) { logv("Service package name is changed from " + mServicePackageName - + " to " + packageName); + + " to " + packageName + ", user id from " + mUserId + " to " + userId); mServicePackageName = packageName; + mUserId = userId; if (!mHandler.hasMessages(EVENT_CONFIG_CHANGED)) { mHandler.sendEmptyMessage(EVENT_CONFIG_CHANGED); }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java index 26d4e1b..14bd273 100644 --- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java +++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -509,7 +509,8 @@ obtainCompleteMessage()); } }; - EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(onComplete); + EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(onComplete, + TelephonyManager.STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED); } else { mPhone.exitEmergencyCallbackMode(); mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java index 5d59327..f190a43 100644 --- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java +++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -42,7 +42,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.database.SQLException; import android.hardware.radio.modem.ImeiInfo; import android.net.Uri; @@ -391,8 +390,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, @@ -470,12 +469,6 @@ } }; - private boolean hasCalling() { - if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true; - return mContext.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_TELEPHONY_CALLING); - } - private void initOnce(CommandsInterface ci) { if (ci instanceof SimulatedRadioControl) { mSimulatedRadioControl = (SimulatedRadioControl) ci; @@ -782,6 +775,7 @@ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @Override public int getPhoneType() { + if (mFeatureFlags.cleanupCdma()) return PhoneConstants.PHONE_TYPE_GSM; if (mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM) { return PhoneConstants.PHONE_TYPE_GSM; } else { @@ -1900,8 +1894,15 @@ @Override public void setRadioPowerForReason(boolean power, boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply, int reason) { - mSST.setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, - forceApply, reason); + if (mFeatureFlags.powerDownRaceFix()) { + // setRadioPowerForReason can be called by the binder thread. We need to move that into + // the main thread to prevent race condition. + post(() -> mSST.setRadioPowerForReason(power, forEmergencyCall, + isSelectedPhoneForEmergencyCall, forceApply, reason)); + } else { + mSST.setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, + forceApply, reason); + } } @Override @@ -2189,7 +2190,12 @@ @Override public void resetCarrierKeysForImsiEncryption() { - mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId); + mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId, false); + } + + @Override + public void resetCarrierKeysForImsiEncryption(boolean forceResetAll) { + mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId, forceResetAll); } @Override @@ -3770,6 +3776,13 @@ && disclosure != null) { mIdentifierDisclosureNotifier.addDisclosure(mContext, getSubId(), disclosure); } + if (mFeatureFlags.cellularIdentifierDisclosureIndications() + && mIdentifierDisclosureNotifier != null + && disclosure != null) { + logd("EVENT_CELL_IDENTIFIER_DISCLOSURE for non-Safety Center listeners " + + "phoneId = " + getPhoneId()); + mNotifier.notifyCellularIdentifierDisclosedChanged(this, disclosure); + } break; case EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE: @@ -3780,13 +3793,21 @@ case EVENT_SECURITY_ALGORITHM_UPDATE: logd("EVENT_SECURITY_ALGORITHM_UPDATE phoneId = " + getPhoneId()); + + ar = (AsyncResult) msg.obj; + SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result; + if (mFeatureFlags.enableModemCipherTransparencyUnsolEvents() && mNullCipherNotifier != null) { - ar = (AsyncResult) msg.obj; - SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result; mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getPhoneId(), getSubId(), update); } + if (mFeatureFlags.securityAlgorithmsUpdateIndications() + && mNullCipherNotifier != null) { + logd("EVENT_SECURITY_ALGORITHM_UPDATE for non-Safety Center listeners " + + "phoneId = " + getPhoneId()); + mNotifier.notifySecurityAlgorithmsChanged(this, update); + } break; case EVENT_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED_DONE:
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java index 3141406..9eebc60 100644 --- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java +++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -48,6 +48,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; +import com.android.internal.telephony.emergency.EmergencyNumberTracker; import com.android.internal.telephony.flags.FeatureFlags; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import com.android.internal.telephony.uicc.IccConstants; @@ -1511,8 +1512,7 @@ @VisibleForTesting public void notifyIfOutgoingEmergencySms(String destAddr) { Phone[] allPhones = mPhoneFactoryProxy.getPhones(); - EmergencyNumber emergencyNumber = mPhone.getEmergencyNumberTracker().getEmergencyNumber( - destAddr); + EmergencyNumber emergencyNumber = getEmergencyNumber(mPhone, destAddr); if (emergencyNumber != null) { mPhone.notifyOutgoingEmergencySms(emergencyNumber); } else if (allPhones.length > 1) { @@ -1522,8 +1522,7 @@ if (phone.getPhoneId() == mPhone.getPhoneId()) { continue; } - emergencyNumber = phone.getEmergencyNumberTracker() - .getEmergencyNumber(destAddr); + emergencyNumber = getEmergencyNumber(phone, destAddr); if (emergencyNumber != null) { mPhone.notifyOutgoingEmergencySms(emergencyNumber); break; @@ -1532,6 +1531,13 @@ } } + private EmergencyNumber getEmergencyNumber(Phone phone, String number) { + if (!phone.hasCalling()) return null; + EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker(); + if (tracker == null) return null; + return tracker.getEmergencyNumber(number); + } + private void returnUnspecifiedFailure(PendingIntent pi) { if (pi != null) { try {
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java index c94480e..6955a55 100644 --- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java +++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -202,8 +202,7 @@ tracker.onSent(mContext); mTrackers.remove(token); mPhone.notifySmsSent(tracker.mDestAddress); - mSmsDispatchersController.notifySmsSent( - tracker.mDestAddress, tracker.mMessageId, true, + mSmsDispatchersController.notifySmsSent(tracker, true, tracker.isSinglePartOrLastPart(), true /*success*/); break; case ImsSmsImplBase.SEND_STATUS_ERROR:
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java index ca03f5d..52ceda5 100644 --- a/src/java/com/android/internal/telephony/InboundSmsHandler.java +++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -66,6 +66,7 @@ import android.telephony.SmsMessage; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.LocalLog; import android.util.Pair; @@ -77,6 +78,7 @@ import com.android.internal.telephony.flags.FeatureFlags; import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.metrics.TelephonyMetrics; +import com.android.internal.telephony.satellite.SatelliteController; import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteSessionStats; import com.android.internal.telephony.util.NotificationChannelController; import com.android.internal.telephony.util.TelephonyUtils; @@ -687,6 +689,16 @@ result = RESULT_SMS_DISPATCH_FAILURE; } + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + if (result == Intents.RESULT_SMS_HANDLED) { + SatelliteController satelliteController = SatelliteController.getInstance(); + if (satelliteController != null + && satelliteController.shouldSendSmsToDatagramDispatcher(mPhone)) { + satelliteController.onSmsReceived(mPhone.getSubId()); + } + } + } + // RESULT_OK means that the SMS will be acknowledged by special handling, // e.g. for SMS-PP data download. Any other result, we should ack here. if (result != Activity.RESULT_OK) { @@ -747,6 +759,20 @@ return Intents.RESULT_SMS_HANDLED; } + if (isMtSmsPollingMessage(smsb)) { + log("Received MT SMS polling message. Ignored."); + return Intents.RESULT_SMS_HANDLED; + } + + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + SatelliteController satelliteController = SatelliteController.getInstance(); + if (satelliteController != null + && satelliteController.shouldDropSms(mPhone)) { + log("SMS not supported during satellite session."); + return Intents.RESULT_SMS_HANDLED; + } + } + int result = dispatchMessageRadioSpecific(smsb, smsSource, token); // In case of error, add to metrics. This is not required in case of success, as the @@ -754,8 +780,7 @@ if (result != Intents.RESULT_SMS_HANDLED && result != Activity.RESULT_OK) { mMetrics.writeIncomingSmsError(mPhone.getPhoneId(), is3gpp2(), smsSource, result); mPhone.getSmsStats().onIncomingSmsError(is3gpp2(), smsSource, result, - TelephonyManager.from(mContext) - .isEmergencyNumber(smsb.getOriginatingAddress())); + isEmergencyNumber(smsb.getOriginatingAddress())); if (mPhone != null) { TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics(); if (telephonyAnalytics != null) { @@ -1034,7 +1059,7 @@ logeWithLocalLog(errorMsg, tracker.getMessageId()); mPhone.getSmsStats().onIncomingSmsError( is3gpp2(), tracker.getSource(), RESULT_SMS_NULL_PDU, - TelephonyManager.from(mContext).isEmergencyNumber(tracker.getAddress())); + isEmergencyNumber(tracker.getAddress())); if (mPhone != null) { TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics(); if (telephonyAnalytics != null) { @@ -1064,8 +1089,7 @@ tracker.getMessageId()); mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(), messageCount, RESULT_SMS_NULL_MESSAGE, tracker.getMessageId(), - TelephonyManager.from(mContext) - .isEmergencyNumber(tracker.getAddress())); + isEmergencyNumber(tracker.getAddress())); return false; } } @@ -1100,8 +1124,7 @@ mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), tracker.getSource(), format, timestamps, wapPushResult, tracker.getMessageId()); mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(), messageCount, - result, tracker.getMessageId(), TelephonyManager.from(mContext) - .isEmergencyNumber(tracker.getAddress())); + result, tracker.getMessageId(), isEmergencyNumber(tracker.getAddress())); // result is Activity.RESULT_OK if an ordered broadcast was sent if (result == Activity.RESULT_OK) { return true; @@ -1122,7 +1145,7 @@ format, timestamps, block, tracker.getMessageId()); mPhone.getSmsStats().onIncomingSmsSuccess(is3gpp2(), tracker.getSource(), messageCount, block, tracker.getMessageId(), - TelephonyManager.from(mContext).isEmergencyNumber(tracker.getAddress())); + isEmergencyNumber(tracker.getAddress())); CarrierRoamingSatelliteSessionStats sessionStats = CarrierRoamingSatelliteSessionStats.getInstance(mPhone.getSubId()); sessionStats.onIncomingSms(mPhone.getSubId()); @@ -1160,6 +1183,13 @@ return true; } + private boolean isEmergencyNumber(String number) { + if (!mPhone.hasCalling()) return false; + TelephonyManager manager = TelephonyManager.from(mContext); + if (manager == null) return false; + return manager.isEmergencyNumber(number); + } + /** * Processes the message part while the credential-encrypted storage is still locked. * @@ -1970,6 +2000,17 @@ sendMessage(EVENT_BROADCAST_COMPLETE); } + private boolean isMtSmsPollingMessage(@NonNull SmsMessageBase smsb) { + if (!mFeatureFlags.carrierRoamingNbIotNtn() + || !mContext.getResources().getBoolean(R.bool.config_enabled_mt_sms_polling)) { + return false; + } + String mtSmsPollingText = mContext.getResources() + .getString(R.string.config_mt_sms_polling_text); + return !TextUtils.isEmpty(mtSmsPollingText) + && mtSmsPollingText.equals(smsb.getMessageBody()); + } + /** Checks whether the flag to skip new message notification is set in the bitmask returned * from the carrier app. */
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java index 3c35f1a..881adde 100644 --- a/src/java/com/android/internal/telephony/MultiSimSettingController.java +++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -513,8 +513,8 @@ SatelliteController satelliteController = SatelliteController.getInstance(); boolean isSatelliteEnabledOrBeingEnabled = false; if (satelliteController != null) { - isSatelliteEnabledOrBeingEnabled = satelliteController.isSatelliteEnabled() - || satelliteController.isSatelliteBeingEnabled(); + isSatelliteEnabledOrBeingEnabled = + satelliteController.isSatelliteEnabledOrBeingEnabled(); } if (DBG) {
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java index b4a37b3..ccf066f 100644 --- a/src/java/com/android/internal/telephony/NetworkResponse.java +++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -551,6 +551,36 @@ } } + /** + * @param responseInfo Response info struct containing response type, serial no. and error + */ + public void setSatellitePlmnResponse(RadioResponseInfo responseInfo) { + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error + */ + public void setSatelliteEnabledForCarrierResponse(RadioResponseInfo responseInfo) { + RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo); + } + + /** + * @param responseInfo Response info struct containing response type, serial no. and error. + * @param isEnabled Indicates whether satellite is enabled for carrier or not. + */ + public void isSatelliteEnabledForCarrierResponse(RadioResponseInfo responseInfo, + boolean isEnabled) { + RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo); + + if (rr != null) { + if (responseInfo.error == RadioError.NONE) { + RadioResponse.sendMessageResponse(rr.mResult, isEnabled); + } + mRil.processResponseDone(rr, responseInfo, isEnabled); + } + } + @Override public String getInterfaceHash() { return IRadioNetworkResponse.HASH;
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java index 6a331cf..b12a3df 100644 --- a/src/java/com/android/internal/telephony/NetworkTypeController.java +++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -22,7 +22,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; import android.os.AsyncResult; +import android.os.Handler; +import android.os.HandlerThread; import android.os.Message; import android.os.PersistableBundle; import android.os.PowerManager; @@ -170,7 +176,6 @@ @Override public void onQosSessionsChanged( @NonNull List<QosBearerSession> qosBearerSessions) { - if (!mIsTimerResetEnabledOnVoiceQos) return; sendMessage(obtainMessage(EVENT_QOS_SESSION_CHANGED, qosBearerSessions)); } @@ -233,6 +238,74 @@ private int mLastAnchorNrCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN; private boolean mDoesPccListIndicateIdle = false; + private boolean mInVoiceCall = false; + private boolean mIsSatelliteConstrainedData = false; + private boolean mIsSatelliteNetworkCallbackRegistered = false; + private ConnectivityManager mConnectivityManager; + + private final ConnectivityManager.NetworkCallback mNetworkCallback = + new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + log("On Available: " + network); + if (network != null) { + if (mConnectivityManager != null) { + NetworkCapabilities capabilities = + mConnectivityManager.getNetworkCapabilities(network); + updateBandwidthConstrainedStatus(capabilities); + } else { + log("network is null"); + } + } + } + + @Override + public void onCapabilitiesChanged(Network network, + NetworkCapabilities networkCapabilities) { + log("onCapabilitiesChanged: " + network); + if (network != null) { + updateBandwidthConstrainedStatus(networkCapabilities); + } else { + log("network is null"); + } + } + + @Override + public void onLost(Network network) { + log("Network Lost"); + if (mIsSatelliteConstrainedData) { + mIsSatelliteConstrainedData = false; + mDisplayInfoController.updateTelephonyDisplayInfo(); + } + } + }; + + private boolean isBandwidthConstrainedCapabilitySupported(NetworkCapabilities + capabilities) { + // TODO (b/382002908: Remove try catch exception for + // NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED & replace datautils with + // NetworkCapabilities on api availability at mainline module) + try { + return capabilities.hasTransport( + NetworkCapabilities.TRANSPORT_SATELLITE) && + !capabilities.hasCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED); + } catch (Exception ignored) { + log("NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED not supported "); + return false; + } + } + + private void updateBandwidthConstrainedStatus(NetworkCapabilities capabilities) { + if (capabilities != null) { + mIsSatelliteConstrainedData + = isBandwidthConstrainedCapabilitySupported(capabilities); + log("satellite constrained data status : " + mIsSatelliteConstrainedData); + mDisplayInfoController.updateTelephonyDisplayInfo(); + } else { + log("capabilities is null"); + } + } + /** * NetworkTypeController constructor. * @@ -265,9 +338,42 @@ mServiceState = mPhone.getServiceStateTracker().getServiceState(); mPhysicalChannelConfigs = mPhone.getServiceStateTracker().getPhysicalChannelConfigList(); + if(mFeatureFlags.carrierEnabledSatelliteFlag()) { + registerForSatelliteNetwork(); + } + sendMessage(EVENT_INITIALIZE); } + public synchronized void registerForSatelliteNetwork() { + if (!mIsSatelliteNetworkCallbackRegistered) { + mIsSatelliteNetworkCallbackRegistered = true; + HandlerThread handlerThread = new HandlerThread("SatelliteDataUsageThread"); + handlerThread.start(); + Handler handler = new Handler(handlerThread.getLooper()); + + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + // TODO (b/382002908: Remove try catch exception for + // NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED & replace datautils with + // NetworkCapabilities on api availability at mainline module) + try { + builder.removeCapability(DataUtils.NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED); + } catch (Exception ignored) { + log("NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED not supported "); + } + mConnectivityManager = + (ConnectivityManager) mPhone.getContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); + if (mConnectivityManager != null) { + mConnectivityManager.registerBestMatchingNetworkCallback( + builder.build(), mNetworkCallback, handler); + } else { + loge("network callback not registered"); + } + } + } + /** * @return The current override network type, used to create TelephonyDisplayInfo in * DisplayInfoController. @@ -288,6 +394,15 @@ } /** + * @return satellite bandwidth constrained connection status, used to create + * TelephonyDisplayInfo in DisplayInfoController. + * + */ + public boolean getSatelliteConstrainedData() { + return mIsSatelliteConstrainedData; + } + + /** * @return {@code true} if either the primary or secondary 5G icon timers are active, * and {@code false} if neither are. */ @@ -714,21 +829,22 @@ break; case EVENT_QOS_SESSION_CHANGED: List<QosBearerSession> qosBearerSessions = (List<QosBearerSession>) msg.obj; - boolean inVoiceCall = false; + mInVoiceCall = false; for (QosBearerSession session : qosBearerSessions) { // TS 23.203 23.501 - 1 means conversational voice - if (session.getQos() instanceof EpsQos qos) { - inVoiceCall = qos.getQci() == 1; - } else if (session.getQos() instanceof NrQos qos) { - inVoiceCall = qos.get5Qi() == 1; - } - if (inVoiceCall) { - if (DBG) log("Device in voice call, reset all timers"); - resetAllTimers(); - transitionToCurrentState(); + if (session.getQos() instanceof EpsQos qos && qos.getQci() == 1) { + mInVoiceCall = true; + break; + } else if (session.getQos() instanceof NrQos qos && qos.get5Qi() == 1) { + mInVoiceCall = true; break; } } + if (mIsTimerResetEnabledOnVoiceQos && mInVoiceCall) { + if (DBG) log("Device in voice call, reset all timers"); + resetAllTimers(); + transitionToCurrentState(); + } break; default: throw new RuntimeException("Received invalid event: " + msg.what); @@ -1371,6 +1487,8 @@ if (mIsPrimaryTimerActive) { log("Transition without timer from " + getCurrentState().getName() + " to " + destName + " due to existing " + mPrimaryTimerState + " primary timer."); + } else if (mIsTimerResetEnabledOnVoiceQos && mInVoiceCall) { + log("Skip primary timer to " + destName + " due to in call"); } else { if (DBG) { log("Transition with primary timer from " + mPreviousState + " to " + destName); @@ -1395,7 +1513,10 @@ log("Transition with secondary timer from " + currentName + " to " + destState.getName()); } - if (!mIsDeviceIdleMode && rule != null && rule.getSecondaryTimer(currentName) > 0) { + if (mIsTimerResetEnabledOnVoiceQos && mInVoiceCall) { + log("Skip secondary timer from " + currentName + " to " + + destState.getName() + " due to in call"); + } else if (!mIsDeviceIdleMode && rule != null && rule.getSecondaryTimer(currentName) > 0) { int duration = rule.getSecondaryTimer(currentName); if (mLastShownNrDueToAdvancedBand && mNrAdvancedBandsSecondaryTimer > 0) { duration = mNrAdvancedBandsSecondaryTimer; @@ -1754,6 +1875,7 @@ pw.println("mPrimaryCellChangedWhileIdle=" + mPrimaryCellChangedWhileIdle); pw.println("mEnableNrAdvancedWhileRoaming=" + mEnableNrAdvancedWhileRoaming); pw.println("mIsDeviceIdleMode=" + mIsDeviceIdleMode); + pw.println("mIsTimerResetEnabledOnVoiceQos=" + mIsTimerResetEnabledOnVoiceQos); pw.decreaseIndent(); pw.flush(); }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java index 69b19aa..0c645a0 100644 --- a/src/java/com/android/internal/telephony/Phone.java +++ b/src/java/com/android/internal/telephony/Phone.java
@@ -74,6 +74,7 @@ import android.telephony.ims.RegistrationManager; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.satellite.NtnSignalStrength; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; @@ -126,6 +127,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -659,7 +661,7 @@ mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName()) .makeSmsStorageMonitor(this, mFeatureFlags); mSmsUsageMonitor = mTelephonyComponentFactory.inject(SmsUsageMonitor.class.getName()) - .makeSmsUsageMonitor(context); + .makeSmsUsageMonitor(context, mFeatureFlags); mUiccController = UiccController.getInstance(); mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null); mSimActivationTracker = mTelephonyComponentFactory @@ -1965,6 +1967,13 @@ } /** + * @return true if this device supports calling, false otherwise. + */ + public boolean hasCalling() { + return TelephonyCapabilities.supportsTelephonyCalling(mFeatureFlags, mContext); + } + + /** * Retrieves the EmergencyNumberTracker of the phone instance. */ public EmergencyNumberTracker getEmergencyNumberTracker() { @@ -2275,6 +2284,7 @@ * @param response is callback message to report one of TelephonyManager#CDMA_ROAMING_MODE_* */ public void queryCdmaRoamingPreference(Message response) { + if (mFeatureFlags.cleanupCdma()) return; mCi.queryCdmaRoamingPreference(response); } @@ -2284,6 +2294,7 @@ * @param response is callback message to report one of TelephonyManager#CDMA_SUBSCRIPTION_* */ public void queryCdmaSubscriptionMode(Message response) { + if (mFeatureFlags.cleanupCdma()) return; mCi.getCdmaSubscriptionSource(response); } @@ -2321,6 +2332,7 @@ * @param response is callback message */ public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) { + if (mFeatureFlags.cleanupCdma()) return; mCi.setCdmaRoamingPreference(cdmaRoamingType, response); } @@ -2330,6 +2342,7 @@ * @param response is callback message */ public void setCdmaSubscriptionMode(int cdmaSubscriptionType, Message response) { + if (mFeatureFlags.cleanupCdma()) return; mCi.setCdmaSubscriptionSource(cdmaSubscriptionType, response); } @@ -2766,6 +2779,7 @@ * @param workSource calling WorkSource */ public void nvReadItem(int itemID, Message response, WorkSource workSource) { + if (mFeatureFlags.cleanupCdma()) return; mCi.nvReadItem(itemID, response, workSource); } @@ -2780,6 +2794,7 @@ */ public void nvWriteItem(int itemID, String itemValue, Message response, WorkSource workSource) { + if (mFeatureFlags.cleanupCdma()) return; mCi.nvWriteItem(itemID, itemValue, response, workSource); } @@ -2791,6 +2806,7 @@ * @param response Callback message. */ public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message response) { + if (mFeatureFlags.cleanupCdma()) return; mCi.nvWriteCdmaPrl(preferredRoamingList, response); } @@ -2812,6 +2828,7 @@ * @param response Callback message. */ public void resetModemConfig(Message response) { + if (mFeatureFlags.cleanupCdma()) return; mCi.nvResetConfig(3 /* factory NV reset */, response); } @@ -2821,6 +2838,7 @@ * @param response Callback message. */ public void eraseModemConfig(Message response) { + if (mFeatureFlags.cleanupCdma()) return; mCi.nvResetConfig(2 /* erase NV */, response); } @@ -3502,6 +3520,7 @@ * @param obj User object. */ public void registerForNumberInfo(Handler h, int what, Object obj) { + if (mFeatureFlags.cleanupCdma()) return; mCi.registerForNumberInfo(h, what, obj); } @@ -3512,6 +3531,7 @@ * @param h Handler to be removed from the registrant list. */ public void unregisterForNumberInfo(Handler h) { + if (mFeatureFlags.cleanupCdma()) return; mCi.unregisterForNumberInfo(h); } @@ -3527,6 +3547,7 @@ * @param obj User object. */ public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) { + if (mFeatureFlags.cleanupCdma()) return; mCi.registerForRedirectedNumberInfo(h, what, obj); } @@ -3537,6 +3558,7 @@ * @param h Handler to be removed from the registrant list. */ public void unregisterForRedirectedNumberInfo(Handler h) { + if (mFeatureFlags.cleanupCdma()) return; mCi.unregisterForRedirectedNumberInfo(h); } @@ -3552,6 +3574,7 @@ * @param obj User object. */ public void registerForLineControlInfo(Handler h, int what, Object obj) { + if (mFeatureFlags.cleanupCdma()) return; mCi.registerForLineControlInfo(h, what, obj); } @@ -3562,6 +3585,7 @@ * @param h Handler to be removed from the registrant list. */ public void unregisterForLineControlInfo(Handler h) { + if (mFeatureFlags.cleanupCdma()) return; mCi.unregisterForLineControlInfo(h); } @@ -3577,6 +3601,7 @@ * @param obj User object. */ public void registerFoT53ClirlInfo(Handler h, int what, Object obj) { + if (mFeatureFlags.cleanupCdma()) return; mCi.registerFoT53ClirlInfo(h, what, obj); } @@ -3587,6 +3612,7 @@ * @param h Handler to be removed from the registrant list. */ public void unregisterForT53ClirInfo(Handler h) { + if (mFeatureFlags.cleanupCdma()) return; mCi.unregisterForT53ClirInfo(h); } @@ -3602,6 +3628,7 @@ * @param obj User object. */ public void registerForT53AudioControlInfo(Handler h, int what, Object obj) { + if (mFeatureFlags.cleanupCdma()) return; mCi.registerForT53AudioControlInfo(h, what, obj); } @@ -3612,6 +3639,7 @@ * @param h Handler to be removed from the registrant list. */ public void unregisterForT53AudioControlInfo(Handler h) { + if (mFeatureFlags.cleanupCdma()) return; mCi.unregisterForT53AudioControlInfo(h); } @@ -4067,6 +4095,16 @@ } /** + * Resets the Carrier Keys in the database. This involves 2 steps: + * 1. Delete the keys from the database. + * 2. Send an intent to download new Certificates. + * + * @param forceResetAll : Force delete the downloaded key if any. + */ + public void resetCarrierKeysForImsiEncryption(boolean forceResetAll) { + } + + /** * Return if UT capability of ImsPhone is enabled or not */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -4140,8 +4178,11 @@ setRoamingOverrideHelper(gsmRoamingList, GSM_ROAMING_LIST_OVERRIDE_PREFIX, iccId); setRoamingOverrideHelper(gsmNonRoamingList, GSM_NON_ROAMING_LIST_OVERRIDE_PREFIX, iccId); - setRoamingOverrideHelper(cdmaRoamingList, CDMA_ROAMING_LIST_OVERRIDE_PREFIX, iccId); - setRoamingOverrideHelper(cdmaNonRoamingList, CDMA_NON_ROAMING_LIST_OVERRIDE_PREFIX, iccId); + if (!mFeatureFlags.cleanupCdma()) { + setRoamingOverrideHelper(cdmaRoamingList, CDMA_ROAMING_LIST_OVERRIDE_PREFIX, iccId); + setRoamingOverrideHelper(cdmaNonRoamingList, CDMA_NON_ROAMING_LIST_OVERRIDE_PREFIX, + iccId); + } // Refresh. ServiceStateTracker tracker = getServiceStateTracker(); @@ -5295,22 +5336,43 @@ } /** - * Start callback mode + * Start the emergency callback mode * @param type for callback mode entry. + * @param durationMillis is the number of milliseconds remaining in the emergency callback + * mode. */ - public void startCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type) { - Rlog.d(mLogTag, "startCallbackMode:type=" + type); - mNotifier.notifyCallbackModeStarted(this, type); + public void startEmergencyCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type, + long durationMillis) { + if (!mFeatureFlags.emergencyCallbackModeNotification()) return; + + Rlog.d(mLogTag, "startEmergencyCallbackMode:type=" + type); + mNotifier.notifyCallbackModeStarted(this, type, durationMillis); } /** - * Stop callback mode + * Restart the emergency callback mode + * @param type for callback mode entry. + * @param durationMillis is the number of milliseconds remaining in the emergency callback + * mode. + */ + public void restartEmergencyCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type, + long durationMillis) { + if (!mFeatureFlags.emergencyCallbackModeNotification()) return; + + Rlog.d(mLogTag, "restartEmergencyCallbackMode:type=" + type); + mNotifier.notifyCallbackModeRestarted(this, type, durationMillis); + } + + /** + * Stop the emergency callback mode * @param type for callback mode exit. * @param reason for stopping callback mode. */ - public void stopCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type, + public void stopEmergencyCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type, @TelephonyManager.EmergencyCallbackModeStopReason int reason) { - Rlog.d(mLogTag, "stopCallbackMode:type=" + type + ", reason=" + reason); + if (!mFeatureFlags.emergencyCallbackModeNotification()) return; + + Rlog.d(mLogTag, "stopEmergencyCallbackMode:type=" + type + ", reason=" + reason); mNotifier.notifyCallbackModeStopped(this, type, reason); } @@ -5347,6 +5409,29 @@ mNotifier.notifyCarrierRoamingNtnEligibleStateChanged(this, eligible); } + /** + * Notify external listeners that carrier roaming non-terrestrial available services changed. + * @param availableServices The list of the supported services. + */ + public void notifyCarrierRoamingNtnAvailableServicesChanged( + @NetworkRegistrationInfo.ServiceType int[] availableServices) { + logd("notifyCarrierRoamingNtnAvailableServicesChanged availableServices:" + + Arrays.toString(availableServices)); + mNotifier.notifyCarrierRoamingNtnAvailableServicesChanged(this, availableServices); + } + + /** + * Notify external listeners that carrier roaming non-terrestrial network + * signal strength changed. + * @param ntnSignalStrength non-terrestrial network signal strength. + */ + public void notifyCarrierRoamingNtnSignalStrengthChanged( + @NonNull NtnSignalStrength ntnSignalStrength) { + logd("notifyCarrierRoamingNtnSignalStrengthChanged: ntnSignalStrength=" + + ntnSignalStrength.getLevel()); + mNotifier.notifyCarrierRoamingNtnSignalStrengthChanged(this, ntnSignalStrength); + } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Phone: subId=" + getSubId()); pw.println(" mPhoneId=" + mPhoneId);
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java index 3f388fb..6c8b222 100644 --- a/src/java/com/android/internal/telephony/PhoneNotifier.java +++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -25,10 +25,13 @@ import android.telephony.CallQuality; import android.telephony.CellIdentity; import android.telephony.CellInfo; +import android.telephony.CellularIdentifierDisclosure; import android.telephony.LinkCapacityEstimate; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseDataConnectionState; +import android.telephony.SecurityAlgorithmUpdate; import android.telephony.ServiceState; import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager.DataEnabledReason; @@ -37,6 +40,7 @@ import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.MediaQualityStatus; +import android.telephony.satellite.NtnSignalStrength; import java.util.List; import java.util.Set; @@ -145,7 +149,12 @@ List<LinkCapacityEstimate> linkCapacityEstimateList); /** Notify callback mode started. */ - void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type); + void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type, + long durationMillis); + + /** Notify callback mode restarted. */ + void notifyCallbackModeRestarted(Phone sender, @EmergencyCallbackModeType int type, + long durationMillis); /** Notify callback mode stopped. */ void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type, @@ -159,4 +168,19 @@ /** Notify eligibility to connect to carrier roaming non-terrestrial network changed. */ void notifyCarrierRoamingNtnEligibleStateChanged(Phone sender, boolean eligible); + + /** Notify carrier roaming non-terrestrial available services changed. */ + void notifyCarrierRoamingNtnAvailableServicesChanged( + Phone sender, @NetworkRegistrationInfo.ServiceType int[] availableServices); + + /** Notify carrier roaming non-terrestrial network signal strength changed. */ + void notifyCarrierRoamingNtnSignalStrengthChanged(Phone sender, + @NonNull NtnSignalStrength ntnSignalStrength); + + /** Notify of a cellular identifier disclosure change. */ + void notifyCellularIdentifierDisclosedChanged(Phone sender, + CellularIdentifierDisclosure disclosure); + + /** Notify of a security algorithm update change. */ + void notifySecurityAlgorithmsChanged(Phone sender, SecurityAlgorithmUpdate update); }
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java index 7ee3de2..9801542 100644 --- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java +++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -53,7 +53,9 @@ import com.android.telephony.Rlog; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; public class PhoneSubInfoController extends IPhoneSubInfo.Stub { private static final String TAG = "PhoneSubInfoController"; @@ -477,39 +479,37 @@ * * @param subId subscriptionId * @param callingPackage package name of the caller - * @param callingFeatureId feature Id of the caller * @return List of public user identities of type android.net.Uri or empty list if * EF_IMPU is not available. * @throws IllegalArgumentException if the subscriptionId is not valid * @throws IllegalStateException in case the ISIM hasn’t been loaded. * @throws SecurityException if the caller does not have the required permission */ - public List<Uri> getImsPublicUserIdentities(int subId, String callingPackage, - String callingFeatureId) { - if (TelephonyPermissions. - checkCallingOrSelfReadPrivilegedPhoneStatePermissionOrReadPhoneNumber( - mContext, subId, callingPackage, callingFeatureId, "getImsPublicUserIdentities")) { - - enforceTelephonyFeatureWithException(callingPackage, - PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getImsPublicUserIdentities"); - - Phone phone = getPhone(subId); - assert phone != null; - IsimRecords isimRecords = phone.getIsimRecords(); - if (isimRecords != null) { - String[] impus = isimRecords.getIsimImpu(); - List<Uri> impuList = new ArrayList<>(); - for (String impu : impus) { - if (impu != null && impu.trim().length() > 0) { - impuList.add(Uri.parse(impu)); - } - } - return impuList; - } - throw new IllegalStateException("ISIM is not loaded"); - } else { - throw new IllegalArgumentException("Invalid SubscriptionID = " + subId); + public List<Uri> getImsPublicUserIdentities(int subId, String callingPackage) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + throw new IllegalArgumentException("Invalid subscription: " + subId); } + + TelephonyPermissions + .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( + mContext, subId, "getImsPublicUserIdentities"); + enforceTelephonyFeatureWithException(callingPackage, + PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getImsPublicUserIdentities"); + + Phone phone = getPhone(subId); + assert phone != null; + IsimRecords isimRecords = phone.getIsimRecords(); + if (isimRecords != null) { + String[] impus = isimRecords.getIsimImpu(); + List<Uri> impuList = new ArrayList<>(); + for (String impu : impus) { + if (impu != null && impu.trim().length() > 0) { + impuList.add(Uri.parse(impu)); + } + } + return impuList; + } + throw new IllegalStateException("ISIM is not loaded"); } /** @@ -546,6 +546,45 @@ } /** + * Fetches the IMS Proxy Call Session Control Function(P-CSCF) based on the subscription. + * + * @param subId subscriptionId + * @param callingPackage package name of the caller + * @return List of IMS Proxy Call Session Control Function strings. + * @throws IllegalArgumentException if the subscriptionId is not valid + * @throws IllegalStateException in case the ISIM hasn’t been loaded. + * @throws SecurityException if the caller does not have the required permission + */ + public List<String> getImsPcscfAddresses(int subId, String callingPackage) { + if (!mFeatureFlags.supportIsimRecord()) { + return new ArrayList<>(); + } + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + throw new IllegalArgumentException("Invalid subscription: " + subId); + } + + TelephonyPermissions + .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( + mContext, subId, "getImsPcscfAddresses"); + enforceTelephonyFeatureWithException(callingPackage, + PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getImsPcscfAddresses"); + + Phone phone = getPhone(subId); + assert phone != null; + IsimRecords isimRecords = phone.getIsimRecords(); + if (isimRecords != null) { + String[] pcscfs = isimRecords.getIsimPcscf(); + List<String> pcscfList = Arrays.stream(pcscfs) + .filter(u -> u != null) + .map(u -> u.trim()) + .filter(u -> u.length() > 0) + .collect(Collectors.toList()); + return pcscfList; + } + throw new IllegalStateException("ISIM is not loaded"); + } + + /** * Returns the USIM service table that fetched from EFUST elementary field that are loaded * based on the appType. */ @@ -620,6 +659,20 @@ }); } + /** + * Return GroupIdLevel2 for the subscriber + */ + public String getGroupIdLevel2ForSubscriber(int subId, String callingPackage, + String callingFeatureId) { + return callPhoneMethodForSubIdWithPrivilegedCheck(subId, + "getGroupIdLevel2", (phone)-> { + enforceTelephonyFeatureWithException(callingPackage, + PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, + "getGroupIdLevel2ForSubscriber"); + return phone.getGroupIdLevel2(); + }); + } + /** Below are utility methods that abstracts the flow that many public methods use: * 1. Check permission: pass, throw exception, or fails (returns false). * 2. clearCallingIdentity.
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java index 5ff3360..2857244 100644 --- a/src/java/com/android/internal/telephony/RIL.java +++ b/src/java/com/android/internal/telephony/RIL.java
@@ -181,6 +181,9 @@ /** @hide */ public static final HalVersion RADIO_HAL_VERSION_2_3 = new HalVersion(2, 3); + /** @hide */ + public static final HalVersion RADIO_HAL_VERSION_2_4 = new HalVersion(2, 4); + // Hal version private final Map<Integer, HalVersion> mHalVersion = new HashMap<>(); @@ -1122,6 +1125,9 @@ SparseArray<RadioServiceProxy> proxies, @NonNull FeatureFlags flags) { super(context); mFeatureFlags = flags; + if (mFeatureFlags.cleanupCdma()) { + cdmaSubscription = TelephonyManager.CDMA_SUBSCRIPTION_UNKNOWN; + } if (RILJ_LOGD) { riljLog("RIL: init allowedNetworkTypes=" + allowedNetworkTypes + " cdmaSubscription=" + cdmaSubscription + ")"); @@ -3215,6 +3221,8 @@ @Override public void setCdmaSubscriptionSource(int cdmaSubscription, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class); if (!canMakeRequest("setCdmaSubscriptionSource", simProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -3235,6 +3243,8 @@ @Override public void queryCdmaRoamingPreference(Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class); if (!canMakeRequest("queryCdmaRoamingPreference", networkProxy, result, RADIO_HAL_VERSION_1_4)) { @@ -3255,6 +3265,8 @@ @Override public void setCdmaRoamingPreference(int cdmaRoamingType, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class); if (!canMakeRequest("setCdmaRoamingPreference", networkProxy, result, RADIO_HAL_VERSION_1_4)) { @@ -3354,6 +3366,8 @@ @Override public void sendCDMAFeatureCode(String featureCode, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioVoiceProxy voiceProxy = getRadioServiceProxy(RadioVoiceProxy.class); if (!canMakeRequest("sendCDMAFeatureCode", voiceProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -3394,6 +3408,8 @@ @Override public void sendCdmaSMSExpectMore(byte[] pdu, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class); if (!canMakeRequest("sendCdmaSMSExpectMore", messagingProxy, result, RADIO_HAL_VERSION_1_4)) { @@ -3420,6 +3436,8 @@ @Override public void sendCdmaSms(byte[] pdu, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class); if (!canMakeRequest("sendCdmaSms", messagingProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -3441,6 +3459,8 @@ @Override public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class); if (!canMakeRequest("acknowledgeLastIncomingCdmaSms", messagingProxy, result, RADIO_HAL_VERSION_1_4)) { @@ -3528,6 +3548,8 @@ @Override public void getCdmaBroadcastConfig(Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class); if (!canMakeRequest("getCdmaBroadcastConfig", messagingProxy, result, RADIO_HAL_VERSION_1_4)) { @@ -3548,6 +3570,8 @@ @Override public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class); if (!canMakeRequest("setCdmaBroadcastConfig", messagingProxy, result, RADIO_HAL_VERSION_1_4)) { @@ -3572,6 +3596,8 @@ @Override public void setCdmaBroadcastActivation(boolean activate, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class); if (!canMakeRequest("setCdmaBroadcastActivation", messagingProxy, result, RADIO_HAL_VERSION_1_4)) { @@ -3593,6 +3619,8 @@ @Override public void getCDMASubscription(Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class); if (!canMakeRequest("getCDMASubscription", simProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -3631,6 +3659,8 @@ @Override public void deleteSmsOnRuim(int index, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioMessagingProxy messagingProxy = getRadioServiceProxy(RadioMessagingProxy.class); if (!canMakeRequest("deleteSmsOnRuim", messagingProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -3784,6 +3814,8 @@ @Override public void getCdmaSubscriptionSource(Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class); if (!canMakeRequest("getCdmaSubscriptionSource", simProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -4071,6 +4103,8 @@ @Override public void nvReadItem(int itemID, Message result, WorkSource workSource) { + if (mFeatureFlags.cleanupCdma()) return; + RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class); if (!canMakeRequest("nvReadItem", modemProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -4091,6 +4125,8 @@ @Override public void nvWriteItem(int itemId, String itemValue, Message result, WorkSource workSource) { + if (mFeatureFlags.cleanupCdma()) return; + RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class); if (!canMakeRequest("nvWriteItem", modemProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -4112,6 +4148,8 @@ @Override public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class); if (!canMakeRequest("nvWriteCdmaPrl", modemProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -4132,6 +4170,8 @@ @Override public void nvResetConfig(int resetType, Message result) { + if (mFeatureFlags.cleanupCdma()) return; + RadioModemProxy modemProxy = getRadioServiceProxy(RadioModemProxy.class); if (!canMakeRequest("nvResetConfig", modemProxy, result, RADIO_HAL_VERSION_1_4)) { return; @@ -5352,6 +5392,102 @@ }); } + /** + * {@inheritDoc} + */ + @Override + public void setSatellitePlmn(int simSlot, @NonNull List<String> carrierPlmnList, + @NonNull List<String> allSatellitePlmnList, Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class); + if (!canMakeRequest( + "setSatellitePlmn", + networkProxy, + result, + RADIO_HAL_VERSION_2_4)) { + return; + } + + RILRequest rr = obtainRequest(RIL_REQUEST_SET_SATELLITE_PLMN, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " simSlot=" + simSlot + " carrierPlmnList=" + carrierPlmnList + + " allSatellitePlmnList=" + allSatellitePlmnList); + } + + radioServiceInvokeHelper( + HAL_SERVICE_NETWORK, + rr, + "setSatellitePlmn", + () -> { + networkProxy.setSatellitePlmn(rr.mSerial, simSlot, carrierPlmnList, + allSatellitePlmnList); + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void setSatelliteEnabledForCarrier(int simSlot, boolean satelliteEnabled, + Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class); + if (!canMakeRequest( + "setSatelliteEnabledForCarrier", + networkProxy, + result, + RADIO_HAL_VERSION_2_4)) { + return; + } + + RILRequest rr = obtainRequest(RIL_REQUEST_SET_SATELLITE_ENABLED_FOR_CARRIER, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " simSlot=" + simSlot + " satelliteEnabled=" + satelliteEnabled); + } + + radioServiceInvokeHelper( + HAL_SERVICE_NETWORK, + rr, + "setSatelliteEnabledForCarrier", + () -> { + networkProxy.setSatelliteEnabledForCarrier(rr.mSerial, simSlot, + satelliteEnabled); + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void isSatelliteEnabledForCarrier(int simSlot, Message result) { + RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class); + if (!canMakeRequest( + "isSatelliteEnabledForCarrier", + networkProxy, + result, + RADIO_HAL_VERSION_2_4)) { + return; + } + + RILRequest rr = obtainRequest(RIL_REQUEST_IS_SATELLITE_ENABLED_FOR_CARRIER, result, + mRILDefaultWorkSource); + + if (RILJ_LOGD) { + riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + + " simSlot=" + simSlot); + } + + radioServiceInvokeHelper( + HAL_SERVICE_NETWORK, rr, "isSatelliteEnabledForCarrier", () -> { + networkProxy.isSatelliteEnabledForCarrier(rr.mSerial, simSlot); + }); + } + + //***** Private Methods /** * This is a helper function to be called when an indication callback is called for any radio @@ -6028,6 +6164,8 @@ @UnsupportedAppUsage void notifyRegistrantsCdmaInfoRec(CdmaInformationRecords infoRec) { + if (mFeatureFlags.cleanupCdma()) return; + int response = RIL_UNSOL_CDMA_INFO_REC; if (infoRec.record instanceof CdmaInformationRecords.CdmaDisplayInfoRec) { if (mDisplayInfoRegistrants != null) { @@ -6225,6 +6363,7 @@ case 2: return RADIO_HAL_VERSION_2_1; case 3: return RADIO_HAL_VERSION_2_2; case 4: return RADIO_HAL_VERSION_2_3; + case 5: return RADIO_HAL_VERSION_2_4; default: return RADIO_HAL_VERSION_UNKNOWN; } }
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java index a81dbc8..15e2374 100644 --- a/src/java/com/android/internal/telephony/RILUtils.java +++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -121,6 +121,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_N1_MODE_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_SATELLITE_ENABLED_FOR_CARRIER; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_SECURITY_ALGORITHMS_UPDATED_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IS_VONR_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_LAST_CALL_FAIL_CAUSE; @@ -180,6 +181,8 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_RADIO_CAPABILITY; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SATELLITE_ENABLED_FOR_CARRIER; +import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SATELLITE_PLMN; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA; import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_SIM_CARD_POWER; @@ -384,6 +387,7 @@ import com.android.internal.telephony.cdma.sms.SmsEnvelope; import com.android.internal.telephony.data.KeepaliveStatus; import com.android.internal.telephony.data.KeepaliveStatus.KeepaliveStatusCode; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.imsphone.ImsCallInfo; import com.android.internal.telephony.uicc.AdnCapacity; import com.android.internal.telephony.uicc.IccCardApplicationStatus; @@ -394,6 +398,7 @@ import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.PortUtils; import com.android.internal.telephony.uicc.SimPhonebookRecord; +import com.android.internal.telephony.uicc.SimTypeInfo; import com.android.telephony.Rlog; import java.io.ByteArrayInputStream; @@ -1169,6 +1174,10 @@ * 2 - erase NV reset (SCRTN) * 3 - factory reset (RTN) */ + if (Flags.cleanupCdma()) { + if (resetType == 1) return android.hardware.radio.V1_0.ResetNvType.RELOAD; + return -1; + } switch (resetType) { case 1: return android.hardware.radio.V1_0.ResetNvType.RELOAD; case 2: return android.hardware.radio.V1_0.ResetNvType.ERASE; @@ -1189,6 +1198,10 @@ * 2 - erase NV reset (SCRTN) * 3 - factory reset (RTN) */ + if (Flags.cleanupCdma()) { + if (resetType == 1) return android.hardware.radio.modem.ResetNvType.RELOAD; + return -1; + } switch (resetType) { case 1: return android.hardware.radio.modem.ResetNvType.RELOAD; case 2: return android.hardware.radio.modem.ResetNvType.ERASE; @@ -1685,6 +1698,9 @@ if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) != 0) { raf |= android.hardware.radio.RadioAccessFamily.NR; } + if ((networkTypeBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NB_IOT_NTN) != 0) { + raf |= android.hardware.radio.RadioAccessFamily.NB_IOT_NTN; + } return (raf == 0) ? android.hardware.radio.RadioAccessFamily.UNKNOWN : raf; } @@ -2913,6 +2929,7 @@ */ public static CellSignalStrengthGsm convertHalGsmSignalStrength( android.hardware.radio.V1_0.GsmSignalStrength ss) { + if (ss == null) return new CellSignalStrengthGsm(); CellSignalStrengthGsm ret = new CellSignalStrengthGsm( CellSignalStrength.getRssiDbmFromAsu(ss.signalStrength), ss.bitErrorRate, ss.timingAdvance); @@ -2930,6 +2947,7 @@ */ public static CellSignalStrengthGsm convertHalGsmSignalStrength( android.hardware.radio.network.GsmSignalStrength ss) { + if (ss == null) return new CellSignalStrengthGsm(); CellSignalStrengthGsm ret = new CellSignalStrengthGsm( CellSignalStrength.getRssiDbmFromAsu(ss.signalStrength), ss.bitErrorRate, ss.timingAdvance); @@ -2950,6 +2968,7 @@ public static CellSignalStrengthCdma convertHalCdmaSignalStrength( android.hardware.radio.V1_0.CdmaSignalStrength cdma, android.hardware.radio.V1_0.EvdoSignalStrength evdo) { + if (cdma == null || evdo == null) return new CellSignalStrengthCdma(); return new CellSignalStrengthCdma(-cdma.dbm, -cdma.ecio, -evdo.dbm, -evdo.ecio, evdo.signalNoiseRatio); } @@ -2964,6 +2983,7 @@ public static CellSignalStrengthCdma convertHalCdmaSignalStrength( android.hardware.radio.network.CdmaSignalStrength cdma, android.hardware.radio.network.EvdoSignalStrength evdo) { + if (cdma == null || evdo == null) return new CellSignalStrengthCdma(); return new CellSignalStrengthCdma(-cdma.dbm, -cdma.ecio, -evdo.dbm, -evdo.ecio, evdo.signalNoiseRatio); } @@ -3427,9 +3447,11 @@ android.hardware.radio.data.SetupDataCallResult result) { if (result == null) return null; List<LinkAddress> laList = new ArrayList<>(); - for (android.hardware.radio.data.LinkAddress la : result.addresses) { - laList.add(convertToLinkAddress(la.address, la.addressProperties, - la.deprecationTime, la.expirationTime)); + if (result.addresses != null) { + for (android.hardware.radio.data.LinkAddress la : result.addresses) { + laList.add(convertToLinkAddress(la.address, la.addressProperties, + la.deprecationTime, la.expirationTime)); + } } List<InetAddress> dnsList = new ArrayList<>(); if (result.dnses != null) { @@ -3471,15 +3493,19 @@ } } List<QosBearerSession> qosSessions = new ArrayList<>(); - for (android.hardware.radio.data.QosSession session : result.qosSessions) { - qosSessions.add(convertHalQosBearerSession(session)); + if (result.qosSessions != null) { + for (android.hardware.radio.data.QosSession session : result.qosSessions) { + qosSessions.add(convertHalQosBearerSession(session)); + } } List<TrafficDescriptor> trafficDescriptors = new ArrayList<>(); - for (android.hardware.radio.data.TrafficDescriptor td : result.trafficDescriptors) { - try { - trafficDescriptors.add(convertHalTrafficDescriptor(td)); - } catch (IllegalArgumentException e) { - loge("convertHalDataCallResult: Failed to convert traffic descriptor. e=" + e); + if (result.trafficDescriptors != null) { + for (android.hardware.radio.data.TrafficDescriptor td : result.trafficDescriptors) { + try { + trafficDescriptors.add(convertHalTrafficDescriptor(td)); + } catch (IllegalArgumentException e) { + loge("convertHalDataCallResult: Failed to convert traffic descriptor. e=" + e); + } } } @@ -3659,6 +3685,7 @@ } private static Qos convertHalQos(android.hardware.radio.V1_6.Qos qos) { + if (qos == null) return null; switch (qos.getDiscriminator()) { case android.hardware.radio.V1_6.Qos.hidl_discriminator.eps: android.hardware.radio.V1_6.EpsQos eps = qos.eps(); @@ -3674,6 +3701,7 @@ } private static Qos convertHalQos(android.hardware.radio.data.Qos qos) { + if (qos == null) return null; switch (qos.getTag()) { case android.hardware.radio.data.Qos.eps: android.hardware.radio.data.EpsQos eps = qos.getEps(); @@ -4132,6 +4160,34 @@ } /** + * This API is for fallback to support getAllowedCarriers too. + * + * Convert an array of CarrierInfo defined in + * radio/aidl/android/hardware/radio/sim/CarrierInfo.aidl to a list of CarrierIdentifiers. + * + * @param carrierInfos array of CarrierInfo defined in + * radio/aidl/android/hardware/radio/sim/CarrierInfo.aidl + * @return The converted list of CarrierIdentifiers + */ + public static List<CarrierIdentifier> convertAidlCarrierInfoListToHalCarrierList( + android.hardware.radio.sim.CarrierInfo[] carrierInfos) { + List<CarrierIdentifier> ret = new ArrayList<>(); + if (carrierInfos == null) { + return ret; + } + for (android.hardware.radio.sim.CarrierInfo carrierInfo : carrierInfos) { + String mcc = carrierInfo.mcc; + String mnc = carrierInfo.mnc; + String spn = carrierInfo.spn; + String imsi = carrierInfo.imsiPrefix; + String gid1 = carrierInfo.gid1; + String gid2 = carrierInfo.gid2; + ret.add(new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2)); + } + return ret; + } + + /** * Convert the sim policy defined in * radio/aidl/android/hardware/radio/sim/SimLockMultiSimPolicy.aidl to the equivalent sim * policy defined in android.telephony/CarrierRestrictionRules.MultiSimPolicy @@ -4191,7 +4247,8 @@ iccCardStatus.setCardState(cardStatus10.cardState); iccCardStatus.setUniversalPinState(cardStatus10.universalPinState); iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus10.gsmUmtsSubscriptionAppIndex; - iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus10.cdmaSubscriptionAppIndex; + iccCardStatus.mCdmaSubscriptionAppIndex = + Flags.cleanupCdma() ? -1 : cardStatus10.cdmaSubscriptionAppIndex; iccCardStatus.mImsSubscriptionAppIndex = cardStatus10.imsSubscriptionAppIndex; int numApplications = cardStatus10.applications.size(); @@ -4261,7 +4318,8 @@ iccCardStatus.setMultipleEnabledProfilesMode(cardStatus.supportedMepMode); iccCardStatus.setUniversalPinState(cardStatus.universalPinState); iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus.gsmUmtsSubscriptionAppIndex; - iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus.cdmaSubscriptionAppIndex; + iccCardStatus.mCdmaSubscriptionAppIndex = + Flags.cleanupCdma() ? -1 : cardStatus.cdmaSubscriptionAppIndex; iccCardStatus.mImsSubscriptionAppIndex = cardStatus.imsSubscriptionAppIndex; iccCardStatus.atr = cardStatus.atr; iccCardStatus.iccid = cardStatus.iccid; @@ -5260,6 +5318,12 @@ return "IS_SECURITY_ALGORITHMS_UPDATED_ENABLED"; case RIL_REQUEST_GET_SIMULTANEOUS_CALLING_SUPPORT: return "GET_SIMULTANEOUS_CALLING_SUPPORT"; + case RIL_REQUEST_SET_SATELLITE_PLMN: + return "SET_SATELLITE_PLMN"; + case RIL_REQUEST_SET_SATELLITE_ENABLED_FOR_CARRIER: + return "SET_SATELLITE_ENABLED_FOR_CARRIER"; + case RIL_REQUEST_IS_SATELLITE_ENABLED_FOR_CARRIER: + return "IS_SATELLITE_ENABLED_FOR_CARRIER"; default: return "<unknown request " + request + ">"; } @@ -5842,6 +5906,23 @@ securityAlgorithmUpdate.isUnprotectedEmergency); } + /** Convert an AIDL-based SimTypeInfo to its Java wrapper. */ + public static ArrayList<SimTypeInfo> convertAidlSimTypeInfo( + android.hardware.radio.config.SimTypeInfo[] simTypeInfos) { + ArrayList<SimTypeInfo> response = new ArrayList<>(); + if (simTypeInfos == null) { + loge("convertAidlSimTypeInfo received NULL simTypeInfos"); + return response; + } + for (android.hardware.radio.config.SimTypeInfo simTypeInfo : simTypeInfos) { + SimTypeInfo info = new SimTypeInfo(); + info.mSupportedSimTypes = simTypeInfo.supportedSimTypes; + info.setCurrentSimType(simTypeInfo.currentSimType); + response.add(info); + } + return response; + } + private static void logd(String log) { Rlog.d("RILUtils", log); }
diff --git a/src/java/com/android/internal/telephony/RadioConfigProxy.java b/src/java/com/android/internal/telephony/RadioConfigProxy.java index 9f34e29..153747b 100644 --- a/src/java/com/android/internal/telephony/RadioConfigProxy.java +++ b/src/java/com/android/internal/telephony/RadioConfigProxy.java
@@ -281,7 +281,11 @@ } public void linkToDeath(long cookie) throws RemoteException { - mService.linkToDeath(this, cookie); + if (mService != null) { + mService.linkToDeath(this, cookie); + } else { + Rlog.w(TAG, "linkToDeath: skipping since mService is null"); + } } public void clear() { @@ -316,7 +320,11 @@ } public void linkToDeath(int cookie) throws RemoteException { - mService.linkToDeath(this, cookie); + if (mService != null) { + mService.linkToDeath(this, cookie); + } else { + Rlog.w(TAG, "linkToDeath: skipping since mService is null"); + } } public void clear() {
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponseAidl.java b/src/java/com/android/internal/telephony/RadioConfigResponseAidl.java index 0a41b11..6142fc2 100644 --- a/src/java/com/android/internal/telephony/RadioConfigResponseAidl.java +++ b/src/java/com/android/internal/telephony/RadioConfigResponseAidl.java
@@ -21,10 +21,10 @@ import android.telephony.PhoneCapability; import com.android.internal.telephony.uicc.IccSlotStatus; +import com.android.internal.telephony.uicc.SimTypeInfo; import com.android.telephony.Rlog; import java.util.ArrayList; -import java.util.Arrays; import java.util.Set; /** @@ -47,8 +47,7 @@ */ @Override public void getHalDeviceCapabilitiesResponse( - android.hardware.radio.RadioResponseInfo info, - boolean modemReducedFeatureSet1) throws RemoteException { + RadioResponseInfo info, boolean modemReducedFeatureSet1) throws RemoteException { // convert hal device capabilities to RadioInterfaceCapabilities RILRequest rr = mRadioConfig.processResponse(info); if (rr != null) { @@ -71,8 +70,7 @@ */ @Override public void getNumOfLiveModemsResponse( - android.hardware.radio.RadioResponseInfo info, byte numOfLiveModems) - throws RemoteException { + RadioResponseInfo info, byte numOfLiveModems) throws RemoteException { RILRequest rr = mRadioConfig.processResponse(info); if (rr != null) { if (info.error == android.hardware.radio.RadioError.NONE) { @@ -93,9 +91,8 @@ */ @Override public void getPhoneCapabilityResponse( - android.hardware.radio.RadioResponseInfo info, - android.hardware.radio.config.PhoneCapability phoneCapability) - throws RemoteException { + RadioResponseInfo info, + android.hardware.radio.config.PhoneCapability phoneCapability) throws RemoteException { RILRequest rr = mRadioConfig.processResponse(info); if (rr != null) { PhoneCapability ret = RILUtils.convertHalPhoneCapability( @@ -118,9 +115,7 @@ */ @Override public void getSimultaneousCallingSupportResponse( - android.hardware.radio.RadioResponseInfo info, - int[] enabledLogicalSlots) - throws RemoteException { + RadioResponseInfo info, int[] enabledLogicalSlots) throws RemoteException { RILRequest rr = mRadioConfig.processResponse(info); if (rr != null) { ArrayList<Integer> ret = RILUtils.primitiveArrayToArrayList(enabledLogicalSlots); @@ -142,7 +137,7 @@ */ @Override public void getSimSlotsStatusResponse( - android.hardware.radio.RadioResponseInfo info, + RadioResponseInfo info, android.hardware.radio.config.SimSlotStatus[] slotStatus) throws RemoteException { RILRequest rr = mRadioConfig.processResponse(info); @@ -166,8 +161,7 @@ * Currently this is being used as the callback for RadioConfig.setNumOfLiveModems() method */ @Override - public void setNumOfLiveModemsResponse( - android.hardware.radio.RadioResponseInfo info) throws RemoteException { + public void setNumOfLiveModemsResponse(RadioResponseInfo info) throws RemoteException { RILRequest rr = mRadioConfig.processResponse(info); if (rr != null) { if (info.error == android.hardware.radio.RadioError.NONE) { @@ -187,8 +181,7 @@ * Response function for IRadioConfig.setPreferredDataModem(). */ @Override - public void setPreferredDataModemResponse( - android.hardware.radio.RadioResponseInfo info) throws RemoteException { + public void setPreferredDataModemResponse(RadioResponseInfo info) throws RemoteException { RILRequest rr = mRadioConfig.processResponse(info); if (rr != null) { if (info.error == android.hardware.radio.RadioError.NONE) { @@ -208,8 +201,7 @@ * Response function for IRadioConfig.setSimSlotsMapping(). */ @Override - public void setSimSlotsMappingResponse( - android.hardware.radio.RadioResponseInfo info) throws RemoteException { + public void setSimSlotsMappingResponse(RadioResponseInfo info) throws RemoteException { RILRequest rr = mRadioConfig.processResponse(info); if (rr != null) { if (info.error == android.hardware.radio.RadioError.NONE) { @@ -225,6 +217,48 @@ } } + /** + * Response function for IRadioConfig.getSimTypeInfo(). + */ + @Override + public void getSimTypeInfoResponse( + RadioResponseInfo info, + android.hardware.radio.config.SimTypeInfo[] simTypeInfo) throws RemoteException { + RILRequest rr = mRadioConfig.processResponse(info); + if (rr != null) { + ArrayList<SimTypeInfo> ret = RILUtils.convertAidlSimTypeInfo(simTypeInfo); + if (info.error == android.hardware.radio.RadioError.NONE) { + // send response + RadioResponse.sendMessageResponse(rr.mResult, ret); + logd(rr, RILUtils.requestToString(rr.mRequest) + " " + ret.toString()); + } else { + rr.onError(info.error, null); + loge(rr, RILUtils.requestToString(rr.mRequest) + " error " + info.error); + } + } else { + loge("getSimTypeInfoResponse: Error " + info.toString()); + } + } + + /** + * Response function for IRadioConfig.setSimTypeResponse(). + */ + @Override + public void setSimTypeResponse(RadioResponseInfo info) throws RemoteException { + RILRequest rr = mRadioConfig.processResponse(info); + if (rr != null) { + if (info.error == android.hardware.radio.RadioError.NONE) { + // send response + RadioResponse.sendMessageResponse(rr.mResult, null); + logd(rr, RILUtils.requestToString(rr.mRequest)); + } else { + rr.onError(info.error, null); + loge(rr, RILUtils.requestToString(rr.mRequest) + " error " + info.error); + } + } else { + loge("setSimTypeResponse: Error " + info.toString()); + } + } private static void logd(String log) { Rlog.d(TAG, log); }
diff --git a/src/java/com/android/internal/telephony/RadioMessagingProxy.java b/src/java/com/android/internal/telephony/RadioMessagingProxy.java index c652284..624c82d 100644 --- a/src/java/com/android/internal/telephony/RadioMessagingProxy.java +++ b/src/java/com/android/internal/telephony/RadioMessagingProxy.java
@@ -20,6 +20,7 @@ import android.telephony.Rlog; import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import java.util.ArrayList; @@ -107,6 +108,7 @@ */ public void acknowledgeLastIncomingCdmaSms(int serial, boolean success, int cause) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { android.hardware.radio.messaging.CdmaSmsAck msg = @@ -147,6 +149,7 @@ * @throws RemoteException */ public void deleteSmsOnRuim(int serial, int index) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mMessagingProxy.deleteSmsOnRuim(serial, index); @@ -176,6 +179,7 @@ * @throws RemoteException */ public void getCdmaBroadcastConfig(int serial) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mMessagingProxy.getCdmaBroadcastConfig(serial); @@ -248,6 +252,7 @@ * @throws RemoteException */ public void sendCdmaSms(int serial, byte[] pdu) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mMessagingProxy.sendCdmaSms(serial, RILUtils.convertToHalCdmaSmsMessageAidl(pdu)); @@ -266,6 +271,7 @@ * @throws RemoteException */ public void sendCdmaSmsExpectMore(int serial, byte[] pdu) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mMessagingProxy.sendCdmaSmsExpectMore( @@ -378,6 +384,7 @@ * @throws RemoteException */ public void setCdmaBroadcastActivation(int serial, boolean activate) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mMessagingProxy.setCdmaBroadcastActivation(serial, activate); @@ -394,6 +401,7 @@ */ public void setCdmaBroadcastConfig(int serial, CdmaSmsBroadcastConfigInfo[] configs) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { ArrayList<android.hardware.radio.messaging.CdmaBroadcastSmsConfigInfo> halConfigs = @@ -513,6 +521,7 @@ * @throws RemoteException */ public void writeSmsToRuim(int serial, int status, byte[] pdu) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { android.hardware.radio.messaging.CdmaSmsWriteArgs args =
diff --git a/src/java/com/android/internal/telephony/RadioModemProxy.java b/src/java/com/android/internal/telephony/RadioModemProxy.java index cdcbcc0..bc19d55 100644 --- a/src/java/com/android/internal/telephony/RadioModemProxy.java +++ b/src/java/com/android/internal/telephony/RadioModemProxy.java
@@ -19,6 +19,8 @@ import android.os.RemoteException; import android.telephony.Rlog; +import com.android.internal.telephony.flags.Flags; + /** * A holder for IRadioModem. * Use getAidl to get IRadioModem and call the AIDL implementations of the HAL APIs. @@ -195,6 +197,7 @@ * @throws RemoteException */ public void nvReadItem(int serial, int itemId) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mModemProxy.nvReadItem(serial, itemId); @@ -210,6 +213,7 @@ * @throws RemoteException */ public void nvResetConfig(int serial, int resetType) throws RemoteException { + if (Flags.cleanupCdma() && resetType != 1) return; if (isEmpty()) return; if (isAidl()) { mModemProxy.nvResetConfig(serial, RILUtils.convertToHalResetNvTypeAidl(resetType)); @@ -225,6 +229,7 @@ * @throws RemoteException */ public void nvWriteCdmaPrl(int serial, byte[] prl) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mModemProxy.nvWriteCdmaPrl(serial, prl); @@ -241,6 +246,7 @@ * @throws RemoteException */ public void nvWriteItem(int serial, int itemId, String itemValue) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { android.hardware.radio.modem.NvWriteItem item =
diff --git a/src/java/com/android/internal/telephony/RadioNetworkProxy.java b/src/java/com/android/internal/telephony/RadioNetworkProxy.java index 4acc71a..c4b6f76 100644 --- a/src/java/com/android/internal/telephony/RadioNetworkProxy.java +++ b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
@@ -28,6 +28,8 @@ import android.telephony.Rlog; import android.telephony.SignalThresholdInfo; +import com.android.internal.telephony.flags.Flags; + import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -172,6 +174,7 @@ * @throws RemoteException */ public void getCdmaRoamingPreference(int serial) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mNetworkProxy.getCdmaRoamingPreference(serial); @@ -431,6 +434,7 @@ * @throws RemoteException */ public void setCdmaRoamingPreference(int serial, int cdmaRoamingType) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mNetworkProxy.setCdmaRoamingPreference(serial, cdmaRoamingType); @@ -979,4 +983,60 @@ } // Only supported on AIDL. } + + /** + * Set the non-terrestrial PLMN with lower priority than terrestrial networks. + * + * @param serial Serial number of request. + * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use + * this information to determine the relevant carrier. + * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks + * supported by user subscription. + * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite + * PLMNs that are not supported by the carrier and make sure not to + * attach to them. + */ + public void setSatellitePlmn(int serial, int simSlot, List<String> carrierPlmnList, + List<String> allSatellitePlmnList) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + String[] carrierPlmnArray = carrierPlmnList.toArray(new String[0]); + String[] allSatellitePlmnArray = allSatellitePlmnList.toArray(new String[0]); + mNetworkProxy.setSatellitePlmn(serial, simSlot, carrierPlmnArray, + allSatellitePlmnArray); + } + // Only supported on AIDL. + } + + /** + * Enable or disable satellite in the cellular modem associated with a carrier. + * + * @param serial Serial number of request. + * @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use + * this information to determine the relevant carrier. + * @param satelliteEnabled {@code true} to enable satellite, {@code false} to disable satellite. + */ + public void setSatelliteEnabledForCarrier(int serial, int simSlot, + boolean satelliteEnabled) throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.setSatelliteEnabledForCarrier(serial, simSlot, satelliteEnabled); + } + // Only supported on AIDL. + } + + /** + * Check whether satellite is enabled in the cellular modem associated with a carrier. + * + * @param serial Serial number of request. + * @param simSlot Indicates the SIM slot to which this API will be applied. + */ + public void isSatelliteEnabledForCarrier(int serial, int simSlot) + throws RemoteException { + if (isEmpty()) return; + if (isAidl()) { + mNetworkProxy.isSatelliteEnabledForCarrier(serial, simSlot); + } + // Only supported on AIDL. + } }
diff --git a/src/java/com/android/internal/telephony/RadioSimProxy.java b/src/java/com/android/internal/telephony/RadioSimProxy.java index 1c864fe..9316ea4 100644 --- a/src/java/com/android/internal/telephony/RadioSimProxy.java +++ b/src/java/com/android/internal/telephony/RadioSimProxy.java
@@ -21,6 +21,7 @@ import android.telephony.ImsiEncryptionInfo; import android.telephony.Rlog; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState; import com.android.internal.telephony.uicc.SimPhonebookRecord; @@ -168,6 +169,7 @@ * @throws RemoteException */ public void getCdmaSubscription(int serial) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mSimProxy.getCdmaSubscription(serial); @@ -182,6 +184,7 @@ * @throws RemoteException */ public void getCdmaSubscriptionSource(int serial) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mSimProxy.getCdmaSubscriptionSource(serial); @@ -631,6 +634,7 @@ * @throws RemoteException */ public void setCdmaSubscriptionSource(int serial, int cdmaSub) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mSimProxy.setCdmaSubscriptionSource(serial, cdmaSub);
diff --git a/src/java/com/android/internal/telephony/RadioVoiceProxy.java b/src/java/com/android/internal/telephony/RadioVoiceProxy.java index e57a61d..d85017b 100644 --- a/src/java/com/android/internal/telephony/RadioVoiceProxy.java +++ b/src/java/com/android/internal/telephony/RadioVoiceProxy.java
@@ -21,6 +21,8 @@ import android.telephony.Rlog; import android.telephony.emergency.EmergencyNumber; +import com.android.internal.telephony.flags.Flags; + import java.util.ArrayList; /** @@ -488,6 +490,7 @@ * @throws RemoteException */ public void sendCdmaFeatureCode(int serial, String featureCode) throws RemoteException { + if (Flags.cleanupCdma()) return; if (isEmpty()) return; if (isAidl()) { mVoiceProxy.sendCdmaFeatureCode(serial, featureCode);
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java index 2f2b62a..88b9958 100644 --- a/src/java/com/android/internal/telephony/SMSDispatcher.java +++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -20,6 +20,7 @@ import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED; import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED; +import static com.android.internal.telephony.SmsDispatchersController.PendingRequest; import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE; import android.annotation.UserIdInt; @@ -824,6 +825,9 @@ SmsResponse smsResponse = new SmsResponse(messageRef, null /* ackPdu */, NO_ERROR_CODE, tracker.mMessageId); + if (Flags.temporaryFailuresInCarrierMessagingService()) { + tracker.mResultCodeFromCarrierMessagingService = result; + } switch (result) { case CarrierMessagingService.SEND_STATUS_OK: @@ -835,10 +839,34 @@ smsResponse, null /* exception*/))); break; - case CarrierMessagingService.SEND_STATUS_ERROR: - Rlog.d(TAG, "processSendSmsResponse: Sending SMS by CarrierMessagingService" - + " failed. " - + SmsController.formatCrossStackMessageId(tracker.mMessageId)); + case CarrierMessagingService.SEND_STATUS_ERROR: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NULL_PDU: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NO_SERVICE: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE: // fall through + case CarrierMessagingService + .SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED: // fall through + case CarrierMessagingService + .SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_NETWORK_REJECT: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_ARGUMENTS: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_STATE: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_SMS_FORMAT: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_NETWORK_ERROR: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_ENCODING_ERROR: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_CANCELLED: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED: // fall through + case CarrierMessagingService + .SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY: // fall through + case CarrierMessagingService.SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED: // fall through + Rlog.d( + TAG, + "processSendSmsResponse: Sending SMS by CarrierMessagingService" + + " failed. " + + SmsController.formatCrossStackMessageId(tracker.mMessageId)); sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE, new AsyncResult(tracker, smsResponse, new CommandException(CommandException.Error.GENERIC_FAILURE)))); @@ -857,6 +885,55 @@ } } + private int toSmsManagerResultForSendSms(int carrierMessagingServiceResult) { + switch (carrierMessagingServiceResult) { + case CarrierMessagingService.SEND_STATUS_OK: + return Activity.RESULT_OK; + case CarrierMessagingService.SEND_STATUS_ERROR: + return SmsManager.RESULT_RIL_GENERIC_ERROR; + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE: + return SmsManager.RESULT_ERROR_GENERIC_FAILURE; + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NULL_PDU: + return SmsManager.RESULT_ERROR_NULL_PDU; + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NO_SERVICE: + return SmsManager.RESULT_ERROR_NO_SERVICE; + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED: + return SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE: + return SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED: + return SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED; + case CarrierMessagingService.SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED: + return SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED; + case CarrierMessagingService.SEND_STATUS_RESULT_NETWORK_REJECT: + return SmsManager.RESULT_NETWORK_REJECT; + case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_ARGUMENTS: + return SmsManager.RESULT_INVALID_ARGUMENTS; + case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_STATE: + return SmsManager.RESULT_INVALID_STATE; + case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_SMS_FORMAT: + return SmsManager.RESULT_INVALID_SMS_FORMAT; + case CarrierMessagingService.SEND_STATUS_RESULT_NETWORK_ERROR: + return SmsManager.RESULT_NETWORK_ERROR; + case CarrierMessagingService.SEND_STATUS_RESULT_ENCODING_ERROR: + return SmsManager.RESULT_ENCODING_ERROR; + case CarrierMessagingService.SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS: + return SmsManager.RESULT_INVALID_SMSC_ADDRESS; + case CarrierMessagingService.SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED: + return SmsManager.RESULT_OPERATION_NOT_ALLOWED; + case CarrierMessagingService.SEND_STATUS_RESULT_CANCELLED: + return SmsManager.RESULT_CANCELLED; + case CarrierMessagingService.SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED: + return SmsManager.RESULT_REQUEST_NOT_SUPPORTED; + case CarrierMessagingService.SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY: + return SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY; + case CarrierMessagingService.SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED: + return SmsManager.RESULT_SMS_SEND_RETRY_FAILED; + default: + return SmsManager.RESULT_ERROR_GENERIC_FAILURE; + } + } + /** * Use the carrier messaging service to send a multipart text SMS. */ @@ -1016,8 +1093,8 @@ */ protected void notifySmsSentFailedToEmergencyStateTracker(SmsTracker tracker, boolean isOverIms) { - mSmsDispatchersController.notifySmsSent(tracker.mDestAddress, tracker.mMessageId, - isOverIms, true /*isLastSmsPart*/, false /*success*/); + mSmsDispatchersController.notifySmsSent(tracker, isOverIms, + true /*isLastSmsPart*/, false /*success*/); } /** @@ -1052,9 +1129,8 @@ } tracker.onSent(mContext); mPhone.notifySmsSent(tracker.mDestAddress); - mSmsDispatchersController.notifySmsSent( - tracker.mDestAddress, tracker.mMessageId, false, - tracker.isSinglePartOrLastPart(), true /*success*/); + mSmsDispatchersController.notifySmsSent(tracker, false, + tracker.isSinglePartOrLastPart(), true /*success*/); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, @@ -1084,10 +1160,20 @@ + SmsController.formatCrossStackMessageId(tracker.mMessageId)); } - int ss = mPhone.getServiceState().getState(); - int error = rilErrorToSmsManagerResult( - ((CommandException) (ar.exception)).getCommandError(), tracker); + int error; + if (Flags.temporaryFailuresInCarrierMessagingService() + && tracker.mResultCodeFromCarrierMessagingService + != CarrierMessagingService.SEND_STATUS_OK) { + error = + toSmsManagerResultForSendSms( + tracker.mResultCodeFromCarrierMessagingService); + } else { + error = + rilErrorToSmsManagerResult( + ((CommandException) (ar.exception)).getCommandError(), tracker); + } + int ss = mPhone.getServiceState().getState(); if (tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) { // This is retry after failure over IMS but voice is not available. // Set retry to max allowed, so no retry is sent and cause @@ -1399,7 +1485,7 @@ @UnsupportedAppUsage protected void sendData(String callingPackage, int callingUser, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, - boolean isForVvm) { + boolean isForVvm, long uniqueMessageId) { int messageRef = nextMessageRef(); SmsMessageBase.SubmitPduBase pdu = getSubmitPdu( scAddr, destAddr, destPort, data, (deliveryIntent != null), messageRef); @@ -1408,7 +1494,8 @@ SmsTracker tracker = getSmsTracker(callingPackage, callingUser, map, sentIntent, deliveryIntent, getFormat(), null /*messageUri*/, false /*expectMore*/, null /*fullMessageText*/, false /*isText*/, - true /*persistMessage*/, isForVvm, 0L /* messageId */, messageRef); + true /*persistMessage*/, isForVvm, 0L /* messageId */, messageRef, + uniqueMessageId); if (!sendSmsByCarrierApp(true /* isDataSms */, tracker)) { sendSubmitPdu(tracker); @@ -1526,7 +1613,7 @@ long messageId) { sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg, callingUser, persistMessage, priority, expectMore, validityPeriod, isForVvm, - messageId, false); + messageId, false, PendingRequest.getNextUniqueMessageId()); } /** @@ -1634,7 +1721,7 @@ PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg, int callingUser, boolean persistMessage, int priority, boolean expectMore, int validityPeriod, boolean isForVvm, - long messageId, boolean skipShortCodeCheck) { + long messageId, boolean skipShortCodeCheck, long uniqueMessageId) { Rlog.d(TAG, "sendText id: " + SmsController.formatCrossStackMessageId(messageId)); int messageRef = nextMessageRef(); SmsMessageBase.SubmitPduBase pdu = getSubmitPdu( @@ -1645,7 +1732,7 @@ SmsTracker tracker = getSmsTracker(callingPkg, callingUser, map, sentIntent, deliveryIntent, getFormat(), messageUri, expectMore, text, true /*isText*/, persistMessage, priority, validityPeriod, isForVvm, messageId, messageRef, - skipShortCodeCheck); + skipShortCodeCheck, uniqueMessageId); if (!sendSmsByCarrierApp(false /* isDataSms */, tracker)) { sendSubmitPdu(tracker); @@ -1827,7 +1914,7 @@ ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg, int callingUser, boolean persistMessage, int priority, boolean expectMore, - int validityPeriod, long messageId) { + int validityPeriod, long messageId, long uniqueMessageId) { final String fullMessageText = getMultipartMessageText(parts); int refNumber = getNextConcatenatedRef() & 0x00FF; int encoding = SmsConstants.ENCODING_UNKNOWN; @@ -1890,7 +1977,7 @@ smsHeader, encoding, sentIntent, deliveryIntent, (i == (msgCount - 1)), unsentPartCount, anyPartFailed, messageUri, fullMessageText, priority, expectMore, validityPeriod, messageId, - messageRef); + messageRef, uniqueMessageId); if (trackers[i] == null) { triggerSentIntentForFailure(sentIntents); return; @@ -1926,7 +2013,7 @@ int encoding, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, String fullMessageText, int priority, boolean expectMore, int validityPeriod, - long messageId, int messageRef) { + long messageId, int messageRef, long uniqueMessageId) { if (isCdmaMo()) { UserData uData = new UserData(); uData.payloadStr = message; @@ -1956,7 +2043,7 @@ deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader, (!lastPart || expectMore), fullMessageText, /*isText*/ true, /*persistMessage*/ true, priority, validityPeriod, /* isForVvm */ - false, messageId, messageRef, false); + false, messageId, messageRef, false, uniqueMessageId); } else { Rlog.e(TAG, "CdmaSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned " + "null " + SmsController.formatCrossStackMessageId(messageId)); @@ -1975,7 +2062,7 @@ messageUri, smsHeader, (!lastPart || expectMore), fullMessageText, /*isText*/ true, /*persistMessage*/ false, priority, validityPeriod, /* isForVvm */ - false, messageId, messageRef, false); + false, messageId, messageRef, false, uniqueMessageId); } else { Rlog.e(TAG, "GsmSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned " + "null " + SmsController.formatCrossStackMessageId(messageId)); @@ -2119,7 +2206,7 @@ } } - if (mTelephonyManager.isEmergencyNumber(trackers[0].mDestAddress)) { + if (mPhone.hasCalling() && mTelephonyManager.isEmergencyNumber(trackers[0].mDestAddress)) { new AsyncEmergencyContactNotifier(mContext).execute(); } } @@ -2488,10 +2575,14 @@ public final long mMessageId; + // A CarrierMessagingService result code to be returned to the caller. + public int mResultCodeFromCarrierMessagingService; + private Boolean mIsFromDefaultSmsApplication; private int mCarrierId; private boolean mSkipShortCodeDestAddrCheck; + public final long mUniqueMessageId; // SMS anomaly uuid -- unexpected error from RIL private final UUID mAnomalyUnexpectedErrorFromRilUUID = UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886"); @@ -2502,7 +2593,8 @@ SmsHeader smsHeader, boolean expectMore, String fullMessageText, int subId, boolean isText, boolean persistMessage, int userId, int priority, int validityPeriod, boolean isForVvm, long messageId, int carrierId, - int messageRef, boolean skipShortCodeDestAddrCheck) { + int messageRef, boolean skipShortCodeDestAddrCheck, + long uniqueMessageId) { mData = data; mSentIntent = sentIntent; mDeliveryIntent = deliveryIntent; @@ -2529,6 +2621,28 @@ mMessageId = messageId; mCarrierId = carrierId; mSkipShortCodeDestAddrCheck = skipShortCodeDestAddrCheck; + mUniqueMessageId = uniqueMessageId; + mResultCodeFromCarrierMessagingService = CarrierMessagingService.SEND_STATUS_OK; + } + + @VisibleForTesting + public SmsTracker(String destAddr, long messageId) { + mData = null; + mSentIntent = null; + mDeliveryIntent = null; + mAppInfo = null; + mDestAddress = destAddr; + mUsesImsServiceForIms = false; + mSmsHeader = null; + mMessageId = messageId; + mUserId = 0; + mPriority = 0; + mValidityPeriod = 0; + mIsForVvm = false; + mCarrierId = 0; + mSkipShortCodeDestAddrCheck = false; + mUniqueMessageId = 0; + mResultCodeFromCarrierMessagingService = CarrierMessagingService.SEND_STATUS_OK; } public HashMap<String, Object> getData() { @@ -2624,6 +2738,14 @@ } /** + * Returns the flag specifying whether any part of this {@link SmsTracker} failed to send + * or not. + */ + protected boolean isAnyPartFailed() { + return mAnyPartFailed != null && mAnyPartFailed.get(); + } + + /** * Persist a sent SMS if required: * 1. It is a text message * 2. SmsApplication tells us to persist: sent from apps that are not default-SMS app or @@ -2820,7 +2942,8 @@ AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader, boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage, int priority, int validityPeriod, boolean isForVvm, - long messageId, int messageRef, boolean skipShortCodeCheck) { + long messageId, int messageRef, boolean skipShortCodeCheck, + long uniqueMessageId) { if (!Flags.smsMmsDeliverBroadcastsRedirectToMainUser()) { callingUser = UserHandle.getUserHandleForUid(Binder.getCallingUid()).getIdentifier(); } @@ -2841,31 +2964,33 @@ unsentPartCount, anyPartFailed, messageUri, smsHeader, expectMore, fullMessageText, getSubId(), isText, persistMessage, callingUser, priority, validityPeriod, isForVvm, messageId, mPhone.getCarrierId(), messageRef, - skipShortCodeCheck); + skipShortCodeCheck, uniqueMessageId); } protected SmsTracker getSmsTracker(String callingPackage, int callingUser, HashMap<String, Object> data, PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri, boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage, boolean isForVvm, - long messageId, int messageRef) { + long messageId, int messageRef, long uniqueMessageId) { return getSmsTracker(callingPackage, callingUser , data, sentIntent, deliveryIntent, format, /*unsentPartCount*/ null, /*anyPartFailed*/ null, messageUri, /*smsHeader*/ null, expectMore, fullMessageText, isText, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, - isForVvm, messageId, messageRef, false); + isForVvm, messageId, messageRef, false, uniqueMessageId); } protected SmsTracker getSmsTracker(String callingPackage, int callingUser, HashMap<String, Object> data, PendingIntent sentIntent, PendingIntent deliveryIntent, String format, Uri messageUri, boolean expectMore, String fullMessageText, boolean isText, boolean persistMessage, int priority, int validityPeriod, - boolean isForVvm, long messageId, int messageRef, boolean skipShortCodeCheck) { + boolean isForVvm, long messageId, int messageRef, boolean skipShortCodeCheck, + long uniqueMessageId) { return getSmsTracker(callingPackage, callingUser, data, sentIntent, deliveryIntent, format, /*unsentPartCount*/ null, /*anyPartFailed*/ null, messageUri, /*smsHeader*/ null, expectMore, fullMessageText, isText, persistMessage, priority, - validityPeriod, isForVvm, messageId, messageRef, skipShortCodeCheck); + validityPeriod, isForVvm, messageId, messageRef, skipShortCodeCheck, + uniqueMessageId); } protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java index e7500a2..3309d21 100644 --- a/src/java/com/android/internal/telephony/ServiceStateTracker.java +++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -327,18 +327,10 @@ private boolean mImsRegistrationOnOff = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private boolean mDeviceShuttingDown = false; + private CarrierDisplayNameData mCarrierDisplayNameData = + new CarrierDisplayNameData.Builder().build(); /** Keep track of SPN display rules, so we only broadcast intent if something changes. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private String mCurSpn = null; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private String mCurDataSpn = null; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private String mCurPlmn = null; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private boolean mCurShowPlmn = false; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private boolean mCurShowSpn = false; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @VisibleForTesting public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private int mPrevSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -452,7 +444,7 @@ // Once sub id becomes valid, we need to update the service provider name // displayed on the UI again. The old SPN update intents sent to // MobileSignalController earlier were actually ignored due to invalid sub id. - updateSpnDisplay(); + updateCarrierDisplayName(); } mSubId = curSubId; } @@ -559,12 +551,12 @@ pollState(); // Depends on modem, ServiceState is not necessarily updated, so make sure updating // SPN. - updateSpnDisplay(); + updateCarrierDisplayName(); } else if (action.equals(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) { String lastKnownNetworkCountry = intent.getStringExtra( TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY); if (!mLastKnownNetworkCountry.equals(lastKnownNetworkCountry)) { - updateSpnDisplay(); + updateCarrierDisplayName(); } } } @@ -1245,7 +1237,7 @@ if (mUiccApplication == null || mUiccApplication.getState() != AppState.APPSTATE_READY) { mIsSimReady = false; - updateSpnDisplay(); + updateCarrierDisplayName(); } break; @@ -1424,7 +1416,7 @@ updateOtaspState(); if (mPhone.isPhoneTypeGsm()) { mCdnr.updateEfFromUsim((SIMRecords) mIccRecords); - updateSpnDisplay(); + updateCarrierDisplayName(); } break; @@ -1531,7 +1523,7 @@ case EVENT_IMS_CAPABILITY_CHANGED: if (DBG) log("EVENT_IMS_CAPABILITY_CHANGED"); - updateSpnDisplay(); + updateCarrierDisplayName(); mImsCapabilityChangedRegistrants.notifyRegistrants(); break; @@ -1623,7 +1615,7 @@ mCdnr.updateEfFromRuim((RuimRecords) mIccRecords); updatePhoneObject(); if (mPhone.isPhoneTypeCdma()) { - updateSpnDisplay(); + updateCarrierDisplayName(); } else { RuimRecords ruim = (RuimRecords) mIccRecords; if (ruim != null) { @@ -2761,53 +2753,34 @@ } } - private void notifySpnDisplayUpdate(CarrierDisplayNameData data) { - int subId = mPhone.getSubId(); - // Update ACTION_SERVICE_PROVIDERS_UPDATED if any value changes - if (mSubId != subId - || data.shouldShowPlmn() != mCurShowPlmn - || data.shouldShowSpn() != mCurShowSpn - || !TextUtils.equals(data.getSpn(), mCurSpn) - || !TextUtils.equals(data.getDataSpn(), mCurDataSpn) - || !TextUtils.equals(data.getPlmn(), mCurPlmn)) { + private void notifyCarrierDisplayNameDataChanged() { + final String log = String.format("notifyCarrierDisplayNameDataChanged: " + + "changed sending intent, " + + "rule=%d, CarrierDisplayNameData=%s, subId=%d", + getCarrierNameDisplayBitmask(mSS), + mCarrierDisplayNameData, + mPhone.getSubId()); + mCdnrLogs.log(log); + if (DBG) log(log); - final String log = String.format("updateSpnDisplay: changed sending intent, " - + "rule=%d, showPlmn='%b', plmn='%s', showSpn='%b', spn='%s', " - + "dataSpn='%s', subId='%d'", - getCarrierNameDisplayBitmask(mSS), - data.shouldShowPlmn(), - data.getPlmn(), - data.shouldShowSpn(), - data.getSpn(), - data.getDataSpn(), - subId); - mCdnrLogs.log(log); - if (DBG) log("updateSpnDisplay: " + log); - Intent intent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED); - intent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, data.shouldShowSpn()); - intent.putExtra(TelephonyManager.EXTRA_SPN, data.getSpn()); - intent.putExtra(TelephonyManager.EXTRA_DATA_SPN, data.getDataSpn()); - intent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, data.shouldShowPlmn()); - intent.putExtra(TelephonyManager.EXTRA_PLMN, data.getPlmn()); - SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); - mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); - - if (SubscriptionManager.isValidSubscriptionId(subId)) { - mSubscriptionManagerService.setCarrierName(subId, TextUtils.emptyIfNull( - getCarrierName(data.shouldShowPlmn(), data.getPlmn(), - data.shouldShowSpn(), data.getSpn()))); - } - } - mCurShowSpn = data.shouldShowSpn(); - mCurShowPlmn = data.shouldShowPlmn(); - mCurSpn = data.getSpn(); - mCurDataSpn = data.getDataSpn(); - mCurPlmn = data.getPlmn(); + Intent intent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED); + intent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, mCarrierDisplayNameData.shouldShowSpn()); + intent.putExtra(TelephonyManager.EXTRA_SPN, mCarrierDisplayNameData.getSpn()); + intent.putExtra(TelephonyManager.EXTRA_DATA_SPN, mCarrierDisplayNameData.getDataSpn()); + intent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, mCarrierDisplayNameData.shouldShowPlmn()); + intent.putExtra(TelephonyManager.EXTRA_PLMN, mCarrierDisplayNameData.getPlmn()); + SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); + mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @NonNull - private String getCarrierName(boolean showPlmn, String plmn, boolean showSpn, String spn) { + private String getCarrierName(CarrierDisplayNameData cdnd) { + boolean showPlmn = cdnd.shouldShowPlmn(); + boolean showSpn = cdnd.shouldShowSpn(); + String plmn = cdnd.getPlmn(); + String spn = cdnd.getSpn(); + String carrierName = ""; if (showPlmn) { carrierName = plmn; @@ -2825,29 +2798,34 @@ } else if (showSpn) { carrierName = spn; } - return carrierName; + return TextUtils.emptyIfNull(carrierName); } - private void updateSpnDisplayCdnr() { - log("updateSpnDisplayCdnr+"); - CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData(); - notifySpnDisplayUpdate(data); - log("updateSpnDisplayCdnr-"); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @VisibleForTesting - public void updateSpnDisplay() { - if (mCarrierConfig.getBoolean( - CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL)) { - updateSpnDisplayCdnr(); - } else { - updateSpnDisplayLegacy(); + public void updateCarrierDisplayName() { + final boolean useCdnr = mCarrierConfig.getBoolean( + CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL); + + final CarrierDisplayNameData cdnd = useCdnr + ? mCdnr.getCarrierDisplayNameData() + : getCarrierDisplayNameLegacy(); + + final int subId = mPhone.getSubId(); + + // Avoid sending unnecessary updates + if (subId == mSubId && cdnd.equals(mCarrierDisplayNameData)) return; + + if (SubscriptionManager.isValidSubscriptionId(subId)) { + mSubscriptionManagerService.setCarrierName(subId, getCarrierName(cdnd)); } + + mCarrierDisplayNameData = cdnd; // notify...() relies on the updated value + notifyCarrierDisplayNameDataChanged(); + } - private void updateSpnDisplayLegacy() { - log("updateSpnDisplayLegacy+"); + private @NonNull CarrierDisplayNameData getCarrierDisplayNameLegacy() { + log("getCarrierDisplayNameLegacy+"); String spn = null; String dataSpn = null; @@ -2888,11 +2866,12 @@ .getStringArray(com.android.internal.R.array.wfcSpnFormats); if (voiceIdx < 0 || voiceIdx >= wfcSpnFormats.length) { - loge("updateSpnDisplay: KEY_WFC_SPN_FORMAT_IDX_INT out of bounds: " + voiceIdx); + loge("updateCarrierDisplayName: KEY_WFC_SPN_FORMAT_IDX_INT out of bounds: " + + voiceIdx); voiceIdx = 0; } if (dataIdx < 0 || dataIdx >= wfcSpnFormats.length) { - loge("updateSpnDisplay: KEY_WFC_DATA_SPN_FORMAT_IDX_INT out of bounds: " + loge("updateCarrierDisplayName: KEY_WFC_DATA_SPN_FORMAT_IDX_INT out of bounds: " + dataIdx); dataIdx = 0; } @@ -2926,7 +2905,7 @@ .getStringArray(R.array.crossSimSpnFormats); if (crossSimSpnFormatIdx < 0 || crossSimSpnFormatIdx >= crossSimSpnFormats.length) { - loge("updateSpnDisplay: KEY_CROSS_SIM_SPN_FORMAT_INT out of bounds: " + loge("updateCarrierDisplayName: KEY_CROSS_SIM_SPN_FORMAT_INT out of bounds: " + crossSimSpnFormatIdx); crossSimSpnFormatIdx = 0; } @@ -2971,21 +2950,25 @@ .toString(); noService = true; } - if (DBG) log("updateSpnDisplay: radio is on but out " + - "of service, set plmn='" + plmn + "'"); + if (DBG) { + log("updateCarrierDisplayName: radio is on but out " + + "of service, set plmn='" + plmn + "'"); + } } else if (combinedRegState == ServiceState.STATE_IN_SERVICE) { // In either home or roaming service plmn = mSS.getOperatorAlpha(); showPlmn = !TextUtils.isEmpty(plmn) && ((rule & CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN) == CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN); - if (DBG) log("updateSpnDisplay: rawPlmn = " + plmn); + if (DBG) log("updateCarrierDisplayName: rawPlmn = " + plmn); } else { // Power off state, such as airplane mode, show plmn as null showPlmn = true; plmn = null; - if (DBG) log("updateSpnDisplay: radio is off w/ showPlmn=" - + showPlmn + " plmn=" + plmn); + if (DBG) { + log("updateCarrierDisplayName: radio is off w/ showPlmn=" + + showPlmn + " plmn=" + plmn); + } } // The value of spn/showSpn are same in different scenarios. @@ -2997,7 +2980,7 @@ showSpn = !noService && !TextUtils.isEmpty(spn) && ((rule & CARRIER_NAME_DISPLAY_BITMASK_SHOW_SPN) == CARRIER_NAME_DISPLAY_BITMASK_SHOW_SPN); - if (DBG) log("updateSpnDisplay: rawSpn = " + spn); + if (DBG) log("updateCarrierDisplayName: rawSpn = " + spn); if (!TextUtils.isEmpty(crossSimSpnFormat)) { if (!TextUtils.isEmpty(spn)) { // Show SPN + Cross-SIM Calling If SIM has SPN and SPN display condition @@ -3056,7 +3039,7 @@ // mOperatorAlpha contains the ERI text plmn = mSS.getOperatorAlpha(); - if (DBG) log("updateSpnDisplay: cdma rawPlmn = " + plmn); + if (DBG) log("updateCarrierDisplayName: cdma rawPlmn = " + plmn); showPlmn = plmn != null; @@ -3068,8 +3051,8 @@ // todo: temporary hack; should have a better fix. This is to avoid using operator // name from ServiceState (populated in processIwlanRegistrationInfo()) until // wifi calling is actually enabled - log("updateSpnDisplay: overwriting plmn from " + plmn + " to null as radio " + - "state is off"); + log("updateCarrierDisplayName: overwriting plmn from " + + plmn + " to null as radio " + "state is off"); plmn = null; } @@ -3077,20 +3060,21 @@ plmn = Resources.getSystem().getText(com.android.internal.R.string .lockscreen_carrier_default).toString(); if (DBG) { - log("updateSpnDisplay: radio is on but out of svc, set plmn='" + plmn + "'"); + log("updateCarrierDisplayName: radio is on but out of svc, set plmn='" + + plmn + "'"); } } - } - notifySpnDisplayUpdate(new CarrierDisplayNameData.Builder() + log("getCarrierDisplayNameLegacy-"); + + return new CarrierDisplayNameData.Builder() .setSpn(spn) .setDataSpn(dataSpn) .setShowSpn(showSpn) .setPlmn(plmn) .setShowPlmn(showPlmn) - .build()); - log("updateSpnDisplayLegacy-"); + .build(); } /** @@ -3299,7 +3283,7 @@ mImsRegistrationOnOff = registered; // It's possible ServiceState changes did not trigger SPN display update; we update it here. - updateSpnDisplay(); + updateCarrierDisplayName(); } public void onImsCapabilityChanged() { @@ -3713,11 +3697,11 @@ String eriText = mPhone.getCdmaEriText(); boolean hasEriChanged = !TextUtils.equals(mEriText, eriText); mEriText = eriText; - // Trigger updateSpnDisplay when + // Trigger updateCarrierDisplayName when // 1. Service state is changed. // 2. phone type is Cdma or CdmaLte and ERI text has changed. if (hasChanged || (!mPhone.isPhoneTypeGsm() && hasEriChanged)) { - updateSpnDisplay(); + updateCarrierDisplayName(); } if (hasChanged) { @@ -5279,11 +5263,7 @@ pw.println(" mStartedGprsRegCheck=" + mStartedGprsRegCheck); pw.println(" mReportedGprsNoReg=" + mReportedGprsNoReg); pw.println(" mNotification=" + mNotification); - pw.println(" mCurSpn=" + mCurSpn); - pw.println(" mCurDataSpn=" + mCurDataSpn); - pw.println(" mCurShowSpn=" + mCurShowSpn); - pw.println(" mCurPlmn=" + mCurPlmn); - pw.println(" mCurShowPlmn=" + mCurShowPlmn); + pw.println(" mCarrierDisplayNameData=" + mCarrierDisplayNameData); pw.flush(); pw.println(" mCurrentOtaspMode=" + mCurrentOtaspMode); pw.println(" mRoamingIndicator=" + mRoamingIndicator);
diff --git a/src/java/com/android/internal/telephony/SimResponse.java b/src/java/com/android/internal/telephony/SimResponse.java index 97692a0..12c9a3c 100644 --- a/src/java/com/android/internal/telephony/SimResponse.java +++ b/src/java/com/android/internal/telephony/SimResponse.java
@@ -123,19 +123,34 @@ if (!carrierRestrictions.allowedCarriersPrioritized) { carrierRestrictionDefault = CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED; } - - CarrierRestrictionRules ret = CarrierRestrictionRules.newBuilder().setAllowedCarriers( - RILUtils.convertHalCarrierList( - carrierRestrictions.allowedCarriers)).setExcludedCarriers( - RILUtils.convertHalCarrierList( - carrierRestrictions.excludedCarriers)).setDefaultCarrierRestriction( - carrierRestrictionDefault).setMultiSimPolicy(policy).setCarrierRestrictionStatus( - carrierRestrictions.status).setAllowedCarrierInfo( - RILUtils.convertAidlCarrierInfoList( - carrierRestrictions.allowedCarrierInfoList)).setExcludedCarrierInfo( - RILUtils.convertAidlCarrierInfoList( - carrierRestrictions.excludedCarrierInfoList)).setCarrierLockInfoFeature( - carrierLockInfoSupported).build(); + CarrierRestrictionRules ret = null; + if (carrierLockInfoSupported) { + // In order to support the old API { @link TelephonyManager#getAllowedCarriers() } we + // are parsing the allowedCarrierInfoList to CarrierIdentifier List also along with + // CarrierInfo List + ret = CarrierRestrictionRules.newBuilder().setAllowedCarriers( + RILUtils.convertAidlCarrierInfoListToHalCarrierList( + carrierRestrictions.allowedCarrierInfoList)).setExcludedCarriers( + RILUtils.convertAidlCarrierInfoListToHalCarrierList( + carrierRestrictions.excludedCarrierInfoList)). + setDefaultCarrierRestriction( + carrierRestrictionDefault).setMultiSimPolicy( + policy).setCarrierRestrictionStatus( + carrierRestrictions.status).setAllowedCarrierInfo( + RILUtils.convertAidlCarrierInfoList( + carrierRestrictions.allowedCarrierInfoList)).setExcludedCarrierInfo( + RILUtils.convertAidlCarrierInfoList( + carrierRestrictions.excludedCarrierInfoList)).setCarrierLockInfoFeature( + true).build(); + } else { + ret = CarrierRestrictionRules.newBuilder().setAllowedCarriers( + RILUtils.convertHalCarrierList( + carrierRestrictions.allowedCarriers)).setExcludedCarriers( + RILUtils.convertHalCarrierList( + carrierRestrictions.excludedCarriers)).setDefaultCarrierRestriction( + carrierRestrictionDefault).setMultiSimPolicy( + policy).build(); + } if (responseInfo.error == RadioError.NONE) { RadioResponse.sendMessageResponse(rr.mResult, ret); }
diff --git a/src/java/com/android/internal/telephony/SimultaneousCallingTracker.java b/src/java/com/android/internal/telephony/SimultaneousCallingTracker.java index 0b427f8..12cc2fa 100644 --- a/src/java/com/android/internal/telephony/SimultaneousCallingTracker.java +++ b/src/java/com/android/internal/telephony/SimultaneousCallingTracker.java
@@ -366,9 +366,8 @@ SubscriptionInfo subInfo = SubscriptionManagerService.getInstance().getSubscriptionInfo(subId); - if (mFeatureFlags.dataOnlyCellularService() && - subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID && subInfo != null && - subInfo.getServiceCapabilities() + if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID && subInfo != null + && subInfo.getServiceCapabilities() .contains(SubscriptionManager.SERVICE_CAPABILITY_VOICE)) { Log.v(LOG_TAG, "generateVoiceCapablePhoneMapBasedOnUserAssociation: adding " + "phoneId = " + phone.getPhoneId());
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java index e3c409d..051fbbd 100644 --- a/src/java/com/android/internal/telephony/SmsController.java +++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -276,6 +276,7 @@ } UserHandle callingUser = Binder.getCallingUserHandle(); + Rlog.d(LOG_TAG, "sendTextForSubscriber caller=" + callingPackage); if (skipFdnCheck || skipShortCodeCheck) { @@ -1194,8 +1195,9 @@ } // Skip FDN check for emergency numbers + if (!TelephonyCapabilities.supportsTelephonyCalling(mFlags, mContext)) return false; TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); - if (tm.isEmergencyNumber(destAddr)) { + if (tm != null && tm.isEmergencyNumber(destAddr)) { return false; }
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java index 7795955..373ee4a 100644 --- a/src/java/com/android/internal/telephony/SmsDispatchersController.java +++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -45,12 +45,14 @@ import android.telephony.ServiceState; import android.telephony.SmsManager; import android.telephony.SmsMessage; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.emergency.EmergencyNumber; import android.telephony.satellite.SatelliteManager; import android.text.TextUtils; import com.android.ims.ImsManager; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; @@ -75,6 +77,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; /** * @@ -136,12 +139,12 @@ /** Time at which the current PARTIAL_SEGMENT_WAIT_DURATION timer was started */ private long mCurrentWaitStartTime = INVALID_TIME; - private SMSDispatcher mCdmaDispatcher; + private SMSDispatcher mCdmaDispatcher = null; private SMSDispatcher mGsmDispatcher; private ImsSmsDispatcher mImsSmsDispatcher; private GsmInboundSmsHandler mGsmInboundSmsHandler; - private CdmaInboundSmsHandler mCdmaInboundSmsHandler; + private CdmaInboundSmsHandler mCdmaInboundSmsHandler = null; private Phone mPhone; /** Outgoing message counter. Shared by all dispatchers. */ @@ -220,6 +223,7 @@ public static final int TYPE_TEXT = 2; public static final int TYPE_MULTIPART_TEXT = 3; public static final int TYPE_RETRY_SMS = 4; + private static final AtomicLong sNextUniqueMessageId = new AtomicLong(0); public final int type; public final SMSDispatcher.SmsTracker tracker; @@ -242,13 +246,16 @@ public final int validityPeriod; public final long messageId; public final boolean skipShortCodeCheck; + public final long uniqueMessageId; + public final boolean isMtSmsPolling; public PendingRequest(int type, SMSDispatcher.SmsTracker tracker, String callingPackage, int callingUser, String destAddr, String scAddr, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents, boolean isForVvm, byte[] data, int destPort, ArrayList<String> texts, Uri messageUri, boolean persistMessage, int priority, boolean expectMore, - int validityPeriod, long messageId, boolean skipShortCodeCheck) { + int validityPeriod, long messageId, boolean skipShortCodeCheck, + boolean isMtSmsPolling) { this.type = type; this.tracker = tracker; this.callingPackage = callingPackage; @@ -270,6 +277,17 @@ this.validityPeriod = validityPeriod; this.messageId = messageId; this.skipShortCodeCheck = skipShortCodeCheck; + if (tracker != null) { + this.uniqueMessageId = tracker.mUniqueMessageId; + } else { + this.uniqueMessageId = getNextUniqueMessageId(); + } + this.isMtSmsPolling = isMtSmsPolling; + } + + public static long getNextUniqueMessageId() { + return sNextUniqueMessageId.getAndUpdate( + id -> ((id + 1) % Long.MAX_VALUE)); } } @@ -391,11 +409,14 @@ // Create dispatchers, inbound SMS handlers and // broadcast undelivered messages in raw table. mImsSmsDispatcher = new ImsSmsDispatcher(phone, this, ImsManager::getConnector); - mCdmaDispatcher = new CdmaSMSDispatcher(phone, this); mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), storageMonitor, phone, looper, mFeatureFlags); - mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), - storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher, looper, mFeatureFlags); + if (!mFeatureFlags.cleanupCdma()) { + mCdmaDispatcher = new CdmaSMSDispatcher(phone, this); + mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), + storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher, looper, + mFeatureFlags); + } mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler); SmsBroadcastUndelivered.initialize(phone.getContext(), mGsmInboundSmsHandler, mCdmaInboundSmsHandler, mFeatureFlags); @@ -437,9 +458,9 @@ mCi.unregisterForImsNetworkStateChanged(this); mPhone.unregisterForServiceStateChanged(this); mGsmDispatcher.dispose(); - mCdmaDispatcher.dispose(); + if (mCdmaDispatcher != null) mCdmaDispatcher.dispose(); mGsmInboundSmsHandler.dispose(); - mCdmaInboundSmsHandler.dispose(); + if (mCdmaInboundSmsHandler != null) mCdmaInboundSmsHandler.dispose(); // Cancels the domain selection request if it's still in progress. finishDomainSelection(mDscHolder); finishDomainSelection(mEmergencyDscHolder); @@ -559,7 +580,7 @@ default: if (isCdmaMo()) { - mCdmaDispatcher.handleMessage(msg); + if (mCdmaDispatcher != null) mCdmaDispatcher.handleMessage(msg); } else { mGsmDispatcher.handleMessage(msg); } @@ -641,7 +662,8 @@ private void handlePartialSegmentTimerExpiry(long waitTimerStart) { if (mGsmInboundSmsHandler.getCurrentState().getName().equals("WaitingState") - || mCdmaInboundSmsHandler.getCurrentState().getName().equals("WaitingState")) { + || (mCdmaInboundSmsHandler != null + && mCdmaInboundSmsHandler.getCurrentState().getName().equals("WaitingState"))) { logd("handlePartialSegmentTimerExpiry: ignoring timer expiry as InboundSmsHandler is" + " in WaitingState"); return; @@ -772,7 +794,7 @@ + ", format=" + format + "to mGsmInboundSmsHandler"); mGsmInboundSmsHandler.sendMessage( InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, token, ar); - } else if (format.equals(SmsConstants.FORMAT_3GPP2)) { + } else if (format.equals(SmsConstants.FORMAT_3GPP2) && mCdmaInboundSmsHandler != null) { Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg + ", format=" + format + "to mCdmaInboundSmsHandler"); mCdmaInboundSmsHandler.sendMessage( @@ -811,8 +833,7 @@ if (!tracker.mUsesImsServiceForIms) { if (isSmsDomainSelectionEnabled()) { - TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); - boolean isEmergency = tm.isEmergencyNumber(tracker.mDestAddress); + boolean isEmergency = isEmergencyNumber(tracker.mDestAddress); // This may be invoked by another thread, so this operation is posted and // handled through the execution flow of SmsDispatchersController. SomeArgs args = SomeArgs.obtain(); @@ -821,7 +842,7 @@ null, UserHandle.USER_NULL, null, null, null, null, false, null, 0, null, null, false, - 0, false, 0, 0L, false); + 0, false, 0, 0L, false, false); args.arg3 = "sendRetrySms"; sendMessage(obtainMessage(EVENT_REQUEST_DOMAIN_SELECTION, args)); return; @@ -867,8 +888,8 @@ // should never come here... Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!"); tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE); - notifySmsSent(tracker.mDestAddress, tracker.mMessageId, - !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/); + notifySmsSent(tracker, !retryUsingImsService, + true /*isLastSmsPart*/, false /*success*/); return; } String scAddr = (String) map.get("scAddr"); @@ -876,8 +897,8 @@ if (destAddr == null) { Rlog.e(TAG, "sendRetrySms failed due to null destAddr"); tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE); - notifySmsSent(tracker.mDestAddress, tracker.mMessageId, - !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/); + notifySmsSent(tracker, !retryUsingImsService, + true /*isLastSmsPart*/, false /*success*/); return; } @@ -918,8 +939,8 @@ + "scAddr: %s, " + "destPort: %s", scAddr, map.get("destPort"))); tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE); - notifySmsSent(tracker.mDestAddress, tracker.mMessageId, - !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/); + notifySmsSent(tracker, !retryUsingImsService, + true /*isLastSmsPart*/, false /*success*/); return; } // replace old smsc and pdu with newly encoded ones @@ -981,6 +1002,7 @@ * @return true if Cdma format should be used for MO SMS, false otherwise. */ protected boolean isCdmaMo() { + if (mFeatureFlags.cleanupCdma()) return false; if (!isIms()) { // IMS is not registered, use Voice technology to determine SMS format. return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()); @@ -996,6 +1018,7 @@ * @return true if format given is CDMA format, false otherwise. */ public boolean isCdmaFormat(String format) { + if (mFeatureFlags.cleanupCdma()) return false; return (mCdmaDispatcher.getFormat().equals(format)); } @@ -1201,8 +1224,7 @@ private void handleSmsSentCompletedUsingDomainSelection(@NonNull String destAddr, long messageId, boolean success, boolean isOverIms, boolean isLastSmsPart) { if (mEmergencyStateTracker != null) { - TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); - if (tm.isEmergencyNumber(destAddr)) { + if (isEmergencyNumber(destAddr)) { mEmergencyStateTracker.endSms(String.valueOf(messageId), success, isOverIms ? NetworkRegistrationInfo.DOMAIN_PS : NetworkRegistrationInfo.DOMAIN_CS, @@ -1214,11 +1236,12 @@ /** * Called when MO SMS is sent. */ - protected void notifySmsSent(@NonNull String destAddr, long messageId, boolean isOverIms, - boolean isLastSmsPart, boolean success) { - notifySmsSentToEmergencyStateTracker( - destAddr, messageId, isOverIms, isLastSmsPart, success); - notifySmsSentToDatagramDispatcher(messageId, success); + protected void notifySmsSent(@NonNull SMSDispatcher.SmsTracker tracker, + boolean isOverIms, boolean isLastSmsPart, boolean success) { + notifySmsSentToEmergencyStateTracker(tracker.mDestAddress, + tracker.mMessageId, isOverIms, isLastSmsPart, success); + notifySmsSentToDatagramDispatcher(tracker.mUniqueMessageId, + tracker.isSinglePartOrLastPart(), success && !tracker.isAnyPartFailed()); } /** @@ -1238,9 +1261,12 @@ } } - private void notifySmsSentToDatagramDispatcher(long messageId, boolean success) { - if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) { - DatagramDispatcher.getInstance().onSendSmsDone(mPhone.getSubId(), messageId, success); + private void notifySmsSentToDatagramDispatcher( + long messageId, boolean isLastSmsPart, boolean success) { + if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone) + && isLastSmsPart) { + DatagramDispatcher.getInstance().onSendSmsDone( + mPhone.getSubId(), messageId, success); } } @@ -1251,8 +1277,7 @@ */ private void handleSmsReceivedViaIms(@Nullable String origAddr) { if (mEmergencyStateTracker != null) { - TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); - if (origAddr != null && tm.isEmergencyNumber(origAddr)) { + if (origAddr != null && isEmergencyNumber(origAddr)) { mEmergencyStateTracker.onEmergencySmsReceived(); } } @@ -1270,7 +1295,9 @@ private boolean isTestEmergencyNumber(String number) { try { + if (!mPhone.hasCalling()) return false; TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + if (tm == null) return false; Map<Integer, List<EmergencyNumber>> eMap = tm.getEmergencyNumberList(); return eMap.values().stream().flatMap(Collection::stream).anyMatch(eNumber -> eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST) @@ -1402,15 +1429,18 @@ if (domain == NetworkRegistrationInfo.DOMAIN_PS) { mImsSmsDispatcher.sendData(request.callingPackage, request.callingUser, request.destAddr, request.scAddr, request.destPort, request.data, - request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm); + request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm, + request.uniqueMessageId); } else if (isCdmaMo(domain)) { mCdmaDispatcher.sendData(request.callingPackage, request.callingUser, request.destAddr, request.scAddr, request.destPort, request.data, - request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm); + request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm, + request.uniqueMessageId); } else { mGsmDispatcher.sendData(request.callingPackage, request.callingUser, request.destAddr, request.scAddr, request.destPort, request.data, - request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm); + request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm, + request.uniqueMessageId); } } @@ -1430,7 +1460,7 @@ request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, /*request.expectMore*/ false, request.validityPeriod, request.isForVvm, request.messageId, - request.skipShortCodeCheck); + request.skipShortCodeCheck, request.uniqueMessageId); } else { if (isCdmaMo(domain)) { mCdmaDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0), @@ -1438,14 +1468,14 @@ request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, request.expectMore, request.validityPeriod, request.isForVvm, request.messageId, - request.skipShortCodeCheck); + request.skipShortCodeCheck, request.uniqueMessageId); } else { mGsmDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0), request.sentIntents.get(0), request.deliveryIntents.get(0), request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, request.expectMore, request.validityPeriod, request.isForVvm, request.messageId, - request.skipShortCodeCheck); + request.skipShortCodeCheck, request.uniqueMessageId); } } } @@ -1465,25 +1495,29 @@ request.sentIntents, request.deliveryIntents, request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, false /*request.expectMore*/, request.validityPeriod, - request.messageId); + request.messageId, request.uniqueMessageId); } else { if (isCdmaMo(domain)) { mCdmaDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts, request.sentIntents, request.deliveryIntents, request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, request.expectMore, request.validityPeriod, - request.messageId); + request.messageId, request.uniqueMessageId); } else { mGsmDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts, request.sentIntents, request.deliveryIntents, request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, request.expectMore, request.validityPeriod, - request.messageId); + request.messageId, request.uniqueMessageId); } } } - private void triggerSentIntentForFailure(@NonNull PendingIntent sentIntent) { + private void triggerSentIntentForFailure(PendingIntent sentIntent) { + if (sentIntent == null) { + logd("sentIntent is null"); + return; + } try { sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); } catch (CanceledException e) { @@ -1491,7 +1525,11 @@ } } - private void triggerSentIntentForFailure(@NonNull List<PendingIntent> sentIntents) { + private void triggerSentIntentForFailure(List<PendingIntent> sentIntents) { + if (sentIntents == null) { + logd("sentIntents is null"); + return; + } for (PendingIntent sentIntent : sentIntents) { triggerSentIntentForFailure(sentIntent); } @@ -1598,20 +1636,23 @@ destAddr, scAddr, asArrayList(sentIntent), asArrayList(deliveryIntent), isForVvm, data, destPort, null, null, false, 0, false, 0, - 0L, false), + 0L, false, false), "sendData"); return; } if (mImsSmsDispatcher.isAvailable()) { mImsSmsDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, - data, sentIntent, deliveryIntent, isForVvm); + data, sentIntent, deliveryIntent, isForVvm, + PendingRequest.getNextUniqueMessageId()); } else if (isCdmaMo()) { mCdmaDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, data, - sentIntent, deliveryIntent, isForVvm); + sentIntent, deliveryIntent, isForVvm, + PendingRequest.getNextUniqueMessageId()); } else { mGsmDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, data, - sentIntent, deliveryIntent, isForVvm); + sentIntent, deliveryIntent, isForVvm, + PendingRequest.getNextUniqueMessageId()); } } @@ -1831,9 +1872,9 @@ callingPkg, callingUser, destAddr, scAddr, asArrayList(sentIntent), asArrayList(deliveryIntent), isForVvm, null, 0, asArrayList(text), messageUri, persistMessage, priority, expectMore, validityPeriod, messageId, - skipShortCodeCheck); + skipShortCodeCheck, false); - if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) { + if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) { // Send P2P SMS using carrier roaming NB IOT NTN DatagramDispatcher.getInstance().sendSms(pendingRequest); return; @@ -1843,10 +1884,10 @@ } private void sendTextInternal(PendingRequest request) { - logd("sendTextInternal: messageId=" + request.messageId); + logd("sendTextInternal: messageId=" + request.messageId + + ", uniqueMessageId=" + request.uniqueMessageId); if (isSmsDomainSelectionEnabled()) { - TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); - boolean isEmergency = tm.isEmergencyNumber(request.destAddr); + boolean isEmergency = isEmergencyNumber(request.destAddr); sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(isEmergency), request, "sendText"); return; @@ -1859,7 +1900,7 @@ request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, false /*expectMore*/, request.validityPeriod, request.isForVvm, request.messageId, - request.skipShortCodeCheck); + request.skipShortCodeCheck, request.uniqueMessageId); } else { if (isCdmaMo()) { mCdmaDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0), @@ -1867,14 +1908,14 @@ request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, request.expectMore, request.validityPeriod, request.isForVvm, request.messageId, - request.skipShortCodeCheck); + request.skipShortCodeCheck, request.uniqueMessageId); } else { mGsmDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0), request.sentIntents.get(0), request.deliveryIntents.get(0), request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, request.expectMore, request.validityPeriod, request.isForVvm, request.messageId, - request.skipShortCodeCheck); + request.skipShortCodeCheck, request.uniqueMessageId); } } } @@ -1995,9 +2036,9 @@ PendingRequest pendingRequest = new PendingRequest(PendingRequest.TYPE_MULTIPART_TEXT, null, callingPkg, callingUser, destAddr, scAddr, sentIntents, deliveryIntents, false, null, 0, parts, messageUri, persistMessage, priority, expectMore, - validityPeriod, messageId, false); + validityPeriod, messageId, false, false); - if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) { + if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) { // Send multipart P2P SMS using carrier roaming NB IOT NTN DatagramDispatcher.getInstance().sendSms(pendingRequest); return; @@ -2006,11 +2047,17 @@ sendMultipartTextInternal(pendingRequest); } + private boolean isEmergencyNumber(String number) { + if (!mPhone.hasCalling()) return false; + TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + if (tm == null) return false; + return tm.isEmergencyNumber(number); + } + private void sendMultipartTextInternal(PendingRequest request) { logd("sendMultipartTextInternal: messageId=" + request.messageId); if (isSmsDomainSelectionEnabled()) { - TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); - boolean isEmergency = tm.isEmergencyNumber(request.destAddr); + boolean isEmergency = isEmergencyNumber(request.destAddr); sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(isEmergency), request, "sendMultipartText"); return; @@ -2021,20 +2068,20 @@ request.sentIntents, request.deliveryIntents, request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, false /*expectMore*/, request.validityPeriod, - request.messageId); + request.messageId, request.uniqueMessageId); } else { if (isCdmaMo()) { mCdmaDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts, request.sentIntents, request.deliveryIntents, request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, request.expectMore, request.validityPeriod, - request.messageId); + request.messageId, request.uniqueMessageId); } else { mGsmDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts, request.sentIntents, request.deliveryIntents, request.messageUri, request.callingPackage, request.callingUser, request.persistMessage, request.priority, request.expectMore, request.validityPeriod, - request.messageId); + request.messageId, request.uniqueMessageId); } } } @@ -2172,7 +2219,7 @@ */ public void sendCarrierRoamingNbIotNtnText(@NonNull PendingRequest request) { if (!mFeatureFlags.carrierRoamingNbIotNtn()) { - logd("onSendCarrierRoamingNbIotNtnTextError: carrier roaming nb iot ntn " + logd("sendCarrierRoamingNbIotNtnText: carrier roaming nb iot ntn " + "feature flag is disabled"); return; } @@ -2200,6 +2247,44 @@ sendMessage(obtainMessage(EVENT_SEND_TEXT_OVER_NTN_ERROR, pendingRequest)); } + /** + * This API should be used only by {@link DatagramDispatcher} to send MT SMS Polling message + * over non-terrestrial network. + * To enable users to receive incoming messages, the device needs to send an MO SMS to itself + * to trigger SMSC to send all pending SMS to the particular subscription. + */ + public void sendMtSmsPollingMessage() { + if (!SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) { + logd("sendMtSmsPollingMessage: not in roaming nb iot ntn"); + return; + } + + SubscriptionManager subscriptionManager = (SubscriptionManager) mContext + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + String destAddr = subscriptionManager.getPhoneNumber(mPhone.getSubId()); + if (TextUtils.isEmpty(destAddr)) { + logd("sendMtSmsPollingMessage: destAddr is null or empty."); + return; + } + + String mtSmsPollingText = mContext.getResources() + .getString(R.string.config_mt_sms_polling_text); + if (TextUtils.isEmpty(mtSmsPollingText)) { + logd("sendMtSmsPollingMessage: mtSmsPollingText is null or empty."); + return; + } + + String callingPackage = mContext.getPackageName(); + PendingRequest pendingRequest = new PendingRequest(PendingRequest.TYPE_TEXT, null, + callingPackage, Binder.getCallingUserHandle().getIdentifier(), destAddr, + getSmscAddressFromUSIMWithPhoneIdentity(callingPackage), asArrayList(null), + asArrayList(null), false, null, 0, asArrayList(mtSmsPollingText), null, false, 0, + false, 5, 0L, true, true); + + if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) { + DatagramDispatcher.getInstance().sendSms(pendingRequest); + } + } public interface SmsInjectionCallback { void onSmsInjectedResult(int result); @@ -2207,9 +2292,9 @@ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mGsmInboundSmsHandler.dump(fd, pw, args); - mCdmaInboundSmsHandler.dump(fd, pw, args); + if (mCdmaInboundSmsHandler != null) mCdmaInboundSmsHandler.dump(fd, pw, args); mGsmDispatcher.dump(fd, pw, args); - mCdmaDispatcher.dump(fd, pw, args); + if (mCdmaDispatcher != null) mCdmaDispatcher.dump(fd, pw, args); mImsSmsDispatcher.dump(fd, pw, args); }
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java index 8e4ac60..7fb5811 100644 --- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java +++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -35,6 +35,7 @@ import android.util.AtomicFile; import android.util.Xml; +import com.android.internal.telephony.flags.FeatureFlags; import com.android.internal.telephony.util.XmlUtils; import com.android.internal.util.FastXmlSerializer; import com.android.telephony.Rlog; @@ -113,6 +114,8 @@ /** Context for retrieving regexes from XML resource. */ private final Context mContext; + private final FeatureFlags mFeatureFlags; + /** Country code for the cached short code pattern matcher. */ private String mCurrentCountry; @@ -255,8 +258,9 @@ * @param context the context to use to load resources and get TelephonyManager service */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public SmsUsageMonitor(Context context) { + public SmsUsageMonitor(Context context, FeatureFlags flags) { mContext = context; + mFeatureFlags = flags; ContentResolver resolver = context.getContentResolver(); mRoleManager = (RoleManager) mContext.getSystemService(Context.ROLE_SERVICE); @@ -404,7 +408,8 @@ synchronized (mSettingsObserverHandler) { TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); // always allow emergency numbers - if (tm.isEmergencyNumber(destAddress)) { + if (TelephonyCapabilities.supportsTelephonyCalling(mFeatureFlags, mContext) + && tm != null && tm.isEmergencyNumber(destAddress)) { if (DBG) Rlog.d(TAG, "isEmergencyNumber"); return SmsManager.SMS_CATEGORY_NOT_SHORT_CODE; }
diff --git a/src/java/com/android/internal/telephony/TelephonyCapabilities.java b/src/java/com/android/internal/telephony/TelephonyCapabilities.java index 71d3b14..b650b43 100644 --- a/src/java/com/android/internal/telephony/TelephonyCapabilities.java +++ b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
@@ -18,6 +18,8 @@ import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; +import android.content.pm.PackageManager; import android.os.Build; import android.os.SystemProperties; @@ -209,4 +211,14 @@ return featureFlags.minimalTelephonyCdmCheck(); } + + /** + * @return true if this device supports telephony calling, false if it does not. + */ + public static boolean supportsTelephonyCalling(@NonNull FeatureFlags featureFlags, + Context context) { + if (!TelephonyCapabilities.minimalTelephonyCdmCheck(featureFlags)) return true; + return context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEPHONY_CALLING); + } }
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java index 10e97b6..b4a3ee6 100644 --- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java +++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -301,8 +301,8 @@ return new SmsStorageMonitor(phone, flags); } - public SmsUsageMonitor makeSmsUsageMonitor(Context context) { - return new SmsUsageMonitor(context); + public SmsUsageMonitor makeSmsUsageMonitor(Context context, FeatureFlags flags) { + return new SmsUsageMonitor(context, flags); } public ServiceStateTracker makeServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci,
diff --git a/src/java/com/android/internal/telephony/TelephonyCountryDetector.java b/src/java/com/android/internal/telephony/TelephonyCountryDetector.java index c2d6018..1e07bc3 100644 --- a/src/java/com/android/internal/telephony/TelephonyCountryDetector.java +++ b/src/java/com/android/internal/telephony/TelephonyCountryDetector.java
@@ -16,7 +16,6 @@ package com.android.internal.telephony; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -111,6 +110,7 @@ @GuardedBy("mLock") private boolean mIsCountryCodesOverridden = false; private final RegistrantList mCountryCodeChangedRegistrants = new RegistrantList(); + private boolean mIsWifiNetworkConnected = false; private FeatureFlags mFeatureFlags = null; @@ -298,7 +298,7 @@ handleNetworkCountryCodeChangedEvent((NetworkCountryCodeInfo) msg.obj); break; case EVENT_WIFI_CONNECTIVITY_STATE_CHANGED: - handleEventWifiConnectivityStateChanged(); + handleEventWifiConnectivityStateChanged((boolean) msg.obj); break; case EVENT_LOCATION_UPDATE_REQUEST_QUOTA_RESET: evaluateRequestingLocationUpdates(); @@ -490,11 +490,21 @@ } } - private void handleEventWifiConnectivityStateChanged() { - mWifiConnectivityStateChangedRegistrantList.notifyResult(isWifiNetworkConnected()); + private void handleEventWifiConnectivityStateChanged(boolean connected) { + logd("handleEventWifiConnectivityStateChanged: " + connected); + evaluateNotifyWifiConnectivityStateChangedEvent(connected); evaluateRequestingLocationUpdates(); } + private void evaluateNotifyWifiConnectivityStateChangedEvent(boolean connected) { + if (connected != mIsWifiNetworkConnected) { + mIsWifiNetworkConnected = connected; + mWifiConnectivityStateChangedRegistrantList.notifyResult(mIsWifiNetworkConnected); + logd("evaluateNotifyWifiConnectivityStateChangedEvent: wifi connectivity state has " + + "changed to " + connected); + } + } + private void setLocationCountryCode(@NonNull Pair<String, Long> countryCodeInfo) { logd("Set location country code to: " + countryCodeInfo.first); if (!isValid(countryCodeInfo.first)) { @@ -526,25 +536,23 @@ private void registerForWifiConnectivityStateChanged() { logd("registerForWifiConnectivityStateChanged"); NetworkRequest.Builder builder = new NetworkRequest.Builder(); - builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() { @Override - public void onAvailable(Network network) { - logd("Wifi network available: " + network); - sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, null); + public void onCapabilitiesChanged(Network network, + NetworkCapabilities networkCapabilities) { + logd("onCapabilitiesChanged: " + networkCapabilities); + sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, + isInternetAvailable(networkCapabilities)); } @Override public void onLost(Network network) { logd("Wifi network lost: " + network); - sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, null); - } - - @Override - public void onUnavailable() { - logd("Wifi network unavailable"); - sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, null); + sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, false); } }; mConnectivityManager.registerNetworkCallback(builder.build(), networkCallback); @@ -560,15 +568,20 @@ /** * Check whether Wi-Fi network is connected or not. - * @return {@code true} is Wi-Fi is connected, {@code false} otherwise. + * @return {@code true} is Wi-Fi is connected, and internet is available, {@code false} + * otherwise. */ public boolean isWifiNetworkConnected() { - Network activeNetwork = mConnectivityManager.getActiveNetwork(); - NetworkCapabilities networkCapabilities = - mConnectivityManager.getNetworkCapabilities(activeNetwork); - return networkCapabilities != null - && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) - && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + logd("isWifiNetworkConnected: " + mIsWifiNetworkConnected); + return mIsWifiNetworkConnected; + } + + private boolean isInternetAvailable(NetworkCapabilities networkCapabilities) { + boolean isWifiConnected = + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + logd("isWifiConnected: " + isWifiConnected); + return isWifiConnected; } /**
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java b/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java index 0fac334..eea49e3 100644 --- a/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java +++ b/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java
@@ -28,6 +28,7 @@ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Phone; +import com.android.internal.telephony.flags.Flags; import com.android.telephony.Rlog; import java.util.concurrent.atomic.AtomicInteger; @@ -63,6 +64,7 @@ // Constructor private CdmaSubscriptionSourceManager(Context context, CommandsInterface ci) { + if (Flags.cleanupCdma()) return; mCi = ci; mCi.registerForCdmaSubscriptionChanged(this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null); mCi.registerForOn(this, EVENT_RADIO_ON, null); @@ -97,7 +99,7 @@ mCdmaSubscriptionSourceChangedRegistrants.remove(h); synchronized (sReferenceCountMonitor) { sReferenceCount--; - if (sReferenceCount <= 0) { + if (sReferenceCount <= 0 && mCi != null) { mCi.unregisterForCdmaSubscriptionChanged(this); mCi.unregisterForOn(this); mCi.unregisterForSubscriptionStatusChanged(this); @@ -112,6 +114,7 @@ */ @Override public void handleMessage(Message msg) { + if (Flags.cleanupCdma()) return; AsyncResult ar; switch (msg.what) { case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED: @@ -165,6 +168,8 @@ * @return Default CDMA subscription source from Settings DB if present. */ public static int getDefault(Context context) { + if (Flags.cleanupCdma()) return Phone.CDMA_SUBSCRIPTION_UNKNOWN; + // Get the default value from the Settings int subscriptionSource = Settings.Global.getInt(context.getContentResolver(), Settings.Global.CDMA_SUBSCRIPTION_MODE, Phone.PREFERRED_CDMA_SUBSCRIPTION);
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java index 3837790..8646981 100644 --- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java +++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -190,7 +190,7 @@ } /** Get the resolved carrier display name. */ - public CarrierDisplayNameData getCarrierDisplayNameData() { + public @NonNull CarrierDisplayNameData getCarrierDisplayNameData() { resolveCarrierDisplayName(); return mCarrierDisplayNameData; }
diff --git a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java index 1bbc692..2691eab 100644 --- a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java +++ b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
@@ -333,7 +333,7 @@ log("onNetworkValidationRequested: networkCapability = [" + DataUtils.networkCapabilityToString(networkCapability) + "]"); - dnc.requestNetworkValidation(networkCapability, result -> post(() -> { + dnc.requestNetworkValidation(networkCapability, result -> { try { log("onNetworkValidationRequestDone:" + DataServiceCallback.resultCodeToString(result)); @@ -342,7 +342,7 @@ // Ignore if the remote process is no longer available to call back. loge("onNetworkValidationRequestDone RemoteException" + e); } - })); + }); } @Override
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java index 40c0081..f5eae91 100644 --- a/src/java/com/android/internal/telephony/data/DataEvaluation.java +++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -225,8 +225,6 @@ SIM_LOADED(true), /** SIM is removed. */ SIM_REMOVAL(true), - /** SIM is disabled. */ - SIM_DISABLED(true), /** Data profiles changed. */ DATA_PROFILES_CHANGED(true), /** When service state changes.(For now only considering data RAT and data registration). */
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java index 9e432e1..d5bc741 100644 --- a/src/java/com/android/internal/telephony/data/DataNetworkController.java +++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -3543,6 +3543,15 @@ } /** + * Called when SIM is absent. + */ + private void onSimAbsent() { + log("onSimAbsent"); + sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS, + DataEvaluationReason.SIM_REMOVAL)); + } + + /** * Called when SIM state changes. * * @param simState SIM state. (Note this is mixed with card state and application state.) @@ -3550,22 +3559,13 @@ private void onSimStateChanged(@SimState int simState) { log("onSimStateChanged: state=" + TelephonyManager.simStateToString(simState)); if (mSimState != simState) { + mSimState = simState; if (simState == TelephonyManager.SIM_STATE_ABSENT) { - log("onSimStateChanged: SIM absent."); - sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS, - DataEvaluationReason.SIM_REMOVAL)); - } else if (simState == TelephonyManager.SIM_STATE_NOT_READY - && mSimState == TelephonyManager.SIM_STATE_LOADED) { - if (mFeatureFlags.simDisabledGracefulTearDown()) { - log("onSimStateChanged: SIM disabled."); - sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS, - DataEvaluationReason.SIM_DISABLED)); - } + onSimAbsent(); } else if (simState == TelephonyManager.SIM_STATE_LOADED) { sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS, DataEvaluationReason.SIM_LOADED)); } - mSimState = simState; mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor( () -> callback.onSimStateChanged(mSimState))); }
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/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java index 1005bb7..079e705 100644 --- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java +++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -1491,14 +1491,18 @@ mPreferredDataPhoneId = mEmergencyOverride.mPhoneId; mLastSwitchPreferredDataReason = DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN; } else { - int imsRegTech = mImsRegTechProvider.get(mContext, mPhoneIdInVoiceCall); - if (isAnyVoiceCallActiveOnDevice() && imsRegTech != REGISTRATION_TECH_IWLAN) { - if (imsRegTech != REGISTRATION_TECH_CROSS_SIM) { - mPreferredDataPhoneId = shouldSwitchDataDueToInCall() - ? mPhoneIdInVoiceCall : getFallbackDataPhoneIdForInternetRequests(); + if (isAnyVoiceCallActiveOnDevice()) { + int imsRegTech = mImsRegTechProvider.get(mContext, mPhoneIdInVoiceCall); + if (imsRegTech != REGISTRATION_TECH_IWLAN) { + if (imsRegTech != REGISTRATION_TECH_CROSS_SIM) { + mPreferredDataPhoneId = shouldSwitchDataDueToInCall() + ? mPhoneIdInVoiceCall : getFallbackDataPhoneIdForInternetRequests(); + } else { + logl("IMS call on cross-SIM, skip switching data to phone " + + mPhoneIdInVoiceCall); + } } else { - logl("IMS call on cross-SIM, skip switching data to phone " - + mPhoneIdInVoiceCall); + mPreferredDataPhoneId = getFallbackDataPhoneIdForInternetRequests(); } } else { mPreferredDataPhoneId = getFallbackDataPhoneIdForInternetRequests(); @@ -1830,16 +1834,18 @@ if (phone == null) { return false; } - + Call bgCall = phone.getBackgroundCall(); + Call fgCall = phone.getForegroundCall(); + if (bgCall == null || fgCall == null) { + return false; + } // A phone in voice call might trigger data being switched to it. // Exclude dialing to give modem time to process an EMC first before dealing with DDS switch // Include alerting because modem RLF leads to delay in switch, so carrier required to // switch in alerting phase. // TODO: check ringing call for vDADA - return (!phone.getBackgroundCall().isIdle() - && phone.getBackgroundCall().getState() != Call.State.DIALING) - || (!phone.getForegroundCall().isIdle() - && phone.getForegroundCall().getState() != Call.State.DIALING); + return (!bgCall.isIdle() && bgCall.getState() != Call.State.DIALING) + || (!fgCall.isIdle() && fgCall.getState() != Call.State.DIALING); } private void updateHalCommandToUse() {
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java index 677db87..681ca12 100644 --- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java +++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
@@ -783,6 +783,20 @@ dispose(); return; } + + if (scanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE) { + // Handle as unknown network. + EmergencyRegistrationResult result = new EmergencyRegistrationResult( + AccessNetworkConstants.AccessNetworkType.UNKNOWN, + NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN, + NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", ""); + + if (mHandler != null) { + Message msg = mHandler.obtainMessage(EVENT_EMERGENCY_NETWORK_SCAN_RESULT, + new AsyncResult(null, result, null)); + msg.sendToTarget(); + } + } } private void onModemReset() {
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java index f983108..b3e8095 100644 --- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java +++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -20,6 +20,12 @@ import static android.telecom.Connection.STATE_DISCONNECTED; import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL; import static android.telephony.CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL; +import static android.telephony.TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL; +import static android.telephony.TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS; +import static android.telephony.TelephonyManager.STOP_REASON_EMERGENCY_SMS_SENT; +import static android.telephony.TelephonyManager.STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED; +import static android.telephony.TelephonyManager.STOP_REASON_TIMER_EXPIRED; +import static android.telephony.TelephonyManager.STOP_REASON_UNKNOWN; import static com.android.internal.telephony.TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED; import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK; @@ -65,6 +71,7 @@ import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.data.PhoneSwitcher; +import com.android.internal.telephony.flags.FeatureFlags; import com.android.internal.telephony.imsphone.ImsPhoneConnection; import com.android.internal.telephony.satellite.SatelliteController; import com.android.telephony.Rlog; @@ -102,8 +109,6 @@ * from the modem. */ private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1 * 1000; - @VisibleForTesting - public static final int DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS = 3 * 1000; /** Default value for if Emergency Callback Mode is supported. */ private static final boolean DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED = true; /** Default Emergency Callback Mode exit timeout value. */ @@ -135,6 +140,7 @@ private final CarrierConfigManager mConfigManager; private final Handler mHandler; private final boolean mIsSuplDdsSwitchRequiredForEmergencyCall; + private final int mWaitForInServiceTimeoutMs; private final PowerManager.WakeLock mWakeLock; private RadioOnHelper mRadioOnHelper; @EmergencyConstants.EmergencyMode @@ -148,7 +154,8 @@ /** For emergency calls */ private final long mEcmExitTimeoutMs; // A runnable which is used to automatically exit from Ecm after a period of time. - private final Runnable mExitEcmRunnable = this::exitEmergencyCallbackMode; + private final Runnable mExitEcmRunnable = () -> exitEmergencyCallbackMode( + STOP_REASON_TIMER_EXPIRED); // Tracks emergency calls by callId that have reached {@link Call.State#ACTIVE}. private final Set<android.telecom.Connection> mActiveEmergencyCalls = new ArraySet<>(); private Phone mPhone; @@ -184,6 +191,8 @@ private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged( slotIndex, subId); + /** Feature flags */ + private final FeatureFlags mFeatureFlags; /** * Listens for Emergency Callback Mode state change intents @@ -326,12 +335,14 @@ if (!isSamePhone(mPhone, mSmsPhone)) { completeEmergencyMode(emergencyType, DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED); - exitEmergencySmsCallbackMode(); + exitEmergencySmsCallbackMode( + STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); } } else { completeEmergencyMode(emergencyType); mIsEmergencyCallStartedDuringEmergencySms = false; - exitEmergencySmsCallbackMode(); + exitEmergencySmsCallbackMode( + STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber); } @@ -343,7 +354,8 @@ if (mIsEmergencyCallStartedDuringEmergencySms) { mIsEmergencyCallStartedDuringEmergencySms = false; - exitEmergencySmsCallbackMode(); + exitEmergencySmsCallbackMode( + STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber); } @@ -409,7 +421,8 @@ // the emergency call was started, needs to exit the emergency mode first. if (mIsEmergencyCallStartedDuringEmergencySms) { final Phone smsPhone = mSmsPhone; - exitEmergencySmsCallbackMode(); + exitEmergencySmsCallbackMode( + STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); if (mPhone != null && smsPhone != null && !isSamePhone(mPhone, smsPhone)) { @@ -428,7 +441,7 @@ break; } case MSG_EXIT_SCBM: { - exitEmergencySmsCallbackModeAndEmergencyMode(); + exitEmergencySmsCallbackModeAndEmergencyMode(STOP_REASON_TIMER_EXPIRED); break; } case MSG_NEW_RINGING_CONNECTION: { @@ -457,11 +470,13 @@ * @param context The context of the application. * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for * emergency call. + * @param featureFlags The telephony feature flags. */ - public static void make(Context context, boolean isSuplDdsSwitchRequiredForEmergencyCall) { + public static void make(Context context, boolean isSuplDdsSwitchRequiredForEmergencyCall, + int waitForInServiceTimeout, @NonNull FeatureFlags featureFlags) { if (INSTANCE == null) { INSTANCE = new EmergencyStateTracker(context, Looper.myLooper(), - isSuplDdsSwitchRequiredForEmergencyCall); + isSuplDdsSwitchRequiredForEmergencyCall, waitForInServiceTimeout, featureFlags); } } @@ -481,12 +496,14 @@ * Initializes EmergencyStateTracker. */ private EmergencyStateTracker(Context context, Looper looper, - boolean isSuplDdsSwitchRequiredForEmergencyCall) { + boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout, + @NonNull FeatureFlags featureFlags) { mEcmExitTimeoutMs = DEFAULT_ECM_EXIT_TIMEOUT_MS; mContext = context; mHandler = new MyHandler(looper); mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall; - + mWaitForInServiceTimeoutMs = waitForInServiceTimeout; + mFeatureFlags = featureFlags; PowerManager pm = context.getSystemService(PowerManager.class); mWakeLock = (pm != null) ? pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "telephony:" + TAG) : null; @@ -518,25 +535,33 @@ * @param looper The {@link Looper} of the application. * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for * emergency call. + * @param waitForInServiceTimeout The timeout duration how long does it wait for + * modem to get in-service state when emergency + * call is dialed in airplane mode before + * starting the emergency call. * @param phoneFactoryProxy The {@link PhoneFactoryProxy} to be injected. * @param phoneSwitcherProxy The {@link PhoneSwitcherProxy} to be injected. * @param telephonyManagerProxy The {@link TelephonyManagerProxy} to be * injected. * @param radioOnHelper The {@link RadioOnHelper} to be injected. + * @param featureFlags The {@link FeatureFlags} to be injected. */ @VisibleForTesting public EmergencyStateTracker(Context context, Looper looper, - boolean isSuplDdsSwitchRequiredForEmergencyCall, PhoneFactoryProxy phoneFactoryProxy, - PhoneSwitcherProxy phoneSwitcherProxy, TelephonyManagerProxy telephonyManagerProxy, - RadioOnHelper radioOnHelper, long ecmExitTimeoutMs) { + boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout, + PhoneFactoryProxy phoneFactoryProxy, PhoneSwitcherProxy phoneSwitcherProxy, + TelephonyManagerProxy telephonyManagerProxy, RadioOnHelper radioOnHelper, + long ecmExitTimeoutMs, FeatureFlags featureFlags) { mContext = context; mHandler = new MyHandler(looper); mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall; + mWaitForInServiceTimeoutMs = waitForInServiceTimeout; mPhoneFactoryProxy = phoneFactoryProxy; mPhoneSwitcherProxy = phoneSwitcherProxy; mTelephonyManagerProxy = telephonyManagerProxy; mRadioOnHelper = radioOnHelper; mEcmExitTimeoutMs = ecmExitTimeoutMs; + mFeatureFlags = featureFlags; mWakeLock = null; // Don't declare a wakelock in tests mConfigManager = context.getSystemService(CarrierConfigManager.class); mConfigManager.registerCarrierConfigChangeListener(mHandler::post, @@ -574,7 +599,7 @@ // Case1) When 2nd emergency call is initiated during an active call on the same phone. // Case2) While the device is in ECBM, an emergency call is initiated on the same phone. if (isSamePhone(mPhone, phone) && (!mActiveEmergencyCalls.isEmpty() || isInEcm())) { - exitEmergencySmsCallbackMode(); + exitEmergencySmsCallbackMode(STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); mOngoingConnection = c; mIsTestEmergencyNumber = isTestEmergencyNumber; if (isInEcm()) { @@ -583,6 +608,11 @@ releaseWakeLock(); ((GsmCdmaPhone) mPhone).notifyEcbmTimerReset(Boolean.TRUE); + if (mFeatureFlags.emergencyCallbackModeNotification()) { + mPhone.stopEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_CALL, + STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); + } + mOngoingCallProperties = 0; mCallEmergencyModeFuture = new CompletableFuture<>(); setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN, @@ -622,7 +652,7 @@ mIsEmergencyCallStartedDuringEmergencySms = false; } - exitEmergencySmsCallbackMode(); + exitEmergencySmsCallbackMode(STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); } if (mIsEmergencyCallStartedDuringEmergencySms) { @@ -1087,7 +1117,7 @@ * Handles the radio power off request. */ public void onCellularRadioPowerOffRequested() { - exitEmergencySmsCallbackModeAndEmergencyMode(); + exitEmergencySmsCallbackModeAndEmergencyMode(STOP_REASON_UNKNOWN); exitEmergencyCallbackMode(); } @@ -1159,10 +1189,14 @@ setEmergencyCallbackMode(mPhone, EMERGENCY_TYPE_CALL); - // Post this runnable so we will automatically exit if no one invokes - // exitEmergencyCallbackMode() directly. long delayInMillis = TelephonyProperties.ecm_exit_timer() .orElse(mEcmExitTimeoutMs); + if (mFeatureFlags.emergencyCallbackModeNotification()) { + mPhone.startEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_CALL, delayInMillis); + } + + // Post this runnable so we will automatically exit if no one invokes + // exitEmergencyCallbackMode() directly. mHandler.postDelayed(mExitEcmRunnable, delayInMillis); // We don't want to go to sleep while in ECM. @@ -1170,9 +1204,27 @@ } /** - * Exits emergency callback mode and notifies relevant listeners. + * Exits the emergency callback mode. + * + * <p>This method exits the emergency callback mode with an unknown stop reason. It removes + * any pending exit requests and notifies relevant listeners about the change in status. */ public void exitEmergencyCallbackMode() { + exitEmergencyCallbackMode(STOP_REASON_UNKNOWN); + } + + /** + * Exits the emergency callback mode. + * + * <p>This method exits the emergency callback mode with the specified stop reason. It removes + * any pending exit requests and notifies relevant listeners about the change in status, + * providing the reason for exiting. + * + * @param reason The reason for exiting. See + * {@link TelephonyManager.EmergencyCallbackModeStopReason} for possible values. + */ + public void exitEmergencyCallbackMode( + @TelephonyManager.EmergencyCallbackModeStopReason int reason) { Rlog.d(TAG, "exit ECBM"); // Remove pending exit ECM runnable, if any. mHandler.removeCallbacks(mExitEcmRunnable); @@ -1191,6 +1243,10 @@ sendEmergencyCallbackModeChange(); gsmCdmaPhone.notifyEmergencyCallRegistrants(false); + if (mFeatureFlags.emergencyCallbackModeNotification()) { + gsmCdmaPhone.stopEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_CALL, reason); + } + // Exit emergency mode on modem. exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL); } @@ -1213,11 +1269,20 @@ } /** - * Exits emergency callback mode and triggers runnable after exit response is received. + * Exits the emergency callback mode and triggers {@link Runnable} after exit response is + * received. + * + * <p>This method exits the emergency callback mode with the specified stop reason and then + * executes the provided {@link Runnable}. + * + * @param onComplete The {@link Runnable} to execute after exiting emergency callback mode. + * @param reason The reason for exiting. See + * {@link TelephonyManager.EmergencyCallbackModeStopReason} for possible values. */ - public void exitEmergencyCallbackMode(Runnable onComplete) { + public void exitEmergencyCallbackMode(Runnable onComplete, + @TelephonyManager.EmergencyCallbackModeStopReason int reason) { mOnEcmExitCompleteRunnable = onComplete; - exitEmergencyCallbackMode(); + exitEmergencyCallbackMode(reason); } /** @@ -1323,7 +1388,7 @@ // emergency SMS callback mode first. exitScbmInOtherPhone = true; mIsEmergencySmsStartedDuringScbm = true; - exitEmergencySmsCallbackModeAndEmergencyMode(); + exitEmergencySmsCallbackModeAndEmergencyMode(STOP_REASON_EMERGENCY_SMS_SENT); } else { Rlog.e(TAG, "Emergency SMS is in progress on the other slot."); return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); @@ -1460,6 +1525,9 @@ */ private void enterEmergencySmsCallbackMode() { Rlog.d(TAG, "enter SCBM while " + (isInScbm() ? "in" : "not in") + " SCBM"); + + boolean shouldRestartEcm = isInScbm(); + // Remove pending message if present. mHandler.removeMessages(MSG_EXIT_SCBM); @@ -1484,17 +1552,29 @@ // Post the message so we will automatically exit if no one invokes // exitEmergencySmsCallbackModeAndEmergencyMode() directly. mHandler.sendEmptyMessageDelayed(MSG_EXIT_SCBM, delayInMillis); + + if (mFeatureFlags.emergencyCallbackModeNotification()) { + if (shouldRestartEcm) { + mSmsPhone.restartEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_SMS, delayInMillis); + } else { + mSmsPhone.startEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_SMS, delayInMillis); + } + } } /** * Exits emergency SMS callback mode and emergency mode if the device is in SCBM and * the emergency mode is in CALLBACK. + * + * @param reason The reason for exiting. See + * {@link TelephonyManager.EmergencyCallbackModeStopReason} for possible values. */ - private void exitEmergencySmsCallbackModeAndEmergencyMode() { + private void exitEmergencySmsCallbackModeAndEmergencyMode( + @TelephonyManager.EmergencyCallbackModeStopReason int reason) { Rlog.d(TAG, "exit SCBM and emergency mode"); final Phone smsPhone = mSmsPhone; boolean wasInScbm = isInScbm(); - exitEmergencySmsCallbackMode(); + exitEmergencySmsCallbackMode(reason); // The emergency mode needs to be checked to ensure that there is no ongoing emergency SMS. if (wasInScbm && mOngoingEmergencySmsIds.isEmpty()) { @@ -1505,13 +1585,22 @@ /** * Exits emergency SMS callback mode. + * + * @param reason The reason for exiting. See + * {@link TelephonyManager.EmergencyCallbackModeStopReason} for possible values. */ - private void exitEmergencySmsCallbackMode() { + private void exitEmergencySmsCallbackMode( + @TelephonyManager.EmergencyCallbackModeStopReason int reason) { // Remove pending message if present. mHandler.removeMessages(MSG_EXIT_SCBM); if (isInScbm()) { Rlog.i(TAG, "exit SCBM"); + + if (mFeatureFlags.emergencyCallbackModeNotification()) { + mSmsPhone.stopEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_SMS, reason); + } + setIsInScbm(false); } @@ -1588,7 +1677,7 @@ final boolean isAirplaneModeOn = isAirplaneModeOn(mContext); boolean needToTurnOnRadio = !isRadioOn() || isAirplaneModeOn; final SatelliteController satelliteController = SatelliteController.getInstance(); - boolean needToTurnOffSatellite = satelliteController.isSatelliteEnabled(); + boolean needToTurnOffSatellite = satelliteController.isSatelliteEnabledOrBeingEnabled(); if (isAirplaneModeOn && !isPowerOff() && !phone.getServiceStateTracker().getDesiredPowerState()) { @@ -1607,14 +1696,24 @@ final Phone phoneForEmergency = phone; final android.telecom.Connection expectedConnection = mOngoingConnection; - final int waitForInServiceTimeout = - needToTurnOnRadio ? DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS : 0; + final int waitForInServiceTimeout = needToTurnOnRadio ? mWaitForInServiceTimeoutMs : 0; Rlog.i(TAG, "turnOnRadioAndSwitchDds: timeout=" + waitForInServiceTimeout); mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() { @Override public void onComplete(RadioOnStateListener listener, boolean isRadioReady) { + // Make sure the Call has not already been canceled by the user. + if (expectedConnection.getState() == STATE_DISCONNECTED) { + Rlog.i(TAG, "Call disconnected before the outgoing call was placed." + + "Skipping call placement."); + // If call is already canceled by the user, notify modem to exit emergency + // call mode by sending radio on with forEmergencyCall=false. + for (Phone phone : mPhoneFactoryProxy.getPhones()) { + phone.setRadioPower(true, false, false, true); + } + return; + } if (!isRadioReady) { - if (satelliteController.isSatelliteEnabled()) { + if (satelliteController.isSatelliteEnabledOrBeingEnabled()) { // Could not turn satellite off Rlog.e(TAG, "Failed to turn off satellite modem."); completeEmergencyMode(emergencyType, DisconnectCause.SATELLITE_ENABLED); @@ -1647,7 +1746,7 @@ return false; } return phone.getServiceStateTracker().isRadioOn() - && !satelliteController.isSatelliteEnabled(); + && !satelliteController.isSatelliteEnabledOrBeingEnabled(); } @Override @@ -1659,10 +1758,9 @@ } // onTimeout shall be called only with the Phone for emergency return phone.getServiceStateTracker().isRadioOn() - && !satelliteController.isSatelliteEnabled(); + && !satelliteController.isSatelliteEnabledOrBeingEnabled(); } - }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, waitForInServiceTimeout, - /* forNormalRoutingEmergencyCall */ false); + }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, waitForInServiceTimeout); } else { switchDdsAndSetEmergencyMode(phone, emergencyType); }
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java index 26da1c2..e2291c9 100644 --- a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java +++ b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
@@ -85,7 +85,7 @@ */ public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback, boolean forEmergencyCall, Phone phoneForEmergencyCall, boolean isTestEmergencyNumber, - int emergencyTimeoutIntervalMillis, boolean forNormalRoutingEmergencyCall) { + int emergencyTimeoutIntervalMillis) { setupListeners(); mCallback = callback; mInProgressListeners.clear(); @@ -102,13 +102,9 @@ mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall, forEmergencyCall && phone == phoneForEmergencyCall, timeoutCallbackInterval); } - powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber, - forNormalRoutingEmergencyCall); - if (SatelliteController.getInstance().isSatelliteEnabled() - || SatelliteController.getInstance().isSatelliteBeingEnabled()) { - // TODO: phoneForEmergencyCall is actually ignored, SatelliteController#mSatelliePhone - // is being used instead. - powerOffSatellite(phoneForEmergencyCall); + powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber); + if (SatelliteController.getInstance().isSatelliteEnabledOrBeingEnabled()) { + powerOffSatellite(); } } @@ -117,25 +113,17 @@ * get an onServiceStateChanged() callback when the radio successfully comes up. */ private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall, - boolean isTestEmergencyNumber, boolean forNormalRoutingEmergencyCall) { + boolean isTestEmergencyNumber) { // Always try to turn on the radio here independent of APM setting - if we got here in the // first place, the radio is off independent of APM setting. for (Phone phone : PhoneFactory.getPhones()) { Rlog.d(TAG, "powerOnRadio, enabling Radio"); if (isTestEmergencyNumber) { - phone.setRadioPowerOnForTestEmergencyCall( - (phone == phoneForEmergencyCall) && !forNormalRoutingEmergencyCall); + phone.setRadioPowerOnForTestEmergencyCall(phone == phoneForEmergencyCall); } else { - if (forNormalRoutingEmergencyCall) { - if (phone.getServiceStateTracker() != null) { - // Clear all radio off reasons to ensure that the radio is turned on for - // normal routing emergency call. - phone.getServiceStateTracker().clearAllRadioOffReasons(); - } - } - phone.setRadioPower(true, forEmergencyCall && !forNormalRoutingEmergencyCall, - (phone == phoneForEmergencyCall) && !forNormalRoutingEmergencyCall, false); + phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall, + false); } } @@ -162,7 +150,7 @@ * Attempt to power off the satellite modem. We'll eventually get an * onSatelliteModemStateChanged() callback when the satellite modem is successfully disabled. */ - private void powerOffSatellite(Phone phoneForEmergencyCall) { + private void powerOffSatellite() { SatelliteController satelliteController = SatelliteController.getInstance(); satelliteController.requestSatelliteEnabled( false /* enableSatellite */, false /* enableDemoMode */, false /* isEmergency */,
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java index 1b1922c..f44dc70 100644 --- a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java +++ b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
@@ -157,6 +157,11 @@ public void onRegistrationFailure(int causeCode) { Rlog.d(TAG, "onRegistrationFailure: causeCode " + causeCode); } + + @Override + public void onTerrestrialNetworkAvailableChanged(boolean isAvailable) { + Rlog.d(TAG, "onTerrestrialNetworkAvailableChanged: isAvailable " + isAvailable); + } }; private Callback mCallback; // The callback to notify upon completion. @@ -230,7 +235,7 @@ // Register for RADIO_OFF to handle cases where emergency call is dialed before // we receive UNSOL_RESPONSE_RADIO_STATE_CHANGED with RADIO_OFF. registerForRadioOff(); - if (mSatelliteController.isSatelliteEnabled()) { + if (mSatelliteController.isSatelliteEnabledOrBeingEnabled()) { // Register for satellite modem state changed to notify when satellite is disabled. registerForSatelliteEnabledChanged(); } @@ -401,7 +406,7 @@ Rlog.d(TAG, "Trying (again) to turn the radio on and satellite modem off."); mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall, false); - if (mSatelliteController.isSatelliteEnabled()) { + if (mSatelliteController.isSatelliteEnabledOrBeingEnabled()) { mSatelliteController.requestSatelliteEnabled( false /* enableSatellite */, false /* enableDemoMode */, false /* isEmergency*/,
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java index b758733..c5cc769 100644 --- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java +++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -749,10 +749,12 @@ @Override public boolean processMessage(Message message) { if (message.what == CMD_SERVICE_DISCONNECTED) { + EuiccSession.get().endAllSessions(); mEuiccService = null; transitionTo(mDisconnectedState); return HANDLED; } else if (message.what == CMD_LINGER_TIMEOUT) { + EuiccSession.get().endAllSessions(); unbind(); transitionTo(mAvailableState); return HANDLED;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java index 715116e..cc9d793 100644 --- a/src/java/com/android/internal/telephony/euicc/EuiccController.java +++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -22,6 +22,7 @@ import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; @@ -70,6 +71,7 @@ import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback; import com.android.internal.telephony.flags.FeatureFlags; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccController; @@ -1089,7 +1091,7 @@ // system or the caller manage the target subscription, we let it continue. This is // because deleting subscription won't change status of any other subscriptions. if (!callerCanWriteEmbeddedSubscriptions - && !mSubscriptionManager.canManageSubscription(sub, callingPackage) + && !canManageSubscription(sub, callingPackage) && !adminOwned) { Log.e(TAG, "No permissions: " + subscriptionId + " adminOwned=" + adminOwned); sendResult(callbackIntent, ERROR, null /* extrasIntent */); @@ -1208,7 +1210,7 @@ if (callerCanWriteEmbeddedSubscriptions) { passConsent = true; } else { - if (!mSubscriptionManager.canManageSubscription(sub, callingPackage)) { + if (!canManageSubscription(sub, callingPackage)) { Log.e(TAG, "Not permitted to switch to sub: " + subscriptionId); sendResult(callbackIntent, ERROR, null /* extrasIntent */); return; @@ -1287,7 +1289,7 @@ if ((cardId == TelephonyManager.UNSUPPORTED_CARD_ID || subInfo.getCardId() == cardId) && subInfo.isEmbedded() && (callerCanWriteEmbeddedSubscriptions - || mSubscriptionManager.canManageSubscription(subInfo, callingPackage))) { + || canManageSubscription(subInfo, callingPackage))) { return subInfo.getPortIndex(); } } @@ -1558,7 +1560,7 @@ // system or the caller can manage the target subscription, we let it continue. This is // because updating subscription nickname won't affect any other subscriptions. if (!callerCanWriteEmbeddedSubscriptions - && !mSubscriptionManager.canManageSubscription(sub, callingPackage)) { + && !canManageSubscription(sub, callingPackage)) { Log.e(TAG, "No permissions: " + subscriptionId); sendResult(callbackIntent, ERROR, null /* extrasIntent */); return; @@ -2067,7 +2069,7 @@ if ((cardId == TelephonyManager.UNSUPPORTED_CARD_ID || subInfo.getCardId() == cardId) && subInfo.isEmbedded() && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex) - && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) { + && canManageSubscription(subInfo, callingPackage)) { return true; } } @@ -2136,8 +2138,7 @@ if (subInfo.isEmbedded() && subInfo.getCardId() == cardId && (!usePortIndex || subInfo.getPortIndex() == targetPortIndex) - && mSubscriptionManager.canManageSubscription( - subInfo, callingPackage)) { + && canManageSubscription(subInfo, callingPackage)) { return true; } } @@ -2156,7 +2157,7 @@ } else { for (SubscriptionInfo subInfo : subInfoList) { if (subInfo.isEmbedded() - && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) { + && canManageSubscription(subInfo, callingPackage)) { return true; } } @@ -2391,4 +2392,13 @@ methodName + " is unsupported without " + FEATURE_TELEPHONY_EUICC); } } + + private boolean canManageSubscription(SubscriptionInfo subInfo, String packageName) { + if (Flags.hsumPackageManager() && UserManager.isHeadlessSystemUserMode()) { + return mSubscriptionManager.canManageSubscriptionAsUser(subInfo, packageName, + UserHandle.of(ActivityManager.getCurrentUser())); + } else { + return mSubscriptionManager.canManageSubscription(subInfo, packageName); + } + } }
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccSession.java b/src/java/com/android/internal/telephony/euicc/EuiccSession.java index 99b8720..f17789f 100644 --- a/src/java/com/android/internal/telephony/euicc/EuiccSession.java +++ b/src/java/com/android/internal/telephony/euicc/EuiccSession.java
@@ -15,6 +15,7 @@ */ package com.android.internal.telephony.euicc; +import android.annotation.Nullable; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; @@ -85,14 +86,18 @@ /** Returns {@code true} if there is at least one session ongoing. */ public boolean hasSession() { - boolean hasSession; - synchronized(this) { - hasSession = !mSessions.isEmpty(); - } + boolean hasSession = hasSessionInternal(); Rlog.i(TAG, "hasSession: " + hasSession); return hasSession; } + // The bare metal implementation of hasSession() without logging. + private boolean hasSessionInternal() { + synchronized(this) { + return !mSessions.isEmpty(); + } + } + /** * Notes that a logical channel may be opened by the {@code apduSender}, which will * be used to close the channel when session ends (see {@link #endSession()}). @@ -104,7 +109,7 @@ public void noteChannelOpen(ApduSender apduSender) { Rlog.i(TAG, "noteChannelOpen: " + apduSender); synchronized(this) { - if (hasSession()) { + if (hasSessionInternal()) { mApduSenders.add(apduSender); } } @@ -112,19 +117,41 @@ /** * Marks the end of a eUICC transaction session. If this ends the last ongoing session, - * try to close the logical channel using the noted {@code apduSender} + * try to close the logical channel using the noted {@code apduSender}s * (see {@link #noteChannelOpen()}). * * @param sessionId The session ID. */ public void endSession(String sessionId) { Rlog.i(TAG, "endSession: " + sessionId); + endSessionInternal(sessionId); + } + + /** + * Marks the end of all eUICC transaction sessions and close the logical + * channels using the noted {@code apduSender}s + * (see {@link #noteChannelOpen()}). + * + * <p>This is useful in global cleanup e.g. when EuiccService + * implementation app crashes and indivial {@link #endSession()} calls + * won't happen in {@link EuiccConnector}. + */ + public void endAllSessions() { + Rlog.i(TAG, "endAllSessions"); + endSessionInternal(null); + } + + // The implementation of endSession(sessionId) or endAllSessions() when the sessionId is null, + // without logging. + private void endSessionInternal(@Nullable String sessionId) { ApduSender[] apduSenders = new ApduSender[0]; synchronized(this) { - boolean sessionRemoved = mSessions.remove(sessionId); - // sessionRemoved is false if the `sessionId` was never started or there was - // no session at all i.e. `sessions` is empty. Don't bother invoke `apduSender`. - if (sessionRemoved && mSessions.isEmpty()) { + boolean sessionRemoved = removeOrClear(mSessions, sessionId); + // 1. sessionRemoved is false if the `sessionId` was never started or there was + // no session. Don't bother invoke `apduSender`. + // 2. If some session is removed, and as a result there is no more session, we + // can clsoe channels. + if (sessionRemoved && !hasSessionInternal()) { // copy mApduSenders to a local variable so we don't call closeAnyOpenChannel() // which can take time in synchronized block. apduSenders = mApduSenders.toArray(apduSenders); @@ -136,6 +163,21 @@ } } + /** + * Removes the given element from the set. If the element is null, clears the set. + * + * @return true if the set changed as a result of the call + */ + private static boolean removeOrClear(Set<String> collection, @Nullable String element) { + if (element == null) { + boolean collectionChanged = !collection.isEmpty(); + collection.clear(); + return collectionChanged; + } else { + return collection.remove(element); + } + } + @VisibleForTesting public EuiccSession() {} }
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java index eb389b7..b95911f 100644 --- a/src/java/com/android/internal/telephony/ims/ImsResolver.java +++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -19,6 +19,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -51,6 +52,7 @@ import android.util.ArraySet; import android.util.LocalLog; import android.util.Log; +import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; @@ -129,6 +131,10 @@ private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 7; // clear any carrier ImsService test overrides. private static final int HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG = 8; + // the user has switched + private static final int HANDLER_USER_SWITCHED = 9; + // A dynamic query has failed permanently, remove the package from being tracked + private static final int HANDLER_REMOVE_PACKAGE_PERM_ERROR = 10; // Delay between dynamic ImsService queries. private static final int DELAY_DYNAMIC_QUERY_MS = 5000; @@ -158,14 +164,19 @@ } private static class OverrideConfig { + public final String packageName; public final int slotId; + public final int userId; public final boolean isCarrierService; - public final Map<Integer, String> featureTypeToPackageMap; + public final int[] featureTypes; - OverrideConfig(int slotIndex, boolean isCarrier, Map<Integer, String> feature) { + OverrideConfig(String pkgName, int slotIndex, int userIndex, boolean isCarrier, + int[] features) { + packageName = pkgName; slotId = slotIndex; + userId = userIndex; isCarrierService = isCarrier; - featureTypeToPackageMap = feature; + featureTypes = features; } } @@ -175,7 +186,8 @@ */ @VisibleForTesting public static class ImsServiceInfo { - public ComponentName name; + public final ComponentName name; + public final Set<UserHandle> users = new HashSet<>(); // Determines if features were created from metadata in the manifest or through dynamic // query. public boolean featureFromMetadata = true; @@ -184,7 +196,8 @@ // Map slotId->Feature private final HashSet<ImsFeatureConfiguration.FeatureSlotPair> mSupportedFeatures; - public ImsServiceInfo() { + public ImsServiceInfo(ComponentName componentName) { + name = componentName; mSupportedFeatures = new HashSet<>(); } @@ -208,37 +221,41 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - ImsServiceInfo that = (ImsServiceInfo) o; - - if (name != null ? !name.equals(that.name) : that.name != null) return false; - if (!mSupportedFeatures.equals(that.mSupportedFeatures)) { - return false; - } - return controllerFactory != null ? controllerFactory.equals(that.controllerFactory) - : that.controllerFactory == null; + return Objects.equals(name, that.name) && Objects.equals(users, that.users); } @Override public int hashCode() { - // We do not include mSupportedFeatures in hashcode because the internal structure - // changes after adding. - int result = name != null ? name.hashCode() : 0; - result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0); - return result; + return Objects.hash(name, users); } @Override public String toString() { return "[ImsServiceInfo] name=" + name - + ", featureFromMetadata=" + + ", user=" + + users + + ",featureFromMetadata=" + featureFromMetadata + "," + printFeatures(mSupportedFeatures); } } + private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final UserHandle handle = intent.getParcelableExtra(Intent.EXTRA_USER, + UserHandle.class); + switch (action) { + case Intent.ACTION_USER_SWITCHED -> + mHandler.obtainMessage(HANDLER_USER_SWITCHED, handle).sendToTarget(); + } + } + }; + // Receives broadcasts from the system involving changes to the installed applications. If // an ImsService that we are configured to use is installed, we must bind to it. private final BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() { @@ -311,6 +328,17 @@ }; /** + * Testing interface used to mock ActivityManager in testing + */ + @VisibleForTesting + public interface ActivityManagerProxy { + /** + * @return The current user + */ + UserHandle getCurrentUser(); + } + + /** * Testing interface used to mock SubscriptionManager in testing */ @VisibleForTesting @@ -360,6 +388,13 @@ } }; + private ActivityManagerProxy mActivityManagerProxy = new ActivityManagerProxy() { + @Override + public UserHandle getCurrentUser() { + return UserHandle.of(ActivityManager.getCurrentUser()); + } + }; + /** * Testing interface for injecting mock ImsServiceControllers. */ @@ -470,6 +505,11 @@ maybeRemovedImsService(packageName); break; } + case HANDLER_REMOVE_PACKAGE_PERM_ERROR: { + Pair<String, UserHandle> packageInfo = (Pair<String, UserHandle>) msg.obj; + maybeRemovedImsServiceForUser(packageInfo.first, packageInfo.second); + break; + } case HANDLER_BOOT_COMPLETE: { if (!mBootCompletedHandlerRan) { mBootCompletedHandlerRan = true; @@ -517,11 +557,15 @@ } case HANDLER_OVERRIDE_IMS_SERVICE_CONFIG: { OverrideConfig config = (OverrideConfig) msg.obj; + setPackageNameUserOverride(config.packageName, config.userId); + Map<Integer, String> featureConfig = new HashMap<>(); + for (int featureType : config.featureTypes) { + featureConfig.put(featureType, config.packageName); + } if (config.isCarrierService) { - overrideCarrierService(config.slotId, - config.featureTypeToPackageMap); + overrideCarrierService(config.slotId, featureConfig); } else { - overrideDeviceService(config.featureTypeToPackageMap); + overrideDeviceService(featureConfig); } break; } @@ -534,6 +578,11 @@ clearCarrierServiceOverrides(msg.arg1); break; } + case HANDLER_USER_SWITCHED: { + UserHandle handle = (UserHandle) msg.obj; + Log.i(TAG, "onUserSwitched=" + handle); + maybeAddedImsService(null); + } default: break; } @@ -561,11 +610,16 @@ } @Override - public void onPermanentError(ComponentName name) { + public void onPermanentError(ComponentName name, UserHandle user) { Log.w(TAG, "onPermanentError: component=" + name); mEventLog.log("onPermanentError - error for " + name); - mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, - name.getPackageName()).sendToTarget(); + if (!mFeatureFlags.imsResolverUserAware()) { + mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, + name.getPackageName()).sendToTarget(); + } else { + mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE_PERM_ERROR, + new Pair<>(name.getPackageName(), user)).sendToTarget(); + } } }; @@ -573,6 +627,8 @@ // Array index corresponds to slot, per slot there is a feature->package name mapping. // should only be accessed from handler private final SparseArray<SparseArray<String>> mOverrideServices; + //Used during testing, restricts the ImsService to be bound on a specific user. + private final Map<String, UserHandle> mImsServiceTestUserRestrictions = new HashMap<>(); // Outer array index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController // Locked on mBoundServicesLock private final SparseArray<SparseArray<ImsServiceController>> mBoundImsServicesByFeature; @@ -628,6 +684,11 @@ } @VisibleForTesting + public void setActivityManagerProxy(ActivityManagerProxy proxy) { + mActivityManagerProxy = proxy; + } + + @VisibleForTesting public Handler getHandler() { return mHandler; } @@ -660,6 +721,10 @@ appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED); appChangedFilter.addDataScheme("package"); mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter); + if (mFeatureFlags.imsResolverUserAware()) { + mReceiverContext.registerReceiver(mUserReceiver, new IntentFilter( + Intent.ACTION_USER_SWITCHED)); + } mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter( CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); @@ -706,19 +771,24 @@ // carrier config changed. private void bindCarrierServicesIfAvailable() { boolean hasConfigChanged = false; + boolean pendingDynamicQuery = false; for (int slotId = 0; slotId < mNumSlots; slotId++) { int subId = mSubscriptionManagerProxy.getSubId(slotId); Map<Integer, String> featureMap = getImsPackageOverrideConfig(subId); for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) { String newPackageName = featureMap.getOrDefault(f, ""); if (!TextUtils.isEmpty(newPackageName)) { + Log.d(TAG, "bindCarrierServicesIfAvailable - carrier package found: " + + newPackageName + ", feature " + + ImsFeature.FEATURE_LOG_MAP.getOrDefault(f, "invalid") + + " on slot " + slotId); mEventLog.log("bindCarrierServicesIfAvailable - carrier package found: " + newPackageName + " on slot " + slotId); // Carrier configs are already available, so mark received. mCarrierConfigReceived = true; setSubId(slotId, subId); setCarrierConfiguredPackageName(newPackageName, slotId, f); - ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName); + ImsServiceInfo info = getVisibleImsServiceInfoFromCache(newPackageName); // We do not want to trigger feature configuration changes unless there is // already a valid carrier config change. if (info != null && info.featureFromMetadata) { @@ -726,11 +796,18 @@ } else { // Config will change when this query completes scheduleQueryForFeatures(info); + if (info != null) pendingDynamicQuery = true; } } } } - if (hasConfigChanged) calculateFeatureConfigurationChange(); + // we want to make sure that we are either pending to bind to a carrier configured service + // or bind to the device config if we potentially missed the carrier config changed + // indication. + if (hasConfigChanged || (mFeatureFlags.imsResolverUserAware() + && mCarrierConfigReceived && !pendingDynamicQuery)) { + calculateFeatureConfigurationChange(); + } } /** @@ -833,14 +910,15 @@ } // Used for testing only. - public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService, - Map<Integer, String> featureConfig) { + public boolean overrideImsServiceConfiguration(String packageName, int slotId, int userId, + boolean isCarrierService, int[] overrideFeatureTypes) { if (slotId < 0 || slotId >= mNumSlots) { Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!"); return false; } - OverrideConfig overrideConfig = new OverrideConfig(slotId, isCarrierService, featureConfig); + OverrideConfig overrideConfig = new OverrideConfig(packageName, slotId, userId, + isCarrierService, overrideFeatureTypes); Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, overrideConfig) .sendToTarget(); return true; @@ -881,10 +959,14 @@ } // not synchronized, access in handler ONLY. - private void removeOverridePackageName(int slotId) { + private Set<String> removeOverridePackageName(int slotId) { + Set<String> removedOverrides = new HashSet<>(); for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) { - getOverridePackageName(slotId).remove(f); + SparseArray<String> overrides = getOverridePackageName(slotId); + String packageName = overrides.removeReturnOld(f); + if (packageName != null) removedOverrides.add(packageName); } + return removedOverrides; } // not synchronized, access in handler ONLY. @@ -894,6 +976,22 @@ } // not synchronized, access in handler ONLY. + private void setPackageNameUserOverride(String packageName, int userId) { + if (packageName == null || packageName.isEmpty() || userId == UserHandle.USER_NULL) return; + Log.i(TAG, "setPackageNameUserOverride: set for " + packageName + ", user= " + userId); + mImsServiceTestUserRestrictions.put(packageName, UserHandle.of(userId)); + } + + // not synchronized, access in handler ONLY. + private void clearPackageNameUserOverride(String packageName) { + UserHandle handle = mImsServiceTestUserRestrictions.remove(packageName); + if (handle != null) { + Log.i(TAG, "clearPackageNameUserOverride: cleared for " + packageName + + "on user " + handle); + } + } + + // not synchronized, access in handler ONLY. private @Nullable String getOverridePackageName(int slotId, @ImsFeature.FeatureType int featureType) { return getOverridePackageName(slotId).get(featureType); @@ -927,13 +1025,14 @@ /** * Check the cached ImsServices that exist on this device to determine if there is a ImsService - * with the same package name that matches the provided configuration. + * with the same package name that matches the provided configuration and is configured to run + * in one of the active users. */ // not synchronized, access in handler ONLY. private boolean doesCachedImsServiceExist(String packageName, int slotId, @ImsFeature.FeatureType int featureType) { // Config exists, but the carrier ImsService also needs to support this feature - ImsServiceInfo info = getImsServiceInfoFromCache(packageName); + ImsServiceInfo info = getVisibleImsServiceInfoFromCache(packageName); return info != null && info.getSupportedFeatures().stream().anyMatch( feature -> feature.slotId == slotId && feature.featureType == featureType); } @@ -1073,7 +1172,8 @@ return null; } - private void putImsController(int slotId, int feature, ImsServiceController controller) { + private void putImsController(int slotId, int subId, int feature, + ImsServiceController controller) { if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID || feature >= ImsFeature.FEATURE_MAX) { Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId @@ -1088,9 +1188,9 @@ } mEventLog.log("putImsController - [" + slotId + ", " + ImsFeature.FEATURE_LOG_MAP.get(feature) + "] -> " + controller); - Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: " - + ImsFeature.FEATURE_LOG_MAP.get(feature) + " using package: " - + controller.getComponentName()); + Log.i(TAG, "ImsServiceController added on slot: " + slotId + ", subId: " + subId + + " with feature: " + ImsFeature.FEATURE_LOG_MAP.get(feature) + + " using package: " + controller.getComponentName()); services.put(feature, controller); } } @@ -1134,6 +1234,10 @@ // features. Will only be one (if it exists), since it is a set. ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name); if (match != null) { + if (mFeatureFlags.imsResolverUserAware()) { + match.users.clear(); + match.users.addAll(info.users); + } // for dynamic query the new "info" will have no supported features yet. Don't wipe // out the cache for the existing features or update yet. Instead start a query // for features dynamically. @@ -1141,9 +1245,8 @@ mEventLog.log("maybeAddedImsService - updating features for " + info.name + ": " + printFeatures(match.getSupportedFeatures()) + " -> " + printFeatures(info.getSupportedFeatures())); - Log.i(TAG, "Updating features in cached ImsService: " + info.name); - Log.d(TAG, "Updating features - Old features: " + match + " new features: " - + info); + Log.d(TAG, "Updating features in cached ImsService: " + info.name + + ", old features: " + match + " new features: " + info); // update features in the cache match.replaceFeatures(info.getSupportedFeatures()); requiresCalculation = true; @@ -1168,10 +1271,9 @@ if (requiresCalculation) calculateFeatureConfigurationChange(); } - // Remove the ImsService from the cache. This may have been due to the ImsService being removed - // from the device or was returning permanent errors when bound. + // Remove the ImsService from the cache due to the ImsService package being removed. // Called from the handler ONLY - private boolean maybeRemovedImsService(String packageName) { + private boolean maybeRemovedImsServiceOld(String packageName) { ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName); if (match != null) { mInstalledServicesCache.remove(match.name); @@ -1184,6 +1286,70 @@ return false; } + // Remove the ImsService from the cache due to the ImsService package being removed. + // Called from the handler ONLY + private boolean maybeRemovedImsService(String packageName) { + if (!mFeatureFlags.imsResolverUserAware()) { + return maybeRemovedImsServiceOld(packageName); + } + ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName); + if (match != null) { + List<ImsServiceInfo> imsServices = searchForImsServices(packageName, + match.controllerFactory); + ImsServiceInfo newMatch = imsServices.isEmpty() ? null : imsServices.getFirst(); + if (newMatch == null) { + clearPackageNameUserOverride(match.name.getPackageName()); + // The package doesn't exist anymore on any user, so remove + mInstalledServicesCache.remove(match.name); + mEventLog.log("maybeRemovedImsService - removing ImsService: " + match); + Log.i(TAG, "maybeRemovedImsService Removing ImsService for all users: " + + match.name); + unbindImsService(match); + } else { + // The Package exists on some users still, so modify the users + match.users.clear(); + match.users.addAll(newMatch.users); + mEventLog.log("maybeRemovedImsService - modifying ImsService users: " + match); + Log.i(TAG, "maybeRemovedImsService - Modifying ImsService users " + match); + // If this package still remains on some users, then it is possible we are unbinding + // an active ImsService, but the assumption here is that the package is being + // removed on an active user. Be safe and unbind now - we will rebind below if + // needed. + unbindImsService(match); + } + calculateFeatureConfigurationChange(); + return true; + } + return false; + } + + /** + * Remove the cached ImsService for a specific user. If there are no more users available after + * removing the specified user, remove the ImsService cache entry entirely. + */ + // Called from the handler ONLY + private boolean maybeRemovedImsServiceForUser(String packageName, UserHandle user) { + ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName); + if (match != null) { + mEventLog.log("maybeRemovedImsServiceForUser - removing ImsService " + match + + "for user " + user); + Log.i(TAG, "maybeRemovedImsServiceForUser: Removing ImsService " + + match + "for user " + user); + unbindImsService(match); + match.users.remove(user); + if (match.users.isEmpty()) { + mEventLog.log("maybeRemovedImsServiceForUser - no more users, removing " + + "ImsService " + match); + Log.i(TAG, "maybeRemovedImsServiceForUser - no more users, removing " + + "ImsService " + match); + mInstalledServicesCache.remove(match.name); + } + calculateFeatureConfigurationChange(); + return true; + } + return false; + } + private boolean isDeviceService(ImsServiceInfo info) { if (info == null) return false; synchronized (mDeviceServices) { @@ -1193,6 +1359,14 @@ private List<Integer> getSlotsForActiveCarrierService(ImsServiceInfo info) { if (info == null) return Collections.emptyList(); + if (mFeatureFlags.imsResolverUserAware()) { + UserHandle activeUser = getUserForBind(info); + if (activeUser == null) { + Log.d(TAG, "getSlotsForActiveCarrierService: ImsService " + info.name + "is not " + + "configured to run for any users, skipping..."); + return Collections.emptyList(); + } + } List<Integer> slots = new ArrayList<>(mNumSlots); for (int i = 0; i < mNumSlots; i++) { if (!TextUtils.isEmpty(getCarrierConfiguredPackageNames(i).values().stream() @@ -1222,7 +1396,7 @@ return searchMap.get(matchValue); } - private void bindImsServiceWithFeatures(ImsServiceInfo info, + private void bindImsServiceWithFeatures(ImsServiceInfo info, UserHandle user, Set<ImsFeatureConfiguration.FeatureSlotPair> features) { // Only bind if there are features that will be created by the service. if (shouldFeaturesCauseBind(features)) { @@ -1230,10 +1404,21 @@ ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info); SparseIntArray slotIdToSubIdMap = mSlotIdToSubIdMap.clone(); if (controller != null) { - Log.i(TAG, "ImsService connection exists for " + info.name + ", updating features " - + features); try { - controller.changeImsServiceFeatures(features, slotIdToSubIdMap); + if (!mFeatureFlags.imsResolverUserAware() + || Objects.equals(user, controller.getBoundUser())) { + Log.i(TAG, "ImsService connection exists for " + info.name + + ", updating features " + features); + controller.changeImsServiceFeatures(features, slotIdToSubIdMap); + } else { + // Changing a user is a pretty rare event, we need to unbind and rebind + // on the correct new user. + Log.i(TAG, "ImsService user changed for " + info.name + + ", rebinding on user " + user + ", features " + features); + controller.unbind(); + controller.bind(user, features, slotIdToSubIdMap); + } + // Features have been set, there was an error adding/removing. When the // controller recovers, it will add/remove again. } catch (RemoteException e) { @@ -1243,8 +1428,9 @@ controller = info.controllerFactory.create(mContext, info.name, this, mRepo, mFeatureFlags); Log.i(TAG, "Binding ImsService: " + controller.getComponentName() - + " with features: " + features); - controller.bind(features, slotIdToSubIdMap); + + "on user " + user + " with features: " + features + ", subIdMap: " + + slotIdToSubIdMap); + controller.bind(user, features, slotIdToSubIdMap); mEventLog.log("bindImsServiceWithFeatures - create new controller: " + controller); } @@ -1285,7 +1471,7 @@ imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream() .filter(feature -> info.name.getPackageName().equals( getCarrierConfiguredPackageName(feature.slotId, feature.featureType))) - .collect(Collectors.toList())); + .toList()); return imsFeaturesBySlot; } if (isDeviceService(info)) { @@ -1298,7 +1484,7 @@ // by the carrier ImsService. .filter(feature -> !doesCarrierConfigurationExist(feature.slotId, feature.featureType)) - .collect(Collectors.toList())); + .toList()); } return imsFeaturesBySlot; } @@ -1309,8 +1495,9 @@ * adds the ImsServiceController from the mBoundImsServicesByFeature structure. */ @Override - public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) { - putImsController(slotId, feature, controller); + public void imsServiceFeatureCreated(int slotId, int subId, int feature, + ImsServiceController controller) { + putImsController(slotId, subId, feature, controller); } /** @@ -1341,13 +1528,19 @@ } @Override - public void imsServiceBindPermanentError(ComponentName name) { + public void imsServiceBindPermanentError(ComponentName name, UserHandle user) { if (name == null) { return; } - Log.w(TAG, "imsServiceBindPermanentError: component=" + name); - mEventLog.log("imsServiceBindPermanentError - for " + name); - mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, name.getPackageName()).sendToTarget(); + Log.w(TAG, "imsServiceBindPermanentError: component=" + name + ", user=" + user); + mEventLog.log("imsServiceBindPermanentError - for " + name + ", user " + user); + if (!mFeatureFlags.imsResolverUserAware()) { + mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, + name.getPackageName()).sendToTarget(); + } else { + mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE_PERM_ERROR, + new Pair<>(name.getPackageName(), user)).sendToTarget(); + } } /** @@ -1381,7 +1574,10 @@ private void clearCarrierServiceOverrides(int slotId) { Log.i(TAG, "clearing carrier ImsService overrides"); mEventLog.log("clearing carrier ImsService overrides"); - removeOverridePackageName(slotId); + Set<String> removedPackages = removeOverridePackageName(slotId); + for (String pkg : removedPackages) { + clearPackageNameUserOverride(pkg); + } carrierConfigChanged(slotId, getSubId(slotId)); } @@ -1399,8 +1595,9 @@ + oldPackageName + " -> " + overridePackageName); mEventLog.log("overrideDeviceService - device package changed (override): " + oldPackageName + " -> " + overridePackageName); + clearPackageNameUserOverride(oldPackageName); setDeviceConfiguration(overridePackageName, featureType); - ImsServiceInfo info = getImsServiceInfoFromCache(overridePackageName); + ImsServiceInfo info = getVisibleImsServiceInfoFromCache(overridePackageName); if (info == null || info.featureFromMetadata) { requiresRecalc = true; } else { @@ -1430,7 +1627,7 @@ ArrayMap<String, ImsServiceInfo> featureDynamicImsPackages = new ArrayMap<>(); for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) { String packageName = getDeviceConfiguration(f); - ImsServiceInfo serviceInfo = getImsServiceInfoFromCache(packageName); + ImsServiceInfo serviceInfo = getVisibleImsServiceInfoFromCache(packageName); if (serviceInfo != null && !serviceInfo.featureFromMetadata && !featureDynamicImsPackages.containsKey(packageName)) { featureDynamicImsPackages.put(packageName, serviceInfo); @@ -1465,13 +1662,7 @@ setCarrierConfiguredPackageName(newPackageName, slotId, f); // Carrier config may have not changed, but we still want to kick off a recalculation // in case there has been a change to the supported device features. - ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName); - Log.i(TAG, "updateBoundServices - carrier package changed: " - + oldPackageName + " -> " + newPackageName + " on slot " + slotId - + ", hasConfigChanged=" + hasConfigChanged); - mEventLog.log("updateBoundServices - carrier package changed: " - + oldPackageName + " -> " + newPackageName + " on slot " + slotId - + ", hasConfigChanged=" + hasConfigChanged); + ImsServiceInfo info = getVisibleImsServiceInfoFromCache(newPackageName); if (info == null || info.featureFromMetadata) { hasConfigChanged = true; } else { @@ -1479,6 +1670,12 @@ scheduleQueryForFeatures(info); didQuerySchedule = true; } + Log.i(TAG, "updateBoundServices - carrier package changed: " + + oldPackageName + " -> " + newPackageName + " on slot " + slotId + + ", hasConfigChanged=" + hasConfigChanged); + mEventLog.log("updateBoundServices - carrier package changed: " + + oldPackageName + " -> " + newPackageName + " on slot " + slotId + + ", hasConfigChanged=" + hasConfigChanged); } if (hasConfigChanged) calculateFeatureConfigurationChange(); @@ -1530,7 +1727,7 @@ } private void scheduleQueryForFeatures(ComponentName name, int delayMs) { - ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName()); + ImsServiceInfo service = getVisibleImsServiceInfoFromCache(name.getPackageName()); if (service == null) { Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name); return; @@ -1614,6 +1811,12 @@ // Starts a dynamic query. Called from handler ONLY. private void startDynamicQuery(ImsServiceInfo service) { + UserHandle user = getUserForBind(service); + if (user == null) { + Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not" + + " running: " + service); + return; + } // if not current device/carrier service, don't perform query. If this changes, this method // will be called again. if (!isDeviceService(service) && getSlotsForActiveCarrierService(service).isEmpty()) { @@ -1622,7 +1825,7 @@ return; } mEventLog.log("startDynamicQuery - starting query for " + service); - boolean queryStarted = mFeatureQueryManager.startQuery(service.name, + boolean queryStarted = mFeatureQueryManager.startQuery(service.name, user, service.controllerFactory.getServiceInterface()); if (!queryStarted) { Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay."); @@ -1637,7 +1840,7 @@ // process complete dynamic query. Called from handler ONLY. private void dynamicQueryComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features) { - ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName()); + ImsServiceInfo service = getVisibleImsServiceInfoFromCache(name.getPackageName()); if (service == null) { Log.w(TAG, "dynamicQueryComplete: Couldn't find cached info for name: " + name); @@ -1683,17 +1886,92 @@ // Calculate the new configuration for the bound ImsServices. // Should ONLY be called from the handler. - private void calculateFeatureConfigurationChange() { + private void calculateFeatureConfigurationChangeOld() { for (ImsServiceInfo info : mInstalledServicesCache.values()) { Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info); if (shouldFeaturesCauseBind(features)) { - bindImsServiceWithFeatures(info, features); + bindImsServiceWithFeatures(info, mContext.getUser(), features); } else { unbindImsService(info); } } } + // Should ONLY be called from the handler. + private void calculateFeatureConfigurationChange() { + if (!mFeatureFlags.imsResolverUserAware()) { + calculateFeatureConfigurationChangeOld(); + return; + } + // There is an implicit assumption here that the ImsServiceController will remove itself + // from caches BEFORE adding a new one. If this assumption is broken, we will remove a valid + // ImsServiceController from the cache accidentally. To keep this assumption valid, we will + // iterate through the cache twice - first to unbind, then to bind and change features of + // existing ImsServiceControllers. This is a little inefficient, but there should be on the + // order of 10 installed ImsServices at most, so running through this list twice is + // reasonable vs the memory cost of caching binding vs unbinding services. + + // Unbind first if needed + for (ImsServiceInfo info : mInstalledServicesCache.values()) { + Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info); + UserHandle user = getUserForBind(info); + if (shouldFeaturesCauseBind(features) && user != null) continue; + unbindImsService(info); + } + // Bind/alter features second + for (ImsServiceInfo info : mInstalledServicesCache.values()) { + Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info); + UserHandle user = getUserForBind(info); + if (shouldFeaturesCauseBind(features) && user != null) { + bindImsServiceWithFeatures(info, user, features); + } + } + } + + /** + * Returns the UserHandle that should be used to bind the ImsService. + * + * @return The UserHandle of the user that telephony is running in if the + * ImsService is configured to run in that user, or the current active user + * if not. Returns null if the ImsService is not configured to run in any + * active user. + */ + private UserHandle getUserForBind(ImsServiceInfo info) { + if (!mFeatureFlags.imsResolverUserAware()) { + return mContext.getUser(); + } + UserHandle currentUser = mActivityManagerProxy.getCurrentUser(); + List<UserHandle> activeUsers = getActiveUsers().stream() + .filter(info.users::contains).toList(); + if (activeUsers.isEmpty()) return null; + // If there is a test restriction in place for this package, prioritize that restriction + UserHandle testRestriction = mImsServiceTestUserRestrictions.getOrDefault( + info.name.getPackageName(), null); + if (testRestriction != null && activeUsers.stream() + .anyMatch(u -> Objects.equals(u, testRestriction))) { + return testRestriction; + } + // Prioritize the User that Telephony is in, since it is always running + if (activeUsers.stream() + .anyMatch(u -> Objects.equals(u, mContext.getUser()))) { + return mContext.getUser(); + } + if (activeUsers.stream().anyMatch(u -> Objects.equals(u, currentUser))) { + return currentUser; + } + return null; + } + + /** + * Returns the set of full users that are currently active. + */ + private Set<UserHandle> getActiveUsers() { + Set<UserHandle> profiles = new HashSet<>(); + profiles.add(mContext.getUser()); + profiles.add(mActivityManagerProxy.getCurrentUser()); + return profiles; + } + private static String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) { StringBuilder featureString = new StringBuilder(); featureString.append(" features: ["); @@ -1711,8 +1989,24 @@ } /** - * Returns the ImsServiceInfo that matches the provided packageName. Visible for testing - * the ImsService caching functionality. + * Returns the ImsServiceInfo that matches the provided packageName if it belongs to a + * package that is visible as part of the set of active users. + */ + public ImsServiceInfo getVisibleImsServiceInfoFromCache(String packageName) { + ImsServiceInfo match = getImsServiceInfoFromCache(packageName); + if (!mFeatureFlags.imsResolverUserAware()) { + return match; + } + if (match == null) return null; + UserHandle targetUser = getUserForBind(match); + Log.d(TAG, "getVisibleImsServiceInfoFromCache: " + packageName + ", match=" + match + + ", targetUser=" + targetUser); + if (targetUser != null) return match; else return null; + } + + /** + * Returns the ImsServiceInfo that matches the provided packageName. This includes + * ImsServiceInfos that are not currently visible for the active users. */ @VisibleForTesting public ImsServiceInfo getImsServiceInfoFromCache(String packageName) { @@ -1738,6 +2032,12 @@ return infos; } + private ImsServiceInfo getInfoFromCache(List<ImsServiceInfo> infos, + ComponentName componentName) { + return infos.stream().filter(info -> Objects.equals(info.name, componentName)).findFirst() + .orElse(null); + } + private List<ImsServiceInfo> searchForImsServices(String packageName, ImsServiceControllerFactory controllerFactory) { List<ImsServiceInfo> infos = new ArrayList<>(); @@ -1745,62 +2045,84 @@ Intent serviceIntent = new Intent(controllerFactory.getServiceInterface()); serviceIntent.setPackage(packageName); + Set<UserHandle> profiles; + if (mFeatureFlags.imsResolverUserAware()) { + profiles = getActiveUsers(); + } else { + profiles = Collections.singleton(mContext.getUser()); + } + Log.v(TAG, "searchForImsServices: package=" + packageName + ", users=" + profiles); + PackageManager packageManager = mContext.getPackageManager(); - for (ResolveInfo entry : packageManager.queryIntentServicesAsUser( - serviceIntent, - PackageManager.GET_META_DATA, - UserHandle.of(UserHandle.myUserId()))) { - ServiceInfo serviceInfo = entry.serviceInfo; + for (UserHandle handle : profiles) { + for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(serviceIntent, + PackageManager.GET_META_DATA, handle)) { + ServiceInfo serviceInfo = entry.serviceInfo; - if (serviceInfo != null) { - ImsServiceInfo info = new ImsServiceInfo(); - info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name); - info.controllerFactory = controllerFactory; + if (serviceInfo != null) { + ComponentName name = new ComponentName(serviceInfo.packageName, + serviceInfo.name); + ImsServiceInfo info = getInfoFromCache(infos, name); + if (info != null) { + info.users.add(handle); + Log.d(TAG, "service modify users:" + info); + continue; + } else { + info = new ImsServiceInfo(name); + info.users.add(handle); + } + info.controllerFactory = controllerFactory; - // we will allow the manifest method of declaring manifest features in two cases: - // 1) it is the device overlay "default" ImsService, where the features do not - // change (the new method can still be used if the default does not define manifest - // entries). - // 2) using the "compat" ImsService, which only supports manifest query. - if (isDeviceService(info) - || mImsServiceControllerFactoryCompat == controllerFactory) { - if (serviceInfo.metaData != null) { - if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) { - info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_MMTEL); - // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined. - if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE, - false)) { - info.addFeatureForAllSlots(mNumSlots, - ImsFeature.FEATURE_EMERGENCY_MMTEL); + // we will allow the manifest method of declaring manifest features in two + // cases: + + // 1) it is the device overlay "default" ImsService, where the features do not + // change (the new method can still be used if the default does not define + // manifest entries). + // 2) using the "compat" ImsService, which only supports manifest query. + if (isDeviceService(info) + || mImsServiceControllerFactoryCompat == controllerFactory) { + if (serviceInfo.metaData != null) { + if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) { + info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_MMTEL); + // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined. + if (serviceInfo.metaData.getBoolean( + METADATA_EMERGENCY_MMTEL_FEATURE, + false)) { + info.addFeatureForAllSlots(mNumSlots, + ImsFeature.FEATURE_EMERGENCY_MMTEL); + } + } + if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) { + info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_RCS); } } - if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) { - info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_RCS); + // Only dynamic query if we are not a compat version of ImsService and the + // default service. + if (mImsServiceControllerFactoryCompat != controllerFactory + && info.getSupportedFeatures().isEmpty()) { + // metadata empty, try dynamic query instead + info.featureFromMetadata = false; } - } - // Only dynamic query if we are not a compat version of ImsService and the - // default service. - if (mImsServiceControllerFactoryCompat != controllerFactory - && info.getSupportedFeatures().isEmpty()) { - // metadata empty, try dynamic query instead + } else { + // We are a carrier service and not using the compat version of ImsService. info.featureFromMetadata = false; } - } else { - // We are a carrier service and not using the compat version of ImsService. - info.featureFromMetadata = false; - } - Log.i(TAG, "service name: " + info.name + ", manifest query: " - + info.featureFromMetadata); - // Check manifest permission to be sure that the service declares the correct - // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to - // true. - // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing. - if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE) - || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) { - infos.add(info); - } else { - Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: " - + info.name); + Log.d(TAG, "service name: " + info.name + ", manifest query: " + + info.featureFromMetadata + ", users: " + info.users); + // Check manifest permission to be sure that the service declares the correct + // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set + // to true. + // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing. + if (TextUtils.equals(serviceInfo.permission, + Manifest.permission.BIND_IMS_SERVICE) + || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, + false)) { + infos.add(info); + } else { + Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: " + + info.name); + } } } }
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java index ea8399f..37c10eb 100644 --- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java +++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -196,7 +196,7 @@ } if (mCallbacks != null) { // Will trigger an unbind. - mCallbacks.imsServiceBindPermanentError(getComponentName()); + mCallbacks.imsServiceBindPermanentError(getComponentName(), mBoundUser); } } @@ -217,7 +217,8 @@ /** * Called by ImsServiceController when a new MMTEL or RCS feature has been created. */ - void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller); + void imsServiceFeatureCreated(int slotId, int subId, int feature, + ImsServiceController controller); /** * Called by ImsServiceController when a new MMTEL or RCS feature has been removed. */ @@ -234,7 +235,7 @@ * Called by the ImsServiceController when there has been an error binding that is * not recoverable, such as the ImsService returning a null binder. */ - void imsServiceBindPermanentError(ComponentName name); + void imsServiceBindPermanentError(ComponentName name, UserHandle user); } /** @@ -273,6 +274,7 @@ private boolean mIsBound = false; private boolean mIsBinding = false; + private UserHandle mBoundUser = null; // Set of a pair of slotId->feature private Set<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures; private SparseIntArray mSlotIdToSubIdMap; @@ -337,7 +339,7 @@ if (mIsBound) { return; } - bind(mImsFeatures, mSlotIdToSubIdMap); + bind(mBoundUser, mImsFeatures, mSlotIdToSubIdMap); } } }; @@ -413,17 +415,18 @@ * @return {@link true} if the service is in the process of being bound, {@link false} if it * has failed. */ - public boolean bind(Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet, - SparseIntArray slotIdToSubIdMap) { + public boolean bind(UserHandle user, Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet, + SparseIntArray slotIdToSubIdMap) { synchronized (mLock) { if (!mIsBound && !mIsBinding) { mIsBinding = true; + mBoundUser = user; sanitizeFeatureConfig(imsFeatureSet); mImsFeatures = imsFeatureSet; mSlotIdToSubIdMap = slotIdToSubIdMap; // Set the number of slots that support the feature mImsEnablementTracker.setNumOfSlots(mSlotIdToSubIdMap.size()); - grantPermissionsToService(); + grantPermissionsToService(user); Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent( mComponentName); mImsServiceConnection = new ImsServiceConnection(); @@ -432,8 +435,8 @@ mLocalLog.log("binding " + imsFeatureSet); Log.i(LOG_TAG, "Binding ImsService:" + mComponentName); try { - boolean bindSucceeded = mContext.bindService(imsServiceIntent, - mImsServiceConnection, serviceFlags); + boolean bindSucceeded = mContext.bindServiceAsUser(imsServiceIntent, + mImsServiceConnection, serviceFlags, user); if (!bindSucceeded) { mLocalLog.log(" binding failed, retrying in " + mBackoff.getCurrentDelay() + " mS"); @@ -482,6 +485,7 @@ changeImsServiceFeatures(new HashSet<>(), mSlotIdToSubIdMap); mIsBound = false; mIsBinding = false; + mBoundUser = null; setServiceController(null); unbindService(); } @@ -608,6 +612,13 @@ } /** + * @return The UserHandle that this controller is bound to or null if bound to no service. + */ + public UserHandle getBoundUser() { + return mBoundUser; + } + + /** * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and * trigger ImsFeature status updates. */ @@ -766,7 +777,7 @@ // Grant runtime permissions to ImsService. PermissionManager ensures that the ImsService is // system/signed before granting permissions. - private void grantPermissionsToService() { + private void grantPermissionsToService(UserHandle user) { mLocalLog.log("grant permissions to " + getComponentName()); Log.i(LOG_TAG, "Granting Runtime permissions to:" + getComponentName()); String[] pkgToGrant = {mComponentName.getPackageName()}; @@ -774,8 +785,7 @@ if (mPermissionManager != null) { CountDownLatch latch = new CountDownLatch(1); mPermissionManager.grantDefaultPermissionsToEnabledImsServices( - pkgToGrant, UserHandle.of(UserHandle.myUserId()), Runnable::run, - isSuccess -> { + pkgToGrant, user, Runnable::run, isSuccess -> { if (isSuccess) { latch.countDown(); } else { @@ -807,7 +817,8 @@ Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId); } // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController - mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this); + mCallbacks.imsServiceFeatureCreated(featurePair.slotId, subId, featurePair.featureType, + this); } // This method should only be called when synchronized on mLock @@ -978,10 +989,11 @@ @Override public String toString() { synchronized (mLock) { - return "[ImsServiceController: componentName=" + getComponentName() + ", features=" - + mImsFeatures + ", isBinding=" + mIsBinding + ", isBound=" + mIsBound - + ", serviceController=" + getImsServiceController() + ", rebindDelay=" - + getRebindDelay() + "]"; + return "[ImsServiceController: componentName=" + getComponentName() + ", boundUser=" + + mBoundUser + ", features=" + mImsFeatures + ", isBinding=" + mIsBinding + + ", isBound=" + mIsBound + ", serviceController=" + getImsServiceController() + + ", rebindDelay=" + getRebindDelay() + ", slotToSubIdMap=" + mSlotIdToSubIdMap + + "]"; } }
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java index 564cdcc..a4b4f46 100644 --- a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java +++ b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
@@ -21,6 +21,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.os.UserHandle; import android.telephony.ims.aidl.IImsServiceController; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.util.Log; @@ -42,14 +43,16 @@ private static final String LOG_TAG = "ImsServiceFeatureQuery"; private final ComponentName mName; + private final UserHandle mUser; private final String mIntentFilter; // Track the status of whether or not the Service has died in case we need to permanently // unbind (see onNullBinding below). private boolean mIsServiceConnectionDead = false; - ImsServiceFeatureQuery(ComponentName name, String intentFilter) { + ImsServiceFeatureQuery(ComponentName name, UserHandle user, String intentFilter) { mName = name; + mUser = user; mIntentFilter = intentFilter; } @@ -62,7 +65,8 @@ Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName); int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE | Context.BIND_IMPORTANT; - boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags); + boolean bindStarted = mContext.bindServiceAsUser(imsServiceIntent, this, + serviceFlags, mUser); if (!bindStarted) { // Docs say to unbind if this fails. cleanup(); @@ -78,7 +82,7 @@ } else { Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null."); cleanup(); - mListener.onPermanentError(name); + mListener.onPermanentError(name, mUser); } } @@ -103,7 +107,7 @@ // permanently unbind and instead let the automatic rebind occur. if (mIsServiceConnectionDead) return; cleanup(); - mListener.onPermanentError(name); + mListener.onPermanentError(name, mUser); } private void queryImsFeatures(IImsServiceController controller) { @@ -154,7 +158,7 @@ /** * Called when a query has failed due to a permanent error and should not be retried. */ - void onPermanentError(ComponentName name); + void onPermanentError(ComponentName name, UserHandle user); } // Maps an active ImsService query (by Package Name String) its query. @@ -171,16 +175,17 @@ /** * Starts an ImsService feature query for the ComponentName and Intent specified. * @param name The ComponentName of the ImsService being queried. + * @param user The User associated with the request. * @param intentFilter The Intent filter that the ImsService specified. * @return true if the query started, false if it was unable to start. */ - public boolean startQuery(ComponentName name, String intentFilter) { + public boolean startQuery(ComponentName name, UserHandle user, String intentFilter) { synchronized (mLock) { if (mActiveQueries.containsKey(name)) { // We already have an active query, wait for it to return. return true; } - ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter); + ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, user, intentFilter); mActiveQueries.put(name, query); return query.start(); }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java index 26b6e18..d47f05b 100644 --- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -1797,7 +1797,8 @@ dialArgs.intentExtras); } }; - EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(onComplete); + EmergencyStateTracker.getInstance().exitEmergencyCallbackMode(onComplete, + TelephonyManager.STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED); } else { try { getEcbmInterface().exitEmergencyCallbackMode(); @@ -2884,7 +2885,7 @@ boolean rejectCall = false; if (mFeatureFlags.preventHangupDuringCallMerge()) { - if (imsCall.isCallSessionMergePending()) { + if (imsCall != null && imsCall.isCallSessionMergePending()) { if (DBG) log("hangup call failed during call merge"); throw new CallStateException("can not hangup during call merge");
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java index cf44aaf..fdd0c0a 100644 --- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java +++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -177,7 +177,6 @@ /** Maximum number of Satellite relevant stats to store between pulls. */ private final int mMaxNumSatelliteStats; - private final int mMaxNumSatelliteControllerStats = 1; private final int mMaxNumCarrierRoamingSatelliteSessionStats = 1; /** Maximum number of data network validation to store during pulls. */ @@ -710,76 +709,75 @@ /** Adds a new {@link SatelliteController} to the storage. */ public synchronized void addSatelliteControllerStats(SatelliteController stats) { - // SatelliteController is a single data point - SatelliteController[] atomArray = mAtoms.satelliteController; - if (atomArray == null || atomArray.length == 0) { - atomArray = new SatelliteController[] {new SatelliteController()}; + // find existing satellite controller atom with same carrier ID. + SatelliteController existingStats = find(stats); + if (existingStats != null) { + existingStats.countOfSatelliteServiceEnablementsSuccess + += stats.countOfSatelliteServiceEnablementsSuccess; + existingStats.countOfSatelliteServiceEnablementsFail + += stats.countOfSatelliteServiceEnablementsFail; + existingStats.countOfOutgoingDatagramSuccess + += stats.countOfOutgoingDatagramSuccess; + existingStats.countOfOutgoingDatagramFail + += stats.countOfOutgoingDatagramFail; + existingStats.countOfIncomingDatagramSuccess + += stats.countOfIncomingDatagramSuccess; + existingStats.countOfIncomingDatagramFail + += stats.countOfIncomingDatagramFail; + existingStats.countOfDatagramTypeSosSmsSuccess + += stats.countOfDatagramTypeSosSmsSuccess; + existingStats.countOfDatagramTypeSosSmsFail + += stats.countOfDatagramTypeSosSmsFail; + existingStats.countOfDatagramTypeLocationSharingSuccess + += stats.countOfDatagramTypeLocationSharingSuccess; + existingStats.countOfDatagramTypeLocationSharingFail + += stats.countOfDatagramTypeLocationSharingFail; + existingStats.countOfProvisionSuccess + += stats.countOfProvisionSuccess; + existingStats.countOfProvisionFail + += stats.countOfProvisionFail; + existingStats.countOfDeprovisionSuccess + += stats.countOfDeprovisionSuccess; + existingStats.countOfDeprovisionFail + += stats.countOfDeprovisionFail; + existingStats.totalServiceUptimeSec + += stats.totalServiceUptimeSec; + existingStats.totalBatteryConsumptionPercent + += stats.totalBatteryConsumptionPercent; + existingStats.totalBatteryChargedTimeSec + += stats.totalBatteryChargedTimeSec; + existingStats.countOfDemoModeSatelliteServiceEnablementsSuccess + += stats.countOfDemoModeSatelliteServiceEnablementsSuccess; + existingStats.countOfDemoModeSatelliteServiceEnablementsFail + += stats.countOfDemoModeSatelliteServiceEnablementsFail; + existingStats.countOfDemoModeOutgoingDatagramSuccess + += stats.countOfDemoModeOutgoingDatagramSuccess; + existingStats.countOfDemoModeOutgoingDatagramFail + += stats.countOfDemoModeOutgoingDatagramFail; + existingStats.countOfDemoModeIncomingDatagramSuccess + += stats.countOfDemoModeIncomingDatagramSuccess; + existingStats.countOfDemoModeIncomingDatagramFail + += stats.countOfDemoModeIncomingDatagramFail; + existingStats.countOfDatagramTypeKeepAliveSuccess + += stats.countOfDatagramTypeKeepAliveSuccess; + existingStats.countOfDatagramTypeKeepAliveFail + += stats.countOfDatagramTypeKeepAliveFail; + existingStats.countOfAllowedSatelliteAccess += stats.countOfAllowedSatelliteAccess; + existingStats.countOfDisallowedSatelliteAccess + += stats.countOfDisallowedSatelliteAccess; + existingStats.countOfSatelliteAccessCheckFail += stats.countOfSatelliteAccessCheckFail; + + existingStats.isProvisioned = stats.isProvisioned; + + existingStats.countOfSatelliteAllowedStateChangedEvents + += stats.countOfSatelliteAllowedStateChangedEvents; + existingStats.countOfSuccessfulLocationQueries += + stats.countOfSuccessfulLocationQueries; + existingStats.countOfFailedLocationQueries += stats.countOfFailedLocationQueries; + } else { + mAtoms.satelliteController = insertAtRandomPlace(mAtoms.satelliteController, stats, + mMaxNumSatelliteStats); } - - SatelliteController atom = atomArray[0]; - atom.countOfSatelliteServiceEnablementsSuccess - += stats.countOfSatelliteServiceEnablementsSuccess; - atom.countOfSatelliteServiceEnablementsFail - += stats.countOfSatelliteServiceEnablementsFail; - atom.countOfOutgoingDatagramSuccess - += stats.countOfOutgoingDatagramSuccess; - atom.countOfOutgoingDatagramFail - += stats.countOfOutgoingDatagramFail; - atom.countOfIncomingDatagramSuccess - += stats.countOfIncomingDatagramSuccess; - atom.countOfIncomingDatagramFail - += stats.countOfIncomingDatagramFail; - atom.countOfDatagramTypeSosSmsSuccess - += stats.countOfDatagramTypeSosSmsSuccess; - atom.countOfDatagramTypeSosSmsFail - += stats.countOfDatagramTypeSosSmsFail; - atom.countOfDatagramTypeLocationSharingSuccess - += stats.countOfDatagramTypeLocationSharingSuccess; - atom.countOfDatagramTypeLocationSharingFail - += stats.countOfDatagramTypeLocationSharingFail; - atom.countOfProvisionSuccess - += stats.countOfProvisionSuccess; - atom.countOfProvisionFail - += stats.countOfProvisionFail; - atom.countOfDeprovisionSuccess - += stats.countOfDeprovisionSuccess; - atom.countOfDeprovisionFail - += stats.countOfDeprovisionFail; - atom.totalServiceUptimeSec - += stats.totalServiceUptimeSec; - atom.totalBatteryConsumptionPercent - += stats.totalBatteryConsumptionPercent; - atom.totalBatteryChargedTimeSec - += stats.totalBatteryChargedTimeSec; - atom.countOfDemoModeSatelliteServiceEnablementsSuccess - += stats.countOfDemoModeSatelliteServiceEnablementsSuccess; - atom.countOfDemoModeSatelliteServiceEnablementsFail - += stats.countOfDemoModeSatelliteServiceEnablementsFail; - atom.countOfDemoModeOutgoingDatagramSuccess - += stats.countOfDemoModeOutgoingDatagramSuccess; - atom.countOfDemoModeOutgoingDatagramFail - += stats.countOfDemoModeOutgoingDatagramFail; - atom.countOfDemoModeIncomingDatagramSuccess - += stats.countOfDemoModeIncomingDatagramSuccess; - atom.countOfDemoModeIncomingDatagramFail - += stats.countOfDemoModeIncomingDatagramFail; - atom.countOfDatagramTypeKeepAliveSuccess - += stats.countOfDatagramTypeKeepAliveSuccess; - atom.countOfDatagramTypeKeepAliveFail - += stats.countOfDatagramTypeKeepAliveFail; - atom.countOfAllowedSatelliteAccess += stats.countOfAllowedSatelliteAccess; - atom.countOfDisallowedSatelliteAccess += stats.countOfDisallowedSatelliteAccess; - atom.countOfSatelliteAccessCheckFail += stats.countOfSatelliteAccessCheckFail; - - atom.isProvisioned = stats.isProvisioned; - atom.carrierId = stats.carrierId; - - atom.countOfSatelliteAllowedStateChangedEvents - += stats.countOfSatelliteAllowedStateChangedEvents; - atom.countOfSuccessfulLocationQueries += stats.countOfSuccessfulLocationQueries; - atom.countOfFailedLocationQueries += stats.countOfFailedLocationQueries; - - mAtoms.satelliteController = atomArray; saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); } @@ -861,26 +859,24 @@ /** Adds a new {@link CarrierRoamingSatelliteControllerStats} to the storage. */ public synchronized void addCarrierRoamingSatelliteControllerStats( CarrierRoamingSatelliteControllerStats stats) { - // CarrierRoamingSatelliteController is a single data point - CarrierRoamingSatelliteControllerStats[] atomArray = - mAtoms.carrierRoamingSatelliteControllerStats; - if (atomArray == null || atomArray.length == 0) { - atomArray = new CarrierRoamingSatelliteControllerStats[] {new - CarrierRoamingSatelliteControllerStats()}; + CarrierRoamingSatelliteControllerStats existingStats = find(stats); + if (existingStats != null) { + existingStats.configDataSource = stats.configDataSource; + existingStats.countOfEntitlementStatusQueryRequest += + stats.countOfEntitlementStatusQueryRequest; + existingStats.countOfSatelliteConfigUpdateRequest += + stats.countOfSatelliteConfigUpdateRequest; + existingStats.countOfSatelliteNotificationDisplayed += + stats.countOfSatelliteNotificationDisplayed; + existingStats.satelliteSessionGapMinSec = stats.satelliteSessionGapMinSec; + existingStats.satelliteSessionGapAvgSec = stats.satelliteSessionGapAvgSec; + existingStats.satelliteSessionGapMaxSec = stats.satelliteSessionGapMaxSec; + existingStats.isDeviceEntitled = stats.isDeviceEntitled; + } else { + mAtoms.carrierRoamingSatelliteControllerStats = insertAtRandomPlace( + mAtoms.carrierRoamingSatelliteControllerStats, stats, mMaxNumSatelliteStats); } - CarrierRoamingSatelliteControllerStats atom = atomArray[0]; - atom.configDataSource = stats.configDataSource; - atom.countOfEntitlementStatusQueryRequest += stats.countOfEntitlementStatusQueryRequest; - atom.countOfSatelliteConfigUpdateRequest += stats.countOfSatelliteConfigUpdateRequest; - atom.countOfSatelliteNotificationDisplayed += stats.countOfSatelliteNotificationDisplayed; - atom.satelliteSessionGapMinSec = stats.satelliteSessionGapMinSec; - atom.satelliteSessionGapAvgSec = stats.satelliteSessionGapAvgSec; - atom.satelliteSessionGapMaxSec = stats.satelliteSessionGapMaxSec; - atom.carrierId = stats.carrierId; - atom.isDeviceEntitled = stats.isDeviceEntitled; - - mAtoms.carrierRoamingSatelliteControllerStats = atomArray; saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); } @@ -1827,7 +1823,7 @@ atoms.outgoingShortCodeSms = sanitizeAtoms(atoms.outgoingShortCodeSms, OutgoingShortCodeSms.class, mMaxOutgoingShortCodeSms); atoms.satelliteController = sanitizeAtoms(atoms.satelliteController, - SatelliteController.class, mMaxNumSatelliteControllerStats); + SatelliteController.class, mMaxNumSatelliteStats); atoms.satelliteSession = sanitizeAtoms(atoms.satelliteSession, SatelliteSession.class, mMaxNumSatelliteStats); atoms.satelliteIncomingDatagram = sanitizeAtoms(atoms.satelliteIncomingDatagram, @@ -1850,7 +1846,7 @@ mMaxNumSatelliteStats); atoms.carrierRoamingSatelliteControllerStats = sanitizeAtoms( atoms.carrierRoamingSatelliteControllerStats, - CarrierRoamingSatelliteControllerStats.class, mMaxNumSatelliteControllerStats); + CarrierRoamingSatelliteControllerStats.class, mMaxNumSatelliteStats); atoms.satelliteEntitlement = sanitizeAtoms(atoms.satelliteEntitlement, SatelliteEntitlement.class, mMaxNumSatelliteStats); atoms.satelliteConfigUpdater = sanitizeAtoms(atoms.satelliteConfigUpdater, @@ -2392,6 +2388,34 @@ } /** + * Returns SatelliteController atom that has same carrier_id value or + * {@code null} if does not exist. + */ + private @Nullable SatelliteController find(SatelliteController key) { + for (SatelliteController stats : mAtoms.satelliteController) { + if (stats.carrierId == key.carrierId) { + return stats; + } + } + return null; + } + + /** + * Returns CarrierRoamingSatelliteControllerStats atom that has same carrier_id value or + * {@code null} if does not exist. + */ + private @Nullable CarrierRoamingSatelliteControllerStats find( + CarrierRoamingSatelliteControllerStats key) { + for (CarrierRoamingSatelliteControllerStats stats : + mAtoms.carrierRoamingSatelliteControllerStats) { + if (stats.carrierId == key.carrierId) { + return stats; + } + } + return null; + } + + /** * Returns SatelliteEntitlement atom that has same values or {@code null} if it does not exist. */ private @Nullable SatelliteEntitlement find(SatelliteEntitlement key) {
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java index a816906..acd3fd1 100644 --- a/src/java/com/android/internal/telephony/satellite/DatagramController.java +++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -16,6 +16,7 @@ package com.android.internal.telephony.satellite; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE; @@ -282,7 +283,7 @@ mSendDatagramTransferState = datagramTransferState; mSendPendingCount = sendPendingCount; mSendErrorCode = errorCode; - notifyDatagramTransferStateChangedToSessionController(); + notifyDatagramTransferStateChangedToSessionController(mDatagramType); mPointingAppController.updateSendDatagramTransferState(mSendSubId, mDatagramType, mSendDatagramTransferState, mSendPendingCount, mSendErrorCode); retryPollPendingDatagramsInDemoMode(); @@ -311,21 +312,23 @@ * @param receivePendingCount The number of datagrams that are currently pending to be received. * @param errorCode If datagram transfer failed, the reason for failure. */ - public void updateReceiveStatus(int subId, + public void updateReceiveStatus(int subId, @SatelliteManager.DatagramType int datagramType, @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState, int receivePendingCount, int errorCode) { synchronized (mLock) { plogd("updateReceiveStatus" + " subId: " + subId + + " datagramType: " + datagramType + " datagramTransferState: " + datagramTransferState + " receivePendingCount: " + receivePendingCount + " errorCode: " + errorCode); mReceiveSubId = subId; + mDatagramType = datagramType; mReceiveDatagramTransferState = datagramTransferState; mReceivePendingCount = receivePendingCount; mReceiveErrorCode = errorCode; - notifyDatagramTransferStateChangedToSessionController(); + notifyDatagramTransferStateChangedToSessionController(mDatagramType); mPointingAppController.updateReceiveDatagramTransferState(mReceiveSubId, mReceiveDatagramTransferState, mReceivePendingCount, mReceiveErrorCode); retryPollPendingDatagramsInDemoMode(); @@ -365,6 +368,24 @@ } /** + * Notify SMS received. + * + * @param subId The subId of the subscription used to receive SMS + */ + public void onSmsReceived(int subId) { + // To keep exist notification flow, need to call with each state. + updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS, + SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); + updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS, + SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS, + getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); + updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS, + SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); + } + + /** * Set whether the device is aligned with the satellite. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @@ -399,6 +420,15 @@ && mSatelltieModemState == SATELLITE_MODEM_STATE_NOT_CONNECTED) { return false; } + boolean allowCheckMessageInNotConnected = + mContext.getResources().getBoolean( + R.bool.config_satellite_allow_check_message_in_not_connected); + if (datagramType == DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS + && mSatelltieModemState == SATELLITE_MODEM_STATE_NOT_CONNECTED + && allowCheckMessageInNotConnected + && mFeatureFlags.carrierRoamingNbIotNtn()) { + return false; + } if (mSatelltieModemState != SATELLITE_MODEM_STATE_CONNECTED && mSatelltieModemState != SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING) { return true; @@ -565,14 +595,16 @@ return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false)); } - private void notifyDatagramTransferStateChangedToSessionController() { + private void notifyDatagramTransferStateChangedToSessionController(int datagramType) { SatelliteSessionController sessionController = SatelliteSessionController.getInstance(); if (sessionController == null) { ploge("notifyDatagramTransferStateChangeToSessionController: SatelliteSessionController" + " is not initialized yet"); } else { - sessionController.onDatagramTransferStateChanged( - mSendDatagramTransferState, mReceiveDatagramTransferState); + synchronized (mLock) { + sessionController.onDatagramTransferStateChanged( + mSendDatagramTransferState, mReceiveDatagramTransferState, datagramType); + } } } @@ -616,7 +648,7 @@ } }; pollPendingSatelliteDatagrams( - SatelliteController.getInstance().getHighestPrioritySubscrption(), + SatelliteController.getInstance().getSelectedSatelliteSubId(), internalCallback); } }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java index 682123f..113c3ee 100644 --- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java +++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -16,9 +16,12 @@ package com.android.internal.telephony.satellite; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN; import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED; +import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED; +import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE; @@ -75,6 +78,7 @@ private static final int EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT = 7; private static final int CMD_SEND_SMS = 8; private static final int EVENT_SEND_SMS_DONE = 9; + private static final int EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT = 10; private static final Long TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE = TimeUnit.SECONDS.toMillis(10); @NonNull private static DatagramDispatcher sInstance; @NonNull private final Context mContext; @@ -130,6 +134,15 @@ private int mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN; @Nullable private PersistentLogger mPersistentLogger = null; + @GuardedBy("mLock") + private int mModemState = SATELLITE_MODEM_STATE_UNKNOWN; + @GuardedBy("mLock") + private boolean mHasEnteredConnectedState = false; + @GuardedBy("mLock") + private boolean mShouldPollMtSms = false; + @GuardedBy("mLock") + private boolean mIsMtSmsPollingThrottled = false; + /** * Create the DatagramDispatcher singleton instance. * @param context The Context to use to create the DatagramDispatcher. @@ -402,6 +415,16 @@ break; } + case EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT: { + synchronized (mLock) { + mIsMtSmsPollingThrottled = false; + if (mIsAligned && mModemState == SATELLITE_MODEM_STATE_CONNECTED) { + sendMtSmsPollingMessage(); + } + } + break; + } + default: plogw("DatagramDispatcherHandler: unexpected message code: " + msg.what); break; @@ -491,6 +514,10 @@ mIsAligned = isAligned; plogd("setDeviceAlignedWithSatellite: " + mIsAligned); if (isAligned && mIsDemoMode) handleEventSatelliteAligned(); + if (isAligned && !mIsMtSmsPollingThrottled + && mModemState == SATELLITE_MODEM_STATE_CONNECTED) { + sendMtSmsPollingMessage(); + } } } @@ -598,6 +625,13 @@ pendingDatagram.iterator().next().getValue(); if (mDatagramController.needsWaitingForSatelliteConnected(datagramArg.datagramType)) { plogd("sendPendingDatagrams: wait for satellite connected"); + mDatagramController.updateSendStatus(datagramArg.subId, + datagramArg.datagramType, + SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + getPendingMessagesCount(), + SatelliteManager.SATELLITE_RESULT_SUCCESS); + startDatagramWaitForConnectedStateTimer( + datagramArg.datagramType); return; } @@ -719,6 +753,8 @@ private void reportSendDatagramCompleted(@NonNull SendSatelliteDatagramArgument argument, @NonNull @SatelliteManager.SatelliteResult int resultCode) { + long datagramTransmissionTime = argument.datagramStartTime > 0 + ? (System.currentTimeMillis() - argument.datagramStartTime) : 0; SatelliteStats.getInstance().onSatelliteOutgoingDatagramMetrics( new SatelliteStats.SatelliteOutgoingDatagramParams.Builder() .setDatagramType(argument.datagramType) @@ -726,15 +762,15 @@ .setDatagramSizeBytes(argument.getDatagramRoundedSizeBytes()) /* In case pending datagram has not been attempted to send to modem interface. transfer time will be 0. */ - .setDatagramTransferTimeMillis(argument.datagramStartTime > 0 - ? (System.currentTimeMillis() - argument.datagramStartTime) : 0) + .setDatagramTransferTimeMillis(datagramTransmissionTime) .setIsDemoMode(mIsDemoMode) .setCarrierId(SatelliteController.getInstance().getSatelliteCarrierId()) .build()); if (resultCode == SatelliteManager.SATELLITE_RESULT_SUCCESS) { mControllerMetricsStats.reportOutgoingDatagramSuccessCount(argument.datagramType, mIsDemoMode); - mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram(argument.datagramType); + mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram(argument.datagramType, + datagramTransmissionTime); } else { mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType, mIsDemoMode); @@ -759,6 +795,7 @@ */ public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) { synchronized (mLock) { + mModemState = state; if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) { plogd("onSatelliteModemStateChanged: cleaning up resources"); @@ -767,10 +804,22 @@ sendPendingMessages(); } - if (state == SATELLITE_MODEM_STATE_CONNECTED - && isDatagramWaitForConnectedStateTimerStarted()) { - stopDatagramWaitForConnectedStateTimer(); - sendPendingMessages(); + if (state == SATELLITE_MODEM_STATE_CONNECTED) { + mHasEnteredConnectedState = true; + if (isDatagramWaitForConnectedStateTimerStarted()) { + stopDatagramWaitForConnectedStateTimer(); + sendPendingMessages(); + } + if (mIsAligned && !mIsMtSmsPollingThrottled) { + sendMtSmsPollingMessage(); + } + } + + if (state == SATELLITE_MODEM_STATE_NOT_CONNECTED) { + if (mHasEnteredConnectedState) { + mHasEnteredConnectedState = false; + mShouldPollMtSms = shouldPollMtSms(); + } } } } @@ -781,7 +830,7 @@ mSendingInProgress = false; mIsEmergencyCommunicationEstablished = false; - int subId = SatelliteController.getInstance().getHighestPrioritySubscrption(); + int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); if (getPendingMessagesCount() > 0) { mDatagramController.updateSendStatus(subId, mLastSendRequestDatagramType, @@ -803,6 +852,10 @@ mSendSatelliteDatagramRequest = null; mIsAligned = false; mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN; + mModemState = SATELLITE_MODEM_STATE_UNKNOWN; + mHasEnteredConnectedState = false; + mShouldPollMtSms = false; + stopMtSmsPollingThrottle(); } /** @return {@code true} if already sent an emergency datagram during a session. */ @@ -867,7 +920,7 @@ @SatelliteManager.DatagramType int datagramType) { plogw("Timed out to wait for satellite connected before sending datagrams"); synchronized (mLock) { - int subId = SatelliteController.getInstance().getHighestPrioritySubscrption(); + int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); // Update send status mDatagramController.updateSendStatus(subId, datagramType, @@ -1051,22 +1104,17 @@ * carrier roaming nb iot ntn SMS. */ public void sendSms(@NonNull PendingRequest pendingSms) { - Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone(); - if (satellitePhone == null) { - ploge("sendSms: satellitePhone is null."); - return; - } - SatelliteController.getInstance().startPointingUI(); - int subId = satellitePhone.getSubId(); - long messageId = pendingSms.messageId; + int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); + long messageId = pendingSms.uniqueMessageId; plogd("sendSms: subId=" + subId + " messageId:" + messageId); synchronized (mLock) { // Add SMS to pending list mPendingSmsMap.put(messageId, pendingSms); - int datagramType = SatelliteManager.DATAGRAM_TYPE_SMS; + int datagramType = pendingSms.isMtSmsPolling ? + DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS; mLastSendRequestDatagramType = datagramType; if (mDatagramController.needsWaitingForSatelliteConnected(datagramType)) { @@ -1098,27 +1146,30 @@ return; } - Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone(); - if (satellitePhone == null) { - ploge("sendPendingSms: satellitePhone is null."); - return; - } - int subId = satellitePhone.getSubId(); - + int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); Set<Entry<Long, PendingRequest>> pendingSms = null; if (!mSendingInProgress) { pendingSms = mPendingSmsMap.entrySet(); } if (pendingSms != null && pendingSms.iterator().hasNext()) { + PendingRequest pendingRequest = pendingSms.iterator().next().getValue(); + int datagramType = pendingRequest.isMtSmsPolling + ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS; if (mDatagramController.needsWaitingForSatelliteConnected(DATAGRAM_TYPE_SMS)) { plogd("sendPendingSms: wait for satellite connected"); + mDatagramController.updateSendStatus(subId, + datagramType, + SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + getPendingMessagesCount(), + SatelliteManager.SATELLITE_RESULT_SUCCESS); + startDatagramWaitForConnectedStateTimer(datagramType); return; } mSendingInProgress = true; - PendingRequest pendingRequest = pendingSms.iterator().next().getValue(); - mDatagramController.updateSendStatus(subId, DATAGRAM_TYPE_SMS, + mDatagramController.updateSendStatus(subId, + datagramType, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS); sendMessage(obtainMessage(CMD_SEND_SMS, pendingRequest)); @@ -1181,18 +1232,30 @@ private void handleEventSendSmsDone(int subId, long messageId, boolean success) { synchronized (mLock) { + PendingRequest pendingSms = mPendingSmsMap.remove(messageId); + if (pendingSms == null) { + // Just return, the SMS is not sent by DatagramDispatcher such as Data SMS + plogd("handleEventSendSmsDone there is no pendingSms for messageId=" + messageId); + return; + } + mSendingInProgress = false; - mPendingSmsMap.remove(messageId); - int datagramType = DATAGRAM_TYPE_SMS; + int datagramType = pendingSms.isMtSmsPolling + ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS; plogd("handleEventSendSmsDone subId=" + subId + " messageId=" + messageId - + " success=" + success); + + " success=" + success + " datagramType=" + datagramType); + if (success) { - // Update send status for current datagram + // Update send status mDatagramController.updateSendStatus(subId, datagramType, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS, getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS); + if (datagramType == DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS) { + startMtSmsPollingThrottle(); + } } else { + // Update send status mDatagramController.updateSendStatus(subId, datagramType, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, getPendingMessagesCount(), SATELLITE_RESULT_NETWORK_ERROR); @@ -1208,6 +1271,69 @@ } } + private boolean isEnabledMtSmsPolling() { + return mContext.getResources().getBoolean(R.bool.config_enabled_mt_sms_polling); + } + + private long getMtSmsPollingThrottleMillis() { + return mContext.getResources().getInteger( + R.integer.config_mt_sms_polling_throttle_millis); + } + + private boolean shouldPollMtSms() { + SatelliteController satelliteController = SatelliteController.getInstance(); + Phone satellitePhone = satelliteController.getSatellitePhone(); + return isEnabledMtSmsPolling() + && satelliteController.shouldSendSmsToDatagramDispatcher(satellitePhone); + } + + @GuardedBy("mLock") + private void sendMtSmsPollingMessage() { + if (!mShouldPollMtSms) { + return; + } + + plogd("sendMtSmsPollingMessage"); + mShouldPollMtSms = false; + + for (Entry<Long, PendingRequest> entry : mPendingSmsMap.entrySet()) { + PendingRequest pendingRequest = entry.getValue(); + if (pendingRequest.isMtSmsPolling) { + plogd("sendMtSmsPollingMessage: mPendingSmsMap already has the polling message."); + return; + } + } + + Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone(); + if (satellitePhone == null) { + ploge("sendMtSmsPollingMessage: satellitePhone is null."); + return; + } + + SmsDispatchersController smsDispatchersController = + satellitePhone.getSmsDispatchersController(); + if (smsDispatchersController == null) { + ploge("sendMtSmsPollingMessage: smsDispatchersController is null."); + return; + } + + smsDispatchersController.sendMtSmsPollingMessage(); + } + + @GuardedBy("mLock") + private void startMtSmsPollingThrottle() { + plogd("startMtSmsPollingThrottle"); + mIsMtSmsPollingThrottled = true; + sendMessageDelayed(obtainMessage(EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT), + getMtSmsPollingThrottleMillis()); + } + + @GuardedBy("mLock") + private void stopMtSmsPollingThrottle() { + mIsMtSmsPollingThrottled = false; + removeMessages(EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT); + } + private static void logd(@NonNull String log) { Rlog.d(TAG, log); }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java index 5a89c40..75771d0 100644 --- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java +++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -352,10 +352,12 @@ if (pendingCount <= 0 && satelliteDatagram == null) { sInstance.mDatagramController.updateReceiveStatus(mSubId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE, pendingCount, SatelliteManager.SATELLITE_RESULT_SUCCESS); } else if (satelliteDatagram != null) { sInstance.mDatagramController.updateReceiveStatus(mSubId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS, pendingCount, SatelliteManager.SATELLITE_RESULT_SUCCESS); @@ -374,8 +376,13 @@ }); } + // Send the captured data about incoming datagram to metric + sInstance.reportMetrics(satelliteDatagram, + SatelliteManager.SATELLITE_RESULT_SUCCESS); + if (pendingCount <= 0) { sInstance.mDatagramController.updateReceiveStatus(mSubId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, pendingCount, SatelliteManager.SATELLITE_RESULT_SUCCESS); } else { @@ -390,10 +397,6 @@ internalCallback::accept); sInstance.pollPendingSatelliteDatagramsInternal(mSubId, callback); } - - // Send the captured data about incoming datagram to metric - sInstance.reportMetrics(satelliteDatagram, - SatelliteManager.SATELLITE_RESULT_SUCCESS); break; } @@ -473,10 +476,12 @@ plogd("EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE error: " + error); if (error != SatelliteManager.SATELLITE_RESULT_SUCCESS) { mDatagramController.updateReceiveStatus(request.subId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED, mDatagramController.getReceivePendingCount(), error); mDatagramController.updateReceiveStatus(request.subId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, mDatagramController.getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); @@ -615,6 +620,7 @@ mPendingPollSatelliteDatagramsRequest = new DatagramReceiverHandlerRequest( callback, SatelliteServiceUtils.getPhone(), subId); mDatagramController.updateReceiveStatus(subId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, mDatagramController.getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); @@ -624,6 +630,7 @@ } mDatagramController.updateReceiveStatus(subId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, mDatagramController.getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); @@ -696,14 +703,16 @@ stopDatagramWaitForConnectedStateTimer(); } - int subId = SatelliteController.getInstance().getHighestPrioritySubscrption(); + int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); if (mDatagramController.isReceivingDatagrams()) { mDatagramController.updateReceiveStatus(subId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED, mDatagramController.getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED); } mDatagramController.updateReceiveStatus(subId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0, SatelliteManager.SATELLITE_RESULT_SUCCESS); cleanupDemoModeResources(); @@ -739,8 +748,8 @@ (int) (Math.round((double) sizeBytes / ROUNDING_UNIT) * ROUNDING_UNIT); } datagramTransferTime = (System.currentTimeMillis() - mDatagramTransferStartTime); - mDatagramTransferStartTime = 0; } + mDatagramTransferStartTime = 0; SatelliteStats.getInstance().onSatelliteIncomingDatagramMetrics( new SatelliteStats.SatelliteIncomingDatagramParams.Builder() @@ -857,11 +866,13 @@ plogw("Timed out to wait for satellite connected before polling datagrams"); mDatagramController.updateReceiveStatus(mPendingPollSatelliteDatagramsRequest.subId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED, mDatagramController.getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE); mDatagramController.updateReceiveStatus(mPendingPollSatelliteDatagramsRequest.subId, + SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, mDatagramController.getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
diff --git a/src/java/com/android/internal/telephony/satellite/DemoSimulator.java b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java index 7b64c61..d908597 100644 --- a/src/java/com/android/internal/telephony/satellite/DemoSimulator.java +++ b/src/java/com/android/internal/telephony/satellite/DemoSimulator.java
@@ -287,12 +287,12 @@ * and {@code false} to disable * @param errorCallback The callback to receive the error code result of the operation. */ - public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled, + public void enableTerrestrialNetworkScanWhileSatelliteModeIsOn(boolean enabled, @NonNull IIntegerConsumer errorCallback) { try { errorCallback.accept(SatelliteResult.SATELLITE_RESULT_SUCCESS); } catch (RemoteException e) { - loge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e); + loge("enableTerrestrialNetworkScanWhileSatelliteModeIsOn: RemoteException " + e); } }
diff --git a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java index dfc7919..4b53178 100644 --- a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java +++ b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
@@ -52,7 +52,8 @@ logd("Registered to satellite PLMN " + satellitePlmn); networkRegistrationInfo.setIsNonTerrestrialNetwork(true); networkRegistrationInfo.setAvailableServices( - satelliteController.getSupportedSatelliteServices(subId, satellitePlmn)); + satelliteController.getSupportedSatelliteServicesForPlmn( + subId, satellitePlmn)); break; } }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java index 8ae6cf4..85f7039 100644 --- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java +++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -16,9 +16,13 @@ package com.android.internal.telephony.satellite; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; import static android.provider.Settings.ACTION_SATELLITE_SETTING; +import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC; import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL; import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_TYPE; +import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL; import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT; import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT; import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY; @@ -26,8 +30,11 @@ import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE; import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT; import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL; +import static android.telephony.CarrierConfigManager.KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT; +import static android.telephony.CarrierConfigManager.KEY_SATELLITE_DATA_SUPPORT_MODE_INT; +import static android.telephony.CarrierConfigManager.KEY_SATELLITE_DISPLAY_NAME_STRING; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_NIDD_APN_NAME_STRING; @@ -36,6 +43,8 @@ import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL; +import static android.telephony.CarrierConfigManager.KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT; +import static android.telephony.CarrierConfigManager.KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY; import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER; import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS; import static android.telephony.SubscriptionManager.isValidSubscriptionId; @@ -49,6 +58,7 @@ import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_ERROR; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT; +import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NO_VALID_SATELLITE_SUBSCRIPTION; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS; @@ -58,6 +68,7 @@ import android.annotation.ArrayRes; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AlertDialog; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -71,9 +82,13 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; +import android.hardware.devicestate.DeviceState; +import android.hardware.devicestate.DeviceStateManager; import android.net.Uri; import android.net.wifi.WifiManager; import android.nfc.NfcAdapter; @@ -103,6 +118,7 @@ import android.provider.Telephony; import android.telecom.TelecomManager; import android.telephony.AccessNetworkConstants; +import android.telephony.AnomalyReporter; import android.telephony.CarrierConfigManager; import android.telephony.DropBoxManagerLoggerBackend; import android.telephony.NetworkRegistrationInfo; @@ -112,6 +128,7 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.TelephonyRegistryManager; import android.telephony.satellite.INtnSignalStrengthCallback; import android.telephony.satellite.ISatelliteCapabilitiesCallback; import android.telephony.satellite.ISatelliteDatagramCallback; @@ -119,6 +136,7 @@ import android.telephony.satellite.ISatelliteProvisionStateCallback; import android.telephony.satellite.ISatelliteSupportedStateCallback; import android.telephony.satellite.ISatelliteTransmissionUpdateCallback; +import android.telephony.satellite.ISelectedNbIotSatelliteSubscriptionCallback; import android.telephony.satellite.NtnSignalStrength; import android.telephony.satellite.SatelliteCapabilities; import android.telephony.satellite.SatelliteDatagram; @@ -127,12 +145,14 @@ import android.telephony.satellite.SatelliteSubscriberInfo; import android.telephony.satellite.SatelliteSubscriberProvisionStatus; import android.telephony.satellite.SatelliteSubscriptionInfo; +import android.telephony.satellite.SystemSelectionSpecifier; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.uwb.UwbManager; +import android.view.WindowManager; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -154,6 +174,7 @@ import com.android.internal.telephony.satellite.metrics.SessionMetricsStats; import com.android.internal.telephony.subscription.SubscriptionInfoInternal; import com.android.internal.telephony.subscription.SubscriptionManagerService; +import com.android.internal.telephony.util.ArrayUtils; import com.android.internal.telephony.util.TelephonyUtils; import com.android.internal.util.FunctionalUtils; @@ -166,6 +187,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -200,10 +223,16 @@ public static final int TIMEOUT_TYPE_DEMO_POINTING_ALIGNED_DURATION_MILLIS = 2; /** This is used by CTS to override demo pointing not aligned duration. */ public static final int TIMEOUT_TYPE_DEMO_POINTING_NOT_ALIGNED_DURATION_MILLIS = 3; - + /** This is used by CTS to override evaluate esos profiles prioritization duration. */ + public static final int TIMEOUT_TYPE_EVALUATE_ESOS_PROFILES_PRIORITIZATION_DURATION_MILLIS = 4; /** Key used to read/write OEM-enabled satellite provision status in shared preferences. */ private static final String OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY = "oem_enabled_satellite_provision_status_key"; + /** Key used to read/write default messages application NTN SMS support + * in shared preferences. */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public static final String NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY = + "ntn_sms_supported_by_messages_app_key"; public static final long DEFAULT_CARRIER_EMERGENCY_CALL_WAIT_FOR_CONNECTION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(30); @@ -212,6 +241,14 @@ private static final long WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(23); + /** + * Delay SatelliteEnable request when network selection auto. current RIL not verified to + * response right after network selection auto changed. Some RIL has delay for waiting in-svc + * with Automatic selection request. + */ + private static final long DELAY_WAITING_SET_NETWORK_SELECTION_AUTO_MILLIS = + TimeUnit.SECONDS.toMillis(1); + /** Message codes used in handleMessage() */ //TODO: Move the Commands and events related to position updates to PointingAppController private static final int CMD_START_SATELLITE_TRANSMISSION_UPDATES = 1; @@ -264,6 +301,12 @@ EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT = 52; private static final int EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT = 53; protected static final int EVENT_SATELLITE_REGISTRATION_FAILURE = 54; + private static final int EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED = 55; + private static final int EVENT_SET_NETWORK_SELECTION_AUTO_DONE = 56; + private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 57; + private static final int CMD_UPDATE_SYSTEM_SELECTION_CHANNELS = 58; + private static final int EVENT_UPDATE_SYSTEM_SELECTION_CHANNELS_DONE = 59; + private static final int EVENT_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_CHANGED = 60; @NonNull private static SatelliteController sInstance; @NonNull private final Context mContext; @@ -346,6 +389,8 @@ new AtomicBoolean(false); private final AtomicBoolean mRegisteredForSatelliteRegistrationFailure = new AtomicBoolean(false); + private final AtomicBoolean mRegisteredForTerrestrialNetworkAvailableChanged = + new AtomicBoolean(false); /** * Map key: subId, value: callback to get error code of the provision request. */ @@ -380,9 +425,22 @@ */ private final ConcurrentHashMap<IBinder, ISatelliteModemStateCallback> mSatelliteRegistrationFailureListeners = new ConcurrentHashMap<>(); - private final Object mIsSatelliteSupportedLock = new Object(); + /** + * Map key: binder of the callback, value: callback to receive terrestrial network + * available changed + */ + private final ConcurrentHashMap<IBinder, ISatelliteModemStateCallback> + mTerrestrialNetworkAvailableChangedListeners = new ConcurrentHashMap<>(); + /** + * Map key: binder of the callback, value: callback to receive selected NB IOT satellite + * subscription changed + */ + private final ConcurrentHashMap<IBinder, ISelectedNbIotSatelliteSubscriptionCallback> + mSelectedNbIotSatelliteSubscriptionChangedListeners = new ConcurrentHashMap<>(); + + protected final Object mIsSatelliteSupportedLock = new Object(); @GuardedBy("mIsSatelliteSupportedLock") - private Boolean mIsSatelliteSupported = null; + protected Boolean mIsSatelliteSupported = null; private boolean mIsDemoModeEnabled = false; private boolean mIsEmergency = false; private final Object mIsSatelliteEnabledLock = new Object(); @@ -396,11 +454,11 @@ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected boolean mRadioOffRequested = false; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - protected final Object mSatelliteViaOemProvisionLock = new Object(); + protected final Object mDeviceProvisionLock = new Object(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - @GuardedBy("mSatelliteViaOemProvisionLock") - protected Boolean mIsSatelliteViaOemProvisioned = null; - @GuardedBy("mSatelliteViaOemProvisionLock") + @GuardedBy("mDeviceProvisionLock") + protected Boolean mIsDeviceProvisioned = null; + @GuardedBy("mDeviceProvisionLock") private Boolean mOverriddenIsSatelliteViaOemProvisioned = null; private final Object mSatelliteCapabilitiesLock = new Object(); @GuardedBy("mSatelliteCapabilitiesLock") @@ -438,6 +496,13 @@ * {@code true} for enabled and {@code false} for disabled. */ @NonNull private final Map<Integer, Boolean> mIsSatelliteAttachEnabledForCarrierArrayPerSub = new HashMap<>(); + /** Key: subId, value: (key: Regional satellite config Id string, value: Integer + * arrays of earfcns in the corresponding regions.) + */ + @GuardedBy("mRegionalSatelliteEarfcnsLock") + @NonNull private final Map<Integer, Map<String, Set<Integer>>> + mRegionalSatelliteEarfcns = new HashMap<>(); + @NonNull private final Object mRegionalSatelliteEarfcnsLock = new Object(); @NonNull private final FeatureFlags mFeatureFlags; @NonNull private final Object mSatelliteConnectedLock = new Object(); /** Key: Subscription ID; Value: Last satellite connected time */ @@ -481,6 +546,10 @@ @NonNull private final Map<Integer, List<Integer>> mSatModeCapabilitiesForCarrierRoaming = new HashMap<>(); + @GuardedBy("mSatelliteConnectedLock") + private SparseArray<NtnSignalStrength> mLastNotifiedCarrierRoamingNtnSignalStrength = + new SparseArray<>(); + /** * This is used for testing only. When mEnforcedEmergencyCallToSatelliteHandoverType is valid, * Telephony will ignore the IMS registration status and cellular availability, and always send @@ -510,6 +579,21 @@ * carrierPlmnList. */ @GuardedBy("mSupportedSatelliteServicesLock") private final SparseArray<List<String>> mMergedPlmnListPerCarrier = new SparseArray<>(); + /** Key Subscription ID, value : map to plmn info with related data plan. */ + @GuardedBy("mSupportedSatelliteServicesLock") + SparseArray<Map<String, Integer>> mEntitlementDataPlanMapPerCarrier = new SparseArray<>(); + /** Key Subscription ID, value : map to plmn info with related service type. */ + @GuardedBy("mSupportedSatelliteServicesLock") + SparseArray<Map<String, List<Integer>>> mEntitlementServiceTypeMapPerCarrier = + new SparseArray<>(); + /** Key Subscription ID, value : map to plmn info with related service policy for data service */ + @GuardedBy("mSupportedSatelliteServicesLock") + SparseArray<Map<String, Integer>> mEntitlementDataServicePolicyMapPerCarrier = + new SparseArray<>(); + /** Key Subscription ID, value : map to plmn info with related service policy for voice service */ + @GuardedBy("mSupportedSatelliteServicesLock") + SparseArray<Map<String, Integer>> mEntitlementVoiceServicePolicyMapPerCarrier = + new SparseArray<>(); private static AtomicLong sNextSatelliteEnableRequestId = new AtomicLong(0); // key : subscriberId, value : provisioned or not. @GuardedBy("mSatelliteTokenProvisionedLock") @@ -521,7 +605,15 @@ // key : priority, low value is high, value : List<SubscriptionInfo> @GuardedBy("mSatelliteTokenProvisionedLock") @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - protected Map<Integer, List<SubscriptionInfo>> mSubsInfoListPerPriority = new HashMap<>(); + protected TreeMap<Integer, List<SubscriptionInfo>> mSubsInfoListPerPriority = new TreeMap<>(); + // List of subscriber information and status at the time of last evaluation + @GuardedBy("mSatelliteTokenProvisionedLock") + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + private List<SatelliteSubscriberProvisionStatus> mLastEvaluatedSubscriberProvisionStatus = + new ArrayList<>(); + // The ID of the satellite subscription that has highest priority and is provisioned. + @GuardedBy("mSatelliteTokenProvisionedLock") + private int mSelectedSatelliteSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; // The last ICC ID that framework configured to modem. @GuardedBy("mSatelliteTokenProvisionedLock") private String mLastConfiguredIccId; @@ -530,11 +622,13 @@ private long mWaitTimeForSatelliteEnablingResponse; private long mDemoPointingAlignedDurationMillis; private long mDemoPointingNotAlignedDurationMillis; + private long mEvaluateEsosProfilesPrioritizationDurationMillis; private final Object mLock = new Object(); @GuardedBy("mLock") private long mLastEmergencyCallTime; private long mSatelliteEmergencyModeDurationMillis; private static final int DEFAULT_SATELLITE_EMERGENCY_MODE_DURATION_SECONDS = 300; + private AlertDialog mNetworkSelectionModeAutoDialog = null; /** Key used to read/write satellite system notification done in shared preferences. */ private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY = @@ -573,6 +667,22 @@ private static final String HOW_IT_WORKS_BUTTON = "how_it_works_button"; private static final String ACTION_NOTIFICATION_CLICK = "action_notification_click"; private static final String ACTION_NOTIFICATION_DISMISS = "action_notification_dismiss"; + private AtomicBoolean mOverrideNtnEligibility; + private String mDefaultSmsPackageName = ""; + private String mSatelliteGatewayServicePackageName = ""; + + private final Object mNtnSmsSupportedByMessagesAppLock = new Object(); + @GuardedBy("mNtnSmsSupportedByMessagesAppLock") + private Boolean mNtnSmsSupportedByMessagesApp = null; + + private final Object mSatelliteModemStateLock = new Object(); + @GuardedBy("mSatelliteModemStateLock") + @SatelliteManager.SatelliteModemState + private int mSatelliteModemState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN; + + // Data Plan types at entitlement for the plmn allowed + public static final int SATELLITE_DATA_PLAN_METERED = 0; + public static final int SATELLITE_DATA_PLAN_UNMETERED = 1; private BroadcastReceiver mDefaultSmsSubscriptionChangedBroadcastReceiver = new BroadcastReceiver() { @Override @@ -585,6 +695,110 @@ } }; + private BroadcastReceiver mPackageStateChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mDefaultSmsPackageName = Telephony.Sms.getDefaultSmsPackage(mContext); + mSatelliteGatewayServicePackageName = getConfigSatelliteGatewayServicePackage(); + String schemeSpecificPart = intent.getData().getSchemeSpecificPart(); + plogd("packageStateChanged: " + intent.getData().toString() + + " DefaultSmsPackageName:" + mDefaultSmsPackageName); + + if (!schemeSpecificPart.equals(mSatelliteGatewayServicePackageName) + && !schemeSpecificPart.equals(mDefaultSmsPackageName)) { + plogv("Neither SMS or SatelliteGateway package"); + return; + } + int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(true); + if (activeSubIds != null) { + for (int activeSubId : activeSubIds) { + plogd("mPackageStateChangedReceiver: activeSubId= " + activeSubId); + handleCarrierRoamingNtnAvailableServicesChanged(activeSubId); + } + } else { + ploge("mPackageStateChangedReceiver: activeSubIds is null"); + } + } + }; + + // List of device states returned from DeviceStateManager to determine if running on a foldable + // device. + private List<DeviceState> mDeviceStates = new ArrayList(); + + public static final int RESULT_RECEIVER_COUNT_ANOMALY_THRESHOLD = 100; + protected final Object mResultReceiverTotalCountLock = new Object(); + @GuardedBy("mResultReceiverTotalCountLock") + protected int mResultReceiverTotalCount; + @GuardedBy("mResultReceiverTotalCountLock") + protected HashMap<String, Integer> mResultReceiverCountPerMethodMap = new HashMap<>(); + + // Satellite anomaly uuid -- ResultReceiver count threshold exceeded + private final UUID mAnomalyUnexpectedResultReceiverCountUUID = + UUID.fromString("e268f22d-9bba-4d27-b76a-1c7f5b42e241"); + + private UUID generateAnomalyUnexpectedResultReceiverCountUUID(int error, int errorCode) { + long lerror = error; + long lerrorCode = errorCode; + return new UUID(mAnomalyUnexpectedResultReceiverCountUUID.getMostSignificantBits(), + mAnomalyUnexpectedResultReceiverCountUUID.getLeastSignificantBits() + + ((lerrorCode << 32) + lerror)); + } + + /** + * Increments the ResultReceiver count and logs the caller information. + * If the count exceeds the threshold, it reports an anomaly via AnomalyReporter. + * + * @param caller The caller information that created the ResultReceiver + * (e.g., class name and method name) + */ + public void incrementResultReceiverCount(String caller) { + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + synchronized (mResultReceiverTotalCountLock) { + mResultReceiverTotalCount++; + logd("[incrementResultReceiverCount] : " + caller + + " | ResultReceiver total count= " + mResultReceiverTotalCount); + mResultReceiverCountPerMethodMap.compute(caller, + (k, v) -> v == null ? 1 : v + 1); + + if (mResultReceiverTotalCount > RESULT_RECEIVER_COUNT_ANOMALY_THRESHOLD) { + loge("[mResultReceiverTotalCount] is exceeds limits : " + + mResultReceiverTotalCount); + loge("[incrementResultReceiverCount] mResultReceiverCountPerMethodMap is " + + mResultReceiverCountPerMethodMap); + AnomalyReporter.reportAnomaly( + generateAnomalyUnexpectedResultReceiverCountUUID(0, 0), + "Satellite ResultReceiver total count= " + + mResultReceiverTotalCount + " exceeds limit."); + } + } + } else { + logd("[incrementResultReceiverCount]: carrierRoamingNbIotNtn is not enabled"); + } + } + + /** + * Decrements the ResultReceiver count and logs the caller information. + * Prevents the count from going below zero. + * + * @param caller The caller information that released the ResultReceiver + * (e.g., class name and method name) + */ + public void decrementResultReceiverCount(String caller) { + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + synchronized (mResultReceiverTotalCountLock) { + if (mResultReceiverTotalCount > 0) { + mResultReceiverTotalCount--; + } + logd("[decrementResultReceiverCount] : " + caller + + " | ResultReceiver total count=" + mResultReceiverTotalCount); + mResultReceiverCountPerMethodMap.computeIfPresent(caller, + (k, v) -> v > 0 ? v - 1 : v); + } + } else { + logd("[decrementResultReceiverCount]: carrierRoamingNbIotNtn is not enabled"); + } + } + /** * @return The singleton instance of SatelliteController. */ @@ -669,6 +883,7 @@ registerForPendingDatagramCount(); registerForSatelliteModemStateChanged(); registerForServiceStateChanged(); + registerForSignalStrengthChanged(); mContentResolver = mContext.getContentResolver(); mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); @@ -689,6 +904,7 @@ } mSatellitePlmnListFromOverlayConfig = readSatellitePlmnsFromOverlayConfig(); + registerApplicationStateChanged(); updateSupportedSatelliteServicesForActiveSubscriptions(); mCarrierConfigChangeListener = (slotIndex, subId, carrierId, specificCarrierId) -> @@ -712,15 +928,25 @@ mDSM.registerForSignalStrengthReportDecision(this, CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING, null); + loadSatelliteSharedPreferences(); + if (mSharedPreferences != null) { + synchronized (mNtnSmsSupportedByMessagesAppLock) { + mNtnSmsSupportedByMessagesApp = mSharedPreferences.getBoolean( + NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY, false); + } + } + mWaitTimeForSatelliteEnablingResponse = getWaitForSatelliteEnablingResponseTimeoutMillis(); mDemoPointingAlignedDurationMillis = getDemoPointingAlignedDurationMillisFromResources(); mDemoPointingNotAlignedDurationMillis = getDemoPointingNotAlignedDurationMillisFromResources(); mSatelliteEmergencyModeDurationMillis = getSatelliteEmergencyModeDurationFromOverlayConfig(context); + mEvaluateEsosProfilesPrioritizationDurationMillis = + getEvaluateEsosProfilesPrioritizationDurationMillis(); sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION), - /* delayMillis= */ TimeUnit.MINUTES.toMillis(1)); + mEvaluateEsosProfilesPrioritizationDurationMillis); SubscriptionManager subscriptionManager = mContext.getSystemService( SubscriptionManager.class); @@ -731,6 +957,9 @@ } registerDefaultSmsSubscriptionChangedBroadcastReceiver(); updateSatelliteProvisionedStatePerSubscriberId(); + if (android.hardware.devicestate.feature.flags.Flags.deviceStatePropertyMigration()) { + mDeviceStates = getSupportedDeviceStates(); + } } class SatelliteSubscriptionsChangedListener @@ -902,15 +1131,13 @@ public void onStateChanged(int state, int reason) { plogd("UwbAdapterStateCallback#onStateChanged() called, state = " + toString(state)); plogd("Adapter state changed reason " + String.valueOf(reason)); - synchronized (mRadioStateLock) { - if (state == UwbManager.AdapterStateCallback.STATE_DISABLED) { - mUwbStateEnabled = false; - evaluateToSendSatelliteEnabledSuccess(); - } else { - mUwbStateEnabled = true; - } - plogd("mUwbStateEnabled: " + mUwbStateEnabled); + if (state == UwbManager.AdapterStateCallback.STATE_DISABLED) { + setUwbEnabledState(false); + evaluateToSendSatelliteEnabledSuccess(); + } else { + setUwbEnabledState(true); } + plogd("mUwbStateEnabled: " + getUwbEnabledState()); } } @@ -927,50 +1154,47 @@ case BluetoothAdapter.ACTION_STATE_CHANGED: int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); - synchronized (mRadioStateLock) { - boolean currentBTStateEnabled = mBTStateEnabled; - if (btState == BluetoothAdapter.STATE_OFF) { - mBTStateEnabled = false; - evaluateToSendSatelliteEnabledSuccess(); - } else if (btState == BluetoothAdapter.STATE_ON) { - mBTStateEnabled = true; - } - if (currentBTStateEnabled != mBTStateEnabled) { - plogd("mBTStateEnabled=" + mBTStateEnabled); - } + boolean currentBTStateEnabled = getBTEnabledState(); + if (btState == BluetoothAdapter.STATE_OFF) { + setBTEnabledState(false); + evaluateToSendSatelliteEnabledSuccess(); + } else if (btState == BluetoothAdapter.STATE_ON) { + setBTEnabledState(true); + } + + if (currentBTStateEnabled != getBTEnabledState()) { + plogd("mBTStateEnabled=" + getBTEnabledState()); } break; case NfcAdapter.ACTION_ADAPTER_STATE_CHANGED: int nfcState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, -1); - synchronized (mRadioStateLock) { - boolean currentNfcStateEnabled = mNfcStateEnabled; - if (nfcState == NfcAdapter.STATE_ON) { - mNfcStateEnabled = true; - } else if (nfcState == NfcAdapter.STATE_OFF) { - mNfcStateEnabled = false; - evaluateToSendSatelliteEnabledSuccess(); - } - if (currentNfcStateEnabled != mNfcStateEnabled) { - plogd("mNfcStateEnabled=" + mNfcStateEnabled); - } + boolean currentNfcStateEnabled = getNfcEnabledState(); + if (nfcState == NfcAdapter.STATE_ON) { + setNfcEnabledState(true); + } else if (nfcState == NfcAdapter.STATE_OFF) { + setNfcEnabledState(false); + evaluateToSendSatelliteEnabledSuccess(); + } + + if (currentNfcStateEnabled != getNfcEnabledState()) { + plogd("mNfcStateEnabled=" + getNfcEnabledState()); } break; case WifiManager.WIFI_STATE_CHANGED_ACTION: int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN); - synchronized (mRadioStateLock) { - boolean currentWifiStateEnabled = mWifiStateEnabled; - if (wifiState == WifiManager.WIFI_STATE_ENABLED) { - mWifiStateEnabled = true; - } else if (wifiState == WifiManager.WIFI_STATE_DISABLED) { - mWifiStateEnabled = false; - evaluateToSendSatelliteEnabledSuccess(); - } - if (currentWifiStateEnabled != mWifiStateEnabled) { - plogd("mWifiStateEnabled=" + mWifiStateEnabled); - } + boolean currentWifiStateEnabled = getWifiEnabledState(); + if (wifiState == WifiManager.WIFI_STATE_ENABLED) { + setWifiEnabledState(true); + } else if (wifiState == WifiManager.WIFI_STATE_DISABLED) { + setWifiEnabledState(false); + evaluateToSendSatelliteEnabledSuccess(); + } + + if (currentWifiStateEnabled != getWifiEnabledState()) { + plogd("mWifiStateEnabled=" + getWifiEnabledState()); } break; default: @@ -1041,6 +1265,18 @@ } } + private static final class UpdateSystemSelectionChannelsArgument { + @NonNull List<SystemSelectionSpecifier> mSystemSelectionSpecifiers; + @NonNull ResultReceiver mResult; + + UpdateSystemSelectionChannelsArgument( + @NonNull List<SystemSelectionSpecifier> systemSelectionSpecifiers, + @NonNull ResultReceiver result) { + this.mSystemSelectionSpecifiers = systemSelectionSpecifiers; + this.mResult = result; + } + } + /** * Arguments to send to SatelliteTransmissionUpdate registrants */ @@ -1193,11 +1429,12 @@ if (mNeedsSatellitePointing) { mPointingAppController.removeListenerForPointingUI(); } + + if (!isWaitingForSatelliteModemOff()) { + moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_SUCCESS); + } + synchronized (mSatelliteEnabledRequestLock) { - if (!mWaitingForSatelliteModemOff) { - moveSatelliteToOffStateAndCleanUpResources( - SATELLITE_RESULT_SUCCESS); - } mWaitingForDisableSatelliteModemResponse = false; } } @@ -1219,8 +1456,8 @@ // If Satellite enable/disable request returned Error, no need to wait for radio argument.callback.accept(error); } - if (argument.enableSatellite) { + mSessionMetricsStats.resetSessionStatsShadowCounters(); mSessionMetricsStats.setInitializationResult(error) .setSatelliteTechnology(getSupportedNtnRadioTechnology()) .setInitializationProcessingTime( @@ -1290,8 +1527,20 @@ } } onCompleted = obtainMessage(EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE, request); + SatelliteModemEnableRequestAttributes enableRequestAttributes = + createModemEnableRequest(argument); + if (enableRequestAttributes == null) { + plogw("UpdateEnableAttributes: enableRequestAttributes is null"); + sendErrorAndReportSessionMetrics( + SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, + argument.callback); + synchronized (mSatelliteEnabledRequestLock) { + mSatelliteEnableAttributesUpdateRequest = null; + } + break; + } mSatelliteModemInterface.requestSatelliteEnabled( - createModemEnableRequest(argument), onCompleted); + enableRequestAttributes, onCompleted); startWaitForUpdateSatelliteEnableAttributesResponseTimer(argument); break; } @@ -1365,6 +1614,7 @@ updateSatelliteSupportedState(false); } ((ResultReceiver) request.argument).send(error, bundle); + decrementResultReceiverCount("SC:requestIsSatelliteEnabled"); break; } @@ -1417,12 +1667,14 @@ synchronized (mNeedsSatellitePointingLock) { mNeedsSatellitePointing = capabilities.isPointingRequired(); } - if (DBG) plogd("getSatelliteCapabilities: " + capabilities); - bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES, - capabilities); + synchronized (mSatelliteCapabilitiesLock) { mSatelliteCapabilities = capabilities; } + overrideSatelliteCapabilitiesIfApplicable(); + if (DBG) plogd("getSatelliteCapabilities: " + getSatelliteCapabilities()); + bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES, + getSatelliteCapabilities()); } } ((ResultReceiver) request.argument).send(error, bundle); @@ -1458,6 +1710,7 @@ } } ((ResultReceiver) request.argument).send(error, bundle); + decrementResultReceiverCount("SC:requestTimeForNextSatelliteVisibility"); break; } @@ -1481,19 +1734,21 @@ if (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE) { if (mSatelliteModemInterface.isSatelliteServiceConnected()) { - synchronized (mIsSatelliteSupportedLock) { - if (mIsSatelliteSupported == null || !mIsSatelliteSupported) { - ResultReceiver receiver = new ResultReceiver(this) { - @Override - protected void onReceiveResult( - int resultCode, Bundle resultData) { - plogd("onRadioStateChanged.requestIsSatelliteSupported: " - + "resultCode=" + resultCode - + ", resultData=" + resultData); - } - }; - sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, receiver, null); - } + Boolean isSatelliteSupported = getIsSatelliteSupported(); + if (isSatelliteSupported == null || !isSatelliteSupported) { + final String caller = "SC:CMD_IS_SATELLITE_SUPPORTED"; + ResultReceiver receiver = new ResultReceiver(this) { + @Override + protected void onReceiveResult( + int resultCode, Bundle resultData) { + decrementResultReceiverCount(caller); + plogd("onRadioStateChanged.requestIsSatelliteSupported: " + + "resultCode=" + resultCode + + ", resultData=" + resultData); + } + }; + sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, receiver, null); + incrementResultReceiverCount(caller); } } } @@ -1531,6 +1786,7 @@ ploge("EVENT_SATELLITE_MODEM_STATE_CHANGED: result is null"); } else { handleEventSatelliteModemStateChanged((int) ar.result); + updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(getSatellitePhone()); } break; @@ -1610,6 +1866,7 @@ } result.send(errorCode, null); } + decrementResultReceiverCount("SC:requestNtnSignalStrength"); break; } @@ -1619,6 +1876,7 @@ ploge("EVENT_NTN_SIGNAL_STRENGTH_CHANGED: result is null"); } else { handleEventNtnSignalStrengthChanged((NtnSignalStrength) ar.result); + updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(getSatellitePhone()); } break; } @@ -1697,12 +1955,13 @@ case EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT: { synchronized (mSatellitePhoneLock) { mNtnEligibilityHysteresisTimedOut = true; - boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone); - plogd("EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT:" - + " isCarrierRoamingNtnEligible=" + eligible); - if (eligible) { - requestIsSatelliteAllowedForCurrentLocation(); - } + } + + boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone); + plogd("EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT:" + + " isCarrierRoamingNtnEligible=" + eligible); + if (eligible) { + requestIsSatelliteAllowedForCurrentLocation(); } break; } @@ -1717,35 +1976,38 @@ RequestProvisionSatelliteArgument argument = (RequestProvisionSatelliteArgument) request.argument; onCompleted = obtainMessage(EVENT_UPDATE_PROVISION_SATELLITE_TOKEN_DONE, request); - // only pass to index 0. - // TODO: Select the subscription with highest priority and set it to mSatelliteSubId - int subId = -1; - synchronized (mSatelliteTokenProvisionedLock) { - subId = mSubscriberIdPerSub.getOrDefault( - argument.mSatelliteSubscriberInfoList.get(0).getSubscriberId(), -1); - } - setSatellitePhone(subId); - String iccId = mSubscriptionManagerService.getSubscriptionInfo(subId).getIccId(); - argument.setIccId(iccId); - boolean sendResponse = false; - synchronized (mSatelliteTokenProvisionedLock) { - if (!iccId.equals(mLastConfiguredIccId)) { - logd("updateSatelliteSubscription subId=" + subId + ", iccId=" + iccId - + " to modem"); - mSatelliteModemInterface.updateSatelliteSubscription(iccId, onCompleted); - } else { - sendResponse = true; + boolean provisionChanged = updateSatelliteSubscriptionProvisionState( + argument.mSatelliteSubscriberInfoList, argument.mProvisioned); + selectBindingSatelliteSubscription(false); + int subId = getSelectedSatelliteSubId(); + SubscriptionInfo subscriptionInfo = + mSubscriptionManagerService.getSubscriptionInfo(subId); + if (subscriptionInfo == null) { + logw("updateSatelliteToken subId=" + subId + " is not found"); + } else { + String iccId = subscriptionInfo.getIccId(); + argument.setIccId(iccId); + synchronized (mSatelliteTokenProvisionedLock) { + if (!iccId.equals(mLastConfiguredIccId)) { + logd("updateSatelliteSubscription subId=" + subId + + ", iccId=" + iccId + " to modem"); + mSatelliteModemInterface.updateSatelliteSubscription( + iccId, onCompleted); + } } } - handleEventSatelliteSubscriptionProvisionStateChanged( - argument.mSatelliteSubscriberInfoList, true); - if (sendResponse) { - // The response is sent immediately because the ICCID has already been - // delivered to the modem. - Bundle bundle = new Bundle(); - bundle.putBoolean(SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS, true); - argument.mResult.send(SATELLITE_RESULT_SUCCESS, bundle); + if (provisionChanged) { + handleEventSatelliteSubscriptionProvisionStateChanged(); } + + // The response is sent immediately because the ICCID has already been + // delivered to the modem. + Bundle bundle = new Bundle(); + bundle.putBoolean( + argument.mProvisioned ? SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS + : SatelliteManager.KEY_DEPROVISION_SATELLITE_TOKENS, true); + argument.mResult.send(SATELLITE_RESULT_SUCCESS, bundle); + decrementResultReceiverCount("SC:provisionSatellite"); break; } @@ -1762,10 +2024,6 @@ } } logd("updateSatelliteSubscription result=" + error); - Bundle bundle = new Bundle(); - bundle.putBoolean(SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS, - error == SATELLITE_RESULT_SUCCESS); - argument.mResult.send(error, bundle); break; } @@ -1775,8 +2033,8 @@ mIsWifiConnected = (boolean) ar.result; plogd("EVENT_WIFI_CONNECTIVITY_STATE_CHANGED: mIsWifiConnected=" + mIsWifiConnected); - handleStateChangedForCarrierRoamingNtnEligibility(); } + evaluateCarrierRoamingNtnEligibilityChange(); break; } case EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT: { @@ -1815,6 +2073,63 @@ } break; + case EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED: + ar = (AsyncResult) msg.obj; + if (ar.result == null) { + loge("EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED: result is null"); + } else { + handleEventTerrestrialNetworkAvailableChanged((boolean) ar.result); + } + break; + + case EVENT_SET_NETWORK_SELECTION_AUTO_DONE: { + logd("EVENT_SET_NETWORK_SELECTION_AUTO_DONE"); + RequestSatelliteEnabledArgument argument = + (RequestSatelliteEnabledArgument) msg.obj; + sendRequestAsync(CMD_SET_SATELLITE_ENABLED, argument, null); + break; + } + + case EVENT_SIGNAL_STRENGTH_CHANGED: { + ar = (AsyncResult) msg.obj; + int phoneId = (int) ar.userObj; + updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify( + PhoneFactory.getPhone(phoneId)); + break; + } + + case CMD_UPDATE_SYSTEM_SELECTION_CHANNELS: { + plogd("CMD_UPDATE_SYSTEM_SELECTION_CHANNELS"); + request = (SatelliteControllerHandlerRequest) msg.obj; + onCompleted = obtainMessage(EVENT_UPDATE_SYSTEM_SELECTION_CHANNELS_DONE, request); + mSatelliteModemInterface.updateSystemSelectionChannels( + ((UpdateSystemSelectionChannelsArgument) (request.argument)) + .mSystemSelectionSpecifiers, + onCompleted); + break; + } + + case EVENT_UPDATE_SYSTEM_SELECTION_CHANNELS_DONE: { + ar = (AsyncResult) msg.obj; + request = (SatelliteControllerHandlerRequest) ar.userObj; + int error = SatelliteServiceUtils.getSatelliteError( + ar, "updateSystemSelectionChannel"); + plogd("EVENT_UPDATE_SYSTEM_SELECTION_CHANNELS_DONE = " + error); + ((UpdateSystemSelectionChannelsArgument) (request.argument)).mResult.send(error, + null); + break; + } + + case EVENT_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_CHANGED: { + ar = (AsyncResult) msg.obj; + if (ar.result == null) { + loge("EVENT_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_CHANGED: result is null"); + } else { + handleEventSelectedNbIotSatelliteSubscriptionChanged((int) ar.result); + } + break; + } + default: Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " + msg.what); @@ -1828,11 +2143,13 @@ public ResultReceiver mResult; public long mRequestId; public String mIccId; + public boolean mProvisioned; RequestProvisionSatelliteArgument(List<SatelliteSubscriberInfo> satelliteSubscriberInfoList, - ResultReceiver result) { + ResultReceiver result, boolean provisioned) { this.mSatelliteSubscriberInfoList = satelliteSubscriberInfoList; this.mResult = result; + this.mProvisioned = provisioned; this.mRequestId = sNextSatelliteEnableRequestId.getAndUpdate( n -> ((n + 1) % Long.MAX_VALUE)); } @@ -1927,16 +2244,29 @@ * SATELLITE_RESULT_ERROR error * 4. ongoing request = enable, current request = disable: send request to modem */ + Boolean isSatelliteEnabled = getIsSatelliteEnabled(); synchronized (mSatelliteEnabledRequestLock) { - if (!isSatelliteEnabledRequestInProgress()) { - synchronized (mIsSatelliteEnabledLock) { - if (mIsSatelliteEnabled != null && mIsSatelliteEnabled == enableSatellite) { - evaluateToUpdateSatelliteEnabledAttributes(result, - SatelliteManager.SATELLITE_RESULT_SUCCESS, request, - mIsDemoModeEnabled, mIsEmergency); - return; - } + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + if (mSatelliteEnabledRequest != null && mNetworkSelectionModeAutoDialog != null + && mNetworkSelectionModeAutoDialog.isShowing() + && request.isEmergency && request.enableSatellite) { + sendErrorAndReportSessionMetrics( + SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE, + FunctionalUtils.ignoreRemoteException( + mSatelliteEnabledRequest.callback::accept)); + mSatelliteEnabledRequest = null; + mNetworkSelectionModeAutoDialog.dismiss(); + mNetworkSelectionModeAutoDialog = null; } + } + if (!isSatelliteEnabledRequestInProgress()) { + if (isSatelliteEnabled != null && isSatelliteEnabled == enableSatellite) { + evaluateToUpdateSatelliteEnabledAttributes(result, + SatelliteManager.SATELLITE_RESULT_SUCCESS, request, + mIsDemoModeEnabled, mIsEmergency); + return; + } + if (enableSatellite) { mSatelliteEnabledRequest = request; } else { @@ -1990,7 +2320,87 @@ } } } - sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null); + + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + Phone satellitePhone = getSatellitePhone(); + if (enableSatellite && satellitePhone != null + && satellitePhone.getServiceStateTracker() != null + && satellitePhone.getServiceStateTracker().getServiceState() + .getIsManualSelection()) { + checkNetworkSelectionModeAuto(request); + } else { + sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null); + } + } else { + sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null); + } + } + + private void checkNetworkSelectionModeAuto(RequestSatelliteEnabledArgument argument) { + plogd("checkNetworkSelectionModeAuto"); + if (argument.isEmergency) { + // ESOS + getSatellitePhone().setNetworkSelectionModeAutomatic(null); + sendMessageDelayed(obtainMessage(EVENT_SET_NETWORK_SELECTION_AUTO_DONE, argument), + DELAY_WAITING_SET_NETWORK_SELECTION_AUTO_MILLIS); + } else { + // P2P + if (mNetworkSelectionModeAutoDialog != null + && mNetworkSelectionModeAutoDialog.isShowing()) { + logd("requestSatelliteEnabled: already auto network selection mode popup showing"); + sendErrorAndReportSessionMetrics( + SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS, + FunctionalUtils.ignoreRemoteException(argument.callback::accept)); + return; + } + logd("requestSatelliteEnabled: auto network selection mode popup"); + Configuration configuration = Resources.getSystem().getConfiguration(); + boolean nightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES; + + AlertDialog.Builder builder = new AlertDialog.Builder(mContext, nightMode + ? AlertDialog.THEME_DEVICE_DEFAULT_DARK + : AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + + String title = mContext.getResources().getString( + R.string.satellite_manual_selection_state_popup_title); + String message = mContext.getResources().getString( + R.string.satellite_manual_selection_state_popup_message); + String ok = mContext.getResources().getString( + R.string.satellite_manual_selection_state_popup_ok); + String cancel = mContext.getResources().getString( + R.string.satellite_manual_selection_state_popup_cancel); + + builder.setTitle(title).setMessage(message) + .setPositiveButton(ok, (dialog, which) -> { + logd("checkNetworkSelectionModeAuto: setPositiveButton"); + getSatellitePhone().setNetworkSelectionModeAutomatic(null); + sendMessageDelayed(obtainMessage(EVENT_SET_NETWORK_SELECTION_AUTO_DONE, + argument), DELAY_WAITING_SET_NETWORK_SELECTION_AUTO_MILLIS); + }) + .setNegativeButton(cancel, (dialog, which) -> { + logd("checkNetworkSelectionModeAuto: setNegativeButton"); + synchronized (mSatelliteEnabledRequestLock) { + mSatelliteEnabledRequest = null; + } + sendErrorAndReportSessionMetrics( + SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE, + FunctionalUtils.ignoreRemoteException(argument.callback::accept)); + }) + .setOnCancelListener(dialog -> { + logd("checkNetworkSelectionModeAuto: setOnCancelListener"); + synchronized (mSatelliteEnabledRequestLock) { + mSatelliteEnabledRequest = null; + } + sendErrorAndReportSessionMetrics( + SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE, + FunctionalUtils.ignoreRemoteException(argument.callback::accept)); + }); + mNetworkSelectionModeAutoDialog = builder.create(); + mNetworkSelectionModeAutoDialog.getWindow() + .setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + mNetworkSelectionModeAutoDialog.show(); + } } /** @@ -2075,17 +2485,17 @@ return; } - synchronized (mIsSatelliteEnabledLock) { - if (mIsSatelliteEnabled != null) { - /* We have already successfully queried the satellite modem. */ - Bundle bundle = new Bundle(); - bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, mIsSatelliteEnabled); - result.send(SATELLITE_RESULT_SUCCESS, bundle); - return; - } + Boolean isSatelliteEnabled = getIsSatelliteEnabled(); + if (isSatelliteEnabled != null) { + /* We have already successfully queried the satellite modem. */ + Bundle bundle = new Bundle(); + bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, isSatelliteEnabled); + result.send(SATELLITE_RESULT_SUCCESS, bundle); + return; } sendRequestAsync(CMD_IS_SATELLITE_ENABLED, result, null); + incrementResultReceiverCount("SC:requestIsSatelliteEnabled"); } /** @@ -2094,13 +2504,15 @@ * * @return {@code true} if the satellite modem is enabled and {@code false} otherwise. */ - public boolean isSatelliteEnabled() { + private boolean isSatelliteEnabled() { if (!mFeatureFlags.oemEnabledSatelliteFlag()) { plogd("isSatelliteEnabled: oemEnabledSatelliteFlag is disabled"); return false; } - if (mIsSatelliteEnabled == null) return false; - return mIsSatelliteEnabled; + synchronized (mIsSatelliteEnabledLock) { + if (mIsSatelliteEnabled == null) return false; + return mIsSatelliteEnabled; + } } /** @@ -2108,16 +2520,31 @@ * * @return {@code true} if the satellite modem is being enabled and {@code false} otherwise. */ - public boolean isSatelliteBeingEnabled() { + private boolean isSatelliteBeingEnabled() { if (!mFeatureFlags.oemEnabledSatelliteFlag()) { plogd("isSatelliteBeingEnabled: oemEnabledSatelliteFlag is disabled"); return false; } - if (mSatelliteSessionController != null) { - return mSatelliteSessionController.isInEnablingState(); + if (mSatelliteSessionController != null + && mSatelliteSessionController.isInEnablingState()) { + return true; } - return false; + + synchronized (mSatelliteEnabledRequestLock) { + return (mSatelliteEnabledRequest != null); + } + } + + /** + * Get whether the satellite modem is enabled or being enabled. + * This will return the cached value instead of querying the satellite modem. + * + * @return {@code true} if the satellite modem is enabled or being enabled, {@code false} + * otherwise. + */ + public boolean isSatelliteEnabledOrBeingEnabled() { + return isSatelliteEnabled() || isSatelliteBeingEnabled(); } /** @@ -2126,10 +2553,14 @@ * @return {@code true} if the satellite modem is being disabled and {@code false} otherwise. */ public boolean isSatelliteBeingDisabled() { - if (mSatelliteSessionController != null) { - return mSatelliteSessionController.isInDisablingState(); + if (mSatelliteSessionController != null + && mSatelliteSessionController.isInDisablingState()) { + return true; } - return false; + + synchronized (mSatelliteEnabledRequestLock) { + return (mSatelliteDisabledRequest != null); + } } /** @@ -2196,15 +2627,16 @@ result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null); return; } - synchronized (mIsSatelliteSupportedLock) { - if (mIsSatelliteSupported != null) { - /* We have already successfully queried the satellite modem. */ - Bundle bundle = new Bundle(); - bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, mIsSatelliteSupported); - bundle.putInt(SATELLITE_SUBSCRIPTION_ID, getHighestPrioritySubscrption()); - result.send(SATELLITE_RESULT_SUCCESS, bundle); - return; - } + + int subId = getSelectedSatelliteSubId(); + Boolean isSatelliteSupported = getIsSatelliteSupported(); + if (isSatelliteSupported != null) { + /* We have already successfully queried the satellite modem. */ + Bundle bundle = new Bundle(); + bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, isSatelliteSupported); + bundle.putInt(SATELLITE_SUBSCRIPTION_ID, subId); + result.send(SATELLITE_RESULT_SUCCESS, bundle); + return; } sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, result, null); @@ -2223,14 +2655,13 @@ return; } - synchronized (mSatelliteCapabilitiesLock) { - if (mSatelliteCapabilities != null) { - Bundle bundle = new Bundle(); - bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES, - mSatelliteCapabilities); - result.send(SATELLITE_RESULT_SUCCESS, bundle); - return; - } + if (getSatelliteCapabilities() != null) { + Bundle bundle = new Bundle(); + overrideSatelliteCapabilitiesIfApplicable(); + bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES, + getSatelliteCapabilities()); + result.send(SATELLITE_RESULT_SUCCESS, bundle); + return; } sendRequestAsync(CMD_GET_SATELLITE_CAPABILITIES, result, null); @@ -2254,7 +2685,7 @@ return; } - final int validSubId = getHighestPrioritySubscrption(); + final int validSubId = getSelectedSatelliteSubId(); mPointingAppController.registerForSatelliteTransmissionUpdates(validSubId, callback); sendRequestAsync(CMD_START_SATELLITE_TRANSMISSION_UPDATES, new SatelliteTransmissionUpdateArgument(result, callback, validSubId), null); @@ -2272,7 +2703,7 @@ @NonNull ISatelliteTransmissionUpdateCallback callback) { Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept); mPointingAppController.unregisterForSatelliteTransmissionUpdates( - getHighestPrioritySubscrption(), result, callback); + getSelectedSatelliteSubId(), result, callback); // Even if handler is null - which means there are no listeners, the modem command to stop // satellite transmission updates might have failed. The callers might want to retry @@ -2296,36 +2727,60 @@ @NonNull String token, @NonNull byte[] provisionData, @NonNull IIntegerConsumer callback) { Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept); - int error = evaluateOemSatelliteRequestAllowed(false); - if (error != SATELLITE_RESULT_SUCCESS) { - result.accept(error); - return null; + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + List<SatelliteSubscriberInfo> subscriberInfoList = + getNtnOnlySatelliteSubscriberInfoList(result); + if (subscriberInfoList == null) { + return null; + } + ResultReceiver internalReceiver = new ResultReceiver(this) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + plogd("provisionSatelliteService: resultCode=" + resultCode + + ", resultData=" + resultData); + result.accept(resultCode); + } + }; + provisionSatellite(subscriberInfoList, internalReceiver); + + ICancellationSignal cancelTransport = CancellationSignal.createTransport(); + CancellationSignal.fromTransport(cancelTransport).setOnCancelListener(() -> { + deprovisionSatellite(subscriberInfoList, internalReceiver); + mProvisionMetricsStats.setIsCanceled(true); + }); + return cancelTransport; + } else { + int error = evaluateOemSatelliteRequestAllowed(false); + if (error != SATELLITE_RESULT_SUCCESS) { + result.accept(error); + return null; + } + + final int validSubId = getSelectedSatelliteSubId(); + if (mSatelliteProvisionCallbacks.containsKey(validSubId)) { + result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS); + return null; + } + + Boolean satelliteProvisioned = isDeviceProvisioned(); + if (satelliteProvisioned != null && satelliteProvisioned) { + result.accept(SATELLITE_RESULT_SUCCESS); + return null; + } + + sendRequestAsync(CMD_PROVISION_SATELLITE_SERVICE, + new ProvisionSatelliteServiceArgument(token, provisionData, result, validSubId), + null); + + ICancellationSignal cancelTransport = CancellationSignal.createTransport(); + CancellationSignal.fromTransport(cancelTransport).setOnCancelListener(() -> { + sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE, + new ProvisionSatelliteServiceArgument(token, provisionData, null, + validSubId), null); + mProvisionMetricsStats.setIsCanceled(true); + }); + return cancelTransport; } - - final int validSubId = getHighestPrioritySubscrption(); - if (mSatelliteProvisionCallbacks.containsKey(validSubId)) { - result.accept(SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS); - return null; - } - - Boolean satelliteProvisioned = isSatelliteViaOemProvisioned(); - if (satelliteProvisioned != null && satelliteProvisioned) { - result.accept(SATELLITE_RESULT_SUCCESS); - return null; - } - - sendRequestAsync(CMD_PROVISION_SATELLITE_SERVICE, - new ProvisionSatelliteServiceArgument(token, provisionData, result, validSubId), - null); - - ICancellationSignal cancelTransport = CancellationSignal.createTransport(); - CancellationSignal.fromTransport(cancelTransport).setOnCancelListener(() -> { - sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE, - new ProvisionSatelliteServiceArgument(token, provisionData, null, - validSubId), null); - mProvisionMetricsStats.setIsCanceled(true); - }); - return cancelTransport; } /** @@ -2341,21 +2796,38 @@ public void deprovisionSatelliteService( @NonNull String token, @NonNull IIntegerConsumer callback) { Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept); - int error = evaluateOemSatelliteRequestAllowed(false); - if (error != SATELLITE_RESULT_SUCCESS) { - result.accept(error); - return; - } + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + List<SatelliteSubscriberInfo> subscriberInfoList = + getNtnOnlySatelliteSubscriberInfoList(result); + if (subscriberInfoList == null) { + return; + } + ResultReceiver internalReceiver = new ResultReceiver(this) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + plogd("deprovisionSatelliteService: resultCode=" + resultCode + + ", resultData=" + resultData); + result.accept(resultCode); + } + }; + deprovisionSatellite(subscriberInfoList, internalReceiver); + } else { + int error = evaluateOemSatelliteRequestAllowed(false); + if (error != SATELLITE_RESULT_SUCCESS) { + result.accept(error); + return; + } - if (Boolean.FALSE.equals(isSatelliteViaOemProvisioned())) { - result.accept(SATELLITE_RESULT_SUCCESS); - return; - } + if (Boolean.FALSE.equals(isDeviceProvisioned())) { + result.accept(SATELLITE_RESULT_SUCCESS); + return; + } - sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE, + sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE, new ProvisionSatelliteServiceArgument(token, null, - result, getHighestPrioritySubscrption()), + result, getSelectedSatelliteSubId()), null); + } } /** @@ -2367,20 +2839,15 @@ */ @SatelliteManager.SatelliteResult public int registerForSatelliteProvisionStateChanged( @NonNull ISatelliteProvisionStateCallback callback) { - int error = evaluateOemSatelliteRequestAllowed(false); - if (error != SATELLITE_RESULT_SUCCESS) { - return error; - } - mSatelliteProvisionStateChangedListeners.put(callback.asBinder(), callback); - boolean isProvisioned = Boolean.TRUE.equals(isSatelliteViaOemProvisioned()); + boolean isProvisioned = Boolean.TRUE.equals(isDeviceProvisioned()); try { callback.onSatelliteProvisionStateChanged(isProvisioned); } catch (RemoteException ex) { loge("registerForSatelliteProvisionStateChanged: " + ex); } - synchronized (mSatelliteViaOemProvisionLock) { + synchronized (mDeviceProvisionLock) { plogd("registerForSatelliteProvisionStateChanged: report current provisioned " + "state, state=" + isProvisioned); } @@ -2419,17 +2886,18 @@ return; } - synchronized (mSatelliteViaOemProvisionLock) { - if (mIsSatelliteViaOemProvisioned != null) { + synchronized (mDeviceProvisionLock) { + if (mIsDeviceProvisioned != null) { Bundle bundle = new Bundle(); bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, - mIsSatelliteViaOemProvisioned); + mIsDeviceProvisioned); result.send(SATELLITE_RESULT_SUCCESS, bundle); return; } } sendRequestAsync(CMD_IS_SATELLITE_PROVISIONED, result, null); + incrementResultReceiverCount("SC:requestIsSatelliteProvisioned"); } /** @@ -2446,8 +2914,9 @@ return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED; } if (mFeatureFlags.carrierRoamingNbIotNtn()) { - plogd("registerForSatelliteModemStateChanged: add RegistrationFailure Listeners"); + plogd("registerForSatelliteModemStateChanged: add Listeners for ModemState"); mSatelliteRegistrationFailureListeners.put(callback.asBinder(), callback); + mTerrestrialNetworkAvailableChangedListeners.put(callback.asBinder(), callback); } if (mSatelliteSessionController != null) { mSatelliteSessionController.registerForSatelliteModemStateChanged(callback); @@ -2479,8 +2948,9 @@ + " is not initialized yet"); } if (mFeatureFlags.carrierRoamingNbIotNtn()) { - plogd("unregisterForModemStateChanged: remove RegistrationFailure Listeners"); + plogd("unregisterForModemStateChanged: remove Listeners for ModemState"); mSatelliteRegistrationFailureListeners.remove(callback.asBinder()); + mTerrestrialNetworkAvailableChangedListeners.remove(callback.asBinder()); } } @@ -2502,7 +2972,7 @@ } plogd("registerForIncomingDatagram: callback=" + callback); return mDatagramController.registerForSatelliteDatagram( - getHighestPrioritySubscrption(), callback); + getSelectedSatelliteSubId(), callback); } /** @@ -2523,7 +2993,7 @@ } plogd("unregisterForIncomingDatagram: callback=" + callback); mDatagramController.unregisterForSatelliteDatagram( - getHighestPrioritySubscrption(), callback); + getSelectedSatelliteSubId(), callback); } /** @@ -2545,7 +3015,7 @@ } mDatagramController.pollPendingSatelliteDatagrams( - getHighestPrioritySubscrption(), result); + getSelectedSatelliteSubId(), result); } /** @@ -2585,7 +3055,7 @@ mIsEmergency); } - mDatagramController.sendSatelliteDatagram(getHighestPrioritySubscrption(), datagramType, + mDatagramController.sendSatelliteDatagram(getSelectedSatelliteSubId(), datagramType, datagram, needFullScreenPointingUI, result); } @@ -2603,10 +3073,11 @@ } sendRequestAsync(CMD_GET_TIME_SATELLITE_NEXT_VISIBLE, result, null); + incrementResultReceiverCount("SC:requestTimeForNextSatelliteVisibility"); } /** - * Inform whether the device is aligned with satellite for demo mode. + * Inform whether the device is aligned with the satellite in both real and demo mode. * * @param isAligned {@true} means device is aligned with the satellite, otherwise {@false}. */ @@ -2755,6 +3226,7 @@ Phone phone = SatelliteServiceUtils.getPhone(); sendRequestAsync(CMD_REQUEST_NTN_SIGNAL_STRENGTH, result, phone); + incrementResultReceiverCount("SC:requestNtnSignalStrength"); } /** @@ -2873,6 +3345,59 @@ } /** + * Registers for selected satellite subscription changed event. + * + * @param callback The callback to handle the selected satellite subscription changed event. + * + * @return The {@link SatelliteManager.SatelliteResult} result of the operation. + */ + @SatelliteManager.SatelliteResult + public int registerForSelectedNbIotSatelliteSubscriptionChanged( + @NonNull ISelectedNbIotSatelliteSubscriptionCallback callback) { + if (DBG) plogd("registerForSelectedNbIotSatelliteSubscriptionChanged()"); + + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("carrierRoamingNbIotNtn flag is disabled"); + return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED; + } + + int error = evaluateOemSatelliteRequestAllowed(false); + if (error != SATELLITE_RESULT_SUCCESS) return error; + + mSelectedNbIotSatelliteSubscriptionChangedListeners.put(callback.asBinder(), callback); + try { + callback.onSelectedNbIotSatelliteSubscriptionChanged(getSelectedSatelliteSubId()); + } catch (RemoteException ex) { + ploge("registerForSelectedNbIotSatelliteSubscriptionChanged: RemoteException ex=" + + ex); + } + return SATELLITE_RESULT_SUCCESS; + } + + /** + * Unregisters for the selected satellite subscription changed event. + * If callback was not registered before, the request will be ignored. + * + * @param callback The callback that was passed to {@link + * #registerForSelectedNbIotSatelliteSubscriptionChanged( + * ISelectedNbIotSatelliteSubscriptionCallback)}. + */ + public void unregisterForSelectedNbIotSatelliteSubscriptionChanged( + @NonNull ISelectedNbIotSatelliteSubscriptionCallback callback) { + if (DBG) plogd("unregisterForSelectedNbIotSatelliteSubscriptionChanged()"); + + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("carrierRoamingNbIotNtn flag is disabled"); + return; + } + + int error = evaluateOemSatelliteRequestAllowed(true); + if (error == SATELLITE_RESULT_SUCCESS) { + mSelectedNbIotSatelliteSubscriptionChangedListeners.remove(callback.asBinder()); + } + } + + /** * This API can be used by only CTS to update satellite vendor service package name. * * @param servicePackageName The package name of the satellite vendor service. @@ -2893,8 +3418,8 @@ synchronized (mIsSatelliteSupportedLock) { mIsSatelliteSupported = null; } - synchronized (mSatelliteViaOemProvisionLock) { - mIsSatelliteViaOemProvisioned = Optional.ofNullable(provisioned) + synchronized (mDeviceProvisionLock) { + mIsDeviceProvisioned = Optional.ofNullable(provisioned) .filter(s -> s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false")) .map(s -> s.equalsIgnoreCase("true")) .orElse(null); @@ -2930,6 +3455,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. * @@ -3008,6 +3548,14 @@ } else { mDemoPointingNotAlignedDurationMillis = timeoutMillis; } + } else if (timeoutType + == TIMEOUT_TYPE_EVALUATE_ESOS_PROFILES_PRIORITIZATION_DURATION_MILLIS) { + if (reset) { + mEvaluateEsosProfilesPrioritizationDurationMillis = + getEvaluateEsosProfilesPrioritizationDurationMillis(); + } else { + mEvaluateEsosProfilesPrioritizationDurationMillis = timeoutMillis; + } } else { plogw("Invalid timeoutType=" + timeoutType); return false; @@ -3094,7 +3642,7 @@ ploge("setOemEnabledSatelliteProvisionStatus: mock modem not allowed"); return false; } - synchronized (mSatelliteViaOemProvisionLock) { + synchronized (mDeviceProvisionLock) { if (reset) { mOverriddenIsSatelliteViaOemProvisioned = null; } else { @@ -3144,15 +3692,18 @@ plogd("onSatelliteServiceConnected"); // Vendor service might have just come back from a crash moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_MODEM_ERROR); + final String caller = "SC:onSatelliteServiceConnected"; ResultReceiver receiver = new ResultReceiver(this) { @Override protected void onReceiveResult( int resultCode, Bundle resultData) { + decrementResultReceiverCount(caller); plogd("onSatelliteServiceConnected.requestIsSatelliteSupported:" + " resultCode=" + resultCode); } }; requestIsSatelliteSupported(receiver); + incrementResultReceiverCount(caller); } else { plogd("onSatelliteServiceConnected: Satellite vendor service is not supported." + " Ignored the event"); @@ -3208,6 +3759,36 @@ } /** + * Notify SMS received. + * + * @param subId The subId of the subscription used to receive SMS + */ + public void onSmsReceived(int subId) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + logd("onSmsReceived: carrierRoamingNbIotNtn is disabled"); + return; + } + + if (!isSatelliteEnabled()) { + logd("onSmsReceived: satellite is not enabled"); + return; + } + + int satelliteSubId = getSelectedSatelliteSubId(); + if (subId != satelliteSubId) { + logd("onSmsReceived: SMS received " + subId + + ", but not satellite subscription " + satelliteSubId); + return; + } + + if (mDatagramController != null) { + mDatagramController.onSmsReceived(subId); + } else { + logd("onSmsReceived: DatagramController is not initialized"); + } + } + + /** * @return {@code true} if satellite is supported via OEM on the device, * {@code false} otherwise. */ @@ -3248,12 +3829,20 @@ * the satellite network {@code plmn}. */ @NonNull - public List<Integer> getSupportedSatelliteServices(int subId, String plmn) { + public List<Integer> getSupportedSatelliteServicesForPlmn(int subId, String plmn) { if (!mFeatureFlags.carrierEnabledSatelliteFlag()) { logd("getSupportedSatelliteServices: carrierEnabledSatelliteFlag is disabled"); return new ArrayList<>(); } synchronized (mSupportedSatelliteServicesLock) { + Map<String, List<Integer>> allowedServicesList + = mEntitlementServiceTypeMapPerCarrier.get(subId); + if (allowedServicesList != null && allowedServicesList.containsKey(plmn)) { + List<Integer> allowedServiceValues = allowedServicesList.get(plmn); + if (allowedServiceValues != null && !allowedServiceValues.isEmpty()) { + return allowedServiceValues; + } + } if (mSatelliteServicesSupportedByCarriers.containsKey(subId)) { Map<String, Set<Integer>> supportedServices = mSatelliteServicesSupportedByCarriers.get(subId); @@ -3298,17 +3887,16 @@ return false; } - synchronized (mSatelliteCapabilitiesLock) { - if (mSatelliteCapabilities == null) { - ploge("isSatelliteAttachRequired: mSatelliteCapabilities is null"); - return false; - } - if (mSatelliteCapabilities.getSupportedRadioTechnologies().contains( - SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN)) { - return true; - } + SatelliteCapabilities satelliteCapabilities = getSatelliteCapabilities(); + if (satelliteCapabilities == null) { + ploge("isSatelliteAttachRequired: mSatelliteCapabilities is null"); return false; } + if (satelliteCapabilities.getSupportedRadioTechnologies().contains( + SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN)) { + return true; + } + return false; } /** @@ -3416,6 +4004,11 @@ } int subId = phone.getSubId(); + int carrierRoamingNtnConnectType = getCarrierRoamingNtnConnectType(subId); + if (carrierRoamingNtnConnectType == CARRIER_ROAMING_NTN_CONNECT_MANUAL) { + return isInCarrierRoamingNbIotNtn(phone); + } + if (!isSatelliteSupportedViaCarrier(subId)) { return false; } @@ -3458,12 +4051,9 @@ * esos session. */ public boolean shouldTurnOffCarrierSatelliteForEmergencyCall() { - synchronized (mSatellitePhoneLock) { - if (mSatellitePhone == null) return false; - return !mDatagramController.isEmergencyCommunicationEstablished() - && getConfigForSubId(mSatellitePhone.getSubId()).getBoolean( - KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL); - } + return !mDatagramController.isEmergencyCommunicationEstablished() + && getConfigForSubId(getSelectedSatelliteSubId()).getBoolean( + KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL); } /** @@ -3481,6 +4071,14 @@ * else {@return false} */ public boolean isInCarrierRoamingNbIotNtn() { + return isInCarrierRoamingNbIotNtn(getSatellitePhone()); + } + + /** + * @return {@code true} if phone is in carrier roaming nb iot ntn mode, + * else {@return false} + */ + private boolean isInCarrierRoamingNbIotNtn(@Nullable Phone phone) { if (!mFeatureFlags.carrierRoamingNbIotNtn()) { plogd("isInCarrierRoamingNbIotNtn: carrier roaming nb iot ntn " + "feature flag is disabled"); @@ -3492,12 +4090,33 @@ return false; } - Phone satellitePhone = getSatellitePhone(); - if (!isCarrierRoamingNtnEligible(satellitePhone)) { - plogd("isInCarrierRoamingNbIotNtn: not carrier roaming ntn eligible."); + if (phone == null) { + plogd("isInCarrierRoamingNbIotNtn: phone is null"); return false; } + int subId = phone.getSubId(); + if (!isSatelliteSupportedViaCarrier(subId)) { + plogd("isInCarrierRoamingNbIotNtn[phoneId=" + phone.getPhoneId() + + "]: satellite is not supported via carrier"); + return false; + } + + int carrierRoamingNtnConnectType = getCarrierRoamingNtnConnectType(subId); + if (carrierRoamingNtnConnectType != CARRIER_ROAMING_NTN_CONNECT_MANUAL) { + plogd("isInCarrierRoamingNbIotNtn[phoneId=" + phone.getPhoneId() + "]: not manual " + + "connect. carrierRoamingNtnConnectType = " + carrierRoamingNtnConnectType); + return false; + } + + if (subId != getSelectedSatelliteSubId()) { + plogd("isInCarrierRoamingNbIotNtn: subId=" + subId + + " does not match satellite subId=" + getSelectedSatelliteSubId()); + return false; + } + + plogd("isInCarrierRoamingNbIotNtn: carrier roaming ntn eligible for phone" + + " associated with subId " + phone.getSubId()); return true; } @@ -3608,10 +4227,18 @@ * @param entitlementEnabled {@code true} Satellite service enabled * @param allowedPlmnList plmn allowed list to use the satellite service * @param barredPlmnList plmn barred list to pass the modem + * @param plmnDataPlanMap data plan map for the plmn + * @param plmnServiceTypeMap available services map for the plmn + * @param plmnDataServicePolicyMap data service policy map for the plmn + * @param plmnVoiceServicePolicyMap voice service policy map for the plmn * @param callback callback for accept */ public void onSatelliteEntitlementStatusUpdated(int subId, boolean entitlementEnabled, @Nullable List<String> allowedPlmnList, @Nullable List<String> barredPlmnList, + @Nullable Map<String,Integer> plmnDataPlanMap, + @Nullable Map<String,List<Integer>> plmnServiceTypeMap, + @Nullable Map<String,Integer> plmnDataServicePolicyMap, + @Nullable Map<String,Integer> plmnVoiceServicePolicyMap, @Nullable IIntegerConsumer callback) { if (!mFeatureFlags.carrierEnabledSatelliteFlag()) { logd("onSatelliteEntitlementStatusUpdated: carrierEnabledSatelliteFlag is not enabled"); @@ -3632,10 +4259,26 @@ if (barredPlmnList == null) { barredPlmnList = new ArrayList<>(); } + if (plmnDataPlanMap == null) { + plmnDataPlanMap = new HashMap<>(); + } + if (plmnServiceTypeMap == null) { + plmnServiceTypeMap = new HashMap<>(); + } + if (plmnDataServicePolicyMap == null) { + plmnDataServicePolicyMap = new HashMap<>(); + } + if (plmnVoiceServicePolicyMap == null) { + plmnVoiceServicePolicyMap = new HashMap<>(); + } logd("onSatelliteEntitlementStatusUpdated subId=" + subId + ", entitlementEnabled=" + entitlementEnabled + ", allowedPlmnList=[" + String.join(",", allowedPlmnList) + "]" + ", barredPlmnList=[" - + String.join(",", barredPlmnList) + "]"); + + String.join(",", barredPlmnList) + "]" + + ", plmnDataPlanMap =" + plmnDataPlanMap.toString() + + ", plmnServiceTypeMap =" + plmnServiceTypeMap.toString() + + ", plmnDataServicePolicyMap=" + plmnDataServicePolicyMap.toString() + + ", plmnVoiceServicePolicyMap=" + plmnVoiceServicePolicyMap.toString()); synchronized (mSupportedSatelliteServicesLock) { if (mSatelliteEntitlementStatusPerCarrier.get(subId, false) != entitlementEnabled) { @@ -3660,6 +4303,10 @@ mMergedPlmnListPerCarrier.remove(subId); mEntitlementPlmnListPerCarrier.put(subId, allowedPlmnList); mEntitlementBarredPlmnListPerCarrier.put(subId, barredPlmnList); + mEntitlementDataPlanMapPerCarrier.put(subId, plmnDataPlanMap); + mEntitlementServiceTypeMapPerCarrier.put(subId, plmnServiceTypeMap); + mEntitlementDataServicePolicyMapPerCarrier.put(subId, plmnDataServicePolicyMap); + mEntitlementVoiceServicePolicyMapPerCarrier.put(subId, plmnVoiceServicePolicyMap); updatePlmnListPerCarrier(subId); configureSatellitePlmnForCarrier(subId); mSubscriptionManagerService.setSatelliteEntitlementPlmnList(subId, allowedPlmnList); @@ -3697,12 +4344,12 @@ * we will retry the query one more time. Otherwise, we will return the cached result. */ private Boolean isSatelliteSupportedViaOemInternal() { - synchronized (mIsSatelliteSupportedLock) { - if (mIsSatelliteSupported != null) { - /* We have already successfully queried the satellite modem. */ - return mIsSatelliteSupported; - } + Boolean isSatelliteSupported = getIsSatelliteSupported(); + if (isSatelliteSupported != null) { + /* We have already successfully queried the satellite modem. */ + return isSatelliteSupported; } + /** * We have not successfully checked whether the modem supports satellite service. * Thus, we need to retry it now. @@ -3711,10 +4358,13 @@ new ResultReceiver(this) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { + decrementResultReceiverCount( + "SC:isSatelliteSupportedViaOemInternal"); plogd("isSatelliteSupportedViaOemInternal.requestIsSatelliteSupported:" + " resultCode=" + resultCode); } }); + incrementResultReceiverCount("SC:isSatelliteSupportedViaOemInternal"); return null; } @@ -3740,8 +4390,8 @@ if (result == SATELLITE_RESULT_SUCCESS || result == SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) { persistOemEnabledSatelliteProvisionStatus(true); - synchronized (mSatelliteViaOemProvisionLock) { - mIsSatelliteViaOemProvisioned = true; + synchronized (mDeviceProvisionLock) { + mIsDeviceProvisioned = true; } callback.accept(SATELLITE_RESULT_SUCCESS); handleEventSatelliteProvisionStateChanged(true); @@ -3768,8 +4418,8 @@ if (result == SATELLITE_RESULT_SUCCESS || result == SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) { persistOemEnabledSatelliteProvisionStatus(false); - synchronized (mSatelliteViaOemProvisionLock) { - mIsSatelliteViaOemProvisioned = false; + synchronized (mDeviceProvisionLock) { + mIsDeviceProvisioned = false; } if (arg.callback != null) { arg.callback.accept(SATELLITE_RESULT_SUCCESS); @@ -3819,21 +4469,21 @@ } /** - * Check if satellite is provisioned for a subscription on the device. - * @return true if satellite is provisioned on the given subscription else return false. + * Check if satellite is provisioned for the device. + * @return {@code true} if device is provisioned for satellite else return {@code false}. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @Nullable - protected Boolean isSatelliteViaOemProvisioned() { - synchronized (mSatelliteViaOemProvisionLock) { + protected Boolean isDeviceProvisioned() { + synchronized (mDeviceProvisionLock) { if (mOverriddenIsSatelliteViaOemProvisioned != null) { return mOverriddenIsSatelliteViaOemProvisioned; } - if (mIsSatelliteViaOemProvisioned == null) { - mIsSatelliteViaOemProvisioned = getPersistedOemEnabledSatelliteProvisionStatus(); + if (mIsDeviceProvisioned == null) { + mIsDeviceProvisioned = getPersistedDeviceProvisionStatus(); } - return mIsSatelliteViaOemProvisioned; + return mIsDeviceProvisioned; } } @@ -3841,6 +4491,23 @@ RequestSatelliteEnabledArgument argument = (RequestSatelliteEnabledArgument) request.argument; handlePersistentLoggingOnSessionStart(argument); + selectBindingSatelliteSubscription(argument.enableSatellite); + SatelliteModemEnableRequestAttributes enableRequestAttributes = + createModemEnableRequest(argument); + if (enableRequestAttributes == null) { + plogw("handleSatelliteEnabled: enableRequestAttributes is null"); + sendErrorAndReportSessionMetrics( + SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE, argument.callback); + synchronized (mSatelliteEnabledRequestLock) { + if (argument.enableSatellite) { + mSatelliteEnabledRequest = null; + } else { + mSatelliteDisabledRequest = null; + } + } + return; + } + if (mSatelliteSessionController != null) { mSatelliteSessionController.onSatelliteEnablementStarted(argument.enableSatellite); } else { @@ -3860,8 +4527,7 @@ Message onCompleted = obtainMessage(EVENT_SET_SATELLITE_ENABLED_DONE, request); mSatelliteModemInterface.requestSatelliteEnabled( - createModemEnableRequest(argument), - onCompleted); + enableRequestAttributes, onCompleted); startWaitForSatelliteEnablingResponseTimer(argument); // Logs satellite session timestamps for session metrics if (argument.enableSatellite) { @@ -3871,11 +4537,15 @@ } /** Get the request attributes that modem needs to enable/disable satellite */ - private SatelliteModemEnableRequestAttributes createModemEnableRequest( + @Nullable private SatelliteModemEnableRequestAttributes createModemEnableRequest( @NonNull RequestSatelliteEnabledArgument arg) { - int subId = getHighestPrioritySubscrption(); + int subId = getSelectedSatelliteSubId(); SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId); - String iccid = subInfo != null ? subInfo.getIccId() : ""; + if (subInfo == null) { + loge("createModemEnableRequest: no SubscriptionInfo found for subId=" + subId); + return null; + } + String iccid = subInfo.getIccId(); String apn = getConfigForSubId(subId).getString(KEY_SATELLITE_NIDD_APN_NAME_STRING, ""); return new SatelliteModemEnableRequestAttributes( arg.enableSatellite, arg.enableDemoMode, arg.isEmergency, @@ -3912,6 +4582,7 @@ registerForNtnSignalStrengthChanged(); registerForCapabilitiesChanged(); registerForSatelliteRegistrationFailure(); + registerForTerrestrialNetworkAvailableChanged(); requestIsSatelliteProvisioned( new ResultReceiver(this) { @@ -3919,6 +4590,7 @@ protected void onReceiveResult(int resultCode, Bundle resultData) { plogd("updateSatelliteSupportedState.requestIsSatelliteProvisioned: " + "resultCode=" + resultCode + ", resultData=" + resultData); + decrementResultReceiverCount("SC:requestIsSatelliteProvisioned"); requestSatelliteEnabled(false, false, false, new IIntegerConsumer.Stub() { @Override @@ -3929,16 +4601,22 @@ }); } }); + incrementResultReceiverCount("SC:requestIsSatelliteProvisioned"); + requestSatelliteCapabilities( new ResultReceiver(this) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { plogd("updateSatelliteSupportedState.requestSatelliteCapabilities: " + "resultCode=" + resultCode + ", resultData=" + resultData); + decrementResultReceiverCount("SC:requestSatelliteCapabilities"); } }); + incrementResultReceiverCount("SC:requestSatelliteCapabilities"); } registerForSatelliteSupportedStateChanged(); + selectBindingSatelliteSubscription(false); + notifySatelliteSupportedStateChanged(supported); } private void updateSatelliteEnabledState(boolean enabled, String caller) { @@ -3954,6 +4632,9 @@ if (!enabled) { mIsModemEnabledReportingNtnSignalStrength.set(false); } + if (mFeatureFlags.satelliteStateChangeListener()) { + notifyEnabledStateChanged(enabled); + } } private void registerForPendingDatagramCount() { @@ -4026,14 +4707,27 @@ } } + private void registerForTerrestrialNetworkAvailableChanged() { + if (mFeatureFlags.carrierRoamingNbIotNtn()) { + if (!mRegisteredForTerrestrialNetworkAvailableChanged.get()) { + mSatelliteModemInterface.registerForTerrestrialNetworkAvailableChanged(this, + EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED, null); + mRegisteredForTerrestrialNetworkAvailableChanged.set(true); + } + } + } + private void handleEventSatelliteProvisionStateChanged(boolean provisioned) { plogd("handleSatelliteProvisionStateChangedEvent: provisioned=" + provisioned); - synchronized (mSatelliteViaOemProvisionLock) { + synchronized (mDeviceProvisionLock) { persistOemEnabledSatelliteProvisionStatus(provisioned); - mIsSatelliteViaOemProvisioned = provisioned; + mIsDeviceProvisioned = provisioned; } + notifyDeviceProvisionStateChanged(provisioned); + } + private void notifyDeviceProvisionStateChanged(boolean provisioned) { List<ISatelliteProvisionStateCallback> deadCallersList = new ArrayList<>(); mSatelliteProvisionStateChangedListeners.values().forEach(listener -> { try { @@ -4048,48 +4742,77 @@ }); } - private void handleEventSatelliteSubscriptionProvisionStateChanged( - List<SatelliteSubscriberInfo> newList, boolean provisioned) { - logd("handleEventSatelliteSubscriptionProvisionStateChanged: newList=" + newList - + " , provisioned=" + provisioned); + private boolean updateSatelliteSubscriptionProvisionState(List<SatelliteSubscriberInfo> newList, + boolean provisioned) { + logd("updateSatelliteSubscriptionProvisionState: List=" + newList + " , provisioned=" + + provisioned); boolean provisionChanged = false; synchronized (mSatelliteTokenProvisionedLock) { for (SatelliteSubscriberInfo subscriberInfo : newList) { - if (mProvisionedSubscriberId.getOrDefault(subscriberInfo.getSubscriberId(), false) - == provisioned) { + + int subId = subscriberInfo.getSubId(); + Boolean currentProvisioned = + mProvisionedSubscriberId.get(subscriberInfo.getSubscriberId()); + if (currentProvisioned == null) { + currentProvisioned = false; + } + + Boolean isProvisionedInPersistentDb = false; + try { + isProvisionedInPersistentDb = mSubscriptionManagerService + .isSatelliteProvisionedForNonIpDatagram(subId); + if (isProvisionedInPersistentDb == null) { + isProvisionedInPersistentDb = false; + } + } catch (IllegalArgumentException | SecurityException ex) { + ploge("isSatelliteProvisionedForNonIpDatagram: subId=" + subId + ", ex=" + + ex); + } + if (currentProvisioned == provisioned + && isProvisionedInPersistentDb == provisioned) { continue; } provisionChanged = true; mProvisionedSubscriberId.put(subscriberInfo.getSubscriberId(), provisioned); - int subId = mSubscriberIdPerSub.getOrDefault(subscriberInfo.getSubscriberId(), - SubscriptionManager.INVALID_SUBSCRIPTION_ID); try { mSubscriptionManagerService.setIsSatelliteProvisionedForNonIpDatagram(subId, provisioned); - plogd("handleEventSatelliteSubscriptionProvisionStateChanged: set Provision " - + "state to db subId=" + subId); + plogd("updateSatelliteSubscriptionProvisionState: set Provision state to db " + + "subId=" + subId); } catch (IllegalArgumentException | SecurityException ex) { ploge("setIsSatelliteProvisionedForNonIpDatagram: subId=" + subId + ", ex=" + ex); } } } - if (!provisionChanged) { - logd("handleEventSatelliteSubscriptionProvisionStateChanged: provision state nothing " - + "changed."); - return; - } + return provisionChanged; + } + + private void handleEventSatelliteSubscriptionProvisionStateChanged() { List<SatelliteSubscriberProvisionStatus> informList = getPrioritizedSatelliteSubscriberProvisionStatusList(); plogd("handleEventSatelliteSubscriptionProvisionStateChanged: " + informList); notifySatelliteSubscriptionProvisionStateChanged(informList); - // Report updated provisioned status + updateDeviceProvisionStatus(); + // Report updated provisioned status to metrics. synchronized (mSatelliteTokenProvisionedLock) { boolean isProvisioned = !mProvisionedSubscriberId.isEmpty() && mProvisionedSubscriberId.containsValue(Boolean.TRUE); mControllerMetricsStats.setIsProvisioned(isProvisioned); } - handleStateChangedForCarrierRoamingNtnEligibility(); + selectBindingSatelliteSubscription(false); + evaluateCarrierRoamingNtnEligibilityChange(); + } + + private void updateDeviceProvisionStatus() { + boolean isProvisioned = getPersistedDeviceProvisionStatus(); + plogd("updateDeviceProvisionStatus: isProvisioned=" + isProvisioned); + synchronized (mDeviceProvisionLock) { + if (mIsDeviceProvisioned == null || mIsDeviceProvisioned != isProvisioned) { + mIsDeviceProvisioned = isProvisioned; + notifyDeviceProvisionStateChanged(isProvisioned); + } + } } private void notifySatelliteSubscriptionProvisionStateChanged( @@ -4111,28 +4834,33 @@ private void handleEventSatelliteModemStateChanged( @SatelliteManager.SatelliteModemState int state) { plogd("handleEventSatelliteModemStateChanged: state=" + state); + + synchronized (mSatelliteModemStateLock) { + mSatelliteModemState = state; + } + if (state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE || state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) { + if (!isWaitingForDisableSatelliteModemResponse()) { + moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_SUCCESS); + } else { + notifyModemStateChangedToSessionController( + SatelliteManager.SATELLITE_MODEM_STATE_OFF); + } + synchronized (mSatelliteEnabledRequestLock) { - if (!mWaitingForDisableSatelliteModemResponse) { - moveSatelliteToOffStateAndCleanUpResources( - SATELLITE_RESULT_SUCCESS); - } else { - notifyModemStateChangedToSessionController( - SatelliteManager.SATELLITE_MODEM_STATE_OFF); - } mWaitingForSatelliteModemOff = false; } } else { - if (isSatelliteEnabled() || isSatelliteBeingEnabled() || isSatelliteBeingDisabled()) { + if (isSatelliteEnabledOrBeingEnabled() || isSatelliteBeingDisabled()) { notifyModemStateChangedToSessionController(state); } else { - // Telephony framework and modem are out of sync. We need to disable modem + // Telephony framework and modem are out of sync. We need to disable synchronized (mSatelliteEnabledRequestLock) { plogw("Satellite modem is in a bad state. Disabling satellite modem now ..."); Consumer<Integer> result = integer -> plogd( "handleEventSatelliteModemStateChanged: disabling satellite result=" - + integer); + + integer); mSatelliteDisabledRequest = new RequestSatelliteEnabledArgument( false /* enableSatellite */, false /* enableDemoMode */, false /* isEmergency */, result); @@ -4188,11 +4916,13 @@ synchronized (mSatelliteCapabilitiesLock) { mSatelliteCapabilities = capabilities; } + overrideSatelliteCapabilitiesIfApplicable(); + SatelliteCapabilities satelliteCapabilities = getSatelliteCapabilities(); List<ISatelliteCapabilitiesCallback> deadCallersList = new ArrayList<>(); mSatelliteCapabilitiesChangedListeners.values().forEach(listener -> { try { - listener.onSatelliteCapabilitiesChanged(capabilities); + listener.onSatelliteCapabilitiesChanged(satelliteCapabilities); } catch (RemoteException e) { plogd("handleEventSatelliteCapabilitiesChanged RemoteException: " + e); deadCallersList.add(listener); @@ -4206,38 +4936,64 @@ private void handleEventSatelliteSupportedStateChanged(boolean supported) { plogd("handleSatelliteSupportedStateChangedEvent: supported=" + supported); - synchronized (mIsSatelliteSupportedLock) { - if (mIsSatelliteSupported != null && mIsSatelliteSupported == supported) { - if (DBG) { - plogd("current satellite support state and new supported state are matched," - + " ignore update."); - } - return; + Boolean isSatelliteSupported = getIsSatelliteSupported(); + if (isSatelliteSupported != null && isSatelliteSupported == supported) { + if (DBG) { + plogd("current satellite support state and new supported state are matched," + + " ignore update."); } - - updateSatelliteSupportedState(supported); - - /* In case satellite has been reported as not support from modem, but satellite is - enabled, request disable satellite. */ - synchronized (mIsSatelliteEnabledLock) { - if (!supported && mIsSatelliteEnabled != null && mIsSatelliteEnabled) { - plogd("Invoke requestSatelliteEnabled(), supported=false, " - + "mIsSatelliteEnabled=true"); - requestSatelliteEnabled(false /* enableSatellite */, false /* enableDemoMode */, - false /* isEmergency */, - new IIntegerConsumer.Stub() { - @Override - public void accept(int result) { - plogd("handleSatelliteSupportedStateChangedEvent: request " - + "satellite disable, result=" + result); - } - }); - - } - } - mIsSatelliteSupported = supported; + return; } + updateSatelliteSupportedState(supported); + + Boolean isSatelliteEnabled = getIsSatelliteEnabled(); + /* In case satellite has been reported as not support from modem, but satellite is + enabled, request disable satellite. */ + if (!supported && isSatelliteEnabled != null && isSatelliteEnabled) { + plogd("Invoke requestSatelliteEnabled(), supported=false, " + + "mIsSatelliteEnabled=true"); + requestSatelliteEnabled(false /* enableSatellite */, false /* enableDemoMode */, + false /* isEmergency */, + new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + plogd("handleSatelliteSupportedStateChangedEvent: request " + + "satellite disable, result=" + result); + } + }); + + } + + synchronized (mIsSatelliteSupportedLock) { + mIsSatelliteSupported = supported; + } + } + + private void handleEventSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("handleEventSelectedNbIotSatelliteSubscriptionChanged: " + + "carrierRoamingNbIotNtn flag is disabled"); + return; + } + + plogd("handleEventSelectedNbIotSatelliteSubscriptionChanged: " + selectedSubId); + + List<ISelectedNbIotSatelliteSubscriptionCallback> deadCallersList = new ArrayList<>(); + mSelectedNbIotSatelliteSubscriptionChangedListeners.values().forEach(listener -> { + try { + listener.onSelectedNbIotSatelliteSubscriptionChanged(selectedSubId); + } catch (RemoteException e) { + logd("handleEventSelectedNbIotSatelliteSubscriptionChanged RemoteException: " + e); + deadCallersList.add(listener); + } + }); + deadCallersList.forEach(listener -> { + mSelectedNbIotSatelliteSubscriptionChangedListeners.remove(listener.asBinder()); + }); + } + + private void notifySatelliteSupportedStateChanged(boolean supported) { List<ISatelliteSupportedStateCallback> deadCallersList = new ArrayList<>(); mSatelliteSupportedStateChangedListeners.values().forEach(listener -> { try { @@ -4274,7 +5030,8 @@ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected void setSettingsKeyToAllowDeviceRotation(int val) { // Only allows on a foldable device type. - if (!isFoldable(mContext)) { + if (!isFoldable(mContext, mDeviceStates)) { + logd("setSettingsKeyToAllowDeviceRotation(" + val + "), device was not a foldable"); return; } @@ -4315,10 +5072,19 @@ * If the device type is foldable. * * @param context context + * @param deviceStates list of {@link DeviceState}s provided from {@link DeviceStateManager} * @return {@code true} if device type is foldable. {@code false} for otherwise. */ - private boolean isFoldable(Context context) { - return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length > 0; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public boolean isFoldable(Context context, List<DeviceState> deviceStates) { + if (android.hardware.devicestate.feature.flags.Flags.deviceStatePropertyMigration()) { + return deviceStates.stream().anyMatch(deviceState -> deviceState.hasProperty( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY) + || deviceState.hasProperty( + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)); + } else { + return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length > 0; + } } /** @@ -4405,6 +5171,7 @@ sendRequestAsync(CMD_UPDATE_SATELLITE_ENABLE_ATTRIBUTES, mSatelliteEnableAttributesUpdateRequest, null); } + updateLastNotifiedNtnModeAndNotify(getSatellitePhone()); } } } @@ -4435,22 +5202,24 @@ public void moveSatelliteToOffStateAndCleanUpResources( @SatelliteManager.SatelliteResult int resultCode) { plogd("moveSatelliteToOffStateAndCleanUpResources"); + setDemoModeEnabled(false); + handlePersistentLoggingOnSessionEnd(mIsEmergency); + setEmergencyMode(false); synchronized (mIsSatelliteEnabledLock) { - setDemoModeEnabled(false); - handlePersistentLoggingOnSessionEnd(mIsEmergency); - setEmergencyMode(false); mIsSatelliteEnabled = false; - setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE); - setSettingsKeyToAllowDeviceRotation(SATELLITE_MODE_ENABLED_FALSE); - abortSatelliteDisableRequest(resultCode); - abortSatelliteEnableRequest(resultCode); - abortSatelliteEnableAttributesUpdateRequest(resultCode); - resetSatelliteEnabledRequest(); - resetSatelliteDisabledRequest(); - // TODO (b/361139260): Stop timer to wait for other radios off - updateSatelliteEnabledState( - false, "moveSatelliteToOffStateAndCleanUpResources"); } + setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE); + setSettingsKeyToAllowDeviceRotation(SATELLITE_MODE_ENABLED_FALSE); + abortSatelliteDisableRequest(resultCode); + abortSatelliteEnableRequest(resultCode); + abortSatelliteEnableAttributesUpdateRequest(resultCode); + resetSatelliteEnabledRequest(); + resetSatelliteDisabledRequest(); + // TODO (b/361139260): Stop timer to wait for other radios off + updateSatelliteEnabledState( + false, "moveSatelliteToOffStateAndCleanUpResources"); + selectBindingSatelliteSubscription(false); + updateLastNotifiedNtnModeAndNotify(getSatellitePhone()); } private void setDemoModeEnabled(boolean enabled) { @@ -4513,6 +5282,7 @@ if (activeSubIds != null) { for (int subId : activeSubIds) { updateSupportedSatelliteServices(subId); + handleCarrierRoamingNtnAvailableServicesChanged(subId); } } else { loge("updateSupportedSatelliteServicesForActiveSubscriptions: " @@ -4625,28 +5395,44 @@ KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE)); } + @NonNull + private Map<String, Set<Integer>> readRegionalSatelliteEarfcnsFromCarrierConfig(int subId) { + PersistableBundle config = getPersistableBundle(subId); + return SatelliteServiceUtils.parseRegionalSatelliteEarfcns( + config.getPersistableBundle(KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE)); + } + @NonNull private PersistableBundle getConfigForSubId(int subId) { PersistableBundle config = null; if (mCarrierConfigManager != null) { - config = mCarrierConfigManager.getConfigForSubId(subId, - KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE, - KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, - KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL, - KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, - KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, - KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY, - KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, - KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT, - KEY_SATELLITE_ESOS_SUPPORTED_BOOL, - KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, - KEY_SATELLITE_NIDD_APN_NAME_STRING, - KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, - KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, - KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT, - KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, - KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, - KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT - ); + try { + config = mCarrierConfigManager.getConfigForSubId(subId, + KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE, + KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, + KEY_SATELLITE_DISPLAY_NAME_STRING, + KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL, + KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, + KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, + KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY, + KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, + KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT, + KEY_SATELLITE_ESOS_SUPPORTED_BOOL, + KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, + KEY_SATELLITE_NIDD_APN_NAME_STRING, + KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, + KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, + KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT, + KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, + KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, + KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, + KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT, + KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY, + KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE, + KEY_SATELLITE_DATA_SUPPORT_MODE_INT + ); + } catch (Exception e) { + logw("getConfigForSubId: " + e); + } } if (config == null || config.isEmpty()) { config = CarrierConfigManager.getDefaultConfig(); @@ -4670,15 +5456,16 @@ updateSupportedSatelliteServicesForActiveSubscriptions(); processNewCarrierConfigData(subId); resetCarrierRoamingSatelliteModeParams(subId); - handleStateChangedForCarrierRoamingNtnEligibility(); + evaluateCarrierRoamingNtnEligibilityChange(); sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION), - TimeUnit.MINUTES.toMillis(1)); + mEvaluateEsosProfilesPrioritizationDurationMillis); + updateRegionalSatelliteEarfcns(subId); } // imsi, msisdn, default sms subId change private void handleSubscriptionsChanged() { sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION), - TimeUnit.MINUTES.toMillis(1)); + mEvaluateEsosProfilesPrioritizationDurationMillis); } private void processNewCarrierConfigData(int subId) { @@ -4760,7 +5547,7 @@ /** If the provision state per subscriberId for the cached is not exist, check the database for * the corresponding value and use it. */ - private void updateSatelliteProvisionedStatePerSubscriberId() { + protected void updateSatelliteProvisionedStatePerSubscriberId() { if (!mFeatureFlags.carrierRoamingNbIotNtn()) { return; } @@ -4784,6 +5571,8 @@ } } } + // Need to update the provision status of the device + updateDeviceProvisionStatus(); } @NonNull @@ -4825,7 +5614,18 @@ return getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL); } - private int getCarrierRoamingNtnConnectType(int subId) { + /** + * 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); + } + + public int getCarrierRoamingNtnConnectType(int subId) { return getConfigForSubId(subId).getInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT); } @@ -4834,6 +5634,11 @@ KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT); } + @CarrierConfigManager.SATELLITE_DATA_SUPPORT_MODE + private int getCarrierSatelliteDataSupportedMode(int subId) { + return getConfigForSubId(subId).getInt(KEY_SATELLITE_DATA_SUPPORT_MODE_INT); + } + /** * Check if satellite attach is enabled by user for the carrier associated with the * {@code subId}. @@ -5086,7 +5891,7 @@ } if (isProvisionRequired) { - Boolean satelliteProvisioned = isSatelliteViaOemProvisioned(); + Boolean satelliteProvisioned = isDeviceProvisioned(); if (satelliteProvisioned == null) { plogd("evaluateOemSatelliteRequestAllowed: satelliteProvisioned is null"); return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE; @@ -5106,13 +5911,23 @@ */ @VisibleForTesting protected @SatelliteManager.NTRadioTechnology int getSupportedNtnRadioTechnology() { - synchronized (mSatelliteCapabilitiesLock) { - if (mSatelliteCapabilities != null) { - return mSatelliteCapabilities.getSupportedRadioTechnologies() - .stream().findFirst().orElse(SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN); - } - return SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN; + SatelliteCapabilities satelliteCapabilities = getSatelliteCapabilities(); + if (satelliteCapabilities != null) { + return satelliteCapabilities.getSupportedRadioTechnologies() + .stream().findFirst().orElse(SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN); } + return SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN; + } + + /** + * Returns a list of messaging apps that support satellite. + */ + @NonNull public List<String> getSatelliteSupportedMsgApps(int subId) { + String[] satelliteSupportedMsgApps = getConfigForSubId(subId) + .getStringArray(KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY); + + return satelliteSupportedMsgApps != null + ? List.of(satelliteSupportedMsgApps) : Collections.emptyList(); } private void sendErrorAndReportSessionMetrics(@SatelliteManager.SatelliteResult int error, @@ -5136,8 +5951,15 @@ } } + private void registerForSignalStrengthChanged() { + for (Phone phone : PhoneFactory.getPhones()) { + phone.getSignalStrengthController().registerForSignalStrengthChanged(this, + EVENT_SIGNAL_STRENGTH_CHANGED, phone.getPhoneId()); + } + } + private void handleEventServiceStateChanged() { - handleStateChangedForCarrierRoamingNtnEligibility(); + evaluateCarrierRoamingNtnEligibilityChange(); handleServiceStateForSatelliteConnectionViaCarrier(); } @@ -5201,6 +6023,7 @@ mWasSatelliteConnectedViaCarrier.put(subId, false); } updateLastNotifiedNtnModeAndNotify(phone); + updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(phone); } } determineAutoConnectSystemNotification(); @@ -5221,6 +6044,7 @@ if (!initialized) mInitialized.put(subId, true); mLastNotifiedNtnMode.put(subId, currNtnMode); phone.notifyCarrierRoamingNtnModeChanged(currNtnMode); + updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(phone); logCarrierRoamingSatelliteSessionStats(phone, lastNotifiedNtnMode, currNtnMode); if(mIsNotificationShowing && !currNtnMode) { dismissSatelliteNotification(); @@ -5249,27 +6073,27 @@ } } - private void handleStateChangedForCarrierRoamingNtnEligibility() { + private void evaluateCarrierRoamingNtnEligibilityChange() { if (!mFeatureFlags.carrierRoamingNbIotNtn()) { - plogd("handleStateChangedForCarrierRoamingNtnEligibility: " + plogd("evaluateCarrierRoamingNtnEligibilityChange: " + "carrierRoamingNbIotNtn flag is disabled"); return; } - synchronized (mSatellitePhoneLock) { - boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone); - plogd("handleStateChangedForCarrierRoamingNtnEligibility: " - + "isCarrierRoamingNtnEligible=" + eligible); + boolean eligible = isCarrierRoamingNtnEligible(getSatellitePhone()); + plogd("evaluateCarrierRoamingNtnEligibilityChange: " + + "isCarrierRoamingNtnEligible=" + eligible); - if (eligible) { - if (shouldStartNtnEligibilityHysteresisTimer(eligible)) { - startNtnEligibilityHysteresisTimer(); - } - } else { - mNtnEligibilityHysteresisTimedOut = false; - stopNtnEligibilityHysteresisTimer(); - updateLastNotifiedNtnEligibilityAndNotify(false); + if (eligible) { + if (shouldStartNtnEligibilityHysteresisTimer(eligible)) { + startNtnEligibilityHysteresisTimer(); } + } else { + synchronized (mSatellitePhoneLock) { + mNtnEligibilityHysteresisTimedOut = false; + } + stopNtnEligibilityHysteresisTimer(); + updateLastNotifiedNtnEligibilityAndNotify(false); } } @@ -5293,20 +6117,22 @@ } private void startNtnEligibilityHysteresisTimer() { - synchronized (mSatellitePhoneLock) { - if (mSatellitePhone == null) { - ploge("startNtnEligibilityHysteresisTimer: mSatellitePhone is null."); - return; - } - - int subId = getHighestPrioritySubscrption(); - long timeout = getCarrierSupportedSatelliteNotificationHysteresisTimeMillis(subId); - mNtnEligibilityHysteresisTimedOut = false; - plogd("startNtnEligibilityHysteresisTimer: sendMessageDelayed subId=" + subId - + ", phoneId=" + mSatellitePhone.getPhoneId() + ", timeout=" + timeout); - sendMessageDelayed(obtainMessage(EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT), - timeout); + Phone satellitePhone = getSatellitePhone(); + if (satellitePhone == null) { + ploge("startNtnEligibilityHysteresisTimer: mSatellitePhone is null."); + return; } + + int subId = getSelectedSatelliteSubId(); + long timeout = getCarrierSupportedSatelliteNotificationHysteresisTimeMillis(subId); + synchronized (mSatellitePhoneLock) { + mNtnEligibilityHysteresisTimedOut = false; + } + plogd("startNtnEligibilityHysteresisTimer: sendMessageDelayed subId=" + subId + + ", phoneId=" + satellitePhone.getPhoneId() + ", timeout=" + timeout); + sendMessageDelayed(obtainMessage(EVENT_NOTIFY_NTN_ELIGIBILITY_HYSTERESIS_TIMED_OUT), + timeout); + } private void stopNtnEligibilityHysteresisTimer() { @@ -5321,19 +6147,26 @@ return; } - synchronized (mSatellitePhoneLock) { - if (mSatellitePhone == null) { - ploge("notifyNtnEligibility: mSatellitePhone is null"); - return; - } + Phone satellitePhone = getSatellitePhone(); + if (satellitePhone == null) { + ploge("notifyNtnEligibility: mSatellitePhone is null"); + return; + } - plogd("notifyNtnEligibility: phoneId=" + mSatellitePhone.getPhoneId() + if (mOverrideNtnEligibility != null) { + satellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility); + return; + } + + int selectedSatelliteSubId = getSelectedSatelliteSubId(); + synchronized (mSatellitePhoneLock) { + plogd("notifyNtnEligibility: phoneId=" + satellitePhone.getPhoneId() + " currentNtnEligibility=" + currentNtnEligibility); if (mLastNotifiedNtnEligibility == null || mLastNotifiedNtnEligibility != currentNtnEligibility) { mLastNotifiedNtnEligibility = currentNtnEligibility; - mSatellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility); - updateSatelliteSystemNotification(getHighestPrioritySubscrption(), + satellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility); + updateSatelliteSystemNotification(selectedSatelliteSubId, CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL, currentNtnEligibility); } @@ -5353,10 +6186,10 @@ } private void persistOemEnabledSatelliteProvisionStatus(boolean isProvisioned) { - synchronized (mSatelliteViaOemProvisionLock) { + synchronized (mDeviceProvisionLock) { plogd("persistOemEnabledSatelliteProvisionStatus: isProvisioned=" + isProvisioned); if (mFeatureFlags.carrierRoamingNbIotNtn()) { - int subId = SatelliteServiceUtils.getNtnOnlySubscriptionId(mContext); + int subId = getNtnOnlySubscriptionId(); if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { try { mSubscriptionManagerService.setIsSatelliteProvisionedForNonIpDatagram(subId, @@ -5383,24 +6216,34 @@ } @Nullable - private Boolean getPersistedOemEnabledSatelliteProvisionStatus() { - plogd("getPersistedOemEnabledSatelliteProvisionStatus:"); - synchronized (mSatelliteViaOemProvisionLock) { + private boolean getPersistedDeviceProvisionStatus() { + plogd("getPersistedDeviceProvisionStatus"); + synchronized (mDeviceProvisionLock) { if (mFeatureFlags.carrierRoamingNbIotNtn()) { - int subId = SatelliteServiceUtils.getNtnOnlySubscriptionId(mContext); + int subId = getNtnOnlySubscriptionId(); if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - return mSubscriptionManagerService.isSatelliteProvisionedForNonIpDatagram( - subId); - } else { - plogd("getPersistedOemEnabledSatelliteProvisionStatus: " - + "subId=INVALID_SUBSCRIPTION_ID, return null"); - return null; + if (mSubscriptionManagerService.isSatelliteProvisionedForNonIpDatagram(subId)) { + return true; + } } + + List<SubscriptionInfo> activeSubscriptionInfoList = + mSubscriptionManagerService.getActiveSubscriptionInfoList( + mContext.getOpPackageName(), mContext.getAttributionTag(), true); + for (SubscriptionInfo info : activeSubscriptionInfoList) { + if (info.isSatelliteESOSSupported()) { + if (mSubscriptionManagerService.isSatelliteProvisionedForNonIpDatagram( + info.getSubscriptionId())) { + return true; + } + } + } + return false; } else { if (!loadSatelliteSharedPreferences()) return false; if (mSharedPreferences == null) { - ploge("getPersistedOemEnabledSatelliteProvisionStatus: mSharedPreferences is " + ploge("getPersistedDeviceProvisionStatus: mSharedPreferences is " + "null"); return false; } else { @@ -5432,8 +6275,9 @@ Bundle bundle = new Bundle(); bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, - Boolean.TRUE.equals(isSatelliteViaOemProvisioned())); + Boolean.TRUE.equals(isDeviceProvisioned())); ((ResultReceiver) request.argument).send(SATELLITE_RESULT_SUCCESS, bundle); + decrementResultReceiverCount("SC:requestIsSatelliteProvisioned"); } private long getWaitForSatelliteEnablingResponseTimeoutMillis() { @@ -5550,50 +6394,51 @@ + argument.requestId + ", enableSatellite=" + argument.enableSatellite); argument.callback.accept(SATELLITE_RESULT_MODEM_TIMEOUT); - synchronized (mIsSatelliteEnabledLock) { - if (argument.enableSatellite) { - resetSatelliteEnabledRequest(); - abortSatelliteEnableAttributesUpdateRequest(SATELLITE_RESULT_REQUEST_ABORTED); - synchronized (mSatelliteEnabledRequestLock) { - if (mSatelliteDisabledRequest == null) { - IIntegerConsumer callback = new IIntegerConsumer.Stub() { - @Override - public void accept(int result) { - plogd("handleEventWaitForSatelliteEnablingResponseTimedOut: " - + "disable satellite result=" + result); - } - }; - Consumer<Integer> result = - FunctionalUtils.ignoreRemoteException(callback::accept); - mSatelliteDisabledRequest = new RequestSatelliteEnabledArgument( - false, false, false, result); - sendRequestAsync(CMD_SET_SATELLITE_ENABLED, mSatelliteDisabledRequest, - null); + if (argument.enableSatellite) { + resetSatelliteEnabledRequest(); + abortSatelliteEnableAttributesUpdateRequest(SATELLITE_RESULT_REQUEST_ABORTED); + if (getSatelliteDisabledRequest() == null) { + IIntegerConsumer callback = new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + plogd("handleEventWaitForSatelliteEnablingResponseTimedOut: " + + "disable satellite result=" + result); } + }; + Consumer<Integer> result = + FunctionalUtils.ignoreRemoteException(callback::accept); + + RequestSatelliteEnabledArgument request; + synchronized (mSatelliteEnabledRequestLock) { + mSatelliteDisabledRequest = new RequestSatelliteEnabledArgument( + false, false, false, result); + request = mSatelliteDisabledRequest; } - mControllerMetricsStats.reportServiceEnablementFailCount(); - mSessionMetricsStats.setInitializationResult(SATELLITE_RESULT_MODEM_TIMEOUT) - .setSatelliteTechnology(getSupportedNtnRadioTechnology()) - .setInitializationProcessingTime( - System.currentTimeMillis() - mSessionProcessingTimeStamp) - .setIsDemoMode(mIsDemoModeEnabled) - .setCarrierId(getSatelliteCarrierId()) - .reportSessionMetrics(); - } else { - resetSatelliteDisabledRequest(); - mControllerMetricsStats.onSatelliteDisabled(); - mSessionMetricsStats.setTerminationResult(SATELLITE_RESULT_MODEM_TIMEOUT) - .setSatelliteTechnology(getSupportedNtnRadioTechnology()) - .setTerminationProcessingTime( - System.currentTimeMillis() - mSessionProcessingTimeStamp) - .setSessionDurationSec(calculateSessionDurationTimeSec()) - .reportSessionMetrics(); + sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null); } - notifyEnablementFailedToSatelliteSessionController(argument.enableSatellite); - mSessionStartTimeStamp = 0; - mSessionProcessingTimeStamp = 0; + + mControllerMetricsStats.reportServiceEnablementFailCount(); + mSessionMetricsStats.setInitializationResult(SATELLITE_RESULT_MODEM_TIMEOUT) + .setSatelliteTechnology(getSupportedNtnRadioTechnology()) + .setInitializationProcessingTime( + System.currentTimeMillis() - mSessionProcessingTimeStamp) + .setIsDemoMode(mIsDemoModeEnabled) + .setCarrierId(getSatelliteCarrierId()) + .reportSessionMetrics(); + } else { + resetSatelliteDisabledRequest(); + mControllerMetricsStats.onSatelliteDisabled(); + mSessionMetricsStats.setTerminationResult(SATELLITE_RESULT_MODEM_TIMEOUT) + .setSatelliteTechnology(getSupportedNtnRadioTechnology()) + .setTerminationProcessingTime( + System.currentTimeMillis() - mSessionProcessingTimeStamp) + .setSessionDurationSec(calculateSessionDurationTimeSec()) + .reportSessionMetrics(); } + notifyEnablementFailedToSatelliteSessionController(argument.enableSatellite); + mSessionStartTimeStamp = 0; + mSessionProcessingTimeStamp = 0; } private void handleCmdUpdateNtnSignalStrengthReporting(boolean shouldReport) { @@ -5603,7 +6448,7 @@ return; } - if (!isSatelliteEnabled()) { + if (!isSatelliteEnabledOrBeingEnabled()) { plogd("handleCmdUpdateNtnSignalStrengthReporting: ignore request, satellite is " + "disabled"); return; @@ -5680,7 +6525,8 @@ + mIsNotificationShowing); } if (isNtn.first) { - if (!notificationKeyStatus) { + if (!notificationKeyStatus && getCarrierRoamingNtnConnectType(isNtn.second) + == CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC) { updateSatelliteSystemNotification(isNtn.second, CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC, /*visible*/ true); @@ -5697,6 +6543,23 @@ updateSatelliteSystemNotification(-1, -1,/*visible*/ false); } + public boolean isSatelliteSystemNotificationsEnabled(int carrierRoamingNtnConnectType) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + return false; + } + if (carrierRoamingNtnConnectType + != CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL) { + return true; + } + boolean notifySatelliteAvailabilityEnabled = + mContext.getResources().getBoolean(R.bool.config_satellite_should_notify_availability); + Boolean isSatelliteSupported = getIsSatelliteSupported(); + if(isSatelliteSupported == null) { + return false; + } + return notifySatelliteAvailabilityEnabled && isSatelliteSupported; + } + /** * Update the system notification to reflect the current satellite status, that's either already * connected OR needs to be manually enabled. The device should only display one notification @@ -5710,6 +6573,11 @@ */ private void updateSatelliteSystemNotification(int subId, @CARRIER_ROAMING_NTN_CONNECT_TYPE int carrierRoamingNtnConnectType, boolean visible) { + if (!isSatelliteSystemNotificationsEnabled(carrierRoamingNtnConnectType)) { + plogd("updateSatelliteSystemNotification: satellite notifications are not enabled."); + return; + } + plogd("updateSatelliteSystemNotification subId=" + subId + ", carrierRoamingNtnConnectType=" + SatelliteServiceUtils.carrierRoamingNtnConnectTypeToString( carrierRoamingNtnConnectType) + ", visible=" + visible); @@ -6015,7 +6883,7 @@ AccessNetworkConstants.TRANSPORT_TYPE_WWAN); for (NetworkRegistrationInfo nri : nriList) { - if (nri.isInService() || nri.isEmergencyEnabled()) { + if (nri.isInService()) { logv("getWwanIsInService: return true"); return true; } @@ -6075,6 +6943,13 @@ } } + private void plogv(@NonNull String log) { + Rlog.v(TAG, log); + if (mPersistentLogger != null) { + mPersistentLogger.debug(TAG, log); + } + } + private void handlePersistentLoggingOnSessionStart(RequestSatelliteEnabledArgument argument) { if (mPersistentLogger == null) { return; @@ -6135,6 +7010,10 @@ return TimeUnit.SECONDS.toMillis(duration); } + private long getEvaluateEsosProfilesPrioritizationDurationMillis() { + return TimeUnit.MINUTES.toMillis(1); + } + /** * Calculate priority * 1. Active eSOS profiles are higher priority than inactive eSOS profiles. @@ -6152,7 +7031,8 @@ List<SubscriptionInfo> allSubInfos = mSubscriptionManagerService.getAllSubInfoList( mContext.getOpPackageName(), mContext.getAttributionTag()); // Key : priority - lower value has higher priority; Value : List<SubscriptionInfo> - Map<Integer, List<SubscriptionInfo>> newSubsInfoListPerPriority = new HashMap<>(); + TreeMap<Integer, List<SubscriptionInfo>> newSubsInfoListPerPriority = new TreeMap<>(); + plogd("evaluateESOSProfilesPrioritization: allSubInfos.size()=" + allSubInfos.size()); synchronized (mSatelliteTokenProvisionedLock) { for (SubscriptionInfo info : allSubInfos) { int subId = info.getSubscriptionId(); @@ -6164,6 +7044,14 @@ if (!isNtnOnly && !isESOSSupported) { continue; } + if (!isActive && !isNtnOnly) { + continue; + } + if (!isNtnOnly && !isCarrierConfigLoaded(subId)) { + // Skip to add priority list if the carrier config is not loaded properly + // for the given carrier subscription. + continue; + } int keyPriority = (isESOSSupported && isActive && isDefaultSmsSubId) ? 0 : (isESOSSupported && isActive) ? 1 @@ -6189,8 +7077,13 @@ logd("Old phone number is removed: id = " + subId); isChanged = true; } + if (!newSubscriberId.isEmpty()) { + mSubscriberIdPerSub.put(newSubscriberId, subId); + } } } + plogd("evaluateESOSProfilesPrioritization: newSubsInfoListPerPriority.size()=" + + newSubsInfoListPerPriority.size()); if (!mHasSentBroadcast && newSubsInfoListPerPriority.size() == 0) { logd("evaluateESOSProfilesPrioritization: no satellite subscription available"); @@ -6199,27 +7092,45 @@ // If priority has changed, send broadcast for provisioned ESOS subs IDs synchronized (mSatelliteTokenProvisionedLock) { + List<SatelliteSubscriberProvisionStatus> newEvaluatedSubscriberProvisionStatus = + getPrioritizedSatelliteSubscriberProvisionStatusList( + newSubsInfoListPerPriority); if (isPriorityChanged(mSubsInfoListPerPriority, newSubsInfoListPerPriority) + || isSubscriberContentChanged(mLastEvaluatedSubscriberProvisionStatus, + newEvaluatedSubscriberProvisionStatus) || isChanged) { mSubsInfoListPerPriority = newSubsInfoListPerPriority; + mLastEvaluatedSubscriberProvisionStatus = newEvaluatedSubscriberProvisionStatus; sendBroadCastForProvisionedESOSSubs(); mHasSentBroadcast = true; + selectBindingSatelliteSubscription(false); } } } + // to check if the contents of carrier config is loaded properly + private Boolean isCarrierConfigLoaded(int subId) { + PersistableBundle carrierConfig = mCarrierConfigManager + .getConfigForSubId(subId, KEY_CARRIER_CONFIG_APPLIED_BOOL); + return carrierConfig != null ? carrierConfig.getBoolean( + CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL) : false; + } + // The subscriberId for ntnOnly SIMs is the Iccid, whereas for ESOS supported SIMs, the // subscriberId is the Imsi prefix 6 digit + phone number. - private Pair<String, Integer> getSubscriberIdAndType(SubscriptionInfo info) { + private Pair<String, Integer> getSubscriberIdAndType(@Nullable SubscriptionInfo info) { String subscriberId = ""; @SatelliteSubscriberInfo.SubscriberIdType int subscriberIdType = SatelliteSubscriberInfo.ICCID; - if (info.isSatelliteESOSSupported()) { - subscriberId = getPhoneNumberBasedCarrier(info.getSubscriptionId()); - subscriberIdType = SatelliteSubscriberInfo.IMSI_MSISDN; + if (info == null) { + logd("getSubscriberIdAndType: subscription info is null"); + return new Pair<>(subscriberId, subscriberIdType); } if (info.isOnlyNonTerrestrialNetwork()) { subscriberId = info.getIccId(); + } else if (info.isSatelliteESOSSupported()) { + subscriberId = getPhoneNumberBasedCarrier(info.getSubscriptionId()); + subscriberIdType = SatelliteSubscriberInfo.IMSI_MSISDN; } logd("getSubscriberIdAndType: subscriberId=" + subscriberId + ", subscriberIdType=" + subscriberIdType); @@ -6267,6 +7178,24 @@ return false; } + // Checks if there are any changes between subscriberInfos. return false if the same. + // Note that, Use lists with the same priority so we can compare contents properly. + private boolean isSubscriberContentChanged(List<SatelliteSubscriberProvisionStatus> currentList, + List<SatelliteSubscriberProvisionStatus> newList) { + if (currentList.size() != newList.size()) { + return true; + } + for (int i = 0; i < currentList.size(); i++) { + SatelliteSubscriberProvisionStatus curSub = currentList.get(i); + SatelliteSubscriberProvisionStatus newSub = newList.get(i); + if (!curSub.getSatelliteSubscriberInfo().equals(newSub.getSatelliteSubscriberInfo())) { + logd("isSubscriberContentChanged: cur=" + curSub + " , new=" + newSub); + return true; + } + } + return false; + } + private void sendBroadCastForProvisionedESOSSubs() { String packageName = getConfigSatelliteGatewayServicePackage(); String className = getConfigSatelliteCarrierRoamingEsosProvisionedClass(); @@ -6300,6 +7229,29 @@ } /** + * Request to get the name to display for Satellite. + * + * @param result The result receiver that returns the name to display for the satellite + * or an error code if the request failed. + */ + public void requestSatelliteDisplayName(@NonNull ResultReceiver result) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("requestSatelliteDisplayName: carrierRoamingNbIotNtn flag is disabled"); + result.send(SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED, null); + return; + } + + int subId = getSelectedSatelliteSubId(); + String displayName = getConfigForSubId(subId).getString( + KEY_SATELLITE_DISPLAY_NAME_STRING, "Satellite"); + + plogd("requestSatelliteDisplayName: " + displayName); + Bundle bundle = new Bundle(); + bundle.putString(SatelliteManager.KEY_SATELLITE_DISPLAY_NAME, displayName); + result.send(SATELLITE_RESULT_SUCCESS, bundle); + } + + /** * Request to get list of prioritized satellite tokens to be used for provision. * * @param result The result receiver, which returns the list of prioritized satellite tokens @@ -6320,10 +7272,18 @@ private List<SatelliteSubscriberProvisionStatus> getPrioritizedSatelliteSubscriberProvisionStatusList() { + synchronized (mSatelliteTokenProvisionedLock) { + return getPrioritizedSatelliteSubscriberProvisionStatusList(mSubsInfoListPerPriority); + } + } + + private List<SatelliteSubscriberProvisionStatus> + getPrioritizedSatelliteSubscriberProvisionStatusList( + Map<Integer, List<SubscriptionInfo>> subsInfoListPerPriority) { List<SatelliteSubscriberProvisionStatus> list = new ArrayList<>(); synchronized (mSatelliteTokenProvisionedLock) { - for (int priority : mSubsInfoListPerPriority.keySet()) { - List<SubscriptionInfo> infoList = mSubsInfoListPerPriority.get(priority); + for (int priority : subsInfoListPerPriority.keySet()) { + List<SubscriptionInfo> infoList = subsInfoListPerPriority.get(priority); if (infoList == null) { logd("getPrioritySatelliteSubscriberProvisionStatusList: no exist this " + "priority " + priority); @@ -6355,7 +7315,7 @@ + ", provisioned=" + provisioned); list.add(new SatelliteSubscriberProvisionStatus.Builder() .setSatelliteSubscriberInfo(satelliteSubscriberInfo) - .setProvisionStatus(provisioned).build()); + .setProvisioned(provisioned).build()); mSubscriberIdPerSub.put(subscriberId, info.getSubscriptionId()); } } @@ -6363,6 +7323,89 @@ return list; } + public int getSelectedSatelliteSubId() { + synchronized (mSatelliteTokenProvisionedLock) { + return mSelectedSatelliteSubId; + } + } + + /** + * Request to get the currently selected satellite subscription id. + * + * @param result The result receiver that returns the currently selected satellite subscription + * id if the request is successful or an error code if the request failed. + */ + public void requestSelectedNbIotSatelliteSubscriptionId(@NonNull ResultReceiver result) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null); + logd("requestSelectedNbIotSatelliteSubscriptionId: carrierRoamingNbIotNtn is disabled"); + return; + } + + int selectedSatelliteSubId = getSelectedSatelliteSubId(); + plogd("requestSelectedNbIotSatelliteSubscriptionId: " + selectedSatelliteSubId); + if (selectedSatelliteSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + result.send(SATELLITE_RESULT_NO_VALID_SATELLITE_SUBSCRIPTION, null); + logd("requestSelectedNbIotSatelliteSubscriptionId: " + + "selectedSatelliteSubId is invalid"); + return; + } + + Bundle bundle = new Bundle(); + bundle.putInt(SatelliteManager.KEY_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_ID, + selectedSatelliteSubId); + result.send(SATELLITE_RESULT_SUCCESS, bundle); + } + + private void selectBindingSatelliteSubscription(boolean shouldIgnoreEnabledState) { + if ((isSatelliteEnabled() || isSatelliteBeingEnabled()) && !shouldIgnoreEnabledState) { + plogd("selectBindingSatelliteSubscription: satellite subscription will be selected " + + "once the satellite session ends"); + return; + } + + int selectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + List<SatelliteSubscriberProvisionStatus> satelliteSubscribers = + getPrioritizedSatelliteSubscriberProvisionStatusList(); + for (SatelliteSubscriberProvisionStatus status : satelliteSubscribers) { + // TODO: need to check if satellite is allowed at current location for the subscription + int subId = getSubIdFromSubscriberId( + status.getSatelliteSubscriberInfo().getSubscriberId()); + if (status.isProvisioned() && isActiveSubId(subId)) { + selectedSubId = subId; + break; + } + } + + if (selectedSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID + && isSatelliteSupportedViaOem()) { + selectedSubId = getNtnOnlySubscriptionId(); + } + + synchronized (mSatelliteTokenProvisionedLock) { + mSelectedSatelliteSubId = selectedSubId; + } + setSatellitePhone(selectedSubId); + plogd("selectBindingSatelliteSubscription: SelectedSatelliteSubId=" + selectedSubId); + handleEventSelectedNbIotSatelliteSubscriptionChanged(selectedSubId); + } + + private int getSubIdFromSubscriberId(String subscriberId) { + synchronized (mSatelliteTokenProvisionedLock) { + return mSubscriberIdPerSub.getOrDefault(subscriberId, + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + } + + private boolean isActiveSubId(int subId) { + SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId); + if (subInfo == null) { + logd("isActiveSubId: subscription associated with subId=" + subId + " not found"); + return false; + } + return subInfo.isActive(); + } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected boolean isSubscriptionProvisioned(int subId) { plogd("isSubscriptionProvisioned: subId=" + subId); @@ -6393,19 +7436,160 @@ @NonNull ResultReceiver result) { if (!mFeatureFlags.carrierRoamingNbIotNtn()) { result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null); + logd("provisionSatellite: carrierRoamingNbIotNtn not support"); return; } - if (list.size() == 0) { + if (list.isEmpty()) { result.send(SATELLITE_RESULT_INVALID_ARGUMENTS, null); + logd("provisionSatellite: SatelliteSubscriberInfo list is empty"); return; } logd("provisionSatellite:" + list); RequestProvisionSatelliteArgument request = new RequestProvisionSatelliteArgument(list, - result); + result, true); sendRequestAsync(CMD_UPDATE_PROVISION_SATELLITE_TOKEN, request, null); + incrementResultReceiverCount("SC:provisionSatellite"); } + /** + * Request to update system selection channels. + * + * @param result The result receiver that returns if the request is successful or + * an error code if the request failed. + */ + public void updateSystemSelectionChannels( + @NonNull List<SystemSelectionSpecifier> selectionSpecifiers, + @NonNull ResultReceiver result) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("updateSystemSelectionChannels: " + + "carrierRoamingNbIotNtn flag is disabled"); + result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null); + return; + } + + sendRequestAsync(CMD_UPDATE_SYSTEM_SELECTION_CHANNELS, + new UpdateSystemSelectionChannelsArgument(selectionSpecifiers, result), null); + } + + /** + * @param subId Subscription ID. + * @return The The map of earfcns with key: regional satellite config Id, + * value: set of earfcns in the corresponding regions associated with the {@code subId}. + */ + @NonNull + public Map<String, Set<Integer>> getRegionalSatelliteEarfcns(int subId) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + logd("getRegionalSatelliteEarfcns: carrierRoamingNbIotNtnFlag is disabled"); + return new HashMap<>(); + } + synchronized (mRegionalSatelliteEarfcnsLock) { + if (mRegionalSatelliteEarfcns.containsKey(subId)) { + return mRegionalSatelliteEarfcns.get(subId); + } else { + logd("getRegionalSatelliteEarfcns: Earfcns for subId: " + subId + " not found"); + return new HashMap<>(); + } + } + } + + /** + * Update regional satellite earfcn information from carrier config. + */ + public void updateRegionalSatelliteEarfcns(int subId) { + plogd("updateRegionalSatelliteEarfcns with subId " + subId); + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("updateRegionalSatelliteEarfcns: " + + "carrierRoamingNbIotNtn flag is disabled"); + return; + } + + synchronized (mRegionalSatelliteEarfcnsLock) { + mRegionalSatelliteEarfcns.put(subId, + readRegionalSatelliteEarfcnsFromCarrierConfig(subId)); + } + } + + /** + * Deliver the list of deprovisioned satellite subscriber ids. + * + * @param list List of deprovisioned satellite subscriber ids. + * @param result The result receiver that returns whether deliver success or fail. + */ + public void deprovisionSatellite(@NonNull List<SatelliteSubscriberInfo> list, + @NonNull ResultReceiver result) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null); + logd("deprovisionSatellite: carrierRoamingNbIotNtn not support"); + return; + } + if (list.isEmpty()) { + result.send(SATELLITE_RESULT_INVALID_ARGUMENTS, null); + logd("deprovisionSatellite: SatelliteSubscriberInfo list is empty"); + return; + } + + logd("deprovisionSatellite:" + list); + RequestProvisionSatelliteArgument request = new RequestProvisionSatelliteArgument(list, + result, false); + sendRequestAsync(CMD_UPDATE_PROVISION_SATELLITE_TOKEN, request, null); + incrementResultReceiverCount("SC:provisionSatellite"); + } + + /** + * Inform whether application supports NTN SMS in satellite mode. + * + * This method is used by default messaging application to inform framework whether it supports + * NTN SMS or not. + * + * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}. + */ + public void setNtnSmsSupportedByMessagesApp(boolean ntnSmsSupported) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + return; + } + persistNtnSmsSupportedByMessagesApp(ntnSmsSupported); + handleCarrierRoamingNtnAvailableServicesChanged(getSelectedSatelliteSubId()); + } + + private void persistNtnSmsSupportedByMessagesApp(boolean ntnSmsSupported) { + plogd("persistNtnSmsSupportedByMessagesApp: ntnSmsSupported=" + ntnSmsSupported); + if (!loadSatelliteSharedPreferences()) return; + + if (mSharedPreferences == null) { + ploge("persistNtnSmsSupportedByMessagesApp: mSharedPreferences is null"); + } else { + mSharedPreferences.edit().putBoolean( + NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY, ntnSmsSupported).apply(); + synchronized (mNtnSmsSupportedByMessagesAppLock) { + mNtnSmsSupportedByMessagesApp = ntnSmsSupported; + } + } + } + + private boolean isNtnSmsSupportedByMessagesApp() { + synchronized (mNtnSmsSupportedByMessagesAppLock) { + if (mNtnSmsSupportedByMessagesApp != null) { + plogd("isNtnSmsSupportedByMessagesApp:" + mNtnSmsSupportedByMessagesApp); + return mNtnSmsSupportedByMessagesApp; + } + } + + if (!loadSatelliteSharedPreferences()) return false; + + if (mSharedPreferences == null) { + ploge("isNtnSmsSupportedByMessagesApp: mSharedPreferences is null"); + return false; + } else { + boolean ntnSmsSupported = mSharedPreferences.getBoolean( + NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY, false); + synchronized (mNtnSmsSupportedByMessagesAppLock) { + mNtnSmsSupportedByMessagesApp = ntnSmsSupported; + plogd("isNtnSmsSupportedByMessagesApp:" + mNtnSmsSupportedByMessagesApp); + } + return ntnSmsSupported; + } + } @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected void setSatellitePhone(int subId) { @@ -6414,7 +7598,8 @@ if (mSatellitePhone == null) { mSatellitePhone = SatelliteServiceUtils.getPhone(); } - plogd("mSatellitePhone:" + (mSatellitePhone != null) + ", subId=" + subId); + plogd("mSatellitePhone: phoneId=" + (mSatellitePhone != null + ? mSatellitePhone.getPhoneId() : "null") + ", subId=" + subId); int carrierId = mSatellitePhone.getCarrierId(); if (carrierId != UNKNOWN_CARRIER_ID) { mControllerMetricsStats.setCarrierId(carrierId); @@ -6427,13 +7612,14 @@ /** Return the carrier ID of the binding satellite subscription. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public int getSatelliteCarrierId() { - synchronized (mSatellitePhoneLock) { - if (mSatellitePhone != null) { - return mSatellitePhone.getCarrierId(); - } else { + synchronized (mSatelliteTokenProvisionedLock) { + SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo( + mSelectedSatelliteSubId); + if (subInfo == null) { logd("getSatelliteCarrierId: returns UNKNOWN_CARRIER_ID"); return UNKNOWN_CARRIER_ID; } + return subInfo.getCarrierId(); } } @@ -6458,12 +7644,22 @@ return false; } + if (!mIsRadioOn) { + plogd("isCarrierRoamingNtnEligible: radio is off"); + return false; + } + if (phone == null) { plogd("isCarrierRoamingNtnEligible: phone is null"); return false; } int subId = phone.getSubId(); + if (!isSatelliteRoamingP2pSmSSupported(subId)) { + plogd("isCarrierRoamingNtnEligible: doesn't support P2P SMS"); + return false; + } + if (!isSatelliteSupportedViaCarrier(subId)) { plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId() + "]: satellite is not supported via carrier"); @@ -6476,8 +7672,9 @@ return false; } - if (!isSatelliteServiceSupportedByCarrier(subId, - NetworkRegistrationInfo.SERVICE_TYPE_SMS)) { + + int[] services = getSupportedServicesOnCarrierRoamingNtn(subId); + if (!ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS)) { plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId() + "]: SMS is not supported by carrier"); return false; @@ -6490,6 +7687,11 @@ return false; } + if (mOverrideNtnEligibility != null) { + // TODO need to send the value from `mOverrideNtnEligibility` or simply true ? + return true; + } + if (SatelliteServiceUtils.isCellularAvailable()) { plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId() + "]: cellular is available"); @@ -6507,11 +7709,19 @@ return true; } - private boolean isSatelliteServiceSupportedByCarrier(int subId, + + /** + * Checks if the satellite service is supported by the carrier for the specified + * subscription ID and servicetype. + * + * @param subId The subscription id. + * @param serviceType The type of service to check + */ + public boolean isSatelliteServiceSupportedByCarrier(int subId, @NetworkRegistrationInfo.ServiceType int serviceType) { List<String> satellitePlmnList = getSatellitePlmnsForCarrier(subId); for (String satellitePlmn : satellitePlmnList) { - if (getSupportedSatelliteServices(subId, satellitePlmn).contains(serviceType)) { + if (getSupportedSatelliteServicesForPlmn(subId, satellitePlmn).contains(serviceType)) { return true; } } @@ -6526,15 +7736,6 @@ } } - /** - * Return the highest priority satellite subscirption ID. - */ - public int getHighestPrioritySubscrption() { - synchronized (mSatellitePhoneLock) { - return mSatellitePhone.getSubId(); - } - } - /** Start PointingUI if it is required. */ public void startPointingUI() { synchronized (mNeedsSatellitePointingLock) { @@ -6623,6 +7824,42 @@ }); } + private void handleEventTerrestrialNetworkAvailableChanged(boolean isAvailable) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("handleEventTerrestrialNetworkAvailableChanged: " + + "carrierRoamingNbIotNtn flag is disabled"); + return; + } + + plogd("handleEventTerrestrialNetworkAvailableChanged: " + isAvailable); + + List<ISatelliteModemStateCallback> deadCallersList = new ArrayList<>(); + mTerrestrialNetworkAvailableChangedListeners.values().forEach(listener -> { + try { + listener.onTerrestrialNetworkAvailableChanged(isAvailable); + } catch (RemoteException e) { + logd("handleEventTerrestrialNetworkAvailableChanged RemoteException: " + e); + deadCallersList.add(listener); + } + }); + deadCallersList.forEach(listener -> { + mTerrestrialNetworkAvailableChangedListeners.remove(listener.asBinder()); + }); + + if (isAvailable && !mIsEmergency) { + requestSatelliteEnabled( + false /* enableSatellite */, false /* enableDemoMode */, + false /* isEmergency */, + new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + plogd("handleEventTerrestrialNetworkAvailableChanged:" + + " requestSatelliteEnabled result=" + result); + } + }); + } + } + /** * This API can be used by only CTS to override the cached value for the device overlay config * value : @@ -6672,7 +7909,8 @@ return result; } - private String getConfigSatelliteGatewayServicePackage() { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected String getConfigSatelliteGatewayServicePackage() { if (!mChangeIntentComponent) { return getStringFromOverlayConfig( R.string.config_satellite_gateway_service_package); @@ -6702,6 +7940,11 @@ mContext.registerReceiver(mDefaultSmsSubscriptionChangedBroadcastReceiver, intentFilter); } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected List<DeviceState> getSupportedDeviceStates() { + return mContext.getSystemService(DeviceStateManager.class).getSupportedDeviceStates(); + } + FeatureFlags getFeatureFlags() { return mFeatureFlags; } @@ -6719,4 +7962,498 @@ return !mWaitingForSatelliteModemOff; } } + + /** + * Method to override the Carrier roaming Non-terrestrial network eligibility check + * + * @param state flag to enable or disable the Ntn eligibility check. + * @param resetRequired reset overriding the check with adb command. + */ + public boolean overrideCarrierRoamingNtnEligibilityChanged(boolean state, + boolean resetRequired) { + Log.d(TAG, "overrideCarrierRoamingNtnEligibilityChanged state = " + state + + " resetRequired = " + resetRequired); + if (resetRequired) { + mOverrideNtnEligibility = null; + } else { + if (mOverrideNtnEligibility == null) { + mOverrideNtnEligibility = new AtomicBoolean(state); + } else { + mOverrideNtnEligibility.set(state); + } + synchronized (mSatellitePhoneLock) { + if (this.mSatellitePhone != null) { + updateLastNotifiedNtnEligibilityAndNotify(state); + } + } + } + return true; + } + + /** + * This method check for the key KEY_SATELLITE_MAX_DATAGRAM_SIZE in carrier config. If + * available it fetches the value and override the same in SatelliteCapabilities. Otherwise it + * uses the value in the existed mSatelliteCapabilities. + */ + private void overrideSatelliteCapabilitiesIfApplicable() { + int subId = getSelectedSatelliteSubId(); + PersistableBundle config = getPersistableBundle(subId); + if (config.containsKey(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT)) { + int datagramSize = config.getInt(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE_BYTES_INT); + SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(subId); + if (!(subInfo == null || subInfo.isOnlyNonTerrestrialNetwork())) { + synchronized (mSatelliteCapabilitiesLock) { + this.mSatelliteCapabilities.setMaxBytesPerOutgoingDatagram(datagramSize); + } + } + } + } + + private int getNtnOnlySubscriptionId() { + List<SubscriptionInfo> infoList = mSubscriptionManagerService.getAllSubInfoList( + mContext.getOpPackageName(), mContext.getAttributionTag()); + int subId = infoList.stream() + .filter(info -> info.isOnlyNonTerrestrialNetwork()) + .mapToInt(SubscriptionInfo::getSubscriptionId) + .findFirst() + .orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID); + logd("getNtnOnlySubscriptionId: subId=" + subId); + return subId; + } + + @Nullable + private List<SatelliteSubscriberInfo> getNtnOnlySatelliteSubscriberInfoList( + Consumer<Integer> result) { + SatelliteSubscriberInfo satelliteSubscriberInfo = getNtnOnlySatelliteSubscriberInfo(); + if (satelliteSubscriberInfo == null) { + result.accept(SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED); + return null; + } + List<SatelliteSubscriberInfo> satelliteSubscriberInfoList = new ArrayList<>(); + satelliteSubscriberInfoList.add(satelliteSubscriberInfo); + + return satelliteSubscriberInfoList; + } + + @Nullable private SatelliteSubscriberInfo getNtnOnlySatelliteSubscriberInfo() { + int ntnOnlySubId = getNtnOnlySubscriptionId(); + if (ntnOnlySubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + logw("getNtnOnlySatelliteSubscriberInfo: no ntn only subscription found"); + return null; + } + SubscriptionInfo subInfo = mSubscriptionManagerService.getSubscriptionInfo(ntnOnlySubId); + if (subInfo == null) { + logw("getNtnOnlySatelliteSubscriberInfo: no subscription info found for subId=" + + ntnOnlySubId); + return null; + } + return getSatelliteSubscriberInfo(subInfo); + } + + @Nullable private SatelliteSubscriberInfo getSatelliteSubscriberInfo( + @NonNull SubscriptionInfo subInfo) { + Pair<String, Integer> subscriberIdPair = getSubscriberIdAndType(subInfo); + String subscriberId = subscriberIdPair.first; + int carrierId = subInfo.getCarrierId(); + String apn = getConfigForSubId(subInfo.getSubscriptionId()) + .getString(KEY_SATELLITE_NIDD_APN_NAME_STRING, ""); + logd("getSatelliteSubscriberInfo: subInfo: " + subInfo + ", subscriberId:" + + subscriberId + " , carrierId=" + carrierId + " , apn=" + apn); + if (subscriberId.isEmpty()) { + logw("getSatelliteSubscriberInfo: not a satellite subscription."); + return null; + } + return new SatelliteSubscriberInfo.Builder().setSubscriberId(subscriberId) + .setCarrierId(carrierId).setNiddApn(apn) + .setSubId(subInfo.getSubscriptionId()) + .setSubscriberIdType(subscriberIdPair.second) + .build(); + } + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected void handleCarrierRoamingNtnAvailableServicesChanged(int subId) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("handleCarrierRoamingNtnAvailableServicesChanged: " + + "carrierRoamingNbIotNtn flag is disabled"); + return; + } + updateLastNotifiedNtnAvailableServicesAndNotify(subId); + evaluateCarrierRoamingNtnEligibilityChange(); + } + + private void updateLastNotifiedNtnAvailableServicesAndNotify(int subId) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("notifyNtnAvailableServices: carrierRoamingNbIotNtn flag is disabled"); + return; + } + Phone phone = SatelliteServiceUtils.getPhone(subId); + if (phone == null) { + plogd("notifyNtnAvailableServices: phone is null."); + return; + } + plogd("updateLastNotifiedNtnAvailableServicesAndNotify: phoneId= " + phone.getPhoneId()); + int[] services = getSupportedServicesOnCarrierRoamingNtn(subId); + phone.notifyCarrierRoamingNtnAvailableServicesChanged(services); + } + + /** Return services that are supported on carrier roaming non-terrestrial network. */ + public int[] getSupportedServicesOnCarrierRoamingNtn(int subId) { + if (isSatelliteSupportedViaCarrier(subId)) { + // TODO: b/377367448 Cleanup get supported satellite services to align with starlink. + int[] services = getSupportedSatelliteServicesForCarrier(subId); + if (isP2PSmsDisallowedOnCarrierRoamingNtn(subId)) { + services = Arrays.stream(services).filter( + value -> value != NetworkRegistrationInfo.SERVICE_TYPE_SMS).toArray(); + } + return services; + } + return new int[0]; + } + + /** + * Whether the P2P SMS over carrier roaming satellite is disallowed or not. + * + * @param subId Associated subscription ID + * return {@code true} when the phone does not support P2P SMS over carrier roaming satellite + * {@code false} otherwise + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public boolean isP2PSmsDisallowedOnCarrierRoamingNtn(int subId) { + int carrierRoamingNtnConnectType = getCarrierRoamingNtnConnectType(subId); + if (carrierRoamingNtnConnectType == CARRIER_ROAMING_NTN_CONNECT_MANUAL) { + // Manual Connected + plogd("isP2PSmsDisallowedOnCarrierRoamingNtn: manual connect"); + if (!isNtnSmsSupportedByMessagesApp() + || !isApplicationSupportsP2P(mSatelliteGatewayServicePackageName)) { + plogd("isP2PSmsDisallowedOnCarrierRoamingNtn: APKs do not supports P2P"); + return true; + } + } + plogd("isP2PSmsDisallowedOnCarrierRoamingNtn: P2P is supported"); + return false; + } + + @NonNull + private int[] getSupportedSatelliteServicesForCarrier(int subId) { + PersistableBundle config = getPersistableBundle(subId); + int[] availableServices = config.getIntArray( + KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY); + if (availableServices == null) { + logd("getSupportedSatelliteServicesForCarrier: defaultCapabilities is null"); + return new int[0]; + } + logd("getSupportedSatelliteServicesForCarrier: subId=" + subId + + ", return default values " + Arrays.toString(availableServices)); + return availableServices; + } + + /** + * Whether application supports the P2P SMS to connect to carrier roaming non-terrestrial + * network. + * + * @param packageName application's default package name + * return {@code true} when the application supports P2P SMS over the roaming satellite + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public boolean isApplicationSupportsP2P(String packageName) { + PackageManager pm = mContext.getPackageManager(); + ApplicationInfo applicationInfo = new ApplicationInfo(); + try { + applicationInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + } catch (PackageManager.NameNotFoundException e) { + logd("isApplicationSupportsP2P pkgName: " + packageName + " is not installed."); + return false; + } + if (applicationInfo == null || applicationInfo.metaData == null) { + logd("isApplicationSupportsP2P pkgName: " + packageName + " meta-data info is empty."); + return false; + } + return applicationInfo.metaData.getBoolean( + SatelliteManager.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT); + } + + /** + * Registers for the applications state changed. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public void registerApplicationStateChanged() { + mDefaultSmsPackageName = Telephony.Sms.getDefaultSmsPackage(mContext); + mSatelliteGatewayServicePackageName = getConfigSatelliteGatewayServicePackage(); + + IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED); + packageFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); + packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); + packageFilter.addDataScheme("package"); + mContext.registerReceiver(mPackageStateChangedReceiver, packageFilter, + mContext.RECEIVER_EXPORTED); + } + + + private void notifyEnabledStateChanged(boolean isEnabled) { + TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class); + if (trm == null) { + loge("Telephony registry service is down!"); + return; + } + + trm.notifySatelliteStateChanged(isEnabled); + logd("notifyEnabledStateChanged to " + isEnabled); + } + + private NtnSignalStrength getCarrierRoamingNtnSignalStrength(@NonNull Phone phone) { + NtnSignalStrength carrierRoamingNtnSignalStrength = new NtnSignalStrength( + NTN_SIGNAL_STRENGTH_NONE); + + if (isInCarrierRoamingNbIotNtn(phone)) { + if (isInConnectedState()) { + synchronized (mNtnSignalsStrengthLock) { + carrierRoamingNtnSignalStrength = mNtnSignalStrength; + } + plogd("getCarrierRoamingNtnSignalStrength[phoneId=" + phone.getPhoneId() + + "]: in carrier roaming nb iot ntn mode."); + } + } else if (isInSatelliteModeForCarrierRoaming(phone)) { + ServiceState serviceState = phone.getServiceState(); + if (serviceState.getState() != ServiceState.STATE_OUT_OF_SERVICE) { + carrierRoamingNtnSignalStrength = new NtnSignalStrength( + phone.getSignalStrength().getLevel()); + plogd("getCarrierRoamingNtnSignalStrength[phoneId=" + phone.getPhoneId() + + "]: is in satellite mode for carrier roaming."); + } + } + + return carrierRoamingNtnSignalStrength; + } + + private boolean isInConnectedState() { + synchronized (mSatelliteModemStateLock) { + switch (mSatelliteModemState) { + case SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED: + case SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING: + plogd("isInConnectedState: return true"); + return true; + default: + plogd("isInConnectedState: return false"); + return false; + } + } + } + + protected void updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify( + @Nullable Phone phone) { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) return; + if (phone == null) { + return; + } + + NtnSignalStrength currSignalStrength = getCarrierRoamingNtnSignalStrength(phone); + int subId = phone.getSubId(); + synchronized (mSatelliteConnectedLock) { + NtnSignalStrength lastNotifiedSignalStrength = + mLastNotifiedCarrierRoamingNtnSignalStrength.get(subId); + if (lastNotifiedSignalStrength == null + || lastNotifiedSignalStrength.getLevel() != currSignalStrength.getLevel()) { + mLastNotifiedCarrierRoamingNtnSignalStrength.put(subId, currSignalStrength); + phone.notifyCarrierRoamingNtnSignalStrengthChanged(currSignalStrength); + } + } + } + + /** Returns whether to send SMS to DatagramDispatcher or not. */ + public boolean shouldSendSmsToDatagramDispatcher(@Nullable Phone phone) { + final long identity = Binder.clearCallingIdentity(); + try { + if (!isInCarrierRoamingNbIotNtn(phone)) { + return false; + } + + if (isDemoModeEnabled()) { + return false; + } + + int[] services = getSupportedServicesOnCarrierRoamingNtn(phone.getSubId()); + return ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** Returns whether to drop SMS or not. */ + public boolean shouldDropSms(@Nullable Phone phone) { + final long identity = Binder.clearCallingIdentity(); + try { + if (!isInCarrierRoamingNbIotNtn(phone)) { + return false; + } + + int[] services = getSupportedServicesOnCarrierRoamingNtn(phone.getSubId()); + return !ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private boolean isWaitingForSatelliteModemOff() { + synchronized (mSatelliteEnabledRequestLock) { + return mWaitingForSatelliteModemOff; + } + } + + @Nullable + private Boolean getIsSatelliteSupported() { + synchronized (mIsSatelliteSupportedLock) { + return mIsSatelliteSupported; + } + } + + private boolean isWaitingForDisableSatelliteModemResponse() { + synchronized (mSatelliteEnabledRequestLock) { + return mWaitingForDisableSatelliteModemResponse; + } + } + + @Nullable + private Boolean getIsSatelliteEnabled() { + synchronized (mIsSatelliteEnabledLock) { + return mIsSatelliteEnabled; + } + } + + @Nullable + private RequestSatelliteEnabledArgument getSatelliteDisabledRequest() { + synchronized (mSatelliteEnabledRequestLock) { + return mSatelliteDisabledRequest; + } + } + + private SatelliteCapabilities getSatelliteCapabilities() { + synchronized (mSatelliteCapabilitiesLock) { + return mSatelliteCapabilities; + } + } + + private void setBTEnabledState(boolean enabled) { + synchronized (mRadioStateLock) { + mBTStateEnabled = enabled; + } + } + + private boolean getBTEnabledState() { + synchronized (mRadioStateLock) { + return mBTStateEnabled; + } + } + + private void setNfcEnabledState(boolean enabled) { + synchronized (mRadioStateLock) { + mNfcStateEnabled = enabled; + } + } + + private boolean getNfcEnabledState() { + synchronized (mRadioStateLock) { + return mNfcStateEnabled; + } + } + + private void setUwbEnabledState(boolean enabled) { + synchronized (mRadioStateLock) { + mUwbStateEnabled = enabled; + } + } + + private boolean getUwbEnabledState() { + synchronized (mRadioStateLock) { + return mUwbStateEnabled; + } + } + + private void setWifiEnabledState(boolean enabled) { + synchronized (mRadioStateLock) { + mWifiStateEnabled = enabled; + } + } + + private boolean getWifiEnabledState() { + synchronized (mRadioStateLock) { + return mWifiStateEnabled; + } + } + + /** + * Method to return the current data plan for the registered plmn based on entitlement + * provisioning information. Note: If no information at + * provisioning is supported this is overridden with operator carrier config information. + * + * @param subId current subscription id + * @param plmn current registered plmn information + * + * @return Data supported modes {@link SatelliteController#SATELLITE_DATA_PLAN_METERED} + */ + public int getSatelliteDataPlanForPlmn(int subId, String plmn) { + if (plmn != null) { + synchronized (mSupportedSatelliteServicesLock) { + Map<String, Integer> dataplanMap = mEntitlementDataPlanMapPerCarrier.get(subId); + logd("data plan available for sub id:" + dataplanMap); + if (dataplanMap != null && dataplanMap.containsKey(plmn)) { + return dataplanMap.get(plmn); + } + } + } + // TODO (Override with carrier config value when configuration defined) + return SATELLITE_DATA_PLAN_METERED; + } + + /** + * Method to return the current satellite data service policy supported mode for the registered + * plmn based on entitlement provisioning information. Note: If no information at + * provisioning is supported this is overridden with operator carrier config information. + * + * @param subId current subscription id + * @param plmn current registered plmn information + * + * @return Supported modes {@link CarrierConfigManager.SATELLITE_DATA_SUPPORT_MODE} + */ + public int getSatelliteDataServicePolicyForPlmn(int subId, String plmn) { + if (plmn != null) { + synchronized (mSupportedSatelliteServicesLock) { + Map<String, Integer> dataServicePolicy = + mEntitlementDataServicePolicyMapPerCarrier.get( + subId); + logd("data policy available for sub id:" + dataServicePolicy); + if (dataServicePolicy != null && dataServicePolicy.containsKey(plmn)) { + return dataServicePolicy.get(plmn); + } + } + } + return getCarrierSatelliteDataSupportedMode(subId); + } + + /** + * Method to return the current satellite voice service policy supported mode for the registered + * plmn based on entitlement provisioning information. Note: If no information at + * provisioning is supported this is overridden with operator carrier config information. + * + * @param subId current subscription id + * @param plmn current registered plmn information + * + * @return Supported modes {@link CarrierConfigManager.SATELLITE_DATA_SUPPORT_MODE} + */ + public int getSatelliteVoiceServicePolicyForPlmn(int subId, String plmn) { + if (plmn != null) { + synchronized (mSupportedSatelliteServicesLock) { + Map<String, Integer> voiceServicePolicy = + mEntitlementVoiceServicePolicyMapPerCarrier.get( + subId); + logd("voice policy available for sub id:" + voiceServicePolicy); + if (voiceServicePolicy != null && voiceServicePolicy.containsKey(plmn)) { + return voiceServicePolicy.get(plmn); + } + } + } + // TODO (Replace below code with related enum value, when voice service policy support mode + // is added) + return 0; // Restricted + } }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java index a19f802..5b032e6 100644 --- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java +++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -41,6 +41,7 @@ import android.telephony.satellite.SatelliteManager; import android.telephony.satellite.SatelliteManager.SatelliteException; import android.telephony.satellite.SatelliteModemEnableRequestAttributes; +import android.telephony.satellite.SystemSelectionSpecifier; import android.telephony.satellite.stub.INtnSignalStrengthConsumer; import android.telephony.satellite.stub.ISatellite; import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer; @@ -104,6 +105,8 @@ new RegistrantList(); @NonNull private final RegistrantList mSatelliteRegistrationFailureRegistrants = new RegistrantList(); + @NonNull private final RegistrantList mTerrestrialNetworkAvailableChangedRegistrants = + new RegistrantList(); private class SatelliteListener extends ISatelliteListener.Stub { @@ -192,6 +195,11 @@ mSatelliteRegistrationFailureRegistrants.notifyResult(causeCode); } + @Override + public void onTerrestrialNetworkAvailableChanged(boolean isAvailable) { + mTerrestrialNetworkAvailableChangedRegistrants.notifyResult(isAvailable); + } + private boolean notifyResultIfExpectedListener() { // Demo listener should notify results only during demo mode // Vendor listener should notify result only during real mode @@ -587,6 +595,27 @@ } /** + * Registers for the terrestrial network available changed. + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + public void registerForTerrestrialNetworkAvailableChanged( + @NonNull Handler h, int what, @Nullable Object obj) { + mTerrestrialNetworkAvailableChangedRegistrants.add(h, what, obj); + } + + /** + * Unregisters for the terrestrial network available changed. + * + * @param h Handler to be removed from the registrant list. + */ + public void unregisterForTerrestrialNetworkAvailableChanged(@NonNull Handler h) { + mTerrestrialNetworkAvailableChangedRegistrants.remove(h); + } + + /** * Request to enable or disable the satellite service listening mode. * Listening mode allows the satellite service to listen for incoming pages. * @@ -652,14 +681,14 @@ }; if (mSatelliteController.isDemoModeEnabled()) { - mDemoSimulator.enableCellularModemWhileSatelliteModeIsOn( + mDemoSimulator.enableTerrestrialNetworkScanWhileSatelliteModeIsOn( enabled, errorCallback); } else { - mSatelliteService.enableCellularModemWhileSatelliteModeIsOn( + mSatelliteService.enableTerrestrialNetworkScanWhileSatelliteModeIsOn( enabled, errorCallback); } } catch (RemoteException e) { - ploge("enableCellularModemWhileSatelliteModeIsOn: RemoteException " + e); + ploge("enableTerrestrialNetworkScanWhileSatelliteModeIsOn: RemoteException " + e); if (message != null) { sendMessageWithResult( message, null, SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR); @@ -1355,6 +1384,42 @@ mExponentialBackoff.start(); } + /** + * Request to update system selection channels + * + * @param systemSelectionSpecifiers system selection specifiers + * @param message The Message to send to result of the operation to. + */ + public void updateSystemSelectionChannels( + @NonNull List<SystemSelectionSpecifier> systemSelectionSpecifiers, + @Nullable Message message) { + plogd("updateSystemSelectionChannels: SystemSelectionSpecifier: " + + systemSelectionSpecifiers.toString()); + if (mSatelliteService != null) { + try { + mSatelliteService.updateSystemSelectionChannels(SatelliteServiceUtils + .toSystemSelectionSpecifier(systemSelectionSpecifiers), + new IIntegerConsumer.Stub() { + @Override + public void accept(int result) { + int error = SatelliteServiceUtils.fromSatelliteError(result); + plogd("updateSystemSelectionChannels: " + error); + Binder.withCleanCallingIdentity(() -> + sendMessageWithResult(message, null, error)); + } + }); + } catch (RemoteException e) { + ploge("updateSystemSelectionChannels: RemoteException " + e); + sendMessageWithResult(message, null, + SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR); + } + } else { + ploge("updateSystemSelectionChannels: Satellite service is unavailable."); + sendMessageWithResult(message, null, + SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE); + } + } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) protected static void sendMessageWithResult(@NonNull Message message, @Nullable Object result, @SatelliteManager.SatelliteResult int error) {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteNetworkInfo.java b/src/java/com/android/internal/telephony/satellite/SatelliteNetworkInfo.java index 7db9195..f101f18 100644 --- a/src/java/com/android/internal/telephony/satellite/SatelliteNetworkInfo.java +++ b/src/java/com/android/internal/telephony/satellite/SatelliteNetworkInfo.java
@@ -16,6 +16,9 @@ package com.android.internal.telephony.satellite; +import java.util.HashMap; +import java.util.Map; + /** * Data class of the satellite configuration received from the entitlement server. */ @@ -28,9 +31,21 @@ * 2. "metered" * 3. empty string. */ public String mDataPlanType; + /** Stored the Allowed Services Info. with key as service type and value as service + * policy for the plmn + * Possible Service Type values: "data" and "voice". + * Possible Service Policy values: "constrained" and "unconstrained". + */ + public Map<String,String> mAllowedServicesInfo; - public SatelliteNetworkInfo(String plmn, String dataPlanType) { + public SatelliteNetworkInfo(String plmn, String dataPlanType, + Map<String,String> allowedServicesInfo) { mPlmn = plmn; mDataPlanType = dataPlanType; + if (allowedServicesInfo != null) { + mAllowedServicesInfo = new HashMap<>(allowedServicesInfo); + } else { + mAllowedServicesInfo = new HashMap<>(); + } } }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java index cca973f..e924878 100644 --- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java +++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -24,6 +24,9 @@ import static android.telephony.TelephonyManager.EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT; import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS; import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911; +import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED; +import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED; +import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP; import static com.android.internal.telephony.flags.Flags.satellitePersistentLogging; import static com.android.internal.telephony.satellite.SatelliteController.INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE; @@ -73,6 +76,7 @@ import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.metrics.SatelliteStats; +import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -262,6 +266,27 @@ return SmsApplication.getDefaultSendToApplication(mContext, false); } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected boolean updateAndGetProvisionState() { + mSatelliteController.updateSatelliteProvisionedStatePerSubscriberId(); + return isDeviceProvisioned(); + } + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected boolean isSatelliteAllowedByReasons() { + SatelliteManager satelliteManager = mContext.getSystemService(SatelliteManager.class); + int[] disallowedReasons = satelliteManager.getSatelliteDisallowedReasons(); + if (Arrays.stream(disallowedReasons).anyMatch(r -> + (r == SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP + || r == SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED + || r == SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED))) { + plogd("isAllowedForDefaultMessageApp:false, disallowedReasons=" + + Arrays.toString(disallowedReasons)); + return false; + } + return true; + } + private void handleEmergencyCallStartedEvent(@NonNull Connection connection) { plogd("handleEmergencyCallStartedEvent: connection=" + connection); mSatelliteController.setLastEmergencyCallTime(); @@ -284,8 +309,9 @@ } private void handleSatelliteProvisionStateChangedEvent(boolean provisioned) { - if (!provisioned) { - cleanUpResources(); + if (!provisioned + && !isSatelliteConnectedViaCarrierWithinHysteresisTime()) { + cleanUpResources(false); } } @@ -310,6 +336,8 @@ return; } + updateAndGetProvisionState(); + /* * The device might be connected to satellite after the emergency call started. Thus, we * need to do this check again so that we will have higher chance of sending the event @@ -321,7 +349,7 @@ boolean isCellularAvailable = SatelliteServiceUtils.isCellularAvailable(); if (!isCellularAvailable && isSatelliteAllowed() - && (isSatelliteViaOemAvailable() + && ((isDeviceProvisioned() && isSatelliteAllowedByReasons()) || isSatelliteConnectedViaCarrierWithinHysteresisTime()) && shouldTrackCall(mEmergencyConnection.getState())) { plogd("handleTimeoutEvent: Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer"); @@ -336,8 +364,7 @@ + ", isCellularAvailable=" + isCellularAvailable + ", isSatelliteAllowed=" + isSatelliteAllowed() + ", shouldTrackCall=" + shouldTrackCall(mEmergencyConnection.getState())); - reportESosRecommenderDecision(isDialerNotified); - cleanUpResources(); + cleanUpResources(isDialerNotified); } } @@ -360,8 +387,8 @@ * @return {@code true} if satellite is provisioned via OEM else return {@code false} */ @VisibleForTesting - public boolean isSatelliteViaOemAvailable() { - Boolean satelliteProvisioned = mSatelliteController.isSatelliteViaOemProvisioned(); + public boolean isDeviceProvisioned() { + Boolean satelliteProvisioned = mSatelliteController.isDeviceProvisioned(); return satelliteProvisioned != null ? satelliteProvisioned : false; } @@ -388,13 +415,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 +431,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) @@ -416,11 +443,12 @@ .setRecommendingHandoverType(getEmergencyCallToSatelliteHandoverType()) .setIsSatelliteAllowedInCurrentLocation(isSatelliteAllowed()) .setIsWifiConnected(mCountryDetector.isWifiNetworkConnected()) - .setCarrierId(getAvailableNtnCarrierID()).build()); + .setCarrierId(mSatelliteController.getSatelliteCarrierId()).build()); } - private void cleanUpResources() { + private void cleanUpResources(boolean isDialerNotified) { plogd("cleanUpResources"); + reportESosRecommenderDecision(isDialerNotified); synchronized (mLock) { stopTimer(); if (mEmergencyConnection != null) { @@ -645,7 +673,7 @@ @NonNull private Bundle createExtraBundleForEventDisplayEmergencyMessage( boolean isTestEmergencyNumber) { - int handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS; + int handoverType = getEmergencyCallToSatelliteHandoverType(); Pair<String, String> oemSatelliteMessagingApp = getOemEnabledSatelliteHandoverAppFromOverlayConfig(mContext); String packageName = oemSatelliteMessagingApp.first; @@ -653,10 +681,8 @@ String action = getSatelliteEmergencyHandoverIntentActionFromOverlayConfig(mContext, isTestEmergencyNumber); - if (isSatelliteConnectedViaCarrierWithinHysteresisTime() - || isEmergencyCallToSatelliteHandoverTypeT911Enforced()) { + if (handoverType == EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911) { ComponentName defaultSmsAppComponent = getDefaultSmsApp(); - handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911; packageName = defaultSmsAppComponent.getPackageName(); className = defaultSmsAppComponent.getClassName(); } @@ -737,14 +763,11 @@ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public int getEmergencyCallToSatelliteHandoverType() { - if (Flags.carrierRoamingNbIotNtn() && isSatelliteViaOemAvailable() + if (Flags.carrierRoamingNbIotNtn() + && isDeviceProvisioned() + && isSatelliteAllowedByReasons() && isSatelliteConnectedViaCarrierWithinHysteresisTime()) { - Phone satellitePhone = mSatelliteController.getSatellitePhone(); - if (satellitePhone == null) { - ploge("getEmergencyCallToSatelliteHandoverType: satellitePhone is null"); - return EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911; - } - int satelliteSubId = satellitePhone.getSubId(); + int satelliteSubId = mSatelliteController.getSelectedSatelliteSubId(); return mSatelliteController.getCarrierRoamingNtnEmergencyCallToSatelliteHandoverType( satelliteSubId); } else if (isSatelliteConnectedViaCarrierWithinHysteresisTime()) { @@ -804,7 +827,7 @@ } private boolean isSatelliteViaOemProvisioned() { - Boolean provisioned = mSatelliteController.isSatelliteViaOemProvisioned(); + Boolean provisioned = mSatelliteController.isDeviceProvisioned(); return (provisioned != null) && provisioned; } @@ -833,23 +856,6 @@ } } - /** Returns the carrier ID of NTN subscription */ - private int getAvailableNtnCarrierID() { - Pair<Boolean, Integer> ntnSubInfo = - mSatelliteController.isUsingNonTerrestrialNetworkViaCarrier(); - if (ntnSubInfo.first) { - TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); - return tm.createForSubscriptionId(ntnSubInfo.second).getSimCarrierId(); - } - - Phone satellitePhone = mSatelliteController.getSatellitePhone(); - if (satellitePhone != null) { - return satellitePhone.getCarrierId(); - } - - return TelephonyManager.UNKNOWN_CARRIER_ID; - } - private void plogd(@NonNull String log) { Rlog.d(TAG, log); if (mPersistentLogger != null) {
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java index 3936a7e..b55c622 100644 --- a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java +++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -36,13 +36,16 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.satellite.AntennaPosition; +import android.telephony.satellite.EarfcnRange; import android.telephony.satellite.NtnSignalStrength; import android.telephony.satellite.PointingInfo; import android.telephony.satellite.SatelliteCapabilities; import android.telephony.satellite.SatelliteDatagram; +import android.telephony.satellite.SatelliteInfo; import android.telephony.satellite.SatelliteManager; import android.telephony.satellite.SatelliteModemEnableRequestAttributes; import android.telephony.satellite.SatelliteSubscriptionInfo; +import android.telephony.satellite.SystemSelectionSpecifier; import android.telephony.satellite.stub.NTRadioTechnology; import android.telephony.satellite.stub.SatelliteModemState; import android.telephony.satellite.stub.SatelliteResult; @@ -542,6 +545,100 @@ return mcc + mnc; } + @NonNull + private static android.telephony.satellite.stub + .SystemSelectionSpecifier convertSystemSelectionSpecifierToHALFormat( + @NonNull SystemSelectionSpecifier systemSelectionSpecifier) { + android.telephony.satellite.stub.SystemSelectionSpecifier convertedSpecifier = + new android.telephony.satellite.stub.SystemSelectionSpecifier(); + + convertedSpecifier.mMccMnc = systemSelectionSpecifier.getMccMnc(); + convertedSpecifier.mBands = systemSelectionSpecifier.getBands(); + convertedSpecifier.mEarfcs = systemSelectionSpecifier.getEarfcns(); + SatelliteInfo[] satelliteInfos = systemSelectionSpecifier.getSatelliteInfos() + .toArray(new SatelliteInfo[0]); + android.telephony.satellite.stub.SatelliteInfo[] halSatelliteInfos = + new android.telephony.satellite.stub.SatelliteInfo[satelliteInfos.length]; + for (int i = 0; i < satelliteInfos.length; i++) { + halSatelliteInfos[i] = new android.telephony.satellite.stub.SatelliteInfo(); + + halSatelliteInfos[i].id = new android.telephony.satellite.stub.UUID(); + halSatelliteInfos[i].id.mostSigBits = + satelliteInfos[i].getSatelliteId().getMostSignificantBits(); + halSatelliteInfos[i].id.leastSigBits = + satelliteInfos[i].getSatelliteId().getLeastSignificantBits(); + + halSatelliteInfos[i].position = + new android.telephony.satellite.stub.SatellitePosition(); + halSatelliteInfos[i].position.longitudeDegree = + satelliteInfos[i].getSatellitePosition().getLongitudeDegrees(); + halSatelliteInfos[i].position.altitudeKm = + satelliteInfos[i].getSatellitePosition().getAltitudeKm(); + + halSatelliteInfos[i].bands = satelliteInfos[i].getBands().stream().mapToInt( + Integer::intValue).toArray(); + + List<EarfcnRange> earfcnRangeList = satelliteInfos[i].getEarfcnRanges(); + halSatelliteInfos[i].earfcnRanges = + new android.telephony.satellite.stub.EarfcnRange[earfcnRangeList.size()]; + for (int j = 0; j < earfcnRangeList.size(); j++) { + halSatelliteInfos[i].earfcnRanges[j] = + new android.telephony.satellite.stub.EarfcnRange(); + halSatelliteInfos[i].earfcnRanges[j].startEarfcn = earfcnRangeList.get( + j).getStartEarfcn(); + halSatelliteInfos[i].earfcnRanges[j].endEarfcn = earfcnRangeList.get( + j).getEndEarfcn(); + } + } + convertedSpecifier.satelliteInfos = halSatelliteInfos; + convertedSpecifier.tagIds = systemSelectionSpecifier.getTagIds(); + return convertedSpecifier; + } + + /** + * Convert SystemSelectionSpecifier from framework definition to service definition + * @param systemSelectionSpecifier The SystemSelectionSpecifier from the framework. + * @return The converted SystemSelectionSpecifier for the satellite service. + */ + @NonNull + public static List<android.telephony.satellite.stub + .SystemSelectionSpecifier> toSystemSelectionSpecifier( + @NonNull List<SystemSelectionSpecifier> systemSelectionSpecifier) { + return systemSelectionSpecifier.stream().map( + SatelliteServiceUtils::convertSystemSelectionSpecifierToHALFormat).collect( + Collectors.toList()); + } + + /** + * Expected format of the input dictionary bundle is: + * <ul> + * <li>Key: Regional satellite config Id string.</li> + * <li>Value: Integer arrays of earfcns in the corresponding regions."</li> + * </ul> + * @return The map of earfcns with key: regional satellite config Id, + * value: set of earfcns in the corresponding regions. + */ + @NonNull + public static Map<String, Set<Integer>> parseRegionalSatelliteEarfcns( + @Nullable PersistableBundle earfcnsBundle) { + Map<String, Set<Integer>> earfcnsMap = new HashMap<>(); + if (earfcnsBundle == null || earfcnsBundle.isEmpty()) { + logd("parseRegionalSatelliteEarfcns: earfcnsBundle is null or empty"); + return earfcnsMap; + } + + for (String configId : earfcnsBundle.keySet()) { + Set<Integer> earfcnsSet = new HashSet<>(); + for (int earfcn : earfcnsBundle.getIntArray(configId)) { + earfcnsSet.add(earfcn); + } + logd("parseRegionalSatelliteEarfcns: configId = " + configId + ", earfcns =" + + earfcnsSet.stream().map(String::valueOf).collect(joining(","))); + earfcnsMap.put(configId, earfcnsSet); + } + return earfcnsMap; + } + private static void logd(@NonNull String log) { Rlog.d(TAG, log); }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java index f4208f7..f2f4cac 100644 --- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java +++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -19,6 +19,8 @@ import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE; @@ -38,20 +40,26 @@ import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AlarmManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.AsyncResult; import android.os.Build; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; import android.os.RemoteException; +import android.os.SystemClock; import android.os.SystemProperties; +import android.os.WorkSource; import android.telephony.DropBoxManagerLoggerBackend; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PersistentLogger; +import android.telephony.ServiceState; import android.telephony.satellite.ISatelliteModemStateCallback; import android.telephony.satellite.SatelliteManager; import android.telephony.satellite.stub.ISatelliteGateway; @@ -60,14 +68,15 @@ import android.util.Log; import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.DeviceStateMonitor; 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.telephony.util.ArrayUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.telephony.Rlog; @@ -123,7 +132,10 @@ private static final int EVENT_SATELLITE_ENABLEMENT_FAILED = 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; + protected static final int EVENT_ESOS_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; + protected static final int EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT = 14; private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute @@ -160,6 +172,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; @@ -169,15 +182,20 @@ boolean mIsScreenOn = true; private boolean mIsDeviceAlignedWithSatellite = false; - @GuardedBy("mLock") - @NonNull private boolean mIsDisableCellularModemInProgress = false; @NonNull private final SatelliteController mSatelliteController; @NonNull private final DatagramController mDatagramController; @Nullable private PersistentLogger mPersistentLogger = null; @Nullable private DeviceStateMonitor mDeviceStateMonitor; @NonNull private SessionMetricsStats mSessionMetricsStats; - @NonNull private FeatureFlags mFeatureFlags; + @NonNull private AlarmManager mAlarmManager; + private final AlarmManager.OnAlarmListener mAlarmListener = new AlarmManager.OnAlarmListener() { + @Override + public void onAlarm() { + plogd("onAlarm: screen off timer expired"); + sendMessage(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT); + } + }; /** * @return The singleton instance of SatelliteSessionController. @@ -205,8 +223,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 +240,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,12 +301,13 @@ 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(); + mAlarmManager = mContext.getSystemService(AlarmManager.class); addState(mUnavailableState); addState(mPowerOffState); @@ -307,9 +332,10 @@ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public void onDatagramTransferStateChanged( @SatelliteManager.SatelliteDatagramTransferState int sendState, - @SatelliteManager.SatelliteDatagramTransferState int receiveState) { + @SatelliteManager.SatelliteDatagramTransferState int receiveState, + @SatelliteManager.DatagramType int datagramType) { sendMessage(EVENT_DATAGRAM_TRANSFER_STATE_CHANGED, - new DatagramTransferState(sendState, receiveState)); + new DatagramTransferState(sendState, receiveState, datagramType)); if (sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING) { mIsSendingTriggeredDuringTransferringState.set(true); } @@ -450,6 +476,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. @@ -511,10 +554,12 @@ mIsDeviceAlignedWithSatellite = isAligned; if (mIsDeviceAlignedWithSatellite) { - stopCarrierRoamingNbIotInactivityTimer(); + stopEsosInactivityTimer(); + stopP2pSmsInactivityTimer(); } else { if (mCurrentState == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) { - evaluateStartingCarrierRoamingNbIotInactivityTimer(); + evaluateStartingEsosInactivityTimer(); + evaluateStartingP2pSmsInactivityTimer(); } } } @@ -547,9 +592,46 @@ plogd("cleanUpResource"); mIsDeviceAlignedWithSatellite = false; unregisterForScreenStateChanged(); + if (mAlarmManager != null) { + mAlarmManager.cancel(mAlarmListener); + } + + 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)); + } + + /** + * Uses this function to set AlarmManager object for testing. + * + * @param alarmManager The instance of AlarmManager. + */ + @VisibleForTesting + public void setAlarmManager(AlarmManager alarmManager) { + mAlarmManager = alarmManager; + } + private boolean isDemoMode() { return mIsDemoMode; } @@ -557,11 +639,14 @@ private static class DatagramTransferState { @SatelliteManager.SatelliteDatagramTransferState public int sendState; @SatelliteManager.SatelliteDatagramTransferState public int receiveState; + @SatelliteManager.DatagramType public int datagramType; DatagramTransferState(@SatelliteManager.SatelliteDatagramTransferState int sendState, - @SatelliteManager.SatelliteDatagramTransferState int receiveState) { + @SatelliteManager.SatelliteDatagramTransferState int receiveState, + @SatelliteManager.DatagramType int datagramType) { this.sendState = sendState; this.receiveState = receiveState; + this.datagramType = datagramType; } } @@ -591,9 +676,6 @@ mPreviousState = mCurrentState; mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_OFF; mIsSendingTriggeredDuringTransferringState.set(false); - synchronized (mLock) { - mIsDisableCellularModemInProgress = false; - } unbindService(); stopNbIotInactivityTimer(); DemoSimulator.getInstance().onSatelliteModeOff(); @@ -790,9 +872,25 @@ mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_IDLE; mIsSendingTriggeredDuringTransferringState.set(false); stopNbIotInactivityTimer(); + //Enable Cellular Modem scanning - mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, null); - notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + boolean configSatelliteAllowTnScanningDuringSatelliteSession = + mContext.getResources().getBoolean( + R.bool.config_satellite_allow_tn_scanning_during_satellite_session); + if (configSatelliteAllowTnScanningDuringSatelliteSession) { + Message onCompleted = + obtainMessage(EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE); + mSatelliteModemInterface + .enableCellularModemWhileSatelliteModeIsOn(true, onCompleted); + } else { + plogd("Device does not allow cellular modem scanning"); + } + if (isConcurrentTnScanningSupported() + || !configSatelliteAllowTnScanningDuringSatelliteSession) { + plogd("IDLE state is hidden from clients"); + } else { + notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + } } @Override @@ -807,7 +905,7 @@ break; case EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE: handleEventDisableCellularModemWhileSatelliteModeIsOnDone( - (AsyncResult) msg.obj); + (AsyncResult) msg.obj); break; case EVENT_SATELLITE_ENABLEMENT_STARTED: handleSatelliteEnablementStarted((boolean) msg.obj); @@ -821,6 +919,22 @@ case EVENT_SATELLITE_MODEM_STATE_CHANGED: handleSatelliteModemStateChanged(msg); break; + case EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE: + plogd("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; @@ -831,68 +945,86 @@ if ((datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING) || (datagramTransferState.receiveState == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING)) { - if (mSatelliteController.isSatelliteAttachRequired()) { - ploge("Unexpected transferring state received for NB-IOT NTN"); - } else { - transitionTo(mTransferringState); - } + transitionTo(mTransferringState); } else if ((datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT) || (datagramTransferState.receiveState == SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT)) { if (mSatelliteController.isSatelliteAttachRequired()) { - disableCellularModemWhileSatelliteModeIsOn(); + transitionTo(mNotConnectedState); } else { ploge("Unexpected transferring state received for non-NB-IOT NTN"); } } } - private void handleEventDisableCellularModemWhileSatelliteModeIsOnDone( - @NonNull AsyncResult result) { - synchronized (mLock) { - if (mIsDisableCellularModemInProgress) { - int error = SatelliteServiceUtils.getSatelliteError( - result, "DisableCellularModemWhileSatelliteModeIsOnDone"); - if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) { - transitionTo(mNotConnectedState); - } - mIsDisableCellularModemInProgress = false; - } else { - ploge("DisableCellularModemWhileSatelliteModeIsOn is not in progress"); + 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); + if (result == SatelliteManager.SATELLITE_RESULT_SUCCESS) { + mSessionMetricsStats.addCountOfAutoExitDueToTnNetwork(); + } + } + }); + } + + private void handleEventDisableCellularModemWhileSatelliteModeIsOnDone( + @NonNull AsyncResult result) { + int error = SatelliteServiceUtils.getSatelliteError( + result, "DisableCellularModemWhileSatelliteModeIsOnDone"); + plogd("Disable TN scanning done with result: " + error); } private void handleSatelliteModemStateChanged(@NonNull Message msg) { int state = msg.arg1; if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) { transitionTo(mPowerOffState); - } - } - - private void disableCellularModemWhileSatelliteModeIsOn() { - synchronized (mLock) { - if (mIsDisableCellularModemInProgress) { - plogd("Cellular scanning is already being disabled"); - return; + } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED + || state == SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED) { + if (isConcurrentTnScanningSupported()) { + plogd("Notifying the new state " + state + " to clients but still" + + " stay at IDLE state internally"); + notifyStateChangedEvent(state); + } else { + plogd("Ignoring the modem state " + state); } - - mIsDisableCellularModemInProgress = true; - Message onCompleted = - obtainMessage(EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE); - mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, - onCompleted); } } @Override public void exit() { if (DBG) plogd("Exiting IdleState"); - if (!mSatelliteController.isSatelliteAttachRequired()) { - // Disable cellular modem scanning - mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, null); - } + // Disable cellular modem scanning + mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, null); } } @@ -1053,14 +1185,16 @@ mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED; notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); startNbIotInactivityTimer(); - evaluateStartingCarrierRoamingNbIotInactivityTimer(); + evaluateStartingEsosInactivityTimer(); + evaluateStartingP2pSmsInactivityTimer(); } @Override public void exit() { if (DBG) plogd("Exiting NotConnectedState"); - stopCarrierRoamingNbIotInactivityTimer(); + stopEsosInactivityTimer(); + stopP2pSmsInactivityTimer(); } @Override @@ -1074,8 +1208,22 @@ case EVENT_SATELLITE_MODEM_STATE_CHANGED: handleEventSatelliteModemStateChanged(msg.arg1); break; - case EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT: - // fall through + case EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT: + if (isP2pSmsInActivityTimerStarted()) { + plogd("NotConnectedState: processing: P2P_SMS inactivity timer running " + + "can not move to IDLE"); + } else { + transitionTo(mIdleState); + } + break; + case EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT: + if (isEsosInActivityTimerStarted()) { + plogd("NotConnectedState: processing: ESOS inactivity timer running " + + "can not move to IDLE"); + } else { + transitionTo(mIdleState); + } + break; case EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT: transitionTo(mIdleState); break; @@ -1112,17 +1260,29 @@ || datagramTransferState.receiveState == SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT) { stopNbIotInactivityTimer(); - stopCarrierRoamingNbIotInactivityTimer(); + + if (mSatelliteController.getRequestIsEmergency()) { + stopEsosInactivityTimer(); + } + stopP2pSmsInactivityTimer(); } else if (datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE && datagramTransferState.receiveState == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE) { startNbIotInactivityTimer(); - evaluateStartingCarrierRoamingNbIotInactivityTimer(); + evaluateStartingEsosInactivityTimer(); + evaluateStartingP2pSmsInactivityTimer(); } else if (isSending(datagramTransferState.sendState) || isReceiving(datagramTransferState.receiveState)) { - restartNbIotInactivityTimer(); - stopCarrierRoamingNbIotInactivityTimer(); - evaluateStartingCarrierRoamingNbIotInactivityTimer(); + stopNbIotInactivityTimer(); + + int datagramType = datagramTransferState.datagramType; + if (datagramType == DATAGRAM_TYPE_SOS_MESSAGE) { + stopEsosInactivityTimer(); + } else if (datagramType == DATAGRAM_TYPE_SMS) { + stopP2pSmsInactivityTimer(); + } else { + plogd("datagram type is not SOS_Message and SMS " + datagramType); + } } } } @@ -1136,11 +1296,18 @@ mCurrentState = SATELLITE_MODEM_STATE_CONNECTED; notifyStateChangedEvent(SATELLITE_MODEM_STATE_CONNECTED); startNbIotInactivityTimer(); + evaluateStartingEsosInactivityTimer(); + evaluateStartingP2pSmsInactivityTimer(); + mSatelliteController.updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify( + mSatelliteController.getSatellitePhone()); } @Override public void exit() { if (DBG) plogd("Exiting ConnectedState"); + + stopEsosInactivityTimer(); + stopP2pSmsInactivityTimer(); } @Override @@ -1169,6 +1336,22 @@ case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT: handleEventScreenOffInactivityTimerTimedOut(); break; + case EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT: + if (isP2pSmsInActivityTimerStarted()) { + plogd("ConnectedState: processing: P2P_SMS inactivity timer running " + + "can not move to IDLE"); + } else { + transitionTo(mIdleState); + } + break; + case EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT: + if (isEsosInActivityTimerStarted()) { + plogd("ConnectedState: processing: ESOS inactivity timer running " + + "can not move to IDLE"); + } else { + transitionTo(mIdleState); + } + break; } // Ignore all unexpected events. return HANDLED; @@ -1229,8 +1412,17 @@ case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT: whatString = "EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT"; break; - case EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT: - whatString = "EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT"; + case EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT: + whatString = "EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT"; + break; + case EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT: + whatString = "EVENT_P2P_SMS_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; @@ -1247,12 +1439,7 @@ } private int getSubId() { - Phone phone = mSatelliteController.getSatellitePhone(); - if (phone == null) { - return SatelliteServiceUtils.getPhone().getSubId(); - } - - return phone.getSubId(); + return mSatelliteController.getSelectedSatelliteSubId(); } private void notifyStateChangedEvent(@SatelliteManager.SatelliteModemState int state) { @@ -1407,8 +1594,13 @@ return; } + if (!mSatelliteController.isInCarrierRoamingNbIotNtn()) { + logd("registerScreenOnOffChanged: device is not in CarrierRoamingNbIotNtn"); + return; + } + if (mSatelliteController.getRequestIsEmergency()) { - if (DBG) logd("registerScreenOnOffChanged: Emergency mode"); + logd("registerScreenOnOffChanged: not register, device is in Emergency mode"); // screen on/off timer is available in not emergency mode return; } @@ -1458,16 +1650,42 @@ } mIsScreenOn = screenOn; + if (mSatelliteController.getRequestIsEmergency()) { + if (DBG) logd("handleEventScreenStateChanged: Emergency mode"); + // This is for coexistence + // emergency mode can be set after registerForScreenStateChanged() called for P2P-sms + return; + } + + int subId = getSubId(); + if (!isP2pSmsSupportedOnCarrierRoamingNtn(subId)) { + if (DBG) plogd("handleEventScreenStateChanged: P2P_SMS is not supported"); + return; + } + if (!screenOn) { // Screen off, start timer int timeoutMillis = getScreenOffInactivityTimeoutDurationSec() * 1000; - sendMessageDelayed(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT, timeoutMillis); + if (mAlarmManager == null) { + plogd("handleEventScreenStateChanged: can not access AlarmManager to start timer"); + return; + } + + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + timeoutMillis, + TAG, new HandlerExecutor(getHandler()), new WorkSource(), mAlarmListener); plogd("handleEventScreenStateChanged: start timer " + timeoutMillis); } else { // Screen on, stop timer removeMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT); + if (mAlarmManager == null) { + plogd("handleEventScreenStateChanged: can not access AlarmManager to stop timer"); + return; + } + + mAlarmManager.cancel(mAlarmListener); plogd("handleEventScreenStateChanged: stop timer"); } } @@ -1479,13 +1697,6 @@ DEFAULT_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC); } - private int getP2pSmsInactivityTimeoutDurationSec() { - PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId()); - - return config.getInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, - DEFAULT_P2P_SMS_INACTIVITY_TIMEOUT_SEC); - } - private int getEsosInactivityTimeoutDurationSec() { PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId()); @@ -1493,62 +1704,128 @@ DEFAULT_ESOS_INACTIVITY_TIMEOUT_SEC); } - private void evaluateStartingCarrierRoamingNbIotInactivityTimer() { + private void evaluateStartingEsosInactivityTimer() { if (!mFeatureFlags.carrierRoamingNbIotNtn()) { - plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: " + plogd("evaluateStartingEsosInactivityTimer: " + "carrierRoamingNbIotNtn is disabled"); return; } + if (isEsosInActivityTimerStarted()) { + plogd("isEsosInActivityTimerStarted: " + + "ESOS inactivity timer already started"); + return; + } + int subId = getSubId(); - if (!mSatelliteController.isSatelliteRoamingP2pSmSSupported(subId) - && !mSatelliteController.isSatelliteEsosSupported(subId)) { - plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: " - + "device does not support P2P SMS and ESOS are disabled"); + if (!mSatelliteController.isSatelliteEsosSupported(subId)) { + plogd("evaluateStartingEsosInactivityTimer: ESOS is not supported"); + return; + } + + if (!mSatelliteController.getRequestIsEmergency()) { + plogd("evaluateStartingEsosInactivityTimer: request is not emergency"); return; } if (mIsDeviceAlignedWithSatellite) { - plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: " - + "can't start inactivity timer due to device aligned satellite"); + plogd("evaluateStartingEsosInactivityTimer: " + + "can't start ESOS inactivity timer due to device aligned satellite"); return; } - if (hasMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT)) { - plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: already started"); - return; - } - - int timeOutMillis; - if (mSatelliteController.getRequestIsEmergency()) { - timeOutMillis = getEsosInactivityTimeoutDurationSec() * 1000; - } else if (mSatelliteController.isInCarrierRoamingNbIotNtn()) { - timeOutMillis = getP2pSmsInactivityTimeoutDurationSec() * 1000; - } else { - plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: " - + "can't start inactivity timer device is in not P2P SMS and ESOS mode"); - return; - } - + int timeOutMillis = getEsosInactivityTimeoutDurationSec() * 1000; DatagramController datagramController = DatagramController.getInstance(); if (datagramController.isSendingInIdleState() && datagramController.isPollingInIdleState()) { - sendMessageDelayed(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT, - timeOutMillis); - plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: start inactivity timer " + sendMessageDelayed(EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT, timeOutMillis); + plogd("evaluateStartingEsosInactivityTimer: start ESOS inactivity timer " + timeOutMillis); } else { - plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: " - + "can't start inactivity timer"); + plogd("evaluateStartingEsosInactivityTimer: " + + "can't start ESOS inactivity timer"); } } - private void stopCarrierRoamingNbIotInactivityTimer() { - removeMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT); - plogd("stopCarrierRoamingNbIotInactivityTimer:"); + private void stopEsosInactivityTimer() { + if (isEsosInActivityTimerStarted()) { + removeMessages(EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT); + plogd("stopEsosInactivityTimer: ESOS inactivity timer stopped"); + } + } + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public boolean isEsosInActivityTimerStarted() { + return hasMessages(EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT); + } + + private int getP2pSmsInactivityTimeoutDurationSec() { + PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId()); + + return config.getInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, + DEFAULT_P2P_SMS_INACTIVITY_TIMEOUT_SEC); + } + + private void evaluateStartingP2pSmsInactivityTimer() { + if (!mFeatureFlags.carrierRoamingNbIotNtn()) { + plogd("evaluateStartingP2pSmsInactivityTimer: " + + "carrierRoamingNbIotNtn is disabled"); + return; + } + + if (isP2pSmsInActivityTimerStarted()) { + plogd("isP2pSmsInActivityTimerStarted: " + + "P2P_SMS inactivity timer already started"); + return; + } + + int subId = getSubId(); + if (!isP2pSmsSupportedOnCarrierRoamingNtn(subId)) { + if (DBG) plogd("evaluateStartingP2pSmsInactivityTimer: P2P_SMS is not supported"); + return; + } + + if (mIsDeviceAlignedWithSatellite) { + plogd("evaluateStartingP2pSmsInactivityTimer: " + + "can't start P2P_SMS inactivity timer due to device aligned satellite"); + return; + } + + int timeOutMillis = getP2pSmsInactivityTimeoutDurationSec() * 1000; + DatagramController datagramController = DatagramController.getInstance(); + if (datagramController.isSendingInIdleState() + && datagramController.isPollingInIdleState()) { + sendMessageDelayed(EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT, timeOutMillis); + plogd("evaluateStartingP2pSmsInactivityTimer: start P2P_SMS inactivity timer " + + timeOutMillis); + } else { + plogd("evaluateStartingP2pSmsInactivityTimer: " + + "can't start P2P_SMS inactivity timer"); + } + } + + private void stopP2pSmsInactivityTimer() { + if (isP2pSmsInActivityTimerStarted()) { + removeMessages(EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT); + plogd("stopP2pSmsInactivityTimer: P2P_SMS inactivity timer stopped"); + } + } + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public boolean isP2pSmsInActivityTimerStarted() { + return hasMessages(EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT); } private void handleEventScreenOffInactivityTimerTimedOut() { + if (mSatelliteController.getRequestIsEmergency()) { + loge("handleEventScreenOffInactivityTimerTimedOut: Emergency mode"); + /* This is for coexistence + * mIsEmergency can be set after + * EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT timer started + */ + return; + } + plogd("handleEventScreenOffInactivityTimerTimedOut: request disable satellite"); mSatelliteController.requestSatelliteEnabled( @@ -1562,7 +1839,6 @@ if (result == SATELLITE_RESULT_SUCCESS) { mSessionMetricsStats.addCountOfAutoExitDueToScreenOff(); } - // TODO(b/364738085): Add CountOfAutoExitDueToTnNetwork } }); } @@ -1645,6 +1921,25 @@ return true; } + private boolean isP2pSmsSupportedOnCarrierRoamingNtn(int subId) { + if (!mSatelliteController.isSatelliteRoamingP2pSmSSupported(subId)) { + if (DBG) plogd("isP2pSmsSupportedOnCarrierRoamingNtn: P2P_SMS is not supported"); + return false; + } + + int[] services = mSatelliteController.getSupportedServicesOnCarrierRoamingNtn(subId); + if (!ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS)) { + if (DBG) { + plogd("isP2pSmsSupportedOnCarrierRoamingNtn: P2P_SMS service is not supported " + + "on carrier roaming ntn."); + } + return false; + } + + if (DBG) plogd("isP2pSmsSupportedOnCarrierRoamingNtn: P2_SMS is supported"); + return true; + } + private boolean isSatellitePersistentLoggingEnabled( @NonNull Context context, @NonNull FeatureFlags featureFlags) { if (featureFlags.satellitePersistentLogging()) { @@ -1658,6 +1953,16 @@ } } + private boolean isConcurrentTnScanningSupported() { + try { + return mContext.getResources().getBoolean( + R.bool.config_satellite_modem_support_concurrent_tn_scanning); + } catch (RuntimeException e) { + plogd("isConcurrentTnScanningSupported: ex=" + e); + return false; + } + } + private void plogd(@NonNull String log) { logd(log); if (mPersistentLogger != null) {
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java index 771432e..3138b16 100644 --- a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java +++ b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java
@@ -325,13 +325,18 @@ return; } String simCountry = MccTable.countryCodeForMcc(subscriptionInfoInternal.getMcc()); - String satelliteRegisteredCountry = MccTable.countryCodeForMcc( - satelliteRegisteredPlmn.substring(0, 3)); - if (simCountry.equalsIgnoreCase(satelliteRegisteredCountry)) { - mIsNtnRoamingInHomeCountry = false; - } else { - // If device is connected to roaming non-terrestrial network, update to true. - mIsNtnRoamingInHomeCountry = true; + mIsNtnRoamingInHomeCountry = true; + if (satelliteRegisteredPlmn != null + && satelliteRegisteredPlmn.length() >= 3) { + String satelliteRegisteredCountry = MccTable.countryCodeForMcc( + satelliteRegisteredPlmn.substring(0, 3)); + if (simCountry.equalsIgnoreCase(satelliteRegisteredCountry)) { + mIsNtnRoamingInHomeCountry = true; + } else { + // If device is connected to roaming non-terrestrial network, then marking as + // roaming in external country + mIsNtnRoamingInHomeCountry = false; + } } logd("updateNtnRoamingInHomeCountry: mIsNtnRoamingInHomeCountry=" + mIsNtnRoamingInHomeCountry);
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java index ec135da..56c3431 100644 --- a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java +++ b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
@@ -141,14 +141,13 @@ builder.setCountOfDemoModeOutgoingDatagramSuccess(ADD_COUNT); } else { builder.setCountOfOutgoingDatagramSuccess(ADD_COUNT); - } - - if (SatelliteServiceUtils.isSosMessage(datagramType)) { - builder.setCountOfDatagramTypeSosSmsSuccess(ADD_COUNT); - } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) { - builder.setCountOfDatagramTypeLocationSharingSuccess(ADD_COUNT); - } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) { - builder.setCountOfDatagramTypeKeepAliveSuccess(ADD_COUNT).build(); + if (SatelliteServiceUtils.isSosMessage(datagramType)) { + builder.setCountOfDatagramTypeSosSmsSuccess(ADD_COUNT); + } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) { + builder.setCountOfDatagramTypeLocationSharingSuccess(ADD_COUNT); + } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) { + builder.setCountOfDatagramTypeKeepAliveSuccess(ADD_COUNT).build(); + } } SatelliteStats.SatelliteControllerParams controllerParam = builder.build(); @@ -166,14 +165,13 @@ builder.setCountOfDemoModeOutgoingDatagramFail(ADD_COUNT); } else { builder.setCountOfOutgoingDatagramFail(ADD_COUNT); - } - - if (SatelliteServiceUtils.isSosMessage(datagramType)) { - builder.setCountOfDatagramTypeSosSmsFail(ADD_COUNT); - } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) { - builder.setCountOfDatagramTypeLocationSharingFail(ADD_COUNT); - } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) { - builder.setCountOfDatagramTypeKeepAliveFail(ADD_COUNT); + if (SatelliteServiceUtils.isSosMessage(datagramType)) { + builder.setCountOfDatagramTypeSosSmsFail(ADD_COUNT); + } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) { + builder.setCountOfDatagramTypeLocationSharingFail(ADD_COUNT); + } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) { + builder.setCountOfDatagramTypeKeepAliveFail(ADD_COUNT); + } } SatelliteStats.SatelliteControllerParams controllerParam = builder.build();
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java index a234378..0a82b99 100644 --- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java +++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -18,6 +18,7 @@ import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE; +import static android.telephony.satellite.SatelliteManager.KEY_SESSION_STATS_V2; import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS; import android.annotation.NonNull; @@ -46,9 +47,13 @@ private long mTerminationProcessingTimeMillis; private int mSessionDurationSec; private int mCountOfSuccessfulOutgoingDatagram; + private int mShadowCountOfSuccessfulOutgoingDatagram; private int mCountOfFailedOutgoingDatagram; + private int mShadowCountOfFailedOutgoingDatagram; private int mCountOfTimedOutUserMessagesWaitingForConnection; + private int mShadowCountOfTimedOutUserMessagesWaitingForConnection; private int mCountOfTimedOutUserMessagesWaitingForAck; + private int mShadowCountOfTimedOutUserMessagesWaitingForAck; private int mCountOfSuccessfulIncomingDatagram; private int mCountOfIncomingDatagramFailed; private boolean mIsDemoMode; @@ -57,9 +62,11 @@ private int mCountOfSatelliteNotificationDisplayed; private int mCountOfAutoExitDueToScreenOff; private int mCountOfAutoExitDueToTnNetwork; + private SatelliteSessionStats datagramStats; private SessionMetricsStats() { initializeSessionMetricsParam(); + datagramStats = new SatelliteSessionStats(); } /** @@ -124,13 +131,16 @@ /** Increase the count of successful outgoing datagram transmission. */ public SessionMetricsStats addCountOfSuccessfulOutgoingDatagram( - @NonNull @SatelliteManager.DatagramType int datagramType) { + @NonNull @SatelliteManager.DatagramType int datagramType, + long datagramTransmissionTime) { + datagramStats.recordSuccessfulOutgoingDatagramStats(datagramType, datagramTransmissionTime); if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) { // Ignore KEEP_ALIVE messages return this; } mCountOfSuccessfulOutgoingDatagram++; + mShadowCountOfSuccessfulOutgoingDatagram++; logd("addCountOfSuccessfulOutgoingDatagram: current count=" + mCountOfSuccessfulOutgoingDatagram); return this; @@ -140,12 +150,14 @@ public SessionMetricsStats addCountOfFailedOutgoingDatagram( @NonNull @SatelliteManager.DatagramType int datagramType, @NonNull @SatelliteManager.SatelliteResult int resultCode) { + datagramStats.addCountOfUnsuccessfulUserMessages(datagramType, resultCode); if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) { // Ignore KEEP_ALIVE messages return this; } mCountOfFailedOutgoingDatagram++; + mShadowCountOfFailedOutgoingDatagram++; logd("addCountOfFailedOutgoingDatagram: current count=" + mCountOfFailedOutgoingDatagram); if (resultCode == SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE) { @@ -166,6 +178,7 @@ } mCountOfTimedOutUserMessagesWaitingForConnection++; + mShadowCountOfTimedOutUserMessagesWaitingForConnection++; logd("addCountOfTimedOutUserMessagesWaitingForConnection: current count=" + mCountOfTimedOutUserMessagesWaitingForConnection); return this; @@ -180,6 +193,7 @@ } mCountOfTimedOutUserMessagesWaitingForAck++; + mShadowCountOfTimedOutUserMessagesWaitingForAck++; logd("addCountOfTimedOutUserMessagesWaitingForAck: current count=" + mCountOfTimedOutUserMessagesWaitingForAck); return this; @@ -276,18 +290,23 @@ /** Returns {@link SatelliteSessionStats} of the satellite service. */ public void requestSatelliteSessionStats(int subId, @NonNull ResultReceiver result) { + Log.i(TAG, "requestSatelliteSessionStats called"); Bundle bundle = new Bundle(); SatelliteSessionStats sessionStats = new SatelliteSessionStats.Builder() - .setCountOfSuccessfulUserMessages(mCountOfSuccessfulOutgoingDatagram) - .setCountOfUnsuccessfulUserMessages(mCountOfFailedOutgoingDatagram) + .setCountOfSuccessfulUserMessages(mShadowCountOfSuccessfulOutgoingDatagram) + .setCountOfUnsuccessfulUserMessages(mShadowCountOfFailedOutgoingDatagram) .setCountOfTimedOutUserMessagesWaitingForConnection( - mCountOfTimedOutUserMessagesWaitingForConnection) + mShadowCountOfTimedOutUserMessagesWaitingForConnection) .setCountOfTimedOutUserMessagesWaitingForAck( - mCountOfTimedOutUserMessagesWaitingForAck) + mShadowCountOfTimedOutUserMessagesWaitingForAck) .setCountOfUserMessagesInQueueToBeSent( DatagramDispatcher.getInstance().getPendingUserMessagesCount()) .build(); bundle.putParcelable(SatelliteManager.KEY_SESSION_STATS, sessionStats); + + // TODO b/381007377 should retrieve MessagesInQueueToBeSent count per messageType and add + // to datagramStats + bundle.putParcelable(KEY_SESSION_STATS_V2, datagramStats); result.send(SATELLITE_RESULT_SUCCESS, bundle); } @@ -302,9 +321,9 @@ } private void initializeSessionMetricsParam() { - mInitializationResult = SatelliteManager.SATELLITE_RESULT_SUCCESS; + mInitializationResult = SATELLITE_RESULT_SUCCESS; mRadioTechnology = SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN; - mTerminationResult = SatelliteManager.SATELLITE_RESULT_SUCCESS; + mTerminationResult = SATELLITE_RESULT_SUCCESS; mInitializationProcessingTimeMillis = 0; mTerminationProcessingTimeMillis = 0; mSessionDurationSec = 0; @@ -322,6 +341,15 @@ mCountOfAutoExitDueToTnNetwork = 0; } + public void resetSessionStatsShadowCounters() { + logd("resetTheStatsCounters"); + mShadowCountOfSuccessfulOutgoingDatagram = 0; + mShadowCountOfFailedOutgoingDatagram = 0; + mShadowCountOfTimedOutUserMessagesWaitingForConnection = 0; + mShadowCountOfTimedOutUserMessagesWaitingForAck = 0; + datagramStats.clear(); + } + private static void logd(@NonNull String log) { if (DBG) { Log.d(TAG, log);
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java index d835f2d..9d62972 100644 --- a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java +++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -2121,9 +2121,6 @@ * @param capabilities Service capabilities bitmasks */ public void setServiceCapabilities(int subId, int capabilities) { - if (!mFeatureFlags.dataOnlyCellularService()) { - return; - } writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SERVICE_CAPABILITIES, capabilities, SubscriptionInfoInternal.Builder::setServiceCapabilities); }
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java index 353493b..a0a050c 100644 --- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -27,6 +27,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; @@ -720,7 +721,7 @@ return false; } } else { - if (!mSubscriptionManager.canManageSubscription(subInfo.toSubscriptionInfo(), + if (!canManageSubscription(subInfo.toSubscriptionInfo(), callingPackage)) { loge("checkCarrierPrivilegeOnSubList: cannot manage sub " + subId); return false; @@ -1450,8 +1451,8 @@ SatelliteController satelliteController = SatelliteController.getInstance(); boolean isSatelliteEnabledOrBeingEnabled = false; if (satelliteController != null) { - isSatelliteEnabledOrBeingEnabled = satelliteController.isSatelliteEnabled() - || satelliteController.isSatelliteBeingEnabled(); + isSatelliteEnabledOrBeingEnabled = + satelliteController.isSatelliteEnabledOrBeingEnabled(); } if (!isSatelliteEnabledOrBeingEnabled) { @@ -1838,37 +1839,35 @@ + " newSetting=" + SubscriptionManager.usageSettingToString(newUsageSetting)); } - if (mFeatureFlags.dataOnlyCellularService()) { - final int[] servicesFromCarrierConfig = - config.getIntArray( - CarrierConfigManager.KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY); - int serviceBitmasks = 0; - boolean allServicesAreValid = true; - // Check if all services from carrier config are valid before setting to db - if (servicesFromCarrierConfig == null) { - allServicesAreValid = false; - } else { - for (int service : servicesFromCarrierConfig) { - if (service < SubscriptionManager.SERVICE_CAPABILITY_VOICE - || service > SubscriptionManager.SERVICE_CAPABILITY_MAX) { - allServicesAreValid = false; - break; - } else { - serviceBitmasks |= SubscriptionManager.serviceCapabilityToBitmask(service); - } + final int[] servicesFromCarrierConfig = + config.getIntArray( + CarrierConfigManager.KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY); + int serviceBitmasks = 0; + boolean allServicesAreValid = true; + // Check if all services from carrier config are valid before setting to db + if (servicesFromCarrierConfig == null) { + allServicesAreValid = false; + } else { + for (int service : servicesFromCarrierConfig) { + if (service < SubscriptionManager.SERVICE_CAPABILITY_VOICE + || service > SubscriptionManager.SERVICE_CAPABILITY_MAX) { + allServicesAreValid = false; + break; + } else { + serviceBitmasks |= SubscriptionManager.serviceCapabilityToBitmask(service); } } - // In case we get invalid service override, fall back to default value. - // DO NOT throw exception which will crash phone process. - if (!allServicesAreValid) { - serviceBitmasks = SubscriptionManager.getAllServiceCapabilityBitmasks(); - } + } + // In case we get invalid service override, fall back to default value. + // DO NOT throw exception which will crash phone process. + if (!allServicesAreValid) { + serviceBitmasks = SubscriptionManager.getAllServiceCapabilityBitmasks(); + } - if (serviceBitmasks != subInfo.getServiceCapabilities()) { - log("updateSubscriptionByCarrierConfig: serviceCapabilities updated from " - + subInfo.getServiceCapabilities() + " to " + serviceBitmasks); - mSubscriptionDatabaseManager.setServiceCapabilities(subId, serviceBitmasks); - } + if (serviceBitmasks != subInfo.getServiceCapabilities()) { + log("updateSubscriptionByCarrierConfig: serviceCapabilities updated from " + + subInfo.getServiceCapabilities() + " to " + serviceBitmasks); + mSubscriptionDatabaseManager.setServiceCapabilities(subId, serviceBitmasks); } } @@ -2268,7 +2267,7 @@ return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle()) .map(SubscriptionInfoInternal::toSubscriptionInfo) .filter(subInfo -> subInfo.isEmbedded() - && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) + && canManageSubscription(subInfo, callingPackage)) .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex) .thenComparing(SubscriptionInfo::getSubscriptionId)) .collect(Collectors.toList()); @@ -2994,7 +2993,7 @@ return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .map(SubscriptionInfoInternal::toSubscriptionInfo) .filter(info -> groupUuid.equals(info.getGroupUuid()) - && (mSubscriptionManager.canManageSubscription(info, callingPackage) + && (canManageSubscription(info, callingPackage) || TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow( mContext, info.getSubscriptionId(), callingPackage, callingFeatureId, "getSubscriptionsInGroup"))) @@ -4903,6 +4902,15 @@ return cardNumber; } + private boolean canManageSubscription(SubscriptionInfo subInfo, String packageName) { + if (Flags.hsumPackageManager() && UserManager.isHeadlessSystemUserMode()) { + return mSubscriptionManager.canManageSubscriptionAsUser(subInfo, packageName, + UserHandle.of(ActivityManager.getCurrentUser())); + } else { + return mSubscriptionManager.canManageSubscription(subInfo, packageName); + } + } + /** * Log debug messages. *
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java index 57ea9b9..cccbf19 100644 --- a/src/java/com/android/internal/telephony/uicc/IccRecords.java +++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -36,6 +36,7 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.MccTable; import com.android.internal.telephony.gsm.SimTlv; @@ -1300,10 +1301,18 @@ return null; } + if (rsp.exception instanceof CommandException commandException) { + switch (commandException.getCommandError()) { + case REQUEST_NOT_SUPPORTED: + throw new UnsupportedOperationException(commandException); + default: + // handle other exceptions in the rsp.exception conditional below + } + } if (rsp.exception != null) { loge("getIccSimChallengeResponse exception: " + rsp.exception); //TODO: propagate better exceptions up to the user now that we have them available - //in the call stack. + //in the call stack (see CommandException switch above). return null; }
diff --git a/src/java/com/android/internal/telephony/uicc/SimTypeInfo.java b/src/java/com/android/internal/telephony/uicc/SimTypeInfo.java new file mode 100644 index 0000000..b19e42a --- /dev/null +++ b/src/java/com/android/internal/telephony/uicc/SimTypeInfo.java
@@ -0,0 +1,78 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.uicc; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class contains the sim type information of active physical slot ids. + */ +public class SimTypeInfo { + + /** + * SimType (bit mask) + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + SimType.SIM_TYPE_UNKNOWN, + SimType.SIM_TYPE_PHYSICAL, + SimType.SIM_TYPE_ESIM, + }) + public @interface SimType { + /** Unknown SIM */ + int SIM_TYPE_UNKNOWN = 0; + /** Physical SIM (Can have eUICC capabilities) */ + int SIM_TYPE_PHYSICAL = 1 << 0; + /** Embedded SIM*/ + int SIM_TYPE_ESIM = 1 << 1; + } + + public @SimType int mCurrentSimType = SimType.SIM_TYPE_UNKNOWN; + // Bitmask of the supported {@code SimType}s + public int mSupportedSimTypes; + + /** + * Set the current SimType according to the input type. + */ + public void setCurrentSimType(int simType) { + switch(simType) { + case android.hardware.radio.config.SimType.UNKNOWN: + mCurrentSimType = SimType.SIM_TYPE_UNKNOWN; + break; + case android.hardware.radio.config.SimType.PHYSICAL: + mCurrentSimType = SimType.SIM_TYPE_PHYSICAL; + break; + case android.hardware.radio.config.SimType.ESIM: + mCurrentSimType = SimType.SIM_TYPE_ESIM; + break; + default: + throw new RuntimeException("Unrecognized RIL_SimType: " + simType); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("SimTypeInfo {activeSimType=").append(mCurrentSimType).append(",") + .append("supportedSimType=").append(mSupportedSimTypes); + sb.append("}"); + return sb.toString(); + } +}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java index dd71c44..9db25b6 100644 --- a/src/java/com/android/internal/telephony/uicc/UiccController.java +++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -80,6 +80,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -162,8 +163,7 @@ // this needs to be here, because on bootup we dont know which index maps to which UiccSlot @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private CommandsInterface[] mCis; - @VisibleForTesting - public UiccSlot[] mUiccSlots; + private UiccSlot[] mUiccSlots; private int[] mPhoneIdToSlotId; private boolean mIsSlotStatusSupported = true; @@ -491,6 +491,27 @@ } /** + * Set UiccSlot object for a specific physical slot index on the device. + * + * This is only supposed to be used internally and by unit tests. + * + * @param slotId Slot index + * @param slot Slot object + */ + @VisibleForTesting + public void setUiccSlot(int slotId, @NonNull UiccSlot slot) { + synchronized (mLock) { + if (!isValidSlotIndex(slotId)) { + throw new ArrayIndexOutOfBoundsException("Invalid slot index: " + slotId); + } + if (mUiccSlots[slotId] != null) { + mUiccSlots[slotId].dispose(); + } + mUiccSlots[slotId] = Objects.requireNonNull(slot); + } + } + + /** * API to get UiccSlot object for a given phone id * @return UiccSlot object for the given phone id */ @@ -1076,7 +1097,7 @@ log("Creating mUiccSlots[" + slotId + "]; mUiccSlots.length = " + mUiccSlots.length); } - mUiccSlots[slotId] = new UiccSlot(mContext, true); + setUiccSlot(slotId, new UiccSlot(mContext, true)); } mUiccSlots[slotId].update(mCis[index], status, index, slotId); @@ -1353,7 +1374,7 @@ if (VDBG) { log("Creating mUiccSlot[" + i + "]; mUiccSlots.length = " + mUiccSlots.length); } - mUiccSlots[i] = new UiccSlot(mContext, isActive); + setUiccSlot(i, new UiccSlot(mContext, isActive)); } if (isActive) { // check isActive flag so that we don't have to iterate through all @@ -1803,6 +1824,17 @@ return mCardStrings; } + /** + * Release resources. Must be called each time this class is used. + */ + @VisibleForTesting + public void dispose() { + for (var slot : mUiccSlots) { + slot.dispose(); + } + mUiccSlots = null; + } + public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); pw.println("mIsCdmaSupported=" + isCdmaSupported(mContext));
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPort.java b/src/java/com/android/internal/telephony/uicc/UiccPort.java index 905db70..8118a12 100644 --- a/src/java/com/android/internal/telephony/uicc/UiccPort.java +++ b/src/java/com/android/internal/telephony/uicc/UiccPort.java
@@ -33,6 +33,8 @@ import com.android.internal.telephony.flags.FeatureFlagsImpl; import com.android.telephony.Rlog; +import dalvik.system.CloseGuard; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -46,6 +48,7 @@ // The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock // between UiccSlot, UiccCard, EuiccCard, UiccPort, EuiccPort and UiccProfile for now. protected final Object mLock; + private final CloseGuard mCloseGuard = CloseGuard.get(); private String mIccid; protected String mCardId; @@ -68,6 +71,7 @@ if (DBG) log("Creating"); mPhoneId = phoneId; mLock = lock; + mCloseGuard.open("cleanup"); update(c, ci, ics, uiccCard); } @@ -97,6 +101,7 @@ public void dispose() { synchronized (mLock) { if (DBG) log("Disposing Port"); + mCloseGuard.close(); if (mUiccProfile != null) { mUiccProfile.dispose(); } @@ -106,9 +111,14 @@ } @Override - protected void finalize() { + protected void finalize() throws Throwable { if (DBG) log("UiccPort finalized"); - cleanupOpenLogicalChannelRecordsIfNeeded(); + try { + if (mCloseGuard != null) mCloseGuard.warnIfOpen(); + cleanupOpenLogicalChannelRecordsIfNeeded(); + } finally { + super.finalize(); + } } /** @@ -440,7 +450,13 @@ * removal or modem reset. The obsoleted records may trigger a redundant release of logical * channel that may have been assigned to other client. */ + @SuppressWarnings("GuardedBy") private void cleanupOpenLogicalChannelRecordsIfNeeded() { + // This check may raise GuardedBy warning, but we need it as long as this method is called + // from finalize(). We can remove it from there once UiccPort is fully protected against + // resource leak (e.g. with CloseGuard) and all (direct and indirect) users are fixed. + if (mOpenChannelRecords == null) return; + synchronized (mOpenChannelRecords) { for (OpenLogicalChannelRecord record : mOpenChannelRecords) { if (DBG) log("Clean up " + record);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java index db10271..d986c93 100644 --- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java +++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -391,6 +391,13 @@ } } + /** + * Release resources. Must be called each time this class is used. + */ + public void dispose() { + nullifyUiccCard(false); + } + public boolean isStateUnknown() { // CardState is not specific to any port index, use default port. CardState cardState = mCardState.get(TelephonyManager.DEFAULT_PORT_INDEX);
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 3bd66f8..7bdec47 100644 --- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java +++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccPort.java
@@ -133,7 +133,8 @@ UiccCard card, MultipleEnabledProfilesMode supportedMepMode) { super(c, ci, ics, phoneId, lock, card); // TODO: Set supportExtendedApdu based on ATR. - mApduSender = new ApduSender(ci, ISD_R_AID, false /* supportExtendedApdu */); + mApduSender = new ApduSender(c, phoneId, 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 0a56b2b..309fe79 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,11 +17,17 @@ 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; @@ -36,7 +42,14 @@ * 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. This class is thread-safe. + * 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. * * @hide */ @@ -50,8 +63,12 @@ // 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); @@ -61,26 +78,48 @@ 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 mChannelOpened. We only allow to open a single logical channel at any - // time for an AID. - private final Object mChannelLock = new Object(); + // 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; private boolean mChannelOpened; /** * @param aid The AID that will be used to open a logical channel to. */ - public ApduSender(CommandsInterface ci, String aid, boolean supportExtendedApdu) { + 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."); + } 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; } /** @@ -99,79 +138,133 @@ RequestProvider requestProvider, ApduSenderResultCallback resultCallback, Handler 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 - } - 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; + if (!acquireChannelLock()) { + AsyncResultHelper.throwException( + new ApduException("The logical channel is still in use."), + resultCallback, + handler); + return; } - 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); + 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); } - }, handler); + 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); + } } - /** - * 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 + 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; + returnRespnseOrException( + channel, + /* closeChannelImmediately= */ false, + /* response= */ null, + new ApduException( + "Failed to open logical channel for AID: " + + mAid + + ", with status: " + + status), + resultCallback, + handler); + 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); + } + }, + handler); } /** @@ -184,6 +277,7 @@ private void sendCommand( List<ApduCommand> commands, int index, + boolean closeChannelImmediately, ApduSenderResultCallback resultCallback, Handler handler) { ApduCommand command = commands.get(index); @@ -198,9 +292,21 @@ 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) { - closeAndReturn(command.channel, null /* response */, - new ApduException(status), resultCallback, handler); + 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); return; } @@ -210,11 +316,17 @@ fullResponse); if (continueSendCommand) { // Sends the next command - sendCommand(commands, index + 1, resultCallback, handler); + sendCommand(commands, index + 1, + closeChannelImmediately, resultCallback, handler); } else { // Returns the result of the last command - closeAndReturn(command.channel, fullResponse.payload, - null /* exception */, resultCallback, handler); + returnRespnseOrException( + command.channel, + closeChannelImmediately, + fullResponse.payload, + null /* exception */, + resultCallback, + handler); } } }, handler); @@ -237,7 +349,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); @@ -263,6 +375,41 @@ }, 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. * @@ -280,10 +427,9 @@ mCloseChannel.invoke(channel, new AsyncResultCallback<Boolean>() { @Override public void onResult(Boolean aBoolean) { - synchronized (mChannelLock) { - mChannelOpened = false; - mChannelLock.notify(); - } + tearDownPreferences(); + mChannelOpened = false; + releaseChannelLock(); if (exception == null) { resultCallback.onResult(response); @@ -293,4 +439,99 @@ } }, 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/android/telephony/ims/RcsConfigTest.java b/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java index 4889187..c690ab4 100644 --- a/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java +++ b/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
@@ -14,7 +14,7 @@ * limitations under the License. */ -package com.telephony.ims; +package android.telephony.ims; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -36,6 +36,7 @@ import com.android.internal.telephony.FakeTelephonyProvider; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -176,6 +177,11 @@ createFakeSimInfo(); } + @After + public void tearDown() { + mFakeTelephonyProvider.close(); + } + @Test @SmallTest public void testLoadAndUpdateConfigForSub() {
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/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java index 70e3dee..9788320 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java +++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -241,6 +241,14 @@ } @Override + public boolean bindServiceAsUser( + Intent serviceIntent, + ServiceConnection connection, + int flags, UserHandle user) { + return bindService(serviceIntent, connection, flags); + } + + @Override public void unbindService( ServiceConnection connection) { IInterface service = mServiceByServiceConnection.remove(connection);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java index 8dad3ec..522cdac 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -15,6 +15,12 @@ */ package com.android.internal.telephony; +import static android.telephony.CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI; +import static android.telephony.CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST; +import static android.telephony.SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_SIP; +import static android.telephony.SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2; +import static android.telephony.SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96; + import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.eq; @@ -26,9 +32,11 @@ import android.telephony.CellIdentityGsm; import android.telephony.CellInfo; +import android.telephony.CellularIdentifierDisclosure; import android.telephony.DisconnectCause; import android.telephony.PreciseCallState; import android.telephony.PreciseDisconnectCause; +import android.telephony.SecurityAlgorithmUpdate; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.telephony.ims.ImsCallProfile; @@ -378,6 +386,51 @@ @Test @SmallTest + public void testNotifyCallbackModeStarted() { + doReturn(true).when(mFeatureFlags).emergencyCallbackModeNotification(); + int phoneId = mPhone.getPhoneId(); + int subId = mPhone.getSubId(); + int type = 1; + long durationMillis = 1000; + + mDefaultPhoneNotifierUT.notifyCallbackModeStarted(mPhone, type, durationMillis); + + verify(mTelephonyRegistryManager).notifyCallbackModeStarted(eq(phoneId), eq(subId), + eq(type), eq(durationMillis)); + } + + @Test + @SmallTest + public void testNotifyCallbackModeRestarted() { + doReturn(true).when(mFeatureFlags).emergencyCallbackModeNotification(); + int phoneId = mPhone.getPhoneId(); + int subId = mPhone.getSubId(); + int type = 1; + long durationMillis = 1000; + + mDefaultPhoneNotifierUT.notifyCallbackModeRestarted(mPhone, type, durationMillis); + + verify(mTelephonyRegistryManager).notifyCallbackModeRestarted(eq(phoneId), eq(subId), + eq(type), eq(durationMillis)); + } + + @Test + @SmallTest + public void testNotifyCallbackModeStopped() { + doReturn(true).when(mFeatureFlags).emergencyCallbackModeNotification(); + int phoneId = mPhone.getPhoneId(); + int subId = mPhone.getSubId(); + int type = 1; + int reason = 0; + + mDefaultPhoneNotifierUT.notifyCallbackModeStopped(mPhone, type, reason); + + verify(mTelephonyRegistryManager).notifyCallbackModeStopped(eq(phoneId), eq(subId), + eq(type), eq(reason)); + } + + @Test + @SmallTest public void testCarrierRoamingNtnModeChanged() { int subId = mPhone.getSubId(); mDefaultPhoneNotifierUT.notifyCarrierRoamingNtnModeChanged(mPhone, true); @@ -393,4 +446,46 @@ verify(mTelephonyRegistryManager).notifyCarrierRoamingNtnEligibleStateChanged( eq(subId), eq(true)); } + + @Test + @SmallTest + public void testCarrierRoamingNtnAvailableServicesChanged() { + int subId = mPhone.getSubId(); + int[] testServices = {3, 6}; + mDefaultPhoneNotifierUT.notifyCarrierRoamingNtnAvailableServicesChanged( + mPhone, testServices); + verify(mTelephonyRegistryManager).notifyCarrierRoamingNtnAvailableServicesChanged( + eq(subId), eq(testServices)); + } + + @Test + @SmallTest + public void testSecurityAlgorithmsChanged() { + doReturn(true).when(mFeatureFlags).securityAlgorithmsUpdateIndications(); + int phoneId = mPhone.getPhoneId(); + int subId = mPhone.getSubId(); + SecurityAlgorithmUpdate update = + new SecurityAlgorithmUpdate( + CONNECTION_EVENT_VOLTE_SIP, SECURITY_ALGORITHM_EEA2, + SECURITY_ALGORITHM_HMAC_SHA1_96, false); + mDefaultPhoneNotifierUT.notifySecurityAlgorithmsChanged(mPhone, update); + verify(mTelephonyRegistryManager).notifySecurityAlgorithmsChanged( + eq(phoneId), eq(subId), eq(update)); + } + + @Test + @SmallTest + public void testCellularIdentifierDisclosedChanged() { + doReturn(true).when(mFeatureFlags).cellularIdentifierDisclosureIndications(); + int phoneId = mPhone.getPhoneId(); + int subId = mPhone.getSubId(); + CellularIdentifierDisclosure disclosure = + new CellularIdentifierDisclosure(NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, + CELLULAR_IDENTIFIER_IMSI, + "001001", + false); + mDefaultPhoneNotifierUT.notifyCellularIdentifierDisclosedChanged(mPhone, disclosure); + verify(mTelephonyRegistryManager).notifyCellularIdentifierDisclosedChanged( + eq(phoneId), eq(subId), eq(disclosure)); + } }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java index c923f69..101c668 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java +++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -196,4 +196,11 @@ selectionArgs); return count; } + + /** + * Release resources. Must be called each time this class is used. + */ + public void close() { + mDbHelper.close(); + } }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java index bad32e9..8898a0f 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
@@ -41,6 +41,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.os.UserHandle; import android.telephony.IBootstrapAuthenticationCallback; import android.telephony.TelephonyManager; import android.telephony.gba.GbaAuthRequest; @@ -51,8 +52,6 @@ import android.testing.TestableLooper; import android.util.Log; -import androidx.test.filters.SmallTest; - import com.android.internal.telephony.metrics.RcsStats; import org.junit.After; @@ -66,7 +65,7 @@ */ @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public final class GbaManagerTest { +public final class GbaManagerTest extends TelephonyTest { private static final String LOG_TAG = "GbaManagerTest"; private static final ComponentName TEST_DEFAULT_SERVICE_NAME = new ComponentName( @@ -91,6 +90,7 @@ @Before public void setUp() throws Exception { + super.setUp(getClass().getSimpleName()); log("setUp"); mMockContext = mock(Context.class); mMockBinder = mock(IBinder.class); @@ -100,7 +100,8 @@ if (Looper.myLooper() == null) { Looper.prepare(); } - when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true); + when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any(UserHandle.class))) + .thenReturn(true); when(mMockGbaServiceBinder.asBinder()).thenReturn(mMockBinder); mTestGbaManager = new GbaManager(mMockContext, TEST_SUB_ID, null, 0, mMockRcsStats); mHandler = mTestGbaManager.getHandler(); @@ -109,137 +110,129 @@ } catch (Exception e) { fail("Unable to create looper from handler."); } + monitorTestableLooper(mLooper); } @After public void tearDown() throws Exception { log("tearDown"); mTestGbaManager.destroy(); - mTestGbaManager = null; - mHandler = null; - mLooper.destroy(); - mLooper = null; + super.tearDown(); } @Test - @SmallTest public void testFailOnRequest() throws Exception { GbaAuthRequest request = createDefaultRequest(); mTestGbaManager.bootstrapAuthenticationRequest(request); - mLooper.processAllMessages(); + processAllMessages(); - verify(mMockContext, never()).bindService(any(), any(), anyInt()); + verify(mMockContext, never()).bindServiceAsUser(any(), any(), anyInt(), + any(UserHandle.class)); verify(mMockCallback).onAuthenticationFailure(anyInt(), anyInt()); assertTrue(!mTestGbaManager.isServiceConnected()); } @Test - @SmallTest public void testBindServiceOnRequest() throws Exception { - mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName()); + mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123); GbaAuthRequest request = createDefaultRequest(); mTestGbaManager.bootstrapAuthenticationRequest(request); - mLooper.processAllMessages(); + processAllMessages(); bindAndConnectService(TEST_DEFAULT_SERVICE_NAME); - mLooper.processAllMessages(); + processAllMessages(); verify(mMockGbaServiceBinder).authenticationRequest(any()); assertTrue(mTestGbaManager.isServiceConnected()); } @Test - @SmallTest public void testFailAndRetryOnRequest() throws RemoteException { - when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(false); - mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName()); + when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any(UserHandle.class))) + .thenReturn(false); + mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123); GbaAuthRequest request = createDefaultRequest(); mTestGbaManager.bootstrapAuthenticationRequest(request); for (int i = 0; i < GbaManager.MAX_RETRY; i++) { - mLooper.processAllMessages(); - verify(mMockContext, times(i + 1)).bindService(any(), any(), anyInt()); - try { - Thread.sleep(GbaManager.RETRY_TIME_MS + 500); - } catch (InterruptedException e) { - } + processAllMessages(); + verify(mMockContext, times(i + 1)).bindServiceAsUser(any(), any(), anyInt(), + any(UserHandle.class)); + moveTimeForward(GbaManager.REQUEST_TIMEOUT_MS); } assertTrue(!mTestGbaManager.isServiceConnected()); - mLooper.processAllMessages(); + processAllMessages(); verify(mMockCallback).onAuthenticationFailure(anyInt(), anyInt()); } @Test - @SmallTest public void testBindServiceWhenPackageNameChanged() { - mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName()); + mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123); mTestGbaManager.overrideReleaseTime(RELEASE_TIME_60S); GbaAuthRequest request = createDefaultRequest(); mTestGbaManager.bootstrapAuthenticationRequest(request); - mLooper.processAllMessages(); + processAllMessages(); ServiceConnection conn = bindAndConnectService(TEST_DEFAULT_SERVICE_NAME); - mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName()); + mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName(), 123); assertEquals(TEST_SERVICE2_NAME.getPackageName(), mTestGbaManager.getServicePackage()); - mLooper.processAllMessages(); + processAllMessages(); unbindService(conn); bindAndConnectService(TEST_SERVICE2_NAME); assertTrue(mTestGbaManager.isServiceConnected()); } @Test - @SmallTest public void testBindServiceWhenReleaseTimeChanged() { - mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName()); + mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123); mTestGbaManager.overrideReleaseTime(RELEASE_NEVER); assertEquals(RELEASE_NEVER, mTestGbaManager.getReleaseTime()); - mLooper.processAllMessages(); + processAllMessages(); bindAndConnectService(TEST_DEFAULT_SERVICE_NAME); assertTrue(mTestGbaManager.isServiceConnected()); } @Test - @SmallTest public void testDontBindServiceWhenPackageNameChanged() { - mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName()); + mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName(), 123); - mLooper.processAllMessages(); + processAllMessages(); - verify(mMockContext, never()).bindService(any(), any(), anyInt()); + verify(mMockContext, never()).bindServiceAsUser(any(), any(), anyInt(), + any(UserHandle.class)); assertTrue(!mTestGbaManager.isServiceConnected()); } @Test - @SmallTest public void testDontBindServiceWhenReleaseTimeChanged() { - mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName()); + mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123); mTestGbaManager.overrideReleaseTime(RELEASE_TIME_60S); - mLooper.processAllMessages(); + processAllMessages(); - verify(mMockContext, never()).bindService(any(), any(), anyInt()); + verify(mMockContext, never()).bindServiceAsUser(any(), any(), anyInt(), + any(UserHandle.class)); assertTrue(!mTestGbaManager.isServiceConnected()); } @Test - @SmallTest public void testMetricsGbaEvent() throws Exception { - mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName()); + mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123); mTestGbaManager.overrideReleaseTime(RELEASE_NEVER); - mLooper.processAllMessages(); + processAllMessages(); bindAndConnectService(TEST_DEFAULT_SERVICE_NAME); GbaAuthRequest request = createDefaultRequest(); // Failure case mTestGbaManager.bootstrapAuthenticationRequest(request); - mLooper.processAllMessages(); + processAllMessages(); ArgumentCaptor<GbaAuthRequest> captor = ArgumentCaptor.forClass(GbaAuthRequest.class); verify(mMockGbaServiceBinder, times(1)).authenticationRequest(captor.capture()); @@ -254,7 +247,7 @@ // Success case mTestGbaManager.bootstrapAuthenticationRequest(request); - mLooper.processAllMessages(); + processAllMessages(); ArgumentCaptor<GbaAuthRequest> captor2 = ArgumentCaptor.forClass(GbaAuthRequest.class); verify(mMockGbaServiceBinder, times(2)).authenticationRequest(captor2.capture()); @@ -280,9 +273,10 @@ ArgumentCaptor.forClass(Intent.class); ArgumentCaptor<ServiceConnection> serviceCaptor = ArgumentCaptor.forClass(ServiceConnection.class); - verify(mMockContext, atLeastOnce()).bindService(intentCaptor.capture(), + verify(mMockContext, atLeastOnce()).bindServiceAsUser(intentCaptor.capture(), serviceCaptor.capture(), eq( - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE)); + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE), + any(UserHandle.class)); Intent testIntent = intentCaptor.getValue(); assertEquals(GbaService.SERVICE_INTERFACE, testIntent.getAction()); assertEquals(component.getPackageName(), testIntent.getPackage());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java index f92643a..a7923cf 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -1044,7 +1044,8 @@ doReturn(new TelephonyDisplayInfo( mNetworkTypeController.getDataNetworkType(), mNetworkTypeController.getOverrideNetworkType(), - false)).when(mDisplayInfoController).getTelephonyDisplayInfo(); + false, false, false)) + .when(mDisplayInfoController).getTelephonyDisplayInfo(); return null; }).when(mDisplayInfoController).updateTelephonyDisplayInfo(); mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java index d8005e8..28d0318 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -1270,8 +1270,7 @@ doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords(); doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu(); - List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG, - FEATURE_ID); + List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG); assertNotNull(impuList); assertEquals(refImpuArray.length, impuList.size()); @@ -1288,8 +1287,7 @@ refImpuArray[2] = "tel:+91987754324"; doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords(); doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu(); - List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG, - FEATURE_ID); + List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG); assertNotNull(impuList); // Null or Empty string cannot be converted to URI assertEquals(refImpuArray.length - 2, impuList.size()); @@ -1300,7 +1298,7 @@ doReturn(null).when(mPhone).getIsimRecords(); try { - mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG, FEATURE_ID); + mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG); fail(); } catch (Exception ex) { assertTrue(ex instanceof IllegalStateException); @@ -1311,32 +1309,121 @@ @Test public void getImsPublicUserIdentities_InValidSubIdCheck() { try { - mPhoneSubInfoControllerUT.getImsPublicUserIdentities(-1, TAG, FEATURE_ID); + mPhoneSubInfoControllerUT.getImsPublicUserIdentities(-1, TAG); fail(); } catch (Exception ex) { assertTrue(ex instanceof IllegalArgumentException); - assertTrue(ex.getMessage().contains("Invalid SubscriptionID")); + assertTrue(ex.getMessage().contains("Invalid subscription")); } } @Test public void getImsPublicUserIdentities_NoReadPrivilegedPermission() { mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL); - String[] refImpuArray = new String[3]; - refImpuArray[0] = "012345678"; - refImpuArray[1] = "sip:test@verify.com"; - refImpuArray[2] = "tel:+91987754324"; - doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords(); - doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu(); - List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG, - FEATURE_ID); + try { + mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG); + fail(); + } catch (Exception ex) { + assertTrue(ex instanceof SecurityException); + assertTrue(ex.getMessage().contains("getImsPublicUserIdentities")); + } - assertNotNull(impuList); - assertEquals(refImpuArray.length, impuList.size()); - assertEquals(impuList.get(0).toString(), refImpuArray[0]); - assertEquals(impuList.get(1).toString(), refImpuArray[1]); - assertEquals(impuList.get(2).toString(), refImpuArray[2]); mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE); } + + @Test + public void getImsPcscfAddresses() { + String[] preDefinedPcscfs = new String[3]; + preDefinedPcscfs[0] = "127.0.0.1"; + preDefinedPcscfs[1] = "192.168.0.1"; + preDefinedPcscfs[2] = "::1"; + doReturn(true).when(mFeatureFlags).supportIsimRecord(); + doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords(); + doReturn(preDefinedPcscfs).when(mIsimUiccRecords).getIsimPcscf(); + + List<String> pcscfAddresses = mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG); + + assertNotNull(pcscfAddresses); + assertEquals(preDefinedPcscfs.length, pcscfAddresses.size()); + assertEquals(preDefinedPcscfs[0], pcscfAddresses.get(0).toString()); + assertEquals(preDefinedPcscfs[1], pcscfAddresses.get(1).toString()); + assertEquals(preDefinedPcscfs[2], pcscfAddresses.get(2).toString()); + } + + @Test + public void getImsPcscfAddresses_InvalidPcscf() { + String[] preDefinedPcscfs = new String[3]; + preDefinedPcscfs[0] = null; + preDefinedPcscfs[2] = ""; + preDefinedPcscfs[2] = "::1"; + doReturn(true).when(mFeatureFlags).supportIsimRecord(); + doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords(); + doReturn(preDefinedPcscfs).when(mIsimUiccRecords).getIsimPcscf(); + + List<String> pcscfAddresses = mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG); + + assertNotNull(pcscfAddresses); + // Null or Empty string is not added to pcscf list + assertEquals(preDefinedPcscfs.length - 2, pcscfAddresses.size()); + } + + @Test + public void getImsPcscfAddresses_IsimNotLoadedError() { + doReturn(true).when(mFeatureFlags).supportIsimRecord(); + doReturn(null).when(mPhone).getIsimRecords(); + + try { + mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG); + fail(); + } catch (Exception ex) { + assertTrue(ex instanceof IllegalStateException); + assertTrue(ex.getMessage().contains("ISIM is not loaded")); + } + } + + @Test + public void getImsPcscfAddresses_InValidSubIdCheck() { + doReturn(true).when(mFeatureFlags).supportIsimRecord(); + + try { + mPhoneSubInfoControllerUT.getImsPcscfAddresses(-1, TAG); + fail(); + } catch (Exception ex) { + assertTrue(ex instanceof IllegalArgumentException); + assertTrue(ex.getMessage().contains("Invalid subscription")); + } + } + + @Test + public void getImsPcscfAddresses_NoReadPrivilegedPermission() { + mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL); + doReturn(true).when(mFeatureFlags).supportIsimRecord(); + + try { + mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG); + fail(); + } catch (Exception ex) { + assertTrue(ex instanceof SecurityException); + assertTrue(ex.getMessage().contains("getImsPcscfAddresses")); + } + + mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE); + } + + @Test + public void getImsPcscfAddresses_FlagDisabled() { + String[] preDefinedPcscfs = new String[3]; + preDefinedPcscfs[0] = "127.0.0.1"; + preDefinedPcscfs[1] = "192.168.0.1"; + preDefinedPcscfs[2] = "::1"; + doReturn(false).when(mFeatureFlags).supportIsimRecord(); + doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords(); + doReturn(preDefinedPcscfs).when(mIsimUiccRecords).getIsimPcscf(); + + List<String> pcscfAddresses = mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG); + + assertNotNull(pcscfAddresses); + assertEquals(0, pcscfAddresses.size()); + } } \ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java index 88c5389..48c9f9c 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -175,6 +175,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.SparseArray; +import android.view.Display; import androidx.test.filters.FlakyTest; @@ -312,6 +313,7 @@ } catch (RuntimeException e) { } Context context = new ContextFixture().getTestDouble(); + doReturn(Display.DEFAULT_DISPLAY).when(context).getDisplayId(); doReturn(true).when(mConnectionManager).isNetworkSupported(ConnectivityManager.TYPE_MOBILE); doReturn(mConnectionManager).when(context) .getSystemService(Context.CONNECTIVITY_SERVICE);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java index 4c42e2e..05107df 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
@@ -173,6 +173,7 @@ rats.add(new Pair<Integer, Boolean>(ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA, false)); rats.add(new Pair<Integer, Boolean>(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, false)); rats.add(new Pair<Integer, Boolean>(ServiceState.RIL_RADIO_TECHNOLOGY_NR, false)); + rats.add(new Pair<Integer, Boolean>(ServiceState.RIL_RADIO_TECHNOLOGY_NB_IOT_NTN, false)); for (Pair<Integer, Boolean> rat : rats) { boolean isCdma = rat.second;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java index 1465176..70bdcba 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -2983,7 +2983,7 @@ doReturn(ServiceState.STATE_IN_SERVICE).when(mSST).getCombinedRegState(ss); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Plmn should be shown, and the string is "Emergency call only" Bundle b = getExtrasFromLastSpnUpdateIntent(); @@ -3005,7 +3005,7 @@ sst.mSS = ss; // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Plmn should be shown, and the string is "No service" Bundle b = getExtrasFromLastSpnUpdateIntent(); @@ -3026,7 +3026,7 @@ sst.mSS = ss; // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Plmn should be shown, and the string is null Bundle b = getExtrasFromLastSpnUpdateIntent(); @@ -3049,7 +3049,7 @@ doReturn(false).when(mPhone).isWifiCallingEnabled(); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Show both spn & plmn String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING); @@ -3091,7 +3091,7 @@ doReturn(formats).when(r).getStringArray(anyInt()); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Only spn should be shown String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING); @@ -3128,7 +3128,7 @@ doReturn(true).when(mPhone).isImsRegistered(); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Only spn should be shown String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING); @@ -3161,7 +3161,7 @@ doReturn(true).when(mPhone).isImsRegistered(); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Only plmn should be shown String plmn = mBundle.getStringArray(CarrierConfigManager.KEY_PNN_OVERRIDE_STRING_ARRAY)[0]; @@ -3187,7 +3187,7 @@ doReturn(false).when(mPhone).isWifiCallingEnabled(); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Show both spn & plmn String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING); @@ -3240,7 +3240,7 @@ doReturn(false).when(mPhone).isWifiCallingEnabled(); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Plmn should be shown, and the string is "No service" Bundle b = getExtrasFromLastSpnUpdateIntent(); @@ -3271,7 +3271,7 @@ doReturn(true).when(mPhone).isWifiCallingEnabled(); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Plmn should be shown, and the string is "No service" Bundle b = getExtrasFromLastSpnUpdateIntent(); @@ -3303,7 +3303,7 @@ doReturn(true).when(mPhone).isWifiCallingEnabled(); // update the spn - sst.updateSpnDisplay(); + sst.updateCarrierDisplayName(); // Plmn should be shown, and the string is "No service" Bundle b = getExtrasFromLastSpnUpdateIntent(); @@ -3503,7 +3503,7 @@ doReturn(Arrays.asList("10123")).when(mSatelliteController).getSatellitePlmnsForCarrier( anyInt()); doReturn(satelliteSupportedServiceList).when(mSatelliteController) - .getSupportedSatelliteServices(sst.mSubId, "10123"); + .getSupportedSatelliteServicesForPlmn(sst.mSubId, "10123"); assertFalse(sst.mSS.isUsingNonTerrestrialNetwork());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java index 054df07..6fd45ea 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/SimultaneousCallingTrackerTest.java
@@ -134,7 +134,6 @@ .getSubscriptionInfo(any(Integer.class)); doReturn(RIL.RADIO_HAL_VERSION_2_2).when(mMockRadioConfigProxy).getVersion(); doReturn(true).when(mFeatureFlags).simultaneousCallingIndications(); - doReturn(true).when(mFeatureFlags).dataOnlyCellularService(); mMockRegistryManager = mContext.getSystemService(TelephonyRegistryManager.class); }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java index acf793e..b8316cb 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.isNull; @@ -116,17 +117,23 @@ public void testNotifySmsSentToEmergencyStateTracker(String destAddr, long messageId, boolean isOverIms, boolean isLastSmsPart) { - notifySmsSent(destAddr, messageId, isOverIms, isLastSmsPart, true/*success*/); + notifySmsSent(getSmsTracker(destAddr, messageId), isOverIms, + isLastSmsPart, true/*success*/); } public void testNotifySmsSentFailedToEmergencyStateTracker(String destAddr, long messageId, boolean isOverIms) { - notifySmsSent(destAddr, messageId, isOverIms, true/*isLastSmsPart*/, false/*success*/); + notifySmsSent(getSmsTracker(destAddr, messageId), isOverIms, + true/*isLastSmsPart*/, false/*success*/); } public void testNotifySmsReceivedViaImsToEmergencyStateTracker(String origAddr) { notifySmsReceivedViaImsToEmergencyStateTracker(origAddr); } + + private SMSDispatcher.SmsTracker getSmsTracker(String destAddr, long messageId) { + return new SMSDispatcher.SmsTracker(destAddr, messageId); + } } /** @@ -140,9 +147,9 @@ @Override public void sendData(String callingPackage, int callingUser, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, - PendingIntent deliveryIntent, boolean isForVvm) { + PendingIntent deliveryIntent, boolean isForVvm, long uniqueMessageId) { super.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, - data, sentIntent, deliveryIntent, isForVvm); + data, sentIntent, deliveryIntent, isForVvm, uniqueMessageId); } @Override @@ -167,9 +174,9 @@ @Override public void sendData(String callingPackage, int callingUser, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, - boolean isForVvm) { + boolean isForVvm, long uniqueMessageId) { super.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, - data, sentIntent, deliveryIntent, isForVvm); + data, sentIntent, deliveryIntent, isForVvm, uniqueMessageId); } @Override @@ -491,7 +498,7 @@ verify(mEmergencySmsDsc).finishSelection(); verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), - eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false)); + eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong()); assertNull(holder.getConnection()); assertFalse(holder.isDomainSelectionRequested()); assertEquals(0, holder.getPendingRequests().size()); @@ -542,7 +549,7 @@ verify(mImsSmsDispatcher).sendMultipartText(eq("911"), eq("2222"), eq(parts), eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), eq(false), - eq(10), eq(1L)); + eq(10), eq(1L), anyLong()); assertNull(holder.getConnection()); assertFalse(holder.isDomainSelectionRequested()); assertEquals(0, holder.getPendingRequests().size()); @@ -741,7 +748,7 @@ verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), - eq(false), eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false)); + eq(false), eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong()); } @Test @@ -782,7 +789,7 @@ verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10), - eq(false), eq(1L), eq(false)); + eq(false), eq(1L), eq(false), anyLong()); } @Test @@ -818,7 +825,7 @@ verify(mImsSmsDispatcher, times(2)).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), eq(false), eq(10), eq(false), eq(1L), - eq(false)); + eq(false), anyLong()); assertNull(holder.getConnection()); assertFalse(holder.isDomainSelectionRequested()); assertEquals(0, holder.getPendingRequests().size()); @@ -839,7 +846,7 @@ // ImsSmsDispatcher handles this text directly. verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), - eq(false), eq(10), eq(false), eq(1L), eq(false)); + eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong()); } @Test @@ -903,7 +910,7 @@ verify(newSmsDsc).finishSelection(); verify(mImsSmsDispatcher, times(2)).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), - eq(false), eq(10), eq(false), eq(1L), eq(false)); + eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong()); assertNull(holder.getConnection()); assertFalse(holder.isDomainSelectionRequested()); assertEquals(0, holder.getPendingRequests().size()); @@ -974,7 +981,7 @@ verify(newEmergencySmsDsc).finishSelection(); verify(mImsSmsDispatcher, times(2)).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), - eq(10), eq(false), eq(1L), eq(false)); + eq(10), eq(false), eq(1L), eq(false), anyLong()); assertNull(holder.getConnection()); assertFalse(holder.isDomainSelectionRequested()); assertEquals(0, holder.getPendingRequests().size()); @@ -1017,7 +1024,7 @@ verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), - eq(false), eq(10), eq(false), eq(1L), eq(false)); + eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong()); } @Test @@ -1060,7 +1067,7 @@ verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), eq(false), - eq(10), eq(false), eq(1L), eq(false)); + eq(10), eq(false), eq(1L), eq(false), anyLong()); } private void switchImsSmsFormat(int phoneType) { @@ -1079,7 +1086,8 @@ @Test public void testSendSmsToDatagramDispatcher() { - when(mSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true); + when(mSatelliteController.shouldSendSmsToDatagramDispatcher(any(Phone.class))) + .thenReturn(true); mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null, "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false); processAllMessages(); @@ -1219,13 +1227,13 @@ verify(mSmsDsc).finishSelection(); if (domain == NetworkRegistrationInfo.DOMAIN_PS) { verify(mImsSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"), - eq(8080), eq(data), eq(mSentIntent), any(), eq(false)); + eq(8080), eq(data), eq(mSentIntent), any(), eq(false), anyLong()); } else if (isCdmaMo) { verify(mCdmaSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"), - eq(8080), eq(data), eq(mSentIntent), any(), eq(false)); + eq(8080), eq(data), eq(mSentIntent), any(), eq(false), anyLong()); } else { verify(mGsmSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"), - eq(8080), eq(data), eq(mSentIntent), any(), eq(false)); + eq(8080), eq(data), eq(mSentIntent), any(), eq(false), anyLong()); } assertNull(holder.getConnection()); assertFalse(holder.isDomainSelectionRequested()); @@ -1256,15 +1264,15 @@ if (domain == NetworkRegistrationInfo.DOMAIN_PS) { verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10), - eq(false), eq(1L), eq(false)); + eq(false), eq(1L), eq(false), anyLong()); } else if (isCdmaMo) { verify(mCdmaSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10), - eq(false), eq(1L), eq(false)); + eq(false), eq(1L), eq(false), anyLong()); } else { verify(mGsmSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10), - eq(false), eq(1L), eq(false)); + eq(false), eq(1L), eq(false), anyLong()); } assertNull(holder.getConnection()); assertFalse(holder.isDomainSelectionRequested()); @@ -1298,16 +1306,16 @@ if (domain == NetworkRegistrationInfo.DOMAIN_PS) { verify(mImsSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts), eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId), - eq(false), eq(0), eq(false), eq(10), eq(1L)); + eq(false), eq(0), eq(false), eq(10), eq(1L), anyLong()); } else if (isCdmaMo) { verify(mCdmaSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts), eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), - eq(false), eq(10), eq(1L)); + eq(false), eq(10), eq(1L), anyLong()); } else { verify(mGsmSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts), eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId), - eq(false), eq(0), eq(false), eq(10), eq(1L)); + eq(false), eq(0), eq(false), eq(10), eq(1L), anyLong()); } assertNull(holder.getConnection()); assertFalse(holder.isDomainSelectionRequested()); @@ -1384,6 +1392,6 @@ SmsDispatchersController.PendingRequest.TYPE_TEXT, null, "test-app", mCallingUserId, "1111", "2222", asArrayList(mSentIntent), asArrayList(null), false, null, 0, asArrayList("text"), null, - false, 0, false, 10, 100L, false); + false, 0, false, 10, 100L, false, false); } }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java index dc1ee63..1846bae 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
@@ -16,13 +16,17 @@ package com.android.internal.telephony; -import android.telephony.TelephonyManager; -import android.test.AndroidTestCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; -import androidx.test.filters.SmallTest; +import android.telephony.TelephonyManager; import com.android.telephony.Rlog; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + /** * Test cases to verify selection of the optimal 7 bit encoding tables * (for all combinations of enabled national language tables) for messages @@ -33,7 +37,7 @@ * Tests both encoding variations: unsupported characters mapped to space, * and unsupported characters force entire message to UCS-2. */ -public class SmsMessageBodyTest extends AndroidTestCase { +public class SmsMessageBodyTest extends TelephonyTest { private static final String TAG = "SmsMessageBodyTest"; // ASCII chars in the GSM 7 bit default alphabet @@ -250,7 +254,12 @@ */ private static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6; - @SmallTest + @Before + public void setUp() { + TelephonyManager.setupISmsForTest(Mockito.mock(ISms.class)); + } + + @Test public void testCalcLengthAscii() throws Exception { StringBuilder sb = new StringBuilder(320); int[] values = {0, 0, 0, SmsConstants.ENCODING_7BIT, 0, 0}; @@ -282,7 +291,7 @@ } } - @SmallTest + @Test public void testCalcLengthUnicode() throws Exception { StringBuilder sb = new StringBuilder(160); int[] values = {0, 0, 0, SmsConstants.ENCODING_16BIT, 0, 0}; @@ -482,7 +491,7 @@ } } - //@LargeTest + //@Test /*public void testCalcLengthMixed7bit() throws Exception { StringBuilder sb = new StringBuilder(320); CounterHelper ch = new CounterHelper();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java index 3b637c9..1e1e43f 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
@@ -26,6 +26,8 @@ import android.os.Looper; +import com.android.internal.telephony.flags.FeatureFlagsImpl; + import org.junit.Ignore; /** @@ -465,7 +467,8 @@ if (Looper.myLooper() == null) { Looper.prepare(); } - SmsUsageMonitor monitor = new SmsUsageMonitor(TestApplication.getAppContext()); + SmsUsageMonitor monitor = new SmsUsageMonitor(TestApplication.getAppContext(), + new FeatureFlagsImpl()); for (ShortCodeTest test : sShortCodeTests) { assertEquals("country: " + test.countryIso + " number: " + test.address, test.category, monitor.checkDestination(test.address, test.countryIso));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java index 1e2b8ce..99ece85 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -48,7 +48,6 @@ @Before public void setUp() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE); mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG); mSubscriptionInfoUT = new SubscriptionInfo.Builder() .setId(1)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java index 9b3777b..5db1206 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java
@@ -112,26 +112,27 @@ when(mSST2.getLocaleTracker()).thenReturn(mMockLocaleTracker2); when(mMockLocaleTracker2.getCurrentCountry()).thenReturn(""); - when(mConnectivityManager.getActiveNetwork()).thenReturn(mMockNetwork); mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true); - when(mConnectivityManager.getNetworkCapabilities(any(Network.class))) - .thenReturn(mNetworkCapabilities); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true); when(mLocationManager.getProviders(true)).thenReturn(Arrays.asList("TEST_PROVIDER")); when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true); mCountryDetectorUT = new TestTelephonyCountryDetector( mLooper, mContext, mLocationManager, mConnectivityManager, mMockFeatureFlags); + verify(mConnectivityManager).registerNetworkCallback( + any(NetworkRequest.class), mNetworkCallbackCaptor.capture()); + mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities); + mTestableLooper.processAllMessages(); if (isGeoCoderImplemented()) { verify(mLocationManager).requestLocationUpdates(anyString(), anyLong(), anyFloat(), mLocationListenerCaptor.capture()); verify(mLocationManager).getProviders(true); verify(mLocationManager).getLastKnownLocation(anyString()); } - verify(mConnectivityManager).registerNetworkCallback( - any(NetworkRequest.class), mNetworkCallbackCaptor.capture()); } @After @@ -285,7 +286,7 @@ // Wi-fi is not available clearInvocations(mLocationManager); - mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false); + mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); mNetworkCallbackCaptor.getValue().onLost(mMockNetwork); mTestableLooper.processAllMessages(); verify(mLocationManager, never()).removeUpdates(any(LocationListener.class)); @@ -294,6 +295,9 @@ clearInvocations(mLocationManager); mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities); mTestableLooper.processAllMessages(); // Location updates were already requested verify(mLocationManager, never()).requestLocationUpdates(anyString(), anyLong(), anyFloat(), @@ -301,7 +305,10 @@ // Make Wi-fi not available and reset the quota clearInvocations(mLocationManager); - mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false); + mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, false); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities); mTestableLooper.moveTimeForward( TestTelephonyCountryDetector.getLocationUpdateRequestQuotaResetTimeoutMillis()); mTestableLooper.processAllMessages(); @@ -311,6 +318,9 @@ clearInvocations(mLocationManager); mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities); mTestableLooper.processAllMessages(); verify(mLocationManager).requestLocationUpdates(anyString(), anyLong(), anyFloat(), any(LocationListener.class)); @@ -324,16 +334,20 @@ verify(mLocationManager, never()).requestLocationUpdates(anyString(), anyLong(), anyFloat(), any(LocationListener.class)); - // Wi-fi becomes not available + // Wi-fi lost clearInvocations(mLocationManager); - mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false); - mNetworkCallbackCaptor.getValue().onUnavailable(); + mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); + mNetworkCallbackCaptor.getValue().onLost(mMockNetwork); mTestableLooper.processAllMessages(); verify(mLocationManager).removeUpdates(any(LocationListener.class)); } @Test public void testRegisterUnregisterForWifiConnectivityStateChanged() { + // Set Wi-Fi unavailable. + mNetworkCallbackCaptor.getValue().onLost(mMockNetwork); + mTestableLooper.processAllMessages(); + WifiConnectivityStateChangedListener listener = new WifiConnectivityStateChangedListener( mLooper); @@ -344,19 +358,63 @@ clearInvocations(mLocationManager); mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities); mTestableLooper.processAllMessages(); assertTrue(listener.getIsWifiConnected()); - // Wi-fi becomes not available + // Wi-fi lost clearInvocations(mLocationManager); - mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false); - mNetworkCallbackCaptor.getValue().onUnavailable(); + mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); + mNetworkCallbackCaptor.getValue().onLost(mMockNetwork); mTestableLooper.processAllMessages(); assertFalse(listener.getIsWifiConnected()); mCountryDetectorUT.unregisterForWifiConnectivityStateChanged(listener); } + @Test + public void testReflectWifiConnectedStatusChanged() { + // 1. Wi-Fi is turned off, network capability is not available. + mNetworkCallbackCaptor.getValue().onLost(mMockNetwork); + mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, false); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, false); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities); + mTestableLooper.processAllMessages(); + assertFalse(mCountryDetectorUT.isWifiNetworkConnected()); + + // 2. Wi-Fi is turned on, but network capability has not been updated. + mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork); + mTestableLooper.processAllMessages(); + assertFalse(mCountryDetectorUT.isWifiNetworkConnected()); + + // 3. Network capability has been updated, not some of them still false. + mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, false); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities); + mTestableLooper.processAllMessages(); + assertFalse(mCountryDetectorUT.isWifiNetworkConnected()); + + // 4. Network capability has been updated to validated. + mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true); + mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true); + mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities); + mTestableLooper.processAllMessages(); + assertTrue(mCountryDetectorUT.isWifiNetworkConnected()); + + // 5. Wi-Fi is turned off. + mNetworkCallbackCaptor.getValue().onLost(mMockNetwork); + mTestableLooper.processAllMessages(); + assertFalse(mCountryDetectorUT.isWifiNetworkConnected()); + } + private static boolean isGeoCoderImplemented() { return Geocoder.isPresent(); }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java index a645439..e5aa541 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -15,7 +15,12 @@ */ package com.android.internal.telephony; +import static android.telephony.CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI; +import static android.telephony.CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST; import static android.telephony.PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN; +import static android.telephony.SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_SIP; +import static android.telephony.SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2; +import static android.telephony.SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96; import static android.telephony.ServiceState.FREQUENCY_RANGE_LOW; import static android.telephony.SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -42,6 +47,7 @@ import android.content.pm.UserInfo; import android.net.LinkProperties; import android.os.Build; +import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -50,17 +56,20 @@ import android.telephony.AccessNetworkConstants; import android.telephony.Annotation; import android.telephony.BarringInfo; +import android.telephony.CallState; import android.telephony.CellIdentity; import android.telephony.CellIdentityGsm; import android.telephony.CellIdentityLte; import android.telephony.CellInfo; import android.telephony.CellInfoLte; import android.telephony.CellLocation; +import android.telephony.CellularIdentifierDisclosure; import android.telephony.LinkCapacityEstimate; import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseDataConnectionState; +import android.telephony.SecurityAlgorithmUpdate; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -68,6 +77,7 @@ import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; +import android.telephony.satellite.NtnSignalStrength; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.text.TextUtils; @@ -86,6 +96,7 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -103,6 +114,7 @@ // Mocked classes private SubscriptionInfo mMockSubInfo; private TelephonyRegistry.ConfigurationProvider mMockConfigurationProvider; + private IBinder mMockIBinder; private TelephonyCallbackWrapper mTelephonyCallback; private List<LinkCapacityEstimate> mLinkCapacityEstimateList; @@ -123,8 +135,14 @@ private CellIdentity mCellIdentityForRegiFail; private int mRegistrationFailReason; private Set<Integer> mSimultaneousCallingSubscriptions; + private int mCallbackModeStopReason = TelephonyManager.STOP_REASON_UNKNOWN; + private long mCallbackModeDurationMillis; private boolean mCarrierRoamingNtnMode; private boolean mCarrierRoamingNtnEligible; + private int[] mCarrierRoamingNtnAvailableServices; + private NtnSignalStrength mCarrierRoamingNtnSignalStrength; + private boolean mIsSatelliteEnabled; + private final List<List<CallState>> mCallStateList = new ArrayList<>(); // All events contribute to TelephonyRegistry#isPhoneStatePermissionRequired private static final Set<Integer> READ_PHONE_STATE_EVENTS; @@ -172,6 +190,10 @@ TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED); READ_PRIVILEGED_PHONE_STATE_EVENTS.add( TelephonyCallback.EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED); + READ_PRIVILEGED_PHONE_STATE_EVENTS.add( + TelephonyCallback.EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED); + READ_PRIVILEGED_PHONE_STATE_EVENTS.add( + TelephonyCallback.EVENT_SECURITY_ALGORITHMS_CHANGED); } // All events contribute to TelephonyRegistry#isActiveEmergencySessionPermissionRequired @@ -201,7 +223,11 @@ TelephonyCallback.RegistrationFailedListener, TelephonyCallback.DataActivityListener, TelephonyCallback.SimultaneousCellularCallingSupportListener, - TelephonyCallback.CarrierRoamingNtnModeListener { + TelephonyCallback.EmergencyCallbackModeListener, + TelephonyCallback.CarrierRoamingNtnModeListener, + TelephonyCallback.SecurityAlgorithmsListener, + TelephonyCallback.CellularIdentifierDisclosedListener, + TelephonyCallback.CallAttributesListener { // This class isn't mockable to get invocation counts because the IBinder is null and // crashes the TelephonyRegistry. Make a cheesy verify(times()) alternative. public AtomicInteger invocationCount = new AtomicInteger(0); @@ -298,6 +324,27 @@ } @Override + public void onCallbackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type, + @NonNull Duration timerDuration, int subId) { + invocationCount.incrementAndGet(); + mCallbackModeDurationMillis = timerDuration.toMillis(); + } + + @Override + public void onCallbackModeRestarted(@TelephonyManager.EmergencyCallbackModeType int type, + @NonNull Duration timerDuration, int subId) { + invocationCount.incrementAndGet(); + mCallbackModeDurationMillis = timerDuration.toMillis(); + } + + @Override + public void onCallbackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, + @TelephonyManager.EmergencyCallbackModeStopReason int reason, int subId) { + invocationCount.incrementAndGet(); + mCallbackModeStopReason = reason; + } + + @Override public void onCarrierRoamingNtnModeChanged(boolean active) { invocationCount.incrementAndGet(); mCarrierRoamingNtnMode = active; @@ -308,6 +355,46 @@ invocationCount.incrementAndGet(); mCarrierRoamingNtnEligible = eligible; } + + @Override + public void onCarrierRoamingNtnAvailableServicesChanged(int[] services) { + invocationCount.incrementAndGet(); + mCarrierRoamingNtnAvailableServices = services; + } + + @Override + public void onCarrierRoamingNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength) { + invocationCount.incrementAndGet(); + mCarrierRoamingNtnSignalStrength = ntnSignalStrength; + } + + @Override + public void onSecurityAlgorithmsChanged(SecurityAlgorithmUpdate update) { + invocationCount.incrementAndGet(); + } + + @Override + public void onCellularIdentifierDisclosedChanged(CellularIdentifierDisclosure disclosure) { + invocationCount.incrementAndGet(); + } + + @Override + public void onCallStatesChanged(List<CallState> callStateList) { + invocationCount.incrementAndGet(); + mCallStateList.add(callStateList); + } + } + + public class MySatelliteStateChangeListener implements ISatelliteStateChangeListener { + @Override + public void onSatelliteEnabledStateChanged(boolean isEnabled) throws RemoteException { + mIsSatelliteEnabled = isEnabled; + } + + @Override + public IBinder asBinder() { + return mMockIBinder; + } } private void addTelephonyRegistryService() { @@ -322,6 +409,7 @@ super.setUp(getClass().getSimpleName()); mMockSubInfo = mock(SubscriptionInfo.class); mMockConfigurationProvider = mock(TelephonyRegistry.ConfigurationProvider.class); + mMockIBinder = mock(IBinder.class); when(mMockConfigurationProvider.getRegistrationLimit()).thenReturn(-1); when(mMockConfigurationProvider.isRegistrationLimitEnabledInPlatformCompat(anyInt())) .thenReturn(false); @@ -349,6 +437,7 @@ processAllMessages(); assertEquals(mTelephonyRegistry.asBinder(), ServiceManager.getService("telephony.registry")); + doReturn(new int[]{1}).when(mSubscriptionManager).getActiveSubscriptionIdList(); } @After @@ -368,6 +457,7 @@ mPhysicalChannelConfigs = null; } mCellLocation = null; + mCallStateList.clear(); super.tearDown(); } @@ -981,6 +1071,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 +1169,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( @@ -1260,7 +1354,7 @@ TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_LTE, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, - false); + false, false, false); // Notify with invalid subId on default phone. Should NOT trigger callback. mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo); @@ -1287,11 +1381,11 @@ TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_LTE, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, - false); + false, false, false); TelephonyDisplayInfo expectDisplayInfo = new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_LTE, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE, - false); + false, false, false); // Notify with invalid subId on default phone. Should NOT trigger callback. mTelephonyRegistry.notifyDisplayInfoChanged(0, INVALID_SUBSCRIPTION_ID, displayInfo); @@ -1314,11 +1408,11 @@ TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_LTE, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, - false); + false, false, false); TelephonyDisplayInfo expectDisplayInfo = new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_LTE, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE, - false); + false, false, false); TelephonyCallback telephonyCallback2 = new TelephonyCallbackWrapper() { @Override public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfoNotify) { @@ -1589,6 +1683,57 @@ } @Test + @EnableFlags(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + public void testNotifyCallbackModeStarted() { + final long durationMillis = 1000; + int[] events = {TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED}; + + mTelephonyRegistry.listenWithEventList(false, false, 1/*subId*/, + mContext.getOpPackageName(), mContext.getAttributionTag(), + mTelephonyCallback.callback, events, true); + mTelephonyRegistry.notifyCallbackModeStarted(0/*phoneId*/, 1/*subId*/, + TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL, durationMillis); + processAllMessages(); + + assertEquals(1, mTelephonyCallback.invocationCount.get()); + assertEquals(durationMillis, mCallbackModeDurationMillis); + } + + @Test + @EnableFlags(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + public void testNotifyCallbackModeReStarted() { + final long durationMillis = 1000; + int[] events = {TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED}; + + mTelephonyRegistry.listenWithEventList(false, false, 1/*subId*/, + mContext.getOpPackageName(), mContext.getAttributionTag(), + mTelephonyCallback.callback, events, true); + mTelephonyRegistry.notifyCallbackModeRestarted(0/*phoneId*/, 1/*subId*/, + TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL, durationMillis); + processAllMessages(); + + assertEquals(1, mTelephonyCallback.invocationCount.get()); + assertEquals(durationMillis, mCallbackModeDurationMillis); + } + + @Test + @EnableFlags(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + public void testNotifyCallbackModeStopped() { + final int reason = TelephonyManager.STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED; + int[] events = {TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED}; + + mTelephonyRegistry.listenWithEventList(false, false, 1/*subId*/, + mContext.getOpPackageName(), mContext.getAttributionTag(), + mTelephonyCallback.callback, events, true); + mTelephonyRegistry.notifyCallbackModeStopped(0/*phoneId*/, 1/*subId*/, + TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL, reason); + processAllMessages(); + + assertEquals(1, mTelephonyCallback.invocationCount.get()); + assertEquals(reason, mCallbackModeStopReason); + } + + @Test public void testNotifyCarrierRoamingNtnModeChanged() { int subId = INVALID_SUBSCRIPTION_ID; doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt()); @@ -1618,4 +1763,186 @@ processAllMessages(); assertTrue(mCarrierRoamingNtnEligible); } + + @Test + @EnableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public void testNotifyCarrierRoamingNtnAvailableServicesChanged() { + int subId = INVALID_SUBSCRIPTION_ID; + doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt()); + doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex(); + int[] events = {TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED}; + + mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(), + mContext.getAttributionTag(), mTelephonyCallback.callback, events, true); + + int[] services = {3, 6}; + mTelephonyRegistry.notifyCarrierRoamingNtnAvailableServicesChanged(subId, services); + processAllMessages(); + assertTrue(Arrays.equals(mCarrierRoamingNtnAvailableServices, services)); + } + + @Test + @EnableFlags(Flags.FLAG_PASS_COPIED_CALL_STATE_LIST) + public void testNotifyPreciseCallStateChangedInProcess() { + doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt()); + doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex(); + + final int subId = 1; + int[] events = {TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED}; + + mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(), + mContext.getAttributionTag(), mTelephonyCallback.callback, events, false); + processAllMessages(); + + int[] callState = {0, 5, 0}; + String[] imsCallId = {"0", "1", "0"}; + int[] imsServiceType = {0, 1, 0}; + int[] imsCallType = {0, 1, 0}; + int[] callState2 = {0, 1, 0}; + mTelephonyRegistry.notifyPreciseCallState( + /*phoneId*/ 0, subId, callState, imsCallId, imsServiceType, imsCallType); + mTelephonyRegistry.notifyPreciseCallState( + /*phoneId*/ 0, subId, callState2, imsCallId, imsServiceType, imsCallType); + processAllMessages(); + + assertEquals(2, mCallStateList.size()); + //make sure the call state is from the first report(callState). + assertEquals(5, mCallStateList.get(0).getFirst().getCallState()); + //make sure the call state is from the second report(callState2). + assertEquals(1, mCallStateList.get(1).getFirst().getCallState()); + } + + @Test + @EnableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public void testNotifyCarrierRoamingNtnSignalStrengthChanged() { + int subId = INVALID_SUBSCRIPTION_ID; + doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt()); + doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex(); + int[] events = {TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED}; + + mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(), + mContext.getAttributionTag(), mTelephonyCallback.callback, events, true); + + mTelephonyRegistry.notifyCarrierRoamingNtnSignalStrengthChanged(subId, + new NtnSignalStrength(NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD)); + processAllMessages(); + assertEquals(mCarrierRoamingNtnSignalStrength.getLevel(), + NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD); + } + + @Test + @EnableFlags(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER) + public void testNotifySatelliteStateChanged_onRegistration_getNotified() { + MySatelliteStateChangeListener listener = new MySatelliteStateChangeListener(); + // Set initial satellite enabled state to true + mTelephonyRegistry.notifySatelliteStateChanged(true); + + try { + // Start monitoring + mTelephonyRegistry.addSatelliteStateChangeListener(listener, + mContext.getOpPackageName(), mContext.getAttributionTag()); + processAllMessages(); + + // verify latest state is immediately available on registration + assertTrue(mIsSatelliteEnabled); + } finally { + // Clean up + mTelephonyRegistry.removeSatelliteStateChangeListener(listener, + mContext.getOpPackageName()); + } + } + + @Test + @EnableFlags(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER) + public void testNotifySatelliteStateChanged_duringRegistration_getNotified() { + MySatelliteStateChangeListener listener = new MySatelliteStateChangeListener(); + // Set initial satellite enabled state to true + mTelephonyRegistry.notifySatelliteStateChanged(true); + + try { + // Start monitoring + mTelephonyRegistry.addSatelliteStateChangeListener(listener, + mContext.getOpPackageName(), mContext.getAttributionTag()); + + // Satellite enabled state changed + mTelephonyRegistry.notifySatelliteStateChanged(false); + processAllMessages(); + // We can receive the new state + assertFalse(mIsSatelliteEnabled); + } finally { + // Clean up + mTelephonyRegistry.removeSatelliteStateChangeListener(listener, + mContext.getOpPackageName()); + } + } + + @Test + @EnableFlags(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER) + public void testNotifySatelliteStateChanged_removeRegistration_notNotified() { + MySatelliteStateChangeListener listener = new MySatelliteStateChangeListener(); + // Set initial satellite enabled state to true + mTelephonyRegistry.notifySatelliteStateChanged(true); + + try { + // Start monitoring + mTelephonyRegistry.addSatelliteStateChangeListener(listener, + mContext.getOpPackageName(), mContext.getAttributionTag()); + mTelephonyRegistry.notifySatelliteStateChanged(false); + } finally { + // Stop monitoring from now on + mTelephonyRegistry.removeSatelliteStateChangeListener(listener, + mContext.getOpPackageName()); + } + + // Satellite enabled state changed again + mTelephonyRegistry.notifySatelliteStateChanged(true); + processAllMessages(); + // We should not receive the new state change after monitoring end + assertFalse(mIsSatelliteEnabled); + } + + + @Test + @EnableFlags(Flags.FLAG_SECURITY_ALGORITHMS_UPDATE_INDICATIONS) + public void testNotifySecurityAlgorithmsChanged() { + int subId = 1; + int[] events = {TelephonyCallback.EVENT_SECURITY_ALGORITHMS_CHANGED}; + + mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(), + mContext.getAttributionTag(), mTelephonyCallback.callback, events, false); + + SecurityAlgorithmUpdate update = + new SecurityAlgorithmUpdate( + CONNECTION_EVENT_VOLTE_SIP, SECURITY_ALGORITHM_EEA2, + SECURITY_ALGORITHM_HMAC_SHA1_96, false); + + mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(), + mContext.getAttributionTag(), mTelephonyCallback.callback, events, false); + int invocationCount = mTelephonyCallback.invocationCount.get(); + mTelephonyRegistry.notifySecurityAlgorithmsChanged(0, 1, update); + processAllMessages(); + assertEquals(invocationCount + 1, mTelephonyCallback.invocationCount.get()); + } + + @Test + @EnableFlags(Flags.FLAG_CELLULAR_IDENTIFIER_DISCLOSURE_INDICATIONS) + public void testNotifyCellularIdentifierDisclosedChanged() { + int subId = 1; + int[] events = {TelephonyCallback.EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED}; + + CellularIdentifierDisclosure disclosure = + new CellularIdentifierDisclosure(NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, + CELLULAR_IDENTIFIER_IMSI, + "001001", + false); + + mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(), + mContext.getAttributionTag(), mTelephonyCallback.callback, events, false); + int invocationCount = mTelephonyCallback.invocationCount.get(); + mTelephonyRegistry.notifyCellularIdentifierDisclosedChanged(0, 1, + disclosure); + processAllMessages(); + assertEquals(invocationCount + 1, mTelephonyCallback.invocationCount.get()); + } + }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java index d80c9a2..0db881c 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -747,6 +747,8 @@ doReturn(mDataRetryManager).when(mDataNetworkController).getDataRetryManager(); doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker(); doReturn(0).when(mPhone).getPhoneId(); + doReturn(true).when(mPhone).hasCalling(); + doReturn(true).when(mPhone2).hasCalling(); //mUiccController doReturn(mUiccCardApplication3gpp).when(mUiccController).getUiccCardApplication(anyInt(), @@ -809,7 +811,7 @@ doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState) .getRilDataRadioTechnology(); doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false)) + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false)) .when(mDisplayInfoController).getTelephonyDisplayInfo(); doReturn(mPhone).when(mCT).getPhone(); doReturn(mImsEcbm).when(mImsManager).getEcbmInterface();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java index 0e2676e..d3f3050 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -101,9 +101,11 @@ public void setUp() throws Exception { super.setUp(getClass().getSimpleName()); mGoodTelephonyDisplayInfo = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false /*roaming*/); + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false /*roaming*/, + false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/); mBadTelephonyDisplayInfo = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UMTS, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false /*roaming*/); + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false /*roaming*/, + false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/); mMockedPhoneSwitcherCallback = mock(AutoDataSwitchController.AutoDataSwitchControllerCallback.class); mMockedAlarmManager = mock(AlarmManager.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java index 95cefd8..88dce51 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
@@ -126,20 +126,23 @@ doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(signalStrength).getLevel(); assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_LTE, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false/*isRoaming*/), + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false/*isRoaming*/, + false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/), signalStrength)).isEqualTo(10227); // Verify if entry contains any invalid negative scores, should yield 0. doReturn(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN).when(signalStrength).getLevel(); assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_LTE, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/), + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/, + false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/), signalStrength)) .isEqualTo(0/*OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE*/); // Verify non-existent entry should yield -1 doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(signalStrength).getLevel(); assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_EDGE, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/), + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/, + false/*isNtn*/, false/*isSatelliteConstrainedDataStatus*/), signalStrength)) .isEqualTo(0/*OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE*/); }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java index ee713c6..3f2d6f2 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -888,11 +888,8 @@ doReturn(PhoneConstants.State.IDLE).when(mCT).getState(); doReturn(new SubscriptionInfoInternal.Builder().setId(1).build()) .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt()); - doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag(); doReturn(true).when(mFeatureFlags).satelliteInternet(); - doReturn(true).when(mFeatureFlags).simDisabledGracefulTearDown(); - when(mContext.getPackageManager()).thenReturn(mMockPackageManager); doReturn(true).when(mMockPackageManager).hasSystemFeature(anyString()); @@ -2481,7 +2478,7 @@ // Change data network type to NR doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false)) + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false)) .when(mDisplayInfoController).getTelephonyDisplayInfo(); dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/); processAllMessages(); @@ -2524,7 +2521,7 @@ // Change data network type to NR doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false)) + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false)) .when(mDisplayInfoController).getTelephonyDisplayInfo(); dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/); processAllMessages(); @@ -2580,7 +2577,7 @@ // Change data network type to NR doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_NR, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false)) + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false)) .when(mDisplayInfoController).getTelephonyDisplayInfo(); dataNetwork.sendMessage(13/*EVENT_DISPLAY_INFO_CHANGED*/); processAllMessages(); @@ -4311,7 +4308,7 @@ } @Test - public void testImsGracefulTearDownSimRemoval() throws Exception { + public void testImsGracefulTearDown() throws Exception { setImsRegistered(true); setRcsRegistered(true); @@ -4357,52 +4354,6 @@ } @Test - public void testImsGracefulTearDownSimDisabled() throws Exception { - setImsRegistered(true); - setRcsRegistered(true); - - NetworkCapabilities netCaps = new NetworkCapabilities(); - netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); - netCaps.maybeMarkCapabilitiesRestricted(); - netCaps.setRequestorPackageName(FAKE_MMTEL_PACKAGE); - - NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps, - ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId, NetworkRequest.Type.REQUEST); - TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest( - nativeNetworkRequest, mPhone, mFeatureFlags); - - mDataNetworkControllerUT.addNetworkRequest(networkRequest); - - processAllMessages(); - Mockito.clearInvocations(mPhone); - - // SIM disabled - mDataNetworkControllerUT.obtainMessage(9/*EVENT_SIM_STATE_CHANGED*/, - TelephonyManager.SIM_STATE_NOT_READY, 0).sendToTarget(); - processAllMessages(); - - // Make sure data network enters disconnecting state - ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor = - ArgumentCaptor.forClass(PreciseDataConnectionState.class); - verify(mPhone).notifyDataConnection(pdcsCaptor.capture()); - PreciseDataConnectionState pdcs = pdcsCaptor.getValue(); - assertThat(pdcs.getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTING); - - // IMS de-registered. Now data network is safe to be torn down. - Mockito.clearInvocations(mPhone); - setImsRegistered(false); - setRcsRegistered(false); - processAllMessages(); - - // All data should be disconnected. - verifyAllDataDisconnected(); - verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_IMS); - verify(mPhone).notifyDataConnection(pdcsCaptor.capture()); - pdcs = pdcsCaptor.getValue(); - assertThat(pdcs.getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTED); - } - - @Test public void testNoGracefulTearDownForEmergencyDataNetwork() throws Exception { setImsRegistered(true);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java index f7990b9..6e41448 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -146,7 +146,7 @@ private AutoDataSwitchController.AutoDataSwitchControllerCallback mAutoDataSwitchCallback; private TelephonyDisplayInfo mTelephonyDisplayInfo = new TelephonyDisplayInfo( TelephonyManager.NETWORK_TYPE_NR, - TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false); + TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false); private SubscriptionManagerServiceCallback mSubscriptionManagerServiceCallback; @Before
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java index 2f3fabf..ec7ad3c 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
@@ -16,6 +16,7 @@ package com.android.internal.telephony.domainselection; import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN; +import static android.telephony.AccessNetworkConstants.AccessNetworkType.UNKNOWN; import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN; import static android.telephony.DomainSelectionService.SCAN_TYPE_FULL_SERVICE; import static android.telephony.DomainSelectionService.SCAN_TYPE_NO_PREFERENCE; @@ -516,6 +517,131 @@ @Test @SmallTest + public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanAndErrorForScanTypeFull() + throws Exception { + mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true, + mDomainSelectionController); + + ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback(); + + assertNotNull(transportCallback); + + DomainSelectionService.SelectionAttributes attr = getSelectionAttributes( + mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true, + false, 0, TELECOM_CALL_ID1, null, null, null); + + mDsc.selectDomain(attr); + + IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class); + transportCallback.onCreated(domainSelector); + + IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback); + + assertNotNull(wwanCallback); + + int[] preferredNetworks = new int[] { EUTRAN, UTRAN }; + int scanType = SCAN_TYPE_FULL_SERVICE; + IWwanSelectorResultCallback resultCallback = + Mockito.mock(IWwanSelectorResultCallback.class); + + wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, + false, resultCallback); + processAllMessages(); + + ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); + ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class); + + verify(mPhone).registerForEmergencyNetworkScan(handlerCaptor.capture(), anyInt(), any()); + verify(mCi).registerForModemReset(any(), anyInt(), any()); + processAllMessages(); + + int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN }; + + verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks), + eq(scanType), msgCaptor.capture()); + + Handler handler = handlerCaptor.getValue(); + Message msg = msgCaptor.getValue(); + + assertNotNull(handler); + assertNotNull(msg); + + CommandException commandException = + new CommandException(CommandException.Error.INTERNAL_ERR); + handler.sendMessage(handler.obtainMessage(msg.what, + new AsyncResult((Integer) scanType, null, commandException))); + processAllMessages(); + + EmergencyRegistrationResult regResult = new EmergencyRegistrationResult(UNKNOWN, + NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN, + NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", ""); + verify(resultCallback).onComplete(eq(regResult)); + verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any()); + } + + @Test + @SmallTest + public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanAndErrorForScanTypeNoPref() + throws Exception { + mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true, + mDomainSelectionController); + + ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback(); + + assertNotNull(transportCallback); + + DomainSelectionService.SelectionAttributes attr = getSelectionAttributes( + mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true, + false, 0, TELECOM_CALL_ID1, null, null, null); + + mDsc.selectDomain(attr); + + IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class); + transportCallback.onCreated(domainSelector); + + IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback); + + assertNotNull(wwanCallback); + + int[] preferredNetworks = new int[] { EUTRAN, UTRAN }; + int scanType = SCAN_TYPE_NO_PREFERENCE; + IWwanSelectorResultCallback resultCallback = + Mockito.mock(IWwanSelectorResultCallback.class); + + wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, + false, resultCallback); + processAllMessages(); + + ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class); + ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class); + + verify(mPhone).registerForEmergencyNetworkScan(handlerCaptor.capture(), anyInt(), any()); + verify(mCi).registerForModemReset(any(), anyInt(), any()); + processAllMessages(); + + int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN }; + + verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks), + eq(scanType), msgCaptor.capture()); + + Handler handler = handlerCaptor.getValue(); + Message msg = msgCaptor.getValue(); + + assertNotNull(handler); + assertNotNull(msg); + + CommandException commandException = + new CommandException(CommandException.Error.INTERNAL_ERR); + handler.sendMessage(handler.obtainMessage(msg.what, + new AsyncResult((Integer) scanType, null, commandException))); + processAllMessages(); + + verify(resultCallback, times(0)).onComplete(any()); + verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any()); + } + + @Test + @SmallTest public void testModemResetOnRequestEmergencyNetworkDuringNetworkScan() throws Exception { mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java index 25e9715..9253fbf 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -21,11 +21,12 @@ import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS_PS; import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME; +import static android.telephony.TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL; +import static android.telephony.TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS; import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK; import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WLAN; import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN; -import static com.android.internal.telephony.emergency.EmergencyStateTracker.DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -36,6 +37,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyVararg; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -103,6 +105,7 @@ private static final String TEST_SMS_ID = "1111"; private static final String TEST_SMS_ID_2 = "2222"; private static final int TEST_ECM_EXIT_TIMEOUT_MS = 500; + private static final int TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS = 3000; private static final EmergencyRegistrationResult E_REG_RESULT = new EmergencyRegistrationResult( EUTRAN, REGISTRATION_STATE_HOME, DOMAIN_CS_PS, true, true, 0, 1, "001", "01", "US"); @@ -121,6 +124,7 @@ doReturn(TelephonyManager.SIM_STATE_READY) .when(mTelephonyManagerProxy).getSimState(anyInt()); + doReturn(true).when(mFeatureFlags).emergencyCallbackModeNotification(); } @After @@ -135,7 +139,8 @@ EmergencyStateTracker.getInstance(); }); - EmergencyStateTracker.make(mContext, true); + EmergencyStateTracker + .make(mContext, true, TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS, mFeatureFlags); assertNotNull(EmergencyStateTracker.getInstance()); } @@ -143,7 +148,8 @@ @Test @SmallTest public void getInstance_returnsSameInstance() { - EmergencyStateTracker.make(mContext, true); + EmergencyStateTracker + .make(mContext, true, TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS, mFeatureFlags); EmergencyStateTracker instance1 = EmergencyStateTracker.getInstance(); EmergencyStateTracker instance2 = EmergencyStateTracker.getInstance(); @@ -180,7 +186,7 @@ ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor .forClass(RadioOnStateListener.Callback.class); verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone), - eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS), eq(false)); + eq(false), eq(TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS)); // isOkToCall() should return true when IN_SERVICE state assertFalse(callback.getValue() .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false)); @@ -239,7 +245,7 @@ ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor .forClass(RadioOnStateListener.Callback.class); verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone), - eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS), eq(false)); + eq(false), eq(TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS)); // onTimeout should return true when radion on assertFalse(callback.getValue() .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false)); @@ -266,6 +272,7 @@ @Test @SmallTest public void startEmergencyCall_radioOff_turnOnRadioHangupCallTurnOffRadio() { + android.telecom.Connection testConnection = new android.telecom.Connection() {}; EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker( true /* isSuplDdsSwitchRequiredForEmergencyCall */); // Create test Phones and set radio off @@ -281,16 +288,17 @@ .build(); doReturn(nri).when(ss).getNetworkRegistrationInfo(anyInt(), anyInt()); CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone, - mTestConnection1, false); + testConnection, false); // startEmergencyCall should trigger radio on ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor .forClass(RadioOnStateListener.Callback.class); verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone), - eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS), eq(false)); + eq(false), eq(TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS)); // Hangup the call - emergencyStateTracker.endCall(mTestConnection1); + testConnection.setDisconnected(null); + emergencyStateTracker.endCall(testConnection); // onTimeout and isOkToCall should return true even in case radion is off assertTrue(callback.getValue() @@ -301,6 +309,7 @@ callback.getValue().onComplete(null, true); assertFalse(future.isDone()); + verify(testPhone).setRadioPower(true, false, false, true); } /** @@ -323,7 +332,7 @@ ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor .forClass(RadioOnStateListener.Callback.class); verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone), - eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS), eq(false)); + eq(false), eq(TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS)); // Verify future completes with DisconnectCause.POWER_OFF if radio not ready CompletableFuture<Void> unused = future.thenAccept((result) -> { assertEquals((Integer) result, (Integer) DisconnectCause.POWER_OFF); @@ -346,7 +355,7 @@ true /* isRadioOn */); when(mSST.isRadioOn()).thenReturn(true); // Satellite enabled - when(mSatelliteController.isSatelliteEnabled()).thenReturn(true); + when(mSatelliteController.isSatelliteEnabledOrBeingEnabled()).thenReturn(true); setConfigForDdsSwitch(testPhone, null, CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150"); @@ -359,11 +368,11 @@ ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor .forClass(RadioOnStateListener.Callback.class); verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone), - eq(false), eq(0), eq(false)); + eq(false), eq(0)); // isOkToCall() should return true once satellite modem is off assertFalse(callback.getValue() .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false)); - when(mSatelliteController.isSatelliteEnabled()).thenReturn(false); + when(mSatelliteController.isSatelliteEnabledOrBeingEnabled()).thenReturn(false); assertTrue(callback.getValue() .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false)); // Once radio on is complete, trigger delay dial @@ -391,7 +400,7 @@ Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */, true /* isRadioOn */); // Satellite enabled - when(mSatelliteController.isSatelliteEnabled()).thenReturn(true); + when(mSatelliteController.isSatelliteEnabledOrBeingEnabled()).thenReturn(true); CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone, mTestConnection1, false); @@ -400,7 +409,7 @@ ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor .forClass(RadioOnStateListener.Callback.class); verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone), - eq(false), eq(0), eq(false)); + eq(false), eq(0)); // Verify future completes with DisconnectCause.POWER_OFF if radio not ready CompletableFuture<Void> unused = future.thenAccept((result) -> { assertEquals((Integer) result, (Integer) DisconnectCause.SATELLITE_ENABLED); @@ -428,7 +437,7 @@ // Radio already on so shouldn't trigger this verify(mRadioOnHelper, never()).triggerRadioOnAndListen(any(), anyBoolean(), any(), - anyBoolean(), eq(0), eq(false)); + anyBoolean(), eq(0)); // Carrier supports control-plane fallback, so no DDS switch verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any()); } @@ -761,6 +770,8 @@ .getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, true)); // Verify emergency callback mode set on modem verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any()); + // Verify emergency callback mode started with the correct emergency type + verify(testPhone).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_CALL), anyLong()); } /** @@ -819,6 +830,9 @@ processAllFutureMessages(); + // Verify emergency callback mode stopped with the correct emergency type and reason. + verify(testPhone).stopEmergencyCallbackMode(eq(EmergencyStateTracker.EMERGENCY_TYPE_CALL), + eq(TelephonyManager.STOP_REASON_TIMER_EXPIRED)); // Verify exitEmergencyMode() is called after timeout verify(testPhone).exitEmergencyMode(any(Message.class)); assertFalse(emergencyStateTracker.isInEmergencyMode()); @@ -854,6 +868,9 @@ emergencyStateTracker.onCellularRadioPowerOffRequested(); + // Verify emergency callback mode stopped with the correct emergency type and reason. + verify(testPhone).stopEmergencyCallbackMode(eq(EmergencyStateTracker.EMERGENCY_TYPE_CALL), + eq(TelephonyManager.STOP_REASON_UNKNOWN)); // Verify exitEmergencyMode() is called. verify(testPhone).exitEmergencyMode(any(Message.class)); assertFalse(emergencyStateTracker.isInEcm()); @@ -1005,7 +1022,8 @@ assertTrue(emergencyStateTracker.isInEcm()); verify(phone0, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class)); verify(phone0, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); - + verify(phone0, times(1)).startEmergencyCallbackMode( + eq(EMERGENCY_CALLBACK_MODE_CALL), anyLong()); // Second emergency call started. CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0, mTestConnection2, false); @@ -1013,6 +1031,8 @@ assertFalse(future.isDone()); verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class)); verify(phone0, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); + verify(phone0, times(1)).startEmergencyCallbackMode( + eq(EMERGENCY_CALLBACK_MODE_CALL), anyLong()); } @Test @@ -1087,6 +1107,10 @@ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WLAN), any(Message.class)); verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); verify(phone0, never()).exitEmergencyMode(any(Message.class)); + verify(phone0, times(2)).startEmergencyCallbackMode( + eq(EMERGENCY_CALLBACK_MODE_CALL), anyLong()); + verify(phone0, times(1)).stopEmergencyCallbackMode( + eq(EMERGENCY_CALLBACK_MODE_CALL), anyInt()); } @Test @@ -1224,6 +1248,7 @@ verify(mCarrierConfigManager).getConfigForSubId(anyInt(), eq(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT)); verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); + verify(phone0).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_SMS), anyLong()); assertTrue(emergencyStateTracker.isInEmergencyMode()); assertTrue(emergencyStateTracker.isInScbm()); } @@ -1256,6 +1281,7 @@ verify(mCarrierConfigManager).getConfigForSubId(anyInt(), eq(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT)); verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); + verify(phone0).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_SMS), anyLong()); assertTrue(emergencyStateTracker.isInEmergencyMode()); assertTrue(emergencyStateTracker.isInScbm()); } @@ -1288,6 +1314,8 @@ verify(mCarrierConfigManager, times(2)).getConfigForSubId(anyInt(), eq(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT)); verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); + verify(phone0).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_SMS), anyLong()); + verify(phone0).restartEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_SMS), anyLong()); assertTrue(emergencyStateTracker.isInEmergencyMode()); assertTrue(emergencyStateTracker.isInScbm()); } @@ -1413,6 +1441,7 @@ processAllMessages(); verify(phone0, times(2)).setEmergencyMode(anyInt(), any(Message.class)); + verify(phone0).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_CALL), anyLong()); // Expect: DisconnectCause#NOT_DISCONNECTED. assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED), Integer.valueOf(DisconnectCause.NOT_DISCONNECTED)); @@ -1473,6 +1502,8 @@ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone1, TEST_SMS_ID_2, false); + verify(phone0).stopEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_SMS), + eq(TelephonyManager.STOP_REASON_EMERGENCY_SMS_SENT)); verify(phone0).exitEmergencyMode(any(Message.class)); // Waits for exiting emergency mode on other phone. assertFalse(future.isDone()); @@ -1726,6 +1757,7 @@ assertFalse(emergencyStateTracker.isInEmergencyCall()); assertTrue(emergencyStateTracker.isInScbm()); verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); + verify(phone0).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_SMS), anyLong()); } @Test @@ -2156,6 +2188,7 @@ processAllMessages(); verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); + verify(phone0).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_CALL), anyLong()); assertTrue(emergencyStateTracker.isInEmergencyMode()); assertTrue(emergencyStateTracker.isInEcm()); assertFalse(emergencyStateTracker.isInEmergencyCall()); @@ -2212,6 +2245,8 @@ // Enter emergency callback mode and emergency mode changed by SMS end. verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); + verify(phone0).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_CALL), anyLong()); + verify(phone0).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_SMS), anyLong()); assertTrue(emergencyStateTracker.isInEmergencyMode()); assertTrue(emergencyStateTracker.isInEcm()); assertFalse(emergencyStateTracker.isInEmergencyCall()); @@ -3168,7 +3203,7 @@ // Wait for the radio off for all phones verify(mSST, times(2)).registerForVoiceRegStateOrRatChanged(any(), anyInt(), any()); verify(mRadioOnHelper, never()).triggerRadioOnAndListen(any(), anyBoolean(), any(), - anyBoolean(), eq(0), eq(false)); + anyBoolean(), eq(0)); } /** @@ -3378,8 +3413,9 @@ doNothing().when(mPhoneSwitcher).overrideDefaultDataForEmergency( anyInt(), anyInt(), any()); return new EmergencyStateTracker(mContext, mTestableLooper.getLooper(), - isSuplDdsSwitchRequiredForEmergencyCall, mPhoneFactoryProxy, mPhoneSwitcherProxy, - mTelephonyManagerProxy, mRadioOnHelper, TEST_ECM_EXIT_TIMEOUT_MS); + isSuplDdsSwitchRequiredForEmergencyCall, TEST_WAIT_FOR_IN_SERVICE_TIMEOUT_MS, + mPhoneFactoryProxy, mPhoneSwitcherProxy, mTelephonyManagerProxy, mRadioOnHelper, + TEST_ECM_EXIT_TIMEOUT_MS, mFeatureFlags); } private Phone setupTestPhoneForEmergencyCall(boolean isRoaming, boolean isRadioOn) { @@ -3483,6 +3519,7 @@ eq(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT)); if (!emergencyStateTracker.isInEcm() && !emergencyStateTracker.isInEmergencyCall()) { verify(phone).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class)); + verify(phone).startEmergencyCallbackMode(eq(EMERGENCY_CALLBACK_MODE_SMS), anyLong()); } assertTrue(emergencyStateTracker.isInEmergencyMode()); assertTrue(emergencyStateTracker.isInScbm());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java index bb349a7..e9bff91 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
@@ -104,7 +104,7 @@ */ @Test public void testRegisterForSatelliteCallback() { - doReturn(true).when(mSatelliteController).isSatelliteEnabled(); + doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled(); mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0); waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS); @@ -229,7 +229,7 @@ @Test public void testTimeout_RetryFailure_WithSatellite() { - doReturn(true).when(mSatelliteController).isSatelliteEnabled(); + doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled(); ServiceState state = new ServiceState(); state.setState(ServiceState.STATE_POWER_OFF); when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java index 879118f..569785b 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -1906,6 +1906,8 @@ when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn( hasPrivileges); + when(mSubscriptionManager.canManageSubscriptionAsUser(eq(subInfo), eq(PACKAGE_NAME), any())) + .thenReturn(hasPrivileges); when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn( Collections.singletonList(subInfo)); } @@ -1943,8 +1945,12 @@ .build(); when(mSubscriptionManager.canManageSubscription(subInfo1, PACKAGE_NAME)).thenReturn( hasPrivileges); + when(mSubscriptionManager.canManageSubscriptionAsUser(eq(subInfo1), eq(PACKAGE_NAME), + any())).thenReturn(hasPrivileges); when(mSubscriptionManager.canManageSubscription(subInfo2, PACKAGE_NAME)).thenReturn( hasPrivileges); + when(mSubscriptionManager.canManageSubscriptionAsUser(eq(subInfo2), eq(PACKAGE_NAME), + any())).thenReturn(hasPrivileges); ArrayList<SubscriptionInfo> subInfos = new ArrayList<>(Arrays.asList(subInfo1, subInfo2)); when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subInfos); } @@ -1963,8 +1969,12 @@ .build(); when(mSubscriptionManager.canManageSubscription(subInfo1, PACKAGE_NAME)).thenReturn( false); + when(mSubscriptionManager.canManageSubscriptionAsUser(eq(subInfo1), eq(PACKAGE_NAME), + any())).thenReturn(false); when(mSubscriptionManager.canManageSubscription(subInfo2, PACKAGE_NAME)).thenReturn( true); + when(mSubscriptionManager.canManageSubscriptionAsUser(eq(subInfo2), eq(PACKAGE_NAME), + any())).thenReturn(true); ArrayList<SubscriptionInfo> subInfos = new ArrayList<>(Arrays.asList(subInfo1, subInfo2)); when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(subInfos); } @@ -1979,6 +1989,8 @@ .build(); when(mSubscriptionManager.canManageSubscription(subInfo, PACKAGE_NAME)).thenReturn( hasPrivileges); + when(mSubscriptionManager.canManageSubscriptionAsUser(eq(subInfo), eq(PACKAGE_NAME), any())) + .thenReturn(hasPrivileges); when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn( Collections.singletonList(subInfo)); }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java index f91088c..6e0d383 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java
@@ -103,6 +103,7 @@ private EuiccSession mEuiccSession; @Mock private ApduSender mApduSender; + @Mock private ApduSender mApduSender2; @Before public void setUp() throws Exception { @@ -114,6 +115,7 @@ public void startOneSession_featureDisabled_noop() throws Exception { mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.noteChannelOpen(mApduSender); + mEuiccSession.noteChannelOpen(mApduSender2); assertThat(mEuiccSession.hasSession()).isFalse(); @@ -121,6 +123,7 @@ assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender, never()).closeAnyOpenChannel(); + verify(mApduSender2, never()).closeAnyOpenChannel(); } @Test @@ -128,6 +131,7 @@ public void startOneSession_endSession_hasSession() throws Exception { mEuiccSession.startSession(SESSION_ID_1); mEuiccSession.noteChannelOpen(mApduSender); + mEuiccSession.noteChannelOpen(mApduSender2); assertThat(mEuiccSession.hasSession()).isTrue(); @@ -140,6 +144,7 @@ assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender).closeAnyOpenChannel(); + verify(mApduSender2).closeAnyOpenChannel(); } @Test @@ -164,7 +169,24 @@ @Test @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) - public void noteChannelOpen_noSession_noop() throws Exception { + public void startTwoSessions_endAllSessions_hasSession() throws Exception { + mEuiccSession.startSession(SESSION_ID_1); + mEuiccSession.noteChannelOpen(mApduSender); + mEuiccSession.startSession(SESSION_ID_2); + mEuiccSession.noteChannelOpen(mApduSender2); + + assertThat(mEuiccSession.hasSession()).isTrue(); + + mEuiccSession.endAllSessions(); + + assertThat(mEuiccSession.hasSession()).isFalse(); + verify(mApduSender).closeAnyOpenChannel(); + verify(mApduSender2).closeAnyOpenChannel(); + } + + @Test + @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) + public void noteChannelOpen_noSession_endSession_noop() throws Exception { // noteChannelOpen called without a session started mEuiccSession.noteChannelOpen(mApduSender); @@ -175,4 +197,18 @@ assertThat(mEuiccSession.hasSession()).isFalse(); verify(mApduSender, never()).closeAnyOpenChannel(); } + + @Test + @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER) + public void endAllSessions_noSession_endAllSessions_noOp() throws Exception { + // noteChannelOpen called without a session started + mEuiccSession.noteChannelOpen(mApduSender); + + assertThat(mEuiccSession.hasSession()).isFalse(); + + mEuiccSession.endAllSessions(); + + assertThat(mEuiccSession.hasSession()).isFalse(); + verify(mApduSender, never()).closeAnyOpenChannel(); + } }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java index 8a10fde..a29de0f 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -70,6 +70,7 @@ import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.TelephonyTestUtils; import com.android.internal.telephony.TestApplication; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.IsimUiccRecords; @@ -313,7 +314,7 @@ // send SMS and check sentIntent mReceivedTestIntent = false; mGsmSmsDispatcher.sendMultipartText("+123" /*destAddr*/, "222" /*scAddr*/, parts, - sentIntents, null, null, null, mCallingUserId, false, -1, false, -1, 0L); + sentIntents, null, null, null, mCallingUserId, false, -1, false, -1, 0L, 0L); waitForMs(500); synchronized (mLock) { @@ -377,23 +378,35 @@ any(ICarrierMessagingCallback.class)); } - @Test - @SmallTest - @Ignore("b/256282780") - public void testSendSmsByCarrierApp() throws Exception { + private int sendSmsWithCarrierAppResponse(int carrierAppResultCode) throws Exception { mockCarrierApp(); - mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK, - mICarrierAppMessagingService, true); + mockCarrierAppStubResults(carrierAppResultCode, mICarrierAppMessagingService, true); registerTestIntentReceiver(); - PendingIntent pendingIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0, - new Intent(TEST_INTENT) - .setPackage(TestApplication.getAppContext().getPackageName()), - PendingIntent.FLAG_MUTABLE); + PendingIntent pendingIntent = + PendingIntent.getBroadcast( + TestApplication.getAppContext(), + 0, + new Intent(TEST_INTENT) + .setPackage(TestApplication.getAppContext().getPackageName()), + PendingIntent.FLAG_MUTABLE); mReceivedTestIntent = false; - mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms", - pendingIntent, null, null, null, mCallingUserId, false, -1, false, -1, false, 0L); + mGsmSmsDispatcher.sendText( + "6501002000", + "121" /*scAddr*/, + "test sms", + pendingIntent, + null, + null, + null, + mCallingUserId, + false, + -1, + false, + -1, + false, + 0L); processAllMessages(); synchronized (mLock) { if (!mReceivedTestIntent) { @@ -402,15 +415,48 @@ } assertEquals(true, mReceivedTestIntent); int resultCode = mTestReceiver.getResultCode(); - assertTrue("Unexpected result code: " + resultCode, - resultCode == SmsManager.RESULT_ERROR_NONE || resultCode == Activity.RESULT_OK); - verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(), - any(Message.class)); + verify(mSimulatedCommandsVerifier, times(0)) + .sendSMS(anyString(), anyString(), any(Message.class)); + return resultCode; } } @Test @SmallTest + @Ignore("b/256282780") + public void testSendSmsByCarrierApp() throws Exception { + int resultCode = sendSmsWithCarrierAppResponse(CarrierMessagingService.SEND_STATUS_OK); + assertTrue( + "Unexpected result code: " + resultCode, + resultCode == SmsManager.RESULT_ERROR_NONE || resultCode == Activity.RESULT_OK); + } + + @Test + @SmallTest + public void testSendSmsByCarrierApp_PermanentFailure() throws Exception { + int resultCode = sendSmsWithCarrierAppResponse(CarrierMessagingService.SEND_STATUS_ERROR); + assertTrue( + "Unexpected result code: " + resultCode, + resultCode == SmsManager.RESULT_RIL_GENERIC_ERROR); + } + + @Test + @SmallTest + public void testSendSmsByCarrierApp_FailureWithReason() throws Exception { + if (!Flags.temporaryFailuresInCarrierMessagingService()) { + return; + } + doReturn(true).when(mFeatureFlags).temporaryFailuresInCarrierMessagingService(); + int resultCode = + sendSmsWithCarrierAppResponse( + CarrierMessagingService.SEND_STATUS_RESULT_ERROR_NO_SERVICE); + assertTrue( + "Unexpected result code: " + resultCode, + resultCode == SmsManager.RESULT_ERROR_NO_SERVICE); + } + + @Test + @SmallTest public void testSendSmsByCarrierAppNoResponse() throws Exception { mockCarrierApp(); // do not mock result, instead reduce the timeout for test @@ -456,7 +502,7 @@ mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts, withSentIntents ? sentIntents : null, null, null, null, mCallingUserId, - false, -1, false, -1, 0L); + false, -1, false, -1, 0L, 0L); } @Test @@ -549,7 +595,7 @@ messageRef += parts.size(); } mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts, - null, null, null, null, mCallingUserId, false, -1, false, -1, 0L); + null, null, null, null, mCallingUserId, false, -1, false, -1, 0L, 0L); waitForMs(150); ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java index 4abf33f..130fba8 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -46,6 +47,7 @@ import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; +import android.os.UserHandle; import android.os.UserManager; import android.telephony.CarrierConfigManager; import android.telephony.TelephonyManager; @@ -69,8 +71,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.stubbing.Answer; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -95,13 +99,14 @@ "TestCarrier2Pkg", "Carrier2ImsService"); private static final int NUM_MAX_SLOTS = 2; - private static final String TAG = ImsResolverTest.class.getSimpleName(); + private static final UserHandle TEST_USER_HANDLE = UserHandle.of(Integer.MAX_VALUE); // Mocked classes Context mMockContext; PackageManager mMockPM; ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy; ImsResolver.TelephonyManagerProxy mTestTelephonyManagerProxy; + ImsResolver.ActivityManagerProxy mTestActivityManagerProxy; CarrierConfigManager mMockCarrierConfigManager; UserManager mMockUserManager; ImsResolver.ImsDynamicQueryManagerFactory mMockQueryManagerFactory; @@ -112,6 +117,7 @@ private BroadcastReceiver mTestPackageBroadcastReceiver; private BroadcastReceiver mTestCarrierConfigReceiver; private BroadcastReceiver mTestBootCompleteReceiver; + private BroadcastReceiver mTestUserChangedReceiver; private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener; private PersistableBundle[] mCarrierConfigs; private FeatureFlags mFeatureFlags; @@ -124,12 +130,14 @@ mMockPM = mock(PackageManager.class); mTestSubscriptionManagerProxy = mock(ImsResolver.SubscriptionManagerProxy.class); mTestTelephonyManagerProxy = mock(ImsResolver.TelephonyManagerProxy.class); + mTestActivityManagerProxy = mock(ImsResolver.ActivityManagerProxy.class); mMockCarrierConfigManager = mock(CarrierConfigManager.class); mMockUserManager = mock(UserManager.class); mMockQueryManagerFactory = mock(ImsResolver.ImsDynamicQueryManagerFactory.class); mMockQueryManager = mock(ImsServiceFeatureQueryManager.class); mMockRepo = mock(ImsFeatureBinderRepository.class); mFeatureFlags = mock(FeatureFlags.class); + when(mFeatureFlags.imsResolverUserAware()).thenReturn(true); } @After @@ -411,7 +419,7 @@ ArgumentCaptor<SparseIntArray> arrayCaptor = ArgumentCaptor.forClass(SparseIntArray.class); - verify(controller).bind(eq(features), arrayCaptor.capture()); + verify(controller).bind(eq(mContext.getUser()), eq(features), arrayCaptor.capture()); SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue(); SparseIntArray compareMap = new SparseIntArray(); compareMap.put(0, 0); @@ -469,11 +477,14 @@ when(mMockQueryManager.isQueryInProgress()).thenReturn(false); setupDynamicQueryFeatures(TEST_CARRIER_2_DEFAULT_NAME, featuresAll, 1); - verify(deviceController).bind(eq(featuresDevice), any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), eq(featuresDevice), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); - verify(carrierController1).bind(eq(featuresMmTel), any(SparseIntArray.class)); + verify(carrierController1).bind(eq(mContext.getUser()), eq(featuresMmTel), + any(SparseIntArray.class)); verify(carrierController1, never()).unbind(); - verify(carrierController2).bind(eq(featuresRcs), any(SparseIntArray.class)); + verify(carrierController2).bind(eq(mContext.getUser()), eq(featuresRcs), + any(SparseIntArray.class)); verify(carrierController2, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController1.getComponentName()); assertEquals(TEST_CARRIER_2_DEFAULT_NAME, carrierController2.getComponentName()); @@ -521,11 +532,14 @@ when(mMockQueryManager.isQueryInProgress()).thenReturn(false); setupDynamicQueryFeatures(TEST_CARRIER_2_DEFAULT_NAME, allFeatures, 1); - verify(deviceController, never()).bind(any(), any(SparseIntArray.class)); + verify(deviceController, never()).bind(eq(mContext.getUser()), any(), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); - verify(carrierController1).bind(eq(featuresMmTel), any(SparseIntArray.class)); + verify(carrierController1).bind(eq(mContext.getUser()), eq(featuresMmTel), + any(SparseIntArray.class)); verify(carrierController1, never()).unbind(); - verify(carrierController2).bind(eq(featuresRcs), any(SparseIntArray.class)); + verify(carrierController2).bind(eq(mContext.getUser()), eq(featuresRcs), + any(SparseIntArray.class)); verify(carrierController2, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController1.getComponentName()); assertEquals(TEST_CARRIER_2_DEFAULT_NAME, carrierController2.getComponentName()); @@ -553,7 +567,7 @@ startBindCarrierConfigAlreadySet(); setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1); - verify(controller).bind(eq(features), any(SparseIntArray.class)); + verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class)); verify(controller, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName()); } @@ -582,7 +596,7 @@ // We will not bind with FEATURE_EMERGENCY_MMTEL features.remove(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_EMERGENCY_MMTEL)); - verify(controller).bind(eq(features), any(SparseIntArray.class)); + verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class)); verify(controller, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName()); } @@ -606,20 +620,23 @@ setupPackageQuery(info); ImsServiceController deviceController1 = mock(ImsServiceController.class); ImsServiceController deviceController2 = mock(ImsServiceController.class); - setImsServiceControllerFactory(deviceController1, deviceController2, null, null); + setImsServiceControllerDDCFactory(deviceController1, deviceController2, null); // Bind using default features startBindNoCarrierConfig(1); HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet1 = convertToHashSet(featuresController1, 0); HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 = convertToHashSet(featuresController2, 0); - verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class)); - verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class)); + verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1), + any(SparseIntArray.class)); + verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2), + any(SparseIntArray.class)); // simulate ImsServiceController binding and setup - mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_EMERGENCY_MMTEL, + mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_EMERGENCY_MMTEL, deviceController1); - mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController1); - mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, deviceController2); + mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_MMTEL, + deviceController1); + mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_RCS, deviceController2); mTestImsResolver.enableIms(0 /*slotId*/); // Verify enableIms is only called once per controller. @@ -651,7 +668,7 @@ // Bind without emergency calling startBindCarrierConfigAlreadySet(); setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1); - verify(controller).bind(eq(features), any(SparseIntArray.class)); + verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class)); verify(controller, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName()); @@ -686,8 +703,8 @@ startBindCarrierConfigAlreadySet(); processAllMessages(); - verify(mMockQueryManager, never()).startQuery(any(), any()); - verify(controller, never()).bind(any(), any(SparseIntArray.class)); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); + verify(controller, never()).bind(any(), any(), any(SparseIntArray.class)); verify(controller, never()).unbind(); } @@ -719,7 +736,7 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0); ArgumentCaptor<SparseIntArray> arrayCaptor = ArgumentCaptor.forClass(SparseIntArray.class); - verify(controller).bind(eq(featureSet), arrayCaptor.capture()); + verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture()); SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue(); SparseIntArray compareMap = new SparseIntArray(); compareMap.put(0, 0); @@ -729,7 +746,7 @@ } verify(controller, never()).unbind(); - verify(mMockQueryManager, never()).startQuery(any(), any()); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName()); } @@ -763,7 +780,7 @@ featureSet.addAll(convertToHashSet(features, 1)); ArgumentCaptor<SparseIntArray> arrayCaptor = ArgumentCaptor.forClass(SparseIntArray.class); - verify(controller).bind(eq(featureSet), arrayCaptor.capture()); + verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture()); SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue(); assertEquals(slotIdToSubIdMap.size(), 2); SparseIntArray compareMap = new SparseIntArray(); @@ -774,7 +791,7 @@ assertEquals(slotIdToSubIdMap.get(i), compareMap.get(i)); } verify(controller, never()).unbind(); - verify(mMockQueryManager, never()).startQuery(any(), any()); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName()); // Change number of SIMs and verify the features in the ImsServiceController are changed @@ -823,7 +840,7 @@ ArgumentCaptor<SparseIntArray> arrayCaptor = ArgumentCaptor.forClass(SparseIntArray.class); - verify(controller).bind(eq(featureSet), arrayCaptor.capture()); + verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture()); SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue(); assertEquals(slotIdToSubIdMap.size(), 1); SparseIntArray compareMap = new SparseIntArray(); @@ -833,7 +850,7 @@ assertEquals(slotIdToSubIdMap.get(i), compareMap.get(i)); } verify(controller, never()).unbind(); - verify(mMockQueryManager, never()).startQuery(any(), any()); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName()); // Change number of SIMs and verify the features in the ImsServiceController are changed @@ -895,17 +912,19 @@ processAllMessages(); // ensure that startQuery was called verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE_DEFAULT_NAME), - any(String.class)); + any(UserHandle.class), any(String.class)); verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE2_DEFAULT_NAME), - any(String.class)); + any(UserHandle.class), any(String.class)); mDynamicQueryListener.onComplete(TEST_DEVICE_DEFAULT_NAME, deviceFeatures1); mDynamicQueryListener.onComplete(TEST_DEVICE2_DEFAULT_NAME, deviceFeatures2); processAllMessages(); - verify(deviceController, times(2)).bind(eq(deviceFeatures1), any(SparseIntArray.class)); - verify(deviceController2, times(1)).bind(eq(deviceFeatures2), any(SparseIntArray.class)); + verify(deviceController, times(2)).bind(eq(mContext.getUser()), eq(deviceFeatures1), + any(SparseIntArray.class)); + verify(deviceController2, times(1)).bind(eq(mContext.getUser()), eq(deviceFeatures2), + any(SparseIntArray.class)); } /** @@ -942,7 +961,8 @@ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that all features that have been defined for the carrier override are bound - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); verify(carrierController, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName()); // Verify that all features that are not defined in the carrier override are bound in the @@ -950,7 +970,8 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet = convertToHashSet(deviceFeatures, 0); deviceFeatureSet.removeAll(carrierFeatures); - verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName()); @@ -1006,7 +1027,8 @@ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that all features that have been defined for the carrier override are bound - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); verify(carrierController, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName()); // Verify that all features that are not defined in the carrier override are bound in the @@ -1015,7 +1037,8 @@ convertToHashSet(deviceFeatures, 0); deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 1)); deviceFeatureSet.removeAll(carrierFeatures); - verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName()); @@ -1062,9 +1085,9 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0); // There is no carrier override set, so make sure that the ImsServiceController binds // to all SIMs. - verify(controller).bind(eq(featureSet), any(SparseIntArray.class)); + verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class)); verify(controller, never()).unbind(); - verify(mMockQueryManager, never()).startQuery(any(), any()); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName()); } @@ -1100,7 +1123,8 @@ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that all features that have been defined for the carrier override are bound - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); verify(carrierController, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName()); // Verify that all features that are not defined in the carrier override are bound in the @@ -1108,7 +1132,8 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet = convertToHashSet(deviceFeatures, 0); deviceFeatureSet.removeAll(carrierFeatures); - verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName()); } @@ -1127,11 +1152,11 @@ // Callback from mock ImsServiceControllers // All features on slot 1 should be the device default - mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_MMTEL, deviceController); - mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_RCS, deviceController); - mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController); + mTestImsResolver.imsServiceFeatureCreated(1, 1, ImsFeature.FEATURE_MMTEL, deviceController); + mTestImsResolver.imsServiceFeatureCreated(1, 1, ImsFeature.FEATURE_RCS, deviceController); + mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_MMTEL, deviceController); // The carrier override contains this feature - mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, carrierController); + mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_RCS, carrierController); } /** @@ -1155,7 +1180,7 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0); featureSet.addAll(convertToHashSet(features, 1)); - verify(controller).bind(eq(featureSet), any(SparseIntArray.class)); + verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class)); // add RCS to features list Set<String> newFeatures = new HashSet<>(features); @@ -1191,7 +1216,7 @@ setupPackageQuery(info); ImsServiceController deviceController1 = mock(ImsServiceController.class); ImsServiceController deviceController2 = mock(ImsServiceController.class); - setImsServiceControllerFactory(deviceController1, deviceController2, null, null); + setImsServiceControllerDDCFactory(deviceController1, deviceController2, null); // Bind using default features startBindNoCarrierConfig(2); HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet1 = @@ -1200,8 +1225,10 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 = convertToHashSet(featuresController2, 0); featureSet2.addAll(convertToHashSet(featuresController2, 1)); - verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class)); - verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class)); + verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1), + any(SparseIntArray.class)); + verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2), + any(SparseIntArray.class)); // add RCS to features list for device 1 Set<String> newFeatures1 = new HashSet<>(featuresController1); @@ -1239,7 +1266,7 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0); featureSet.addAll(convertToHashSet(features, 1)); - verify(controller).bind(eq(featureSet), any(SparseIntArray.class)); + verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class)); // add RCS to features list Set<String> newFeatures = new HashSet<>(features); @@ -1288,7 +1315,8 @@ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that all features that have been defined for the carrier override are bound - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); verify(carrierController, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName()); // Verify that all features that are not defined in the carrier override are bound in the @@ -1297,7 +1325,8 @@ convertToHashSet(deviceFeatures, 1); deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0)); deviceFeatureSet.removeAll(carrierFeatures); - verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName()); @@ -1356,13 +1385,13 @@ ImsServiceController deviceController1 = mock(ImsServiceController.class); ImsServiceController deviceController2 = mock(ImsServiceController.class); ImsServiceController carrierController = mock(ImsServiceController.class); - setImsServiceControllerFactory(deviceController1, deviceController2, carrierController, - null); + setImsServiceControllerDDCFactory(deviceController1, deviceController2, carrierController); startBindCarrierConfigAlreadySet(); setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that all features that have been defined for the carrier override are bound - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); verify(carrierController, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName()); // Verify that all features that are not defined in the carrier override are bound in the @@ -1370,13 +1399,16 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet1 = convertToHashSet(deviceFeatures1, 1); deviceFeatureSet1.removeAll(carrierFeatures); - verify(deviceController1).bind(eq(deviceFeatureSet1), any(SparseIntArray.class)); + verify(deviceController1).bind(eq(mContext.getUser()), eq(deviceFeatureSet1), + any(SparseIntArray.class)); verify(deviceController1, never()).unbind(); HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet2 = convertToHashSet(deviceFeatures2, 0); deviceFeatureSet2.addAll(convertToHashSet(deviceFeatures2, 1)); + deviceFeatureSet2.addAll(convertToHashSet(deviceFeatures2, 1)); deviceFeatureSet2.removeAll(carrierFeatures); - verify(deviceController2).bind(eq(deviceFeatureSet2), any(SparseIntArray.class)); + verify(deviceController2).bind(eq(mContext.getUser()), eq(deviceFeatureSet2), + any(SparseIntArray.class)); verify(deviceController2, never()).unbind(); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName()); assertEquals(TEST_DEVICE2_DEFAULT_NAME, deviceController2.getComponentName()); @@ -1392,7 +1424,7 @@ verify(carrierController).changeImsServiceFeatures(eq(carrierFeatures), any(SparseIntArray.class)); deviceFeatureSet1.removeAll(carrierFeatures); - verify(deviceController1, times(2)).changeImsServiceFeatures(eq(deviceFeatureSet1), + verify(deviceController1).changeImsServiceFeatures(eq(deviceFeatureSet1), any(SparseIntArray.class)); deviceFeatureSet2.removeAll(carrierFeatures); verify(deviceController2).changeImsServiceFeatures(eq(deviceFeatureSet2), @@ -1429,7 +1461,8 @@ startBindCarrierConfigAlreadySet(); setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that all features that have been defined for the carrier override are bound - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); verify(carrierController, never()).unbind(); assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName()); // Verify that all features that are not defined in the carrier override are bound in the @@ -1438,7 +1471,8 @@ convertToHashSet(deviceFeatures, 1); deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0)); deviceFeatureSet.removeAll(carrierFeatures); - verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName()); @@ -1497,14 +1531,20 @@ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that all features that have been defined for the carrier override are bound - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); // device features change HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet = convertToHashSet(deviceFeatures, 1); deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0)); deviceFeatureSet.removeAll(carrierFeatures); - verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet), - any(SparseIntArray.class)); + if (mFeatureFlags.imsResolverUserAware()) { + verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet), + any(SparseIntArray.class)); + } else { + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); + } } /** @@ -1644,7 +1684,8 @@ assertNotNull(mTestImsResolver.getImsServiceInfoFromCache( TEST_CARRIER_DEFAULT_NAME.getPackageName())); // Verify that carrier 2 is bound - verify(carrierController2).bind(eq(carrierFeatures2), any(SparseIntArray.class)); + verify(carrierController2).bind(eq(mContext.getUser()), eq(carrierFeatures2), + any(SparseIntArray.class)); assertNotNull(mTestImsResolver.getImsServiceInfoFromCache( TEST_CARRIER_2_DEFAULT_NAME.getPackageName())); // device features change to accommodate for the features carrier 2 lacks @@ -1692,7 +1733,8 @@ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that all features that have been defined for the carrier override are bound - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); // device features change HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet = convertToHashSet(deviceFeatures, 1); @@ -1733,13 +1775,15 @@ setupDynamicQueryFeaturesFailure(TEST_CARRIER_DEFAULT_NAME, 1); // Verify that a bind never occurs for the carrier controller. - verify(carrierController, never()).bind(any(), any(SparseIntArray.class)); + verify(carrierController, never()).bind(eq(mContext.getUser()), any(), + any(SparseIntArray.class)); verify(carrierController, never()).unbind(); // Verify that all features are used to bind to the device ImsService since the carrier // ImsService failed to bind properly. HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet = convertToHashSet(deviceFeatures, 0); - verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName()); } @@ -1774,18 +1818,21 @@ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); // Verify that a bind never occurs for the carrier controller. - verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class)); + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); verify(carrierController, never()).unbind(); // Verify that all features that are not defined in the carrier override are bound in the // device controller (including emergency voice for slot 0) HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet = convertToHashSet(deviceFeatures, 0); deviceFeatureSet.removeAll(carrierFeatures); - verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); verify(deviceController, never()).unbind(); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName()); - mTestImsResolver.imsServiceBindPermanentError(TEST_CARRIER_DEFAULT_NAME); + mTestImsResolver.imsServiceBindPermanentError(TEST_CARRIER_DEFAULT_NAME, + mContext.getUser()); processAllMessages(); verify(carrierController).unbind(); // Verify that the device ImsService features are changed to include the ones previously @@ -1815,7 +1862,7 @@ setupPackageQuery(info); ImsServiceController deviceController1 = mock(ImsServiceController.class); ImsServiceController deviceController2 = mock(ImsServiceController.class); - setImsServiceControllerFactory(deviceController1, deviceController2, null, null); + setImsServiceControllerDDCFactory(deviceController1, deviceController2, null); startBindNoCarrierConfig(1); processAllMessages(); @@ -1825,11 +1872,12 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureResultSet = convertToHashSet(featureResult, 0); featureResultSet.addAll(convertToHashSet(featureResult, 1)); - verify(deviceController1).bind(eq(featureResultSet), any(SparseIntArray.class)); + verify(deviceController1).bind(eq(mContext.getUser()), eq(featureResultSet), + any(SparseIntArray.class)); verify(deviceController1, never()).unbind(); - verify(deviceController2, never()).bind(any(), any(SparseIntArray.class)); + verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class)); verify(deviceController2, never()).unbind(); - verify(mMockQueryManager, never()).startQuery(any(), any()); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName()); } @@ -1852,7 +1900,7 @@ setupPackageQuery(info); ImsServiceController deviceController1 = mock(ImsServiceController.class); ImsServiceController deviceController2 = mock(ImsServiceController.class); - setImsServiceControllerFactory(deviceController1, deviceController2, null, null); + setImsServiceControllerDDCFactory(deviceController1, deviceController2, null); startBindNoCarrierConfig(1); processAllMessages(); @@ -1862,11 +1910,12 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureResultSet = convertToHashSet(featureResult, 0); featureResultSet.addAll(convertToHashSet(featureResult, 1)); - verify(deviceController1).bind(eq(featureResultSet), any(SparseIntArray.class)); + verify(deviceController1).bind(eq(mContext.getUser()), eq(featureResultSet), + any(SparseIntArray.class)); verify(deviceController1, never()).unbind(); - verify(deviceController2, never()).bind(any(), any(SparseIntArray.class)); + verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class)); verify(deviceController2, never()).unbind(); - verify(mMockQueryManager, never()).startQuery(any(), any()); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName()); } @@ -1893,7 +1942,7 @@ setupPackageQuery(info); ImsServiceController deviceController1 = mock(ImsServiceController.class); ImsServiceController deviceController2 = mock(ImsServiceController.class); - setImsServiceControllerFactory(deviceController1, deviceController2, null, null); + setImsServiceControllerDDCFactory(deviceController1, deviceController2, null); startBindNoCarrierConfig(1); processAllMessages(); @@ -1904,11 +1953,13 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 = convertToHashSet(features2, 0); featureSet2.addAll(convertToHashSet(features2, 1)); - verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class)); + verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1), + any(SparseIntArray.class)); verify(deviceController1, never()).unbind(); - verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class)); + verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2), + any(SparseIntArray.class)); verify(deviceController2, never()).unbind(); - verify(mMockQueryManager, never()).startQuery(any(), any()); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName()); assertEquals(TEST_DEVICE2_DEFAULT_NAME, deviceController2.getComponentName()); } @@ -1934,16 +1985,134 @@ setupPackageQuery(info); ImsServiceController deviceController1 = mock(ImsServiceController.class); ImsServiceController deviceController2 = mock(ImsServiceController.class); - setImsServiceControllerFactory(deviceController1, deviceController2, null, null); + setImsServiceControllerDDCFactory(deviceController1, deviceController2, null); startBindNoCarrierConfig(1); processAllMessages(); - verify(deviceController1, never()).bind(any(), any(SparseIntArray.class)); + verify(deviceController1, never()).bind(any(), any(), any(SparseIntArray.class)); verify(deviceController1, never()).unbind(); - verify(deviceController2, never()).bind(any(), any(SparseIntArray.class)); + verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class)); verify(deviceController2, never()).unbind(); - verify(mMockQueryManager, never()).startQuery(any(), any()); + verify(mMockQueryManager, never()).startQuery(any(), any(), any()); + } + + /** + * Change the current active user while having ImsServices in system user. The ImsService config + * should not change. + */ + @Test + @SmallTest + public void testChangeCurrentUserServicesInSystem() throws RemoteException { + if (!mFeatureFlags.imsResolverUserAware()) { + return; + } + setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(), + TEST_DEVICE_DEFAULT_NAME.getPackageName()); + List<ResolveInfo> info = new ArrayList<>(); + Set<String> deviceFeatures = new HashSet<>(); + deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE); + deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE); + // Set the carrier override package for slot 0 + setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName()); + HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>(); + // Carrier service doesn't support the voice feature. + carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS)); + info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true)); + info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true)); + // Use device default package, which will load the ImsService that the device provides + setupPackageQuery(info); + + ImsServiceController deviceController = mock(ImsServiceController.class); + ImsServiceController carrierController = mock(ImsServiceController.class); + setImsServiceControllerFactory(deviceController, carrierController); + + startBindCarrierConfigAlreadySet(); + setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); + + // Perform a user switch + userChanged(TEST_USER_HANDLE); + setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2); + + + // Verify that all features that have been defined for the device/carrier override are bound + // and are not changed when the user changes. + verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures), + any(SparseIntArray.class)); + verify(carrierController, atLeastOnce()).changeImsServiceFeatures(eq(carrierFeatures), + any(SparseIntArray.class)); + verify(carrierController, never()).unbind(); + HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet = + convertToHashSet(deviceFeatures, 0); + deviceFeatureSet.removeAll(carrierFeatures); + verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet), + any(SparseIntArray.class)); + verify(deviceController, atLeastOnce()).changeImsServiceFeatures(eq(deviceFeatureSet), + any(SparseIntArray.class)); + verify(deviceController, never()).unbind(); + } + + /** + * Change the current active user while having a carrier ImsService installed for second user. + * The features should change when the current user changes to the second user and back. + */ + @Test + @SmallTest + public void testChangeCurrentUserCarrierInSecondUser() throws RemoteException { + if (!mFeatureFlags.imsResolverUserAware()) { + return; + } + setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(), + TEST_DEVICE_DEFAULT_NAME.getPackageName()); + Set<String> deviceFeatures = new HashSet<>(); + deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE); + deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE); + // Set the carrier override package for slot 0 + setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName()); + HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>(); + // Carrier service doesn't support the voice feature. + carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS)); + // Use device default package, which will load the ImsService that the device provides + setupPackageQuery(Collections.singletonList( + getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true))); + setupPackageQueryForUser(Collections.singletonList( + getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true)), + TEST_USER_HANDLE); + + ImsServiceController deviceController = mock(ImsServiceController.class); + ImsServiceController carrierController = mock(ImsServiceController.class); + setImsServiceControllerFactory(deviceController, carrierController); + + startBindCarrierConfigAlreadySet(); + + verify(carrierController, never()).bind(eq(mContext.getUser()), any(), + any(SparseIntArray.class)); + verify(deviceController).bind(eq(mContext.getUser()), + eq(convertToHashSet(deviceFeatures, 0)), any(SparseIntArray.class)); + + // Perform a user switch + setBoundImsServiceControllerUser(carrierController, TEST_USER_HANDLE); + userChanged(TEST_USER_HANDLE); + setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1); + + + // Verify the carrier controller was bound only when the user changed + verify(carrierController).bind(eq(TEST_USER_HANDLE), eq(carrierFeatures), + any(SparseIntArray.class)); + verify(carrierController, never()).changeImsServiceFeatures(eq(carrierFeatures), + any(SparseIntArray.class)); + verify(carrierController, never()).unbind(); + + HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet = + convertToHashSet(deviceFeatures, 0); + deviceFeatureSet.removeAll(carrierFeatures); + verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet), + any(SparseIntArray.class)); + verify(deviceController, never()).unbind(); + } + + private void setCurrentUser(UserHandle handle) { + when(mTestActivityManagerProxy.getCurrentUser()).thenReturn(handle); } private void setupResolver(int numSlots, String deviceMmTelPkgName, @@ -1970,12 +2139,15 @@ when(mTestTelephonyManagerProxy.getSimState(any(Context.class), eq(i))).thenReturn( TelephonyManager.SIM_STATE_READY); } + when(mMockContext.getUser()).thenReturn(mContext.getUser()); + when(mTestActivityManagerProxy.getCurrentUser()).thenReturn(mContext.getUser()); mTestImsResolver = new ImsResolver(mMockContext, deviceMmTelPkgName, deviceRcsPkgName, numSlots, mMockRepo, Looper.myLooper(), mFeatureFlags); mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy); mTestImsResolver.setTelephonyManagerProxy(mTestTelephonyManagerProxy); + mTestImsResolver.setActivityManagerProxy(mTestActivityManagerProxy); when(mMockQueryManagerFactory.create(any(Context.class), any(ImsServiceFeatureQueryManager.Listener.class))).thenReturn(mMockQueryManager); mTestImsResolver.setImsDynamicQueryManagerFactory(mMockQueryManagerFactory); @@ -1983,24 +2155,55 @@ } private void setupPackageQuery(List<ResolveInfo> infos) { - // Only return info if not using the compat argument - when(mMockPM.queryIntentServicesAsUser( + doAnswer((Answer<List<ResolveInfo>>) invocation -> { + Intent intent = (Intent) invocation.getArguments()[0]; + String pkg = intent.getPackage(); + if (pkg == null) { + return infos; + } else { + for (ResolveInfo info : infos) { + if (pkg.equals(info.serviceInfo.packageName)) { + return Collections.singletonList(info); + } + } + } + return Collections.emptyList(); + }).when(mMockPM).queryIntentServicesAsUser( + // Only return info if not using the compat argument argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())), - anyInt(), any())).thenReturn(infos); + anyInt(), any()); + } + + private void setupPackageQueryForUser(List<ResolveInfo> infos, UserHandle user) { + doAnswer((Answer<List<ResolveInfo>>) invocation -> { + Intent intent = (Intent) invocation.getArguments()[0]; + String pkg = intent.getPackage(); + if (pkg == null) { + return infos; + } else { + for (ResolveInfo info : infos) { + if (pkg.equals(info.serviceInfo.packageName)) { + return Collections.singletonList(info); + } + } + } + return Collections.emptyList(); + }).when(mMockPM).queryIntentServicesAsUser( + // Only return info if not using the compat argument + argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())), + anyInt(), eq(user)); } private void setupPackageQuery(ComponentName name, Set<String> features, boolean isPermissionGranted) { List<ResolveInfo> info = new ArrayList<>(); info.add(getResolveInfo(name, features, isPermissionGranted)); - // Only return info if not using the compat argument - when(mMockPM.queryIntentServicesAsUser( - argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())), - anyInt(), any())).thenReturn(info); + setupPackageQuery(info); } private ImsServiceController setupController() { ImsServiceController controller = mock(ImsServiceController.class); + when(controller.getBoundUser()).thenReturn(mContext.getUser()); mTestImsResolver.setImsServiceControllerFactory( new ImsResolver.ImsServiceControllerFactory() { @Override @@ -2028,15 +2231,25 @@ processAllMessages(); ArgumentCaptor<BroadcastReceiver> receiversCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any()); - mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0); - mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1); - mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2); + if (!mFeatureFlags.imsResolverUserAware()) { + verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any()); + mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0); + mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1); + mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2); + } else { + verify(mMockContext, times(4)).registerReceiver(receiversCaptor.capture(), any()); + mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0); + mTestUserChangedReceiver = receiversCaptor.getAllValues().get(1); + mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(2); + mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(3); + + } ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor = ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class); verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture()); mDynamicQueryListener = queryManagerCaptor.getValue(); - when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class))) + when(mMockQueryManager.startQuery(any(ComponentName.class), any(UserHandle.class), + any(String.class))) .thenReturn(true); processAllMessages(); } @@ -2050,10 +2263,18 @@ processAllMessages(); ArgumentCaptor<BroadcastReceiver> receiversCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any()); - mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0); - mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1); - mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2); + if (!mFeatureFlags.imsResolverUserAware()) { + verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any()); + mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0); + mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1); + mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2); + } else { + verify(mMockContext, times(4)).registerReceiver(receiversCaptor.capture(), any()); + mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0); + mTestUserChangedReceiver = receiversCaptor.getAllValues().get(1); + mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(2); + mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(3); + } ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor = ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class); verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture()); @@ -2069,7 +2290,8 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) { processAllMessages(); // ensure that startQuery was called - verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class)); + verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class), + any(String.class)); mDynamicQueryListener.onComplete(name, features); processAllMessages(); } @@ -2078,7 +2300,8 @@ HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) { processAllFutureMessages(); // ensure that startQuery was called - verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class)); + verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class), + any(String.class)); mDynamicQueryListener.onComplete(name, features); processAllMessages(); } @@ -2086,8 +2309,19 @@ private void setupDynamicQueryFeaturesFailure(ComponentName name, int times) { processAllMessages(); // ensure that startQuery was called - verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class)); - mDynamicQueryListener.onPermanentError(name); + verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class), + any(String.class)); + mDynamicQueryListener.onPermanentError(name, mContext.getUser()); + processAllMessages(); + } + + public void userChanged(UserHandle newUser) { + setCurrentUser(newUser); + // Tell the package manager that a new device feature is installed + Intent userSwitchedIntent = new Intent(); + userSwitchedIntent.setAction(Intent.ACTION_USER_SWITCHED); + userSwitchedIntent.putExtra(Intent.EXTRA_USER, newUser); + mTestUserChangedReceiver.onReceive(null, userSwitchedIntent); processAllMessages(); } @@ -2110,7 +2344,14 @@ processAllMessages(); } + private void setBoundImsServiceControllerUser(ImsServiceController controller, + UserHandle handle) { + when(controller.getBoundUser()).thenReturn(handle); + } + private void setImsServiceControllerFactory(Map<String, ImsServiceController> controllerMap) { + controllerMap.values() + .forEach(c -> setBoundImsServiceControllerUser(c, mContext.getUser())); mTestImsResolver.setImsServiceControllerFactory( new ImsResolver.ImsServiceControllerFactory() { @Override @@ -2129,6 +2370,8 @@ private void setImsServiceControllerFactory(ImsServiceController deviceController, ImsServiceController carrierController) { + setBoundImsServiceControllerUser(deviceController, mContext.getUser()); + setBoundImsServiceControllerUser(carrierController, mContext.getUser()); mTestImsResolver.setImsServiceControllerFactory( new ImsResolver.ImsServiceControllerFactory() { @Override @@ -2156,6 +2399,9 @@ private void setImsServiceControllerFactory(ImsServiceController deviceController, ImsServiceController carrierController1, ImsServiceController carrierController2) { + setBoundImsServiceControllerUser(deviceController, mContext.getUser()); + setBoundImsServiceControllerUser(carrierController1, mContext.getUser()); + setBoundImsServiceControllerUser(carrierController2, mContext.getUser()); mTestImsResolver.setImsServiceControllerFactory( new ImsResolver.ImsServiceControllerFactory() { @Override @@ -2185,9 +2431,13 @@ }); } - private void setImsServiceControllerFactory(ImsServiceController deviceController1, - ImsServiceController deviceController2, ImsServiceController carrierController1, - ImsServiceController carrierController2) { + private void setImsServiceControllerDDCFactory(ImsServiceController deviceController1, + ImsServiceController deviceController2, ImsServiceController carrierController1) { + setBoundImsServiceControllerUser(deviceController1, mContext.getUser()); + setBoundImsServiceControllerUser(deviceController2, mContext.getUser()); + if (carrierController1 != null) { + setBoundImsServiceControllerUser(carrierController1, mContext.getUser()); + } mTestImsResolver.setImsServiceControllerFactory( new ImsResolver.ImsServiceControllerFactory() { @Override @@ -2211,10 +2461,6 @@ componentName.getPackageName())) { when(carrierController1.getComponentName()).thenReturn(componentName); return carrierController1; - } else if (TEST_CARRIER_2_DEFAULT_NAME.getPackageName().equals( - componentName.getPackageName())) { - when(carrierController2.getComponentName()).thenReturn(componentName); - return carrierController2; } return null; }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java index 2544fc1..aa6bd7f 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
@@ -108,9 +108,9 @@ mMmTelCompatAdapterSpy = spy(new MmTelFeatureCompatAdapter(mMockContext, SLOT_0, mMockMmTelInterfaceAdapter)); mTestImsServiceController = new ImsServiceControllerCompat(mMockContext, mTestComponentName, - mMockCallbacks, mHandler, REBIND_RETRY, mRepo, + mMockCallbacks, mHandler, REBIND_RETRY, mRepo, (a, b, c) -> mMmTelCompatAdapterSpy); - when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true); + when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true); when(mMockServiceControllerBinder.createMMTelFeature(anyInt())) .thenReturn(mMockRemoteMMTelFeature); when(mMockRemoteMMTelFeature.getConfigInterface()).thenReturn(mMockImsConfig); @@ -146,8 +146,8 @@ verify(mMockServiceControllerBinder).createMMTelFeature(SLOT_0); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_1), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); // Remove the feature conn.onBindingDied(mTestComponentName); @@ -191,8 +191,9 @@ SparseIntArray slotIdToSubIdMap) { ArgumentCaptor<ServiceConnection> serviceCaptor = ArgumentCaptor.forClass(ServiceConnection.class); - assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap)); - verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt()); + assertTrue(mTestImsServiceController.bind(mContext.getUser(), testFeatures, + slotIdToSubIdMap)); + verify(mMockContext).bindServiceAsUser(any(), serviceCaptor.capture(), anyInt(), any()); return serviceCaptor.getValue(); } }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java index 65b73fb..5f16d9b 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -39,6 +39,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.os.UserHandle; import android.telephony.SubscriptionManager; import android.telephony.ims.ImsService; import android.telephony.ims.aidl.IImsConfig; @@ -135,6 +136,7 @@ private final Handler mHandler = new Handler(Looper.getMainLooper()); private ImsServiceController mTestImsServiceController; private ImsFeatureBinderRepository mRepo; + private UserHandle mUser; @Before @Override @@ -150,11 +152,12 @@ mMockCallbacks = mock(ImsServiceController.ImsServiceControllerCallbacks.class); mFeatureFlags = mock(FeatureFlags.class); mMockContext = mock(Context.class); + mUser = UserHandle.of(UserHandle.myUserId()); mRepo = new ImsFeatureBinderRepository(); mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName, mMockCallbacks, mHandler, REBIND_RETRY, mRepo, mFeatureFlags); - when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true); + when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true); when(mMockServiceControllerBinder.createMmTelFeature(anyInt(), anyInt())) .thenReturn(mMockMmTelFeature); when(mMockServiceControllerBinder.createRcsFeature(anyInt(), anyInt())) @@ -198,11 +201,12 @@ SparseIntArray slotIdToSubIdMap = new SparseIntArray(); slotIdToSubIdMap.put(SLOT_0, SUB_2); - assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone())); + assertTrue(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone())); int expectedFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE | Context.BIND_IMPORTANT; - verify(mMockContext).bindService(intentCaptor.capture(), any(), eq(expectedFlags)); + verify(mMockContext).bindServiceAsUser(intentCaptor.capture(), any(), eq(expectedFlags), + any()); Intent testIntent = intentCaptor.getValue(); assertEquals(ImsService.SERVICE_INTERFACE, testIntent.getAction()); assertEquals(mTestComponentName, testIntent.getComponent()); @@ -222,9 +226,9 @@ bindAndConnectService(testFeatures, slotIdToSubIdMap.clone()); // already bound, should return false - assertFalse(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone())); + assertFalse(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone())); - verify(mMockContext, times(1)).bindService(any(), any(), anyInt()); + verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any()); } /** @@ -250,10 +254,10 @@ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateRcsFeatureContainerExists(SLOT_0); } @@ -282,10 +286,10 @@ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateRcsFeatureContainerExists(SLOT_0); @@ -313,9 +317,9 @@ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_3); verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_3), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); - verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_3), eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateRcsFeatureContainerExists(SLOT_0); @@ -341,13 +345,13 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateMmTelFeatureContainerExists(SLOT_1); @@ -373,12 +377,12 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_4); verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_5); verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_1), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_5), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateMmTelFeatureContainerExists(SLOT_1); @@ -411,13 +415,14 @@ verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_0); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), + eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExistsWithEmergency(SLOT_0); validateMmTelFeatureContainerExistsWithEmergency(SLOT_1); @@ -437,8 +442,9 @@ verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_1); verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_1), - eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), + eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL), + eq(mTestImsServiceController)); validateMmTelFeatureContainerExistsWithEmergency(SLOT_0); validateMmTelFeatureContainerExistsWithEmergency(SLOT_1); @@ -470,10 +476,10 @@ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateFeatureContainerExistsWithSipDelegate(ImsFeature.FEATURE_MMTEL, SLOT_0); validateFeatureContainerExistsWithSipDelegate(ImsFeature.FEATURE_RCS, SLOT_0); } @@ -499,7 +505,8 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); // verify CAPABILITY_SIP_DELEGATE_CREATION is not set because MMTEL and RCS are not set. validateFeatureContainerDoesNotHaveSipDelegate(ImsFeature.FEATURE_MMTEL, SLOT_0); @@ -525,9 +532,9 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController)); // Make sure this callback happens, which will notify the framework of emergency calling // availability. @@ -553,9 +560,11 @@ verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_0); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), + eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), + eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController)); // Make sure this callback happens, which will notify the framework of emergency calling // availability. @@ -584,17 +593,17 @@ verify(mMockServiceControllerBinder, never()).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder, never()).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); - verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerDoesntExist(SLOT_0); // verify RCS feature is created verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateRcsFeatureContainerExists(SLOT_0); } @@ -798,9 +807,9 @@ long delay = mTestImsServiceController.getRebindDelay(); waitForHandlerActionDelayed(mHandler, delay, 2 * delay); - verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(), + verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(), anyInt(), eq(mTestImsServiceController)); - verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName)); + verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName), eq(mUser)); validateMmTelFeatureContainerDoesntExist(SLOT_0); validateRcsFeatureContainerDoesntExist(SLOT_0); } @@ -822,8 +831,8 @@ bindAndConnectService(testFeatures, slotIdToSubIdMap.clone()); verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateRcsFeatureContainerDoesntExist(SLOT_0); } @@ -843,8 +852,8 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); // Create a new list with an additional item HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>( testFeatures); @@ -859,8 +868,8 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateMmTelFeatureContainerExists(SLOT_1); } @@ -880,8 +889,8 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); // Create a new list with an additional item HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>( testFeatures); @@ -895,8 +904,8 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateMmTelFeatureContainerExists(SLOT_1); } @@ -918,8 +927,8 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); assertEquals(mMockMmTelBinder, cb.container.imsFeature); assertTrue((ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL @@ -934,9 +943,8 @@ mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition, slotIdToSubIdMap.clone()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), - eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExistsWithEmergency(SLOT_0); assertEquals(mMockMmTelBinder, cb.container.imsFeature); @@ -972,8 +980,8 @@ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateRcsFeatureContainerExists(SLOT_0); // Add FEATURE_EMERGENCY_MMTEL and ensure it doesn't cause MMTEL bind HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>( @@ -988,9 +996,8 @@ verify(mMockServiceControllerBinder, never()).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder, never()).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_1), - eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerDoesntExist(SLOT_1); } @@ -1010,8 +1017,8 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); // Call change with the same features and make sure it is disregarded @@ -1027,7 +1034,7 @@ verify(mMockServiceControllerBinder, never()).removeFeatureStatusCallback(anyInt(), anyInt(), any()); verify(mMockCallbacks, times(1)).imsServiceFeatureCreated(eq(SLOT_0), - eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); + eq(SUB_2), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockCallbacks, never()).imsServiceFeatureRemoved(anyInt(), anyInt(), any()); validateMmTelFeatureContainerExists(SLOT_0); } @@ -1050,13 +1057,13 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateMmTelFeatureContainerExists(SLOT_1); // Create a new list with one less item @@ -1098,13 +1105,13 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateMmTelFeatureContainerExists(SLOT_1); // Create a new list with one less item @@ -1149,23 +1156,23 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createRcsFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_RCS), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateMmTelFeatureContainerExists(SLOT_1); validateRcsFeatureContainerExists(SLOT_0); @@ -1205,12 +1212,12 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_4); verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_4); verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4), eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateRcsFeatureContainerExists(SLOT_0); @@ -1235,13 +1242,13 @@ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3); verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), any()); - verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL), - eq(mTestImsServiceController)); + verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3), + eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController)); validateMmTelFeatureContainerExists(SLOT_0); validateMmTelFeatureContainerExists(SLOT_1); @@ -1291,7 +1298,7 @@ verify(mMockServiceControllerBinder, never()).createRcsFeature(SLOT_0, SUB_2); verify(mMockServiceControllerBinder, never()).removeFeatureStatusCallback(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS), any()); - verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), + verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2), eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController)); validateMmTelFeatureContainerDoesntExist(SLOT_0); validateRcsFeatureContainerDoesntExist(SLOT_0); @@ -1318,7 +1325,7 @@ long delay = REBIND_RETRY.getStartDelay(); waitForHandlerActionDelayed(mHandler, delay, 2 * delay); // The service should autobind after rebind event occurs - verify(mMockContext, times(2)).bindService(any(), any(), anyInt()); + verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any()); } /** @@ -1344,7 +1351,7 @@ long delay = REBIND_RETRY.getStartDelay(); waitForHandlerActionDelayed(mHandler, delay, 2 * delay); // The service should autobind after rebind event occurs - verify(mMockContext, times(2)).bindService(any(), any(), anyInt()); + verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any()); } /** @@ -1365,7 +1372,7 @@ conn.onBindingDied(null /*null*/); // Be sure that there are no binds before the RETRY_TIMEOUT expires - verify(mMockContext, times(1)).bindService(any(), any(), anyInt()); + verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any()); } /** @@ -1390,7 +1397,7 @@ waitForHandlerActionDelayed(mHandler, delay, 2 * delay); // Unbind should stop the autobind from occurring. - verify(mMockContext, times(1)).bindService(any(), any(), anyInt()); + verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any()); } /** @@ -1412,10 +1419,10 @@ long delay = REBIND_RETRY.getStartDelay(); waitForHandlerActionDelayed(mHandler, delay, 2 * delay); - mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone()); + mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone()); // Should only see two binds, not three from the auto rebind that occurs. - verify(mMockContext, times(2)).bindService(any(), any(), anyInt()); + verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any()); } private void validateMmTelFeatureContainerExists(int slotId) { @@ -1504,8 +1511,8 @@ SparseIntArray slotIdToSubIdMap) { ArgumentCaptor<ServiceConnection> serviceCaptor = ArgumentCaptor.forClass(ServiceConnection.class); - assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap)); - verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt()); + assertTrue(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap)); + verify(mMockContext).bindServiceAsUser(any(), serviceCaptor.capture(), anyInt(), any()); return serviceCaptor.getValue(); } }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java index e371c59..e6e4c30 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -4370,94 +4370,95 @@ public void addSatelliteControllerStats_withExistingEntries() throws Exception { createEmptyTestFile(); mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); - mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController1); - mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController2); + mPersistAtomsStorage.addSatelliteControllerStats(copyOf(mSatelliteController1)); + mPersistAtomsStorage.addSatelliteControllerStats(copyOf(mSatelliteController1)); mPersistAtomsStorage.incTimeMillis(100L); SatelliteController expected = new SatelliteController(); expected.countOfSatelliteServiceEnablementsSuccess = - mSatelliteController1.countOfSatelliteServiceEnablementsSuccess - + mSatelliteController2.countOfSatelliteServiceEnablementsSuccess; + mSatelliteController1.countOfSatelliteServiceEnablementsSuccess * 2; expected.countOfSatelliteServiceEnablementsFail = - mSatelliteController1.countOfSatelliteServiceEnablementsFail - + mSatelliteController2.countOfSatelliteServiceEnablementsFail; + mSatelliteController1.countOfSatelliteServiceEnablementsFail * 2; expected.countOfOutgoingDatagramSuccess = - mSatelliteController1.countOfOutgoingDatagramSuccess - + mSatelliteController2.countOfOutgoingDatagramSuccess; + mSatelliteController1.countOfOutgoingDatagramSuccess * 2; expected.countOfOutgoingDatagramFail = - mSatelliteController1.countOfOutgoingDatagramFail - + mSatelliteController2.countOfOutgoingDatagramFail; + mSatelliteController1.countOfOutgoingDatagramFail * 2; expected.countOfIncomingDatagramSuccess = - mSatelliteController1.countOfIncomingDatagramSuccess - + mSatelliteController2.countOfIncomingDatagramSuccess; + mSatelliteController1.countOfIncomingDatagramSuccess * 2; expected.countOfIncomingDatagramFail = - mSatelliteController1.countOfIncomingDatagramFail - + mSatelliteController2.countOfIncomingDatagramFail; + mSatelliteController1.countOfIncomingDatagramFail * 2; expected.countOfDatagramTypeSosSmsSuccess = - mSatelliteController1.countOfDatagramTypeSosSmsSuccess - + mSatelliteController2.countOfDatagramTypeSosSmsSuccess; + mSatelliteController1.countOfDatagramTypeSosSmsSuccess * 2; expected.countOfDatagramTypeSosSmsFail = - mSatelliteController1.countOfDatagramTypeSosSmsFail - + mSatelliteController2.countOfDatagramTypeSosSmsFail; + mSatelliteController1.countOfDatagramTypeSosSmsFail * 2; expected.countOfDatagramTypeLocationSharingSuccess = - mSatelliteController1.countOfDatagramTypeLocationSharingSuccess - + mSatelliteController2.countOfDatagramTypeLocationSharingSuccess; + mSatelliteController1.countOfDatagramTypeLocationSharingSuccess * 2; expected.countOfDatagramTypeLocationSharingFail = - mSatelliteController1.countOfDatagramTypeLocationSharingFail - + mSatelliteController2.countOfDatagramTypeLocationSharingFail; + mSatelliteController1.countOfDatagramTypeLocationSharingFail * 2; expected.countOfProvisionSuccess = - mSatelliteController1.countOfProvisionSuccess - + mSatelliteController2.countOfProvisionSuccess; + mSatelliteController1.countOfProvisionSuccess * 2; expected.countOfProvisionFail = - mSatelliteController1.countOfProvisionFail - + mSatelliteController2.countOfProvisionFail; + mSatelliteController1.countOfProvisionFail * 2; expected.countOfDeprovisionSuccess = - mSatelliteController1.countOfDeprovisionSuccess - + mSatelliteController2.countOfDeprovisionSuccess; + mSatelliteController1.countOfDeprovisionSuccess * 2; expected.countOfDeprovisionFail = - mSatelliteController1.countOfDeprovisionFail - + mSatelliteController2.countOfDeprovisionFail; + mSatelliteController1.countOfDeprovisionFail * 2; expected.totalServiceUptimeSec = - mSatelliteController1.totalServiceUptimeSec - + mSatelliteController2.totalServiceUptimeSec; + mSatelliteController1.totalServiceUptimeSec * 2; expected.totalBatteryConsumptionPercent = - mSatelliteController1.totalBatteryConsumptionPercent - + mSatelliteController2.totalBatteryConsumptionPercent; + mSatelliteController1.totalBatteryConsumptionPercent * 2; expected.totalBatteryChargedTimeSec = - mSatelliteController1.totalBatteryChargedTimeSec - + mSatelliteController2.totalBatteryChargedTimeSec; + mSatelliteController1.totalBatteryChargedTimeSec * 2; expected.countOfDemoModeSatelliteServiceEnablementsSuccess = - mSatelliteController1.countOfDemoModeSatelliteServiceEnablementsSuccess - + mSatelliteController2.countOfDemoModeSatelliteServiceEnablementsSuccess; + mSatelliteController1.countOfDemoModeSatelliteServiceEnablementsSuccess * 2; expected.countOfDemoModeSatelliteServiceEnablementsFail = - mSatelliteController1.countOfDemoModeSatelliteServiceEnablementsFail - + mSatelliteController2.countOfDemoModeSatelliteServiceEnablementsFail; + mSatelliteController1.countOfDemoModeSatelliteServiceEnablementsFail * 2; expected.countOfDemoModeOutgoingDatagramSuccess = - mSatelliteController1.countOfDemoModeOutgoingDatagramSuccess - + mSatelliteController2.countOfDemoModeOutgoingDatagramSuccess; + mSatelliteController1.countOfDemoModeOutgoingDatagramSuccess * 2; expected.countOfDemoModeOutgoingDatagramFail = - mSatelliteController1.countOfDemoModeOutgoingDatagramFail - + mSatelliteController2.countOfDemoModeOutgoingDatagramFail; + mSatelliteController1.countOfDemoModeOutgoingDatagramFail * 2; expected.countOfDemoModeIncomingDatagramSuccess = - mSatelliteController1.countOfDemoModeIncomingDatagramSuccess - + mSatelliteController2.countOfDemoModeIncomingDatagramSuccess; + mSatelliteController1.countOfDemoModeIncomingDatagramSuccess * 2; expected.countOfDemoModeIncomingDatagramFail = - mSatelliteController1.countOfDemoModeIncomingDatagramFail - + mSatelliteController2.countOfDemoModeIncomingDatagramFail; + mSatelliteController1.countOfDemoModeIncomingDatagramFail * 2; expected.countOfDatagramTypeKeepAliveSuccess = - mSatelliteController1.countOfDatagramTypeKeepAliveSuccess - + mSatelliteController2.countOfDatagramTypeKeepAliveSuccess; + mSatelliteController1.countOfDatagramTypeKeepAliveSuccess * 2; expected.countOfDatagramTypeKeepAliveFail = - mSatelliteController1.countOfDatagramTypeKeepAliveFail - + mSatelliteController2.countOfDatagramTypeKeepAliveFail; - expected.isProvisioned = mSatelliteController2.isProvisioned; - expected.carrierId = mSatelliteController2.carrierId; + mSatelliteController1.countOfDatagramTypeKeepAliveFail * 2; + expected.isProvisioned = mSatelliteController1.isProvisioned; + expected.carrierId = mSatelliteController1.carrierId; + expected.countOfSatelliteAllowedStateChangedEvents = + mSatelliteController1.countOfSatelliteAllowedStateChangedEvents * 2; + expected.countOfSuccessfulLocationQueries = + mSatelliteController1.countOfSuccessfulLocationQueries * 2; + expected.countOfFailedLocationQueries = + mSatelliteController1.countOfFailedLocationQueries * 2; // Service state and service switch should be added successfully verifyCurrentStateSavedToFileOnce(); SatelliteController[] output = mPersistAtomsStorage.getSatelliteControllerStats(0L); - assertHasStats(output, expected); + assertHasStats(output, expected, 1); + } + + @Test + public void addSatelliteControllerStats_addNewCarrierId() throws Exception { + createEmptyTestFile(); + mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); + mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController1); + mPersistAtomsStorage.addSatelliteControllerStats(mSatelliteController2); + mPersistAtomsStorage.incTimeMillis(100L); + + SatelliteController expected1 = mSatelliteController1; + SatelliteController expected2 = mSatelliteController2; + + // Service state and service switch should be added successfully + verifyCurrentStateSavedToFileOnce(); + SatelliteController[] output = + mPersistAtomsStorage.getSatelliteControllerStats(0L); + + assertHasStats(output, expected1, 1); + assertHasStats(output, expected2, 1); } @Test @@ -4974,43 +4975,56 @@ } @Test - public void addCarrierRoamingSatelliteControllerStats_withExistingEntries() throws Exception { + public void addCarrierRoamingSatelliteControllerStats_withExistingCarrierId() throws Exception { createEmptyTestFile(); mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats( - mCarrierRoamingSatelliteControllerStats1); + copyOf(mCarrierRoamingSatelliteControllerStats1)); mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats( - mCarrierRoamingSatelliteControllerStats2); + copyOf(mCarrierRoamingSatelliteControllerStats1)); mPersistAtomsStorage.incTimeMillis(100L); CarrierRoamingSatelliteControllerStats expected = new CarrierRoamingSatelliteControllerStats(); - expected.configDataSource = mCarrierRoamingSatelliteControllerStats2.configDataSource; + expected.configDataSource = mCarrierRoamingSatelliteControllerStats1.configDataSource; expected.countOfEntitlementStatusQueryRequest = - mCarrierRoamingSatelliteControllerStats1.countOfEntitlementStatusQueryRequest - + mCarrierRoamingSatelliteControllerStats2 - .countOfEntitlementStatusQueryRequest; + mCarrierRoamingSatelliteControllerStats1.countOfEntitlementStatusQueryRequest * 2; expected.countOfSatelliteConfigUpdateRequest = - mCarrierRoamingSatelliteControllerStats1.countOfSatelliteConfigUpdateRequest - + mCarrierRoamingSatelliteControllerStats2 - .countOfSatelliteConfigUpdateRequest; + mCarrierRoamingSatelliteControllerStats1.countOfSatelliteConfigUpdateRequest * 2; expected.countOfSatelliteNotificationDisplayed = - mCarrierRoamingSatelliteControllerStats1.countOfSatelliteNotificationDisplayed - + mCarrierRoamingSatelliteControllerStats2 - .countOfSatelliteNotificationDisplayed; + mCarrierRoamingSatelliteControllerStats1.countOfSatelliteNotificationDisplayed * 2; expected.satelliteSessionGapMinSec = - mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMinSec; + mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMinSec; expected.satelliteSessionGapAvgSec = - mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapAvgSec; + mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapAvgSec; expected.satelliteSessionGapMaxSec = - mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMaxSec; - expected.carrierId = mCarrierRoamingSatelliteControllerStats2.carrierId; - expected.isDeviceEntitled = mCarrierRoamingSatelliteControllerStats2.isDeviceEntitled; - + mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMaxSec; + expected.carrierId = mCarrierRoamingSatelliteControllerStats1.carrierId; + expected.isDeviceEntitled = mCarrierRoamingSatelliteControllerStats1.isDeviceEntitled; verifyCurrentStateSavedToFileOnce(); CarrierRoamingSatelliteControllerStats[] output = mPersistAtomsStorage.getCarrierRoamingSatelliteControllerStats(0L); - assertHasStats(output, expected); + assertHasStats(output, expected, 1); + } + + @Test + public void addCarrierRoamingSatelliteControllerStats_addNewCarrierId() throws Exception { + createEmptyTestFile(); + mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); + mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats( + copyOf(mCarrierRoamingSatelliteControllerStats1)); + mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats( + copyOf(mCarrierRoamingSatelliteControllerStats2)); + mPersistAtomsStorage.incTimeMillis(100L); + + CarrierRoamingSatelliteControllerStats expected1 = mCarrierRoamingSatelliteControllerStats1; + CarrierRoamingSatelliteControllerStats expected2 = mCarrierRoamingSatelliteControllerStats2; + + CarrierRoamingSatelliteControllerStats[] output = + mPersistAtomsStorage.getCarrierRoamingSatelliteControllerStats(0L); + + assertHasStats(output, expected1, 1); + assertHasStats(output, expected2, 1); } @Test @@ -5816,60 +5830,71 @@ assertEquals(expectedCount, actualCount); } - private static void assertHasStats( - SatelliteController[] tested, - @Nullable SatelliteController expectedStats) { + private static void assertHasStats(SatelliteController[] tested, + @Nullable SatelliteController expectedStats, int expectedCount) { assertNotNull(tested); - assertEquals(tested[0].countOfSatelliteServiceEnablementsSuccess, - expectedStats.countOfSatelliteServiceEnablementsSuccess); - assertEquals(tested[0].countOfSatelliteServiceEnablementsFail, - expectedStats.countOfSatelliteServiceEnablementsFail); - assertEquals(tested[0].countOfOutgoingDatagramSuccess, - expectedStats.countOfOutgoingDatagramSuccess); - assertEquals(tested[0].countOfOutgoingDatagramFail, - expectedStats.countOfOutgoingDatagramFail); - assertEquals(tested[0].countOfIncomingDatagramSuccess, - expectedStats.countOfIncomingDatagramSuccess); - assertEquals(tested[0].countOfIncomingDatagramFail, - expectedStats.countOfIncomingDatagramFail); - assertEquals(tested[0].countOfDatagramTypeSosSmsSuccess, - expectedStats.countOfDatagramTypeSosSmsSuccess); - assertEquals(tested[0].countOfDatagramTypeSosSmsFail, - expectedStats.countOfDatagramTypeSosSmsFail); - assertEquals(tested[0].countOfDatagramTypeLocationSharingSuccess, - expectedStats.countOfDatagramTypeLocationSharingSuccess); - assertEquals(tested[0].countOfDatagramTypeLocationSharingFail, - expectedStats.countOfDatagramTypeLocationSharingFail); - assertEquals(tested[0].totalServiceUptimeSec, - expectedStats.totalServiceUptimeSec); - assertEquals(tested[0].totalBatteryConsumptionPercent, - expectedStats.totalBatteryConsumptionPercent); - assertEquals(tested[0].totalBatteryChargedTimeSec, - expectedStats.totalBatteryChargedTimeSec); - assertEquals(tested[0].countOfDemoModeSatelliteServiceEnablementsSuccess, - expectedStats.countOfDemoModeSatelliteServiceEnablementsSuccess); - assertEquals(tested[0].countOfDemoModeSatelliteServiceEnablementsFail, - expectedStats.countOfDemoModeSatelliteServiceEnablementsFail); - assertEquals(tested[0].countOfDemoModeOutgoingDatagramSuccess, - expectedStats.countOfDemoModeOutgoingDatagramSuccess); - assertEquals(tested[0].countOfDemoModeOutgoingDatagramFail, - expectedStats.countOfDemoModeOutgoingDatagramFail); - assertEquals(tested[0].countOfDemoModeIncomingDatagramSuccess, - expectedStats.countOfDemoModeIncomingDatagramSuccess); - assertEquals(tested[0].countOfDemoModeIncomingDatagramFail, - expectedStats.countOfDemoModeIncomingDatagramFail); - assertEquals(tested[0].countOfDatagramTypeKeepAliveSuccess, - expectedStats.countOfDatagramTypeKeepAliveSuccess); - assertEquals(tested[0].countOfDatagramTypeKeepAliveFail, - expectedStats.countOfDatagramTypeKeepAliveFail); - assertEquals(tested[0].countOfAllowedSatelliteAccess, - expectedStats.countOfAllowedSatelliteAccess); - assertEquals(tested[0].countOfDisallowedSatelliteAccess, - expectedStats.countOfDisallowedSatelliteAccess); - assertEquals(tested[0].countOfSatelliteAccessCheckFail, - expectedStats.countOfSatelliteAccessCheckFail); - assertEquals(tested[0].isProvisioned, expectedStats.isProvisioned); - assertEquals(tested[0].carrierId, expectedStats.carrierId); + int actualCount = 0; + int i = 0; + for (SatelliteController stats : tested) { + if (expectedStats.carrierId == stats.carrierId) { + assertEquals(expectedStats.countOfSatelliteServiceEnablementsSuccess, + stats.countOfSatelliteServiceEnablementsSuccess); + assertEquals(expectedStats.countOfSatelliteServiceEnablementsFail, + stats.countOfSatelliteServiceEnablementsFail); + assertEquals(expectedStats.countOfOutgoingDatagramSuccess, + stats.countOfOutgoingDatagramSuccess); + assertEquals(expectedStats.countOfOutgoingDatagramFail, + stats.countOfOutgoingDatagramFail); + assertEquals(expectedStats.countOfIncomingDatagramSuccess, + stats.countOfIncomingDatagramSuccess); + assertEquals(expectedStats.countOfIncomingDatagramFail, + stats.countOfIncomingDatagramFail); + assertEquals(expectedStats.countOfDatagramTypeSosSmsSuccess, + stats.countOfDatagramTypeSosSmsSuccess); + assertEquals(expectedStats.countOfDatagramTypeSosSmsFail, + stats.countOfDatagramTypeSosSmsFail); + assertEquals(expectedStats.countOfDatagramTypeLocationSharingSuccess, + stats.countOfDatagramTypeLocationSharingSuccess); + assertEquals(expectedStats.countOfDatagramTypeLocationSharingFail, + stats.countOfDatagramTypeLocationSharingFail); + assertEquals(expectedStats.totalServiceUptimeSec, stats.totalServiceUptimeSec); + assertEquals(expectedStats.totalBatteryConsumptionPercent, + stats.totalBatteryConsumptionPercent); + assertEquals(expectedStats.totalBatteryChargedTimeSec, + stats.totalBatteryChargedTimeSec); + assertEquals(expectedStats.countOfDemoModeSatelliteServiceEnablementsSuccess, + stats.countOfDemoModeSatelliteServiceEnablementsSuccess); + assertEquals(expectedStats.countOfDemoModeSatelliteServiceEnablementsFail, + stats.countOfDemoModeSatelliteServiceEnablementsFail); + assertEquals(expectedStats.countOfDemoModeOutgoingDatagramSuccess, + stats.countOfDemoModeOutgoingDatagramSuccess); + assertEquals(expectedStats.countOfDemoModeOutgoingDatagramFail, + stats.countOfDemoModeOutgoingDatagramFail); + assertEquals(expectedStats.countOfDemoModeIncomingDatagramSuccess, + stats.countOfDemoModeIncomingDatagramSuccess); + assertEquals(expectedStats.countOfDemoModeIncomingDatagramFail, + stats.countOfDemoModeIncomingDatagramFail); + assertEquals(expectedStats.countOfDatagramTypeKeepAliveSuccess, + stats.countOfDatagramTypeKeepAliveSuccess); + assertEquals(expectedStats.countOfDatagramTypeKeepAliveFail, + stats.countOfDatagramTypeKeepAliveFail); + assertEquals(expectedStats.countOfAllowedSatelliteAccess, + stats.countOfAllowedSatelliteAccess); + assertEquals(expectedStats.countOfDisallowedSatelliteAccess, + stats.countOfDisallowedSatelliteAccess); + assertEquals(expectedStats.countOfSatelliteAccessCheckFail, + stats.countOfSatelliteAccessCheckFail); + assertEquals(expectedStats.isProvisioned, stats.isProvisioned); + assertEquals(expectedStats.countOfSatelliteAllowedStateChangedEvents, + stats.countOfSatelliteAllowedStateChangedEvents); + assertEquals(expectedStats.countOfSuccessfulLocationQueries, + stats.countOfSuccessfulLocationQueries); + assertEquals(expectedStats.countOfFailedLocationQueries, + stats.countOfFailedLocationQueries); + actualCount++; + } + } + assertEquals(expectedCount, actualCount); } private static void assertHasStatsAndCount( @@ -6251,20 +6276,29 @@ } private static void assertHasStats(CarrierRoamingSatelliteControllerStats[] tested, - @Nullable CarrierRoamingSatelliteControllerStats expectedStats) { + @Nullable CarrierRoamingSatelliteControllerStats expectedStats, int expectedCount) { assertNotNull(tested); - assertEquals(tested[0].configDataSource, expectedStats.configDataSource); - assertEquals(tested[0].countOfEntitlementStatusQueryRequest, - expectedStats.countOfEntitlementStatusQueryRequest); - assertEquals(tested[0].countOfSatelliteConfigUpdateRequest, - expectedStats.countOfSatelliteConfigUpdateRequest); - assertEquals(tested[0].countOfSatelliteNotificationDisplayed, - expectedStats.countOfSatelliteNotificationDisplayed); - assertEquals(tested[0].satelliteSessionGapMinSec, expectedStats.satelliteSessionGapMinSec); - assertEquals(tested[0].satelliteSessionGapAvgSec, expectedStats.satelliteSessionGapAvgSec); - assertEquals(tested[0].satelliteSessionGapMaxSec, expectedStats.satelliteSessionGapMaxSec); - assertEquals(tested[0].carrierId, expectedStats.carrierId); - assertEquals(tested[0].isDeviceEntitled, expectedStats.isDeviceEntitled); + int count = 0; + for (CarrierRoamingSatelliteControllerStats stats : tested) { + if (expectedStats.carrierId == stats.carrierId) { + assertEquals(expectedStats.configDataSource, stats.configDataSource); + assertEquals(expectedStats.countOfEntitlementStatusQueryRequest, + stats.countOfEntitlementStatusQueryRequest); + assertEquals(expectedStats.countOfSatelliteConfigUpdateRequest, + stats.countOfSatelliteConfigUpdateRequest); + assertEquals(expectedStats.countOfSatelliteNotificationDisplayed, + stats.countOfSatelliteNotificationDisplayed); + assertEquals(expectedStats.satelliteSessionGapMinSec, + stats.satelliteSessionGapMinSec); + assertEquals(expectedStats.satelliteSessionGapAvgSec, + stats.satelliteSessionGapAvgSec); + assertEquals(expectedStats.satelliteSessionGapMaxSec, + stats.satelliteSessionGapMaxSec); + assertEquals(expectedStats.isDeviceEntitled, stats.isDeviceEntitled); + count++; + } + } + assertEquals(expectedCount, count); } private static void assertHasStatsAndCount(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java index 8173bbc..fdb94c9 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
@@ -180,7 +180,7 @@ assertEquals(0, mTestStats.mCountOfIncomingDatagramFail); assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess); assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail); - assertEquals(10, mTestStats.mCountOfDatagramTypeLocationSharingSuccess); + assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess); assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail); assertEquals(0, mTestStats.mCountOfProvisionSuccess); assertEquals(0, mTestStats.mCountOfProvisionFail); @@ -282,7 +282,7 @@ assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess); assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail); assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess); - assertEquals(10, mTestStats.mCountOfDatagramTypeLocationSharingFail); + assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail); assertEquals(0, mTestStats.mCountOfProvisionSuccess); assertEquals(0, mTestStats.mCountOfProvisionFail); assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java index 2961b4d..271e0ad 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramControllerTest.java
@@ -16,10 +16,12 @@ package com.android.internal.telephony.satellite; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE; import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE; @@ -44,6 +46,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import com.android.internal.R; import com.android.internal.telephony.TelephonyTest; import org.junit.After; @@ -92,8 +95,8 @@ // Move both send and receive to IDLE state mDatagramControllerUT.updateSendStatus(SUB_ID, DATAGRAM_TYPE_UNKNOWN, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0, SATELLITE_RESULT_SUCCESS); - mDatagramControllerUT.updateReceiveStatus(SUB_ID, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0, - SATELLITE_RESULT_SUCCESS); + mDatagramControllerUT.updateReceiveStatus(SUB_ID, DATAGRAM_TYPE_SOS_MESSAGE, + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0, SATELLITE_RESULT_SUCCESS); pushDemoModeSosDatagram(DATAGRAM_TYPE_SOS_MESSAGE); } @@ -187,6 +190,76 @@ } } + @Test + public void testNeedsWaitingForSatelliteConnected_checkMessageInNotConnected_returnsFalse() + throws Exception { + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + mContextFixture.putBooleanResource( + R.bool.config_satellite_allow_check_message_in_not_connected, true); + mDatagramControllerUT.onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + + boolean result = + mDatagramControllerUT.needsWaitingForSatelliteConnected( + DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS); + + assertFalse(result); + } + + @Test + public void testNeedsWaitingForSatelliteConnected_regularSmsInNotConnected_returnsTrue() + throws Exception { + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + mContextFixture.putBooleanResource( + R.bool.config_satellite_allow_check_message_in_not_connected, true); + mDatagramControllerUT.onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + + boolean result = + mDatagramControllerUT.needsWaitingForSatelliteConnected( + DATAGRAM_TYPE_SMS); + + assertTrue(result); + } + + @Test + public void + testNeedsWaitingForSatelliteConnected_checkMessageInNotConnected_allowCheckMessageFalse_returnsTrue() + throws Exception { + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + mContextFixture.putBooleanResource( + R.bool.config_satellite_allow_check_message_in_not_connected, false); + mDatagramControllerUT.onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + + boolean result = + mDatagramControllerUT.needsWaitingForSatelliteConnected( + DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS); + + assertTrue(result); + } + + @Test + public void + testNeedsWaitingForSatelliteConnected_checkMessageInNotConnected_carrierRoamingNbIotNtnFalse_returnsTrue() + throws Exception { + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false); + mContextFixture.putBooleanResource( + R.bool.config_satellite_allow_check_message_in_not_connected, true); + mDatagramControllerUT.onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + + boolean result = + mDatagramControllerUT.needsWaitingForSatelliteConnected( + DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS); + + assertTrue(result); + } + private void testUpdateSendStatus(boolean isDemoMode, int datagramType, int sendState) { mDatagramControllerUT.setDemoMode(isDemoMode); clearAllInvocations(); @@ -197,7 +270,7 @@ errorCode); verify(mMockSatelliteSessionController) - .onDatagramTransferStateChanged(eq(sendState), anyInt()); + .onDatagramTransferStateChanged(eq(sendState), anyInt(), anyInt()); verify(mMockPointingAppController).updateSendDatagramTransferState( eq(SUB_ID), eq(datagramType), eq(sendState), eq(sendPendingCount), eq(errorCode)); @@ -219,10 +292,10 @@ int receivePendingCount = 1; int errorCode = SATELLITE_RESULT_SUCCESS; mDatagramControllerUT.updateReceiveStatus( - SUB_ID, receiveState, receivePendingCount, errorCode); + SUB_ID, DATAGRAM_TYPE_SOS_MESSAGE, receiveState, receivePendingCount, errorCode); verify(mMockSatelliteSessionController) - .onDatagramTransferStateChanged(anyInt(), eq(receiveState)); + .onDatagramTransferStateChanged(anyInt(), eq(receiveState), anyInt()); verify(mMockPointingAppController).updateReceiveDatagramTransferState( eq(SUB_ID), eq(receiveState), eq(receivePendingCount), eq(errorCode));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java index 21ee476..dc973af 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; @@ -257,7 +258,7 @@ eq(SATELLITE_RESULT_SUCCESS)); verifyNoMoreInteractions(mMockDatagramController); verify(mMockSessionMetricsStats, times(1)) - .addCountOfSuccessfulOutgoingDatagram(eq(datagramType)); + .addCountOfSuccessfulOutgoingDatagram(eq(datagramType), anyLong()); verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram( any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class)); assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted()); @@ -398,7 +399,7 @@ any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class)); assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS); verify(mMockSessionMetricsStats, times(1)) - .addCountOfSuccessfulOutgoingDatagram(anyInt()); + .addCountOfSuccessfulOutgoingDatagram(anyInt(), anyLong()); clearInvocations(mMockSatelliteModemInterface); clearInvocations(mMockDatagramController); clearInvocations(mMockSessionMetricsStats); @@ -523,7 +524,7 @@ eq(SATELLITE_RESULT_SUCCESS)); assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_SUCCESS); verify(mMockSessionMetricsStats, times(1)) - .addCountOfSuccessfulOutgoingDatagram(eq(datagramType)); + .addCountOfSuccessfulOutgoingDatagram(eq(datagramType), anyLong()); mDatagramDispatcherUT.setDemoMode(false); mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false); } @@ -618,7 +619,7 @@ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0), eq(SATELLITE_RESULT_SUCCESS)); verify(mMockSessionMetricsStats, times(1)) - .addCountOfSuccessfulOutgoingDatagram(eq(DATAGRAM_TYPE2)); + .addCountOfSuccessfulOutgoingDatagram(eq(DATAGRAM_TYPE2), anyLong()); mDatagramDispatcherUT.setDemoMode(false); mDatagramDispatcherUT.setDeviceAlignedWithSatellite(false); @@ -731,7 +732,7 @@ anyInt(), any(SatelliteDatagram.class)); verify(mMockDatagramController).pollPendingSatelliteDatagrams(anyInt(), any()); verify(mMockSessionMetricsStats, times(1)) - .addCountOfSuccessfulOutgoingDatagram(anyInt()); + .addCountOfSuccessfulOutgoingDatagram(anyInt(), anyLong()); // Test when overlay config config_send_satellite_datagram_to_modem_in_demo_mode is // false @@ -824,7 +825,7 @@ eq(SATELLITE_RESULT_SUCCESS)); verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms)); - mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true); + mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true); processAllMessages(); mInOrder.verify(mMockDatagramController) @@ -858,7 +859,7 @@ eq(SATELLITE_RESULT_SUCCESS)); verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms)); - mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, false); + mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, false); processAllMessages(); mInOrder.verify(mMockDatagramController) @@ -1041,7 +1042,7 @@ eq(1), eq(SATELLITE_RESULT_SUCCESS)); verify(mMockSessionMetricsStats, times(1)) - .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos)); + .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos), anyLong()); verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram( any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class)); assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted()); @@ -1056,7 +1057,7 @@ eq(SATELLITE_RESULT_SUCCESS)); verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms)); - mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true); + mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true); processAllMessages(); mInOrder.verify(mMockDatagramController) @@ -1107,7 +1108,7 @@ processAllMessages(); verifyZeroInteractions(mMockSatelliteModemInterface); - mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true); + mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true); processAllMessages(); mInOrder.verify(mMockDatagramController) .updateSendStatus(eq(SUB_ID), eq(datagramTypeSms), @@ -1133,7 +1134,7 @@ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0), eq(SATELLITE_RESULT_SUCCESS)); verify(mMockSessionMetricsStats, times(1)) - .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos)); + .addCountOfSuccessfulOutgoingDatagram(eq(datagramTypeSos), anyLong()); verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram( any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class)); assertFalse(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted()); @@ -1204,6 +1205,6 @@ SmsDispatchersController.PendingRequest.TYPE_TEXT, null, "test-app", Binder.getCallingUserHandle().getIdentifier(), "1111", "2222", asArrayList(null), asArrayList(null), false, null, 0, asArrayList("text"), null, false, 0, false, - 10, 100L, false); + 10, 100L, false, false); } }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java index e964ced..a1f63d0 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -187,6 +187,7 @@ mInOrder.verify(mMockDatagramController) .needsWaitingForSatelliteConnected(eq(SatelliteManager.DATAGRAM_TYPE_UNKNOWN)); mInOrder.verify(mMockDatagramController).updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState(eq(false)); @@ -201,6 +202,7 @@ mInOrder.verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); verify(mMockSatelliteModemInterface, times(1)) @@ -225,9 +227,11 @@ moveTimeForward(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS); processAllMessages(); mInOrder.verify(mMockDatagramController).updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED), eq(0), eq(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE)); mInOrder.verify(mMockDatagramController).updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); verifyZeroInteractions(mMockSatelliteModemInterface); @@ -266,10 +270,12 @@ mInOrder.verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); mInOrder.verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR)); @@ -289,10 +295,12 @@ mInOrder.verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); mInOrder.verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); @@ -312,10 +320,12 @@ mInOrder.verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); mInOrder.verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); assertTrue(testSatelliteDatagramCallback.waitForOnSatelliteDatagramReceived()); @@ -339,6 +349,7 @@ mInOrder.verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS), eq(10), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); verify(mMockSessionMetricsStats, times(1)).addCountOfSuccessfulIncomingDatagram(); @@ -356,16 +367,19 @@ verify(mMockDatagramController, times(1)).popDemoModeDatagram(); verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), anyInt(), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED), anyInt(), eq(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE)); verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), anyInt(), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); @@ -387,17 +401,20 @@ verify(mMockDatagramController, never()).popDemoModeDatagram(); verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING), anyInt(), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); processAllFutureMessages(); verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED), anyInt(), eq(SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE)); verify(mMockDatagramController) .updateReceiveStatus(eq(SUB_ID), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), anyInt(), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); @@ -449,10 +466,12 @@ mInOrder.verify(mMockDatagramController) .updateReceiveStatus(anyInt(), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED), eq(10), eq(SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED)); mInOrder.verify(mMockDatagramController) .updateReceiveStatus(anyInt(), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); } @@ -468,6 +487,7 @@ mInOrder.verify(mMockDatagramController) .updateReceiveStatus(anyInt(), + eq(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE), eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS)); }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java index 873078e..354b20f 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
@@ -80,7 +80,7 @@ doReturn(Arrays.asList(SATELLITE_PLMN_ARRAY)) .when(mMockSatelliteController).getSatellitePlmnsForCarrier(anyInt()); doReturn(mSatelliteSupportedServiceList).when(mMockSatelliteController) - .getSupportedSatelliteServices(SUB_ID, SATELLITE_PLMN); + .getSupportedSatelliteServicesForPlmn(SUB_ID, SATELLITE_PLMN); } @After
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java index db46a00..6f716b5 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -16,6 +16,14 @@ package com.android.internal.telephony.satellite; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED; +import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN; +import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION; +import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC; +import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL; +import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL; import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT; import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT; import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT; @@ -23,14 +31,21 @@ import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_NIDD_APN_NAME_STRING; +import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL; +import static android.telephony.CarrierConfigManager.SATELLITE_DATA_SUPPORT_ALL; +import static android.telephony.CarrierConfigManager.SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED; +import static android.telephony.CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED; import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_DATA; +import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_SMS; +import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_VOICE; import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS; import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD; import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GREAT; import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE; import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_POOR; import static android.telephony.satellite.SatelliteManager.KEY_DEMO_MODE_ENABLED; +import static android.telephony.satellite.SatelliteManager.KEY_DEPROVISION_SATELLITE_TOKENS; import static android.telephony.satellite.SatelliteManager.KEY_EMERGENCY_MODE_ENABLED; import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH; import static android.telephony.satellite.SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS; @@ -41,6 +56,7 @@ import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_NEXT_VISIBILITY; import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_PROVISIONED; import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_SUPPORTED; +import static android.telephony.satellite.SatelliteManager.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT; import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_EMTC_NTN; import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN; import static android.telephony.satellite.SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN; @@ -71,16 +87,20 @@ import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS; import static com.android.internal.telephony.satellite.SatelliteController.DEFAULT_CARRIER_EMERGENCY_CALL_WAIT_FOR_CONNECTION_TIMEOUT_MILLIS; +import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_DATA_PLAN_METERED; +import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_DATA_PLAN_UNMETERED; import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_FALSE; import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_MODE_ENABLED_TRUE; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -89,6 +109,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyVararg; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -104,10 +125,16 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.NotificationManager; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; +import android.hardware.devicestate.DeviceState; +import android.net.Uri; import android.os.AsyncResult; import android.os.Bundle; import android.os.CancellationSignal; @@ -119,14 +146,20 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ResultReceiver; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.telephony.AccessNetworkConstants; import android.telephony.CarrierConfigManager; import android.telephony.CellSignalStrength; import android.telephony.NetworkRegistrationInfo; import android.telephony.Rlog; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; +import android.telephony.satellite.EarfcnRange; import android.telephony.satellite.INtnSignalStrengthCallback; import android.telephony.satellite.ISatelliteCapabilitiesCallback; import android.telephony.satellite.ISatelliteDatagramCallback; @@ -134,17 +167,22 @@ import android.telephony.satellite.ISatelliteProvisionStateCallback; import android.telephony.satellite.ISatelliteSupportedStateCallback; import android.telephony.satellite.ISatelliteTransmissionUpdateCallback; +import android.telephony.satellite.ISelectedNbIotSatelliteSubscriptionCallback; import android.telephony.satellite.NtnSignalStrength; import android.telephony.satellite.SatelliteCapabilities; import android.telephony.satellite.SatelliteDatagram; +import android.telephony.satellite.SatelliteInfo; import android.telephony.satellite.SatelliteManager; import android.telephony.satellite.SatelliteManager.SatelliteException; import android.telephony.satellite.SatelliteModemEnableRequestAttributes; +import android.telephony.satellite.SatellitePosition; import android.telephony.satellite.SatelliteSubscriberInfo; import android.telephony.satellite.SatelliteSubscriberProvisionStatus; import android.telephony.satellite.SatelliteSubscriptionInfo; +import android.telephony.satellite.SystemSelectionSpecifier; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.IntArray; import android.util.Pair; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -166,6 +204,7 @@ import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -181,9 +220,12 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -206,6 +248,7 @@ private static final int TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(60); + private static final String SATELLITE_PLMN = "00103"; private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>> mCarrierConfigChangedListenerList = new ArrayList<>(); @@ -237,6 +280,7 @@ @Mock private Resources mResources; @Mock private SubscriptionManager mSubscriptionManager; @Mock private SubscriptionInfo mSubscriptionInfo; + @Mock private PackageManager mMockPManager; private Semaphore mIIntegerConsumerSemaphore = new Semaphore(0); private IIntegerConsumer mIIntegerConsumer = new IIntegerConsumer.Stub() { @@ -513,6 +557,26 @@ } }; + private int mQueriedSystemSelectionChannelUpdatedResultCode = SATELLITE_RESULT_SUCCESS; + private Semaphore mSystemSelectionChannelUpdatedSemaphore = new Semaphore(0); + private ResultReceiver mSystemSelectionChannelUpdatedReceiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + mQueriedSystemSelectionChannelUpdatedResultCode = resultCode; + try { + mSystemSelectionChannelUpdatedSemaphore.release(); + } catch (Exception ex) { + fail("mSystemSelectionChannelUpdatedReceiver: Got exception in releasing " + + "semaphore, ex=" + + ex); + } + } + }; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + @Before public void setUp() throws Exception { super.setUp(getClass().getSimpleName()); @@ -548,9 +612,11 @@ when(mPhone.getServiceState()).thenReturn(mServiceState); when(mPhone.getSubId()).thenReturn(SUB_ID); when(mPhone.getPhoneId()).thenReturn(0); + when(mPhone.getSignalStrengthController()).thenReturn(mSignalStrengthController); when(mPhone2.getServiceState()).thenReturn(mServiceState2); when(mPhone2.getSubId()).thenReturn(SUB_ID1); when(mPhone2.getPhoneId()).thenReturn(1); + when(mPhone2.getSignalStrengthController()).thenReturn(mSignalStrengthController); mContextFixture.putStringArrayResource( R.array.config_satellite_providers, @@ -561,6 +627,9 @@ mContextFixture.putIntResource( R.integer.config_satellite_wait_for_cellular_modem_off_timeout_millis, TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS); + mContextFixture.putIntArrayResource( + R.array.config_foldedDeviceStates, + new int[0]); doReturn(ACTIVE_SUB_IDS).when(mMockSubscriptionManagerService).getActiveSubIdList(true); mCarrierConfigBundle = mContextFixture.getCarrierConfigBundle(); @@ -619,7 +688,7 @@ doNothing().when(mMockProvisionMetricsStats).reportProvisionMetrics(); doNothing().when(mMockControllerMetricsStats).reportDeprovisionCount(anyInt()); when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true); - when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false); + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); doReturn(mSST).when(mPhone).getServiceStateTracker(); doReturn(mSST).when(mPhone2).getServiceStateTracker(); doReturn(mServiceState).when(mSST).getServiceState(); @@ -922,6 +991,7 @@ @Test public void testRequestSatelliteEnabled() { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mFeatureFlags.satelliteStateChangeListener()).thenReturn(true); mIsSatelliteEnabledSemaphore.drainPermits(); // Fail to enable satellite when SatelliteController is not fully loaded yet. @@ -978,6 +1048,7 @@ doReturn(false).when(mTelecomManager).isInEmergencyCall(); // Successfully enable satellite + reset(mTelephonyRegistryManager); mIIntegerConsumerResults.clear(); mIIntegerConsumerSemaphore.drainPermits(); mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false; @@ -999,12 +1070,14 @@ verify(mMockDatagramController, times(2)).setDemoMode(eq(false)); verify(mMockControllerMetricsStats, times(1)).onSatelliteEnabled(); verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementSuccessCount(); + verify(mTelephonyRegistryManager).notifySatelliteStateChanged(eq(true)); // Successfully disable satellite when radio is turned off. + reset(mTelephonyRegistryManager); clearInvocations(mMockSatelliteSessionController); clearInvocations(mMockDatagramController); mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController); - when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(true); + mSatelliteControllerUT.isSatelliteBeingDisabled = true; mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false; mSatelliteControllerUT.setSettingsKeyToAllowDeviceRotationCalled = false; setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS); @@ -1023,7 +1096,8 @@ verify(mMockSatelliteSessionController, times(2)).setDemoMode(eq(false)); verify(mMockDatagramController, times(2)).setDemoMode(eq(false)); verify(mMockControllerMetricsStats, times(1)).onSatelliteDisabled(); - when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(false); + mSatelliteControllerUT.isSatelliteBeingDisabled = false; + verify(mTelephonyRegistryManager, atLeastOnce()).notifySatelliteStateChanged(eq(false)); // Fail to enable satellite when radio is off. mIIntegerConsumerResults.clear(); @@ -1059,6 +1133,7 @@ verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementFailCount(); // Successfully enable satellite when radio is on. + reset(mTelephonyRegistryManager); mIIntegerConsumerResults.clear(); mIIntegerConsumerSemaphore.drainPermits(); mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false; @@ -1077,6 +1152,7 @@ verify(mMockDatagramController, times(3)).setDemoMode(eq(false)); verify(mMockControllerMetricsStats, times(2)).onSatelliteEnabled(); verify(mMockControllerMetricsStats, times(2)).reportServiceEnablementSuccessCount(); + verify(mTelephonyRegistryManager).notifySatelliteStateChanged(eq(true)); // Successfully enable satellite when it is already enabled. mIIntegerConsumerResults.clear(); @@ -1097,6 +1173,7 @@ verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS); // Successfully disable satellite. + reset(mTelephonyRegistryManager); mIIntegerConsumerResults.clear(); mIIntegerConsumerSemaphore.drainPermits(); setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS); @@ -1105,6 +1182,7 @@ assertTrue(waitForIIntegerConsumerResult(1)); assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0)); verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS); + verify(mTelephonyRegistryManager, atLeastOnce()).notifySatelliteStateChanged(eq(false)); // Disable satellite when satellite is already disabled. mIIntegerConsumerResults.clear(); @@ -1171,7 +1249,7 @@ mIIntegerConsumerResults.clear(); mIIntegerConsumerSemaphore.drainPermits(); mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController); - when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(true); + mSatelliteControllerUT.isSatelliteBeingDisabled = true; mSatelliteControllerUT.requestSatelliteEnabled(true, false, false, mIIntegerConsumer); processAllMessages(); assertTrue(waitForIIntegerConsumerResult(1)); @@ -1180,7 +1258,7 @@ mIIntegerConsumerResults.clear(); mIIntegerConsumerSemaphore.drainPermits(); resetSatelliteControllerUTToOffAndProvisionedState(); - when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(false); + mSatelliteControllerUT.isSatelliteBeingDisabled = false; /** * Make areAllRadiosDisabled return false and move mWaitingForRadioDisabled to true, which @@ -1548,7 +1626,7 @@ public void testIsSatelliteEnabled() { logd("testIsSatelliteEnabled: starting"); setUpResponseForRequestIsSatelliteEnabled(true, SATELLITE_RESULT_SUCCESS); - assertFalse(mSatelliteControllerUT.isSatelliteEnabled()); + assertFalse(mSatelliteControllerUT.isSatelliteEnabledOrBeingEnabled()); mIsSatelliteEnabledSemaphore.drainPermits(); mSatelliteControllerUT.requestIsSatelliteEnabled(mIsSatelliteEnabledReceiver); processAllMessages(); @@ -1564,7 +1642,8 @@ mSatelliteControllerUT.requestIsSatelliteEnabled(mIsSatelliteEnabledReceiver); processAllMessages(); assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedIsSatelliteEnabledResultCode); - assertEquals(mSatelliteControllerUT.isSatelliteEnabled(), mQueriedIsSatelliteEnabled); + assertEquals(mSatelliteControllerUT.isSatelliteEnabledOrBeingEnabled(), + mQueriedIsSatelliteEnabled); } @Test @@ -1602,6 +1681,11 @@ public void onRegistrationFailure(int causeCode) { logd("onRegistrationFailure: causeCode=" + causeCode); } + + @Override + public void onTerrestrialNetworkAvailableChanged(boolean isAvailable) { + logd("onTerrestrialNetworkAvailableChanged: isAvailable=" + isAvailable); + } }; int errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(callback); assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode); @@ -1633,6 +1717,11 @@ public void onRegistrationFailure(int causeCode) { logd("onRegistrationFailure: causeCode=" + causeCode); } + + @Override + public void onTerrestrialNetworkAvailableChanged(boolean isAvailable) { + logd("onTerrestrialNetworkAvailableChanged: isAvailable=" + isAvailable); + } }; mSatelliteControllerUT.unregisterForModemStateChanged(callback); verify(mMockSatelliteSessionController, never()) @@ -1646,6 +1735,7 @@ @Test public void testRegisterForSatelliteProvisionStateChanged() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); Semaphore semaphore = new Semaphore(0); ISatelliteProvisionStateCallback callback = new ISatelliteProvisionStateCallback.Stub() { @@ -1667,12 +1757,7 @@ } }; int errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback); - assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode); - - setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS); - verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS); - errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback); - assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode); + assertEquals(SATELLITE_RESULT_SUCCESS, errorCode); resetSatelliteControllerUT(); setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS); @@ -1683,6 +1768,14 @@ semaphore, 1, "testRegisterForSatelliteProvisionStateChanged")); assertEquals(SATELLITE_RESULT_SUCCESS, errorCode); + try { + setSatelliteSubscriberTesting(); + } catch (Exception ex) { + fail("provisionSatelliteService.setSatelliteSubscriberTesting: ex=" + ex); + } + doReturn(true).when(mMockSubscriptionManagerService).isSatelliteProvisionedForNonIpDatagram( + anyInt()); + String mText = "This is test provision data."; byte[] testProvisionData = mText.getBytes(); CancellationSignal cancellationSignal = new CancellationSignal(); @@ -1694,8 +1787,8 @@ processAllMessages(); assertTrue(waitForForEvents( semaphore, 1, "testRegisterForSatelliteProvisionStateChanged")); - mSatelliteControllerUT.unregisterForSatelliteProvisionStateChanged(callback); + semaphore.drainPermits(); cancelRemote = mSatelliteControllerUT.provisionSatelliteService( TEST_SATELLITE_TOKEN, testProvisionData, mIIntegerConsumer); @@ -1715,11 +1808,11 @@ logd("onSatelliteDatagramReceived"); } }; - when(mMockDatagramController.registerForSatelliteDatagram(eq(SUB_ID), eq(callback))) + when(mMockDatagramController.registerForSatelliteDatagram(anyInt(), eq(callback))) .thenReturn(SATELLITE_RESULT_SUCCESS); int errorCode = mSatelliteControllerUT.registerForIncomingDatagram(callback); assertEquals(SATELLITE_RESULT_SUCCESS, errorCode); - verify(mMockDatagramController).registerForSatelliteDatagram(eq(SUB_ID), eq(callback)); + verify(mMockDatagramController).registerForSatelliteDatagram(anyInt(), eq(callback)); } @Test @@ -1734,9 +1827,9 @@ } }; doNothing().when(mMockDatagramController) - .unregisterForSatelliteDatagram(eq(SUB_ID), eq(callback)); + .unregisterForSatelliteDatagram(anyInt(), eq(callback)); mSatelliteControllerUT.unregisterForIncomingDatagram(callback); - verify(mMockDatagramController).unregisterForSatelliteDatagram(eq(SUB_ID), eq(callback)); + verify(mMockDatagramController).unregisterForSatelliteDatagram(anyInt(), eq(callback)); } @Test @@ -1829,6 +1922,8 @@ @Test public void testProvisionSatelliteService() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false); + String mText = "This is test provision data."; byte[] testProvisionData = mText.getBytes(); CancellationSignal cancellationSignal = new CancellationSignal(); @@ -1913,6 +2008,7 @@ @Test public void testDeprovisionSatelliteService() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false); mIIntegerConsumerSemaphore.drainPermits(); mIIntegerConsumerResults.clear(); setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS); @@ -1984,7 +2080,7 @@ SUB_ID); assertEquals(EMPTY_STRING_ARRAY.length, satellitePlmnList.size()); List<Integer> supportedSatelliteServices = - mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00101"); + mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(SUB_ID, "00101"); assertTrue(supportedSatelliteServices.isEmpty()); String[] satelliteProviderStrArray = {"00101", "00102"}; @@ -2011,7 +2107,7 @@ satellitePlmnList = testSatelliteController.getSatellitePlmnsForCarrier(SUB_ID); assertTrue(satellitePlmnList.isEmpty()); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00101"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00101"); assertTrue(supportedSatelliteServices.isEmpty()); // Add entitlement provided PLMNs. @@ -2027,16 +2123,16 @@ processAllMessages(); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00102"); assertTrue(supportedSatelliteServices.isEmpty()); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00103"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00103"); assertTrue(supportedSatelliteServices.isEmpty()); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00104"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00104"); assertTrue(supportedSatelliteServices.isEmpty()); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00105"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00105"); assertTrue(supportedSatelliteServices.isEmpty()); // Trigger carrier config changed with carrierEnabledSatelliteFlag enabled @@ -2055,27 +2151,27 @@ assertTrue(Arrays.equals( expectedSupportedSatellitePlmns, satellitePlmnList.stream().toArray())); supportedSatelliteServices = - mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00102"); + mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(SUB_ID, "00102"); // "00101" should return carrier config assigned value, though it is in allowed list. assertTrue(Arrays.equals(expectedSupportedServices2, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); supportedSatelliteServices = - mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00103"); + mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn(SUB_ID, "00103"); assertTrue(Arrays.equals(expectedSupportedServices3, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); // "00104", and "00105" should return default supported service. supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00104"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00104"); assertTrue(Arrays.equals(defaultSupportedServices, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00105"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00105"); assertTrue(Arrays.equals(defaultSupportedServices, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) @@ -2096,33 +2192,33 @@ assertTrue(satellitePlmnList.isEmpty()); // "00102" and "00103" should return default supported service for SUB_ID. supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00102"); assertTrue(Arrays.equals(defaultSupportedServices, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00103"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00103"); assertTrue(Arrays.equals(defaultSupportedServices, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); // "00104", and "00105" should return default supported service for SUB_ID. supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00104"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00104"); assertTrue(Arrays.equals(defaultSupportedServices, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00105"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID, "00105"); assertTrue(Arrays.equals(defaultSupportedServices, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00102"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID1, "00102"); assertNotNull(supportedSatelliteServices); assertTrue(Arrays.equals(expectedSupportedServices2, supportedSatelliteServices.stream() @@ -2130,20 +2226,20 @@ .toArray())); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00103"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID1, "00103"); assertTrue(Arrays.equals(expectedSupportedServices3, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); /* "00104", and "00105" should return default supported service. */ supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00104"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID1, "00104"); assertTrue(Arrays.equals(defaultSupportedServices, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) .toArray())); supportedSatelliteServices = - testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00105"); + testSatelliteController.getSupportedSatelliteServicesForPlmn(SUB_ID1, "00105"); assertTrue(Arrays.equals(defaultSupportedServices, supportedSatelliteServices.stream() .mapToInt(Integer::intValue) @@ -2491,6 +2587,7 @@ assertTrue( mQueriedSatelliteCapabilities.getSupportedRadioTechnologies().contains( satelliteController.getSupportedNtnRadioTechnology())); + assertEquals(mQueriedSatelliteCapabilities.getMaxBytesPerOutgoingDatagram(), 255); assertTrue(satelliteController.isSatelliteAttachRequired()); when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false); @@ -2517,14 +2614,14 @@ clearInvocations(mMockSatelliteSessionController); clearInvocations(mMockDatagramController); - when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(true); + mSatelliteControllerUT.isSatelliteBeingDisabled = true; sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_NOT_CONNECTED, null); processAllMessages(); verify(mMockSatelliteSessionController, times(1)).onSatelliteModemStateChanged( SATELLITE_MODEM_STATE_NOT_CONNECTED); clearInvocations(mMockSatelliteSessionController); - when(mMockSatelliteSessionController.isInDisablingState()).thenReturn(false); + mSatelliteControllerUT.isSatelliteBeingDisabled = false; sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_NOT_CONNECTED, null); processAllMessages(); verify(mMockSatelliteSessionController, never()).onSatelliteModemStateChanged( @@ -2534,7 +2631,6 @@ @Test public void testRequestNtnSignalStrengthWithFeatureFlagEnabled() { when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true); - resetSatelliteControllerUT(); mRequestNtnSignalStrengthSemaphore.drainPermits(); @@ -3077,7 +3173,8 @@ // Verify call the requestSetSatelliteEnabledForCarrier to enable the satellite when // satellite service is enabled by entitlement server. mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true, new ArrayList<>(), - new ArrayList<>(), mIIntegerConsumer); + new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), + new HashMap<>(), mIIntegerConsumer); processAllMessages(); assertTrue(waitForIIntegerConsumerResult(1)); @@ -3097,7 +3194,8 @@ .when(mMockSatelliteModemInterface).isSatelliteServiceSupported(); setUpResponseForRequestSetSatelliteEnabledForCarrier(false, SATELLITE_RESULT_SUCCESS); mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, new ArrayList<>(), - new ArrayList<>(), mIIntegerConsumer); + new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), + new HashMap<>(), mIIntegerConsumer); processAllMessages(); assertTrue(waitForIIntegerConsumerResult(1)); @@ -3128,7 +3226,8 @@ List<String> entitlementPlmnList = new ArrayList<>(); List<String> barredPlmnList = new ArrayList<>(); mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, - entitlementPlmnList, barredPlmnList, mIIntegerConsumer); + entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled( any(SatelliteModemEnableRequestAttributes.class), any(Message.class)); @@ -3186,7 +3285,8 @@ reset(mMockSatelliteModemInterface); entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", ""}).toList(); mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, - entitlementPlmnList, barredPlmnList, mIIntegerConsumer); + entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled( any(SatelliteModemEnableRequestAttributes.class), any(Message.class)); @@ -3194,7 +3294,8 @@ reset(mMockSatelliteModemInterface); entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "123456789"}).toList(); mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, - entitlementPlmnList, barredPlmnList, mIIntegerConsumer); + entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled( any(SatelliteModemEnableRequestAttributes.class), any(Message.class)); @@ -3202,7 +3303,8 @@ reset(mMockSatelliteModemInterface); entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "12"}).toList(); mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, - entitlementPlmnList, barredPlmnList, mIIntegerConsumer); + entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled( any(SatelliteModemEnableRequestAttributes.class), any(Message.class)); @@ -3210,7 +3312,8 @@ reset(mMockSatelliteModemInterface); entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "1234"}).toList(); mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, - entitlementPlmnList, barredPlmnList, mIIntegerConsumer); + entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled( any(SatelliteModemEnableRequestAttributes.class), any(Message.class)); } @@ -3219,7 +3322,8 @@ List<String> mergedPlmnList, List<String> overlayConfigPlmnList, List<String> barredPlmnList) { mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, - entitlementPlmnList, barredPlmnList, mIIntegerConsumer); + entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); List<String> plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier( SUB_ID); @@ -3267,7 +3371,8 @@ setConfigData(new ArrayList<>()); setCarrierConfigDataPlmnList(new ArrayList<>()); invokeCarrierConfigChanged(); - servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "31016"); + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "31016"); assertEquals(new ArrayList<>(), servicesPerPlmn); // Verify whether the carrier config plmn list is returned with conditions below @@ -3276,13 +3381,16 @@ setConfigData(new ArrayList<>()); setCarrierConfigDataPlmnList(Arrays.asList("00101", "00102", "00104")); invokeCarrierConfigChanged(); - servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00101"); + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "00101"); assertEquals(Arrays.asList(2).stream().sorted().toList(), servicesPerPlmn.stream().sorted().toList()); - servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00102"); + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "00102"); assertEquals(Arrays.asList(1, 3).stream().sorted().toList(), servicesPerPlmn.stream().sorted().toList()); - servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00104"); + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "00104"); assertEquals(Arrays.asList(2).stream().sorted().toList(), servicesPerPlmn.stream().sorted().toList()); @@ -3292,15 +3400,19 @@ setConfigData(Arrays.asList("00101", "00102", "31024")); setCarrierConfigDataPlmnList(Arrays.asList("00101", "00102", "00104")); invokeCarrierConfigChanged(); - servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00101"); + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "00101"); assertEquals(Arrays.asList(1).stream().sorted().toList(), servicesPerPlmn.stream().sorted().toList()); - servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00102"); + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "00102"); assertEquals(Arrays.asList(3).stream().sorted().toList(), servicesPerPlmn.stream().sorted().toList()); - servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00104"); + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "00104"); assertEquals(new ArrayList<>(), servicesPerPlmn.stream().sorted().toList()); - servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "31024"); + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "31024"); assertEquals(Arrays.asList(5).stream().sorted().toList(), servicesPerPlmn.stream().sorted().toList()); } @@ -3433,21 +3545,24 @@ // Change SUB_ID's EntitlementStatus to true mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true, new ArrayList<>(), - new ArrayList<>(), mIIntegerConsumer); + new ArrayList<>(), new HashMap<>(), new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID)); assertEquals(false, satelliteEnabledPerCarrier.get(SUB_ID1)); // Change SUB_ID1's EntitlementStatus to true mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID1, true, new ArrayList<>(), - new ArrayList<>(), mIIntegerConsumer); + new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), + new HashMap<>(), mIIntegerConsumer); assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID)); assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID1)); // Change SUB_ID's EntitlementStatus to false mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, new ArrayList<>(), - new ArrayList<>(), mIIntegerConsumer); + new ArrayList<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), + new HashMap<>(), mIIntegerConsumer); assertEquals(false, satelliteEnabledPerCarrier.get(SUB_ID)); assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID1)); @@ -3579,10 +3694,18 @@ @Test public void testHandleEventServiceStateChanged() { + mContextFixture.putBooleanResource( + R.bool.config_satellite_should_notify_availability, true); when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, + CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC); + invokeCarrierConfigChanged(); + // Do nothing when the satellite is not connected doReturn(false).when(mServiceState).isUsingNonTerrestrialNetwork(); sendServiceStateChangedEvent(); + setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS); processAllMessages(); assertFalse(mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false)); verify(mMockNotificationManager, never()).notifyAsUser(anyString(), anyInt(), any(), any()); @@ -3908,6 +4031,96 @@ } @Test + public void testRegisterForSelectedNbIotSatelliteSubscriptionChanged_WithFeatureFlagEnabled() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + + Semaphore semaphore = new Semaphore(0); + final int[] selectedSubIds = new int[1]; + ISelectedNbIotSatelliteSubscriptionCallback callback = + new ISelectedNbIotSatelliteSubscriptionCallback.Stub() { + @Override + public void onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) { + logd("onSelectedNbIotSatelliteSubscriptionChanged: selectedSubId=" + + selectedSubId); + try { + selectedSubIds[0] = selectedSubId; + semaphore.release(); + } catch (Exception ex) { + loge("onSelectedNbIotSatelliteSubscriptionChanged: Got exception in " + + "releasing semaphore, ex=" + ex); + } + } + }; + + int errorCode = mSatelliteControllerUT.registerForSelectedNbIotSatelliteSubscriptionChanged( + callback); + assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode); + + setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS); + verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS); + errorCode = mSatelliteControllerUT.registerForSelectedNbIotSatelliteSubscriptionChanged( + callback); + assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode); + + // Register the callback and verify that the event is reported. + resetSatelliteControllerUT(); + setUpResponseForRequestIsSatelliteProvisioned(true,SATELLITE_RESULT_SUCCESS); + setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS); + verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS); + errorCode = mSatelliteControllerUT.registerForSelectedNbIotSatelliteSubscriptionChanged( + callback); + assertEquals(SATELLITE_RESULT_SUCCESS, errorCode); + int expectedSubId = 1; + sendSelectedNbIotSatelliteSubscriptionChangedEvent(expectedSubId, null); + processAllMessages(); + assertTrue(waitForForEvents( + semaphore, 1, "testRegisterForSelectedNbIotSatelliteSubscriptionChanged")); + assertEquals(expectedSubId, selectedSubIds[0]); + + // Unregister the callback and verify that the event is not reported. + mSatelliteControllerUT.unregisterForSelectedNbIotSatelliteSubscriptionChanged(callback); + sendSelectedNbIotSatelliteSubscriptionChangedEvent(2, null); + processAllMessages(); + assertTrue(waitForForEvents( + semaphore, 0, "testRegisterForSelectedNbIotSatelliteSubscriptionChanged")); + } + + @Test + public void testRegisterForSelectedNbIotSatelliteSubscriptionChanged_WithFeatureFlagDisabled() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(false); + + Semaphore semaphore = new Semaphore(0); + final int[] selectedSubIds = new int[1]; + ISelectedNbIotSatelliteSubscriptionCallback callback = + new ISelectedNbIotSatelliteSubscriptionCallback.Stub() { + @Override + public void onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId) { + logd("onSelectedNbIotSatelliteSubscriptionChanged: selectedSubId=" + + selectedSubId); + try { + selectedSubIds[0] = selectedSubId; + semaphore.release(); + } catch (Exception ex) { + loge("onSelectedNbIotSatelliteSubscriptionChanged: Got exception in " + + "releasing semaphore, ex=" + ex); + } + } + }; + + int errorCode = mSatelliteControllerUT.registerForSelectedNbIotSatelliteSubscriptionChanged( + callback); + assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode); + + // Verify that the event is not reported. + sendSelectedNbIotSatelliteSubscriptionChangedEvent(1, null); + processAllMessages(); + assertTrue(waitForForEvents( + semaphore, 0, "testRegisterForSelectedNbIotSatelliteSubscriptionChanged")); + + + } + + @Test public void testIsSatelliteEmergencyMessagingSupportedViaCarrier() { // Carrier-enabled flag is off when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(false); @@ -4015,8 +4228,10 @@ when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE); when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE); + mSatelliteControllerUT.mIsApplicationSupportsP2P = true; mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true); mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1); + mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, true); int[] supportedServices2 = {2}; int[] supportedServices3 = {1, 3}; PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle(); @@ -4037,18 +4252,51 @@ processAllMessages(); assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone)); + + when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE); + when(mServiceState2.getState()).thenReturn(ServiceState.STATE_IN_SERVICE); + processAllMessages(); + assertFalse(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone)); + + when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE); + when(mServiceState2.getState()).thenReturn(ServiceState.STATE_IN_SERVICE); + mSatelliteControllerUT.overrideCarrierRoamingNtnEligibilityChanged(true, false); + processAllMessages(); + assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone)); + } + + @Test + public void testOverrideCarrierRoamingNtNEligibilityChange() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + mSatelliteControllerUT.overrideCarrierRoamingNtnEligibilityChanged(true, false); + verify(mPhone, times(1)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true)); + clearInvocations(mPhone); + + mSatelliteControllerUT.overrideCarrierRoamingNtnEligibilityChanged(false, false); + verify(mPhone, times(1)).notifyCarrierRoamingNtnEligibleStateChanged(eq(false)); + clearInvocations(mPhone); + + mSatelliteControllerUT.overrideCarrierRoamingNtnEligibilityChanged(false, true); + verify(mPhone, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true)); + clearInvocations(mPhone); } @Test public void testNotifyNtnEligibilityHysteresisTimedOut() { + mContextFixture.putBooleanResource( + R.bool.config_satellite_should_notify_availability, true); when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE); when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE); + mSatelliteControllerUT.mIsApplicationSupportsP2P = true; + mSatelliteControllerUT.setIsSatelliteSupported(true); mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true); - mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1); + mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, + CARRIER_ROAMING_NTN_CONNECT_MANUAL); mCarrierConfigBundle.putInt( KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 1 * 60); + mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, true); int[] supportedServices2 = {2}; int[] supportedServices3 = {1, 3}; PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle(); @@ -4067,6 +4315,7 @@ } mSatelliteControllerUT.setSatellitePhone(1); mSatelliteControllerUT.isSatelliteAllowedCallback = null; + setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS); processAllMessages(); mSatelliteControllerUT.elapsedRealtime = 0; assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone)); @@ -4123,6 +4372,43 @@ } @Test + public void testNotifyCarrierRoamingNtnSignalStrengthChanged() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + + sendSignalStrengthChangedEvent(mPhone.getPhoneId()); + processAllMessages(); + ArgumentCaptor<NtnSignalStrength> captor = ArgumentCaptor.forClass(NtnSignalStrength.class); + verify(mPhone, times(1)).notifyCarrierRoamingNtnSignalStrengthChanged( + captor.capture()); + NtnSignalStrength actualSignalStrength = captor.getValue(); + assertEquals(NTN_SIGNAL_STRENGTH_NONE, actualSignalStrength.getLevel()); + clearInvocations(mPhone); + + when(mSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_GOOD); + when(mPhone.getSignalStrength()).thenReturn(mSignalStrength); + mCarrierConfigBundle.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 1 * 60); + mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true); + for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair + : mCarrierConfigChangedListenerList) { + pair.first.execute(() -> pair.second.onCarrierConfigChanged( + /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0) + ); + } + processAllMessages(); + when(mServiceState.isUsingNonTerrestrialNetwork()).thenReturn(true); + when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE); + sendServiceStateChangedEvent(); + processAllMessages(); + captor = ArgumentCaptor.forClass(NtnSignalStrength.class); + verify(mPhone, times(1)).notifyCarrierRoamingNtnSignalStrengthChanged( + captor.capture()); + actualSignalStrength = captor.getValue(); + assertEquals(NTN_SIGNAL_STRENGTH_GOOD, actualSignalStrength.getLevel()); + clearInvocations(mPhone); + } + + @Test public void testGetWwanIsInService() { when(mServiceState.getNetworkRegistrationInfoListForTransportType( eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN))) @@ -4145,16 +4431,8 @@ .thenReturn(List.of(nri)); assertTrue(mSatelliteControllerUT.getWwanIsInService(mServiceState)); - nri = new NetworkRegistrationInfo.Builder() - .setEmergencyOnly(true) - .build(); - when(mServiceState.getNetworkRegistrationInfoListForTransportType( - eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN))) - .thenReturn(List.of(nri)); - assertTrue(mSatelliteControllerUT.getWwanIsInService(mServiceState)); - nri = new NetworkRegistrationInfo.Builder().setRegistrationState( - NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING) + NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING) .build(); when(mServiceState.getNetworkRegistrationInfoListForTransportType( eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN))) @@ -4184,6 +4462,11 @@ resultErrorCode[0] = causeCode; semaphore.release(); } + + @Override + public void onTerrestrialNetworkAvailableChanged(boolean isAvailable) { + logd("onTerrestrialNetworkAvailableChanged: isAvailable=" + isAvailable); + } }; resetSatelliteControllerUTToSupportedAndProvisionedState(); mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController); @@ -4202,6 +4485,86 @@ assertEquals(expectedErrorCode, resultErrorCode[0]); } + @RequiresFlagsDisabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + @Test + public void testDetermineIsFoldable_overlayConfigurationValues() { + // isFoldable should return false with the base configuration. + assertFalse(mSatelliteControllerUT.isFoldable(mContext, + mSatelliteControllerUT.getSupportedDeviceStates())); + + mContextFixture.putIntArrayResource(R.array.config_foldedDeviceStates, new int[2]); + assertTrue(mSatelliteControllerUT.isFoldable(mContext, + mSatelliteControllerUT.getSupportedDeviceStates())); + } + + @RequiresFlagsEnabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION) + @Test + public void testDetermineIsFoldable_deviceStateManager() { + // isFoldable should return false with the base configuration. + assertFalse(mSatelliteControllerUT.isFoldable(mContext, + mSatelliteControllerUT.getSupportedDeviceStates())); + + DeviceState foldedDeviceState = new DeviceState(new DeviceState.Configuration.Builder( + 0 /* identifier */, "FOLDED") + .setSystemProperties(Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) + .setPhysicalProperties( + Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) + .build()); + DeviceState unfoldedDeviceState = new DeviceState(new DeviceState.Configuration.Builder( + 1 /* identifier */, "UNFOLDED") + .setSystemProperties(Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) + .setPhysicalProperties( + Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)) + .build()); + List<DeviceState> foldableDeviceStateList = List.of(foldedDeviceState, unfoldedDeviceState); + assertTrue(mSatelliteControllerUT.isFoldable(mContext, foldableDeviceStateList)); + } + + @Test + public void testTerrestrialNetworkAvailableChangedCallback() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + Semaphore semaphore = new Semaphore(0); + final int[] receivedScanResult = new int[1]; + ISatelliteModemStateCallback callback = new ISatelliteModemStateCallback.Stub() { + @Override + public void onSatelliteModemStateChanged(int state) { + logd("onSatelliteModemStateChanged: state=" + state); + } + + @Override + public void onEmergencyModeChanged(boolean isEmergency) { + logd("onEmergencyModeChanged: emergency=" + isEmergency); + } + + @Override + public void onRegistrationFailure(int causeCode) { + logd("onRegistrationFailure: causeCode=" + causeCode); + } + + @Override + public void onTerrestrialNetworkAvailableChanged(boolean isAvailable) { + logd("onTerrestrialNetworkAvailableChanged: isAvailable=" + isAvailable); + receivedScanResult[0] = isAvailable ? 1 : 0; + semaphore.release(); + } + }; + resetSatelliteControllerUTToSupportedAndProvisionedState(); + mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController); + + int RegisterErrorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged( + callback); + assertEquals(SATELLITE_RESULT_SUCCESS, RegisterErrorCode); + verify(mMockSatelliteSessionController).registerForSatelliteModemStateChanged(callback); + + int expectedErrorCode = 1; + mIIntegerConsumerResults.clear(); + sendTerrestrialNetworkAvailableChangedEvent(true, null); + processAllMessages(); + assertTrue(waitForForEvents( + semaphore, 1, "testRegistrationFailureCallback")); + assertEquals(expectedErrorCode, receivedScanResult[0]); + } + private boolean mProvisionState = false; private int mProvisionSateResultCode = -1; private Semaphore mProvisionSateSemaphore = new Semaphore(0); @@ -4272,6 +4635,7 @@ private void verifyRequestSatelliteSubscriberProvisionStatus() throws Exception { setSatelliteSubscriberTesting(); List<SatelliteSubscriberInfo> list = getExpectedSatelliteSubscriberInfoList(); + mCarrierConfigBundle.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true); mCarrierConfigBundle.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, mNiddApn); mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, true); for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair @@ -4318,7 +4682,6 @@ assertTrue(mProvisionState); } - @Test public void testRegisterForSatelliteSubscriptionProvisionStateChanged() throws Exception { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); @@ -4349,6 +4712,18 @@ } } }; + + TestSubscriptionManager testSubscriptionManager = new TestSubscriptionManager(); + doAnswer(invocation -> { + testSubscriptionManager.setIsSatelliteProvisionedForNonIpDatagram( + invocation.getArgument(0), invocation.getArgument(1)); + return null; + }).when(mMockSubscriptionManagerService).setIsSatelliteProvisionedForNonIpDatagram(anyInt(), + anyBoolean()); + doAnswer(invocation -> testSubscriptionManager.isSatelliteProvisionedForNonIpDatagram( + invocation.getArgument(0))).when( + mMockSubscriptionManagerService).isSatelliteProvisionedForNonIpDatagram(anyInt()); + setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS); verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS); int errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback); @@ -4368,7 +4743,7 @@ any()); assertTrue(waitForForEvents( semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged")); - assertTrue(resultArray[0].getProvisionStatus()); + assertTrue(resultArray[0].isProvisioned()); assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId()); // Request provisioning with SatelliteSubscriberInfo that has not been provisioned @@ -4377,21 +4752,101 @@ inputList.add(list.get(1)); verifyProvisionSatellite(inputList); - verify(mMockSatelliteModemInterface, times(2)).updateSatelliteSubscription(anyString(), - any()); assertTrue(waitForForEvents( semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged")); - assertTrue(resultArray[1].getProvisionStatus()); + assertTrue(resultArray[1].isProvisioned()); assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId()); // Request provisioning with the same SatelliteSubscriberInfo that was previously // requested, and verify that onSatelliteSubscriptionProvisionStateChanged is not called. verifyProvisionSatellite(inputList); - verify(mMockSatelliteModemInterface, times(2)).updateSatelliteSubscription(anyString(), - any()); assertFalse(waitForForEvents( semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged")); + + // Request deprovision for subscriberID 2, verify that subscriberID 2 is set to + // deprovision and that subscriberID 1 is set to provision. + verifyDeprovisionSatellite(inputList); + assertTrue(waitForForEvents( + semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged")); + assertFalse(resultArray[1].isProvisioned()); + assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId()); + assertTrue(resultArray[0].isProvisioned()); + assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId()); + + // Request deprovision for subscriberID 1, verify that subscriberID 1 is set to deprovision. + inputList = new ArrayList<>(); + inputList.add(list.get(0)); + verifyDeprovisionSatellite(inputList); + assertTrue(waitForForEvents( + semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged")); + assertFalse(resultArray[1].isProvisioned()); + assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId()); + assertFalse(resultArray[0].isProvisioned()); + assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId()); + + // Request provision for subscriberID 2, verify that subscriberID 2 is set to provision. + inputList = new ArrayList<>(); + inputList.add(list.get(1)); + verifyProvisionSatellite(inputList); + + assertTrue(waitForForEvents( + semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged")); + assertTrue(resultArray[1].isProvisioned()); + assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId()); + assertFalse(resultArray[0].isProvisioned()); + assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId()); + } + + private boolean mDeprovisionDone = false; + private int mDeprovisionSateResultCode = -1; + private Semaphore mDeprovisionSateSemaphore = new Semaphore(0); + private ResultReceiver mDeprovisionSatelliteReceiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + mDeprovisionSateResultCode = resultCode; + logd("DeprovisionSatelliteReceiver: resultCode=" + resultCode); + if (resultCode == SATELLITE_RESULT_SUCCESS) { + if (resultData.containsKey(KEY_DEPROVISION_SATELLITE_TOKENS)) { + mDeprovisionDone = resultData.getBoolean(KEY_DEPROVISION_SATELLITE_TOKENS); + logd("DeprovisionSatelliteReceiver: deprovision=" + mDeprovisionDone); + } else { + loge("KEY_DEPROVISION_SATELLITE_TOKENS does not exist."); + mDeprovisionDone = false; + } + } else { + mDeprovisionDone = false; + } + try { + mDeprovisionSateSemaphore.release(); + } catch (Exception ex) { + loge("DeprovisionSatelliteReceiver: Got exception in releasing semaphore " + ex); + } + } + }; + + @Test + public void testDeprovisionSatellite() throws Exception { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true); + verifyRequestSatelliteSubscriberProvisionStatus(); + List<SatelliteSubscriberInfo> inputList = getExpectedSatelliteSubscriberInfoList(); + verifyProvisionSatellite(inputList); + verifyDeprovisionSatellite(inputList); + } + + private void verifyDeprovisionSatellite(List<SatelliteSubscriberInfo> inputList) { + doAnswer(invocation -> { + Message message = (Message) invocation.getArguments()[1]; + AsyncResult.forMessage(message, null, new SatelliteException(SATELLITE_RESULT_SUCCESS)); + message.sendToTarget(); + return null; + }).when(mMockSatelliteModemInterface).updateSatelliteSubscription(anyString(), any()); + + mSatelliteControllerUT.deprovisionSatellite(inputList, mDeprovisionSatelliteReceiver); + processAllMessages(); + assertEquals(SATELLITE_RESULT_SUCCESS, mDeprovisionSateResultCode); + assertTrue(mDeprovisionDone); } private void setSatelliteSubscriberTesting() throws Exception { @@ -4508,6 +4963,7 @@ @Test public void testCheckForSubscriberIdChange_changed() { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + mCarrierConfigBundle.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true); List<SubscriptionInfo> allSubInfos = new ArrayList<>(); String imsi = "012345"; @@ -4526,6 +4982,7 @@ .thenReturn(allSubInfos); when(mSubscriptionInfo.isSatelliteESOSSupported()).thenReturn(true); + when(mSubscriptionInfo.isActive()).thenReturn(true); when(mMockSubscriptionManagerService.getSubscriptionInfoInternal(SUB_ID)) .thenReturn(subInfoInternal); @@ -4580,6 +5037,260 @@ eq(SUB_ID), eq(true)); } + @Test + public void testIsCarrierRoamingNtnAvailableServicesForManualConnect() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true); + // CARRIER_ROAMING_NTN_CONNECT_MANUAL: 1 + mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1); + + mSatelliteControllerUT.setSatellitePhone(1); + processAllMessages(); + when(mContext.getPackageManager()).thenReturn(mMockPManager); + try { + when(mMockPManager.getApplicationInfo(anyString(), + anyInt())).thenReturn(getApplicationInfo()); + } catch (PackageManager.NameNotFoundException e) { + logd("NameNotFoundException"); + } + assertTrue(mSatelliteControllerUT + .isP2PSmsDisallowedOnCarrierRoamingNtn(/*subId*/ SUB_ID)); + } + + @Test + public void testIsCarrierRoamingNtnAvailableServicesForAutomaticConnect() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true); + // CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC: 0 + mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0); + + mSatelliteControllerUT.setSatellitePhone(1); + processAllMessages(); + when(mContext.getPackageManager()).thenReturn(mMockPManager); + try { + when(mMockPManager.getApplicationInfo(anyString(), + anyInt())).thenReturn(getApplicationInfo()); + } catch (PackageManager.NameNotFoundException e) { + logd("NameNotFoundException"); + } + // If it is automatic connection case, it is not support the callback. + assertFalse(mSatelliteControllerUT + .isP2PSmsDisallowedOnCarrierRoamingNtn(/*subId*/ SUB_ID)); + } + + ApplicationInfo getApplicationInfo() { + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.metaData = new Bundle(); + applicationInfo.metaData.putBoolean( + METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT, true); + return applicationInfo; + } + + @Test + public void testRegisterApplicationStateChanged() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false); + when(mMockSubscriptionManagerService.getActiveSubIdList(true)) + .thenReturn(new int[]{SUB_ID1}); + + ArgumentCaptor<IntentFilter> intentFilterCaptor = + ArgumentCaptor.forClass(IntentFilter.class); + ArgumentCaptor<BroadcastReceiver> receiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(receiverCaptor.capture(), intentFilterCaptor.capture(), + anyInt()); + + BroadcastReceiver receiver = receiverCaptor.getValue(); + mSatelliteControllerUT = + new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags); + assertFalse(mSatelliteControllerUT.isApplicationUpdated); + Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED); + intent.setData(Uri.parse("com.example.app")); + receiver.onReceive(mContext, intent); + CountDownLatch latch1 = new CountDownLatch(1); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + latch1.countDown(); + }, 100); + try { + latch1.await(); + } catch (InterruptedException e) { + } + assertTrue(mSatelliteControllerUT.isApplicationUpdated); + mSatelliteControllerUT = + new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags); + assertFalse(mSatelliteControllerUT.isApplicationUpdated); + intent = new Intent(Intent.ACTION_PACKAGE_REPLACED); + intent.setData(Uri.parse("com.example.app")); + receiver.onReceive(mContext, intent); + CountDownLatch latch2 = new CountDownLatch(1); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + latch2.countDown(); + }, 100); + try { + latch2.await(); + } catch (InterruptedException e) { + } + assertTrue(mSatelliteControllerUT.isApplicationUpdated); + mSatelliteControllerUT = + new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags); + assertFalse(mSatelliteControllerUT.isApplicationUpdated); + intent = new Intent(Intent.ACTION_PACKAGE_REMOVED); + intent.setData(Uri.parse("com.example.app")); + receiver.onReceive(mContext, intent); + CountDownLatch latch3 = new CountDownLatch(1); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + latch3.countDown(); + }, 100); + try { + latch3.await(); + } catch (InterruptedException e) { + } + assertTrue(mSatelliteControllerUT.isApplicationUpdated); + mSatelliteControllerUT = + new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags); + assertFalse(mSatelliteControllerUT.isApplicationUpdated); + intent = new Intent(Intent.ACTION_PACKAGE_ADDED); + intent.setData(Uri.parse("com.example.different")); + receiver.onReceive(mContext, intent); + CountDownLatch latch4 = new CountDownLatch(1); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + latch4.countDown(); + }, 100); + try { + latch4.await(); + } catch (InterruptedException e) { + } + assertFalse(mSatelliteControllerUT.isApplicationUpdated); + } + + @Test + public void testUpdateSystemSelectionChannels() { + when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true); + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + + String mccmnc = "123455"; + int[] bands1 = {200, 201, 202}; + IntArray intArraybands1 = new IntArray(3); + intArraybands1.addAll(bands1); + int[] earfcns1 = {300, 301, 310, 311}; + IntArray intArrayEarfcns1 = new IntArray(4); + intArrayEarfcns1.addAll(earfcns1); + String seed1 = "test-seed-satellite1"; + UUID uuid1 = UUID.nameUUIDFromBytes(seed1.getBytes()); + SatellitePosition satellitePosition1 = new SatellitePosition(0, 35876); + EarfcnRange earfcnRange1 = new EarfcnRange(301, 300); + EarfcnRange earfcnRange2 = new EarfcnRange(311, 310); + List<EarfcnRange> earfcnRangeList1 = new ArrayList<>( + Arrays.asList(earfcnRange1, earfcnRange2)); + SatelliteInfo satelliteInfo1 = new SatelliteInfo(uuid1, satellitePosition1, Arrays.stream( + bands1).boxed().collect(Collectors.toList()), earfcnRangeList1); + int[] tagIds = {1, 2, 3}; + IntArray intArrayTagIds = new IntArray(3); + intArrayTagIds.addAll(tagIds); + SystemSelectionSpecifier systemSelectionSpecifier1 = new SystemSelectionSpecifier(mccmnc, + intArraybands1, intArrayEarfcns1, new SatelliteInfo[]{satelliteInfo1}, + intArrayTagIds); + + setUpResponseForUpdateSystemSelectionChannels(SATELLITE_RESULT_ERROR); + mSatelliteControllerUT.updateSystemSelectionChannels( + new ArrayList<>(List.of(systemSelectionSpecifier1)), + mSystemSelectionChannelUpdatedReceiver); + processAllMessages(); + assertTrue(waitForRequestUpdateSystemSelectionChannelResult(1)); + assertEquals(SATELLITE_RESULT_ERROR, mQueriedSystemSelectionChannelUpdatedResultCode); + + // Verify whether callback receives expected result + setUpResponseForUpdateSystemSelectionChannels(SATELLITE_RESULT_SUCCESS); + mSatelliteControllerUT.updateSystemSelectionChannels( + new ArrayList<>(List.of(systemSelectionSpecifier1)), + mSystemSelectionChannelUpdatedReceiver); + processAllMessages(); + assertTrue(waitForRequestUpdateSystemSelectionChannelResult(1)); + assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSystemSelectionChannelUpdatedResultCode); + + // Verify whether SatelliteModemInterface API was invoked and data is valid, when single + // data was provided. + ArgumentCaptor<List<SystemSelectionSpecifier>> systemSelectionSpecifierListCaptor = + ArgumentCaptor.forClass(List.class); + verify(mMockSatelliteModemInterface, times(2)).updateSystemSelectionChannels( + systemSelectionSpecifierListCaptor.capture(), any(Message.class)); + List<SystemSelectionSpecifier> capturedList = systemSelectionSpecifierListCaptor.getValue(); + SystemSelectionSpecifier systemSelectionSpecifier = capturedList.getFirst(); + + assertEquals(mccmnc, systemSelectionSpecifier.getMccMnc()); + int[] actualBandsArray = systemSelectionSpecifier.getBands(); + assertArrayEquals(bands1, actualBandsArray); + int[] actualEarfcnsArray = systemSelectionSpecifier.getEarfcns(); + assertArrayEquals(earfcns1, actualEarfcnsArray); + assertArrayEquals(new SatelliteInfo[]{satelliteInfo1}, + systemSelectionSpecifier.getSatelliteInfos().toArray(new SatelliteInfo[0])); + int[] actualTagIdArray = systemSelectionSpecifier.getTagIds(); + assertArrayEquals(tagIds, actualTagIdArray); + + // Verify whether SatelliteModemInterface API was invoked and data is valid, when list + // of data was provided. + int[] bands2 = {210, 211, 212}; + IntArray intArraybands2 = new IntArray(3); + intArraybands2.addAll(bands2); + int[] earfcns2 = {320, 321, 330, 331}; + IntArray intArrayEarfcns2 = new IntArray(4); + intArrayEarfcns2.addAll(earfcns2); + String seed2 = "test-seed-satellite2"; + UUID uuid2 = UUID.nameUUIDFromBytes(seed2.getBytes()); + SatellitePosition satellitePosition2 = new SatellitePosition(120, 35876); + EarfcnRange earfcnRange3 = new EarfcnRange(321, 320); + EarfcnRange earfcnRange4 = new EarfcnRange(331, 330); + List<EarfcnRange> earfcnRangeList2 = new ArrayList<>( + Arrays.asList(earfcnRange3, earfcnRange4)); + SatelliteInfo satelliteInfo2 = new SatelliteInfo(uuid2, satellitePosition2, Arrays.stream( + bands1).boxed().collect(Collectors.toList()), earfcnRangeList2); + SystemSelectionSpecifier systemSelectionSpecifier2 = new SystemSelectionSpecifier(mccmnc, + intArraybands2, intArrayEarfcns2, new SatelliteInfo[]{satelliteInfo2}, + intArrayTagIds); + + // Verify whether callback receives expected result + setUpResponseForUpdateSystemSelectionChannels(SATELLITE_RESULT_SUCCESS); + mSatelliteControllerUT.updateSystemSelectionChannels( + new ArrayList<>(List.of(systemSelectionSpecifier1, systemSelectionSpecifier2)), + mSystemSelectionChannelUpdatedReceiver); + processAllMessages(); + assertTrue(waitForRequestUpdateSystemSelectionChannelResult(1)); + assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSystemSelectionChannelUpdatedResultCode); + + // Verify whether SatelliteModemInterface API was invoked and data is valid, + verify(mMockSatelliteModemInterface, times(3)).updateSystemSelectionChannels( + systemSelectionSpecifierListCaptor.capture(), any(Message.class)); + capturedList = systemSelectionSpecifierListCaptor.getValue(); + SystemSelectionSpecifier capturedSystemSelectionSpecifier1 = capturedList.getFirst(); + SystemSelectionSpecifier capturedSystemSelectionSpecifier2 = capturedList.get(1); + + // Verify first SystemSelectionSpecifier + assertEquals(mccmnc, systemSelectionSpecifier.getMccMnc()); + actualBandsArray = capturedSystemSelectionSpecifier1.getBands(); + assertArrayEquals(bands1, actualBandsArray); + actualEarfcnsArray = capturedSystemSelectionSpecifier1.getEarfcns(); + assertArrayEquals(earfcns1, actualEarfcnsArray); + assertArrayEquals(new SatelliteInfo[]{satelliteInfo1}, + capturedSystemSelectionSpecifier1.getSatelliteInfos().toArray( + new SatelliteInfo[0])); + actualTagIdArray = capturedSystemSelectionSpecifier1.getTagIds(); + assertArrayEquals(tagIds, actualTagIdArray); + + // Verify second SystemSelectionSpecifier + assertEquals(mccmnc, systemSelectionSpecifier.getMccMnc()); + actualBandsArray = capturedSystemSelectionSpecifier2.getBands(); + assertArrayEquals(bands2, actualBandsArray); + actualEarfcnsArray = capturedSystemSelectionSpecifier2.getEarfcns(); + assertArrayEquals(earfcns2, actualEarfcnsArray); + assertArrayEquals(new SatelliteInfo[]{satelliteInfo2}, + capturedSystemSelectionSpecifier2.getSatelliteInfos().toArray( + new SatelliteInfo[0])); + actualTagIdArray = capturedSystemSelectionSpecifier2.getTagIds(); + assertArrayEquals(tagIds, actualTagIdArray); + } + private void verifyProvisionStatusPerSubscriberIdGetFromDb(boolean provision) { doReturn(provision).when( mMockSubscriptionManagerService).isSatelliteProvisionedForNonIpDatagram(anyInt()); @@ -4600,7 +5311,7 @@ assertEquals(SATELLITE_RESULT_SUCCESS, mRequestSatelliteSubscriberProvisionStatusResultCode); assertEquals(provision, - mRequestSatelliteSubscriberProvisionStatusResultList.get(0).getProvisionStatus()); + mRequestSatelliteSubscriberProvisionStatusResultList.get(0).isProvisioned()); } private void setComponentName() { @@ -4894,6 +5605,19 @@ }).when(mMockSatelliteModemInterface).stopSendingNtnSignalStrength(any(Message.class)); } + private void setUpResponseForUpdateSystemSelectionChannels( + @SatelliteManager.SatelliteResult int error) { + SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS) + ? null : new SatelliteException(error); + doAnswer(invocation -> { + Message message = (Message) invocation.getArguments()[1]; + AsyncResult.forMessage(message, null, exception); + message.sendToTarget(); + return null; + }).when(mMockSatelliteModemInterface).updateSystemSelectionChannels(anyList(), + any(Message.class)); + } + private boolean waitForRequestIsSatelliteSupportedResult(int expectedNumberOfEvents) { for (int i = 0; i < expectedNumberOfEvents; i++) { try { @@ -5017,6 +5741,24 @@ return true; } + private boolean waitForRequestUpdateSystemSelectionChannelResult(int expectedNumberOfEvents) { + for (int i = 0; i < expectedNumberOfEvents; i++) { + try { + if (!mSystemSelectionChannelUpdatedSemaphore.tryAcquire(TIMEOUT, + TimeUnit.MILLISECONDS)) { + logd("Timeout to receive " + + "updateSystemSelectionChannel()" + + " callback"); + return false; + } + } catch (Exception ex) { + logd("updateSystemSelectionChannel: Got exception=" + ex); + return false; + } + } + return true; + } + private void verifySatelliteSupported(boolean supported, int expectedErrorCode) { mSatelliteSupportSemaphore.drainPermits(); mSatelliteControllerUT.requestIsSatelliteSupported(mSatelliteSupportReceiver); @@ -5086,6 +5828,13 @@ msg.sendToTarget(); } + private void sendSignalStrengthChangedEvent(int phoneId) { + Message msg = mSatelliteControllerUT.obtainMessage( + 57 /* EVENT_SIGNAL_STRENGTH_CHANGED */); + msg.obj = new AsyncResult(phoneId, null, null); + msg.sendToTarget(); + } + private void sendCmdStartSendingNtnSignalStrengthChangedEvent(boolean shouldReport) { Message msg = mSatelliteControllerUT.obtainMessage( 35 /* CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING */); @@ -5129,6 +5878,22 @@ msg.sendToTarget(); } + private void sendTerrestrialNetworkAvailableChangedEvent(boolean isAvailable, + Throwable exception) { + Message msg = mSatelliteControllerUT.obtainMessage( + 55 /* EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED */); + msg.obj = new AsyncResult(null, isAvailable, exception); + msg.sendToTarget(); + } + + private void sendSelectedNbIotSatelliteSubscriptionChangedEvent(int selectedSubId, + Throwable exception) { + Message msg = mSatelliteControllerUT.obtainMessage( + 60 /* EVENT_SELECTED_NB_IOT_SATELLITE_SUBSCRIPTION_CHANGED */); + msg.obj = new AsyncResult(null, selectedSubId, exception); + msg.sendToTarget(); + } + private void setRadioPower(boolean on) { mSimulatedCommands.setRadioPower(on, false, false, null); } @@ -5158,6 +5923,15 @@ setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS); verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS); verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS); + + try { + setSatelliteSubscriberTesting(); + } catch (Exception ex) { + fail("provisionSatelliteService.setSatelliteSubscriberTesting: ex=" + ex); + } + doReturn(true).when(mMockSubscriptionManagerService).isSatelliteProvisionedForNonIpDatagram( + anyInt()); + cancelRemote = mSatelliteControllerUT.provisionSatelliteService( TEST_SATELLITE_TOKEN, testProvisionData, mIIntegerConsumer); @@ -5329,11 +6103,16 @@ public int satelliteModeSettingValue = SATELLITE_MODE_ENABLED_FALSE; public boolean setSettingsKeyToAllowDeviceRotationCalled = false; public OutcomeReceiver<Boolean, SatelliteException> isSatelliteAllowedCallback = null; + public static boolean isApplicationUpdated; + public String packageName = "com.example.app"; + public boolean isSatelliteBeingDisabled = false; + public boolean mIsApplicationSupportsP2P = false; TestSatelliteController( Context context, Looper looper, @NonNull FeatureFlags featureFlags) { super(context, looper, featureFlags); logd("Constructing TestSatelliteController"); + isApplicationUpdated = false; } @Override @@ -5402,9 +6181,47 @@ return false; } + @Override + protected List<DeviceState> getSupportedDeviceStates() { + return List.of(new DeviceState(new DeviceState.Configuration.Builder(0 /* identifier */, + "DEFAULT" /* name */).build())); + } + + @Override + public boolean isSatelliteBeingDisabled() { + return isSatelliteBeingDisabled; + } + + protected String getConfigSatelliteGatewayServicePackage() { + String packageName = "com.example.app"; + return packageName; + } + + @Override + protected void handleCarrierRoamingNtnAvailableServicesChanged(int subId) { + isApplicationUpdated = true; + } + + @Override + public boolean isApplicationSupportsP2P(String packageName) { + return mIsApplicationSupportsP2P; + } + + @Override + public int[] getSupportedServicesOnCarrierRoamingNtn(int subId) { + return new int[]{3, 5}; + } + + void setSatelliteProvisioned(@Nullable Boolean isProvisioned) { - synchronized (mSatelliteViaOemProvisionLock) { - mIsSatelliteViaOemProvisioned = isProvisioned; + synchronized (mDeviceProvisionLock) { + mIsDeviceProvisioned = isProvisioned; + } + } + + void setIsSatelliteSupported(@Nullable Boolean isSatelliteSupported) { + synchronized (mIsSatelliteSupportedLock) { + mIsSatelliteSupported = isSatelliteSupported; } } @@ -5447,5 +6264,327 @@ public boolean isAnyWaitForSatelliteEnablingResponseTimerStarted() { return hasMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT); } + + public int getResultReceiverTotalCount() { + synchronized (mResultReceiverTotalCountLock) { + return mResultReceiverTotalCount; + } + } + + public HashMap<String, Integer> getResultReceiverCountPerMethodMap() { + synchronized (mResultReceiverTotalCountLock) { + return mResultReceiverCountPerMethodMap; + } + } + } + + @Test + public void testLoggingCodeForResultReceiverCount() throws Exception { + final String callerSC = "SC:ResultReceiver"; + final String callerSAC = "SAC:ResultReceiver"; + + doReturn(false).when(mFeatureFlags).carrierRoamingNbIotNtn(); + + mSatelliteControllerUT.incrementResultReceiverCount(callerSC); + assertEquals(0, mSatelliteControllerUT.getResultReceiverTotalCount()); + mSatelliteControllerUT.decrementResultReceiverCount(callerSC); + assertEquals(0, mSatelliteControllerUT.getResultReceiverTotalCount()); + + doReturn(true).when(mFeatureFlags).carrierRoamingNbIotNtn(); + + mSatelliteControllerUT.incrementResultReceiverCount(callerSC); + assertEquals(1, mSatelliteControllerUT.getResultReceiverTotalCount()); + assertEquals(1, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size()); + assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0)); + assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0)); + + mSatelliteControllerUT.incrementResultReceiverCount(callerSC); + assertEquals(2, mSatelliteControllerUT.getResultReceiverTotalCount()); + assertEquals(1, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size()); + assertEquals(2, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0)); + assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0)); + + mSatelliteControllerUT.incrementResultReceiverCount(callerSAC); + assertEquals(3, mSatelliteControllerUT.getResultReceiverTotalCount()); + assertEquals(2, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size()); + assertEquals(2, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0)); + assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0)); + + mSatelliteControllerUT.decrementResultReceiverCount(callerSC); + assertEquals(2, mSatelliteControllerUT.getResultReceiverTotalCount()); + assertEquals(2, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size()); + assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0)); + assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0)); + + mSatelliteControllerUT.decrementResultReceiverCount(callerSC); + assertEquals(1, mSatelliteControllerUT.getResultReceiverTotalCount()); + assertEquals(2, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size()); + assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0)); + assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0)); + + mSatelliteControllerUT.decrementResultReceiverCount(callerSAC); + assertEquals(0, mSatelliteControllerUT.getResultReceiverTotalCount()); + assertEquals(2, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size()); + assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0)); + assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT + .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0)); + } + + @Test + public void testSetNtnSmsSupportedByMessagesApp() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + mSatelliteControllerUT.setNtnSmsSupportedByMessagesApp(true); + assertTrue(mSharedPreferences.getBoolean( + SatelliteController.NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY, false)); + } + + private static class TestSubscriptionManager { + public Map<Integer, Boolean> mSatelliteProvisionedForNonIpDatagram = new HashMap<>(); + + public void resetProvisionMapForNonIpDatagram() { + mSatelliteProvisionedForNonIpDatagram.clear(); + } + + public void setIsSatelliteProvisionedForNonIpDatagram(int subId, boolean provisioned) { + mSatelliteProvisionedForNonIpDatagram.put(subId, provisioned); + } + + public boolean isSatelliteProvisionedForNonIpDatagram(int subId) { + Boolean isProvisioned = mSatelliteProvisionedForNonIpDatagram.get(subId); + return isProvisioned != null ? isProvisioned : false; + } + } + + @Test + public void testGetSatelliteDataPlanForPlmn_WithEntitlement() throws Exception { + logd("testGetSatelliteDataPlanForPlmn_WithEntitlement"); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + + replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier", + mSatelliteControllerUT, new SparseArray<>()); + List<String> overlayConfigPlmnList = new ArrayList<>(); + replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig", + mSatelliteControllerUT, overlayConfigPlmnList); + mCarrierConfigBundle.putBoolean( + CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true); + mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, + true); + + List<String> entitlementPlmnList = + Arrays.stream(new String[]{"00101", "00102", "00103", "00104"}) + .toList(); + List<String> barredPlmnList = new ArrayList<>(); + Map<String, Integer> dataPlanListMap = Map.of( + "00101", SATELLITE_DATA_PLAN_METERED, + "00103", SATELLITE_DATA_PLAN_UNMETERED); + mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, + entitlementPlmnList, barredPlmnList, dataPlanListMap, new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); + + int dataPlanForPlmn; + dataPlanForPlmn = mSatelliteControllerUT.getSatelliteDataPlanForPlmn(SUB_ID, "00101"); + assertEquals(SATELLITE_DATA_PLAN_METERED, dataPlanForPlmn); + + dataPlanForPlmn = mSatelliteControllerUT.getSatelliteDataPlanForPlmn(SUB_ID, "00103"); + assertEquals(SATELLITE_DATA_PLAN_UNMETERED, dataPlanForPlmn); + } + + @Test + public void testGetSatelliteDataPlanForPlmn_WithoutEntitlement() throws Exception { + logd("testGetSatelliteDataPlanForPlmn_WithoutEntitlement"); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + + replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier", + mSatelliteControllerUT, new SparseArray<>()); + List<String> overlayConfigPlmnList = new ArrayList<>(); + replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig", + mSatelliteControllerUT, overlayConfigPlmnList); + mCarrierConfigBundle.putBoolean( + CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true); + mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, + true); + + List<String> entitlementPlmnList = + Arrays.stream(new String[]{"00101", "00102", "00103", "00104"}) + .toList(); + List<String> barredPlmnList = new ArrayList<>(); + Map<String, Integer> dataPlanListMap = new HashMap<>(); + mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, + entitlementPlmnList, barredPlmnList, dataPlanListMap, new HashMap<>(), + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); + + int dataPlanForPlmn = mSatelliteControllerUT.getSatelliteDataPlanForPlmn(SUB_ID, "00101"); + assertEquals(SATELLITE_DATA_PLAN_METERED, dataPlanForPlmn); + } + + @Test + public void TestGetSupportedSatelliteServicesForPlmn_WithEntitlement() throws Exception { + logd("TestGetSupportedSatelliteServicesForPlmn_WithEntitlement"); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + + replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier", + mSatelliteControllerUT, new SparseArray<>()); + List<String> overlayConfigPlmnList = new ArrayList<>(); + replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig", + mSatelliteControllerUT, overlayConfigPlmnList); + mCarrierConfigBundle.putBoolean( + CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true); + mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, + true); + + List<String> entitlementPlmnList = + Arrays.stream(new String[]{"00101", "00102", "00103", "00104"}) + .toList(); + List<String> barredPlmnList = new ArrayList<>(); + Map<String, List<Integer>> serviceTypeListMap = Map.of( + "00101", List.of(SERVICE_TYPE_DATA, SERVICE_TYPE_SMS), + "00102", List.of(SERVICE_TYPE_VOICE, SERVICE_TYPE_SMS), + "00103", List.of(SERVICE_TYPE_DATA, SERVICE_TYPE_VOICE, SERVICE_TYPE_SMS)); + mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, + entitlementPlmnList, barredPlmnList, new HashMap<>(), serviceTypeListMap, + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); + + List<Integer> allowedServiceForPlmn; + allowedServiceForPlmn = mSatelliteControllerUT + .getSupportedSatelliteServicesForPlmn(SUB_ID, "00101"); + assertEquals(List.of(SERVICE_TYPE_DATA, SERVICE_TYPE_SMS), allowedServiceForPlmn); + + allowedServiceForPlmn = mSatelliteControllerUT + .getSupportedSatelliteServicesForPlmn(SUB_ID, "00102"); + assertEquals(List.of(SERVICE_TYPE_VOICE, SERVICE_TYPE_SMS), allowedServiceForPlmn); + + allowedServiceForPlmn = mSatelliteControllerUT + .getSupportedSatelliteServicesForPlmn(SUB_ID, "00103"); + assertEquals(List.of(SERVICE_TYPE_DATA, SERVICE_TYPE_VOICE, SERVICE_TYPE_SMS), + allowedServiceForPlmn); + } + + @Test + public void TestGetSupportedSatelliteServicesForPlmn_WithoutEntitlement() throws Exception { + logd("TestGetSupportedSatelliteServicesForPlmn_WithoutAllowedServices"); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + + replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier", + mSatelliteControllerUT, new SparseArray<>()); + List<String> overlayConfigPlmnList = new ArrayList<>(); + replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig", + mSatelliteControllerUT, overlayConfigPlmnList); + mCarrierConfigBundle.putBoolean( + CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true); + mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, + true); + + List<String> entitlementPlmnList = + Arrays.stream(new String[]{"00101", "00102", "00103", "00104"}) + .toList(); + List<String> barredPlmnList = new ArrayList<>(); + Map<String, Integer> dataPlanListMap = new HashMap<>(); + Map<String, List<Integer>> allowedServiceListMap = new HashMap<>(); + mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, + entitlementPlmnList, barredPlmnList, dataPlanListMap, allowedServiceListMap, + new HashMap<>(), new HashMap<>(), mIIntegerConsumer); + + // Verify whether the carrier config plmn list is returned with conditions below + // the config data plmn list : empty + // the carrier config plmn list : exist with services {{2}} + setConfigData(new ArrayList<>()); + mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, + true); + PersistableBundle carrierSupportedSatelliteServicesPerProvider = + new PersistableBundle(); + List<String> carrierConfigPlmnList = List.of("00101"); + carrierSupportedSatelliteServicesPerProvider.putIntArray( + carrierConfigPlmnList.get(0), new int[]{2}); + mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager + .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE, + carrierSupportedSatelliteServicesPerProvider); + invokeCarrierConfigChanged(); + + List<Integer> servicesPerPlmn; + servicesPerPlmn = mSatelliteControllerUT.getSupportedSatelliteServicesForPlmn( + SUB_ID, "00101"); + assertEquals(Arrays.asList(2).stream().sorted().toList(), + servicesPerPlmn.stream().sorted().toList()); + } + + @Test + public void testGetSupportedSatelliteDataModeForPlmn_WithEntitlement() throws Exception { + logd("testGetSupportedSatelliteDataModeForPlmn_WithEntitlement"); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + + replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier", + mSatelliteControllerUT, new SparseArray<>()); + List<String> overlayConfigPlmnList = new ArrayList<>(); + replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig", + mSatelliteControllerUT, overlayConfigPlmnList); + mCarrierConfigBundle.putBoolean( + CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true); + mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, + true); + + List<String> entitlementPlmnList = + Arrays.stream(new String[]{"00101", "00102", "00103", "00104"}) + .toList(); + List<String> barredPlmnList = new ArrayList<>(); + Map<String, Integer> dataServicePolicyMap = Map.of( + "00101", SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED, + "00102", SATELLITE_DATA_SUPPORT_ALL + ); + mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, + entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(), + dataServicePolicyMap, new HashMap<>(), mIIntegerConsumer); + + int dataSupportModeForPlmn; + dataSupportModeForPlmn = mSatelliteControllerUT + .getSatelliteDataServicePolicyForPlmn(SUB_ID, "00101"); + assertEquals(SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED, dataSupportModeForPlmn); + + dataSupportModeForPlmn = mSatelliteControllerUT + .getSatelliteDataServicePolicyForPlmn(SUB_ID, "00102"); + assertEquals(SATELLITE_DATA_SUPPORT_ALL, dataSupportModeForPlmn); + + } + + @Test + public void testGetSupportedSatelliteDataModeForPlmn_WithoutEntitlement() throws Exception { + logd("testGetSupportedSatelliteDataModeForPlmn_WithoutEntitlement"); + when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true); + + replaceInstance(SatelliteController.class, "mMergedPlmnListPerCarrier", + mSatelliteControllerUT, new SparseArray<>()); + List<String> overlayConfigPlmnList = new ArrayList<>(); + replaceInstance(SatelliteController.class, "mSatellitePlmnListFromOverlayConfig", + mSatelliteControllerUT, overlayConfigPlmnList); + mCarrierConfigBundle.putBoolean( + CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true); + mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, + true); + + List<String> entitlementPlmnList = + Arrays.stream(new String[]{"00101", "00102", "00103", "00104"}) + .toList(); + List<String> barredPlmnList = new ArrayList<>(); + Map<String, Integer> dataServicePolicyMap = new HashMap<>(); + mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, false, + entitlementPlmnList, barredPlmnList, new HashMap<>(), new HashMap<>(), + dataServicePolicyMap, new HashMap<>(), mIIntegerConsumer); + + mCarrierConfigBundle.putInt( + CarrierConfigManager.KEY_SATELLITE_DATA_SUPPORT_MODE_INT, + SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED); + int dataSupportModeForPlmn = mSatelliteControllerUT + .getSatelliteDataServicePolicyForPlmn(SUB_ID, "00101"); + assertEquals(SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED, dataSupportModeForPlmn); } }
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..529088b 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; @@ -39,6 +42,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.hardware.devicestate.DeviceState; import android.net.Uri; import android.os.Bundle; import android.os.Looper; @@ -67,6 +71,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 +123,8 @@ private Uri mTestConnectionAddress = Uri.parse("tel:1234"); private TestSOSMessageRecommender mTestSOSMessageRecommender; private ServiceState mServiceState2; + @Mock + private SatelliteStats mMockSatelliteStats; @Before public void setUp() throws Exception { @@ -145,14 +152,20 @@ mServiceState2 = Mockito.mock(ServiceState.class); when(mPhone.getServiceState()).thenReturn(mServiceState); when(mPhone.getPhoneId()).thenReturn(PHONE_ID); + when(mPhone.getSignalStrengthController()).thenReturn(mSignalStrengthController); when(mPhone2.getServiceState()).thenReturn(mServiceState2); when(mPhone2.getPhoneId()).thenReturn(PHONE_ID2); + when(mPhone2.getSignalStrengthController()).thenReturn(mSignalStrengthController); mTestSOSMessageRecommender = new TestSOSMessageRecommender(mContext, Looper.myLooper(), mTestSatelliteController, mTestImsManager); when(mServiceState.getState()).thenReturn(STATE_OUT_OF_SERVICE); 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 +178,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 +192,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 +205,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 +216,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 +229,7 @@ processAllMessages(); assertFalse(mTestSOSMessageRecommender.isTimerStarted()); assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted()); + verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any()); } private void testTimeoutBeforeEmergencyCallEnd(int expectedHandoverType, @@ -239,12 +261,14 @@ } assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1); assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1); + verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any()); + assertTrue(mTestSOSMessageRecommender.isDialerNotified()); } @Test public void testTimeoutBeforeEmergencyCallEnd_EventDisplayEmergencyMessageNotSent() { mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false); - mTestSatelliteController.setIsSatelliteViaOemProvisioned(false); + mTestSatelliteController.setDeviceProvisioned(false); mTestSOSMessageRecommender.isSatelliteAllowedCallback = null; mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false); processAllMessages(); @@ -266,6 +290,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 +328,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,10 +367,13 @@ assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1); assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1); assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted()); + verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any()); + assertTrue(mTestSOSMessageRecommender.isDialerNotified()); } @Test public void testSatelliteProvisionStateChangedBeforeTimeout() { + mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false); mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false); processAllMessages(); @@ -357,7 +390,11 @@ assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted()); assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1); assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1); + verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any()); + assertFalse(mTestSOSMessageRecommender.isDialerNotified()); + reset(mMockSatelliteStats); + mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true); mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false); processAllMessages(); assertTrue(mTestSOSMessageRecommender.isTimerStarted()); @@ -387,6 +424,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 +465,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 +520,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,38 +550,42 @@ assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 1); assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1); assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1); + verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any()); + assertFalse(mTestSOSMessageRecommender.isDialerNotified()); } @Test public void testOnEmergencyCallStarted() { - SatelliteController satelliteController = new SatelliteController( - mContext, Looper.myLooper(), mFeatureFlags); + SatelliteController satelliteController = new MinimalSatelliteControllerWrapper(mContext, + Looper.myLooper(), mFeatureFlags); TestSOSMessageRecommender testSOSMessageRecommender = new TestSOSMessageRecommender( mContext, Looper.myLooper(), satelliteController, mTestImsManager); + mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true); testSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false); processAllMessages(); assertFalse(testSOSMessageRecommender.isTimerStarted()); assertEquals(0, testSOSMessageRecommender.getCountOfTimerStarted()); + verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any()); } @Test - public void testIsSatelliteViaOemAvailable() { + public void testIsDeviceProvisioned() { Boolean originalIsSatelliteViaOemProvisioned = - mTestSatelliteController.mIsSatelliteViaOemProvisioned; + mTestSatelliteController.mIsDeviceProvisionedForTest; - mTestSatelliteController.mIsSatelliteViaOemProvisioned = null; - assertFalse(mTestSOSMessageRecommender.isSatelliteViaOemAvailable()); + mTestSatelliteController.mIsDeviceProvisionedForTest = null; + assertFalse(mTestSOSMessageRecommender.isDeviceProvisioned()); - mTestSatelliteController.mIsSatelliteViaOemProvisioned = true; - assertTrue(mTestSOSMessageRecommender.isSatelliteViaOemAvailable()); + mTestSatelliteController.mIsDeviceProvisionedForTest = true; + assertTrue(mTestSOSMessageRecommender.isDeviceProvisioned()); - mTestSatelliteController.mIsSatelliteViaOemProvisioned = false; - assertFalse(mTestSOSMessageRecommender.isSatelliteViaOemAvailable()); + mTestSatelliteController.mIsDeviceProvisionedForTest = false; + assertFalse(mTestSOSMessageRecommender.isDeviceProvisioned()); - mTestSatelliteController.mIsSatelliteViaOemProvisioned = + mTestSatelliteController.mIsDeviceProvisionedForTest = originalIsSatelliteViaOemProvisioned; } @@ -548,6 +604,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 +617,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 +628,7 @@ mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false); processAllMessages(); assertEquals(carrierTimeoutMillis, mTestSOSMessageRecommender.getTimeOutMillis()); + verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any()); } @Test @@ -577,10 +636,11 @@ mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN); mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true); - mTestSatelliteController.mIsSatelliteViaOemProvisioned = true; + mTestSatelliteController.mIsDeviceProvisionedForTest = true; 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); } @@ -590,10 +650,11 @@ mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN); mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true); - mTestSatelliteController.mIsSatelliteViaOemProvisioned = false; + mTestSatelliteController.mIsDeviceProvisionedForTest = false; 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); } @@ -603,16 +664,17 @@ mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN); mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false); - mTestSatelliteController.mIsSatelliteViaOemProvisioned = true; + mTestSatelliteController.mIsDeviceProvisionedForTest = true; mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false); assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS, mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType()); mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false); - mTestSatelliteController.mIsSatelliteViaOemProvisioned = false; + mTestSatelliteController.mIsDeviceProvisionedForTest = false; 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); } @@ -713,7 +775,7 @@ mProvisionStateChangedCallbacks; private int mRegisterForSatelliteProvisionStateChangedCalls = 0; private int mUnregisterForSatelliteProvisionStateChangedCalls = 0; - private Boolean mIsSatelliteViaOemProvisioned = true; + private Boolean mIsDeviceProvisionedForTest = true; private boolean mIsSatelliteConnectedViaCarrierWithinHysteresisTime = true; public boolean isOemEnabledSatelliteSupported = true; public boolean isCarrierEnabledSatelliteSupported = true; @@ -734,8 +796,8 @@ } @Override - public Boolean isSatelliteViaOemProvisioned() { - return mIsSatelliteViaOemProvisioned; + public Boolean isDeviceProvisioned() { + return mIsDeviceProvisionedForTest; } @Override @@ -790,6 +852,12 @@ return carrierEmergencyCallWaitForConnectionTimeoutMillis; } + @Override + protected List<DeviceState> getSupportedDeviceStates() { + return List.of(new DeviceState(new DeviceState.Configuration.Builder(0 /* identifier */, + "DEFAULT" /* name */).build())); + } + public void setSatelliteConnectedViaCarrierWithinHysteresisTime( boolean connectedViaCarrier) { mIsSatelliteConnectedViaCarrierWithinHysteresisTime = connectedViaCarrier; @@ -803,12 +871,12 @@ return mUnregisterForSatelliteProvisionStateChangedCalls; } - public void setIsSatelliteViaOemProvisioned(boolean provisioned) { - mIsSatelliteViaOemProvisioned = provisioned; + public void setDeviceProvisioned(boolean provisioned) { + mIsDeviceProvisionedForTest = provisioned; } public void sendProvisionStateChangedEvent(int subId, boolean provisioned) { - mIsSatelliteViaOemProvisioned = provisioned; + mIsDeviceProvisionedForTest = provisioned; Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks = mProvisionStateChangedCallbacks.get(SUB_ID); if (perSubscriptionCallbacks != null) { @@ -823,6 +891,26 @@ } } + /** + * Now that {@link SatelliteController} uses + * {@link android.hardware.devicestate.DeviceStateManager} to determine if a device is a + * foldable or not, we have to provide a minimal wrapper for {@link SatelliteController} for + * tests that want to use a non-fake {@link SatelliteController}. + */ + private static class MinimalSatelliteControllerWrapper extends SatelliteController { + + protected MinimalSatelliteControllerWrapper( + Context context, Looper looper, FeatureFlags featureFlags) { + super(context, looper, featureFlags); + } + + @Override + protected List<DeviceState> getSupportedDeviceStates() { + return List.of(new DeviceState(new DeviceState.Configuration.Builder(0 /* identifier */, + "DEFAULT" /* name */).build())); + } + } + private static class TestImsManager extends ImsManager { private final List<RegistrationManager.RegistrationCallback> mCallbacks; @@ -896,6 +984,9 @@ isSatelliteAllowedCallback = null; private ComponentName mSmsAppComponent = new ComponentName( DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS); + private boolean mIsDialerNotified; + private boolean mProvisionState = true; + private boolean mSatelliteAllowedByReasons = true; /** * Create an instance of SatelliteSOSMessageRecommender. @@ -924,6 +1015,22 @@ isSatelliteAllowedCallback = callback; } + @Override + protected void reportESosRecommenderDecision(boolean isDialerNotified) { + super.reportESosRecommenderDecision(isDialerNotified); + mIsDialerNotified = isDialerNotified; + } + + @Override + protected boolean updateAndGetProvisionState() { + return mProvisionState; + } + + @Override + protected boolean isSatelliteAllowedByReasons() { + return mSatelliteAllowedByReasons; + } + public boolean isTimerStarted() { return hasMessages(EVENT_TIME_OUT); } @@ -939,6 +1046,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..0d11fed 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -19,29 +19,40 @@ import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT; import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE; +import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED; +import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS; import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT; +import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; 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.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AlarmManager; import android.content.Context; import android.content.res.Resources; import android.os.AsyncResult; @@ -49,14 +60,18 @@ import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; +import android.telephony.NetworkRegistrationInfo; +import android.telephony.ServiceState; import android.telephony.satellite.ISatelliteModemStateCallback; import android.telephony.satellite.SatelliteManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import com.android.internal.R; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.flags.FeatureFlags; +import com.android.internal.telephony.satellite.metrics.SessionMetricsStats; import org.junit.After; import org.junit.Before; @@ -69,6 +84,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -104,9 +120,14 @@ @Mock private DatagramReceiver mMockDatagramReceiver; @Mock private DatagramDispatcher mMockDatagramDispatcher; @Mock private DatagramController mMockDatagramController; + @Mock private ServiceState mMockServiceState; + @Mock private SessionMetricsStats mMockSessionMetricsStats; + @Mock private AlarmManager mAlarmManager; @Captor ArgumentCaptor<Handler> mHandlerCaptor; @Captor ArgumentCaptor<Integer> mMsgCaptor; + @Captor ArgumentCaptor<Executor> mExecutorArgumentCaptor; + @Captor ArgumentCaptor<AlarmManager.OnAlarmListener> mOnAlarmListenerArgumentCaptor; @Before public void setUp() throws Exception { @@ -121,16 +142,27 @@ mMockSatelliteController); replaceInstance(DatagramController.class, "sInstance", null, mMockDatagramController); + replaceInstance(SessionMetricsStats.class, "sInstance", null, + mMockSessionMetricsStats); Resources resources = mContext.getResources(); when(resources.getInteger(anyInt())).thenReturn(TEST_SATELLITE_TIMEOUT_MILLIS); - + when(resources.getBoolean( + R.bool.config_satellite_modem_support_concurrent_tn_scanning)) + .thenReturn(false); + when(resources.getBoolean( + R.bool.config_satellite_allow_tn_scanning_during_satellite_session)) + .thenReturn(true); when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true); when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(false); when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( anyInt())).thenReturn(false); when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(false); when(mMockSatelliteController.getSatellitePhone()).thenReturn(mPhone); + when(mMockSessionMetricsStats.addCountOfAutoExitDueToScreenOff()).thenReturn( + mMockSessionMetricsStats); + when(mMockSessionMetricsStats.addCountOfAutoExitDueToTnNetwork()).thenReturn( + mMockSessionMetricsStats); mSatelliteModemInterface = new TestSatelliteModemInterface( mContext, mMockSatelliteController, Looper.myLooper(), mFeatureFlags); mTestSatelliteSessionController = new TestSatelliteSessionController(mContext, @@ -142,6 +174,7 @@ mTestSatelliteModemStateCallback); assertSuccessfulModemStateChangedCallback( mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_OFF); + mTestSatelliteSessionController.setAlarmManager(mAlarmManager); } @After @@ -195,6 +228,13 @@ @Test public void testScreenOffInactivityTimer() { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + // Support P2P_SMS + when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( + anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged( eq(mTestSatelliteSessionController.getHandler()), anyInt(), any()); when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false); @@ -202,6 +242,7 @@ bundle.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, SCREEN_OFF_INACTIVITY_TIMEOUT_SEC); when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle); + when(mMockSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true); // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. assertNotNull(mTestSatelliteSessionController); @@ -216,12 +257,20 @@ // Notify Screen off sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), false); processAllMessages(); + clearInvocations(mMockSatelliteController); - // Verify that the screen off inactivity timer is started. - assertTrue(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted()); - - // Time shift to cause timeout - moveTimeForward(SCREEN_OFF_INACTIVITY_TIMEOUT_SEC * 1000); + // Verify that the screen off inactivity timer is set. + verify(mAlarmManager).setExact( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + anyLong(), + anyString(), + mExecutorArgumentCaptor.capture(), + any(), + mOnAlarmListenerArgumentCaptor.capture() + ); + // Notify alarm expired + mExecutorArgumentCaptor.getValue().execute( + () -> mOnAlarmListenerArgumentCaptor.getValue().onAlarm()); processAllMessages(); // Verify that SatelliteController#requestSatelliteEnabled() was called. @@ -232,6 +281,13 @@ @Test public void testScreenOffInactivityTimerStop() { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + // Support P2P_SMS + when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( + anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged( eq(mTestSatelliteSessionController.getHandler()), anyInt(), any()); // Satellite enabling request is for an emergency. @@ -240,6 +296,7 @@ bundle.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, SCREEN_OFF_INACTIVITY_TIMEOUT_SEC); when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle); + when(mMockSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true); // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. assertNotNull(mTestSatelliteSessionController); @@ -266,27 +323,37 @@ sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), false); processAllMessages(); - // Verify that the screen off inactivity timer is started. - assertTrue(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted()); + // Verify that the screen off inactivity timer is set. + verify(mAlarmManager).setExact( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + anyLong(), + anyString(), + mExecutorArgumentCaptor.capture(), + any(), + mOnAlarmListenerArgumentCaptor.capture() + ); // Notify Screen on sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), true); + processAllMessages(); - // Verify that the screen off inactivity timer is stopped - assertFalse(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted()); + // Verify that the screen off inactivity timer is clear. + verify(mAlarmManager).cancel(eq(mOnAlarmListenerArgumentCaptor.getValue())); } @Test public void testP2pSmsInactivityTimer() { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); - doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged( - eq(mTestSatelliteSessionController.getHandler()), anyInt(), any()); when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false); when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); when(mMockSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true); PersistableBundle bundle = new PersistableBundle(); bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, @@ -295,23 +362,24 @@ // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. assertNotNull(mTestSatelliteSessionController); + mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false); assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); setupDatagramTransferringState(true); moveToNotConnectedState(); // Verify that the P2P SMS inactivity timer is started. - assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted()); + assertTrue(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted()); mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true); // Verify that the P2P SMS inactivity timer is stopped. - assertFalse(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted()); + assertFalse(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted()); mTestSatelliteSessionController.setDeviceAlignedWithSatellite(false); // Verify that the P2P SMS inactivity timer is started. - assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted()); + assertTrue(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted()); // Time shift to cause timeout moveTimeForward(P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000); @@ -325,8 +393,6 @@ @Test public void testEsosInactivityTimer() { when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); - doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged( - eq(mTestSatelliteSessionController.getHandler()), anyInt(), any()); when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true); @@ -338,23 +404,24 @@ // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. assertNotNull(mTestSatelliteSessionController); + mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false); assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); setupDatagramTransferringState(true); moveToNotConnectedState(); // Verify that the ESOS inactivity timer is started. - assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted()); + assertTrue(mTestSatelliteSessionController.isEsosInActivityTimerStarted()); mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true); // Verify that the ESOS inactivity timer is stopped. - assertFalse(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted()); + assertFalse(mTestSatelliteSessionController.isEsosInActivityTimerStarted()); mTestSatelliteSessionController.setDeviceAlignedWithSatellite(false); // Verify that the ESOS inactivity timer is started. - assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted()); + assertTrue(mTestSatelliteSessionController.isEsosInActivityTimerStarted()); // Time shift to cause timeout moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000); @@ -366,6 +433,543 @@ } @Test + public void testEsosP2pSmsInactivityTimerCase1() { + // Send eSOS and SMS + // After 10 minutes SatelliteSessionController moves to idle + // TN network reports IN_SERVICE + // Report the callback only and don't auto exit + + long passedTime = 0; + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + // Support ESOS + when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true); + when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true); + // Support P2P_SMS + when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( + anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); + + // Setup carrier config for timer values + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, + ESOS_INACTIVITY_TIMEOUT_SEC); + bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, + P2P_SMS_INACTIVITY_TIMEOUT_SEC); + when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle); + + // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. + assertNotNull(mTestSatelliteSessionController); + mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE + setupDatagramTransferringState(true); + + moveToNotConnectedState(); + + // Notify datagram controller is in WAITING_TO_CONNECT. + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // Verify that ESOS, P2P_SMS timer are not started. + verifyEsosP2pSmsInactivityTimer(false, false); + + // Sent ESOS + sendMessage(DATAGRAM_TYPE_SOS_MESSAGE); + + // Verify that ESOS, P2P_SMS timer are started. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Sent SMS + sendMessage(DATAGRAM_TYPE_SMS); + + // Verify that ESOS, P2P_SMS timer are started. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Time shift to cause P2P_SMS timeout + passedTime = P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000; + moveTimeForward(P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000); + processAllMessages(); + + // Verify that keep ESOS timer, expired P2P_SMS timer. + // NOT_CONNECTED state, satellite disabling not called. + verifyEsosP2pSmsInactivityTimer(true, false); + assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + verify(mMockSatelliteController, never()).requestSatelliteEnabled( + eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class)); + + // Time shift to cause ESOS timeout + moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000 - passedTime); + processAllMessages(); + + // Verify that expired ESOS and P2P_SMS timer + // reported IDLE state, not called satellite disabling. + verifyEsosP2pSmsInactivityTimer(false, false); + assertSuccessfulModemStateChangedCallback( + mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + verify(mMockSatelliteController, never()).requestSatelliteEnabled( + eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class)); + } + + @Test + public void testEsosP2pSmsInactivityTimerCase2() { + // Send eSOS and SMS + // Send SMS after 3 mins + // Send SMS after 1 mins + // After 10 minutes SatelliteSessionController moves to idle + // TN network reports IN_SERVICE + // Report the callback only and don't auto exit + + long passedTime = 0; + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + // Support ESOS + when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true); + when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true); + // Support P2P_SMS + when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( + anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); + + // Setup carrier config for timer values + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, + ESOS_INACTIVITY_TIMEOUT_SEC); + bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, + P2P_SMS_INACTIVITY_TIMEOUT_SEC); + when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle); + + // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. + assertNotNull(mTestSatelliteSessionController); + mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE + setupDatagramTransferringState(true); + + moveToNotConnectedState(); + + // Notify datagram controller is in WAITING_TO_CONNECT. + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // Verify that ESOS, P2P_SMS timer are not started. + verifyEsosP2pSmsInactivityTimer(false, false); + + // Sent ESOS, SMS + sendMessage(DATAGRAM_TYPE_SOS_MESSAGE); + sendMessage(DATAGRAM_TYPE_SMS); + // Verify that ESOS, P2P_SMS timer are started. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Sent SMS again after 3 mins + passedTime = 3 * 60 * 1000; + moveTimeForward(3 * 60 * 1000); + processAllMessages(); + sendMessage(DATAGRAM_TYPE_SMS); + + // Sent SMS again after 1 mins + passedTime += 1 * 60 * 1000; + moveTimeForward(1 * 60 * 1000); + processAllMessages(); + sendMessage(DATAGRAM_TYPE_SMS); + + // Time shift to cause ESOS timeout + moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000 - passedTime); + processAllMessages(); + + // Verify that expired ESOS and P2P_SMS timer + // reported IDLE state, not called satellite disabling. + verifyEsosP2pSmsInactivityTimer(false, false); + assertSuccessfulModemStateChangedCallback( + mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + verify(mMockSatelliteController, never()).requestSatelliteEnabled( + eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class)); + } + + @Test + public void testEsosP2pSmsInactivityTimerCase3() { + // Send eSOS and SMS + // Send eSOS after 5 mins + // After 15 minutes SatelliteSessionController moves to idle + // TN network reports IN_SERVICE + // Report the callback only and don't auto exit + + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + // Support ESOS + when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true); + when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true); + // Support P2P_SMS + when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( + anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); + + // Setup carrier config for timer values + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, + ESOS_INACTIVITY_TIMEOUT_SEC); + bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, + P2P_SMS_INACTIVITY_TIMEOUT_SEC); + when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle); + + // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. + assertNotNull(mTestSatelliteSessionController); + mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE + setupDatagramTransferringState(true); + + moveToNotConnectedState(); + + // Notify datagram controller is in WAITING_TO_CONNECT. + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // Verify that ESOS, P2P_SMS timer are not started. + verifyEsosP2pSmsInactivityTimer(false, false); + + // Sent ESOS + sendMessage(DATAGRAM_TYPE_SOS_MESSAGE); + // Verify that ESOS, P2P_SMS timer are started. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Sent ESOS after 5 mins + moveTimeForward(3 * 60 * 1000); + processAllMessages(); + sendMessage(DATAGRAM_TYPE_SOS_MESSAGE); + + // Time shift to cause ESOS timeout + moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000); + processAllMessages(); + + // Verify that expired ESOS and P2P_SMS timer + // reported IDLE state, not called satellite disabling. + verifyEsosP2pSmsInactivityTimer(false, false); + assertSuccessfulModemStateChangedCallback( + mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + verify(mMockSatelliteController, never()).requestSatelliteEnabled( + eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class)); + } + + @Test + public void testEsosP2pSmsInactivityTimerCase4() { + // Send SMS + // Send SMS after 2 mins + // After 3 minutes SatelliteSessionController moves to idle + // TN network reports IN_SERVICE + // Report the callback only and auto exit + + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + // Support ESOS, Satellite is not in emergency mode + when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true); + when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false); + // Support P2P_SMS + when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( + anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); + + // Setup carrier config for timer values + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, + ESOS_INACTIVITY_TIMEOUT_SEC); + bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, + P2P_SMS_INACTIVITY_TIMEOUT_SEC); + when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle); + + // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. + assertNotNull(mTestSatelliteSessionController); + mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE + setupDatagramTransferringState(true); + + moveToNotConnectedState(); + + // Notify datagram controller is in WAITING_TO_CONNECT. + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // Verify that ESOS, P2P_SMS timer are not started. + verifyEsosP2pSmsInactivityTimer(false, false); + + // Sent SMS + sendMessage(DATAGRAM_TYPE_SMS); + + // Verify that ESOS is not started, P2P_SMS timer is started. + verifyEsosP2pSmsInactivityTimer(false, true); + + // Sent SMS again after 2 mins + moveTimeForward(2 * 60 * 1000); + processAllMessages(); + sendMessage(DATAGRAM_TYPE_SMS); + + // Verify that ESOS is not started, P2P_SMS timer is started. + verifyEsosP2pSmsInactivityTimer(false, true); + + // Time shift to cause P2P_SMS timeout + moveTimeForward(P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000); + processAllMessages(); + } + + @Test + public void testEsosP2pSmsInactivityTimerCase5() { + // Send SMS + // Send ESOS after 2 mins + // After 12 minutes SatelliteSessionController moves to idle + // TN network reports IN_SERVICE + // Report the callback only and don'tauto exit + + long passedTime = 0; + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + // Support ESOS + when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true); + when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true); + // Support P2P_SMS + when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( + anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); + + // Setup carrier config for timer values + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, + ESOS_INACTIVITY_TIMEOUT_SEC); + bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, + P2P_SMS_INACTIVITY_TIMEOUT_SEC); + when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle); + + // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. + assertNotNull(mTestSatelliteSessionController); + mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE + setupDatagramTransferringState(true); + + moveToNotConnectedState(); + + // Notify datagram controller is in WAITING_TO_CONNECT. + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // Verify that ESOS, P2P_SMS timer are not started. + verifyEsosP2pSmsInactivityTimer(false, false); + + // Sent SMS + sendMessage(DATAGRAM_TYPE_SMS); + + // Verify that ESOS is not started, P2P_SMS timer is started. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Sent ESOS again after 2 mins + passedTime = 2 * 60 * 1000; + moveTimeForward(2 * 60 * 1000); + processAllMessages(); + sendMessage(DATAGRAM_TYPE_SOS_MESSAGE); + + // Verify that ESOS is not started, P2P_SMS timer is started. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Time shift + moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000 - passedTime); + processAllMessages(); + verifyEsosP2pSmsInactivityTimer(true, false); + + // Time shift + moveTimeForward(passedTime); + processAllMessages(); + + // Verify that expired P2P_SMS timer + // reported IDLE state, called satellite disabling. + verifyEsosP2pSmsInactivityTimer(false, false); + assertSuccessfulModemStateChangedCallback( + mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + verify(mMockSatelliteController, never()).requestSatelliteEnabled( + eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class)); + } + + @Test + public void testEsosP2pSmsInactivityTimerInConnectedState() { + // Send eSOS and SMS + // After 10 minutes SatelliteSessionController moves to idle + // TN network reports IN_SERVICE + // Report the callback only and don't auto exit + + long passedTime = 0; + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + // Support ESOS + when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true); + when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true); + // Support P2P_SMS + when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported( + anyInt())).thenReturn(true); + when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt())) + .thenReturn(new int[]{ + NetworkRegistrationInfo.SERVICE_TYPE_SMS, + NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY}); + + // Setup carrier config for timer values + PersistableBundle bundle = new PersistableBundle(); + bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, + ESOS_INACTIVITY_TIMEOUT_SEC); + bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, + P2P_SMS_INACTIVITY_TIMEOUT_SEC); + when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle); + + // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. + assertNotNull(mTestSatelliteSessionController); + mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE + setupDatagramTransferringState(true); + + moveToNotConnectedState(); + moveNotConnectedToConnectedState(); + + // Verify that ESOS, P2P_SMS timer are started. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Sent ESOS + // CONNECTED -> TRANSFERRING -> CONNECTED + sendMessage(DATAGRAM_TYPE_SOS_MESSAGE); + + // Verify that ESOS, P2P_SMS timer are restarted. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Sent SMS + // CONNECTED -> TRANSFERRING -> CONNECTED + sendMessage(DATAGRAM_TYPE_SMS); + + // Verify that ESOS, P2P_SMS timer are restarted. + verifyEsosP2pSmsInactivityTimer(true, true); + + // Time shift to cause P2P_SMS timeout + passedTime = P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000; + moveTimeForward(P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000); + processAllMessages(); + + // Verify that keep ESOS timer, expired P2P_SMS timer. + // CONNECTED state + verifyEsosP2pSmsInactivityTimer(true, false); + assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + + // Time shift to cause ESOS timeout + moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000 - passedTime); + processAllMessages(); + + // Verify that expired ESOS and P2P_SMS timer + // reported IDLE state. + verifyEsosP2pSmsInactivityTimer(false, false); + assertSuccessfulModemStateChangedCallback( + mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + } + + @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); + setUpResponseForRequestSatelliteEnabled(SATELLITE_RESULT_SUCCESS); + + // 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)); + verify(mMockSessionMetricsStats, never()).addCountOfAutoExitDueToTnNetwork(); + + // 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)); + verify(mMockSessionMetricsStats, never()).addCountOfAutoExitDueToTnNetwork(); + + // 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)); + verify(mMockSessionMetricsStats, never()).addCountOfAutoExitDueToTnNetwork(); + // 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)); + verify(mMockSessionMetricsStats, times(1)).addCountOfAutoExitDueToTnNetwork(); + } + + @Test public void testStateTransition() { /** * Since satellite is supported, SatelliteSessionController should move to POWER_OFF state. @@ -401,7 +1005,8 @@ // Start sending datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -413,7 +1018,8 @@ // Sending datagrams failed mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to IDLE state. @@ -425,7 +1031,8 @@ // Start sending datagrams again mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -437,7 +1044,8 @@ // Sending datagrams is successful and done. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to LISTENING state. @@ -450,7 +1058,8 @@ // Start receiving datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -462,7 +1071,8 @@ // Receiving datagrams is successful and done. mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to LISTENING state. @@ -475,7 +1085,8 @@ // Start receiving datagrams again mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -488,7 +1099,8 @@ // Receiving datagrams failed. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to IDLE state. @@ -500,7 +1112,8 @@ // Start receiving datagrams again mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -511,7 +1124,8 @@ // Receiving datagrams is successful and done. mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to LISTENING state. @@ -535,7 +1149,8 @@ // Start receiving datagrams again mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -547,7 +1162,8 @@ // Start sending datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should stay at TRANSFERRING state. @@ -558,7 +1174,8 @@ // Receiving datagrams failed. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should stay at TRANSFERRING state instead of moving to IDLE @@ -570,7 +1187,8 @@ // Start receiving datagrams again. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should stay at TRANSFERRING state. @@ -581,7 +1199,8 @@ // Sending datagrams failed. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should stay at TRANSFERRING state instead of moving to IDLE @@ -657,7 +1276,8 @@ // Start sending datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // The datagram sending event should be ignored. @@ -680,7 +1300,8 @@ // Start sending datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -695,7 +1316,8 @@ // Sending datagrams failed mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to CONNECTED state. @@ -710,7 +1332,8 @@ // Start sending datagrams again mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -725,7 +1348,8 @@ // Sending datagrams is successful and done. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to CONNECTED state. @@ -740,7 +1364,8 @@ // Start receiving datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -754,7 +1379,8 @@ // Receiving datagrams is successful and done. mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to CONNECTED state. @@ -769,7 +1395,8 @@ // Start receiving datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS); + SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -783,7 +1410,8 @@ // Receiving datagrams is successful and done. mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to CONNECTED state. @@ -811,7 +1439,8 @@ // Start sending datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to NOT_CONNECTED state. @@ -903,22 +1532,6 @@ SatelliteManager.SATELLITE_MODEM_STATE_IDLE); assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); - // Set up error response for the request to disable cellular scanning - mSatelliteModemInterface.setErrorCode(SatelliteManager.SATELLITE_RESULT_MODEM_ERROR); - - // Start sending datagrams - mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); - processAllMessages(); - - // SatelliteSessionController should stay at IDLE state because it failed to disable - // cellular scanning. - assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback); - assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); - - mSatelliteModemInterface.setErrorCode(SatelliteManager.SATELLITE_RESULT_SUCCESS); - // Power off the modem. mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false); processAllMessages(); @@ -965,7 +1578,8 @@ // Start sending datagrams and the NB-IOT inactivity timer should be stopped. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS); processAllMessages(); @@ -977,10 +1591,12 @@ // The NB-IOT inactivity timer should be started. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); @@ -1230,9 +1846,307 @@ assertEmergencyModeChangedCallbackNotCalled(mTestSatelliteModemStateCallback); } - private void setupDatagramTransferringState(boolean isTransferring) { - when(mMockDatagramController.isSendingInIdleState()).thenReturn(isTransferring); - when(mMockDatagramController.isPollingInIdleState()).thenReturn(isTransferring); + @Test + public void testNotConnectedToIdleToNotConnectedStateTransition() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + when(mContext.getResources().getBoolean( + R.bool.config_satellite_modem_support_concurrent_tn_scanning)).thenReturn(true); + + assertNotNull(mTestSatelliteSessionController); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + setupDatagramTransferringState(true); + + powerOnSatelliteModem(); + + // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem + // is powered on. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + // The inactivity timer should be started. + assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + clearInvocations(mMockDatagramController); + + // Wait for timeout + moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS); + processAllMessages(); + + // SatelliteSessionController should move to IDLE state, but the state transition will + // be hidden because device does not support satellite modem IDLE state. + assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback); + assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); + // The inactivity timer should be stopped. + assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + // The transition is hidden and thus DatagramController is not notified. + verify(mMockDatagramController, never()).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + clearInvocations(mMockDatagramController); + + // Start sending datagrams + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // SatelliteSessionController should move to NOT_CONNECTED state + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + clearInvocations(mMockDatagramController); + } + + @Test + public void testNotConnectedToIdleToTransferringStateTransition() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + when(mContext.getResources().getBoolean( + R.bool.config_satellite_modem_support_concurrent_tn_scanning)).thenReturn(true); + + assertNotNull(mTestSatelliteSessionController); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + setupDatagramTransferringState(true); + + powerOnSatelliteModem(); + + // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem + // is powered on. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + // The inactivity timer should be started. + assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + clearInvocations(mMockDatagramController); + + // Wait for timeout + moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS); + processAllMessages(); + + // SatelliteSessionController should move to IDLE state, but the state transition will + // be hidden because device does not support satellite modem IDLE state. + assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback); + assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); + // The inactivity timer should be stopped. + assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + // The transition is hidden and thus DatagramController is not notified. + verify(mMockDatagramController, never()).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + clearInvocations(mMockDatagramController); + + // Modem report CONNECTED state + mTestSatelliteSessionController.onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + processAllMessages(); + + // SatelliteSessionController should stay in IDLE state, but clients should be + // notified that modem has moved to CONNECTED state. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + clearInvocations(mMockDatagramController); + + // Start sending datagrams + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // SatelliteSessionController should move to TRANSFERRING state. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING); + assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING); + clearInvocations(mMockDatagramController); + } + + @Test + public void testConnectedToIdleToTransferringStateTransition() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + when(mContext.getResources().getBoolean( + R.bool.config_satellite_modem_support_concurrent_tn_scanning)).thenReturn(true); + + assertNotNull(mTestSatelliteSessionController); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + setupDatagramTransferringState(false); + + powerOnSatelliteModem(); + + // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem + // is powered on. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + clearInvocations(mMockDatagramController); + + // Modem report CONNECTED state + setupDatagramTransferringState(true); + mTestSatelliteSessionController.onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + processAllMessages(); + + // SatelliteSessionController should move to CONNECTED state. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + // The inactivity timer should be started. + assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + clearInvocations(mMockDatagramController); + + // Wait for timeout + moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS); + processAllMessages(); + + // SatelliteSessionController should move to IDLE state, but the state transition will + // be hidden because device does not support satellite modem IDLE state. + assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback); + assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); + // The inactivity timer should be stopped. + assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + // The transition is hidden and thus DatagramController is not notified. + verify(mMockDatagramController, never()).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + clearInvocations(mMockDatagramController); + + // Start sending datagrams + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // SatelliteSessionController should move to TRANSFERRING state. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING); + assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING); + clearInvocations(mMockDatagramController); + } + + @Test + public void testConnectedToIdleToNotConnectedStateTransition() { + when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); + when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true); + when(mContext.getResources().getBoolean( + R.bool.config_satellite_modem_support_concurrent_tn_scanning)).thenReturn(true); + + assertNotNull(mTestSatelliteSessionController); + assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName()); + setupDatagramTransferringState(false); + + powerOnSatelliteModem(); + + // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem + // is powered on. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + clearInvocations(mMockDatagramController); + + // Modem report CONNECTED state + setupDatagramTransferringState(true); + mTestSatelliteSessionController.onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + processAllMessages(); + + // SatelliteSessionController should move to CONNECTED state. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + // The inactivity timer should be started. + assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED); + clearInvocations(mMockDatagramController); + + // Wait for timeout + moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS); + processAllMessages(); + + // SatelliteSessionController should move to IDLE state, but the state transition will + // be hidden because device does not support satellite modem IDLE state. + assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback); + assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); + // The inactivity timer should be stopped. + assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted()); + // The transition is hidden and thus DatagramController is not notified. + verify(mMockDatagramController, never()).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_IDLE); + clearInvocations(mMockDatagramController); + + // Modem report NOT_CONNECTED state + mTestSatelliteSessionController.onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + processAllMessages(); + + // SatelliteSessionController should stay in IDLE state, but the clients + // should be notified that modem has moved to NOT_CONNECTED state. + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + clearInvocations(mMockDatagramController); + + // Start sending datagrams + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); + processAllMessages(); + + // SatelliteSessionController should move to NOT_CONNECTED state + assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback, + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName()); + verify(mMockDatagramController).onSatelliteModemStateChanged( + SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED); + clearInvocations(mMockDatagramController); + } + + private void verifyEsosP2pSmsInactivityTimer(boolean esosTimer, boolean p2pSmsTimer) { + assertEquals(mTestSatelliteSessionController.isEsosInActivityTimerStarted(), esosTimer); + assertEquals(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted(), + p2pSmsTimer); + } + + private void sendMessage(@SatelliteManager.DatagramType int datagramType) { + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, datagramType); + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS, + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, datagramType); + mTestSatelliteSessionController.onDatagramTransferStateChanged( + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, datagramType); + + processAllMessages(); + } + + private void setupDatagramTransferringState(boolean isIdle) { + when(mMockDatagramController.isSendingInIdleState()).thenReturn(isIdle); + when(mMockDatagramController.isPollingInIdleState()).thenReturn(isIdle); } private void powerOnSatelliteModem() { @@ -1293,7 +2207,8 @@ assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName()); // Start sending datagrams mTestSatelliteSessionController.onDatagramTransferStateChanged( - SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to TRANSFERRING state. @@ -1308,7 +2223,8 @@ // Sending datagrams is successful and done. mTestSatelliteSessionController.onDatagramTransferStateChanged( SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, - SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); + SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, + DATAGRAM_TYPE_UNKNOWN); processAllMessages(); // SatelliteSessionController should move to LISTENING state. @@ -1419,6 +2335,8 @@ } private static class TestSatelliteSessionController extends SatelliteSessionController { + boolean mSatelliteEnabledForNtnOnlySubscription = true; + TestSatelliteSessionController(Context context, Looper looper, FeatureFlags featureFlags, boolean isSatelliteSupported, SatelliteModemInterface satelliteModemInterface) { @@ -1441,16 +2359,12 @@ return hasDeferredMessages(event); } - boolean isScreenOffInActivityTimerStarted() { - return hasMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT); - } - - boolean isCarrierRoamingNbIotInActivityTimerStarted() { - return hasMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT); - } - protected boolean isSatelliteEnabledForNtnOnlySubscription() { - return true; + return mSatelliteEnabledForNtnOnlySubscription; + } + + void setSatelliteEnabledForNtnOnlySubscription(boolean enabled) { + mSatelliteEnabledForNtnOnlySubscription = false; } } @@ -1493,6 +2407,11 @@ logd("onRegistrationFailure: causeCode=" + causeCode); } + @Override + public void onTerrestrialNetworkAvailableChanged(boolean isAvailable) { + logd("onTerrestrialNetworkAvailableChanged: isAvailable=" + isAvailable); + } + public boolean waitUntilResultForModemStateChanged() { try { if (!mSemaphoreForModemStateChanged.tryAcquire(EVENT_PROCESSING_TIME_MILLIS, @@ -1587,4 +2506,14 @@ msg.obj = new AsyncResult(null, screenOn, null); h.sendMessage(msg); } + + private void setUpResponseForRequestSatelliteEnabled( + @SatelliteManager.SatelliteResult int expectedResult) { + doAnswer(invocation -> { + IIntegerConsumer integerConsumer = invocation.getArgument(3); + integerConsumer.accept(expectedResult); + return null; + }).when(mMockSatelliteController).requestSatelliteEnabled(anyBoolean(), anyBoolean(), + anyBoolean(), any(IIntegerConsumer.class)); + } }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java index 4b1b4a5..5e560bc 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -457,7 +457,6 @@ doReturn(1).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID1)); doReturn(2).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID2)); when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true); - when(mFeatureFlags.dataOnlyCellularService()).thenReturn(true); when(mFeatureFlags.supportPsimToEsimConversion()).thenReturn(true); when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true); mDatabaseManagerUT = new SubscriptionDatabaseManager(mContext, Looper.myLooper(),
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java index 925cf71..f639a51 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
@@ -24,8 +24,6 @@ import android.telephony.UiccAccessRule; import android.telephony.ims.ImsMmTelManager; -import com.android.internal.telephony.flags.Flags; - import org.junit.Rule; import org.junit.Test; @@ -160,7 +158,6 @@ @Test public void testSubscriptionInfoInternalSetAndGet() { - mSetFlagsRule.enableFlags(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE); assertThat(mSubInfo.getSubscriptionId()).isEqualTo(1); assertThat(mSubInfo.getIccId()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_ICCID1); assertThat(mSubInfo.getSimSlotIndex()).isEqualTo(0); @@ -271,7 +268,6 @@ @Test public void testConvertToSubscriptionInfo() { - mSetFlagsRule.enableFlags(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE); SubscriptionInfo subInfo = mSubInfo.toSubscriptionInfo(); assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java index 65790f8..66b2eb8 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -70,6 +70,7 @@ import android.Manifest; import android.annotation.NonNull; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PropertyInvalidatedCache; import android.compat.testing.PlatformCompatChangeRule; @@ -1123,9 +1124,12 @@ public void testGetAccessibleSubscriptionInfoList() { doReturn(true).when(mEuiccManager).isEnabled(); insertSubscription(FAKE_SUBSCRIPTION_INFO2); + UserHandle user = UserHandle.of(ActivityManager.getCurrentUser()); doReturn(true).when(mSubscriptionManager).canManageSubscription( any(SubscriptionInfo.class), eq(CALLING_PACKAGE)); + doReturn(true).when(mSubscriptionManager).canManageSubscriptionAsUser( + any(SubscriptionInfo.class), eq(CALLING_PACKAGE), any(UserHandle.class)); // FAKE_SUBSCRIPTION_INFO2 is a not eSIM. So the list should be empty. assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList( CALLING_PACKAGE)).isEmpty(); @@ -1138,6 +1142,8 @@ doReturn(false).when(mSubscriptionManager).canManageSubscription( any(SubscriptionInfo.class), eq(CALLING_PACKAGE)); + doReturn(false).when(mSubscriptionManager).canManageSubscriptionAsUser( + any(SubscriptionInfo.class), eq(CALLING_PACKAGE), eq(user)); doReturn(true).when(mEuiccManager).isEnabled(); assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList( @@ -1145,6 +1151,8 @@ doReturn(true).when(mSubscriptionManager).canManageSubscription( any(SubscriptionInfo.class), eq(CALLING_PACKAGE)); + doReturn(true).when(mSubscriptionManager).canManageSubscriptionAsUser( + any(SubscriptionInfo.class), eq(CALLING_PACKAGE), eq(user)); assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList( CALLING_PACKAGE)).isEqualTo(List.of(new SubscriptionInfoInternal.Builder( FAKE_SUBSCRIPTION_INFO1).setId(2).build().toSubscriptionInfo())); @@ -1363,6 +1371,9 @@ doReturn(true).when(mEuiccManager).isEnabled(); doReturn(true).when(mSubscriptionManager).canManageSubscription( any(SubscriptionInfo.class), eq(CALLING_PACKAGE)); + UserHandle user = UserHandle.of(ActivityManager.getCurrentUser()); + doReturn(true).when(mSubscriptionManager).canManageSubscriptionAsUser( + any(SubscriptionInfo.class), eq(CALLING_PACKAGE), eq(user)); assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList( CALLING_PACKAGE)).isEqualTo(List.of(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo())); // Test getActiveSubIdList, System @@ -1499,6 +1510,9 @@ doReturn(true).when(mEuiccManager).isEnabled(); doReturn(true).when(mSubscriptionManager).canManageSubscription( any(SubscriptionInfo.class), eq(CALLING_PACKAGE)); + UserHandle user = UserHandle.of(ActivityManager.getCurrentUser()); + doReturn(true).when(mSubscriptionManager).canManageSubscriptionAsUser( + any(SubscriptionInfo.class), eq(CALLING_PACKAGE), eq(user)); assertThat(mSubscriptionManagerServiceUT.getAccessibleSubscriptionInfoList( CALLING_PACKAGE)).isEqualTo(List.of(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo())); // Test getActiveSubIdList, System
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java index 33b195c..bfdca0f 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -69,6 +69,7 @@ @After public void tearDown() throws Exception { + mUiccCard.dispose(); mUiccCard = null; mIccIoResult = null; super.tearDown();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java index 58a8153..3343570 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -15,8 +15,6 @@ */ package com.android.internal.telephony.uicc; -import static junit.framework.Assert.fail; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -131,6 +129,7 @@ @After public void tearDown() throws Exception { + if (mUiccControllerUT != null) mUiccControllerUT.dispose(); mUiccControllerUT = null; super.tearDown(); } @@ -145,6 +144,7 @@ com.android.internal.R.array.non_removable_euicc_slots, nonRemovableEuiccSlots); replaceInstance(UiccController.class, "mInstance", null, null); + mUiccControllerUT.dispose(); mUiccControllerUT = UiccController.make(mContext, mFeatureFlags); processAllMessages(); } @@ -250,7 +250,7 @@ mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(mMockCard).when(mMockSlot).getUiccCard(); doReturn(mMockPort).when(mMockCard).getUiccPort(0); doReturn("A1B2C3D4").when(mMockPort).getIccId(); @@ -296,7 +296,7 @@ mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(true).when(mMockSlot).isEuicc(); // simulate slot status loaded so that the UiccController sets the card ID @@ -323,7 +323,7 @@ mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(true).when(mMockSlot).isEuicc(); // simulate slot status loaded so that the UiccController sets the card ID @@ -351,7 +351,7 @@ mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(false).when(mMockSlot).isEuicc(); doReturn(mMockCard).when(mMockSlot).getUiccCard(); doReturn("ASDF1234").when(mMockCard).getCardId(); @@ -402,7 +402,7 @@ mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(false).when(mMockSlot).isEuicc(); doReturn(mMockCard).when(mMockSlot).getUiccCard(); doReturn("ASDF1234").when(mMockCard).getCardId(); @@ -453,7 +453,7 @@ mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(true).when(mMockSlot).isEuicc(); doReturn(null).when(mMockSlot).getUiccCard(); doReturn(false).when(mMockSlot).isRemovable(); @@ -499,21 +499,17 @@ * The default eUICC should not be the removable slot if there is a built-in eUICC. */ @Test - public void testDefaultEuiccIsNotRemovable() { - try { - reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */); - } catch (Exception e) { - fail("Unable to reconfigure slots."); - } + public void testDefaultEuiccIsNotRemovable() throws Exception { + reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */); // Give UiccController a real context so it can use shared preferences mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots so that [0] is a removable eUICC and [1] is built-in - mUiccControllerUT.mUiccSlots[0] = mMockRemovableEuiccSlot; + mUiccControllerUT.setUiccSlot(0, mMockRemovableEuiccSlot); doReturn(true).when(mMockRemovableEuiccSlot).isEuicc(); doReturn(true).when(mMockRemovableEuiccSlot).isRemovable(); - mUiccControllerUT.mUiccSlots[1] = mMockSlot; + mUiccControllerUT.setUiccSlot(1, mMockSlot); doReturn(true).when(mMockSlot).isEuicc(); doReturn(false).when(mMockSlot).isRemovable(); @@ -550,21 +546,17 @@ * not depend on the order of the slots. */ @Test - public void testDefaultEuiccIsNotRemovable_swapSlotOrder() { - try { - reconfigureSlots(2, new int[]{ 0 } /* non-removable slot */); - } catch (Exception e) { - fail("Unable to reconfigure slots."); - } + public void testDefaultEuiccIsNotRemovable_swapSlotOrder() throws Exception { + reconfigureSlots(2, new int[]{ 0 } /* non-removable slot */); // Give UiccController a real context so it can use shared preferences mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots so that [0] is a built-in eUICC and [1] is removable - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(true).when(mMockSlot).isEuicc(); doReturn(false).when(mMockSlot).isRemovable(); - mUiccControllerUT.mUiccSlots[1] = mMockRemovableEuiccSlot; + mUiccControllerUT.setUiccSlot(1, mMockRemovableEuiccSlot); doReturn(true).when(mMockRemovableEuiccSlot).isEuicc(); doReturn(true).when(mMockRemovableEuiccSlot).isRemovable(); @@ -603,21 +595,17 @@ * the removable eUICC. */ @Test - public void testDefaultEuiccIsNotRemovable_EuiccIsInactive() { - try { - reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */); - } catch (Exception e) { - fail(); - } + public void testDefaultEuiccIsNotRemovable_EuiccIsInactive() throws Exception { + reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */); // Give UiccController a real context so it can use shared preferences mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots. Slot 0 is inactive here. - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(true).when(mMockSlot).isEuicc(); doReturn(false).when(mMockSlot).isRemovable(); - mUiccControllerUT.mUiccSlots[1] = mMockRemovableEuiccSlot; + mUiccControllerUT.setUiccSlot(1, mMockRemovableEuiccSlot); doReturn(true).when(mMockRemovableEuiccSlot).isEuicc(); doReturn(true).when(mMockRemovableEuiccSlot).isRemovable(); @@ -669,7 +657,7 @@ mUiccControllerUT.mContext = InstrumentationRegistry.getContext(); // Mock out UiccSlots - mUiccControllerUT.mUiccSlots[0] = mMockSlot; + mUiccControllerUT.setUiccSlot(0, mMockSlot); doReturn(true).when(mMockSlot).isEuicc(); doReturn(null).when(mMockSlot).getUiccCard(); //doReturn("123451234567890").when(mMockSlot).getIccId();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java index a2b42af..47b7c53 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
@@ -82,6 +82,7 @@ @After public void tearDown() throws Exception { + mUiccPort.dispose(); mUiccPort = null; mIccIoResult = null; super.tearDown();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java index 671f273..8449ecc 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -103,6 +103,7 @@ mTestHandlerThread = null; mTestHandler.removeCallbacksAndMessages(null); mTestHandler = null; + mUiccSlot.dispose(); mUiccSlot = null; super.tearDown(); }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java index f88bc1e..c9b159c 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -51,6 +51,8 @@ private static final int CARD_COUNT = 1; private static final String PROVISIONING_PACKAGE_NAME = "test.provisioning.package"; + private UiccCard mUiccCardToDispose; + // Mocked classes private Resources mResources; @@ -77,6 +79,9 @@ @After public void tearDown() throws Exception { super.tearDown(); + + if (mUiccCardToDispose != null) mUiccCardToDispose.dispose(); + mUiccCardToDispose = null; } @Test @SmallTest @@ -99,7 +104,7 @@ msg.what = integerArgumentCaptor.getValue(); // The first broadcast should be sent after initialization. - UiccCard card = new UiccCard(mContext, mSimulatedCommands, + UiccCard card = mUiccCardToDispose = new UiccCard(mContext, mSimulatedCommands, makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */, new Object(), IccSlotStatus.MultipleEnabledProfilesMode.NONE); when(UiccController.getInstance().getUiccCardForPhone(0)).thenReturn(card);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java index bcb5c4c..560c9aa 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -109,7 +109,10 @@ mHandler.removeCallbacksAndMessages(null); mHandler = null; } - mEuiccCard = null; + if (mEuiccCard != null) { + mEuiccCard.dispose(); + mEuiccCard = null; + } super.tearDown(); } @@ -132,6 +135,7 @@ @Test public void testPassEidInConstructor() { mMockIccCardStatus.eid = "1A2B3C4D"; + mEuiccCard.dispose(); mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi, mMockIccCardStatus, 0 /* phoneId */, new Object(), IccSlotStatus.MultipleEnabledProfilesMode.NONE); @@ -154,6 +158,7 @@ public void testLoadEidAndNotifyRegistrants() { int channel = mockLogicalChannelResponses("BF3E065A041A2B3C4D9000"); mHandler.post(() -> { + mEuiccCard.dispose(); mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi, mMockIccCardStatus, 0 /* phoneId */, new Object(), IccSlotStatus.MultipleEnabledProfilesMode.NONE);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java index 2fef021..f0f1af3 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
@@ -141,6 +141,7 @@ public void tearDown() throws Exception { mHandler.removeCallbacksAndMessages(null); mHandler = null; + mEuiccPort.dispose(); mEuiccPort = null; super.tearDown(); }
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 a65814e..1252ff8 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,36 +16,52 @@ 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; @@ -75,6 +91,13 @@ } } + 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; @@ -82,19 +105,20 @@ private Handler mHandler; private ResponseCaptor mResponseCaptor; private byte[] mSelectResponse; - private static final String AID = "B2C3D4"; private ApduSender mSender; @Before public void setUp() { - mMockCi = mock(CommandsInterface.class); - mHandler = new Handler(Looper.myLooper()); + mSetFlagsRule.enableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER); + mMockCi = mock(CommandsInterface.class); + mLooper = TestableLooper.get(this); + mHandler = new Handler(mLooper.getLooper()); mResponseCaptor = new ResponseCaptor(); mSelectResponse = null; - mSender = new ApduSender(mMockCi, AID, false /* supportExtendedApdu */); - mLooper = TestableLooper.get(this); + mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID, + mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */); } @After @@ -105,6 +129,19 @@ 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 @@ -119,7 +156,7 @@ assertEquals("A1A1A19000", IccUtils.bytesToHexString(mSelectResponse)); assertNull(mResponseCaptor.response); assertNull(mResponseCaptor.exception); - verify(mMockCi).iccOpenLogicalChannel(eq(AID), anyInt(), any()); + verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any()); verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any()); } @@ -135,7 +172,7 @@ assertNull("Request provider should not be called when failed to open channel.", mSelectResponse); assertTrue(mResponseCaptor.exception instanceof ApduException); - verify(mMockCi).iccOpenLogicalChannel(eq(AID), anyInt(), any()); + verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any()); } @Test @@ -149,8 +186,11 @@ mLooper.processAllMessages(); assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response)); - verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2), - eq(3), eq(0), eq("a"), anyBoolean(), any()); + 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()); } @Test @@ -169,14 +209,17 @@ mLooper.processAllMessages(); assertEquals("A4", IccUtils.bytesToHexString(mResponseCaptor.response)); - 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()); + 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()); } @Test @@ -339,6 +382,158 @@ assertNull("Should not open channel when another one is already opened.", mSelectResponse); assertTrue(mResponseCaptor.exception instanceof ApduException); - verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(AID), anyInt(), any()); + verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any()); + } + + @Test + public void testConstructor_doNotCloseOpenChannelInSharedPreference() + 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, times(0)).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(); } }