Merge "Support handling reconnect to QualifiedNetworkType" into main
diff --git a/OWNERS b/OWNERS
index 7e57200..ff1a04e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -18,4 +18,4 @@
xiaotonj@google.com
# Domain Selection code is co-owned, adding additional owners for this code
-per-file EmergencyStateTracker*=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com
+per-file EmergencyStateTracker*=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,jdyou@google.com
diff --git a/flags/calling.aconfig b/flags/calling.aconfig
index 82f932b..e67ebc6 100644
--- a/flags/calling.aconfig
+++ b/flags/calling.aconfig
@@ -5,4 +5,11 @@
namespace: "telephony"
description: "APIs that are used to notify simultaneous calling changes to other applications."
bug: "297446980"
+}
+
+flag {
+ name: "show_call_fail_notification_for_2g_toggle"
+ namespace: "telephony"
+ description: "Used in DisconnectCause and TelephonyConnection if a non-emergency call fails on a device with no 2G, to guard whether a user can see an updated error message reminding the 2G is disabled and potentially disrupting their call connectivity"
+ bug: "300142897"
}
\ No newline at end of file
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index dff6426..e67dabe 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -70,3 +70,13 @@
description: "Allows applications to launch Reset Mobile Network Settings page in Settings app."
bug:"271921464"
}
+
+flag {
+ name: "fix_crash_on_getting_config_when_phone_is_gone"
+ namespace: "telephony"
+ description: "Fix VCN crash when calling CarrierConfigManager.getConfigForSubId while phone process has gone."
+ bug:"319791612"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/flags/network.aconfig b/flags/network.aconfig
index e4ae5ee..c0394e8 100644
--- a/flags/network.aconfig
+++ b/flags/network.aconfig
@@ -53,5 +53,12 @@
name: "support_nr_sa_rrc_idle"
namespace: "telephony"
description: "Support RRC idle for NR SA."
- bug: "301467052"
+ bug: "298233308"
+}
+
+flag {
+ name: "network_registration_info_reject_cause"
+ namespace: "telephony"
+ description: "Elevate NRI#getRejectCause from System to Public"
+ bug: "239730435"
}
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index d2c36d6..cebedd5 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -26,4 +26,18 @@
namespace: "telephony"
description: "Support emergency call only for data only cellular service."
bug: "296097429"
+}
+
+flag {
+ name: "support_psim_to_esim_conversion"
+ namespace: "telephony"
+ description: "Support the psim to esim conversion."
+ bug: "315073761"
+}
+
+flag {
+ name: "subscription_user_association_query"
+ namespace: "telephony"
+ description: "Supports querying if a subscription is associated with the caller"
+ bug: "325045841"
}
\ No newline at end of file
diff --git a/flags/telephony.aconfig b/flags/telephony.aconfig
index c6fd2c4..d59b249 100644
--- a/flags/telephony.aconfig
+++ b/flags/telephony.aconfig
@@ -19,4 +19,11 @@
namespace: "telephony"
description: "This flag controls the order of the binder to prevent deadlock in system_server"
bug: "315973270"
+}
+
+flag {
+ name: "prevent_invocation_repeat_of_ril_call_when_device_does_not_support_voice"
+ namespace: "telephony"
+ description: "This flag prevents repeat invocation of call related APIs in RIL when the device is not voice capable"
+ bug: "290833783"
}
\ No newline at end of file
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index 5b74d1a..c1b860f 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -18,10 +18,15 @@
description: "This flag controls the visibility of the getCarrierRestrictionStatus in carrierRestrictionRules class."
bug:"313553044"
}
-
flag {
name: "carrier_restriction_rules_enhancement"
namespace: "telephony"
description: "This flag controls the new enhancements to the existing carrier restrictions rules"
bug:"317226653"
-}
\ No newline at end of file
+}
+flag {
+ name: "esim_available_memory"
+ namespace: "telephony"
+ description: "This flag controls eSIM available memory feature."
+ bug:"318348580"
+}
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 02a4125..c07c797 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -421,6 +421,7 @@
}
message ImsRegistrationStats {
+ reserved 16;
optional int32 carrier_id = 1;
optional int32 sim_slot_index = 2;
optional int32 rat = 3;
@@ -437,7 +438,7 @@
optional int64 registering_millis = 13;
optional int64 unregistered_millis = 14;
optional bool is_iwlan_cross_sim = 15;
- optional int64 registered_times = 16;
+ optional int32 registered_times = 17;
// Internal use only
optional int64 last_used_millis = 10001;
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
index 38c6672..5e617f9 100644
--- a/src/java/com/android/internal/telephony/CallTracker.java
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
@@ -25,8 +26,11 @@
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import com.android.internal.telephony.flags.FeatureFlags;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -55,6 +59,9 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected boolean mNumberConverted = false;
+
+ protected final @NonNull FeatureFlags mFeatureFlags;
+
private final int VALID_COMPARE_LENGTH = 3;
//***** Events
@@ -77,7 +84,8 @@
protected static final int EVENT_THREE_WAY_DIAL_BLANK_FLASH = 20;
@UnsupportedAppUsage
- public CallTracker() {
+ public CallTracker(FeatureFlags featureFlags) {
+ mFeatureFlags = featureFlags;
}
protected void pollCallsWhenSafe() {
@@ -91,6 +99,14 @@
protected void
pollCallsAfterDelay() {
+ if (mFeatureFlags.preventInvocationRepeatOfRilCallWhenDeviceDoesNotSupportVoice()) {
+ if (!mCi.getHalVersion(TelephonyManager.HAL_SERVICE_VOICE)
+ .greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)) {
+ log("Skip polling because HAL_SERVICE_VOICE < RADIO_HAL_VERSION_1.4");
+ return;
+ }
+ }
+
Message msg = obtainMessage();
msg.what = EVENT_REPOLL_AFTER_DELAY;
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index d76ee19..5517bc6 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -47,6 +47,7 @@
import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
import com.android.internal.telephony.domainselection.DomainSelectionResolver;
import com.android.internal.telephony.emergency.EmergencyStateTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.telephony.Rlog;
@@ -155,7 +156,9 @@
//***** Constructors
- public GsmCdmaCallTracker (GsmCdmaPhone phone) {
+ public GsmCdmaCallTracker(GsmCdmaPhone phone, FeatureFlags featureFlags) {
+ super(featureFlags);
+
this.mPhone = phone;
mCi = phone.mCi;
mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 7e2143a..aca759b 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -16,10 +16,7 @@
package com.android.internal.telephony;
-import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
-import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS_PS;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
-import static android.telephony.NetworkRegistrationInfo.DOMAIN_UNKNOWN;
import static com.android.internal.telephony.CommandException.Error.GENERIC_FAILURE;
import static com.android.internal.telephony.CommandException.Error.SIM_BUSY;
@@ -75,7 +72,6 @@
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Annotation.DataActivityType;
import android.telephony.Annotation.RadioPowerState;
-import android.telephony.AnomalyReporter;
import android.telephony.BarringInfo;
import android.telephony.CarrierConfigManager;
import android.telephony.CellBroadcastIdRange;
@@ -96,6 +92,7 @@
import android.telephony.UssdResponse;
import android.telephony.ims.ImsCallProfile;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -119,6 +116,7 @@
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
+import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource;
import com.android.internal.telephony.security.NullCipherNotifier;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
@@ -150,7 +148,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Set;
-import java.util.UUID;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -304,15 +301,24 @@
private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
private final CallWaitingController mCallWaitingController;
+ private CellularNetworkSecuritySafetySource mSafetySource;
private CellularIdentifierDisclosureNotifier mIdentifierDisclosureNotifier;
private NullCipherNotifier mNullCipherNotifier;
+ /**
+ * Temporary placeholder variables until b/312788638 is resolved, whereupon these should be
+ * ported to TelephonyManager.
+ */
// Set via Carrier Config
- private boolean mIsN1ModeAllowedByCarrier = true;
+ private static final Integer N1_MODE_DISALLOWED_REASON_CARRIER = 1;
// Set via a call to the method on Phone; the only caller is IMS, and all of this code will
// need to be updated to a voting mechanism (...enabled for reason...) if additional callers
// are desired.
- private boolean mIsN1ModeAllowedByIms = true;
+ private static final Integer N1_MODE_DISALLOWED_REASON_IMS = 2;
+
+ // Set of use callers/reasons why N1 Mode is disallowed. If the set is empty, it's allowed.
+ private final Set<Integer> mN1ModeDisallowedReasons = new ArraySet<>();
+
// If this value is null, then the modem value is unknown. If a caller explicitly sets the
// N1 mode, this value will be initialized before any attempt to set the value in the modem.
private Boolean mModemN1Mode = null;
@@ -464,7 +470,7 @@
}
mCT = mTelephonyComponentFactory.inject(GsmCdmaCallTracker.class.getName())
- .makeGsmCdmaCallTracker(this);
+ .makeGsmCdmaCallTracker(this, mFeatureFlags);
mIccPhoneBookIntManager = mTelephonyComponentFactory
.inject(IccPhoneBookInterfaceManager.class.getName())
.makeIccPhoneBookInterfaceManager(this);
@@ -525,6 +531,12 @@
mCi.registerForImeiMappingChanged(this, EVENT_IMEI_MAPPING_CHANGED, null);
+ if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()
+ || mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) {
+ mSafetySource =
+ mTelephonyComponentFactory.makeCellularNetworkSecuritySafetySource(mContext);
+ }
+
if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()) {
logi(
"enable_identifier_disclosure_transparency_unsol_events is on. Registering for "
@@ -533,7 +545,7 @@
mIdentifierDisclosureNotifier =
mTelephonyComponentFactory
.inject(CellularIdentifierDisclosureNotifier.class.getName())
- .makeIdentifierDisclosureNotifier();
+ .makeIdentifierDisclosureNotifier(mSafetySource);
mCi.registerForCellularIdentifierDisclosures(
this, EVENT_CELL_IDENTIFIER_DISCLOSURE, null);
}
@@ -1486,24 +1498,6 @@
&& (isWpsCall ? allowWpsOverIms : true);
Bundle extras = dialArgs.intentExtras;
- if (extras != null && extras.containsKey(PhoneConstants.EXTRA_COMPARE_DOMAIN)) {
- int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN);
- if (!isEmergency && (!isMmiCode || isPotentialUssdCode)) {
- if ((domain == DOMAIN_PS && !useImsForCall)
- || (domain == DOMAIN_CS && useImsForCall)
- || domain == DOMAIN_UNKNOWN || domain == DOMAIN_CS_PS) {
- loge("[Anomaly] legacy-useImsForCall:" + useImsForCall
- + ", NCDS-domain:" + domain);
-
- AnomalyReporter.reportAnomaly(
- UUID.fromString("bfae6c2e-ca2f-4121-b167-9cad26a3b353"),
- "Domain selection results don't match. useImsForCall:"
- + useImsForCall + ", NCDS-domain:" + domain);
- }
- }
- extras.remove(PhoneConstants.EXTRA_COMPARE_DOMAIN);
- }
-
// Only when the domain selection service is supported, EXTRA_DIAL_DOMAIN extra shall exist.
if (extras != null && extras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN)) {
int domain = extras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN);
@@ -2426,7 +2420,11 @@
// This might be called by IMS on another thread, so to avoid the requirement to
// lock, post it through the handler.
post(() -> {
- mIsN1ModeAllowedByIms = enable;
+ if (enable) {
+ mN1ModeDisallowedReasons.remove(N1_MODE_DISALLOWED_REASON_IMS);
+ } else {
+ mN1ModeDisallowedReasons.add(N1_MODE_DISALLOWED_REASON_IMS);
+ }
if (mModemN1Mode == null) {
mCi.isN1ModeEnabled(obtainMessage(EVENT_GET_N1_MODE_ENABLED_DONE, result));
} else {
@@ -2440,7 +2438,7 @@
/** Only called on the handler thread. */
private void maybeUpdateModemN1Mode(@Nullable Message result) {
- final boolean wantN1Enabled = mIsN1ModeAllowedByCarrier && mIsN1ModeAllowedByIms;
+ final boolean wantN1Enabled = mN1ModeDisallowedReasons.isEmpty();
logd("N1 Mode: isModemN1Enabled=" + mModemN1Mode + ", wantN1Enabled=" + wantN1Enabled);
@@ -2466,8 +2464,15 @@
final int[] supportedNrModes = b.getIntArray(
CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
- mIsN1ModeAllowedByCarrier = ArrayUtils.contains(
- supportedNrModes, CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA);
+
+ if (ArrayUtils.contains(
+ supportedNrModes,
+ CarrierConfigManager.CARRIER_NR_AVAILABILITY_SA)) {
+ mN1ModeDisallowedReasons.remove(N1_MODE_DISALLOWED_REASON_CARRIER);
+ } else {
+ mN1ModeDisallowedReasons.add(N1_MODE_DISALLOWED_REASON_CARRIER);
+ }
+
if (mModemN1Mode == null) {
mCi.isN1ModeEnabled(obtainMessage(EVENT_GET_N1_MODE_ENABLED_DONE));
} else {
@@ -2602,7 +2607,7 @@
Bundle extras = new Bundle();
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
- final TelecomManager telecomManager = TelecomManager.from(mContext);
+ final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
telecomManager.placeCall(
Uri.fromParts(PhoneAccount.SCHEME_TEL, cfNumber, null), extras);
@@ -2863,7 +2868,7 @@
Bundle extras = new Bundle();
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
- final TelecomManager telecomManager = TelecomManager.from(mContext);
+ final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
telecomManager.placeCall(
Uri.fromParts(PhoneAccount.SCHEME_TEL, cwPrefix, null), extras);
@@ -3717,7 +3722,7 @@
if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()
&& mIdentifierDisclosureNotifier != null
&& disclosure != null) {
- mIdentifierDisclosureNotifier.addDisclosure(getSubId(), disclosure);
+ mIdentifierDisclosureNotifier.addDisclosure(mContext, getSubId(), disclosure);
}
break;
@@ -5002,7 +5007,7 @@
}
private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
- final TelecomManager telecomManager = TelecomManager.from(mContext);
+ final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
final TelephonyManager telephonyManager = TelephonyManager.from(mContext);
final Iterator<PhoneAccountHandle> phoneAccounts =
telecomManager.getCallCapablePhoneAccounts(true).listIterator();
@@ -5360,11 +5365,11 @@
// enable/disable API, so we only toggle the enable state if the unsol events feature
// flag is enabled.
if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()) {
- if (prefEnabled) {
- mIdentifierDisclosureNotifier.enable();
- } else {
- mIdentifierDisclosureNotifier.disable();
- }
+ if (prefEnabled) {
+ mIdentifierDisclosureNotifier.enable(mContext);
+ } else {
+ mIdentifierDisclosureNotifier.disable(mContext);
+ }
} else {
logi("Not toggling enable state for disclosure notifier. Feature flag "
+ "enable_identifier_disclosure_transparency_unsol_events is disabled");
@@ -5415,4 +5420,12 @@
public boolean isNullCipherNotificationSupported() {
return mIsNullCipherNotificationSupported;
}
+
+ @Override
+ public void refreshSafetySources(String refreshBroadcastId) {
+ if (mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()
+ || mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) {
+ mSafetySource.refresh(mContext, refreshBroadcastId);
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/NetworkIndication.java b/src/java/com/android/internal/telephony/NetworkIndication.java
index e847060..5c49234 100644
--- a/src/java/com/android/internal/telephony/NetworkIndication.java
+++ b/src/java/com/android/internal/telephony/NetworkIndication.java
@@ -45,7 +45,7 @@
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.CellularIdentifierDisclosure;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhysicalChannelConfig;
@@ -415,7 +415,7 @@
android.hardware.radio.network.EmergencyRegResult result) {
mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
- EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(result);
+ EmergencyRegistrationResult response = RILUtils.convertHalEmergencyRegResult(result);
if (mRil.isLogOrTrace()) {
mRil.unsljLogRet(RIL_UNSOL_EMERGENCY_NETWORK_SCAN_RESULT, response);
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java
index eb2cd16..b4a37b3 100644
--- a/src/java/com/android/internal/telephony/NetworkResponse.java
+++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -24,7 +24,7 @@
import android.os.AsyncResult;
import android.telephony.BarringInfo;
import android.telephony.CellInfo;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
import android.telephony.RadioAccessSpecifier;
import android.telephony.SignalStrength;
@@ -429,7 +429,7 @@
RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
if (rr != null) {
- EmergencyRegResult response = RILUtils.convertHalEmergencyRegResult(regState);
+ EmergencyRegistrationResult response = RILUtils.convertHalEmergencyRegResult(regState);
if (responseInfo.error == RadioError.NONE) {
RadioResponse.sendMessageResponse(rr.mResult, response);
}
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index ea7a6de..b9ad388 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -37,6 +37,9 @@
import android.telephony.TelephonyManager;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataCallResponse.LinkStatus;
+import android.telephony.data.EpsQos;
+import android.telephony.data.NrQos;
+import android.telephony.data.QosBearerSession;
import android.text.TextUtils;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
@@ -112,8 +115,10 @@
private static final int EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED = 11;
/** Event for device idle mode changed, when device goes to deep sleep and pauses all timers. */
private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 12;
+ /** Event for qos sessions changed. */
+ private static final int EVENT_QOS_SESSION_CHANGED = 13;
- private static final String[] sEvents = new String[EVENT_DEVICE_IDLE_MODE_CHANGED + 1];
+ private static final String[] sEvents = new String[EVENT_QOS_SESSION_CHANGED + 1];
static {
sEvents[EVENT_UPDATE] = "EVENT_UPDATE";
sEvents[EVENT_QUIT] = "EVENT_QUIT";
@@ -129,6 +134,7 @@
sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE";
sEvents[EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED";
sEvents[EVENT_DEVICE_IDLE_MODE_CHANGED] = "EVENT_DEVICE_IDLE_MODE_CHANGED";
+ sEvents[EVENT_QOS_SESSION_CHANGED] = "EVENT_QOS_SESSION_CHANGED";
}
@NonNull private final Phone mPhone;
@@ -158,6 +164,30 @@
}
};
+ @NonNull private final DataNetworkControllerCallback mDataNetworkControllerCallback =
+ new DataNetworkControllerCallback(getHandler()::post) {
+ @Override
+ public void onQosSessionsChanged(
+ @NonNull List<QosBearerSession> qosBearerSessions) {
+ if (!mIsTimerResetEnabledOnVoiceQos) return;
+ sendMessage(obtainMessage(EVENT_QOS_SESSION_CHANGED, qosBearerSessions));
+ }
+
+ @Override
+ public void onNrAdvancedCapableByPcoChanged(boolean nrAdvancedCapable) {
+ if (mNrAdvancedCapablePcoId <= 0) return;
+ log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable);
+ mIsNrAdvancedAllowedByPco = nrAdvancedCapable;
+ sendMessage(EVENT_UPDATE);
+ }
+
+ @Override
+ public void onPhysicalLinkStatusChanged(@LinkStatus int status) {
+ if (isUsingPhysicalChannelConfigForRrcDetection()) return;
+ sendMessage(obtainMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED, status));
+ }
+ };
+
@NonNull private Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
@NonNull private String mLteEnhancedPattern = "";
@Annotation.OverrideNetworkType private int mOverrideNetworkType;
@@ -165,6 +195,10 @@
private boolean mIsPrimaryTimerActive;
private boolean mIsSecondaryTimerActive;
private boolean mIsTimerResetEnabledForLegacyStateRrcIdle;
+ /** Carrier config to reset timers when mccmnc changes */
+ private boolean mIsTimerResetEnabledOnPlmnChanges;
+ /** Carrier config to reset timers when QCI(LTE) or 5QI(NR) is 1(conversational voice) */
+ private boolean mIsTimerResetEnabledOnVoiceQos;
private int mLtePlusThresholdBandwidth;
private int mNrAdvancedThresholdBandwidth;
private boolean mIncludeLteForNrAdvancedThresholdBandwidth;
@@ -172,6 +206,8 @@
@NonNull private final Set<Integer> mAdditionalNrAdvancedBands = new HashSet<>();
@NonNull private String mPrimaryTimerState;
@NonNull private String mSecondaryTimerState;
+ // TODO(b/316425811 remove the workaround)
+ private int mNrAdvancedBandsSecondaryTimer;
@NonNull private String mPreviousState;
@LinkStatus private int mPhysicalLinkStatus;
private boolean mIsPhysicalChannelConfig16Supported;
@@ -182,15 +218,14 @@
private boolean mIsDeviceIdleMode = false;
private boolean mPrimaryCellChangedWhileIdle = false;
- @Nullable private DataNetworkControllerCallback mNrAdvancedCapableByPcoChangedCallback = null;
- @Nullable private DataNetworkControllerCallback mNrPhysicalLinkStatusChangedCallback = null;
-
// Cached copies below to prevent race conditions
@NonNull private ServiceState mServiceState;
@Nullable private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
// Ratchet physical channel config fields to prevent 5G/5G+ flickering
@NonNull private Set<Integer> mRatchetedNrBands = new HashSet<>();
+ // TODO(b/316425811 remove the workaround)
+ private boolean mLastShownNrDueToAdvancedBand = false;
private int mRatchetedNrBandwidths = 0;
private int mLastAnchorNrCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
private boolean mDoesPccListIndicateIdle = false;
@@ -271,6 +306,8 @@
TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
mPhone.getDeviceStateMonitor().registerForPhysicalChannelConfigNotifChanged(getHandler(),
EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED, null);
+ mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
+ mDataNetworkControllerCallback);
IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
@@ -283,6 +320,8 @@
mPhone.unregisterForPreferredNetworkTypeChanged(getHandler());
mPhone.getServiceStateTracker().unregisterForServiceStateChanged(getHandler());
mPhone.getDeviceStateMonitor().unregisterForPhysicalChannelConfigNotifChanged(getHandler());
+ mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
+ mDataNetworkControllerCallback);
mPhone.getContext().unregisterReceiver(mIntentReceiver);
CarrierConfigManager ccm = mPhone.getContext().getSystemService(CarrierConfigManager.class);
if (mCarrierConfigChangeListener != null) {
@@ -304,6 +343,10 @@
CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
mIsTimerResetEnabledForLegacyStateRrcIdle = config.getBoolean(
CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
+ mIsTimerResetEnabledOnPlmnChanges = config.getBoolean(
+ CarrierConfigManager.KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL);
+ mIsTimerResetEnabledOnVoiceQos = config.getBoolean(
+ CarrierConfigManager.KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL);
mLtePlusThresholdBandwidth = config.getInt(
CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT);
mNrAdvancedThresholdBandwidth = config.getInt(
@@ -322,43 +365,10 @@
}
mNrAdvancedCapablePcoId = config.getInt(
CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
- if (mNrAdvancedCapablePcoId > 0 && mNrAdvancedCapableByPcoChangedCallback == null) {
- mNrAdvancedCapableByPcoChangedCallback =
- new DataNetworkControllerCallback(getHandler()::post) {
- @Override
- public void onNrAdvancedCapableByPcoChanged(boolean nrAdvancedCapable) {
- log("mIsNrAdvancedAllowedByPco=" + nrAdvancedCapable);
- mIsNrAdvancedAllowedByPco = nrAdvancedCapable;
- sendMessage(EVENT_UPDATE);
- }
- };
- mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
- mNrAdvancedCapableByPcoChangedCallback);
- } else if (mNrAdvancedCapablePcoId == 0 && mNrAdvancedCapableByPcoChangedCallback != null) {
- mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
- mNrAdvancedCapableByPcoChangedCallback);
- mNrAdvancedCapableByPcoChangedCallback = null;
- }
mIsUsingUserDataForRrcDetection = config.getBoolean(
CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
- if (!isUsingPhysicalChannelConfigForRrcDetection()) {
- if (mNrPhysicalLinkStatusChangedCallback == null) {
- mNrPhysicalLinkStatusChangedCallback =
- new DataNetworkControllerCallback(getHandler()::post) {
- @Override
- public void onPhysicalLinkStatusChanged(@LinkStatus int status) {
- sendMessage(obtainMessage(EVENT_PHYSICAL_LINK_STATUS_CHANGED,
- new AsyncResult(null, status, null)));
- }
- };
- mPhone.getDataNetworkController().registerDataNetworkControllerCallback(
- mNrPhysicalLinkStatusChangedCallback);
- }
- } else if (mNrPhysicalLinkStatusChangedCallback != null) {
- mPhone.getDataNetworkController().unregisterDataNetworkControllerCallback(
- mNrPhysicalLinkStatusChangedCallback);
- mNrPhysicalLinkStatusChangedCallback = null;
- }
+ mNrAdvancedBandsSecondaryTimer = config.getInt(
+ CarrierConfigManager.KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT);
String nrIconConfiguration = config.getString(
CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING);
String overrideTimerRule = config.getString(
@@ -366,7 +376,8 @@
String overrideSecondaryTimerRule = config.getString(
CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
- updatePhysicalChannelConfigs();
+ updatePhysicalChannelConfigs(
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList());
}
private void createTimerRules(String icons, String timers, String secondaryTimers) {
@@ -598,6 +609,7 @@
private final class DefaultState extends State {
@Override
public boolean processMessage(Message msg) {
+ AsyncResult ar;
if (DBG) log("DefaultState: process " + getEventName(msg.what));
switch (msg.what) {
case EVENT_UPDATE:
@@ -618,17 +630,15 @@
parseCarrierConfigs();
break;
case EVENT_SERVICE_STATE_CHANGED:
- mServiceState = mPhone.getServiceStateTracker().getServiceState();
- if (DBG) log("ServiceState updated: " + mServiceState);
+ onServiceStateChanged();
transitionToCurrentState();
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
- AsyncResult ar = (AsyncResult) msg.obj;
- mPhysicalLinkStatus = (int) ar.result;
+ mPhysicalLinkStatus = msg.arg1;
break;
case EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED:
- AsyncResult result = (AsyncResult) msg.obj;
- mIsPhysicalChannelConfigOn = (boolean) result.result;
+ ar = (AsyncResult) msg.obj;
+ mIsPhysicalChannelConfigOn = (boolean) ar.result;
if (DBG) {
log("mIsPhysicalChannelConfigOn changed to: " + mIsPhysicalChannelConfigOn);
}
@@ -655,11 +665,17 @@
mIsSecondaryTimerActive = false;
mSecondaryTimerState = "";
updateTimers();
+ mLastShownNrDueToAdvancedBand = false;
updateOverrideNetworkType();
break;
case EVENT_RADIO_OFF_OR_UNAVAILABLE:
if (DBG) log("Reset timers since radio is off or unavailable.");
resetAllTimers();
+ mRatchetedNrBands.clear();
+ mRatchetedNrBandwidths = 0;
+ mLastAnchorNrCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
+ mDoesPccListIndicateIdle = false;
+ mPhysicalChannelConfigs = null;
transitionTo(mLegacyState);
break;
case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
@@ -668,7 +684,8 @@
transitionToCurrentState();
break;
case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
- updatePhysicalChannelConfigs();
+ ar = (AsyncResult) msg.obj;
+ updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
@@ -686,6 +703,24 @@
}
transitionToCurrentState();
break;
+ case EVENT_QOS_SESSION_CHANGED:
+ List<QosBearerSession> qosBearerSessions = (List<QosBearerSession>) msg.obj;
+ boolean inVoiceCall = 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();
+ break;
+ }
+ }
+ break;
default:
throw new RuntimeException("Received invalid event: " + msg.what);
}
@@ -720,10 +755,10 @@
public boolean processMessage(Message msg) {
if (DBG) log("LegacyState: process " + getEventName(msg.what));
updateTimers();
+ AsyncResult ar;
switch (msg.what) {
case EVENT_SERVICE_STATE_CHANGED:
- mServiceState = mPhone.getServiceStateTracker().getServiceState();
- if (DBG) log("ServiceState updated: " + mServiceState);
+ onServiceStateChanged();
// fallthrough
case EVENT_UPDATE:
int rat = getDataNetworkType();
@@ -748,18 +783,19 @@
mIsNrRestricted = isNrRestricted();
break;
case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
- updatePhysicalChannelConfigs();
+ ar = (AsyncResult) msg.obj;
+ updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
if (DBG) log("Reset timers since timer reset is enabled for RRC idle.");
resetAllTimers();
+ updateOverrideNetworkType();
}
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
- AsyncResult ar = (AsyncResult) msg.obj;
- mPhysicalLinkStatus = (int) ar.result;
+ mPhysicalLinkStatus = msg.arg1;
if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
if (DBG) log("Reset timers since timer reset is enabled for RRC idle.");
resetAllTimers();
@@ -801,10 +837,10 @@
public boolean processMessage(Message msg) {
if (DBG) log("IdleState: process " + getEventName(msg.what));
updateTimers();
+ AsyncResult ar;
switch (msg.what) {
case EVENT_SERVICE_STATE_CHANGED:
- mServiceState = mPhone.getServiceStateTracker().getServiceState();
- if (DBG) log("ServiceState updated: " + mServiceState);
+ onServiceStateChanged();
// fallthrough
case EVENT_UPDATE:
int rat = getDataNetworkType();
@@ -829,7 +865,8 @@
}
break;
case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
- updatePhysicalChannelConfigs();
+ ar = (AsyncResult) msg.obj;
+ updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
if (isPhysicalLinkActive()) {
@@ -841,8 +878,7 @@
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
- AsyncResult ar = (AsyncResult) msg.obj;
- mPhysicalLinkStatus = (int) ar.result;
+ mPhysicalLinkStatus = msg.arg1;
if (isPhysicalLinkActive()) {
transitionWithTimerTo(mLteConnectedState);
} else {
@@ -885,10 +921,10 @@
public boolean processMessage(Message msg) {
if (DBG) log("LteConnectedState: process " + getEventName(msg.what));
updateTimers();
+ AsyncResult ar;
switch (msg.what) {
case EVENT_SERVICE_STATE_CHANGED:
- mServiceState = mPhone.getServiceStateTracker().getServiceState();
- if (DBG) log("ServiceState updated: " + mServiceState);
+ onServiceStateChanged();
// fallthrough
case EVENT_UPDATE:
int rat = getDataNetworkType();
@@ -913,7 +949,8 @@
}
break;
case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
- updatePhysicalChannelConfigs();
+ ar = (AsyncResult) msg.obj;
+ updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
if (!isPhysicalLinkActive()) {
@@ -925,8 +962,7 @@
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
- AsyncResult ar = (AsyncResult) msg.obj;
- mPhysicalLinkStatus = (int) ar.result;
+ mPhysicalLinkStatus = msg.arg1;
if (!isPhysicalLinkActive()) {
transitionWithTimerTo(mIdleState);
} else {
@@ -969,10 +1005,10 @@
public boolean processMessage(Message msg) {
if (DBG) log("NrIdleState: process " + getEventName(msg.what));
updateTimers();
+ AsyncResult ar;
switch (msg.what) {
case EVENT_SERVICE_STATE_CHANGED:
- mServiceState = mPhone.getServiceStateTracker().getServiceState();
- if (DBG) log("ServiceState updated: " + mServiceState);
+ onServiceStateChanged();
// fallthrough
case EVENT_UPDATE:
int rat = getDataNetworkType();
@@ -994,7 +1030,8 @@
}
break;
case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
- updatePhysicalChannelConfigs();
+ ar = (AsyncResult) msg.obj;
+ updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
@@ -1006,8 +1043,7 @@
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
- AsyncResult ar = (AsyncResult) msg.obj;
- mPhysicalLinkStatus = (int) ar.result;
+ mPhysicalLinkStatus = msg.arg1;
if (isPhysicalLinkActive()) {
transitionWithTimerTo(mNrConnectedState);
}
@@ -1047,10 +1083,10 @@
public boolean processMessage(Message msg) {
if (DBG) log("NrConnectedState: process " + getEventName(msg.what));
updateTimers();
+ AsyncResult ar;
switch (msg.what) {
case EVENT_SERVICE_STATE_CHANGED:
- mServiceState = mPhone.getServiceStateTracker().getServiceState();
- if (DBG) log("ServiceState updated: " + mServiceState);
+ onServiceStateChanged();
// fallthrough
case EVENT_UPDATE:
int rat = getDataNetworkType();
@@ -1072,7 +1108,8 @@
}
break;
case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
- updatePhysicalChannelConfigs();
+ ar = (AsyncResult) msg.obj;
+ updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
@@ -1084,8 +1121,7 @@
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
- AsyncResult ar = (AsyncResult) msg.obj;
- mPhysicalLinkStatus = (int) ar.result;
+ mPhysicalLinkStatus = msg.arg1;
if (!isPhysicalLinkActive() && mFeatureFlags.supportNrSaRrcIdle()) {
transitionWithTimerTo(mNrIdleState);
}
@@ -1124,12 +1160,16 @@
@Override
public boolean processMessage(Message msg) {
- if (DBG) log("NrConnectedAdvancedState: process " + getEventName(msg.what));
+ mLastShownNrDueToAdvancedBand = isAdditionalNrAdvancedBand(mRatchetedNrBands);
+ if (DBG) {
+ log("NrConnectedAdvancedState: process " + getEventName(msg.what)
+ + ", been using advanced band is " + mLastShownNrDueToAdvancedBand);
+ }
updateTimers();
+ AsyncResult ar;
switch (msg.what) {
case EVENT_SERVICE_STATE_CHANGED:
- mServiceState = mPhone.getServiceStateTracker().getServiceState();
- if (DBG) log("ServiceState updated: " + mServiceState);
+ onServiceStateChanged();
// fallthrough
case EVENT_UPDATE:
int rat = getDataNetworkType();
@@ -1158,7 +1198,8 @@
}
break;
case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
- updatePhysicalChannelConfigs();
+ ar = (AsyncResult) msg.obj;
+ updatePhysicalChannelConfigs((List<PhysicalChannelConfig>) ar.result);
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
@@ -1170,8 +1211,7 @@
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
- AsyncResult ar = (AsyncResult) msg.obj;
- mPhysicalLinkStatus = (int) ar.result;
+ mPhysicalLinkStatus = msg.arg1;
break;
default:
return NOT_HANDLED;
@@ -1191,9 +1231,19 @@
private final NrConnectedAdvancedState mNrConnectedAdvancedState =
new NrConnectedAdvancedState();
- private void updatePhysicalChannelConfigs() {
- List<PhysicalChannelConfig> physicalChannelConfigs =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ /** On service state changed. */
+ private void onServiceStateChanged() {
+ ServiceState ss = mPhone.getServiceStateTracker().getServiceState();
+ if (mIsTimerResetEnabledOnPlmnChanges
+ && !TextUtils.equals(mServiceState.getOperatorNumeric(), ss.getOperatorNumeric())) {
+ log("Reset any timers due to nr_timers_reset_on_plmn_change_bool");
+ resetAllTimers();
+ }
+ mServiceState = ss;
+ if (DBG) log("ServiceState updated: " + mServiceState);
+ }
+
+ private void updatePhysicalChannelConfigs(List<PhysicalChannelConfig> physicalChannelConfigs) {
boolean isPccListEmpty = physicalChannelConfigs == null || physicalChannelConfigs.isEmpty();
if (isPccListEmpty && isUsingPhysicalChannelConfigForRrcDetection()) {
log("Physical channel configs updated: not updating PCC fields for empty PCC list "
@@ -1249,7 +1299,7 @@
} else {
if (mFeatureFlags.supportNrSaRrcIdle() && mDoesPccListIndicateIdle
&& isUsingPhysicalChannelConfigForRrcDetection()
- && !mPrimaryCellChangedWhileIdle && isTimerActiveForNrSaRrcIdle()
+ && !mPrimaryCellChangedWhileIdle && isTimerActiveForRrcIdle()
&& !isNrAdvancedForPccFields(nrBandwidths, nrBands)) {
log("Allow primary cell change during RRC idle timer without changing state: "
+ mLastAnchorNrCellId + " -> " + anchorNrCellId);
@@ -1299,6 +1349,10 @@
}
if (!mIsDeviceIdleMode && rule != null && rule.getSecondaryTimer(currentName) > 0) {
int duration = rule.getSecondaryTimer(currentName);
+ if (mLastShownNrDueToAdvancedBand && mNrAdvancedBandsSecondaryTimer > 0) {
+ duration = mNrAdvancedBandsSecondaryTimer;
+ if (DBG) log("timer adjusted by nr_advanced_bands_secondary_timer_seconds_int");
+ }
if (DBG) log(duration + "s secondary timer started for state: " + currentName);
mSecondaryTimerState = currentName;
mPreviousState = currentName;
@@ -1349,7 +1403,9 @@
String currentState = getCurrentState().getName();
- if (mIsPrimaryTimerActive && getOverrideNetworkType() == getCurrentOverrideNetworkType()) {
+ if (mIsPrimaryTimerActive && getOverrideNetworkType() == getCurrentOverrideNetworkType()
+ && getDataNetworkType()
+ == mDisplayInfoController.getTelephonyDisplayInfo().getNetworkType()) {
// remove primary timer if device goes back to the original icon
if (DBG) {
log("Remove primary timer since icon of primary state and current icon equal: "
@@ -1401,13 +1457,17 @@
mIsSecondaryTimerActive = false;
mPrimaryTimerState = "";
mSecondaryTimerState = "";
+
+ mLastShownNrDueToAdvancedBand = false;
}
- private boolean isTimerActiveForNrSaRrcIdle() {
+ private boolean isTimerActiveForRrcIdle() {
if (mIsPrimaryTimerActive) {
- return mPrimaryTimerState.equals(STATE_CONNECTED_RRC_IDLE);
+ return mPrimaryTimerState.equals(STATE_CONNECTED_RRC_IDLE)
+ || mPrimaryTimerState.equals(STATE_NOT_RESTRICTED_RRC_IDLE);
} else if (mIsSecondaryTimerActive) {
- return mSecondaryTimerState.equals(STATE_CONNECTED_RRC_IDLE);
+ return mSecondaryTimerState.equals(STATE_CONNECTED_RRC_IDLE)
+ || mSecondaryTimerState.equals(STATE_NOT_RESTRICTED_RRC_IDLE);
} else {
return false;
}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 3b47670..97eb447 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -5214,6 +5214,12 @@
}
/**
+ * Refresh the safety sources in response to the identified broadcast.
+ */
+ public void refreshSafetySources(String refreshBroadcastId) {
+ }
+
+ /**
* Notifies the IMS call status to the modem.
*
* @param imsCallInfo The list of {@link ImsCallInfo}.
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 1e0aa3a..7141f37 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -34,6 +34,7 @@
import android.telephony.PhoneCapability;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyRegistryManager;
import android.text.TextUtils;
import android.util.Log;
@@ -46,7 +47,10 @@
import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Optional;
import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* This class manages phone's configuration which defines the potential capability (static) of the
@@ -74,7 +78,10 @@
private static PhoneConfigurationManager sInstance = null;
private final Context mContext;
private PhoneCapability mStaticCapability;
- private Set<Integer> mSlotsSupportingSimultaneousCellularCalls = new HashSet<>();
+ private final Set<Integer> mSlotsSupportingSimultaneousCellularCalls = new HashSet<>(3);
+ private final Set<Integer> mSubIdsSupportingSimultaneousCellularCalls = new HashSet<>(3);
+ private final HashSet<Consumer<Set<Integer>>> mSimultaneousCellularCallingListeners =
+ new HashSet<>(1);
private final RadioConfig mRadioConfig;
private final Handler mHandler;
// mPhones is obtained from PhoneFactory and can have phones corresponding to inactive modems as
@@ -148,6 +155,25 @@
}
}
+ /**
+ * Updates the mapping between the slot IDs that support simultaneous calling and the
+ * associated sub IDs as well as notifies listeners.
+ */
+ private void updateSimultaneousSubIdsFromPhoneIdMappingAndNotify() {
+ if (!mFeatureFlags.simultaneousCallingIndications()) return;
+ Set<Integer> slotCandidates = mSlotsSupportingSimultaneousCellularCalls.stream()
+ .map(i -> mPhones[i].getSubId())
+ .filter(i ->i > SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ .collect(Collectors.toSet());
+ if (mSubIdsSupportingSimultaneousCellularCalls.equals(slotCandidates)) return;
+ log("updateSimultaneousSubIdsFromPhoneIdMapping update: "
+ + mSubIdsSupportingSimultaneousCellularCalls + " -> " + slotCandidates);
+ mSubIdsSupportingSimultaneousCellularCalls.clear();
+ mSubIdsSupportingSimultaneousCellularCalls.addAll(slotCandidates);
+ mNotifier.notifySimultaneousCellularCallingSubscriptionsChanged(
+ mSubIdsSupportingSimultaneousCellularCalls);
+ }
+
private void registerForRadioState(Phone phone) {
phone.mCi.registerForAvailable(mHandler, Phone.EVENT_RADIO_AVAILABLE, phone);
}
@@ -163,12 +189,16 @@
/**
* If virtual DSDA is enabled for this UE, then increase maxActiveVoiceSubscriptions to 2.
*/
- private PhoneCapability maybeUpdateMaxActiveVoiceSubscriptions(
+ private PhoneCapability maybeOverrideMaxActiveVoiceSubscriptions(
final PhoneCapability staticCapability) {
- if (staticCapability.getLogicalModemList().size() > 1 && mVirtualDsdaEnabled) {
+ boolean isVDsdaEnabled = staticCapability.getLogicalModemList().size() > 1
+ && mVirtualDsdaEnabled;
+ boolean isBkwdCompatDsdaEnabled = mFeatureFlags.simultaneousCallingIndications()
+ && mMi.getMultiSimProperty().orElse(SSSS).equals(DSDA);
+ if (isVDsdaEnabled || isBkwdCompatDsdaEnabled) {
// Since we already initialized maxActiveVoiceSubscriptions to the count the
- // modem is capable of, vDSDA is only able to increase that count via this method. We do
- // not allow vDSDA to decrease maxActiveVoiceSubscriptions:
+ // modem is capable of, we are only able to increase that count via this method. We do
+ // not allow a decrease of maxActiveVoiceSubscriptions:
int updatedMaxActiveVoiceSubscriptions =
Math.max(staticCapability.getMaxActiveVoiceSubscriptions(), 2);
return new PhoneCapability.Builder(staticCapability)
@@ -180,13 +210,39 @@
}
private void maybeEnableCellularDSDASupport() {
- if (mRadioConfig != null && mRadioConfig.getRadioConfigProxy(null)
- .getVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_2_2) &&
- getPhoneCount() > 1 &&
- mStaticCapability.getMaxActiveVoiceSubscriptions() > 1) {
+ boolean bkwdsCompatDsda = mFeatureFlags.simultaneousCallingIndications()
+ && getPhoneCount() > 1
+ && mMi.getMultiSimProperty().orElse(SSSS).equals(DSDA);
+ boolean halSupportSimulCalling = mRadioConfig != null
+ && mRadioConfig.getRadioConfigProxy(null).getVersion().greaterOrEqual(
+ RIL.RADIO_HAL_VERSION_2_2)
+ && getPhoneCount() > 1 && mStaticCapability.getMaxActiveVoiceSubscriptions() > 1;
+ // Register for simultaneous calling support changes in the modem if the HAL supports it
+ if (halSupportSimulCalling) {
updateSimultaneousCallingSupport();
mRadioConfig.registerForSimultaneousCallingSupportStatusChanged(mHandler,
EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED, null);
+ } else if (bkwdsCompatDsda) {
+ // For older devices that only declare that they support DSDA via modem config,
+ // set DSDA as capable now statically.
+ log("DSDA modem config detected - setting DSDA enabled");
+ for (Phone p : mPhones) {
+ mSlotsSupportingSimultaneousCellularCalls.add(p.getPhoneId());
+ }
+ updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
+ notifySimultaneousCellularCallingSlotsChanged();
+ }
+ // Register for subId updates to notify listeners when simultaneous calling is configured
+ if (mFeatureFlags.simultaneousCallingIndications()
+ && (bkwdsCompatDsda || halSupportSimulCalling)) {
+ mContext.getSystemService(TelephonyRegistryManager.class)
+ .addOnSubscriptionsChangedListener(
+ new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
+ }
+ }, mHandler::post);
}
}
@@ -291,7 +347,6 @@
if (mSlotsSupportingSimultaneousCellularCalls.size() > getPhoneCount()) {
loge("Invalid size of DSDA slots. Disabling cellular DSDA.");
mSlotsSupportingSimultaneousCellularCalls.clear();
- break;
}
} else {
log(msg.what + " failure. Not getting logical slots that support "
@@ -299,8 +354,8 @@
mSlotsSupportingSimultaneousCellularCalls.clear();
}
if (mFeatureFlags.simultaneousCallingIndications()) {
- mNotifier.notifySimultaneousCellularCallingSubscriptionsChanged(
- mSlotsSupportingSimultaneousCellularCalls);
+ updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
+ notifySimultaneousCellularCallingSlotsChanged();
}
break;
default:
@@ -439,7 +494,7 @@
mHandler, EVENT_GET_PHONE_CAPABILITY_DONE);
mRadioConfig.getPhoneCapability(callback);
}
- mStaticCapability = maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability);
+ mStaticCapability = maybeOverrideMaxActiveVoiceSubscriptions(mStaticCapability);
log("getStaticPhoneCapability: mStaticCapability " + mStaticCapability);
return mStaticCapability;
}
@@ -455,8 +510,31 @@
return mStaticCapability.getMaxActiveDataSubscriptions();
}
+ /**
+ * Register to listen to changes in the Phone slots that support simultaneous calling.
+ * @param consumer A consumer that will be used to consume the new slots supporting simultaneous
+ * cellular calling when it changes.
+ */
+ public void registerForSimultaneousCellularCallingSlotsChanged(
+ Consumer<Set<Integer>> consumer) {
+ mSimultaneousCellularCallingListeners.add(consumer);
+ }
+
+ private void notifySimultaneousCellularCallingSlotsChanged() {
+ log("notifying listeners of changes to simultaneous cellular calling - new state:"
+ + mSlotsSupportingSimultaneousCellularCalls);
+ for (Consumer<Set<Integer>> consumer : mSimultaneousCellularCallingListeners) {
+ try {
+ consumer.accept(new HashSet<>(mSlotsSupportingSimultaneousCellularCalls));
+ } catch (Exception e) {
+ log("Unexpected Exception encountered when notifying listener: " + e);
+ }
+ }
+ }
+
private void notifyCapabilityChanged() {
- mNotifier.notifyPhoneCapabilityChanged(mStaticCapability);
+ mNotifier.notifyPhoneCapabilityChanged(maybeOverrideMaxActiveVoiceSubscriptions(
+ mStaticCapability));
}
/**
@@ -540,6 +618,10 @@
} else {
// The number of active modems is 0 or 1, disable cellular DSDA:
mSlotsSupportingSimultaneousCellularCalls.clear();
+ if (mFeatureFlags.simultaneousCallingIndications()) {
+ updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
+ notifySimultaneousCellularCallingSlotsChanged();
+ }
}
// When the user enables DSDS mode, the default VOICE and SMS subId should be switched
@@ -717,6 +799,13 @@
Context context, int numOfActiveModems) {
PhoneFactory.onMultiSimConfigChanged(context, numOfActiveModems);
}
+
+ /**
+ * Wrapper function to query the sysprop for multi_sim_config
+ */
+ public Optional<String> getMultiSimProperty() {
+ return TelephonyProperties.multi_sim_config();
+ }
}
private static void log(String s) {
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index 0617656..8897db4 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -305,10 +305,10 @@
import android.net.LinkProperties;
import android.os.SystemClock;
import android.service.carrier.CarrierIdentifier;
-import android.telephony.CarrierInfo;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
import android.telephony.BarringInfo;
+import android.telephony.CarrierInfo;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellConfigLte;
import android.telephony.CellIdentity;
@@ -335,7 +335,7 @@
import android.telephony.CellularIdentifierDisclosure;
import android.telephony.ClosedSubscriberGroupInfo;
import android.telephony.DomainSelectionService;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
import android.telephony.LinkCapacityEstimate;
import android.telephony.ModemInfo;
import android.telephony.NetworkRegistrationInfo;
@@ -4546,13 +4546,13 @@
}
/**
- * Convert EmergencyRegResult.aidl to EmergencyRegResult.
+ * Convert EmergencyRegResult.aidl to EmergencyRegistrationResult.
* @param halResult EmergencyRegResult.aidl in HAL.
- * @return Converted EmergencyRegResult.
+ * @return Converted EmergencyRegistrationResult.
*/
- public static EmergencyRegResult convertHalEmergencyRegResult(
+ public static EmergencyRegistrationResult convertHalEmergencyRegResult(
android.hardware.radio.network.EmergencyRegResult halResult) {
- return new EmergencyRegResult(
+ return new EmergencyRegistrationResult(
halResult.accessNetwork,
convertHalRegState(halResult.regState),
halResult.emcDomain,
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 54c27c5..04f5c08 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -2544,7 +2544,13 @@
/** Return if the SMS was originated from the default SMS application. */
public boolean isFromDefaultSmsApplication(Context context) {
if (mIsFromDefaultSmsApplication == null) {
- UserHandle userHandle = TelephonyUtils.getSubscriptionUserHandle(context, mSubId);
+ UserHandle userHandle;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ userHandle = TelephonyUtils.getSubscriptionUserHandle(context, mSubId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
// Perform a lazy initialization, due to the cost of the operation.
mIsFromDefaultSmsApplication = SmsApplication.isDefaultSmsApplicationAsUser(context,
getAppPackageName(), userHandle);
diff --git a/src/java/com/android/internal/telephony/SimResponse.java b/src/java/com/android/internal/telephony/SimResponse.java
index 164ec7d..59defc3 100644
--- a/src/java/com/android/internal/telephony/SimResponse.java
+++ b/src/java/com/android/internal/telephony/SimResponse.java
@@ -112,6 +112,8 @@
android.hardware.radio.sim.CarrierRestrictions carrierRestrictions,
int multiSimPolicy) {
RILRequest rr = mRil.processResponse(HAL_SERVICE_SIM, responseInfo);
+ boolean carrierLockInfoSupported = mRil.getHalVersion(HAL_SERVICE_SIM).greater(
+ RIL.RADIO_HAL_VERSION_2_2);
if (rr == null) {
return;
}
@@ -132,7 +134,8 @@
RILUtils.convertAidlCarrierInfoList(
carrierRestrictions.allowedCarrierInfoList)).setExcludedCarrierInfo(
RILUtils.convertAidlCarrierInfoList(
- carrierRestrictions.excludedCarrierInfoList)).build();
+ carrierRestrictions.excludedCarrierInfoList)).setCarrierLockInfoFeature(
+ carrierLockInfoSupported).build();
if (responseInfo.error == RadioError.NONE) {
RadioResponse.sendMessageResponse(rr.mResult, ret);
}
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 8b41f6e..f5aa074 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -48,6 +48,7 @@
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.nitz.NitzStateMachineImpl;
import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
+import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource;
import com.android.internal.telephony.security.NullCipherNotifier;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.UiccCard;
@@ -278,8 +279,14 @@
return sInstance;
}
- public GsmCdmaCallTracker makeGsmCdmaCallTracker(GsmCdmaPhone phone) {
- return new GsmCdmaCallTracker(phone);
+ /**
+ * Create a new GsmCdmaCallTracker
+ * @param phone GsmCdmaPhone
+ * @param featureFlags Telephony feature flag
+ */
+ public GsmCdmaCallTracker makeGsmCdmaCallTracker(GsmCdmaPhone phone,
+ @NonNull FeatureFlags featureFlags) {
+ return new GsmCdmaCallTracker(phone, featureFlags);
}
public SmsStorageMonitor makeSmsStorageMonitor(Phone phone) {
@@ -569,9 +576,16 @@
return new DataSettingsManager(phone, dataNetworkController, looper, callback);
}
+ /** Create CellularNetworkSecuritySafetySource. */
+ public CellularNetworkSecuritySafetySource makeCellularNetworkSecuritySafetySource(
+ Context context) {
+ return CellularNetworkSecuritySafetySource.getInstance(context);
+ }
+
/** Create CellularIdentifierDisclosureNotifier. */
- public CellularIdentifierDisclosureNotifier makeIdentifierDisclosureNotifier() {
- return CellularIdentifierDisclosureNotifier.getInstance();
+ public CellularIdentifierDisclosureNotifier makeIdentifierDisclosureNotifier(
+ CellularNetworkSecuritySafetySource safetySource) {
+ return CellularIdentifierDisclosureNotifier.getInstance(safetySource);
}
/** Create NullCipherNotifier. */
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
index 8cffe72..02c459a 100644
--- a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
+++ b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
@@ -23,6 +23,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -34,6 +35,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemClock;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
@@ -41,7 +43,6 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
@@ -61,7 +62,10 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -107,7 +111,7 @@
/** Event for signal strength changed. */
private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 4;
/** Event indicates the switch state is stable, proceed to validation as the next step. */
- private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 5;
+ private static final int EVENT_STABILITY_CHECK_PASSED = 5;
/** Event when subscriptions changed. */
private static final int EVENT_SUBSCRIPTIONS_CHANGED = 6;
@@ -126,22 +130,54 @@
/** Notification ID **/
private static final int AUTO_DATA_SWITCH_NOTIFICATION_ID = 1;
+ /**
+ * The threshold of long timer, longer than or equal to which we use alarm manager to schedule
+ * instead of handler.
+ */
+ private static final long RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS = TimeUnit
+ .MINUTES.toMillis(1);
+
private final @NonNull LocalLog mLocalLog = new LocalLog(128);
private final @NonNull Context mContext;
private static @NonNull FeatureFlags sFeatureFlags = new FeatureFlagsImpl();
private final @NonNull SubscriptionManagerService mSubscriptionManagerService;
private final @NonNull PhoneSwitcher mPhoneSwitcher;
private final @NonNull AutoDataSwitchControllerCallback mPhoneSwitcherCallback;
+ private final @NonNull AlarmManager mAlarmManager;
+ /** A map of a scheduled event to its associated extra for action when the event fires off. */
+ private final @NonNull Map<Integer, Object> mScheduledEventsToExtras;
+ /** A map of an event to its associated alarm listener callback for when the event fires off. */
+ private final @NonNull Map<Integer, AlarmManager.OnAlarmListener> mEventsToAlarmListener;
+ /**
+ * Event extras for checking environment stability.
+ * @param targetPhoneId The target phone Id to switch to when the stability check pass.
+ * @param isForPerformance Whether the switch is due to RAT/signal strength performance.
+ * @param needValidation Whether ping test needs to pass.
+ */
+ private record StabilityEventExtra(int targetPhoneId, boolean isForPerformance,
+ boolean needValidation) {}
+
+ /**
+ * Event extras for evaluating switch environment.
+ * @param evaluateReason The reason that triggers the evaluation.
+ */
+ private record EvaluateEventExtra(@AutoDataSwitchEvaluationReason int evaluateReason) {}
private boolean mDefaultNetworkIsOnNonCellular = false;
/** {@code true} if we've displayed the notification the first time auto switch occurs **/
private boolean mDisplayedNotification = false;
/**
- * Time threshold in ms to define a internet connection status to be stable(e.g. out of service,
- * in service, wifi is the default active network.etc), while -1 indicates auto switch
- * feature disabled.
+ * Configurable time threshold in ms to define an internet connection status to be stable(e.g.
+ * out of service, in service, wifi is the default active network.etc), while -1 indicates auto
+ * switch feature disabled.
*/
private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1;
/**
+ * Configurable time threshold in ms to define an internet connection performance status to be
+ * stable (e.g. LTE + 4 signal strength, UMTS + 2 signal strength), while -1 indicates
+ * auto switch feature based on RAT/SS is disabled.
+ */
+ private long mAutoDataSwitchPerformanceStabilityTimeThreshold = -1;
+ /**
* The tolerated gap of score for auto data switch decision, larger than which the device will
* switch to the SIM with higher score. If 0, the device will always switch to the higher score
* SIM. If < 0, the network type and signal strength based auto switch is disabled.
@@ -250,8 +286,8 @@
return "{phone " + mPhone.getPhoneId()
+ " score=" + getRatSignalScore() + " dataRegState="
+ NetworkRegistrationInfo.registrationStateToString(mDataRegState)
- + " " + getUsableState()
- + " display=" + mDisplayInfo + " signalStrength=" + mSignalStrength.getLevel()
+ + " " + getUsableState() + " " + mDisplayInfo
+ + " signalStrength=" + mSignalStrength.getLevel()
+ " listeningForEvents=" + mListeningForEvents
+ "}";
@@ -296,9 +332,12 @@
super(looper);
mContext = context;
sFeatureFlags = featureFlags;
+ mPhoneSwitcherCallback = phoneSwitcherCallback;
+ mAlarmManager = context.getSystemService(AlarmManager.class);
+ mScheduledEventsToExtras = new HashMap<>();
+ mEventsToAlarmListener = new HashMap<>();
mSubscriptionManagerService = SubscriptionManagerService.getInstance();
mPhoneSwitcher = phoneSwitcher;
- mPhoneSwitcherCallback = phoneSwitcherCallback;
readDeviceResourceConfig();
int numActiveModems = PhoneFactory.getPhones().length;
mPhonesSignalStatus = new PhoneSignalStatus[numActiveModems];
@@ -407,6 +446,8 @@
mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired();
mAutoDataSwitchAvailabilityStabilityTimeThreshold =
dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
+ mAutoDataSwitchPerformanceStabilityTimeThreshold =
+ dataConfig.getAutoDataSwitchPerformanceStabilityTimeThreshold();
mAutoDataSwitchValidationMaxRetry =
dataConfig.getAutoDataSwitchValidationMaxRetry();
}
@@ -432,15 +473,35 @@
onSignalStrengthChanged(phoneId);
break;
case EVENT_EVALUATE_AUTO_SWITCH:
- int reason = (int) msg.obj;
- onEvaluateAutoDataSwitch(reason);
+ if (sFeatureFlags.autoDataSwitchRatSs()) {
+ Object obj = mScheduledEventsToExtras.get(EVENT_EVALUATE_AUTO_SWITCH);
+ if (obj instanceof EvaluateEventExtra extra) {
+ mScheduledEventsToExtras.remove(EVENT_EVALUATE_AUTO_SWITCH);
+ onEvaluateAutoDataSwitch(extra.evaluateReason);
+ }
+ } else {
+ int reason = (int) msg.obj;
+ onEvaluateAutoDataSwitch(reason);
+ }
break;
- case EVENT_MEETS_AUTO_DATA_SWITCH_STATE:
- int targetPhoneId = msg.arg1;
- boolean needValidation = msg.arg2 == 1;
- log("require validation on phone " + targetPhoneId
- + (needValidation ? "" : " no") + " need to pass");
- mPhoneSwitcherCallback.onRequireValidation(targetPhoneId, needValidation);
+ case EVENT_STABILITY_CHECK_PASSED:
+ if (sFeatureFlags.autoDataSwitchRatSs()) {
+ Object obj = mScheduledEventsToExtras.get(EVENT_STABILITY_CHECK_PASSED);
+ if (obj instanceof StabilityEventExtra extra) {
+ int targetPhoneId = extra.targetPhoneId;
+ boolean needValidation = extra.needValidation;
+ log("require validation on phone " + targetPhoneId
+ + (needValidation ? "" : " no") + " need to pass");
+ mScheduledEventsToExtras.remove(EVENT_STABILITY_CHECK_PASSED);
+ mPhoneSwitcherCallback.onRequireValidation(targetPhoneId, needValidation);
+ }
+ } else {
+ int targetPhoneId = msg.arg1;
+ boolean needValidation = msg.arg2 == 1;
+ log("require validation on phone " + targetPhoneId
+ + (needValidation ? "" : " no") + " need to pass");
+ mPhoneSwitcherCallback.onRequireValidation(targetPhoneId, needValidation);
+ }
break;
case EVENT_SUBSCRIPTIONS_CHANGED:
onSubscriptionsChanged();
@@ -533,16 +594,21 @@
/**
* Called as a preliminary check for the frequent signal/display info change.
- * @return The phone Id if found a candidate phone with higher signal score.
+ * @return The phone Id if found a candidate phone with higher signal score, or the DDS has
+ * an equal score.
*/
private int getHigherScoreCandidatePhoneId() {
int preferredPhoneId = mPhoneSwitcher.getPreferredDataPhoneId();
- if (isActiveModemPhone(preferredPhoneId)) {
+ int ddsPhoneId = mSubscriptionManagerService.getPhoneId(
+ mSubscriptionManagerService.getDefaultDataSubId());
+ if (isActiveModemPhone(preferredPhoneId) && isActiveModemPhone(ddsPhoneId)) {
int currentScore = mPhonesSignalStatus[preferredPhoneId].getRatSignalScore();
for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
+ if (phoneId == preferredPhoneId) continue;
int candidateScore = mPhonesSignalStatus[phoneId].getRatSignalScore();
- if (phoneId != preferredPhoneId
- && (candidateScore - currentScore) > mScoreTolerance) {
+ if ((candidateScore - currentScore) > mScoreTolerance
+ // Also reevaluate if DDS has the same score as the current phone.
+ || (candidateScore >= currentScore && phoneId == ddsPhoneId)) {
return phoneId;
}
}
@@ -559,8 +625,15 @@
? mAutoDataSwitchAvailabilityStabilityTimeThreshold
<< mAutoSwitchValidationFailedCount
: 0;
- if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
- sendMessageDelayed(obtainMessage(EVENT_EVALUATE_AUTO_SWITCH, reason), delayMs);
+ if (sFeatureFlags.autoDataSwitchRatSs()) {
+ if (!mScheduledEventsToExtras.containsKey(EVENT_EVALUATE_AUTO_SWITCH)) {
+ scheduleEventWithTimer(EVENT_EVALUATE_AUTO_SWITCH, new EvaluateEventExtra(reason),
+ delayMs);
+ }
+ } else {
+ if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
+ sendMessageDelayed(obtainMessage(EVENT_EVALUATE_AUTO_SWITCH, reason), delayMs);
+ }
}
}
@@ -576,14 +649,15 @@
int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId();
// check is valid DSDS
if (mSubscriptionManagerService.getActiveSubIdList(true).length < 2) return;
- Phone defaultDataPhone = PhoneFactory.getPhone(mSubscriptionManagerService.getPhoneId(
- defaultDataSubId));
+ int defaultDataPhoneId = mSubscriptionManagerService.getPhoneId(
+ defaultDataSubId);
+ Phone defaultDataPhone = PhoneFactory.getPhone(defaultDataPhoneId);
if (defaultDataPhone == null) {
loge("onEvaluateAutoDataSwitch: cannot find the phone associated with default data"
+ " subscription " + defaultDataSubId);
return;
}
- int defaultDataPhoneId = defaultDataPhone.getPhoneId();
+
int preferredPhoneId = mPhoneSwitcher.getPreferredDataPhoneId();
StringBuilder debugMessage = new StringBuilder("onEvaluateAutoDataSwitch:");
debugMessage.append(" defaultPhoneId: ").append(defaultDataPhoneId)
@@ -591,11 +665,11 @@
.append(", reason: ").append(evaluationReasonToString(reason));
if (preferredPhoneId == defaultDataPhoneId) {
// on default data sub
- int candidatePhoneId = getSwitchCandidatePhoneId(defaultDataPhoneId, debugMessage);
+ StabilityEventExtra res = evaluateAnyCandidateToUse(defaultDataPhoneId, debugMessage);
log(debugMessage.toString());
- if (candidatePhoneId != INVALID_PHONE_INDEX) {
- mSelectedTargetPhoneId = candidatePhoneId;
- startStabilityCheck(candidatePhoneId, mRequirePingTestBeforeSwitch);
+ if (res.targetPhoneId != INVALID_PHONE_INDEX) {
+ mSelectedTargetPhoneId = res.targetPhoneId;
+ startStabilityCheck(res.targetPhoneId, res.isForPerformance, res.needValidation);
} else {
cancelAnyPendingSwitch();
}
@@ -617,6 +691,7 @@
}
boolean backToDefault = false;
+ boolean isForPerformance = false;
boolean needValidation = true;
if (sFeatureFlags.autoSwitchAllowRoaming()) {
@@ -654,12 +729,13 @@
.getRatSignalScore();
int currentScore = mPhonesSignalStatus[preferredPhoneId]
.getRatSignalScore();
- if ((defaultScore - currentScore) > mScoreTolerance) {
+ if (defaultScore >= currentScore) {
debugMessage
- .append(", back to default for higher score ")
+ .append(", back to default for higher or equal score ")
.append(defaultScore).append(" versus current ")
.append(currentScore);
backToDefault = true;
+ isForPerformance = true;
needValidation = mRequirePingTestBeforeSwitch;
}
} else {
@@ -688,12 +764,13 @@
} else if (isRatSignalStrengthBasedSwitchEnabled()) {
int defaultScore = mPhonesSignalStatus[defaultDataPhoneId].getRatSignalScore();
int currentScore = mPhonesSignalStatus[preferredPhoneId].getRatSignalScore();
- if ((defaultScore - currentScore) > mScoreTolerance) {
+ if (defaultScore >= currentScore) {
debugMessage
- .append(", back to default as default phone has higher score ")
+ .append(", back to default as default has higher or equal score ")
.append(defaultScore).append(" versus current ")
.append(currentScore);
backToDefault = true;
+ isForPerformance = true;
needValidation = mRequirePingTestBeforeSwitch;
}
} else if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) {
@@ -706,7 +783,7 @@
if (backToDefault) {
log(debugMessage.toString());
mSelectedTargetPhoneId = defaultDataPhoneId;
- startStabilityCheck(DEFAULT_PHONE_INDEX, needValidation);
+ startStabilityCheck(DEFAULT_PHONE_INDEX, isForPerformance, needValidation);
} else {
// cancel any previous attempts of switching back to default phone
cancelAnyPendingSwitch();
@@ -718,25 +795,29 @@
* Called when consider switching from primary default data sub to another data sub.
* @param defaultPhoneId The default data phone
* @param debugMessage Debug message.
- * @return the target subId if a suitable candidate is found, otherwise return
- * {@link SubscriptionManager#INVALID_PHONE_INDEX}
+ * @return StabilityEventExtra As evaluation result.
*/
- private int getSwitchCandidatePhoneId(int defaultPhoneId, @NonNull StringBuilder debugMessage) {
+ @NonNull private StabilityEventExtra evaluateAnyCandidateToUse(int defaultPhoneId,
+ @NonNull StringBuilder debugMessage) {
Phone defaultDataPhone = PhoneFactory.getPhone(defaultPhoneId);
+ boolean isForPerformance = false;
+ StabilityEventExtra invalidResult = new StabilityEventExtra(INVALID_PHONE_INDEX,
+ isForPerformance, mRequirePingTestBeforeSwitch);
+
if (defaultDataPhone == null) {
debugMessage.append(", no candidate as no sim loaded");
- return INVALID_PHONE_INDEX;
+ return invalidResult;
}
if (!defaultDataPhone.isUserDataEnabled()) {
debugMessage.append(", no candidate as user disabled mobile data");
- return INVALID_PHONE_INDEX;
+ return invalidResult;
}
if (mDefaultNetworkIsOnNonCellular) {
debugMessage.append(", no candidate as default network is active")
.append(" on non-cellular transport");
- return INVALID_PHONE_INDEX;
+ return invalidResult;
}
if (sFeatureFlags.autoSwitchAllowRoaming()) {
@@ -744,14 +825,14 @@
if (!isRatSignalStrengthBasedSwitchEnabled()
&& isHomeService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
debugMessage.append(", no candidate as default phone is in HOME service");
- return INVALID_PHONE_INDEX;
+ return invalidResult;
}
} else {
// check whether primary and secondary signal status are worth switching
if (!isRatSignalStrengthBasedSwitchEnabled()
&& isInService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
debugMessage.append(", no candidate as default phone is in service");
- return INVALID_PHONE_INDEX;
+ return invalidResult;
}
}
@@ -764,27 +845,27 @@
if (sFeatureFlags.autoSwitchAllowRoaming()) {
PhoneSignalStatus.UsableState currentUsableState =
mPhonesSignalStatus[defaultPhoneId].getUsableState();
- PhoneSignalStatus.UsableState candidatePhoneUsableRank =
+ PhoneSignalStatus.UsableState candidateUsableState =
mPhonesSignalStatus[phoneId].getUsableState();
- debugMessage.append(", found phone ").append(phoneId).append(" is ").append(
- candidatePhoneUsableRank)
- .append(", current is ").append(currentUsableState);
- if (candidatePhoneUsableRank.mScore > currentUsableState.mScore) {
+ debugMessage.append(", found phone ").append(phoneId).append(" ")
+ .append(candidateUsableState)
+ .append(", default is ").append(currentUsableState);
+ if (candidateUsableState.mScore > currentUsableState.mScore) {
secondaryDataPhone = PhoneFactory.getPhone(phoneId);
} else if (isRatSignalStrengthBasedSwitchEnabled()
- && currentUsableState.mScore == candidatePhoneUsableRank.mScore) {
+ && currentUsableState.mScore == candidateUsableState.mScore) {
// Both phones are home or both roaming enabled, so compare RAT/signal score.
int defaultScore = defaultPhoneStatus.getRatSignalScore();
int candidateScore = candidatePhoneStatus.getRatSignalScore();
if ((candidateScore - defaultScore) > mScoreTolerance) {
- debugMessage.append(" with higher score ").append(
- candidateScore)
- .append(" versus current ").append(defaultScore);
+ debugMessage.append(" with ").append(defaultScore)
+ .append(" versus candidate higher score ").append(candidateScore);
secondaryDataPhone = PhoneFactory.getPhone(phoneId);
+ isForPerformance = true;
} else {
- debugMessage.append(", but its score ").append(candidateScore)
- .append(" doesn't meet the bar to switch given the current ")
+ debugMessage.append(", candidate's score ").append(candidateScore)
+ .append(" doesn't justify the switch given the current ")
.append(defaultScore);
}
}
@@ -802,6 +883,7 @@
debugMessage.append(" with higher score ").append(candidateScore)
.append(" versus current ").append(defaultScore);
secondaryDataPhone = PhoneFactory.getPhone(phoneId);
+ isForPerformance = true;
} else {
debugMessage.append(", but its score ").append(candidateScore)
.append(" doesn't meet the bar to switch given the current ")
@@ -817,21 +899,23 @@
if (secondaryDataPhone != null) {
// check auto switch feature enabled
if (secondaryDataPhone.isDataAllowed()) {
- return phoneId;
+ return new StabilityEventExtra(phoneId,
+ isForPerformance, mRequirePingTestBeforeSwitch);
} else {
- debugMessage.append(", but its data is not allowed");
+ debugMessage.append(", but candidate's data is not allowed");
}
}
}
debugMessage.append(", found no qualified candidate.");
- return INVALID_PHONE_INDEX;
+ return invalidResult;
}
/**
* @return {@code true} If the feature of switching base on RAT and signal strength is enabled.
*/
private boolean isRatSignalStrengthBasedSwitchEnabled() {
- return sFeatureFlags.autoDataSwitchRatSs() && mScoreTolerance >= 0;
+ return sFeatureFlags.autoDataSwitchRatSs() && mScoreTolerance >= 0
+ && mAutoDataSwitchPerformanceStabilityTimeThreshold >= 0;
}
/**
@@ -839,18 +923,77 @@
* Start pre-switch validation if the current environment suits auto data switch for
* {@link #mAutoDataSwitchAvailabilityStabilityTimeThreshold} MS.
* @param targetPhoneId the target phone Id.
+ * @param isForPerformance {@code true} entails longer stability check.
* @param needValidation {@code true} if validation is needed.
*/
- private void startStabilityCheck(int targetPhoneId, boolean needValidation) {
- log("startAutoDataSwitchStabilityCheck: targetPhoneId=" + targetPhoneId
- + " needValidation=" + needValidation);
+ private void startStabilityCheck(int targetPhoneId, boolean isForPerformance,
+ boolean needValidation) {
String combinationIdentifier = targetPhoneId + "" + needValidation;
- if (!hasEqualMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, combinationIdentifier)) {
- removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE);
- sendMessageDelayed(obtainMessage(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, targetPhoneId,
+ if (sFeatureFlags.autoDataSwitchRatSs()) {
+ StabilityEventExtra eventExtras = (StabilityEventExtra)
+ mScheduledEventsToExtras.getOrDefault(EVENT_STABILITY_CHECK_PASSED,
+ new StabilityEventExtra(INVALID_PHONE_INDEX, false /*need validation*/,
+ false /*isForPerformance*/));
+ long delayMs = -1;
+ // Check if already scheduled one with that combination of extras.
+ if (eventExtras.targetPhoneId != targetPhoneId
+ || eventExtras.needValidation != needValidation
+ || eventExtras.isForPerformance != isForPerformance) {
+ eventExtras =
+ new StabilityEventExtra(targetPhoneId, isForPerformance, needValidation);
+
+ // Reset with new timer.
+ delayMs = isForPerformance
+ ? mAutoDataSwitchPerformanceStabilityTimeThreshold
+ : mAutoDataSwitchAvailabilityStabilityTimeThreshold;
+ scheduleEventWithTimer(EVENT_STABILITY_CHECK_PASSED, eventExtras, delayMs);
+ }
+ log("startStabilityCheck: "
+ + (delayMs != -1 ? "scheduling " : "already scheduled ")
+ + eventExtras);
+ } else if (!hasEqualMessages(EVENT_STABILITY_CHECK_PASSED, combinationIdentifier)) {
+ removeMessages(EVENT_STABILITY_CHECK_PASSED);
+ sendMessageDelayed(obtainMessage(EVENT_STABILITY_CHECK_PASSED, targetPhoneId,
needValidation ? 1 : 0,
combinationIdentifier),
mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+ log("startStabilityCheck: targetPhoneId=" + targetPhoneId
+ + " isForPerformance=" + isForPerformance
+ + " needValidation=" + needValidation);
+ }
+ }
+
+ /**
+ * Use when need to schedule with timer. Short timer uses handler, while the longer timer uses
+ * alarm manager to account for real time elapse.
+ *
+ * @param event The event.
+ * @param extras Any extra data associated with the event.
+ * @param delayMs The delayed interval in ms.
+ */
+ private void scheduleEventWithTimer(int event, @NonNull Object extras, long delayMs) {
+ // Get singleton alarm listener.
+ mEventsToAlarmListener.putIfAbsent(event, () -> sendEmptyMessage(event));
+ AlarmManager.OnAlarmListener listener = mEventsToAlarmListener.get(event);
+
+ // Cancel any existing.
+ removeMessages(event);
+ mAlarmManager.cancel(listener);
+ // Override with new extras.
+ mScheduledEventsToExtras.put(event, extras);
+ // Reset timer.
+ if (delayMs <= RETRY_LONG_DELAY_TIMER_THRESHOLD_MILLIS) {
+ // Use handler for short timer.
+ sendEmptyMessageDelayed(event, delayMs);
+ } else {
+ // Not using setWhileIdle because it can wait util the next time the device wakes up to
+ // save power.
+ // If another evaluation is processed before the alarm fires,
+ // this timer is restarted (AlarmManager using the same listener resets the
+ // timer).
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime() + delayMs,
+ LOG_TAG /*debug tag*/, listener, this);
}
}
@@ -906,7 +1049,19 @@
private void cancelAnyPendingSwitch() {
mSelectedTargetPhoneId = INVALID_PHONE_INDEX;
resetFailedCount();
- removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE);
+ if (sFeatureFlags.autoDataSwitchRatSs()) {
+ if (mScheduledEventsToExtras.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
+ if (mEventsToAlarmListener.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
+ mAlarmManager.cancel(mEventsToAlarmListener.get(EVENT_STABILITY_CHECK_PASSED));
+ } else {
+ loge("cancelAnyPendingSwitch: EVENT_STABILITY_CHECK_PASSED listener is null");
+ }
+ removeMessages(EVENT_STABILITY_CHECK_PASSED);
+ mScheduledEventsToExtras.remove(EVENT_STABILITY_CHECK_PASSED);
+ }
+ } else {
+ removeMessages(EVENT_STABILITY_CHECK_PASSED);
+ }
mPhoneSwitcherCallback.onRequireCancelAnyPendingAutoSwitchValidation();
}
@@ -920,8 +1075,6 @@
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (mDisplayedNotification) {
// cancel posted notification if any exist
- log("displayAutoDataSwitchNotification: canceling any notifications for phone "
- + phoneId);
notificationManager.cancel(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
AUTO_DATA_SWITCH_NOTIFICATION_ID);
return;
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index ea7b1da..90743f8 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -870,6 +870,14 @@
}
/**
+ * @return the data limit in bytes that can be used for esim bootstrap usage.
+ */
+ public long getEsimBootStrapMaxDataLimitBytes() {
+ return mResources.getInteger(
+ com.android.internal.R.integer.config_esim_bootstrap_data_limit_bytes);
+ }
+
+ /**
* Update the TCP buffer sizes from the resource overlays.
*/
private void updateTcpBuffers() {
@@ -1061,6 +1069,16 @@
}
/**
+ * @return Time threshold in ms to define a internet connection performance status to be stable
+ * (e.g. LTE + 4 signal strength, UMTS + 2 signal strength), while -1 indicates
+ * auto switch feature based on RAT/SS is disabled.
+ */
+ public long getAutoDataSwitchPerformanceStabilityTimeThreshold() {
+ return mResources.getInteger(com.android.internal.R.integer
+ .auto_data_switch_performance_stability_time_threshold_millis);
+ }
+
+ /**
* Get the TCP config string, used by {@link LinkProperties#setTcpBufferSizes(String)}.
* The config string will have the following form, with values in bytes:
* "read_min,read_default,read_max,write_min,write_default,write_max"
@@ -1398,6 +1416,17 @@
}
/**
+ * @return Indicating whether the retry timer from setup data call response for data throttling
+ * should be honored for emergency network request. By default this is off, meaning emergency
+ * network requests will ignore the previous retry timer passed in from setup data call
+ * response.
+ */
+ public boolean shouldHonorRetryTimerForEmergencyNetworkRequest() {
+ return mResources.getBoolean(
+ com.android.internal.R.bool.config_honor_data_retry_timer_for_emergency_network);
+ }
+
+ /**
* Log debug messages.
* @param s debug messages
*/
@@ -1440,6 +1469,8 @@
pw.increaseIndent();
mDataHandoverRetryRules.forEach(pw::println);
pw.decreaseIndent();
+ pw.println("shouldHonorRetryTimerForEmergencyNetworkRequest="
+ + shouldHonorRetryTimerForEmergencyNetworkRequest());
pw.println("mSetupDataCallAnomalyReport=" + mSetupDataCallAnomalyReportThreshold);
pw.println("mNetworkUnwantedAnomalyReport=" + mNetworkUnwantedAnomalyReportThreshold);
pw.println("mImsReleaseRequestAnomalyReport=" + mImsReleaseRequestAnomalyReportThreshold);
@@ -1456,6 +1487,8 @@
+ Arrays.toString(value)));
pw.println("getAutoDataSwitchAvailabilityStabilityTimeThreshold="
+ getAutoDataSwitchAvailabilityStabilityTimeThreshold());
+ pw.println("getAutoDataSwitchPerformanceStabilityTimeThreshold="
+ + getAutoDataSwitchPerformanceStabilityTimeThreshold());
pw.println("getAutoDataSwitchValidationMaxRetry=" + getAutoDataSwitchValidationMaxRetry());
pw.decreaseIndent();
pw.println("Metered APN types=" + mMeteredApnTypes.stream()
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java
index 6858c6a..3d10e9c 100644
--- a/src/java/com/android/internal/telephony/data/DataEvaluation.java
+++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -257,7 +257,9 @@
/** Tracking area code changed. */
TAC_CHANGED(true),
/** Unsatisfied network request detached. */
- UNSATISFIED_REQUEST_DETACHED(true);
+ UNSATISFIED_REQUEST_DETACHED(true),
+ /** track bootstrap sim data usage */
+ CHECK_DATA_USAGE(false);
/**
* {@code true} if the evaluation is due to environmental changes (i.e. SIM removal,
@@ -342,7 +344,9 @@
/** Data enabled settings are not ready. */
DATA_SETTINGS_NOT_READY(true),
/** Handover max retry stopped but network is not on the preferred transport. */
- HANDOVER_RETRY_STOPPED(true);
+ HANDOVER_RETRY_STOPPED(true),
+ /** BootStrap sim data limit reached. */
+ DATA_LIMIT_REACHED(true);
private final boolean mIsHardReason;
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 6ae861c..c1e6155 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -122,6 +122,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -312,6 +313,7 @@
TEAR_DOWN_REASON_ILLEGAL_STATE,
TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK,
TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED,
+ TEAR_DOWN_REASON_DATA_LIMIT_REACHED,
})
public @interface TearDownReason {}
@@ -408,6 +410,9 @@
/** Data network tear down due to preferred data switched to another phone. */
public static final int TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED = 30;
+ /** Data network tear down due to bootstrap sim data limit reached. */
+ public static final int TEAR_DOWN_REASON_DATA_LIMIT_REACHED = 31;
+
//********************************************************************************************//
// WHENEVER ADD A NEW TEAR DOWN REASON, PLEASE UPDATE DataDeactivateReasonEnum in enums.proto //
//********************************************************************************************//
@@ -917,6 +922,14 @@
*/
public abstract void onRetryUnsatisfiedNetworkRequest(
@NonNull TelephonyNetworkRequest networkRequest);
+
+ /**
+ * Called when QosBearerSessions bearer changed, which indicates VoNr or VoLte calls.
+ *
+ * @param qosBearerSessions The current qosBearerSessions.
+ */
+ public abstract void onQosSessionsChanged(
+ @NonNull List<QosBearerSession> qosBearerSessions);
}
/**
@@ -2672,6 +2685,12 @@
mDefaultQos = response.getDefaultQos();
+
+ Set<QosBearerSession> newSessions = new HashSet<>(response.getQosBearerSessions());
+ if (newSessions.size() != mQosBearerSessions.size()
+ || !newSessions.containsAll(mQosBearerSessions)) {
+ mDataNetworkCallback.onQosSessionsChanged(response.getQosBearerSessions());
+ }
mQosBearerSessions.clear();
mQosBearerSessions.addAll(response.getQosBearerSessions());
if (mQosCallbackTracker != null) {
@@ -3757,6 +3776,8 @@
return "TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK";
case TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED:
return "TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED";
+ case TEAR_DOWN_REASON_DATA_LIMIT_REACHED:
+ return "TEAR_DOWN_REASON_DATA_LIMIT_REACHED";
default:
return "UNKNOWN(" + reason + ")";
}
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 645e8bd..3ca28a3 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -20,6 +20,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -29,6 +31,7 @@
import android.net.NetworkPolicyManager;
import android.net.NetworkPolicyManager.SubscriptionCallback;
import android.net.NetworkRequest;
+import android.net.NetworkTemplate;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Handler;
@@ -66,6 +69,7 @@
import android.telephony.data.DataCallResponse.LinkStatus;
import android.telephony.data.DataProfile;
import android.telephony.data.DataServiceCallback;
+import android.telephony.data.QosBearerSession;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsReasonInfo;
@@ -238,6 +242,18 @@
private static final long REEVALUATE_UNSATISFIED_NETWORK_REQUESTS_AFTER_DETACHED_DELAY_MILLIS =
TimeUnit.SECONDS.toMillis(1);
+ /**
+ * The delay in milliseconds to re-evaluate existing data networks for bootstrap sim data usage
+ * limit.
+ */
+ private static final long REEVALUATE_BOOTSTRAP_SIM_DATA_USAGE_MILLIS =
+ TimeUnit.SECONDS.toMillis(60);
+
+ /**
+ * bootstrap sim total data usage bytes
+ */
+ private long mBootStrapSimTotalDataUsageBytes = 0L;
+
private final Phone mPhone;
private final String mLogTag;
private final LocalLog mLocalLog = new LocalLog(128);
@@ -632,6 +648,13 @@
* @param simState The current SIM state
*/
public void onSimStateChanged(@SimState int simState) {}
+
+ /**
+ * Called when QosBearerSessions changed.
+ *
+ * @param qosBearerSessions The latest QOS bearer sessions.
+ */
+ public void onQosSessionsChanged(@NonNull List<QosBearerSession> qosBearerSessions) {}
}
/**
@@ -1505,11 +1528,27 @@
// Bypass all checks for emergency network request.
if (networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
- evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_REQUEST);
- evaluation.setCandidateDataProfile(mDataProfileManager.getDataProfileForNetworkRequest(
+ DataProfile emergencyProfile = mDataProfileManager.getDataProfileForNetworkRequest(
networkRequest, getDataNetworkType(transport),
mServiceState.isUsingNonTerrestrialNetwork(),
- isEsimBootStrapProvisioningActivated(), true));
+ isEsimBootStrapProvisioningActivated(), true);
+
+ // Check if the profile is being throttled.
+ if (mDataConfigManager.shouldHonorRetryTimerForEmergencyNetworkRequest()
+ && emergencyProfile != null
+ && mDataRetryManager.isDataProfileThrottled(emergencyProfile, transport)) {
+ evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_THROTTLED);
+ log("Emergency network request is throttled by the previous setup data "
+ + "call response.");
+ log(evaluation.toString());
+ networkRequest.setEvaluation(evaluation);
+ return evaluation;
+ }
+
+ evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_REQUEST);
+ if (emergencyProfile != null) {
+ evaluation.setCandidateDataProfile(emergencyProfile);
+ }
networkRequest.setEvaluation(evaluation);
log(evaluation.toString());
return evaluation;
@@ -1685,7 +1724,14 @@
}
if (!evaluation.containsDisallowedReasons()) {
- evaluation.setCandidateDataProfile(dataProfile);
+ if (transport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
+ && isEsimBootStrapProvisioningActivated()
+ && isEsimBootStrapMaxDataLimitReached()) {
+ log("BootStrap Sim Data Usage limit reached");
+ evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_LIMIT_REACHED);
+ } else {
+ evaluation.setCandidateDataProfile(dataProfile);
+ }
}
networkRequest.setEvaluation(evaluation);
@@ -1702,6 +1748,61 @@
}
/**
+ * This method
+ * - At evaluation network request and evaluation data network determines, if
+ * bootstrap sim current data usage reached bootstrap sim max data limit allowed set
+ * at {@link DataConfigManager#getEsimBootStrapMaxDataLimitBytes()}
+ * - Query the current data usage at {@link #getDataUsage()}
+ *
+ * @return true, if bootstrap sim data limit is reached
+ * else false, if bootstrap sim max data limit allowed set is -1(Unlimited) or current
+ * bootstrap sim total data usage is less than bootstrap sim max data limit allowed.
+ *
+ */
+ private boolean isEsimBootStrapMaxDataLimitReached() {
+ long esimBootStrapMaxDataLimitBytes =
+ mDataConfigManager.getEsimBootStrapMaxDataLimitBytes();
+
+ if (esimBootStrapMaxDataLimitBytes < 0L) {
+ return false;
+ }
+
+ log("current bootstrap sim data Usage: " + mBootStrapSimTotalDataUsageBytes);
+ if (mBootStrapSimTotalDataUsageBytes >= esimBootStrapMaxDataLimitBytes) {
+ return true;
+ } else {
+ mBootStrapSimTotalDataUsageBytes = getDataUsage();
+ return mBootStrapSimTotalDataUsageBytes >= esimBootStrapMaxDataLimitBytes;
+ }
+ }
+
+ /**
+ * Query network usage statistics summaries based on {@link
+ * NetworkStatsManager#querySummaryForDevice(NetworkTemplate, long, long)}
+ *
+ * @return Data usage in bytes for the connected networks related to the current subscription
+ */
+ private long getDataUsage() {
+ NetworkStatsManager networkStatsManager =
+ mPhone.getContext().getSystemService(NetworkStatsManager.class);
+
+ if (networkStatsManager != null) {
+ final NetworkTemplate.Builder builder =
+ new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE);
+ final String subscriberId = mPhone.getSubscriberId();
+
+ if (!TextUtils.isEmpty(subscriberId)) {
+ builder.setSubscriberIds(Set.of(subscriberId));
+ NetworkTemplate template = builder.build();
+ final NetworkStats.Bucket ret = networkStatsManager
+ .querySummaryForDevice(template, 0L, System.currentTimeMillis());
+ return ret.getRxBytes() + ret.getTxBytes();
+ }
+ }
+ return 0L;
+ }
+
+ /**
* @return The grouped unsatisfied network requests. The network requests that have the same
* network capabilities is grouped into one {@link NetworkRequestList}.
*/
@@ -1787,6 +1888,25 @@
evaluation.addDataDisallowedReason(DataDisallowedReason.SERVICE_OPTION_NOT_SUPPORTED);
}
+ // Check whether data limit reached for bootstrap sim, else re-evaluate based on the timer
+ // set.
+ if (isEsimBootStrapProvisioningActivated()
+ && dataNetwork.getTransport() == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ if (isEsimBootStrapMaxDataLimitReached()) {
+ log("BootStrap Sim Data Usage limit reached");
+ evaluation.addDataDisallowedReason(DataDisallowedReason.DATA_LIMIT_REACHED);
+ } else {
+ if (!hasMessages(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS)) {
+ sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
+ DataEvaluationReason.CHECK_DATA_USAGE),
+ REEVALUATE_BOOTSTRAP_SIM_DATA_USAGE_MILLIS);
+ } else {
+ log("skip scheduling evaluating existing data networks since already"
+ + "scheduled");
+ }
+ }
+ }
+
// Check if there are other network that has higher priority, and only single data network
// is allowed.
if (isOnlySingleDataNetworkAllowed(dataNetwork.getTransport())
@@ -2169,6 +2289,8 @@
return DataNetwork.TEAR_DOWN_REASON_ONLY_ALLOWED_SINGLE_NETWORK;
case HANDOVER_RETRY_STOPPED:
return DataNetwork.TEAR_DOWN_REASON_HANDOVER_FAILED;
+ case DATA_LIMIT_REACHED:
+ return DataNetwork.TEAR_DOWN_REASON_DATA_LIMIT_REACHED;
}
}
return DataNetwork.TEAR_DOWN_REASON_NONE;
@@ -2675,6 +2797,14 @@
DataNetworkController.this.onRetryUnsatisfiedNetworkRequest(
networkRequest);
}
+
+ @Override
+ public void onQosSessionsChanged(
+ @NonNull List<QosBearerSession> qosBearerSessions) {
+ mDataNetworkControllerCallbacks.forEach(
+ callback -> callback.invokeFromExecutor(() ->
+ callback.onQosSessionsChanged(qosBearerSessions)));
+ }
}
));
if (!mAnyDataNetworkExisting) {
@@ -2797,6 +2927,12 @@
+ TelephonyUtils.dataStateToString(mImsDataNetworkState) + " to CONNECTED.");
mImsDataNetworkState = TelephonyManager.DATA_CONNECTED;
}
+
+ if (isEsimBootStrapProvisioningActivated()) {
+ sendMessageDelayed(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
+ DataEvaluationReason.CHECK_DATA_USAGE),
+ REEVALUATE_BOOTSTRAP_SIM_DATA_USAGE_MILLIS);
+ }
}
/**
@@ -4052,6 +4188,7 @@
.map(TelephonyManager::getNetworkTypeName).collect(Collectors.joining(",")));
pw.println("mImsThrottleCounter=" + mImsThrottleCounter);
pw.println("mNetworkUnwantedCounter=" + mNetworkUnwantedCounter);
+ pw.println("mBootStrapSimTotalDataUsageBytes=" + mBootStrapSimTotalDataUsageBytes);
pw.println("Local logs:");
pw.increaseIndent();
mLocalLog.dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index b4055a3..51fc71b 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -34,6 +34,7 @@
import android.telephony.Annotation.NetworkType;
import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.SimState;
@@ -286,7 +287,8 @@
log("Added " + dataProfile);
isInternetSupported |= apn.canHandleType(ApnSetting.TYPE_DEFAULT);
- if (mDataConfigManager.isApnConfigAnomalyReportEnabled()) {
+ if (mDataConfigManager.isApnConfigAnomalyReportEnabled()
+ && apn.getEditedStatus() == Telephony.Carriers.UNEDITED) {
checkApnSetting(apn);
}
}
@@ -818,8 +820,13 @@
})
.collect(Collectors.toList());
if (dataProfiles.size() == 0) {
+ String ntnReason = "";
+ if (mFeatureFlags.carrierEnabledSatelliteFlag()) {
+ ntnReason = " and infrastructure for "
+ + NetworkRegistrationInfo.isNonTerrestrialNetworkToString(isNtn);
+ }
log("Can't find any data profile for network type "
- + TelephonyManager.getNetworkTypeName(networkType));
+ + TelephonyManager.getNetworkTypeName(networkType) + ntnReason);
return null;
}
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
index c66aebc..e3eed00 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
@@ -33,7 +33,7 @@
import android.telephony.DomainSelectionService;
import android.telephony.DomainSelectionService.EmergencyScanType;
import android.telephony.DomainSelector;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.data.ApnSetting;
import android.util.LocalLog;
@@ -66,6 +66,8 @@
protected static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 2;
protected static final int EVENT_SERVICE_CONNECTED = 3;
protected static final int EVENT_SERVICE_BINDING_TIMEOUT = 4;
+ protected static final int EVENT_RESET_NETWORK_SCAN_DONE = 5;
+ protected static final int EVENT_LAST = EVENT_RESET_NETWORK_SCAN_DONE;
private static final int DEFAULT_BIND_RETRY_TIMEOUT_MS = 4 * 1000;
@@ -73,6 +75,7 @@
private static final int STATUS_DOMAIN_SELECTED = 1 << 1;
private static final int STATUS_WAIT_BINDING = 1 << 2;
private static final int STATUS_WAIT_SCAN_RESULT = 1 << 3;
+ private static final int STATUS_WAIT_RESET_SCAN_RESULT = 1 << 4;
/** Callback to receive responses from DomainSelectionConnection. */
public interface DomainSelectionConnectionCallback {
@@ -85,6 +88,16 @@
void onSelectionTerminated(@DisconnectCauses int cause);
}
+ private static class ScanRequest {
+ final int[] mPreferredNetworks;
+ final int mScanType;
+
+ ScanRequest(int[] preferredNetworks, int scanType) {
+ mPreferredNetworks = preferredNetworks;
+ mScanType = scanType;
+ }
+ }
+
/**
* A wrapper class for {@link ITransportSelectorCallback} interface.
*/
@@ -95,7 +108,7 @@
mDomainSelector = selector;
if (checkState(STATUS_DISPOSED)) {
try {
- selector.cancelSelection();
+ selector.finishSelection();
} catch (RemoteException e) {
// ignore exception
}
@@ -117,20 +130,6 @@
}
@Override
- public @NonNull IWwanSelectorCallback onWwanSelected() {
- synchronized (mLock) {
- if (mWwanSelectorCallback == null) {
- mWwanSelectorCallback = new WwanSelectorCallbackAdaptor();
- }
- if (checkState(STATUS_DISPOSED)) {
- return mWwanSelectorCallback;
- }
- DomainSelectionConnection.this.onWwanSelected();
- return mWwanSelectorCallback;
- }
- }
-
- @Override
public void onWwanSelectedAsync(@NonNull final ITransportSelectorResultCallback cb) {
synchronized (mLock) {
if (checkState(STATUS_DISPOSED)) {
@@ -139,25 +138,41 @@
if (mWwanSelectorCallback == null) {
mWwanSelectorCallback = new WwanSelectorCallbackAdaptor();
}
- initHandler();
- mHandler.post(() -> {
- synchronized (mLock) {
- if (checkState(STATUS_DISPOSED)) {
- return;
+ if (mIsTestMode || !mIsEmergency
+ || (mSelectorType != DomainSelectionService.SELECTOR_TYPE_CALLING)) {
+ initHandler();
+ mHandler.post(() -> {
+ onWwanSelectedAsyncInternal(cb);
+ });
+ } else {
+ Thread workerThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ onWwanSelectedAsyncInternal(cb);
}
- DomainSelectionConnection.this.onWwanSelected();
- }
- try {
- cb.onCompleted(mWwanSelectorCallback);
- } catch (RemoteException e) {
- loge("onWwanSelectedAsync executor exception=" + e);
- synchronized (mLock) {
- // Since remote service is not available,
- // wait for binding or timeout.
- waitForServiceBinding(null);
- }
- }
- });
+ });
+ workerThread.start();
+ }
+ }
+ }
+
+ private void onWwanSelectedAsyncInternal(
+ @NonNull final ITransportSelectorResultCallback cb) {
+ synchronized (mLock) {
+ if (checkState(STATUS_DISPOSED)) {
+ return;
+ }
+ }
+ DomainSelectionConnection.this.onWwanSelected();
+ try {
+ cb.onCompleted(mWwanSelectorCallback);
+ } catch (RemoteException e) {
+ loge("onWwanSelectedAsync executor exception=" + e);
+ synchronized (mLock) {
+ // Since remote service is not available,
+ // wait for binding or timeout.
+ waitForServiceBinding(null);
+ }
}
}
@@ -180,7 +195,8 @@
@Override
public void onRequestEmergencyNetworkScan(
@NonNull @RadioAccessNetworkType int[] preferredNetworks,
- @EmergencyScanType int scanType, @NonNull IWwanSelectorResultCallback cb) {
+ @EmergencyScanType int scanType, boolean resetScan,
+ @NonNull IWwanSelectorResultCallback cb) {
synchronized (mLock) {
if (checkState(STATUS_DISPOSED)) {
return;
@@ -190,7 +206,7 @@
mHandler.post(() -> {
synchronized (mLock) {
DomainSelectionConnection.this.onRequestEmergencyNetworkScan(
- preferredNetworks, scanType);
+ preferredNetworks, scanType, resetScan);
}
});
}
@@ -232,7 +248,7 @@
switch (msg.what) {
case EVENT_EMERGENCY_NETWORK_SCAN_RESULT:
ar = (AsyncResult) msg.obj;
- EmergencyRegResult regResult = (EmergencyRegResult) ar.result;
+ EmergencyRegistrationResult regResult = (EmergencyRegistrationResult) ar.result;
if (DBG) logd("EVENT_EMERGENCY_NETWORK_SCAN_RESULT result=" + regResult);
synchronized (mLock) {
clearState(STATUS_WAIT_SCAN_RESULT);
@@ -275,6 +291,17 @@
}
}
break;
+ case EVENT_RESET_NETWORK_SCAN_DONE:
+ synchronized (mLock) {
+ clearState(STATUS_WAIT_RESET_SCAN_RESULT);
+ if (checkState(STATUS_DISPOSED)
+ || (mPendingScanRequest == null)) {
+ return;
+ }
+ onRequestEmergencyNetworkScan(mPendingScanRequest.mPreferredNetworks,
+ mPendingScanRequest.mScanType, false);
+ }
+ break;
default:
loge("handleMessage unexpected msg=" + msg.what);
break;
@@ -320,6 +347,10 @@
private @NonNull AndroidFuture<Integer> mOnComplete;
+ private @Nullable ScanRequest mPendingScanRequest;
+
+ private boolean mIsTestMode = false;
+
/**
* Creates an instance.
*
@@ -364,7 +395,7 @@
*
* @return The {@link IWwanSelectorResultCallback} interface.
*/
- public @Nullable IWwanSelectorResultCallback getEmergencyRegResultCallback() {
+ public @Nullable IWwanSelectorResultCallback getWwanSelectorResultCallback() {
return mResultCallback;
}
@@ -449,10 +480,11 @@
*
* @param preferredNetworks The ordered list of preferred networks to scan.
* @param scanType Indicates the scan preference, such as full service or limited service.
+ * @param resetScan Indicates that the previous scan result shall be reset before scanning.
*/
public void onRequestEmergencyNetworkScan(
@NonNull @RadioAccessNetworkType int[] preferredNetworks,
- @EmergencyScanType int scanType) {
+ @EmergencyScanType int scanType, boolean resetScan) {
// Can be overridden if required
synchronized (mLock) {
@@ -464,6 +496,29 @@
return;
}
+ if (checkState(STATUS_WAIT_RESET_SCAN_RESULT)) {
+ if (mPendingScanRequest != null) {
+ /* Consecutive scan requests without cancellation is not an expected use case.
+ * DomainSelector should cancel the previous request or wait for the result
+ * before requesting a new scan.*/
+ logi("onRequestEmergencyNetworkScan consecutive scan requests");
+ return;
+ } else {
+ // The reset has not been completed.
+ // case1) Long delay in cancelEmergencyNetworkScan by modem.
+ // case2) A consecutive scan requests with short interval from DomainSelector.
+ logi("onRequestEmergencyNetworkScan reset not completed");
+ }
+ mPendingScanRequest = new ScanRequest(preferredNetworks, scanType);
+ return;
+ } else if (resetScan) {
+ setState(STATUS_WAIT_RESET_SCAN_RESULT);
+ mPendingScanRequest = new ScanRequest(preferredNetworks, scanType);
+ mPhone.cancelEmergencyNetworkScan(resetScan,
+ mHandler.obtainMessage(EVENT_RESET_NETWORK_SCAN_DONE));
+ return;
+ }
+
if (!mRegisteredRegistrant) {
mPhone.registerForEmergencyNetworkScan(mHandler,
EVENT_EMERGENCY_NETWORK_SCAN_RESULT, null);
@@ -471,6 +526,7 @@
}
setState(STATUS_WAIT_SCAN_RESULT);
mPhone.triggerEmergencyNetworkScan(preferredNetworks, scanType, null);
+ mPendingScanRequest = null;
}
}
@@ -506,6 +562,7 @@
}
private void onCancel(boolean resetScan) {
+ mPendingScanRequest = null;
if (checkState(STATUS_WAIT_SCAN_RESULT)) {
clearState(STATUS_WAIT_SCAN_RESULT);
mPhone.cancelEmergencyNetworkScan(resetScan, null);
@@ -517,17 +574,7 @@
* to clean up all ongoing operations with the framework.
*/
public void cancelSelection() {
- synchronized (mLock) {
- try {
- if (mDomainSelector != null) {
- mDomainSelector.cancelSelection();
- }
- } catch (RemoteException e) {
- loge("cancelSelection exception=" + e);
- } finally {
- dispose();
- }
- }
+ finishSelection();
}
/**
@@ -732,6 +779,16 @@
}
/**
+ * Set whether it is unit test or not.
+ *
+ * @param testMode Indicates whether it is unit test or not.
+ */
+ @VisibleForTesting
+ public void setTestMode(boolean testMode) {
+ mIsTestMode = testMode;
+ }
+
+ /**
* Dumps local log.
*/
public void dump(@NonNull PrintWriter printWriter) {
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
index 6d64a31..ee8517d 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionController.java
@@ -101,6 +101,7 @@
private ExponentialBackoff mBackoff;
private boolean mBackoffStarted = false;
+ private boolean mUnbind = false;
// Retry the bind to the DomainSelectionService that has died after mBindRetry timeout.
private Runnable mRestartBindingRunnable = new Runnable() {
@@ -470,12 +471,14 @@
*/
public boolean bind(@NonNull ComponentName componentName) {
mComponentName = componentName;
+ mUnbind = false;
return bind();
}
private boolean bind() {
logd("bind isBindingOrBound=" + mIsBound);
synchronized (mLock) {
+ if (mUnbind) return false;
if (!mIsBound) {
mIsBound = true;
Intent serviceIntent = new Intent(DomainSelectionService.SERVICE_INTERFACE)
@@ -512,6 +515,7 @@
*/
public void unbind() {
synchronized (mLock) {
+ mUnbind = true;
stopBackoffTimer();
mIsBound = false;
setServiceController(null);
@@ -548,7 +552,8 @@
}
private void notifyBindFailure() {
- logi("notifyBindFailure " + mBackoffStarted);
+ logi("notifyBindFailure started=" + mBackoffStarted + ", unbind=" + mUnbind);
+ if (mUnbind) return;
if (mBackoffStarted) {
mBackoff.notifyFailed();
} else {
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
index 8a0dcf5..410f89b 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionResolver.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.os.SystemProperties;
import android.telephony.DomainSelectionService;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
@@ -34,6 +35,7 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -48,6 +50,10 @@
@VisibleForTesting
protected static final String PACKAGE_NAME_NONE = "none";
private static final String TAG = DomainSelectionResolver.class.getSimpleName();
+ private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
+ /** For test purpose only with userdebug release */
+ private static final String PROP_DISABLE_DOMAIN_SELECTION =
+ "telephony.test.disable_domain_selection";
private static DomainSelectionResolver sInstance = null;
/**
@@ -132,6 +138,10 @@
* {@code false} otherwise.
*/
public boolean isDomainSelectionSupported() {
+ if (DBG && SystemProperties.getBoolean(PROP_DISABLE_DOMAIN_SELECTION, false)) {
+ logi("Disabled for test");
+ return false;
+ }
return mDefaultComponentName != null && PhoneFactory.getDefaultPhone()
.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1);
}
@@ -198,7 +208,6 @@
* @return {@code true} if the requested operation is successfully done,
* {@code false} otherwise.
*/
- @VisibleForTesting
public boolean setDomainSelectionServiceOverride(@NonNull ComponentName componentName) {
if (mController == null) {
logd("Controller is not initialized.");
@@ -224,7 +233,6 @@
* @return {@code true} if the requested operation is successfully done,
* {@code false} otherwise.
*/
- @VisibleForTesting
public boolean clearDomainSelectionServiceOverride() {
if (mController == null) {
logd("Controller is not initialized.");
diff --git a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
index af4b03a..cb4ddab 100644
--- a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
@@ -28,12 +28,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.Uri;
+import android.telecom.PhoneAccount;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Annotation.DisconnectCauses;
import android.telephony.Annotation.NetCapability;
import android.telephony.DomainSelectionService;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsReasonInfo;
@@ -107,7 +109,7 @@
/** {@inheritDoc} */
@Override
public void onWwanSelected() {
- mEmergencyStateTracker.onEmergencyTransportChanged(
+ mEmergencyStateTracker.onEmergencyTransportChangedAndWait(
EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN);
}
@@ -202,6 +204,7 @@
* @param exited {@code true} if the request caused the device to move out of airplane mode.
* @param callId The call identifier.
* @param number The dialed number.
+ * @param isTest Indicates it's a test emergency number.
* @param callFailCause The reason why the last CS attempt failed.
* @param imsReasonInfo The reason why the last PS attempt failed.
* @param emergencyRegResult The current registration result for emergency services.
@@ -209,20 +212,21 @@
*/
public static @NonNull DomainSelectionService.SelectionAttributes getSelectionAttributes(
int slotId, int subId, boolean exited,
- @NonNull String callId, @NonNull String number, int callFailCause,
- @Nullable ImsReasonInfo imsReasonInfo,
- @Nullable EmergencyRegResult emergencyRegResult) {
+ @NonNull String callId, @NonNull String number, boolean isTest,
+ int callFailCause, @Nullable ImsReasonInfo imsReasonInfo,
+ @Nullable EmergencyRegistrationResult emergencyRegResult) {
DomainSelectionService.SelectionAttributes.Builder builder =
new DomainSelectionService.SelectionAttributes.Builder(
slotId, subId, SELECTOR_TYPE_CALLING)
.setEmergency(true)
+ .setTestEmergencyNumber(isTest)
.setExitedFromAirplaneMode(exited)
.setCallId(callId)
- .setNumber(number)
+ .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null))
.setCsDisconnectCause(callFailCause);
if (imsReasonInfo != null) builder.setPsDisconnectCause(imsReasonInfo);
- if (emergencyRegResult != null) builder.setEmergencyRegResult(emergencyRegResult);
+ if (emergencyRegResult != null) builder.setEmergencyRegistrationResult(emergencyRegResult);
return builder.build();
}
@@ -234,13 +238,15 @@
if (attr == null) return null;
DomainSelectionService.SelectionAttributes.Builder builder =
new DomainSelectionService.SelectionAttributes.Builder(
- attr.getSlotId(), attr.getSubId(), SELECTOR_TYPE_CALLING)
+ attr.getSlotIndex(), attr.getSubscriptionId(), SELECTOR_TYPE_CALLING)
.setCallId(attr.getCallId())
- .setNumber(attr.getNumber())
+ .setAddress(attr.getAddress())
.setVideoCall(attr.isVideoCall())
.setEmergency(true)
+ .setTestEmergencyNumber(attr.isTestEmergencyNumber())
.setExitedFromAirplaneMode(attr.isExitedFromAirplaneMode())
- .setEmergencyRegResult(new EmergencyRegResult(AccessNetworkType.UNKNOWN,
+ .setEmergencyRegistrationResult(
+ new EmergencyRegistrationResult(AccessNetworkType.UNKNOWN,
NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN,
NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0,
"", "", ""));
diff --git a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
index 0532a05..0fd9201 100644
--- a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.Uri;
+import android.telecom.PhoneAccount;
import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
import android.telephony.Annotation.DisconnectCauses;
import android.telephony.DisconnectCause;
@@ -78,7 +80,7 @@
/** {@inheritDoc} */
@Override
public void onRequestEmergencyNetworkScan(@RadioAccessNetworkType int[] preferredNetworks,
- @EmergencyScanType int scanType) {
+ @EmergencyScanType int scanType, boolean resetScan) {
// Not expected with normal calling.
// Override to prevent abnormal behavior.
}
@@ -119,7 +121,7 @@
slotId, subId, SELECTOR_TYPE_CALLING)
.setEmergency(false)
.setCallId(callId)
- .setNumber(number)
+ .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null))
.setCsDisconnectCause(callFailCause)
.setVideoCall(isVideoCall);
diff --git a/src/java/com/android/internal/telephony/domainselection/OWNERS b/src/java/com/android/internal/telephony/domainselection/OWNERS
index b9112be..2a76770 100644
--- a/src/java/com/android/internal/telephony/domainselection/OWNERS
+++ b/src/java/com/android/internal/telephony/domainselection/OWNERS
@@ -6,3 +6,4 @@
mkoon@google.com
seheele@google.com
radhikaagrawal@google.com
+jdyou@google.com
diff --git a/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
index 36a7b17..b3f4924 100644
--- a/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/SmsDomainSelectionConnection.java
@@ -53,17 +53,6 @@
if (mCallback != null) mCallback.onSelectionTerminated(cause);
}
- @Override
- public void finishSelection() {
- CompletableFuture<Integer> future = getCompletableFuture();
-
- if (future != null && !future.isDone()) {
- cancelSelection();
- } else {
- super.finishSelection();
- }
- }
-
/**
* Requests a domain selection for SMS.
*
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index f3c0a6c..a35cccf 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -35,6 +35,7 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.provider.Settings;
@@ -43,7 +44,7 @@
import android.telephony.Annotation.DisconnectCauses;
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
@@ -84,13 +85,17 @@
* Timeout before we continue with the emergency call without waiting for DDS switch response
* from the modem.
*/
- private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000;
+ 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. */
private static final long DEFAULT_ECM_EXIT_TIMEOUT_MS = 300000;
private static final int DEFAULT_EPDN_DISCONNECTION_TIMEOUT_MS = 500;
+ private static final int DEFAULT_TRANSPORT_CHANGE_TIMEOUT_MS = 1 * 1000;
+
/** The emergency types used when setting the emergency mode on modem. */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "EMERGENCY_TYPE_",
@@ -117,7 +122,7 @@
@EmergencyConstants.EmergencyMode
private int mEmergencyMode = MODE_EMERGENCY_NONE;
private boolean mWasEmergencyModeSetOnModem;
- private EmergencyRegResult mLastEmergencyRegResult;
+ private EmergencyRegistrationResult mLastEmergencyRegistrationResult;
private boolean mIsEmergencyModeInProgress;
private boolean mIsEmergencyCallStartedDuringEmergencySms;
@@ -126,13 +131,13 @@
// A runnable which is used to automatically exit from Ecm after a period of time.
private final Runnable mExitEcmRunnable = this::exitEmergencyCallbackMode;
// Tracks emergency calls by callId that have reached {@link Call.State#ACTIVE}.
- private final Set<String> mActiveEmergencyCalls = new ArraySet<>();
+ private final Set<android.telecom.Connection> mActiveEmergencyCalls = new ArraySet<>();
private Phone mPhoneToExit;
private int mPdnDisconnectionTimeoutMs = DEFAULT_EPDN_DISCONNECTION_TIMEOUT_MS;
private final Object mLock = new Object();
private Phone mPhone;
- // Tracks ongoing emergency callId to handle a second emergency call
- private String mOngoingCallId;
+ // Tracks ongoing emergency connection to handle a second emergency call
+ private android.telecom.Connection mOngoingConnection;
// Domain of the active emergency call. Assuming here that there will only be one domain active.
private int mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN;
private CompletableFuture<Integer> mCallEmergencyModeFuture;
@@ -148,6 +153,8 @@
private CompletableFuture<Integer> mSmsEmergencyModeFuture;
private boolean mIsTestEmergencyNumberForSms;
+ private CompletableFuture<Boolean> mEmergencyTransportChangedFuture;
+
private final android.util.ArrayMap<Integer, Boolean> mNoSimEcbmSupported =
new android.util.ArrayMap<>();
private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
@@ -286,14 +293,18 @@
Rlog.v(TAG, "MSG_SET_EMERGENCY_MODE_DONE for "
+ emergencyTypeToString(emergencyType));
if (ar.exception == null) {
- mLastEmergencyRegResult = (EmergencyRegResult) ar.result;
+ mLastEmergencyRegistrationResult = (EmergencyRegistrationResult) ar.result;
} else {
- mLastEmergencyRegResult = null;
- Rlog.w(TAG, "LastEmergencyRegResult not set. AsyncResult.exception: "
+ mLastEmergencyRegistrationResult = null;
+ Rlog.w(TAG,
+ "LastEmergencyRegistrationResult not set. AsyncResult.exception: "
+ ar.exception);
}
setEmergencyModeInProgress(false);
+ // Transport changed from WLAN to WWAN or CALLBACK to WWAN
+ maybeNotifyTransportChangeCompleted(emergencyType, false);
+
if (emergencyType == EMERGENCY_TYPE_CALL) {
setIsInEmergencyCall(true);
completeEmergencyMode(emergencyType);
@@ -496,24 +507,25 @@
* Handles turning on radio and switching DDS.
*
* @param phone the {@code Phone} on which to process the emergency call.
- * @param callId the call id on which to process the emergency call.
+ * @param c the {@code Connection} on which to process the emergency call.
* @param isTestEmergencyNumber whether this is a test emergency number.
* @return a {@code CompletableFuture} that results in {@code DisconnectCause.NOT_DISCONNECTED}
* if emergency call successfully started.
*/
public CompletableFuture<Integer> startEmergencyCall(@NonNull Phone phone,
- @NonNull String callId, boolean isTestEmergencyNumber) {
- Rlog.i(TAG, "startEmergencyCall: phoneId=" + phone.getPhoneId() + ", callId=" + callId);
+ @NonNull android.telecom.Connection c, boolean isTestEmergencyNumber) {
+ Rlog.i(TAG, "startEmergencyCall: phoneId=" + phone.getPhoneId()
+ + ", callId=" + c.getTelecomCallId());
if (mPhone != null) {
// Create new future to return as to not interfere with any uncompleted futures.
// 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())) {
- mOngoingCallId = callId;
+ mOngoingConnection = c;
mIsTestEmergencyNumber = isTestEmergencyNumber;
// Ensure that domain selector requests scan.
- mLastEmergencyRegResult = new EmergencyRegResult(
+ mLastEmergencyRegistrationResult = new EmergencyRegistrationResult(
AccessNetworkConstants.AccessNetworkType.UNKNOWN,
NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN,
NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", "");
@@ -545,13 +557,13 @@
}
mPhone = phone;
- mOngoingCallId = callId;
+ mOngoingConnection = c;
mIsTestEmergencyNumber = isTestEmergencyNumber;
return mCallEmergencyModeFuture;
}
mPhone = phone;
- mOngoingCallId = callId;
+ mOngoingConnection = c;
mIsTestEmergencyNumber = isTestEmergencyNumber;
turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber);
return mCallEmergencyModeFuture;
@@ -564,13 +576,13 @@
* Enter ECM only once all active emergency calls have ended. If a call never reached
* {@link Call.State#ACTIVE}, then no need to enter ECM.
*
- * @param callId the call id on which to end the emergency call.
+ * @param c the emergency call disconnected.
*/
- public void endCall(@NonNull String callId) {
- boolean wasActive = mActiveEmergencyCalls.remove(callId);
+ public void endCall(@NonNull android.telecom.Connection c) {
+ boolean wasActive = mActiveEmergencyCalls.remove(c);
- if (Objects.equals(mOngoingCallId, callId)) {
- mOngoingCallId = null;
+ if (Objects.equals(mOngoingConnection, c)) {
+ mOngoingConnection = null;
mOngoingCallProperties = 0;
}
@@ -578,11 +590,11 @@
&& isEmergencyCallbackModeSupported()) {
enterEmergencyCallbackMode();
- if (mOngoingCallId == null) {
+ if (mOngoingConnection == null) {
mIsEmergencyCallStartedDuringEmergencySms = false;
mCallEmergencyModeFuture = null;
}
- } else if (mOngoingCallId == null) {
+ } else if (mOngoingConnection == null) {
if (isInEcm()) {
mIsEmergencyCallStartedDuringEmergencySms = false;
mCallEmergencyModeFuture = null;
@@ -596,6 +608,9 @@
clearEmergencyCallInfo();
}
}
+
+ // Release any blocked thread immediately
+ maybeNotifyTransportChangeCompleted(EMERGENCY_TYPE_CALL, true);
}
private void clearEmergencyCallInfo() {
@@ -603,7 +618,7 @@
mIsTestEmergencyNumber = false;
mIsEmergencyCallStartedDuringEmergencySms = false;
mCallEmergencyModeFuture = null;
- mOngoingCallId = null;
+ mOngoingConnection = null;
mOngoingCallProperties = 0;
mPhone = null;
}
@@ -616,10 +631,10 @@
Rlog.e(TAG, "DDS Switch failed.");
}
// Once radio is on and DDS switched, must call setEmergencyMode() before selecting
- // emergency domain. EmergencyRegResult is required to determine domain and this is the
- // only API that can receive it before starting domain selection. Once domain selection
- // is finished, the actual emergency mode will be set when onEmergencyTransportChanged()
- // is called.
+ // emergency domain. EmergencyRegistrationResult is required to determine domain and
+ // this is the only API that can receive it before starting domain selection.
+ // Once domain selection is finished, the actual emergency mode will be set when
+ // onEmergencyTransportChanged() is called.
setEmergencyMode(phone, emergencyType, MODE_EMERGENCY_WWAN,
MSG_SET_EMERGENCY_MODE_DONE);
});
@@ -639,14 +654,15 @@
+ emergencyTypeToString(emergencyType));
if (mEmergencyMode == mode) {
+ // Initial transport selection of DomainSelector
+ maybeNotifyTransportChangeCompleted(emergencyType, false);
return;
}
mEmergencyMode = mode;
setEmergencyModeInProgress(true);
Message m = mHandler.obtainMessage(msg, Integer.valueOf(emergencyType));
- if ((mIsTestEmergencyNumber && emergencyType == EMERGENCY_TYPE_CALL)
- || (mIsTestEmergencyNumberForSms && emergencyType == EMERGENCY_TYPE_SMS)) {
+ if (mIsTestEmergencyNumberForSms && emergencyType == EMERGENCY_TYPE_SMS) {
Rlog.d(TAG, "TestEmergencyNumber for " + emergencyTypeToString(emergencyType)
+ ": Skipping setting emergency mode on modem.");
// Send back a response for the command, but with null information
@@ -808,9 +824,72 @@
}
}
- /** Returns last {@link EmergencyRegResult} as set by {@code setEmergencyMode()}. */
- public EmergencyRegResult getEmergencyRegResult() {
- return mLastEmergencyRegResult;
+ /** Returns last {@link EmergencyRegistrationResult} as set by {@code setEmergencyMode()}. */
+ public EmergencyRegistrationResult getEmergencyRegistrationResult() {
+ return mLastEmergencyRegistrationResult;
+ }
+
+ private void waitForTransportChangeCompleted(CompletableFuture<Boolean> future) {
+ if (future != null) {
+ synchronized (future) {
+ if ((mEmergencyMode == MODE_EMERGENCY_NONE)
+ || mHandler.getLooper().isCurrentThread()) {
+ // Do not block the Handler's thread
+ return;
+ }
+ long now = SystemClock.elapsedRealtime();
+ long deadline = now + DEFAULT_TRANSPORT_CHANGE_TIMEOUT_MS;
+ // Guard with while loop to handle spurious wakeups
+ while (!future.isDone() && now < deadline) {
+ try {
+ future.wait(deadline - now);
+ } catch (Exception e) {
+ Rlog.e(TAG, "waitForTransportChangeCompleted wait e=" + e);
+ }
+ now = SystemClock.elapsedRealtime();
+ }
+ }
+ }
+ }
+
+ private void maybeNotifyTransportChangeCompleted(@EmergencyType int emergencyType,
+ boolean enforced) {
+ if (emergencyType != EMERGENCY_TYPE_CALL) {
+ // It's not for the emergency call
+ return;
+ }
+ CompletableFuture<Boolean> future = mEmergencyTransportChangedFuture;
+ if (future != null) {
+ synchronized (future) {
+ if (!future.isDone()
+ && ((!isEmergencyModeInProgress() && mEmergencyMode == MODE_EMERGENCY_WWAN)
+ || enforced)) {
+ future.complete(Boolean.TRUE);
+ future.notifyAll();
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles emergency transport change by setting new emergency mode.
+ *
+ * @param emergencyType the emergency type to identify an emergency call or SMS
+ * @param mode the new emergency mode
+ */
+ public void onEmergencyTransportChangedAndWait(@EmergencyType int emergencyType,
+ @EmergencyConstants.EmergencyMode int mode) {
+ // Wait for the completion of setting MODE_EMERGENCY_WWAN only for emergency calls
+ if (emergencyType == EMERGENCY_TYPE_CALL && mode == MODE_EMERGENCY_WWAN) {
+ CompletableFuture<Boolean> future = new CompletableFuture<>();
+ synchronized (future) {
+ mEmergencyTransportChangedFuture = future;
+ onEmergencyTransportChanged(emergencyType, mode);
+ waitForTransportChangeCompleted(future);
+ }
+ return;
+ }
+ onEmergencyTransportChanged(emergencyType, mode);
}
/**
@@ -842,10 +921,10 @@
/**
* Notify the tracker that the emergency call domain has been updated.
* @param phoneType The new PHONE_TYPE_* of the call.
- * @param callId The ID of the call
+ * @param c The connection of the call
*/
- public void onEmergencyCallDomainUpdated(int phoneType, String callId) {
- Rlog.d(TAG, "domain update for callId: " + callId);
+ public void onEmergencyCallDomainUpdated(int phoneType, android.telecom.Connection c) {
+ Rlog.d(TAG, "domain update for callId: " + c.getTelecomCallId());
int domain = -1;
switch(phoneType) {
case (PhoneConstants.PHONE_TYPE_CDMA_LTE):
@@ -873,13 +952,13 @@
* Handles emergency call state change.
*
* @param state the new call state
- * @param callId the callId whose state has changed
+ * @param c the call whose state has changed
*/
- public void onEmergencyCallStateChanged(Call.State state, String callId) {
+ public void onEmergencyCallStateChanged(Call.State state, android.telecom.Connection c) {
if (state == Call.State.ACTIVE) {
- mActiveEmergencyCalls.add(callId);
- if (Objects.equals(mOngoingCallId, callId)) {
- Rlog.i(TAG, "call connected " + callId);
+ mActiveEmergencyCalls.add(c);
+ if (Objects.equals(mOngoingConnection, c)) {
+ Rlog.i(TAG, "call connected " + c.getTelecomCallId());
if (mPhone != null
&& isVoWiFi(mOngoingCallProperties)
&& mEmergencyMode == EmergencyConstants.MODE_EMERGENCY_WLAN) {
@@ -894,10 +973,10 @@
* Handles the change of emergency call properties.
*
* @param properties the new call properties.
- * @param callId the callId whose state has changed.
+ * @param c the call whose state has changed.
*/
- public void onEmergencyCallPropertiesChanged(int properties, String callId) {
- if (Objects.equals(mOngoingCallId, callId)) {
+ public void onEmergencyCallPropertiesChanged(int properties, android.telecom.Connection c) {
+ if (Objects.equals(mOngoingConnection, c)) {
mOngoingCallProperties = properties;
}
}
@@ -1160,7 +1239,7 @@
if (isInEcm()) {
// When the emergency mode is not in MODE_EMERGENCY_CALLBACK,
// it needs to notify the emergency callback mode to modem.
- if (mActiveEmergencyCalls.isEmpty() && mOngoingCallId == null) {
+ if (mActiveEmergencyCalls.isEmpty() && mOngoingConnection == null) {
setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK,
MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
}
@@ -1203,8 +1282,8 @@
*
* <p>
* Once radio is on and DDS switched, must call setEmergencyMode() before completing the future
- * and selecting emergency domain. EmergencyRegResult is required to determine domain and
- * setEmergencyMode() is the only API that can receive it before starting domain selection.
+ * and selecting emergency domain. EmergencyRegistrationResult is required to determine domain
+ * and setEmergencyMode() is the only API that can receive it before starting domain selection.
* Once domain selection is finished, the actual emergency mode will be set when
* onEmergencyTransportChanged() is called.
*
@@ -1227,6 +1306,11 @@
mRadioOnHelper = new RadioOnHelper(mContext);
}
+ final Phone phoneForEmergency = phone;
+ final android.telecom.Connection expectedConnection = mOngoingConnection;
+ final int waitForInServiceTimeout =
+ needToTurnOnRadio ? DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS : 0;
+ Rlog.i(TAG, "turnOnRadioAndSwitchDds: timeout=" + waitForInServiceTimeout);
mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
@Override
public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
@@ -1241,25 +1325,34 @@
completeEmergencyMode(emergencyType, DisconnectCause.POWER_OFF);
}
} else {
+ if (!Objects.equals(mOngoingConnection, expectedConnection)) {
+ Rlog.i(TAG, "onComplete "
+ + expectedConnection.getTelecomCallId() + " canceled.");
+ return;
+ }
switchDdsAndSetEmergencyMode(phone, emergencyType);
}
}
@Override
public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) {
- // We currently only look to make sure that the radio is on before dialing. We
- // should be able to make emergency calls at any time after the radio has been
- // powered on and isn't in the UNAVAILABLE state, even if it is reporting the
- // OUT_OF_SERVICE state.
+ // Wait for normal service state or timeout if required.
+ if (phone == phoneForEmergency
+ && waitForInServiceTimeout > 0
+ && !isNetworkRegistered(phone)) {
+ return false;
+ }
return phone.getServiceStateTracker().isRadioOn()
&& !satelliteController.isSatelliteEnabled();
}
@Override
public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) {
- return true;
+ // onTimeout shall be called only with the Phone for emergency
+ return phone.getServiceStateTracker().isRadioOn()
+ && !satelliteController.isSatelliteEnabled();
}
- }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, 0);
+ }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, waitForInServiceTimeout);
} else {
switchDdsAndSetEmergencyMode(phone, emergencyType);
}
@@ -1412,6 +1505,27 @@
|| phone.getServiceState().isEmergencyOnly();
}
+ private static boolean isNetworkRegistered(Phone phone) {
+ ServiceState ss = phone.getServiceStateTracker().getServiceState();
+ if (ss != null) {
+ NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (nri != null && nri.isNetworkRegistered()) {
+ // PS is IN_SERVICE state.
+ return true;
+ }
+ nri = ss.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (nri != null && nri.isNetworkRegistered()) {
+ // CS is IN_SERVICE state.
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Checks whether both {@code Phone}s are same or not.
*/
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
index 9c4ebfa..384112d 100644
--- a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
@@ -96,7 +96,7 @@
continue;
}
- int timeoutCallbackInterval = (forEmergencyCall && phone == phoneForEmergencyCall)
+ int timeoutCallbackInterval = (phone == phoneForEmergencyCall)
? emergencyTimeoutIntervalMillis : 0;
mInProgressListeners.add(mListeners.get(i));
mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall, forEmergencyCall
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index c417a34..3c444ef 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -47,6 +47,7 @@
import android.service.euicc.IEraseSubscriptionsCallback;
import android.service.euicc.IEuiccService;
import android.service.euicc.IEuiccServiceDumpResultCallback;
+import android.service.euicc.IGetAvailableMemoryInBytesCallback;
import android.service.euicc.IGetDefaultDownloadableSubscriptionListCallback;
import android.service.euicc.IGetDownloadableSubscriptionMetadataCallback;
import android.service.euicc.IGetEidCallback;
@@ -155,6 +156,7 @@
private static final int CMD_START_OTA_IF_NECESSARY = 112;
private static final int CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS = 113;
private static final int CMD_DUMP_EUICC_SERVICE = 114;
+ private static final int CMD_GET_AVAILABLE_MEMORY_IN_BYTES = 115;
private static boolean isEuiccCommand(int what) {
return what >= CMD_GET_EID;
@@ -208,6 +210,13 @@
void onGetEidComplete(String eid);
}
+ /** Callback class for {@link #getAvailableMemoryInBytes}. */
+ @VisibleForTesting(visibility = PACKAGE)
+ public interface GetAvailableMemoryInBytesCommandCallback extends BaseEuiccCommandCallback {
+ /** Called when the available memory in bytes lookup has completed. */
+ void onGetAvailableMemoryInBytesComplete(long availableMemoryInBytes);
+ }
+
/** Callback class for {@link #getOtaStatus}. */
@VisibleForTesting(visibility = PACKAGE)
public interface GetOtaStatusCommandCallback extends BaseEuiccCommandCallback {
@@ -436,6 +445,13 @@
sendMessage(CMD_GET_EID, cardId, 0 /* arg2 */, callback);
}
+ /** Asynchronously fetch the available memory in bytes. */
+ @VisibleForTesting(visibility = PACKAGE)
+ public void getAvailableMemoryInBytes(
+ int cardId, GetAvailableMemoryInBytesCommandCallback callback) {
+ sendMessage(CMD_GET_AVAILABLE_MEMORY_IN_BYTES, cardId, 0 /* arg2 */, callback);
+ }
+
/** Asynchronously get OTA status. */
@VisibleForTesting(visibility = PACKAGE)
public void getOtaStatus(int cardId, GetOtaStatusCommandCallback callback) {
@@ -760,6 +776,22 @@
});
break;
}
+ case CMD_GET_AVAILABLE_MEMORY_IN_BYTES: {
+ mEuiccService.getAvailableMemoryInBytes(slotId,
+ new IGetAvailableMemoryInBytesCallback.Stub() {
+ @Override
+ public void onSuccess(long availableMemoryInBytes) {
+ sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
+ ((GetAvailableMemoryInBytesCommandCallback)
+ callback)
+ .onGetAvailableMemoryInBytesComplete(
+ availableMemoryInBytes);
+ onCommandEnd(callback);
+ });
+ }
+ });
+ break;
+ }
case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA: {
GetMetadataRequest request = (GetMetadataRequest) message.obj;
mEuiccService.getDownloadableSubscriptionMetadata(slotId,
@@ -1036,6 +1068,7 @@
case CMD_GET_OTA_STATUS:
case CMD_START_OTA_IF_NECESSARY:
case CMD_DUMP_EUICC_SERVICE:
+ case CMD_GET_AVAILABLE_MEMORY_IN_BYTES:
return (BaseEuiccCommandCallback) message.obj;
case CMD_GET_DOWNLOADABLE_SUBSCRIPTION_METADATA:
return ((GetMetadataRequest) message.obj).mCallback;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index a24ab43..6dd6f65 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -24,6 +24,8 @@
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.flags.Flags;
import android.app.compat.CompatChanges;
import android.content.ComponentName;
import android.content.Context;
@@ -33,6 +35,8 @@
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.service.euicc.DownloadSubscriptionResult;
import android.service.euicc.EuiccService;
@@ -54,6 +58,7 @@
import android.telephony.euicc.EuiccManager;
import android.telephony.euicc.EuiccManager.OtaStatus;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
@@ -72,13 +77,16 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.Stack;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
/** Backing implementation of {@link android.telephony.euicc.EuiccManager}. */
public class EuiccController extends IEuiccController.Stub {
@@ -121,6 +129,7 @@
// the phone process, 3) values are updated remotely by server flags.
private List<String> mSupportedCountries;
private List<String> mUnsupportedCountries;
+ private List<Integer> mPsimConversionSupportedCarrierIds;
/** Initialize the instance. Should only be called once. */
public static EuiccController init(Context context, FeatureFlags featureFlags) {
@@ -251,6 +260,36 @@
}
/**
+ * Return the available memory in bytes of the eUICC.
+ *
+ * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load,
+ * that IPC should generally be fast, and the available memory shouldn't be needed in the normal
+ * course of operation.
+ */
+ @Override
+ public long getAvailableMemoryInBytes(int cardId, String callingPackage) {
+ boolean callerCanReadPhoneStatePrivileged = callerCanReadPhoneStatePrivileged();
+ boolean callerCanReadPhoneState = callerCanReadPhoneState();
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
+ long token = Binder.clearCallingIdentity();
+ try {
+ if (!callerCanReadPhoneStatePrivileged
+ && !callerCanReadPhoneState
+ && !canManageSubscriptionOnTargetSim(
+ cardId, callingPackage, false, TelephonyManager.INVALID_PORT_INDEX)) {
+ throw new SecurityException(
+ "Must have READ_PHONE_STATE permission or READ_PRIVILEGED_PHONE_STATE"
+ + " permission or carrier privileges to read the available memory for"
+ + "cardId="
+ + cardId);
+ }
+ return blockingGetAvailableMemoryInBytesFromEuiccService(cardId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
* Return the current status of OTA update.
*
* <p>For API simplicity, this call blocks until completion; while it requires an IPC to load,
@@ -577,6 +616,19 @@
boolean switchAfterDownload, String callingPackage, boolean forceDeactivateSim,
Bundle resolvedBundle, PendingIntent callbackIntent) {
boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
+ boolean callerCanDownloadAdminManagedSubscription =
+ Flags.esimManagementEnabled()
+ && callerCanManageDevicePolicyManagedSubscriptions(callingPackage);
+ if (Flags.esimManagementEnabled()) {
+ if (mContext
+ .getSystemService(UserManager.class)
+ .hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY)
+ && !callerCanDownloadAdminManagedSubscription) {
+ // Only admin managed subscriptions are allowed, but the caller is not authorised to
+ // download admin managed subscriptions. Abort.
+ throw new SecurityException("Caller is not authorized to download subscriptions");
+ }
+ }
mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
// Don't try to resolve the port index for apps which are not targeting on T for backward
// compatibility. instead always use default port 0.
@@ -594,18 +646,27 @@
isConsentNeededToResolvePortIndex = (portIndex
== TelephonyManager.INVALID_PORT_INDEX);
}
+ // Caller has admin privileges if they can download admin managed subscription,
+ // and are not switching the subscription after download (admins cannot silently
+ // enable the subscription).
+ boolean hasAdminPrivileges =
+ callerCanDownloadAdminManagedSubscription && !switchAfterDownload;
Log.d(TAG, " downloadSubscription cardId: " + cardId + " switchAfterDownload: "
+ switchAfterDownload + " portIndex: " + portIndex
+ " forceDeactivateSim: " + forceDeactivateSim + " callingPackage: "
+ callingPackage
+ " isConsentNeededToResolvePortIndex: " + isConsentNeededToResolvePortIndex
- + " shouldResolvePortIndex:" + shouldResolvePortIndex);
- if (!isConsentNeededToResolvePortIndex && callerCanWriteEmbeddedSubscriptions) {
+ + " shouldResolvePortIndex:" + shouldResolvePortIndex
+ + " hasAdminPrivileges:" + hasAdminPrivileges);
+ if (!isConsentNeededToResolvePortIndex
+ && (callerCanWriteEmbeddedSubscriptions
+ || hasAdminPrivileges)) {
// With WRITE_EMBEDDED_SUBSCRIPTIONS, we can skip profile-specific permission checks
// and move straight to the profile download.
downloadSubscriptionPrivileged(cardId, portIndex, token, subscription,
switchAfterDownload, forceDeactivateSim, callingPackage, resolvedBundle,
- callbackIntent);
+ callbackIntent, callerCanDownloadAdminManagedSubscription,
+ getCurrentEmbeddedSubscriptionIds(cardId));
return;
}
@@ -741,11 +802,30 @@
true /* withUserConsent */, portIndex));
}
+ void downloadSubscriptionPrivileged(int cardId, int portIndex, final long callingToken,
+ DownloadableSubscription subscription, boolean switchAfterDownload,
+ boolean forceDeactivateSim, final String callingPackage,
+ Bundle resolvedBundle, final PendingIntent callbackIntent) {
+ downloadSubscriptionPrivileged(
+ cardId,
+ portIndex,
+ callingToken,
+ subscription,
+ switchAfterDownload,
+ forceDeactivateSim,
+ callingPackage,
+ resolvedBundle,
+ callbackIntent,
+ false /* markAsOwnedByAdmin */,
+ new ArraySet<>() /* existingSubscriptions */);
+ }
+
// Continue to download subscription without checking anything.
void downloadSubscriptionPrivileged(int cardId, int portIndex, final long callingToken,
DownloadableSubscription subscription, boolean switchAfterDownload,
boolean forceDeactivateSim, final String callingPackage, Bundle resolvedBundle,
- final PendingIntent callbackIntent) {
+ final PendingIntent callbackIntent, boolean markAsOwnedByAdmin,
+ Set<Integer> existingSubscriptions) {
mConnector.downloadSubscription(
cardId,
portIndex,
@@ -774,7 +854,13 @@
// Since we're not switching, nothing will trigger a
// subscription list refresh on its own, so request one here.
refreshSubscriptionsAndSendResult(
- callbackIntent, resultCode, extrasIntent);
+ callbackIntent,
+ resultCode,
+ extrasIntent,
+ markAsOwnedByAdmin,
+ callingPackage,
+ cardId,
+ existingSubscriptions);
return;
}
break;
@@ -976,6 +1062,9 @@
public void deleteSubscription(int cardId, int subscriptionId, String callingPackage,
PendingIntent callbackIntent) {
boolean callerCanWriteEmbeddedSubscriptions = callerCanWriteEmbeddedSubscriptions();
+ boolean callerIsAdmin =
+ Flags.esimManagementEnabled()
+ && callerCanManageDevicePolicyManagedSubscriptions(callingPackage);
mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
long token = Binder.clearCallingIdentity();
@@ -986,13 +1075,14 @@
sendResult(callbackIntent, ERROR, null /* extrasIntent */);
return;
}
-
+ boolean adminOwned = callerIsAdmin && sub.getGroupOwner().equals(callingPackage);
// For both single active SIM device and multi-active SIM device, if the caller is
// 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)) {
- Log.e(TAG, "No permissions: " + subscriptionId);
+ && !mSubscriptionManager.canManageSubscription(sub, callingPackage)
+ && !adminOwned) {
+ Log.e(TAG, "No permissions: " + subscriptionId + " adminOwned=" + adminOwned);
sendResult(callbackIntent, ERROR, null /* extrasIntent */);
return;
}
@@ -1610,9 +1700,43 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public void refreshSubscriptionsAndSendResult(
PendingIntent callbackIntent, int resultCode, Intent extrasIntent) {
+ refreshSubscriptionsAndSendResult(
+ callbackIntent,
+ resultCode,
+ extrasIntent,
+ /* isCallerAdmin= */ false,
+ /* callingPackage= */ "",
+ /* cardId */ -1,
+ /* subscriptionsBefore= */ new ArraySet<>());
+ }
+
+ /** Refresh the embedded subscription list and dispatch the given result upon completion. */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void refreshSubscriptionsAndSendResult(
+ PendingIntent callbackIntent,
+ int resultCode,
+ Intent extrasIntent,
+ boolean isCallerAdmin,
+ String callingPackage,
+ int cardId,
+ Set<Integer> subscriptionsBefore) {
SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions(
List.of(mTelephonyManager.getCardIdForDefaultEuicc()),
- () -> sendResult(callbackIntent, resultCode, extrasIntent));
+ () -> {
+ if (Flags.esimManagementEnabled() && isCallerAdmin) {
+ // Mark the newly downloaded subscriptions as being owned by an admin so
+ // that actions for that subscription can be restricted,
+ // and the admin is limited to effecting only these subscriptions.
+ Set<Integer> subscriptionsAfter = getCurrentEmbeddedSubscriptionIds(cardId);
+ subscriptionsAfter.removeAll(subscriptionsBefore);
+ for (int subId: subscriptionsAfter) {
+ SubscriptionManagerService
+ .getInstance().setGroupOwner(subId, callingPackage);
+ }
+ }
+ sendResult(callbackIntent, resultCode, extrasIntent);
+ });
+
}
/** Dispatch the given callback intent with the given result code and data. */
@@ -1728,6 +1852,23 @@
return null;
}
+ private Set<Integer> getCurrentEmbeddedSubscriptionIds(int cardId) {
+ if (!Flags.esimManagementEnabled()) {
+ return new ArraySet<>();
+ }
+ List<SubscriptionInfo> subscriptionInfos =
+ mSubscriptionManager.getAvailableSubscriptionInfoList();
+ int subCount = (subscriptionInfos != null) ? subscriptionInfos.size() : 0;
+ Set<Integer> currentEmbeddedSubscriptionIds = new ArraySet<>();
+ for (int i = 0; i < subCount; i++) {
+ SubscriptionInfo subscriptionInfo = subscriptionInfos.get(i);
+ if (subscriptionInfo.isEmbedded() && subscriptionInfo.getCardId() == cardId) {
+ currentEmbeddedSubscriptionIds.add(subscriptionInfo.getSubscriptionId());
+ }
+ }
+ return currentEmbeddedSubscriptionIds;
+ }
+
@Nullable
private String blockingGetEidFromEuiccService(int cardId) {
CountDownLatch latch = new CountDownLatch(1);
@@ -1747,6 +1888,27 @@
return awaitResult(latch, eidRef);
}
+ private long blockingGetAvailableMemoryInBytesFromEuiccService(int cardId) {
+ CountDownLatch latch = new CountDownLatch(1);
+ AtomicReference<Long> memoryRef =
+ new AtomicReference<>(EuiccManager.EUICC_MEMORY_FIELD_UNAVAILABLE);
+ mConnector.getAvailableMemoryInBytes(
+ cardId,
+ new EuiccConnector.GetAvailableMemoryInBytesCommandCallback() {
+ @Override
+ public void onGetAvailableMemoryInBytesComplete(long availableMemoryInBytes) {
+ memoryRef.set(availableMemoryInBytes);
+ latch.countDown();
+ }
+
+ @Override
+ public void onEuiccServiceUnavailable() {
+ latch.countDown();
+ }
+ });
+ return awaitResult(latch, memoryRef);
+ }
+
private @OtaStatus int blockingGetOtaStatusFromEuiccService(int cardId) {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Integer> statusRef =
@@ -1954,12 +2116,49 @@
== PackageManager.PERMISSION_GRANTED;
}
+ private boolean callerCanReadPhoneState() {
+ return mContext.checkCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
private boolean callerCanWriteEmbeddedSubscriptions() {
return mContext.checkCallingOrSelfPermission(
Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
== PackageManager.PERMISSION_GRANTED;
}
+ private DevicePolicyManager retrieveDevicePolicyManagerFromUserContext(UserHandle userHandle) {
+ Context userContext;
+ long ident = Binder.clearCallingIdentity();
+ try {
+ userContext = mContext.createPackageContextAsUser(
+ mContext.getPackageName(), /* flags= */ 0, userHandle);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unknown package name");
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return userContext.getSystemService(DevicePolicyManager.class);
+ }
+
+ private boolean callerCanManageDevicePolicyManagedSubscriptions(String callingPackage) {
+ // isProfileOwner/isDeviceOwner needs to callers user, so create device policy manager
+ // with the correct context associated with the caller.
+ DevicePolicyManager devicePolicyManager =
+ retrieveDevicePolicyManagerFromUserContext(Binder.getCallingUserHandle());
+ if (devicePolicyManager == null) {
+ Log.w(TAG, "Unable to get device policy manager");
+ return false;
+ }
+ boolean isAdmin =
+ devicePolicyManager.isProfileOwnerApp(callingPackage)
+ || devicePolicyManager.isDeviceOwnerApp(callingPackage);
+ return isAdmin || mContext.checkCallingOrSelfPermission(
+ Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage) {
mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage);
@@ -2073,6 +2272,34 @@
return changeEnabled;
}
+
+ @Override
+ public void setPsimConversionSupportedCarriers(int[] carrierIds) {
+ if (!callerCanWriteEmbeddedSubscriptions()) {
+ throw new SecurityException(
+ "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to "
+ + "set pSIM conversion supported carriers");
+ }
+ mPsimConversionSupportedCarrierIds = Arrays.stream(carrierIds).boxed()
+ .collect(Collectors.toList());
+ }
+
+
+
+ @Override
+ public boolean isPsimConversionSupported(int carrierId) {
+ if (!callerCanWriteEmbeddedSubscriptions()) {
+ throw new SecurityException(
+ "Must have WRITE_EMBEDDED_SUBSCRIPTIONS "
+ + "to check if the carrier is supported pSIM conversion");
+ }
+ if (mPsimConversionSupportedCarrierIds == null
+ || mPsimConversionSupportedCarrierIds.isEmpty()) {
+ return false;
+ }
+ return mPsimConversionSupportedCarrierIds.contains(carrierId);
+ }
+
/**
* Make sure the device has required telephony feature
*
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java b/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
index 6554077..234723f 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsNrSaModeHandler.java
@@ -263,9 +263,10 @@
PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId(),
KEY_NR_SA_DISABLE_POLICY_INT, KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
mNrSaDisablePolicy = bundle.getInt(KEY_NR_SA_DISABLE_POLICY_INT);
- mIsNrSaSupported = Arrays.stream(
- bundle.getIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY)).anyMatch(
- value -> value == CARRIER_NR_AVAILABILITY_SA);
+ int[] nrAvailabilities = bundle.getIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
+ mIsNrSaSupported = nrAvailabilities != null
+ && Arrays.stream(nrAvailabilities).anyMatch(
+ value -> value == CARRIER_NR_AVAILABILITY_SA);
Log.d(TAG, "setNrSaDisablePolicy : NrSaDisablePolicy = "
+ mNrSaDisablePolicy + ", IsNrSaSupported = " + mIsNrSaSupported);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 9f3ec3b..f4f632e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -21,10 +21,10 @@
import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED;
import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_NONE;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS;
import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT;
import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_RAT_BLOCK;
-import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
@@ -49,7 +49,6 @@
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.Notification;
@@ -124,7 +123,6 @@
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.flags.FeatureFlags;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.metrics.ImsStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
@@ -2561,7 +2559,6 @@
/** Clear the IMS phone number from IMS associated Uris when IMS registration is lost. */
@VisibleForTesting
- @FlaggedApi(Flags.FLAG_CLEAR_CACHED_IMS_PHONE_NUMBER_WHEN_DEVICE_LOST_IMS_REGISTRATION)
public void clearPhoneNumberForSourceIms() {
int subId = getSubId();
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -2832,6 +2829,15 @@
mCT.triggerNotifyAnbr(mediaType, direction, bitsPerSecond);
}
+ /**
+ * Check whether making a call using Wi-Fi is possible or not.
+ * @return {code true} if IMS is registered over IWLAN else return {code false}.
+ */
+ public boolean canMakeWifiCall() {
+ return isImsRegistered() && (getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index e95433c..b5a052d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -1259,8 +1259,6 @@
}
}
- private @NonNull final FeatureFlags mFeatureFlags;
-
//***** Events
@@ -1273,8 +1271,9 @@
@VisibleForTesting
public ImsPhoneCallTracker(ImsPhone phone, ConnectorFactory factory, Executor executor,
FeatureFlags featureFlags) {
+ super(featureFlags);
+
this.mPhone = phone;
- mFeatureFlags = featureFlags;
mConnectorFactory = factory;
if (executor != null) {
mExecutor = executor;
@@ -4453,13 +4452,15 @@
public void onCallQualityChanged(ImsCall imsCall, CallQuality callQuality) {
// convert ServiceState.radioTech to TelephonyManager.NetworkType constant
mPhone.onCallQualityChanged(callQuality, imsCall.getNetworkType());
- String callId = imsCall.getSession().getCallId();
- CallQualityMetrics cqm = mCallQualityMetrics.get(callId);
- if (cqm == null) {
- cqm = new CallQualityMetrics(mPhone);
+ if (imsCall.getSession() != null) {
+ String callId = imsCall.getSession().getCallId();
+ CallQualityMetrics cqm = mCallQualityMetrics.get(callId);
+ if (cqm == null) {
+ cqm = new CallQualityMetrics(mPhone);
+ }
+ cqm.saveCallQuality(callQuality);
+ mCallQualityMetrics.put(callId, cqm);
}
- cqm.saveCallQuality(callQuality);
- mCallQualityMetrics.put(callId, cqm);
ImsPhoneConnection conn = findConnection(imsCall);
if (conn != null) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 104c925..a71355d 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -1357,17 +1357,15 @@
* @param imsCall The call to check for changes in extras.
* @return Whether the extras fields have been changed.
*/
- boolean updateExtras(ImsCall imsCall) {
+ boolean updateExtras(ImsCall imsCall) {
if (imsCall == null) {
return false;
}
-
final ImsCallProfile callProfile = imsCall.getCallProfile();
final Bundle extras = callProfile != null ? callProfile.mCallExtras : null;
if (extras == null && DBG) {
Rlog.d(LOG_TAG, "Call profile extras are null.");
}
-
final boolean changed = !areBundlesEqual(extras, mExtras);
if (changed) {
updateImsCallRatFromExtras(extras);
@@ -1377,11 +1375,44 @@
if (extras != null) {
mExtras.putAll(extras);
}
+ if (com.android.server.telecom.flags.Flags.businessCallComposer()) {
+ maybeInjectBusinessComposerExtras(mExtras);
+ }
setConnectionExtras(mExtras);
}
return changed;
}
+ /**
+ * The Ims Vendor is responsible for setting the ImsCallProfile business call composer
+ * values (ImsCallProfile.EXTRA_IS_BUSINESS_CALL and
+ * ImsCallProfile.EXTRA_ASSERTED_DISPLAY_NAME). This helper notifies Telecom of the business
+ * composer values which will then be injected into the android.telecom.Call object.
+ */
+ private void maybeInjectBusinessComposerExtras(Bundle extras) {
+ if (extras == null) {
+ return;
+ }
+ try {
+ if (extras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL)
+ && !extras.containsKey(android.telecom.Call.EXTRA_IS_BUSINESS_CALL)) {
+ boolean v = extras.getBoolean(ImsCallProfile.EXTRA_IS_BUSINESS_CALL);
+ Rlog.i(LOG_TAG, String.format("mIBCE: EXTRA_IS_BUSINESS_CALL=[%s]", v));
+ extras.putBoolean(android.telecom.Call.EXTRA_IS_BUSINESS_CALL, v);
+ }
+
+ if (extras.containsKey(ImsCallProfile.EXTRA_ASSERTED_DISPLAY_NAME)
+ && !extras.containsKey(android.telecom.Call.EXTRA_ASSERTED_DISPLAY_NAME)) {
+ String v = extras.getString(ImsCallProfile.EXTRA_ASSERTED_DISPLAY_NAME);
+ Rlog.i(LOG_TAG, String.format("mIBCE: ASSERTED_DISPLAY_NAME=[%s]", v));
+ extras.putString(android.telecom.Call.EXTRA_ASSERTED_DISPLAY_NAME, v);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
if (extras == null || newExtras == null) {
return extras == newExtras;
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index cc80a2e..a315f1e 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -1125,7 +1125,7 @@
roundAndConvertMillisToSeconds(stats.registeringMillis),
roundAndConvertMillisToSeconds(stats.unregisteredMillis),
stats.isIwlanCrossSim,
- roundAndConvertMillisToSeconds(stats.registeredTimes));
+ stats.registeredTimes);
}
private static StatsEvent buildStatsEvent(ImsRegistrationTermination termination) {
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 4a1fdb9..f3fe8fa 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -2291,8 +2291,6 @@
normalizeDurationTo24H(stats[i].registeringMillis, intervalMillis);
stats[i].unregisteredMillis =
normalizeDurationTo24H(stats[i].unregisteredMillis, intervalMillis);
- stats[i].registeredTimes =
- normalizeDurationTo24H(stats[i].registeredTimes, intervalMillis);
}
return stats;
}
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
index 0c68d4b..877eaf1 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramController.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -18,6 +18,8 @@
import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_IDLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF;
import android.annotation.NonNull;
import android.content.Context;
@@ -29,6 +31,7 @@
import android.telephony.satellite.SatelliteDatagram;
import android.telephony.satellite.SatelliteManager;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -50,8 +53,12 @@
public static final long MAX_DATAGRAM_ID = (long) Math.pow(2, 16);
public static final int ROUNDING_UNIT = 10;
public static final long SATELLITE_ALIGN_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
- public static final long DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT =
- TimeUnit.SECONDS.toMillis(60);
+ /** This type is used by CTS to override the satellite align timeout */
+ public static final int TIMEOUT_TYPE_ALIGN = 1;
+ /** This type is used by CTS to override the time to wait for connected state */
+ public static final int TIMEOUT_TYPE_DATAGRAM_WAIT_FOR_CONNECTED_STATE = 2;
+ /** This type is used by CTS to override the time to wait for response of the send request */
+ public static final int TIMEOUT_TYPE_WAIT_FOR_DATAGRAM_SENDING_RESPONSE = 3;
private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
private static final boolean DEBUG = !"user".equals(Build.TYPE);
@@ -80,7 +87,8 @@
private SatelliteDatagram mDemoModeDatagram;
private boolean mIsDemoMode = false;
private long mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT;
- private long mDatagramWaitTimeForConnectedState = DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT;
+ private long mDatagramWaitTimeForConnectedState;
+ private long mModemImageSwitchingDuration;
@GuardedBy("mLock")
@SatelliteManager.SatelliteModemState
private int mSatelltieModemState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
@@ -132,6 +140,9 @@
// Create the DatagramReceiver singleton,
// which is used to receive satellite datagrams.
mDatagramReceiver = DatagramReceiver.make(mContext, looper, this);
+
+ mDatagramWaitTimeForConnectedState = getDatagramWaitForConnectedStateTimeoutMillis();
+ mModemImageSwitchingDuration = getSatelliteModemImageSwitchingDurationMillis();
}
/**
@@ -367,25 +378,51 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public long getDatagramWaitTimeForConnectedState() {
- return mDatagramWaitTimeForConnectedState;
+ synchronized (mLock) {
+ if (mSatelltieModemState == SATELLITE_MODEM_STATE_OFF
+ || mSatelltieModemState == SATELLITE_MODEM_STATE_IDLE) {
+ return (mDatagramWaitTimeForConnectedState + mModemImageSwitchingDuration);
+ }
+ return mDatagramWaitTimeForConnectedState;
+ }
}
/**
- * This API can be used by only CTS to update the timeout duration in milliseconds whether
- * the device is aligned with the satellite for demo mode
+ * This API can be used by only CTS to timeout durations used by DatagramController module.
*
* @param timeoutMillis The timeout duration in millisecond.
* @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
*/
- boolean setSatelliteDeviceAlignedTimeoutDuration(long timeoutMillis) {
+ boolean setDatagramControllerTimeoutDuration(
+ boolean reset, int timeoutType, long timeoutMillis) {
if (!isMockModemAllowed()) {
- loge("Updating align timeout duration is not allowed");
+ loge("Updating timeout duration is not allowed");
return false;
}
- logd("setSatelliteDeviceAlignedTimeoutDuration: timeoutMillis=" + timeoutMillis);
- mAlignTimeoutDuration = timeoutMillis;
- mDatagramWaitTimeForConnectedState = timeoutMillis;
+ logd("setDatagramControllerTimeoutDuration: timeoutMillis=" + timeoutMillis
+ + ", reset=" + reset + ", timeoutType=" + timeoutType);
+ if (timeoutType == TIMEOUT_TYPE_ALIGN) {
+ if (reset) {
+ mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT;
+ } else {
+ mAlignTimeoutDuration = timeoutMillis;
+ }
+ } else if (timeoutType == TIMEOUT_TYPE_DATAGRAM_WAIT_FOR_CONNECTED_STATE) {
+ if (reset) {
+ mDatagramWaitTimeForConnectedState =
+ getDatagramWaitForConnectedStateTimeoutMillis();
+ mModemImageSwitchingDuration = getSatelliteModemImageSwitchingDurationMillis();
+ } else {
+ mDatagramWaitTimeForConnectedState = timeoutMillis;
+ mModemImageSwitchingDuration = 0;
+ }
+ } else if (timeoutType == TIMEOUT_TYPE_WAIT_FOR_DATAGRAM_SENDING_RESPONSE) {
+ mDatagramDispatcher.setWaitTimeForDatagramSendingResponse(reset, timeoutMillis);
+ } else {
+ loge("Invalid timeout type " + timeoutType);
+ return false;
+ }
return true;
}
@@ -404,6 +441,16 @@
}
}
+ private long getDatagramWaitForConnectedStateTimeoutMillis() {
+ return mContext.getResources().getInteger(
+ R.integer.config_datagram_wait_for_connected_state_timeout_millis);
+ }
+
+ private long getSatelliteModemImageSwitchingDurationMillis() {
+ return mContext.getResources().getInteger(
+ R.integer.config_satellite_modem_image_switching_duration_millis);
+ }
+
/**
* This API can be used by only CTS to override the cached value for the device overlay config
* value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index ae4c1f2..5cac1dd 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.satellite;
import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT;
@@ -58,6 +59,8 @@
private static final int EVENT_SEND_SATELLITE_DATAGRAM_DONE = 2;
private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3;
private static final int EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT = 4;
+ private static final int EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT = 5;
+ private static final int EVENT_ABORT_SENDING_SATELLITE_DATAGRAMS_DONE = 6;
@NonNull private static DatagramDispatcher sInstance;
@NonNull private final Context mContext;
@@ -93,6 +96,8 @@
private final LinkedHashMap<Long, SendSatelliteDatagramArgument>
mPendingNonEmergencyDatagramsMap = new LinkedHashMap<>();
+ private long mWaitTimeForDatagramSendingResponse;
+
/**
* Create the DatagramDispatcher singleton instance.
* @param context The Context to use to create the DatagramDispatcher.
@@ -126,6 +131,7 @@
synchronized (mLock) {
mSendingDatagramInProgress = false;
}
+ mWaitTimeForDatagramSendingResponse = getWaitForDatagramSendingResponseTimeoutMillis();
}
private static final class DatagramDispatcherHandlerRequest {
@@ -196,13 +202,17 @@
(SendSatelliteDatagramArgument) request.argument;
onCompleted = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request);
- if (mIsDemoMode && !shouldSendDatagramToModemInDemoMode()) {
- AsyncResult.forMessage(onCompleted, SATELLITE_RESULT_SUCCESS, null);
- onCompleted.sendToTarget();
- } else {
- SatelliteModemInterface.getInstance().sendSatelliteDatagram(argument.datagram,
- argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
- argument.needFullScreenPointingUI, onCompleted);
+ synchronized (mLock) {
+ if (mIsDemoMode && !shouldSendDatagramToModemInDemoMode()) {
+ AsyncResult.forMessage(onCompleted, SATELLITE_RESULT_SUCCESS, null);
+ onCompleted.sendToTarget();
+ } else {
+ SatelliteModemInterface.getInstance().sendSatelliteDatagram(
+ argument.datagram,
+ argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+ argument.needFullScreenPointingUI, onCompleted);
+ startWaitForDatagramSendingResponseTimer(argument);
+ }
}
break;
}
@@ -223,13 +233,25 @@
break;
}
}
-
logd("EVENT_SEND_SATELLITE_DATAGRAM_DONE error: " + error);
- // log metrics about the outgoing datagram
- reportSendDatagramCompleted(argument, error);
+ /*
+ * The response should be ignored if either of the following hold
+ * 1) Framework has already received this response from the vendor service.
+ * 2) Framework has timed out to wait for the response from vendor service for
+ * the send request.
+ * 3) All pending send requests have been aborted due to some error.
+ */
+ if (!shouldProcessEventSendSatelliteDatagramDone(argument)) {
+ logw("The message " + argument.datagramId + " was already processed");
+ break;
+ }
+
+ stopWaitForDatagramSendingResponseTimer();
mSendingDatagramInProgress = false;
+ // Log metrics about the outgoing datagram
+ reportSendDatagramCompleted(argument, error);
// Remove current datagram from pending map.
if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
mPendingEmergencyDatagramsMap.remove(argument.datagramId);
@@ -278,6 +300,11 @@
break;
}
+ case EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT:
+ handleEventWaitForDatagramSendingResponseTimedOut(
+ (SendSatelliteDatagramArgument) msg.obj);
+ break;
+
case EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT: {
handleEventSatelliteAlignedTimeout((DatagramDispatcherHandlerRequest) msg.obj);
break;
@@ -596,6 +623,7 @@
stopSatelliteAlignedTimer();
stopDatagramWaitForConnectedStateTimer();
+ stopWaitForDatagramSendingResponseTimer();
mIsDemoMode = false;
mSendSatelliteDatagramRequest = null;
mIsAligned = false;
@@ -620,6 +648,32 @@
return hasMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT);
}
+ /**
+ * This API is used by CTS tests to override the mWaitTimeForDatagramSendingResponse.
+ */
+ void setWaitTimeForDatagramSendingResponse(boolean reset, long timeoutMillis) {
+ if (reset) {
+ mWaitTimeForDatagramSendingResponse = getWaitForDatagramSendingResponseTimeoutMillis();
+ } else {
+ mWaitTimeForDatagramSendingResponse = timeoutMillis;
+ }
+ }
+
+ private void startWaitForDatagramSendingResponseTimer(
+ @NonNull SendSatelliteDatagramArgument argument) {
+ if (hasMessages(EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT)) {
+ logd("WaitForDatagramSendingResponseTimer was already started");
+ return;
+ }
+ sendMessageDelayed(obtainMessage(
+ EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT, argument),
+ mWaitTimeForDatagramSendingResponse);
+ }
+
+ private void stopWaitForDatagramSendingResponseTimer() {
+ removeMessages(EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT);
+ }
+
private void handleEventDatagramWaitForConnectedStateTimedOut() {
logw("Timed out to wait for satellite connected before sending datagrams");
synchronized (mLock) {
@@ -654,6 +708,61 @@
}
}
+ private long getWaitForDatagramSendingResponseTimeoutMillis() {
+ return mContext.getResources().getInteger(
+ R.integer.config_wait_for_datagram_sending_response_timeout_millis);
+ }
+
+ private boolean shouldProcessEventSendSatelliteDatagramDone(
+ @NonNull SendSatelliteDatagramArgument argument) {
+ synchronized (mLock) {
+ if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+ return mPendingEmergencyDatagramsMap.containsKey(argument.datagramId);
+ } else {
+ return mPendingNonEmergencyDatagramsMap.containsKey(argument.datagramId);
+ }
+ }
+ }
+
+ private void handleEventWaitForDatagramSendingResponseTimedOut(
+ @NonNull SendSatelliteDatagramArgument argument) {
+ synchronized (mLock) {
+ logw("Timed out to wait for the response of the request to send the datagram "
+ + argument.datagramId);
+
+ // Ask vendor service to abort all datagram-sending requests
+ SatelliteModemInterface.getInstance().abortSendingSatelliteDatagrams(
+ obtainMessage(EVENT_ABORT_SENDING_SATELLITE_DATAGRAMS_DONE, argument));
+ mSendingDatagramInProgress = false;
+
+ // Update send status
+ mDatagramController.updateSendStatus(argument.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
+ getPendingDatagramCount(), SATELLITE_RESULT_MODEM_TIMEOUT);
+ mDatagramController.updateSendStatus(argument.subId,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ 0, SatelliteManager.SATELLITE_RESULT_SUCCESS);
+
+ // Send response for current datagram after updating datagram transfer state
+ // internally.
+ argument.callback.accept(SATELLITE_RESULT_MODEM_TIMEOUT);
+
+ // Log metrics about the outgoing datagram
+ reportSendDatagramCompleted(argument, SATELLITE_RESULT_MODEM_TIMEOUT);
+ mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType);
+ // Remove current datagram from pending map.
+ if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
+ mPendingEmergencyDatagramsMap.remove(argument.datagramId);
+ } else {
+ mPendingNonEmergencyDatagramsMap.remove(argument.datagramId);
+ }
+
+ // Abort sending all the pending datagrams
+ abortSendingPendingDatagrams(argument.subId,
+ SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED);
+ }
+ }
+
/**
* This API can be used by only CTS to override the cached value for the device overlay config
* value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index aaabaff..0d3973d 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -16,10 +16,13 @@
package com.android.internal.telephony.satellite;
+import static android.provider.Settings.ACTION_SATELLITE_SETTING;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_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_ENTITLEMENT_SUPPORTED_BOOL;
import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
+import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS;
import static android.telephony.SubscriptionManager.isValidSubscriptionId;
import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
@@ -27,12 +30,17 @@
import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH;
import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT;
import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
import android.annotation.ArrayRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -42,6 +50,7 @@
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.Uri;
import android.net.wifi.WifiManager;
import android.nfc.NfcAdapter;
import android.os.AsyncResult;
@@ -62,6 +71,7 @@
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
@@ -105,9 +115,11 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
/**
@@ -126,6 +138,11 @@
public static final int SATELLITE_MODE_ENABLED_TRUE = 1;
public static final int SATELLITE_MODE_ENABLED_FALSE = 0;
public static final int INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE = -1;
+ /**
+ * This is used by CTS to override the timeout duration to wait for the response of the request
+ * to enable satellite.
+ */
+ public static final int TIMEOUT_TYPE_WAIT_FOR_SATELLITE_ENABLING_RESPONSE = 1;
/** Key used to read/write OEM-enabled satellite provision status in shared preferences. */
private static final String OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY =
@@ -169,6 +186,7 @@
private static final int EVENT_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING_DONE = 36;
private static final int EVENT_SERVICE_STATE_CHANGED = 37;
private static final int EVENT_SATELLITE_CAPABILITIES_CHANGED = 38;
+ private static final int EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT = 39;
@NonNull private static SatelliteController sInstance;
@NonNull private final Context mContext;
@@ -334,6 +352,18 @@
* carrierPlmnList. */
@GuardedBy("mSupportedSatelliteServicesLock")
private final SparseArray<List<String>> mMergedPlmnListPerCarrier = new SparseArray<>();
+ private static AtomicLong sNextSatelliteEnableRequestId = new AtomicLong(0);
+ private long mWaitTimeForSatelliteEnablingResponse;
+
+ /** Key used to read/write satellite system notification done in shared preferences. */
+ private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY =
+ "satellite_system_notification_done_key";
+ // The notification tag used when showing a notification. The combination of notification tag
+ // and notification id should be unique within the phone app.
+ private static final String NOTIFICATION_TAG = "SatelliteController";
+ private static final int NOTIFICATION_ID = 1;
+ private static final String NOTIFICATION_CHANNEL = "satelliteChannel";
+ private static final String NOTIFICATION_CHANNEL_ID = "satellite";
/**
* @return The singleton instance of SatelliteController.
@@ -427,6 +457,7 @@
mDSM.registerForSignalStrengthReportDecision(this, CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING,
null);
loadSatelliteSharedPreferences();
+ mWaitTimeForSatelliteEnablingResponse = getWaitForSatelliteEnablingResponseTimeoutMillis();
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -634,12 +665,15 @@
public boolean enableSatellite;
public boolean enableDemoMode;
@NonNull public Consumer<Integer> callback;
+ public long requestId;
RequestSatelliteEnabledArgument(boolean enableSatellite, boolean enableDemoMode,
Consumer<Integer> callback) {
this.enableSatellite = enableSatellite;
this.enableDemoMode = enableDemoMode;
this.callback = callback;
+ this.requestId = sNextSatelliteEnableRequestId.getAndUpdate(
+ n -> ((n + 1) % Long.MAX_VALUE));
}
}
@@ -792,6 +826,17 @@
int error = SatelliteServiceUtils.getSatelliteError(ar, "setSatelliteEnabled");
logd("EVENT_SET_SATELLITE_ENABLED_DONE = " + error);
+ /*
+ * The timer to wait for EVENT_SET_SATELLITE_ENABLED_DONE might have expired and
+ * thus the request resources might have been cleaned up.
+ */
+ if (!shouldProcessEventSetSatelliteEnabledDone(argument)) {
+ logw("The request ID=" + argument.requestId + ", enableSatellite="
+ + argument.enableSatellite + " was already processed");
+ return;
+ }
+ stopWaitForSatelliteEnablingResponseTimer(argument);
+
if (error == SATELLITE_RESULT_SUCCESS) {
if (argument.enableSatellite) {
synchronized (mSatelliteEnabledRequestLock) {
@@ -867,6 +912,11 @@
break;
}
+ case EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT:
+ handleEventWaitForSatelliteEnablingResponseTimedOut(
+ (RequestSatelliteEnabledArgument) msg.obj);
+ break;
+
case CMD_IS_SATELLITE_ENABLED: {
request = (SatelliteControllerHandlerRequest) msg.obj;
onCompleted = obtainMessage(EVENT_IS_SATELLITE_ENABLED_DONE, request);
@@ -2143,18 +2193,56 @@
}
/**
- * This API can be used by only CTS to update the timeout duration in milliseconds whether
- * the device is aligned with the satellite for demo mode
+ * This API can be used by only CTS to override timeout durations used by DatagramController
+ * module.
*
* @param timeoutMillis The timeout duration in millisecond.
* @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
*/
- public boolean setSatelliteDeviceAlignedTimeoutDuration(long timeoutMillis) {
+ public boolean setDatagramControllerTimeoutDuration(
+ boolean reset, int timeoutType, long timeoutMillis) {
if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
- logd("setSatelliteDeviceAlignedTimeoutDuration: oemEnabledSatelliteFlag is disabled");
+ logd("setDatagramControllerTimeoutDuration: oemEnabledSatelliteFlag is disabled");
return false;
}
- return mDatagramController.setSatelliteDeviceAlignedTimeoutDuration(timeoutMillis);
+ logd("setDatagramControllerTimeoutDuration: reset=" + reset + ", timeoutType="
+ + timeoutType + ", timeoutMillis=" + timeoutMillis);
+ return mDatagramController.setDatagramControllerTimeoutDuration(
+ reset, timeoutType, timeoutMillis);
+ }
+
+ /**
+ * This API can be used by only CTS to override timeout durations used by SatelliteController
+ * module.
+ *
+ * @param timeoutMillis The timeout duration in millisecond.
+ * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
+ */
+ public boolean setSatelliteControllerTimeoutDuration(
+ boolean reset, int timeoutType, long timeoutMillis) {
+ if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+ logd("setSatelliteControllerTimeoutDuration: oemEnabledSatelliteFlag is disabled");
+ return false;
+ }
+ if (!isMockModemAllowed()) {
+ logd("setSatelliteControllerTimeoutDuration: mock modem is not allowed");
+ return false;
+ }
+ logd("setSatelliteControllerTimeoutDuration: reset=" + reset + ", timeoutType="
+ + timeoutType + ", timeoutMillis=" + timeoutMillis);
+ if (timeoutType == TIMEOUT_TYPE_WAIT_FOR_SATELLITE_ENABLING_RESPONSE) {
+ if (reset) {
+ mWaitTimeForSatelliteEnablingResponse =
+ getWaitForSatelliteEnablingResponseTimeoutMillis();
+ } else {
+ mWaitTimeForSatelliteEnablingResponse = timeoutMillis;
+ }
+ logd("mWaitTimeForSatelliteEnablingResponse=" + mWaitTimeForSatelliteEnablingResponse);
+ } else {
+ logw("Invalid timeoutType=" + timeoutType);
+ return false;
+ }
+ return true;
}
/**
@@ -2513,19 +2601,28 @@
}
};
}
+ logd("onSatelliteEntitlementStatusUpdated subId=" + subId + " , entitlementEnabled="
+ + entitlementEnabled + ", allowedPlmnList=" + (Objects.equals(null, allowedPlmnList)
+ ? "" : allowedPlmnList + ""));
synchronized (mSupportedSatelliteServicesLock) {
if (mSatelliteEntitlementStatusPerCarrier.get(subId, false) != entitlementEnabled) {
logd("update the carrier satellite enabled to " + entitlementEnabled);
mSatelliteEntitlementStatusPerCarrier.put(subId, entitlementEnabled);
+ try {
+ mSubscriptionManagerService.setSubscriptionProperty(subId,
+ SATELLITE_ENTITLEMENT_STATUS, entitlementEnabled ? "1" : "0");
+ } catch (IllegalArgumentException | SecurityException e) {
+ loge("onSatelliteEntitlementStatusUpdated: setSubscriptionProperty, e=" + e);
+ }
}
mMergedPlmnListPerCarrier.remove(subId);
mEntitlementPlmnListPerCarrier.put(subId, allowedPlmnList);
updatePlmnListPerCarrier(subId);
configureSatellitePlmnForCarrier(subId);
+ mSubscriptionManagerService.setSatelliteEntitlementPlmnList(subId, allowedPlmnList);
- // TODO b/322143408 store entitlement status in telephony db.
if (mSatelliteEntitlementStatusPerCarrier.get(subId, false)) {
removeAttachRestrictionForCarrier(subId,
SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT, callback);
@@ -2726,6 +2823,7 @@
Message onCompleted = obtainMessage(EVENT_SET_SATELLITE_ENABLED_DONE, request);
mSatelliteModemInterface.requestSatelliteEnabled(argument.enableSatellite,
argument.enableDemoMode, onCompleted);
+ startWaitForSatelliteEnablingResponseTimer(argument);
}
private void handleRequestSatelliteAttachRestrictionForCarrierCmd(
@@ -3144,7 +3242,8 @@
PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId,
KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
- KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT);
+ KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT,
+ KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL);
if (config == null || config.isEmpty()) {
config = CarrierConfigManager.getDefaultConfig();
}
@@ -3161,8 +3260,7 @@
}
updateCarrierConfig(subId);
- // TODO b/322143408 read the telephony db to get the entitlementStatus and update the
- // restriction
+ updateEntitlementPlmnListPerCarrier(subId);
updateSupportedSatelliteServicesForActiveSubscriptions();
configureSatellitePlmnForCarrier(subId);
@@ -3172,6 +3270,7 @@
}
setSatelliteAttachEnabledForCarrierOnSimLoaded(subId);
+ updateRestrictReasonForEntitlementPerCarrier(subId);
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -3181,6 +3280,31 @@
}
}
+ /** If there is no cached entitlement plmn list, read it from the db and use it if it is not an
+ * empty list. */
+ private void updateEntitlementPlmnListPerCarrier(int subId) {
+ if (!getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false)) {
+ logd("don't support entitlement");
+ return;
+ }
+
+ synchronized (mSupportedSatelliteServicesLock) {
+ if (mEntitlementPlmnListPerCarrier.indexOfKey(subId) < 0) {
+ logd("updateEntitlementPlmnListPerCarrier: no correspondent cache, load from "
+ + "persist storage");
+ List<String> entitlementPlmnList =
+ mSubscriptionManagerService.getSatelliteEntitlementPlmnList(subId);
+ if (entitlementPlmnList.isEmpty()) {
+ loge("updateEntitlementPlmnListPerCarrier: no data for subId(" + subId + ")");
+ return;
+ }
+ logd("updateEntitlementPlmnListPerCarrier: entitlementPlmnList="
+ + entitlementPlmnList);
+ mEntitlementPlmnListPerCarrier.put(subId, entitlementPlmnList);
+ }
+ }
+ }
+
/**
* When a SIM is loaded, we need to check if users has enabled satellite attach for the carrier
* associated with the SIM, and evaluate if satellite should be enabled for the carrier.
@@ -3285,6 +3409,54 @@
}
}
+ private void updateRestrictReasonForEntitlementPerCarrier(int subId) {
+ if (!getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false)) {
+ logd("don't support entitlement");
+ return;
+ }
+
+ IIntegerConsumer callback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ logd("updateRestrictReasonForEntitlementPerCarrier:" + result);
+ }
+ };
+ synchronized (mSupportedSatelliteServicesLock) {
+ if (mSatelliteEntitlementStatusPerCarrier.indexOfKey(subId) < 0) {
+ logd("updateRestrictReasonForEntitlementPerCarrier: no correspondent cache, "
+ + "load from persist storage");
+ String entitlementStatus = null;
+ try {
+ entitlementStatus =
+ mSubscriptionManagerService.getSubscriptionProperty(subId,
+ SATELLITE_ENTITLEMENT_STATUS, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (IllegalArgumentException | SecurityException e) {
+ loge("updateRestrictReasonForEntitlementPerCarrier, e=" + e);
+ }
+
+ if (entitlementStatus == null) {
+ loge("updateRestrictReasonForEntitlementPerCarrier: invalid subId, subId="
+ + subId + " set to default value");
+ entitlementStatus = "0";
+ }
+
+ if (entitlementStatus.isEmpty()) {
+ loge("updateRestrictReasonForEntitlementPerCarrier: no data for subId(" + subId
+ + "). set to default value");
+ entitlementStatus = "0";
+ }
+ boolean result = entitlementStatus.equals("1");
+ mSatelliteEntitlementStatusPerCarrier.put(subId, result);
+ }
+
+ if (!mSatelliteEntitlementStatusPerCarrier.get(subId, false)) {
+ addAttachRestrictionForCarrier(subId,
+ SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT, callback);
+ }
+ }
+ }
+
/**
* Save user setting for enabling satellite attach for the carrier associated with the
* {@code subId} to persistent storage.
@@ -3462,6 +3634,7 @@
private void handleEventServiceStateChanged() {
handleServiceStateForSatelliteConnectionViaCarrier();
+ determineSystemNotification();
}
private void handleServiceStateForSatelliteConnectionViaCarrier() {
@@ -3573,6 +3746,109 @@
((ResultReceiver) request.argument).send(error, bundle);
}
+ private long getWaitForSatelliteEnablingResponseTimeoutMillis() {
+ return mContext.getResources().getInteger(
+ R.integer.config_wait_for_satellite_enabling_response_timeout_millis);
+ }
+
+ private void startWaitForSatelliteEnablingResponseTimer(
+ @NonNull RequestSatelliteEnabledArgument argument) {
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (hasMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT, argument)) {
+ logd("WaitForSatelliteEnablingResponseTimer of request ID "
+ + argument.requestId + " was already started");
+ return;
+ }
+ logd("Start timer to wait for response of the satellite enabling request ID="
+ + argument.requestId + ", enableSatellite=" + argument.enableSatellite
+ + ", mWaitTimeForSatelliteEnablingResponse="
+ + mWaitTimeForSatelliteEnablingResponse);
+ sendMessageDelayed(obtainMessage(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT,
+ argument), mWaitTimeForSatelliteEnablingResponse);
+ }
+ }
+
+ private void stopWaitForSatelliteEnablingResponseTimer(
+ @NonNull RequestSatelliteEnabledArgument argument) {
+ synchronized (mSatelliteEnabledRequestLock) {
+ logd("Stop timer to wait for response of the satellite enabling request ID="
+ + argument.requestId + ", enableSatellite=" + argument.enableSatellite);
+ removeMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT, argument);
+ }
+ }
+
+ private boolean shouldProcessEventSetSatelliteEnabledDone(
+ @NonNull RequestSatelliteEnabledArgument argument) {
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (hasMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT, argument)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private void handleEventWaitForSatelliteEnablingResponseTimedOut(
+ @NonNull RequestSatelliteEnabledArgument argument) {
+ logw("Timed out to wait for response of the satellite enabling request ID="
+ + argument.requestId + ", enableSatellite=" + argument.enableSatellite);
+
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (mSatelliteEnabledRequest != null) {
+ if (mSatelliteEnabledRequest.enableSatellite && !argument.enableSatellite
+ && mWaitingForRadioDisabled) {
+ // Previous mSatelliteEnabledRequest is successful but waiting for
+ // all radios to be turned off.
+ mSatelliteEnabledRequest.callback.accept(SATELLITE_RESULT_SUCCESS);
+ resetSatelliteEnabledRequest();
+ } else if (mSatelliteEnabledRequest.requestId == argument.requestId) {
+ resetSatelliteEnabledRequest();
+ }
+ }
+ }
+ argument.callback.accept(SATELLITE_RESULT_MODEM_TIMEOUT);
+
+ synchronized (mIsSatelliteEnabledLock) {
+ if (argument.enableSatellite) {
+ if (!mWaitingForDisableSatelliteModemResponse && !mWaitingForSatelliteModemOff) {
+ resetSatelliteEnabledRequest();
+ IIntegerConsumer callback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ logd("handleEventWaitForSatelliteEnablingResponseTimedOut: "
+ + "disable satellite result=" + result);
+ }
+ };
+ Consumer<Integer> result =
+ FunctionalUtils.ignoreRemoteException(callback::accept);
+ RequestSatelliteEnabledArgument request = new RequestSatelliteEnabledArgument(
+ false, false, result);
+ synchronized (mSatelliteEnabledRequestLock) {
+ mSatelliteEnabledRequest = request;
+ }
+ sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null);
+ }
+ mControllerMetricsStats.reportServiceEnablementFailCount();
+ SessionMetricsStats.getInstance()
+ .setInitializationResult(SATELLITE_RESULT_MODEM_TIMEOUT)
+ .setRadioTechnology(getSupportedNtnRadioTechnology())
+ .reportSessionMetrics();
+ } else {
+ /*
+ * Unregister Importance Listener for Pointing UI when Satellite is disabled
+ */
+ synchronized (mNeedsSatellitePointingLock) {
+ if (mNeedsSatellitePointing) {
+ mPointingAppController.removeListenerForPointingUI();
+ }
+ }
+ moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_MODEM_TIMEOUT, null);
+ mControllerMetricsStats.onSatelliteDisabled();
+ mWaitingForDisableSatelliteModemResponse = false;
+ mWaitingForSatelliteModemOff = false;
+ }
+ }
+ }
+
/**
* This API can be used by only CTS to override the cached value for the device overlay config
* value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether
@@ -3598,10 +3874,86 @@
return true;
}
+ private void determineSystemNotification() {
+ if (isUsingNonTerrestrialNetworkViaCarrier()) {
+ if (mSharedPreferences == null) {
+ try {
+ mSharedPreferences = mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
+ Context.MODE_PRIVATE);
+ } catch (Exception e) {
+ loge("Cannot get default shared preferences: " + e);
+ }
+ }
+ if (mSharedPreferences == null) {
+ loge("handleEventServiceStateChanged: Cannot get default shared preferences");
+ return;
+ }
+ if (!mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false)) {
+ showSatelliteSystemNotification();
+ mSharedPreferences.edit().putBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY,
+ true).apply();
+ }
+ }
+ }
+
+ private void showSatelliteSystemNotification() {
+ logd("showSatelliteSystemNotification");
+ final NotificationChannel notificationChannel = new NotificationChannel(
+ NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL,
+ NotificationManager.IMPORTANCE_DEFAULT
+ );
+ notificationChannel.setSound(null, null);
+ NotificationManager notificationManager = mContext.getSystemService(
+ NotificationManager.class);
+ notificationManager.createNotificationChannel(notificationChannel);
+
+ Notification.Builder notificationBuilder = new Notification.Builder(mContext)
+ .setContentTitle(mContext.getResources().getString(
+ R.string.satellite_notification_title))
+ .setContentText(mContext.getResources().getString(
+ R.string.satellite_notification_summary))
+ .setSmallIcon(R.drawable.ic_satellite_alt_24px)
+ .setChannelId(NOTIFICATION_CHANNEL_ID)
+ .setAutoCancel(true)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setVisibility(Notification.VISIBILITY_PUBLIC);
+
+ // Add action to invoke `What to expect` dialog of Messaging application.
+ Intent intentOpenMessage = new Intent(Intent.ACTION_VIEW);
+ intentOpenMessage.setData(Uri.parse("sms:"));
+ // TODO : b/322733285 add putExtra to invoke "What to expect" dialog.
+ PendingIntent pendingIntentOpenMessage = PendingIntent.getActivity(mContext, 0,
+ intentOpenMessage, PendingIntent.FLAG_IMMUTABLE);
+
+ Notification.Action actionOpenMessage = new Notification.Action.Builder(0,
+ mContext.getResources().getString(R.string.satellite_notification_open_message),
+ pendingIntentOpenMessage).build();
+ notificationBuilder.addAction(actionOpenMessage);
+
+ // Add action to invoke Satellite setting activity in Settings.
+ Intent intentSatelliteSetting = new Intent(ACTION_SATELLITE_SETTING);
+ PendingIntent pendingIntentSatelliteSetting = PendingIntent.getActivity(mContext, 0,
+ intentSatelliteSetting, PendingIntent.FLAG_IMMUTABLE);
+
+ Notification.Action actionOpenSatelliteSetting = new Notification.Action.Builder(null,
+ mContext.getResources().getString(R.string.satellite_notification_how_it_works),
+ pendingIntentSatelliteSetting).build();
+ notificationBuilder.addAction(actionOpenSatelliteSetting);
+
+ notificationManager.notifyAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
+ notificationBuilder.build(), UserHandle.ALL);
+ }
+
private static void logd(@NonNull String log) {
Rlog.d(TAG, log);
}
+ private static void logw(@NonNull String log) {
+ Rlog.w(TAG, log);
+ }
+
private static void loge(@NonNull String log) {
Rlog.e(TAG, log);
}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 900e124..2f86eea 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -1317,6 +1317,35 @@
}
}
+ /**
+ * The satellite service should abort all datagram-sending requests.
+ *
+ * @param message The Message to send to result of the operation to.
+ */
+ public void abortSendingSatelliteDatagrams(@NonNull Message message) {
+ if (mSatelliteService != null) {
+ try {
+ mSatelliteService.abortSendingSatelliteDatagrams(new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ int error = SatelliteServiceUtils.fromSatelliteError(result);
+ logd("abortSendingSatelliteDatagrams: " + error);
+ Binder.withCleanCallingIdentity(() ->
+ sendMessageWithResult(message, null, error));
+ }
+ });
+ } catch (RemoteException e) {
+ loge("abortSendingSatelliteDatagrams: RemoteException " + e);
+ sendMessageWithResult(message, null,
+ SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
+ }
+ } else {
+ loge("abortSendingSatelliteDatagrams: Satellite service is unavailable.");
+ sendMessageWithResult(message, null,
+ SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
+ }
+ }
+
public boolean isSatelliteServiceSupported() {
return mIsSatelliteServiceSupported;
}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index 541a029..5c79c28 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -358,6 +358,7 @@
}
return true;
}
+
/**
* Adjusts listening timeout duration when demo mode is on
*
@@ -711,6 +712,9 @@
&& datagramTransferState.receiveState
== SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE) {
startNbIotInactivityTimer();
+ } else if (isSending(datagramTransferState.sendState)
+ || isReceiving(datagramTransferState.receiveState)) {
+ restartNbIotInactivityTimer();
}
}
}
@@ -761,9 +765,8 @@
private void handleEventDatagramTransferStateChanged(
@NonNull DatagramTransferState datagramTransferState) {
- if (datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING
- || datagramTransferState.receiveState
- == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING) {
+ if (isSending(datagramTransferState.sendState)
+ || isReceiving(datagramTransferState.receiveState)) {
transitionTo(mTransferringState);
}
}
@@ -974,6 +977,11 @@
R.integer.config_satellite_nb_iot_inactivity_timeout_millis);
}
+ private void restartNbIotInactivityTimer() {
+ stopNbIotInactivityTimer();
+ startNbIotInactivityTimer();
+ }
+
private void startNbIotInactivityTimer() {
if (isNbIotInactivityTimerStarted()) {
logd("NB IOT inactivity timer is already started");
diff --git a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
index e7f66a3..4540b8a 100644
--- a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
+++ b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.security;
+import android.content.Context;
import android.telephony.CellularIdentifierDisclosure;
import com.android.internal.annotations.GuardedBy;
@@ -49,6 +50,7 @@
private static CellularIdentifierDisclosureNotifier sInstance = null;
private final long mWindowCloseDuration;
private final TimeUnit mWindowCloseUnit;
+ private final CellularNetworkSecuritySafetySource mSafetySource;
private final Object mEnabledLock = new Object();
@GuardedBy("mEnabledLock")
@@ -61,11 +63,12 @@
// outside of that thread would require additional synchronization.
private Map<Integer, DisclosureWindow> mWindows;
- public CellularIdentifierDisclosureNotifier() {
+ public CellularIdentifierDisclosureNotifier(CellularNetworkSecuritySafetySource safetySource) {
this(
Executors.newSingleThreadScheduledExecutor(),
DEFAULT_WINDOW_CLOSE_DURATION_IN_MINUTES,
- TimeUnit.MINUTES);
+ TimeUnit.MINUTES,
+ safetySource);
}
/**
@@ -79,18 +82,20 @@
public CellularIdentifierDisclosureNotifier(
ScheduledExecutorService notificationQueue,
long windowCloseDuration,
- TimeUnit windowCloseUnit) {
+ TimeUnit windowCloseUnit,
+ CellularNetworkSecuritySafetySource safetySource) {
mSerializedWorkQueue = notificationQueue;
mWindowCloseDuration = windowCloseDuration;
mWindowCloseUnit = windowCloseUnit;
mWindows = new HashMap<>();
+ mSafetySource = safetySource;
}
/**
* Add a CellularIdentifierDisclosure to be tracked by this instance. If appropriate, this will
* trigger a user notification.
*/
- public void addDisclosure(int subId, CellularIdentifierDisclosure disclosure) {
+ public void addDisclosure(Context context, int subId, CellularIdentifierDisclosure disclosure) {
Rlog.d(TAG, "Identifier disclosure reported: " + disclosure);
synchronized (mEnabledLock) {
@@ -111,7 +116,7 @@
// because we know that any actions taken on disabled will be scheduled after this
// incrementAndNotify call.
try {
- mSerializedWorkQueue.execute(incrementAndNotify(subId));
+ mSerializedWorkQueue.execute(incrementAndNotify(context, subId));
} catch (RejectedExecutionException e) {
Rlog.e(TAG, "Failed to schedule incrementAndNotify: " + e.getMessage());
}
@@ -122,12 +127,12 @@
* Re-enable if previously disabled. This means that {@code addDisclsoure} will start tracking
* disclosures again and potentially emitting notifications.
*/
- public void enable() {
+ public void enable(Context context) {
synchronized (mEnabledLock) {
Rlog.d(TAG, "enabled");
mEnabled = true;
try {
- mSerializedWorkQueue.execute(onEnableNotifier());
+ mSerializedWorkQueue.execute(onEnableNotifier(context));
} catch (RejectedExecutionException e) {
Rlog.e(TAG, "Failed to schedule onEnableNotifier: " + e.getMessage());
}
@@ -139,12 +144,12 @@
* This can be used to in response to a user disabling the feature to emit notifications.
* If {@code addDisclosure} is called while in a disabled state, disclosures will be dropped.
*/
- public void disable() {
+ public void disable(Context context) {
Rlog.d(TAG, "disabled");
synchronized (mEnabledLock) {
mEnabled = false;
try {
- mSerializedWorkQueue.execute(onDisableNotifier());
+ mSerializedWorkQueue.execute(onDisableNotifier(context));
} catch (RejectedExecutionException e) {
Rlog.e(TAG, "Failed to schedule onDisableNotifier: " + e.getMessage());
}
@@ -158,15 +163,16 @@
}
/** Get a singleton CellularIdentifierDisclosureNotifier. */
- public static synchronized CellularIdentifierDisclosureNotifier getInstance() {
+ public static synchronized CellularIdentifierDisclosureNotifier getInstance(
+ CellularNetworkSecuritySafetySource safetySource) {
if (sInstance == null) {
- sInstance = new CellularIdentifierDisclosureNotifier();
+ sInstance = new CellularIdentifierDisclosureNotifier(safetySource);
}
return sInstance;
}
- private Runnable incrementAndNotify(int subId) {
+ private Runnable incrementAndNotify(Context context, int subId) {
return () -> {
DisclosureWindow window = mWindows.get(subId);
if (window == null) {
@@ -174,7 +180,7 @@
mWindows.put(subId, window);
}
- window.increment(this);
+ window.increment(context, this);
int disclosureCount = window.getDisclosureCount();
@@ -185,31 +191,29 @@
+ ". New disclosure count "
+ disclosureCount);
- // TODO (b/308985417) emit safety center issue
- // mSafetySource.setIdentifierDisclosure(
- // subId,
- // disclosureCount,
- // window.getFirstOpen(),
- // window.getCurrentEnd());
+ mSafetySource.setIdentifierDisclosure(
+ context,
+ subId,
+ disclosureCount,
+ window.getFirstOpen(),
+ window.getCurrentEnd());
};
}
- private Runnable onDisableNotifier() {
+ private Runnable onDisableNotifier(Context context) {
return () -> {
Rlog.d(TAG, "On disable notifier");
for (DisclosureWindow window : mWindows.values()) {
window.close();
}
- // TODO (b/308985417) disable safety center issues
- // mSafetySource.setIdentifierDisclosureIssueEnabled(false);
+ mSafetySource.setIdentifierDisclosureIssueEnabled(context, false);
};
}
- private Runnable onEnableNotifier() {
+ private Runnable onEnableNotifier(Context context) {
return () -> {
Rlog.i(TAG, "On enable notifier");
- // TODO (b/308985417) enable safety center issues
- // mSafetySource.setIdentifierDisclosureIssueEnabled(true);
+ mSafetySource.setIdentifierDisclosureIssueEnabled(context, true);
};
}
@@ -262,7 +266,7 @@
* A helper class that maintains all state associated with the disclosure window for a single
* subId. No methods are thread safe. Callers must implement all synchronization.
*/
- private static class DisclosureWindow {
+ private class DisclosureWindow {
private int mDisclosureCount;
private Instant mWindowFirstOpen;
private Instant mLastEvent;
@@ -278,7 +282,7 @@
mWhenWindowCloses = null;
}
- void increment(CellularIdentifierDisclosureNotifier notifier) {
+ void increment(Context context, CellularIdentifierDisclosureNotifier notifier) {
mDisclosureCount++;
@@ -295,7 +299,7 @@
try {
mWhenWindowCloses =
notifier.mSerializedWorkQueue.schedule(
- closeWindowRunnable(),
+ closeWindowRunnable(context),
notifier.mWindowCloseDuration,
notifier.mWindowCloseUnit);
} catch (RejectedExecutionException e) {
@@ -331,7 +335,7 @@
mWhenWindowCloses = null;
}
- private Runnable closeWindowRunnable() {
+ private Runnable closeWindowRunnable(Context context) {
return () -> {
Rlog.i(
TAG,
@@ -340,9 +344,7 @@
+ ". Disclosure count was "
+ getDisclosureCount());
close();
-
- // TODO (b/308985417) clear safety center issue
- // mSafetySource.setIdentifierDisclosure(mSubId, 0, null, null);
+ mSafetySource.clearIdentifierDisclosure(context, mSubId);
};
}
diff --git a/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java b/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java
new file mode 100644
index 0000000..34f26e3
--- /dev/null
+++ b/src/java/com/android/internal/telephony/security/CellularNetworkSecuritySafetySource.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 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.security;
+
+import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED;
+import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED;
+import static android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION;
+import static android.safetycenter.SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION;
+
+import android.annotation.IntDef;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.safetycenter.SafetyCenterManager;
+import android.safetycenter.SafetyEvent;
+import android.safetycenter.SafetySourceData;
+import android.safetycenter.SafetySourceIssue;
+import android.safetycenter.SafetySourceStatus;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * Holds the state needed to report the Safety Center status and issues related to cellular
+ * network security.
+ */
+public class CellularNetworkSecuritySafetySource {
+ private static final String SAFETY_SOURCE_ID = "AndroidCellularNetworkSecurity";
+
+ private static final String NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID = "null_cipher_non_encrypted";
+ private static final String NULL_CIPHER_ISSUE_ENCRYPTED_ID = "null_cipher_encrypted";
+
+ private static final String NULL_CIPHER_ACTION_SETTINGS_ID = "cellular_security_settings";
+ private static final String NULL_CIPHER_ACTION_LEARN_MORE_ID = "learn_more";
+
+ private static final String IDENTIFIER_DISCLOSURE_ISSUE_ID = "identifier_disclosure";
+
+ private static final Intent CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT =
+ new Intent("android.settings.CELLULAR_NETWORK_SECURITY");
+ // TODO(b/321999913): direct to a help page URL e.g.
+ // new Intent(Intent.ACTION_VIEW, Uri.parse("https://..."));
+ private static final Intent LEARN_MORE_INTENT = new Intent();
+
+ static final int NULL_CIPHER_STATE_ENCRYPTED = 0;
+ static final int NULL_CIPHER_STATE_NOTIFY_ENCRYPTED = 1;
+ static final int NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED = 2;
+
+ @IntDef(
+ prefix = {"NULL_CIPHER_STATE_"},
+ value = {
+ NULL_CIPHER_STATE_ENCRYPTED,
+ NULL_CIPHER_STATE_NOTIFY_ENCRYPTED,
+ NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface NullCipherState {}
+
+ private static CellularNetworkSecuritySafetySource sInstance;
+
+ private final SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
+ private final SubscriptionManagerService mSubscriptionManagerService;
+
+ private boolean mNullCipherStateIssuesEnabled;
+ private HashMap<Integer, Integer> mNullCipherStates = new HashMap<>();
+
+ private boolean mIdentifierDisclosureIssuesEnabled;
+ private HashMap<Integer, IdentifierDisclosure> mIdentifierDisclosures = new HashMap<>();
+
+ /**
+ * Gets a singleton CellularNetworkSecuritySafetySource.
+ */
+ public static synchronized CellularNetworkSecuritySafetySource getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new CellularNetworkSecuritySafetySource(
+ new SafetyCenterManagerWrapper(context));
+ }
+ return sInstance;
+ }
+
+ @VisibleForTesting
+ public CellularNetworkSecuritySafetySource(
+ SafetyCenterManagerWrapper safetyCenterManagerWrapper) {
+ mSafetyCenterManagerWrapper = safetyCenterManagerWrapper;
+ mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+ }
+
+ /** Enables or disables the null cipher issue and clears any current issues. */
+ public synchronized void setNullCipherIssueEnabled(Context context, boolean enabled) {
+ mNullCipherStateIssuesEnabled = enabled;
+ mNullCipherStates.clear();
+ updateSafetyCenter(context);
+ }
+
+ /** Sets the null cipher issue state for the identified subscription. */
+ public synchronized void setNullCipherState(
+ Context context, int subId, @NullCipherState int nullCipherState) {
+ mNullCipherStates.put(subId, nullCipherState);
+ updateSafetyCenter(context);
+ }
+
+ /**
+ * Enables or disables the identifier disclosure issue and clears any current issues if the
+ * enable state is changed.
+ */
+ public synchronized void setIdentifierDisclosureIssueEnabled(Context context, boolean enabled) {
+ // This check ensures that if we're enabled and we are asked to enable ourselves again (can
+ // happen if the modem restarts), we don't clear our state.
+ if (enabled != mIdentifierDisclosureIssuesEnabled) {
+ mIdentifierDisclosureIssuesEnabled = enabled;
+ mIdentifierDisclosures.clear();
+ updateSafetyCenter(context);
+ }
+ }
+
+ /** Sets the identifier disclosure issue state for the identifier subscription. */
+ public synchronized void setIdentifierDisclosure(
+ Context context, int subId, int count, Instant start, Instant end) {
+ IdentifierDisclosure disclosure = new IdentifierDisclosure(count, start, end);
+ mIdentifierDisclosures.put(subId, disclosure);
+ updateSafetyCenter(context);
+ }
+
+ /** Clears the identifier disclosure issue state for the identified subscription. */
+ public synchronized void clearIdentifierDisclosure(Context context, int subId) {
+ mIdentifierDisclosures.remove(subId);
+ updateSafetyCenter(context);
+ }
+
+ /** Refreshed the safety source in response to the identified broadcast. */
+ public synchronized void refresh(Context context, String refreshBroadcastId) {
+ mSafetyCenterManagerWrapper.setRefreshedSafetySourceData(
+ refreshBroadcastId, getSafetySourceData(context));
+ }
+
+ private void updateSafetyCenter(Context context) {
+ mSafetyCenterManagerWrapper.setSafetySourceData(getSafetySourceData(context));
+ }
+
+ private boolean isSafetySourceHidden() {
+ return !mNullCipherStateIssuesEnabled && !mIdentifierDisclosureIssuesEnabled;
+ }
+
+ private SafetySourceData getSafetySourceData(Context context) {
+ if (isSafetySourceHidden()) {
+ // The cellular network security safety source is configured with
+ // initialDisplayState="hidden"
+ return null;
+ }
+
+ Stream<Optional<SafetySourceIssue>> nullCipherIssues =
+ mNullCipherStates.entrySet().stream()
+ .map(e -> getNullCipherIssue(context, e.getKey(), e.getValue()));
+ Stream<Optional<SafetySourceIssue>> identifierDisclosureIssues =
+ mIdentifierDisclosures.entrySet().stream()
+ .map(e -> getIdentifierDisclosureIssue(context, e.getKey(), e.getValue()));
+ SafetySourceIssue[] issues = Stream.concat(nullCipherIssues, identifierDisclosureIssues)
+ .flatMap(Optional::stream)
+ .toArray(SafetySourceIssue[]::new);
+
+ SafetySourceData.Builder builder = new SafetySourceData.Builder();
+ int maxSeverity = SEVERITY_LEVEL_INFORMATION;
+ for (SafetySourceIssue issue : issues) {
+ builder.addIssue(issue);
+ maxSeverity = Math.max(maxSeverity, issue.getSeverityLevel());
+ }
+
+ builder.setStatus(
+ new SafetySourceStatus.Builder(
+ context.getString(R.string.scCellularNetworkSecurityTitle),
+ context.getString(R.string.scCellularNetworkSecuritySummary),
+ maxSeverity)
+ .setPendingIntent(mSafetyCenterManagerWrapper.getActivityPendingIntent(
+ context, CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
+ .build());
+ return builder.build();
+ }
+
+ /** Builds the null cipher issue if it's enabled and there are null ciphers to report. */
+ private Optional<SafetySourceIssue> getNullCipherIssue(
+ Context context, int subId, @NullCipherState int state) {
+ if (!mNullCipherStateIssuesEnabled) {
+ return Optional.empty();
+ }
+
+ SubscriptionInfoInternal subInfo =
+ mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
+ final SafetySourceIssue.Builder builder;
+ switch (state) {
+ case NULL_CIPHER_STATE_ENCRYPTED:
+ return Optional.empty();
+ case NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED:
+ builder = new SafetySourceIssue.Builder(
+ NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID + "_" + subId,
+ context.getString(
+ R.string.scNullCipherIssueNonEncryptedTitle, subInfo.getDisplayName()),
+ context.getString(R.string.scNullCipherIssueNonEncryptedSummary),
+ SEVERITY_LEVEL_RECOMMENDATION,
+ NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID);
+ break;
+ case NULL_CIPHER_STATE_NOTIFY_ENCRYPTED:
+ builder = new SafetySourceIssue.Builder(
+ NULL_CIPHER_ISSUE_NON_ENCRYPTED_ID + "_" + subId,
+ context.getString(
+ R.string.scNullCipherIssueEncryptedTitle, subInfo.getDisplayName()),
+ context.getString(R.string.scNullCipherIssueEncryptedSummary),
+ SEVERITY_LEVEL_INFORMATION,
+ NULL_CIPHER_ISSUE_ENCRYPTED_ID);
+ break;
+ default:
+ throw new AssertionError();
+ }
+
+ return Optional.of(
+ builder
+ .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
+ .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+ .addAction(
+ new SafetySourceIssue.Action.Builder(
+ NULL_CIPHER_ACTION_SETTINGS_ID,
+ context.getString(R.string.scNullCipherIssueActionSettings),
+ mSafetyCenterManagerWrapper.getActivityPendingIntent(
+ context, CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
+ .build())
+ .addAction(
+ new SafetySourceIssue.Action.Builder(
+ NULL_CIPHER_ACTION_LEARN_MORE_ID,
+ context.getString(R.string.scNullCipherIssueActionLearnMore),
+ mSafetyCenterManagerWrapper.getActivityPendingIntent(
+ context, LEARN_MORE_INTENT))
+ .build())
+ .build());
+ }
+
+ /** Builds the identity disclosure issue if it's enabled and there are disclosures to report. */
+ private Optional<SafetySourceIssue> getIdentifierDisclosureIssue(
+ Context context, int subId, IdentifierDisclosure disclosure) {
+ if (!mIdentifierDisclosureIssuesEnabled || disclosure.getDisclosureCount() == 0) {
+ return Optional.empty();
+ }
+
+ SubscriptionInfoInternal subInfo =
+ mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
+ return Optional.of(
+ new SafetySourceIssue.Builder(
+ IDENTIFIER_DISCLOSURE_ISSUE_ID + "_" + subId,
+ context.getString(R.string.scIdentifierDisclosureIssueTitle),
+ context.getString(
+ R.string.scIdentifierDisclosureIssueSummary,
+ disclosure.getDisclosureCount(),
+ Date.from(disclosure.getWindowStart()),
+ Date.from(disclosure.getWindowEnd()),
+ subInfo.getDisplayName()),
+ SEVERITY_LEVEL_RECOMMENDATION,
+ IDENTIFIER_DISCLOSURE_ISSUE_ID)
+ .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
+ .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
+ .addAction(
+ new SafetySourceIssue.Action.Builder(
+ NULL_CIPHER_ACTION_SETTINGS_ID,
+ context.getString(R.string.scNullCipherIssueActionSettings),
+ mSafetyCenterManagerWrapper.getActivityPendingIntent(
+ context, CELLULAR_NETWORK_SECURITY_SETTINGS_INTENT))
+ .build())
+ .addAction(
+ new SafetySourceIssue.Action.Builder(
+ NULL_CIPHER_ACTION_LEARN_MORE_ID,
+ context.getString(R.string.scNullCipherIssueActionLearnMore),
+ mSafetyCenterManagerWrapper.getActivityPendingIntent(
+ context, LEARN_MORE_INTENT))
+ .build())
+ .build());
+ }
+
+ /** A wrapper around {@link SafetyCenterManager} that can be instrumented in tests. */
+ @VisibleForTesting
+ public static class SafetyCenterManagerWrapper {
+ private final SafetyCenterManager mSafetyCenterManager;
+
+ public SafetyCenterManagerWrapper(Context context) {
+ mSafetyCenterManager = context.getSystemService(SafetyCenterManager.class);
+ }
+
+ /** Retrieve a {@link PendingIntent} that will start a new activity. */
+ public PendingIntent getActivityPendingIntent(Context context, Intent intent) {
+ return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+ }
+
+ /** Set the {@link SafetySourceData} for this safety source. */
+ public void setSafetySourceData(SafetySourceData safetySourceData) {
+ mSafetyCenterManager.setSafetySourceData(
+ SAFETY_SOURCE_ID,
+ safetySourceData,
+ new SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build());
+ }
+
+ /** Sets the {@link SafetySourceData} in response to a refresh request. */
+ public void setRefreshedSafetySourceData(
+ String refreshBroadcastId, SafetySourceData safetySourceData) {
+ mSafetyCenterManager.setSafetySourceData(
+ SAFETY_SOURCE_ID,
+ safetySourceData,
+ new SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .setRefreshBroadcastId(refreshBroadcastId)
+ .build());
+ }
+ }
+
+ private static class IdentifierDisclosure {
+ private final int mDisclosureCount;
+ private final Instant mWindowStart;
+ private final Instant mWindowEnd;
+
+ private IdentifierDisclosure(int count, Instant start, Instant end) {
+ mDisclosureCount = count;
+ mWindowStart = start;
+ mWindowEnd = end;
+ }
+
+ private int getDisclosureCount() {
+ return mDisclosureCount;
+ }
+
+ private Instant getWindowStart() {
+ return mWindowStart;
+ }
+
+ private Instant getWindowEnd() {
+ return mWindowEnd;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof IdentifierDisclosure)) {
+ return false;
+ }
+ IdentifierDisclosure other = (IdentifierDisclosure) o;
+ return mDisclosureCount == other.mDisclosureCount
+ && Objects.equals(mWindowStart, other.mWindowStart)
+ && Objects.equals(mWindowEnd, other.mWindowEnd);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDisclosureCount, mWindowStart, mWindowEnd);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
index bbe88a8..3d07d47 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -284,7 +284,16 @@
SubscriptionInfoInternal::getOnlyNonTerrestrialNetwork),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_SERVICE_CAPABILITIES,
- SubscriptionInfoInternal::getServiceCapabilities)
+ SubscriptionInfoInternal::getServiceCapabilities),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_TRANSFER_STATUS,
+ SubscriptionInfoInternal::getTransferStatus),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS,
+ SubscriptionInfoInternal::getSatelliteEntitlementStatus),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS,
+ SubscriptionInfoInternal::getSatelliteEntitlementPlmns)
);
/**
@@ -418,7 +427,13 @@
SubscriptionDatabaseManager::setNtn),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_SERVICE_CAPABILITIES,
- SubscriptionDatabaseManager::setServiceCapabilities)
+ SubscriptionDatabaseManager::setServiceCapabilities),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_TRANSFER_STATUS,
+ SubscriptionDatabaseManager::setTransferStatus),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS,
+ SubscriptionDatabaseManager::setSatelliteEntitlementStatus)
);
/**
@@ -480,7 +495,10 @@
SubscriptionDatabaseManager::setNumberFromCarrier),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_PHONE_NUMBER_SOURCE_IMS,
- SubscriptionDatabaseManager::setNumberFromIms)
+ SubscriptionDatabaseManager::setNumberFromIms),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS,
+ SubscriptionDatabaseManager::setSatelliteEntitlementPlmns)
);
/**
@@ -2099,6 +2117,51 @@
}
/**
+ * Set whether satellite entitlement status is enabled by entitlement query result.
+ *
+ * @param subId Subscription id.
+ * @param isSatelliteEntitlementStatus Whether satellite entitlement status is enabled or
+ * disabled.
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setSatelliteEntitlementStatus(int subId,
+ int isSatelliteEntitlementStatus) {
+ writeDatabaseAndCacheHelper(subId,
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS,
+ isSatelliteEntitlementStatus,
+ SubscriptionInfoInternal.Builder::setSatelliteEntitlementStatus);
+ }
+
+ /**
+ * Set satellite entitlement plmns by entitlement query result.
+ *
+ * @param subId Subscription id.
+ * @param satelliteEntitlementPlmns Satellite entitlement plmns
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setSatelliteEntitlementPlmns(int subId,
+ @NonNull String satelliteEntitlementPlmns) {
+ writeDatabaseAndCacheHelper(subId,
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS,
+ satelliteEntitlementPlmns,
+ SubscriptionInfoInternal.Builder::setSatelliteEntitlementPlmns);
+ }
+
+ /**
+ * Set satellite entitlement plmn list by entitlement query result.
+ *
+ * @param subId Subscription id.
+ * @param satelliteEntitlementPlmnList Satellite entitlement plmn list
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setSatelliteEntitlementPlmnList(int subId,
+ @NonNull List<String> satelliteEntitlementPlmnList) {
+ String satelliteEntitlementPlmns = satelliteEntitlementPlmnList.stream().collect(
+ Collectors.joining(","));
+ setSatelliteEntitlementPlmns(subId, satelliteEntitlementPlmns);
+ }
+
+ /**
* Reload the database from content provider to the cache. This must be a synchronous operation
* to prevent cache/database out-of-sync. Callers should be cautious to call this method because
* it might take longer time to complete.
@@ -2330,11 +2393,21 @@
SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER)))
.setServiceCapabilities(cursor.getInt(
cursor.getColumnIndexOrThrow(
- SimInfo.COLUMN_SERVICE_CAPABILITIES)));
+ SimInfo.COLUMN_SERVICE_CAPABILITIES)))
+ .setSatelliteEntitlementStatus(cursor.getInt(
+ cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS)))
+ .setSatelliteEntitlementPlmns(cursor.getString(
+ cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS)));
if (mFeatureFlags.oemEnabledSatelliteFlag()) {
builder.setOnlyNonTerrestrialNetwork(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_IS_NTN)));
}
+ if (mFeatureFlags.supportPsimToEsimConversion()) {
+ builder.setTransferStatus(cursor.getInt(cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_TRANSFER_STATUS)));
+ }
return builder.build();
}
@@ -2409,6 +2482,25 @@
}
/**
+ * Set the transfer status of the subscriptionInfo that corresponds to subId.
+ *
+ * @param subId Subscription ID.
+ * @param status The transfer status to change.
+ *
+ * @throws IllegalArgumentException if the subscription does not exist.
+ */
+ public void setTransferStatus(int subId, int status) {
+ if (!mFeatureFlags.supportPsimToEsimConversion()) {
+ log("SubscriptionDatabaseManager:supportPsimToEsimConversion is false");
+ return;
+ }
+
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_TRANSFER_STATUS,
+ status,
+ SubscriptionInfoInternal.Builder::setTransferStatus);
+ }
+
+ /**
* Log debug messages.
*
* @param s debug messages
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
index c6fc23d..82af4e8 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -444,7 +444,7 @@
/**
* Whether satellite attach for carrier is enabled or disabled by user.
- * By default, its disabled. It is intended to use integer to fit the database format.
+ * By default, its enabled. It is intended to use integer to fit the database format.
*/
private final int mIsSatelliteAttachEnabledForCarrier;
@@ -474,6 +474,23 @@
private final int mServiceCapabilities;
/**
+ * The transfer status of the subscription
+ */
+ private final int mTransferStatus;
+
+ /**
+ * Whether satellite entitlement status is enabled or disabled by the entitlement query result.
+ * By default, its disabled. It is intended to use integer to fit the database format.
+ */
+ private final int mIsSatelliteEntitlementStatus;
+
+ /**
+ * The satellite entitlement plmns based on the entitlement query results
+ * By default, its empty. It is intended to use string to fit the database format.
+ */
+ @NonNull private final String mSatelliteEntitlementPlmns;
+
+ /**
* Constructor from builder.
*
* @param builder Builder of {@link SubscriptionInfoInternal}.
@@ -549,6 +566,9 @@
this.mCardId = builder.mCardId;
this.mIsGroupDisabled = builder.mIsGroupDisabled;
this.mServiceCapabilities = builder.mServiceCapabilities;
+ this.mTransferStatus = builder.mTransferStatus;
+ this.mIsSatelliteEntitlementStatus = builder.mIsSatelliteEntitlementStatus;
+ this.mSatelliteEntitlementPlmns = builder.mSatelliteEntitlementPlmns;
}
/**
@@ -621,7 +641,8 @@
* @return the number of this subscription.
*/
public String getNumber() {
- return mNumber;
+ if (TextUtils.isEmpty(mNumberFromCarrier)) return mNumber;
+ return mNumberFromCarrier;
}
/**
@@ -1205,6 +1226,29 @@
public int getServiceCapabilities() {
return mServiceCapabilities;
}
+ /**
+ * @return Transfer status.
+ */
+ public int getTransferStatus() {
+ return mTransferStatus;
+ }
+
+ /**
+ * @return {@code 1} if satellite entitlement status is enabled by entitlement query result.
+ */
+ public int getSatelliteEntitlementStatus() {
+ return mIsSatelliteEntitlementStatus;
+ }
+
+ /**
+ * @return Satellite entitlement plmns is empty or not by entitlement query result.
+ *
+ * For example, "123123, 12310" or ""
+ */
+ @NonNull
+ public String getSatelliteEntitlementPlmns() {
+ return mSatelliteEntitlementPlmns;
+ }
/** @return converted {@link SubscriptionInfo}. */
@NonNull
@@ -1217,7 +1261,7 @@
.setCarrierName(mCarrierName)
.setDisplayNameSource(mDisplayNameSource)
.setIconTint(mIconTint)
- .setNumber(mNumber)
+ .setNumber(getNumber())
.setDataRoaming(mDataRoaming)
.setMcc(mMcc)
.setMnc(mMnc)
@@ -1244,6 +1288,7 @@
.setOnlyNonTerrestrialNetwork(mIsOnlyNonTerrestrialNetwork == 1)
.setServiceCapabilities(
SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities))
+ .setTransferStatus(mTransferStatus)
.build();
}
@@ -1264,7 +1309,7 @@
+ " displayNameSource="
+ SubscriptionManager.displayNameSourceToString(mDisplayNameSource)
+ " iconTint=" + mIconTint
- + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
+ + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, getNumber())
+ " dataRoaming=" + mDataRoaming
+ " mcc=" + mMcc
+ " mnc=" + mMnc
@@ -1304,6 +1349,9 @@
+ " getOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
+ " isGroupDisabled=" + mIsGroupDisabled
+ " serviceCapabilities=" + mServiceCapabilities
+ + " transferStatus=" + mTransferStatus
+ + " satelliteEntitlementStatus=" + mIsSatelliteEntitlementStatus
+ + " satelliteEntitlementPlmns=" + mSatelliteEntitlementPlmns
+ "]";
}
@@ -1361,7 +1409,10 @@
that.mNumberFromCarrier) && mNumberFromIms.equals(that.mNumberFromIms)
&& mIsSatelliteAttachEnabledForCarrier == that.mIsSatelliteAttachEnabledForCarrier
&& mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork
- && mServiceCapabilities == that.mServiceCapabilities;
+ && mServiceCapabilities == that.mServiceCapabilities
+ && mTransferStatus == that.mTransferStatus
+ && mIsSatelliteEntitlementStatus == that.mIsSatelliteEntitlementStatus
+ && mSatelliteEntitlementPlmns == that.mSatelliteEntitlementPlmns;
}
@Override
@@ -1384,7 +1435,8 @@
mNumberFromIms, mPortIndex, mUsageSetting, mLastUsedTPMessageReference, mUserId,
mIsSatelliteEnabled, mCardId, mIsGroupDisabled,
mIsSatelliteAttachEnabledForCarrier, mIsOnlyNonTerrestrialNetwork,
- mServiceCapabilities);
+ mServiceCapabilities, mTransferStatus, mIsSatelliteEntitlementStatus,
+ mSatelliteEntitlementPlmns);
result = 31 * result + Arrays.hashCode(mNativeAccessRules);
result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
result = 31 * result + Arrays.hashCode(mRcsConfig);
@@ -1750,7 +1802,7 @@
/**
* Whether satellite attach for carrier is enabled by user.
*/
- private int mIsSatelliteAttachEnabledForCarrier = 0;
+ private int mIsSatelliteAttachEnabledForCarrier = 1;
/**
* Whether this subscription is used for communicating with non-terrestrial network or not.
@@ -1777,6 +1829,22 @@
private int mServiceCapabilities;
/**
+ * The transfer status of the subscription
+ */
+ private int mTransferStatus;
+
+ /**
+ * Whether satellite entitlement status is enabled by entitlement query result.
+ */
+ private int mIsSatelliteEntitlementStatus = 0;
+
+ /**
+ * Whether satellite entitlement plmns is empty or not by entitlement query result.
+ */
+ @NonNull
+ private String mSatelliteEntitlementPlmns = "";
+
+ /**
* Default constructor.
*/
public Builder() {
@@ -1855,6 +1923,9 @@
mCardId = info.mCardId;
mIsGroupDisabled = info.mIsGroupDisabled;
mServiceCapabilities = info.mServiceCapabilities;
+ mTransferStatus = info.mTransferStatus;
+ mIsSatelliteEntitlementStatus = info.mIsSatelliteEntitlementStatus;
+ mSatelliteEntitlementPlmns = info.mSatelliteEntitlementPlmns;
}
/**
@@ -2787,6 +2858,44 @@
}
/**
+ * Set the transfer status of the subscription.
+ *
+ * @param status The transfer status
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setTransferStatus(int status) {
+ mTransferStatus = status;
+ return this;
+ }
+
+ /**
+ * Set whether satellite entitlement status is enabled by entitlement query result.
+ *
+ * @param isSatelliteEntitlementStatus {@code 1} if satellite entitlement status is
+ * enabled by entitlement query result.
+ * @return The builder
+ */
+ @NonNull
+ public Builder setSatelliteEntitlementStatus(int isSatelliteEntitlementStatus) {
+ mIsSatelliteEntitlementStatus = isSatelliteEntitlementStatus;
+ return this;
+ }
+
+ /**
+ * Set whether satellite entitlement plmns is empty or not by entitlement query result.
+ *
+ * @param satelliteEntitlementPlmns satellite entitlement plmns is empty or not by
+ * entitlement query result.
+ * @return The builder
+ */
+ @NonNull
+ public Builder setSatelliteEntitlementPlmns(@NonNull String satelliteEntitlementPlmns) {
+ mSatelliteEntitlementPlmns = satelliteEntitlementPlmns;
+ return this;
+ }
+
+ /**
* Build the {@link SubscriptionInfoInternal}.
*
* @return The {@link SubscriptionInfoInternal} instance.
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index ddf80a8..8757c97 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -22,6 +22,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -186,7 +187,9 @@
SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED,
SimInfo.COLUMN_SATELLITE_ENABLED,
SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
- SimInfo.COLUMN_IS_NTN
+ SimInfo.COLUMN_IS_NTN,
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS,
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS
);
/**
@@ -922,6 +925,26 @@
}
/**
+ * Set the group owner on the subscription
+ *
+ * <p> Note: This only sets the group owner field and doesn't update other relevant fields.
+ * Prefer to call {@link #addSubscriptionsIntoGroup}.
+ *
+ * @param subId Subscription id.
+ * @param groupOwner The group owner to assign to the subscription
+ */
+ public void setGroupOwner(int subId, @NonNull String groupOwner) {
+ // This can throw IllegalArgumentException if the subscription does not exist.
+ try {
+ mSubscriptionDatabaseManager.setGroupOwner(
+ subId,
+ groupOwner);
+ } catch (IllegalArgumentException e) {
+ loge("setManaged: invalid subId=" + subId);
+ }
+ }
+
+ /**
* Set last used TP message reference.
*
* @param subId Subscription id.
@@ -1219,7 +1242,11 @@
builder.setCardString(mUiccController.convertToCardString(cardId));
}
+ if (mFeatureFlags.supportPsimToEsimConversion()) {
+ builder.setTransferStatus(subInfo.getTransferStatus());
+ }
embeddedSubs.add(subInfo.getSubscriptionId());
+
subInfo = builder.build();
log("updateEmbeddedSubscriptions: update subscription " + subInfo);
mSubscriptionDatabaseManager.updateSubscription(subInfo);
@@ -2161,6 +2188,7 @@
* @return 0 if success, < 0 on error
*
* @throws SecurityException if the caller does not have required permissions.
+ * @throws IllegalArgumentException if {@code slotIndex} is invalid.
*/
@Override
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -2174,6 +2202,11 @@
enforceTelephonyFeatureWithException(getCurrentPackageName(), "addSubInfo");
+ if (!SubscriptionManager.isValidSlotIndex(slotIndex)
+ && slotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ throw new IllegalArgumentException("Invalid slotIndex " + slotIndex);
+ }
+
// Now that all security checks passes, perform the operation as ourselves.
final long identity = Binder.clearCallingIdentity();
try {
@@ -3911,12 +3944,31 @@
}
/**
+ * Returns whether the given subscription is associated with the calling user.
+ *
+ * @param subscriptionId the subscription ID of the subscription
+ * @return {@code true} if the subscription is associated with the user that the calling process
+ * is running in; {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if subscription doesn't exist.
+ * @throws SecurityException if the caller doesn't have permissions required.
+ */
+ @Override
+ public boolean isSubscriptionAssociatedWithCallingUser(int subscriptionId) {
+ enforcePermissions("isSubscriptionAssociatedWithCallingUser",
+ Manifest.permission.READ_PHONE_STATE);
+
+ UserHandle myUserHandle = UserHandle.of(UserHandle.getCallingUserId());
+ return mFeatureFlags.subscriptionUserAssociationQuery()
+ && isSubscriptionAssociatedWithUserNoCheck(subscriptionId, myUserHandle);
+ }
+
+ /**
* Check if subscription and user are associated with each other.
*
* @param subscriptionId the subId of the subscription
* @param userHandle user handle of the user
* @return {@code true} if subscription is associated with user
- * {@code true} if there are no subscriptions on device
* else {@code false} if subscription is not associated with user.
*
* @throws SecurityException if the caller doesn't have permissions required.
@@ -3927,6 +3979,12 @@
@NonNull UserHandle userHandle) {
enforcePermissions("isSubscriptionAssociatedWithUser",
Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+
+ return isSubscriptionAssociatedWithUserNoCheck(subscriptionId, userHandle);
+ }
+
+ private boolean isSubscriptionAssociatedWithUserNoCheck(int subscriptionId,
+ @NonNull UserHandle userHandle) {
SubscriptionInfoInternal subInfoInternal = mSubscriptionDatabaseManager
.getSubscriptionInfoInternal(subscriptionId);
// Throw IAE if no record of the sub's association state.
@@ -4299,6 +4357,70 @@
}
}
+
+
+ /**
+ * Set the transfer status of the subscriptionInfo that corresponds to subId.
+ * @param subId The unique SubscriptionInfo key in database.
+ * @param status The transfer status to change. This value must be one of the following.
+ * {@link SubscriptionManager#TRANSFER_STATUS_NONE},
+ * {@link SubscriptionManager#TRANSFER_STATUS_TRANSFERRED_OUT} or
+ * {@link SubscriptionManager#TRANSFER_STATUS_CONVERTED}
+ *
+ */
+ @Override
+ @EnforcePermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ public void setTransferStatus(int subId, int status) {
+ setTransferStatus_enforcePermission();
+ if (mContext.checkCallingOrSelfPermission(
+ Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to"
+ + "setTransferStatus");
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ mSubscriptionDatabaseManager.setTransferStatus(subId, status);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Set the satellite entitlement plmn list value in the subscription database.
+ *
+ * @param subId subscription id.
+ * @param satelliteEntitlementPlmnList satellite entitlement plmn list
+ */
+ public void setSatelliteEntitlementPlmnList(int subId,
+ @NonNull List<String> satelliteEntitlementPlmnList) {
+ try {
+ mSubscriptionDatabaseManager.setSatelliteEntitlementPlmnList(
+ subId, satelliteEntitlementPlmnList);
+ } catch (IllegalArgumentException e) {
+ loge("setSatelliteEntitlementPlmnList: invalid subId=" + subId);
+ }
+ }
+
+ /**
+ * Get the satellite entitlement plmn list value from the subscription database.
+ *
+ * @param subId subscription id.
+ * @return satellite entitlement plmn list
+ */
+ @NonNull
+ public List<String> getSatelliteEntitlementPlmnList(int subId) {
+ SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager.getSubscriptionInfoInternal(
+ subId);
+ if (subInfo == null) {
+ loge("getSatelliteEntitlementPlmnList: invalid subId=" + subId);
+ return new ArrayList<>();
+ }
+
+ return Arrays.stream(subInfo.getSatelliteEntitlementPlmns().split(",")).collect(
+ Collectors.toList());
+ }
+
/**
* Get the current calling package name.
* @return the current calling package name
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index da3920e..bef8944 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -34,6 +34,7 @@
import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.app.UiModeManager;
+import android.app.admin.DevicePolicyManager;
import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -310,6 +311,8 @@
return mNetworkPolicyManager;
case Context.TELEPHONY_IMS_SERVICE:
return mImsManager;
+ case Context.DEVICE_POLICY_SERVICE:
+ return mDevicePolicyManager;
default:
return null;
}
@@ -357,6 +360,8 @@
return Context.EUICC_SERVICE;
} else if (serviceClass == AlarmManager.class) {
return Context.ALARM_SERVICE;
+ } else if (serviceClass == DevicePolicyManager.class) {
+ return Context.DEVICE_POLICY_SERVICE;
}
return super.getSystemServiceName(serviceClass);
}
@@ -731,6 +736,7 @@
private final VcnManager mVcnManager = mock(VcnManager.class);
private final NetworkPolicyManager mNetworkPolicyManager = mock(NetworkPolicyManager.class);
private final ImsManager mImsManager = mock(ImsManager.class);
+ private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
private final Configuration mConfiguration = new Configuration();
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private final SharedPreferences mSharedPreferences = PreferenceManager
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index c4b957e..a13a92c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -130,9 +130,12 @@
+ UserHandle.USER_NULL + ","
+ Telephony.SimInfo.COLUMN_SATELLITE_ENABLED + " INTEGER DEFAULT 0,"
+ Telephony.SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
- + " INTEGER DEFAULT 0, "
+ + " INTEGER DEFAULT 1, "
+ Telephony.SimInfo.COLUMN_IS_NTN + " INTEGER DEFAULT 0,"
- + Telephony.SimInfo.COLUMN_SERVICE_CAPABILITIES + " INTEGER DEFAULT 7"
+ + Telephony.SimInfo.COLUMN_SERVICE_CAPABILITIES + " INTEGER DEFAULT 7,"
+ + Telephony.SimInfo.COLUMN_TRANSFER_STATUS + " INTEGER DEFAULT 0,"
+ + Telephony.SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS + " INTEGER DEFAULT 0,"
+ + Telephony.SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS + " TEXT"
+ ");";
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index c1f0c5f..7de75ae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -75,7 +75,7 @@
mSimulatedCommands.setRadioPower(true, null);
mPhone.mCi = this.mSimulatedCommands;
- mCTUT = new GsmCdmaCallTracker(mPhone);
+ mCTUT = new GsmCdmaCallTracker(mPhone, mFeatureFlags);
logd("GsmCdmaCallTracker initiated, waiting for Power on");
/* Make sure radio state is power on before dial.
* When radio state changed from off to on, CallTracker
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index a903a34..e493a18 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -2868,7 +2868,10 @@
@Test
public void testCellularIdentifierDisclosure_disclosureEventAddedToNotifier() {
+ int phoneId = 0;
+ int subId = 10;
when(mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()).thenReturn(true);
+ when(mSubscriptionManagerService.getSubId(phoneId)).thenReturn(subId);
Phone phoneUT =
new GsmCdmaPhone(
@@ -2876,7 +2879,7 @@
mMockCi,
mNotifier,
true,
- 0,
+ phoneId,
PhoneConstants.PHONE_TYPE_GSM,
mTelephonyComponentFactory,
(c, p) -> mImsManager,
@@ -2895,20 +2898,22 @@
processAllMessages();
verify(mIdentifierDisclosureNotifier, times(1))
- .addDisclosure(eq(mPhoneUT.getSubId()), eq(disclosure));
+ .addDisclosure(eq(mContext), eq(subId), eq(disclosure));
}
@Test
public void testCellularIdentifierDisclosure_disclosureEventNull() {
+ int phoneId = 4;
+ int subId = 6;
when(mFeatureFlags.enableIdentifierDisclosureTransparencyUnsolEvents()).thenReturn(true);
-
+ when(mSubscriptionManagerService.getSubId(phoneId)).thenReturn(subId);
Phone phoneUT =
new GsmCdmaPhone(
mContext,
mMockCi,
mNotifier,
true,
- 0,
+ phoneId,
PhoneConstants.PHONE_TYPE_GSM,
mTelephonyComponentFactory,
(c, p) -> mImsManager,
@@ -2920,7 +2925,7 @@
processAllMessages();
verify(mIdentifierDisclosureNotifier, never())
- .addDisclosure(eq(mPhoneUT.getSubId()), any(CellularIdentifierDisclosure.class));
+ .addDisclosure(eq(mContext), eq(subId), any(CellularIdentifierDisclosure.class));
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index 0869bab..1ea989a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -42,6 +43,9 @@
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.telephony.data.DataCallResponse;
+import android.telephony.data.EpsQos;
+import android.telephony.data.Qos;
+import android.telephony.data.QosBearerSession;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -57,6 +61,7 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
@RunWith(AndroidTestingRunner.class)
@@ -268,7 +273,7 @@
assertEquals("DefaultState", getCurrentState().getName());
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
+ DataCallResponse.LINK_STATUS_DORMANT);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -285,7 +290,9 @@
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(false);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null,
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -305,7 +312,7 @@
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
+ DataCallResponse.LINK_STATUS_DORMANT);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -316,7 +323,7 @@
assertEquals("DefaultState", getCurrentState().getName());
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+ DataCallResponse.LINK_STATUS_ACTIVE);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
@@ -335,7 +342,9 @@
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(true);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null,
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
@@ -356,7 +365,7 @@
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+ DataCallResponse.LINK_STATUS_ACTIVE);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
@@ -621,32 +630,37 @@
physicalChannelConfigs.add(pcc2);
doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
// bands and bandwidths should stay ratcheted even if an empty PCC list is sent
doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, new ArrayList<>(), null));
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
// bands and bandwidths should stay ratcheted as long as anchor NR cell is the same
physicalChannelConfigs.remove(pcc2);
doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
// bands and bandwidths should no longer be ratcheted if anchor NR cell changes
// add pcc3 to front of list to ensure anchor NR cell changes from 1 -> 3
physicalChannelConfigs.add(0, pcc3);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected", getCurrentState().getName());
physicalChannelConfigs.add(pcc2);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
}
@@ -680,20 +694,23 @@
physicalChannelConfigs.add(pcc2);
doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
// bands and bandwidths should stay ratcheted even if an empty PCC list is sent
doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, new ArrayList<>(), null));
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
// bands and bandwidths should change if PCC list changes
physicalChannelConfigs.remove(pcc2);
doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected", getCurrentState().getName());
}
@@ -729,19 +746,22 @@
physicalChannelConfigs.add(pcc2);
doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
// bands and bandwidths should not stay the same even if an empty PCC list is sent
doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, new ArrayList<>(), null));
processAllMessages();
assertEquals("connected", getCurrentState().getName());
// bands and bandwidths should change if PCC list changes
doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
}
@@ -752,7 +772,7 @@
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+ DataCallResponse.LINK_STATUS_ACTIVE);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
@@ -769,7 +789,9 @@
testTransitionToCurrentStateNrConnectedMmwave();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(true);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null,
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
@@ -790,7 +812,7 @@
testTransitionToCurrentStateNrConnectedMmwave();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+ DataCallResponse.LINK_STATUS_ACTIVE);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
@@ -806,7 +828,9 @@
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null,
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -824,7 +848,7 @@
// Transition to LTE connected state
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+ DataCallResponse.LINK_STATUS_ACTIVE);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
@@ -834,7 +858,9 @@
// LTE -> LTE+
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null,
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -852,7 +878,7 @@
// Transition to idle state
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
+ DataCallResponse.LINK_STATUS_DORMANT);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -862,7 +888,9 @@
// LTE -> LTE+
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null,
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -874,7 +902,7 @@
testTransitionToCurrentStateLteConnected();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
+ DataCallResponse.LINK_STATUS_DORMANT);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -891,7 +919,9 @@
testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
setPhysicalLinkStatus(false);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null,
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList(), null));
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -911,7 +941,7 @@
testTransitionToCurrentStateLteConnected_usingUserDataForRrcDetection();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_DORMANT, null));
+ DataCallResponse.LINK_STATUS_DORMANT);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -932,15 +962,32 @@
@Test
public void testEventRadioOffOrUnavailable() throws Exception {
- testTransitionToCurrentStateNrConnected();
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
- mNetworkTypeController.getOverrideNetworkType());
+ mBundle.putBoolean(CarrierConfigManager.KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL,
+ true);
+ testTransitionToCurrentStateNrConnectedMmwaveWithAdditionalBandAndNoMmwaveNrNsa();
+ // Radio off
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(9 /* EVENT_RADIO_OFF_OR_UNAVAILABLE */);
processAllMessages();
+
+ assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
+
+ // NR connected: Primary serving NR PCC with cell ID = 1, band = none
+ PhysicalChannelConfig pcc = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setPhysicalCellId(1)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .build();
+
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, List.of(pcc), null));
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+ assertEquals("connected", getCurrentState().getName());
}
@Test
@@ -992,6 +1039,54 @@
}
@Test
+ public void testPrimaryTimerNetworkTypeChanged() throws Exception {
+ doAnswer(invocation -> {
+ doReturn(new TelephonyDisplayInfo(
+ mNetworkTypeController.getDataNetworkType(),
+ mNetworkTypeController.getOverrideNetworkType(),
+ false)).when(mDisplayInfoController).getTelephonyDisplayInfo();
+ return null;
+ }).when(mDisplayInfoController).updateTelephonyDisplayInfo();
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
+ sendCarrierConfigChanged();
+
+ assertEquals("connected", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // trigger 10 second timer after disconnecting from NR advanced
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals("legacy", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // timer expires
+ moveTimeForward(10 * 1000);
+ processAllMessages();
+
+ assertEquals("legacy", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
+ }
+
+ @Test
public void testPrimaryTimerDeviceIdleMode() throws Exception {
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
@@ -1339,6 +1434,89 @@
}
@Test
+ public void testSecondaryTimerAdvanceBand() throws Exception {
+ doReturn(true).when(mFeatureFlags).supportNrSaRrcIdle();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
+ ArrayList<PhysicalChannelConfig> physicalChannelConfigs = new ArrayList<>();
+ // use advanced band
+ physicalChannelConfigs.add(new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(1)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .setBand(41)
+ .build());
+ doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
+ mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
+ new int[]{41});
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,10");
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,5");
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT,
+ 20);
+ sendCarrierConfigChanged();
+
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // lost the advance band, trigger 10 second connected_mmwave -> connected primary timer
+ physicalChannelConfigs.clear();
+ physicalChannelConfigs.add(new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(1)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .build());
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
+ processAllMessages();
+
+ assertEquals("connected", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // switch to connected_rrc_idle before primary timer expires
+ physicalChannelConfigs.clear();
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
+ processAllMessages();
+
+ assertEquals("connected_rrc_idle", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // primary timer expires
+ moveTimeForward(10 * 1000);
+ processAllMessages();
+
+ // should trigger 20(not 5) seconds connected_mmwave -> connected_rrc_idle secondary timer
+ assertEquals("connected_rrc_idle", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // 5 seconds passed during connected_mmwave -> connected_rrc_idle secondary timer
+ moveTimeForward(5 * 1000);
+ processAllMessages();
+ assertEquals("connected_rrc_idle", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // secondary timer expired
+ moveTimeForward(15 * 1000);
+ processAllMessages();
+
+ assertEquals("connected_rrc_idle", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
+ }
+
+ @Test
public void testSecondaryTimerExpireNrIdle() throws Exception {
doReturn(true).when(mFeatureFlags).supportNrSaRrcIdle();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
@@ -1369,7 +1547,8 @@
.setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
.setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
.build());
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected", getCurrentState().getName());
@@ -1379,7 +1558,8 @@
// switch to connected_rrc_idle
physicalChannelConfigs.clear();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_rrc_idle", getCurrentState().getName());
@@ -1438,7 +1618,8 @@
.setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
.setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
.build());
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected", getCurrentState().getName());
@@ -1448,7 +1629,8 @@
// switch to connected_rrc_idle
physicalChannelConfigs.clear();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_rrc_idle", getCurrentState().getName());
@@ -1473,7 +1655,8 @@
.setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
.setBand(41)
.build());
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
// secondary timer expires
moveTimeForward(30 * 1000);
@@ -1517,7 +1700,8 @@
.setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
.setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
.build());
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected", getCurrentState().getName());
@@ -1527,7 +1711,8 @@
// switch to connected_rrc_idle
physicalChannelConfigs.clear();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_rrc_idle", getCurrentState().getName());
@@ -1552,7 +1737,8 @@
.setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
.setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
.build());
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
assertEquals("connected_rrc_idle", getCurrentState().getName());
@@ -1611,7 +1797,7 @@
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
mNetworkTypeController.sendMessage(4 /* EVENT_PHYSICAL_LINK_STATUS_CHANGED */,
- new AsyncResult(null, DataCallResponse.LINK_STATUS_ACTIVE, null));
+ DataCallResponse.LINK_STATUS_ACTIVE);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
@@ -1677,6 +1863,91 @@
assertTrue(mNetworkTypeController.areAnyTimersActive());
}
+ @Test
+ public void testNrTimerResetWhenPlmnChanged() throws Exception {
+ testTransitionToCurrentStateNrConnectedMmwave();
+ mBundle.putBoolean(CarrierConfigManager.KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL,
+ true);
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,30");
+ sendCarrierConfigChanged();
+
+ // should trigger 10 second primary timer
+ doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN).when(mServiceState).getNrFrequencyRange();
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals("legacy", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // PLMN changed, should cancel any active timers
+ ServiceState newSS = mock(ServiceState.class);
+ doReturn("different plmn").when(newSS).getOperatorNumeric();
+ doReturn(newSS).when(mSST).getServiceState();
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
+ }
+
+ @Test
+ public void testNrTimerResetWhenVoiceQos() throws Exception {
+ testTransitionToCurrentStateNrConnectedMmwave();
+ mBundle.putBoolean(CarrierConfigManager.KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL,
+ true);
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,30");
+ sendCarrierConfigChanged();
+
+ // should trigger 10 second primary timer
+ doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN).when(mServiceState).getNrFrequencyRange();
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+
+ assertEquals("legacy", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // Qos changed, but not in call, so no thing happens.
+ ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
+ ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
+ verify(mDataNetworkController).registerDataNetworkControllerCallback(
+ dataNetworkControllerCallbackCaptor.capture());
+ DataNetworkControllerCallback callback = dataNetworkControllerCallbackCaptor.getValue();
+ callback.onQosSessionsChanged(List.of(
+ new QosBearerSession(1, new EpsQos(
+ new Qos.QosBandwidth(1000, 1),
+ new Qos.QosBandwidth(1000, 0),
+ 9 /* QCI */), Collections.emptyList())));
+ processAllMessages();
+
+ // Confirm if QCI not 1, timers are still active.
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // Send Voice QOS where QCI is 1, confirm timers are cancelled.
+ callback.onQosSessionsChanged(List.of(
+ new QosBearerSession(1, new EpsQos(
+ new Qos.QosBandwidth(1000, 1),
+ new Qos.QosBandwidth(1000, 0),
+ 1 /* QCI */), Collections.emptyList())));
+ processAllMessages();
+
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
+ }
+
private void setPhysicalLinkStatus(boolean state) {
List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
// If PhysicalChannelConfigList is empty, PhysicalLinkStatus is
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 90105e3..6743d1c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -38,6 +38,7 @@
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
+import android.telephony.ModemInfo;
import android.telephony.PhoneCapability;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyRegistryManager;
@@ -55,8 +56,12 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -69,10 +74,24 @@
PhoneConfigurationManager.MockableInterface mMi;
private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 1;
+ private static final PhoneCapability STATIC_DSDA_CAPABILITY;
PhoneConfigurationManager mPcm;
private FeatureFlags mFeatureFlags;
private TelephonyRegistryManager mMockRegistryManager;
+ static {
+ ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true);
+ ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true);
+
+ List<ModemInfo> logicalModemList = new ArrayList<>();
+ logicalModemList.add(modemInfo1);
+ logicalModemList.add(modemInfo2);
+ int[] deviceNrCapabilities = new int[0];
+
+ STATIC_DSDA_CAPABILITY = new PhoneCapability(2, 1, logicalModemList, false,
+ deviceNrCapabilities);
+ }
+
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
@@ -85,7 +104,7 @@
mPhone.mCi = mMockCi0;
mCT.mCi = mMockCi0;
mPhone1.mCi = mMockCi1;
- doReturn(RIL.RADIO_HAL_VERSION_2_1).when(mMockRadioConfigProxy).getVersion();
+ doReturn(RIL.RADIO_HAL_VERSION_2_2).when(mMockRadioConfigProxy).getVersion();
mMockRegistryManager = mContext.getSystemService(TelephonyRegistryManager.class);
}
@@ -138,15 +157,7 @@
init(1);
assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
- ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
- verify(mMockRadioConfig).getPhoneCapability(captor.capture());
- Message msg = captor.getValue();
- AsyncResult.forMessage(msg, PhoneCapability.DEFAULT_DSDS_CAPABILITY, null);
- msg.sendToTarget();
- processAllMessages();
-
- // Not static capability should indicate DSDS capable.
- assertEquals(PhoneCapability.DEFAULT_DSDS_CAPABILITY, mPcm.getStaticPhoneCapability());
+ setAndVerifyStaticCapability(PhoneCapability.DEFAULT_DSDS_CAPABILITY);
}
@Test
@@ -211,12 +222,77 @@
assertTrue(mPcm.getSlotsSupportingSimultaneousCellularCalls().isEmpty());
}
+ /**
+ * If the device uses the older "dsda" multi_sim_config setting, ensure that DSDA is set
+ * statically for that device and subId updates work.
+ */
+ @Test
+ @SmallTest
+ public void testBkwdsCompatSimultaneousCallingDsda() throws Exception {
+ doReturn(true).when(mFeatureFlags).simultaneousCallingIndications();
+ doReturn(RIL.RADIO_HAL_VERSION_2_1).when(mMockRadioConfigProxy).getVersion();
+ doReturn(Optional.of("dsda")).when(mMi).getMultiSimProperty();
+ final int phone0SubId = 2;
+ final int phone1SubId = 3;
+ mPhones = new Phone[]{mPhone, mPhone1};
+ doReturn(0).when(mPhone).getPhoneId();
+ doReturn(1).when(mPhone1).getPhoneId();
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ init(2);
+ doReturn(phone0SubId).when(mPhone).getSubId();
+ doReturn(phone1SubId).when(mPhone1).getSubId();
+ Set<Integer>[] cachedSimultaneousCallingSlots = new Set[]{Collections.emptySet()};
+ mPcm.registerForSimultaneousCellularCallingSlotsChanged(newSlots ->
+ cachedSimultaneousCallingSlots[0] = newSlots);
+
+ mPcm.getStaticPhoneCapability();
+ setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
+ ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> cBCaptor =
+ ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
+ verify(mMockRegistryManager).addOnSubscriptionsChangedListener(cBCaptor.capture(), any());
+ processAllMessages();
+
+ int[] enabledLogicalSlots = {0, 1};
+ HashSet<Integer> expectedSlots = new HashSet<>(2);
+ for (int i : enabledLogicalSlots) {
+ expectedSlots.add(i);
+ }
+ HashSet<Integer> expectedSubIds = new HashSet<>(2);
+ expectedSubIds.add(phone0SubId);
+ expectedSubIds.add(phone1SubId);
+ assertEquals(expectedSlots, mPcm.getSlotsSupportingSimultaneousCellularCalls());
+ verify(mMockRegistryManager).notifySimultaneousCellularCallingSubscriptionsChanged(
+ eq(expectedSubIds));
+ assertEquals(expectedSlots, cachedSimultaneousCallingSlots[0]);
+
+ // Change sub ID mapping
+ final int phone1SubIdV2 = 4;
+ expectedSubIds.clear();
+ expectedSubIds.add(phone0SubId);
+ expectedSubIds.add(phone1SubIdV2);
+ doReturn(phone1SubIdV2).when(mPhone1).getSubId();
+ cBCaptor.getValue().onSubscriptionsChanged();
+ processAllMessages();
+ verify(mMockRegistryManager, times(2))
+ .notifySimultaneousCellularCallingSubscriptionsChanged(eq(expectedSubIds));
+ }
+
@Test
@SmallTest
public void testUpdateSimultaneousCallingSupportNotifications() throws Exception {
// retry simultaneous calling tests, but with notifications enabled this time
doReturn(true).when(mFeatureFlags).simultaneousCallingIndications();
+
+ final int phone0SubId = 2;
+ final int phone1SubId = 3;
+ mPhones = new Phone[]{mPhone, mPhone1};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
init(2);
+ doReturn(phone0SubId).when(mPhone).getSubId();
+ doReturn(phone1SubId).when(mPhone1).getSubId();
+ Set<Integer>[] cachedSimultaneousCallingSlots = new Set[]{Collections.emptySet()};
+ mPcm.registerForSimultaneousCellularCallingSlotsChanged(newSlots ->
+ cachedSimultaneousCallingSlots[0] = newSlots);
// Simultaneous calling enabled
mPcm.updateSimultaneousCallingSupport();
@@ -228,13 +304,17 @@
msg.sendToTarget();
processAllMessages();
- HashSet<Integer> expectedSlots = new HashSet<>();
+ HashSet<Integer> expectedSlots = new HashSet<>(2);
for (int i : enabledLogicalSlots) {
expectedSlots.add(i);
}
+ HashSet<Integer> expectedSubIds = new HashSet<>(2);
+ expectedSubIds.add(phone0SubId);
+ expectedSubIds.add(phone1SubId);
assertEquals(expectedSlots, mPcm.getSlotsSupportingSimultaneousCellularCalls());
verify(mMockRegistryManager).notifySimultaneousCellularCallingSubscriptionsChanged(
- eq(expectedSlots));
+ eq(expectedSubIds));
+ assertEquals(expectedSlots, cachedSimultaneousCallingSlots[0]);
// Simultaneous Calling Disabled
mPcm.updateSimultaneousCallingSupport();
@@ -249,6 +329,59 @@
assertEquals(Collections.emptySet(), mPcm.getSlotsSupportingSimultaneousCellularCalls());
verify(mMockRegistryManager, times(2))
.notifySimultaneousCellularCallingSubscriptionsChanged(eq(Collections.emptySet()));
+ assertEquals(Collections.emptySet(), cachedSimultaneousCallingSlots[0]);
+ }
+
+ @Test
+ @SmallTest
+ public void testSimultaneousCallingSubIdMappingChanges() throws Exception {
+ doReturn(true).when(mFeatureFlags).simultaneousCallingIndications();
+ final int phone0SubId = 2;
+ final int phone1SubId = 3;
+ mPhones = new Phone[]{mPhone, mPhone1};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ init(2);
+ doReturn(phone0SubId).when(mPhone).getSubId();
+ doReturn(phone1SubId).when(mPhone1).getSubId();
+
+ // Set the capability to DSDA mode to register listener, which will also trigger
+ // simultaneous calling evaluation
+ mPcm.getCurrentPhoneCapability();
+ setAndVerifyStaticCapability(STATIC_DSDA_CAPABILITY);
+ ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> cBCaptor =
+ ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
+ verify(mMockRegistryManager).addOnSubscriptionsChangedListener(cBCaptor.capture(), any());
+
+ int[] enabledLogicalSlots = {0, 1};
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockRadioConfig).updateSimultaneousCallingSupport(captor.capture());
+ Message msg = captor.getValue();
+ // Simultaneous calling enabled
+ AsyncResult.forMessage(msg, enabledLogicalSlots, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ HashSet<Integer> expectedSlots = new HashSet<>(2);
+ for (int i : enabledLogicalSlots) {
+ expectedSlots.add(i);
+ }
+ HashSet<Integer> expectedSubIds = new HashSet<>(2);
+ expectedSubIds.add(phone0SubId);
+ expectedSubIds.add(phone1SubId);
+ assertEquals(expectedSlots, mPcm.getSlotsSupportingSimultaneousCellularCalls());
+ verify(mMockRegistryManager).notifySimultaneousCellularCallingSubscriptionsChanged(
+ eq(expectedSubIds));
+
+ // Change sub ID mapping
+ final int phone1SubIdV2 = 4;
+ expectedSubIds.clear();
+ expectedSubIds.add(phone0SubId);
+ expectedSubIds.add(phone1SubIdV2);
+ doReturn(phone1SubIdV2).when(mPhone1).getSubId();
+ cBCaptor.getValue().onSubscriptionsChanged();
+ processAllMessages();
+ verify(mMockRegistryManager, times(2))
+ .notifySimultaneousCellularCallingSubscriptionsChanged(eq(expectedSubIds));
}
@Test
@@ -429,4 +562,15 @@
verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any());
verify(mMockCi1, times(1)).onSlotActiveStatusChange(anyBoolean());
}
+
+ private void setAndVerifyStaticCapability(PhoneCapability capability) {
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockRadioConfig).getPhoneCapability(captor.capture());
+ Message msg = captor.getValue();
+ AsyncResult.forMessage(msg, capability, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ assertEquals(capability, mPcm.getStaticPhoneCapability());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
index 4afdfe9..ac92b8f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -68,6 +68,7 @@
.setOnlyNonTerrestrialNetwork(true)
.setServiceCapabilities(SubscriptionManager.getServiceCapabilitiesSet(
SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK))
+ .setTransferStatus(1)
.build();
}
@@ -92,6 +93,9 @@
}
assertThat(mSubscriptionInfoUT.getServiceCapabilities()).isEqualTo(
Set.of(SubscriptionManager.SERVICE_CAPABILITY_DATA));
+ if (Flags.supportPsimToEsimConversion()) {
+ assertThat(mSubscriptionInfoUT.getTransferStatus()).isEqualTo(1);
+ }
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index b515372..a6e06e3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -125,6 +125,7 @@
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.satellite.SatelliteController;
import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
+import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource;
import com.android.internal.telephony.security.NullCipherNotifier;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.test.SimulatedCommands;
@@ -285,6 +286,7 @@
protected ServiceStateStats mServiceStateStats;
protected SatelliteController mSatelliteController;
protected DeviceStateHelper mDeviceStateHelper;
+ protected CellularNetworkSecuritySafetySource mSafetySource;
protected CellularIdentifierDisclosureNotifier mIdentifierDisclosureNotifier;
protected DomainSelectionResolver mDomainSelectionResolver;
protected NullCipherNotifier mNullCipherNotifier;
@@ -422,6 +424,16 @@
field.set(obj, newValue);
}
+ protected static <T> T getPrivateField(Object object, String fieldName, Class<T> fieldType)
+ throws Exception {
+
+ Class<?> clazz = object.getClass();
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+
+ return fieldType.cast(field.get(object));
+ }
+
protected synchronized void restoreInstance(final Class c, final String instanceName,
final Object obj) throws Exception {
InstanceKey key = new InstanceKey(c, instanceName, obj);
@@ -560,6 +572,7 @@
mServiceStateStats = Mockito.mock(ServiceStateStats.class);
mSatelliteController = Mockito.mock(SatelliteController.class);
mDeviceStateHelper = Mockito.mock(DeviceStateHelper.class);
+ mSafetySource = Mockito.mock(CellularNetworkSecuritySafetySource.class);
mIdentifierDisclosureNotifier = Mockito.mock(CellularIdentifierDisclosureNotifier.class);
mDomainSelectionResolver = Mockito.mock(DomainSelectionResolver.class);
mNullCipherNotifier = Mockito.mock(NullCipherNotifier.class);
@@ -635,7 +648,7 @@
nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class),
nullable(Object.class), any(FeatureFlags.class));
doReturn(mCT).when(mTelephonyComponentFactory)
- .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
+ .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class), any(FeatureFlags.class));
doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
.makeIccPhoneBookInterfaceManager(nullable(Phone.class));
doReturn(mDisplayInfoController).when(mTelephonyComponentFactory)
@@ -676,9 +689,11 @@
any(DataServiceManager.class), any(Looper.class),
any(FeatureFlags.class),
any(DataProfileManager.DataProfileManagerCallback.class));
+ doReturn(mSafetySource).when(mTelephonyComponentFactory)
+ .makeCellularNetworkSecuritySafetySource(any(Context.class));
doReturn(mIdentifierDisclosureNotifier)
.when(mTelephonyComponentFactory)
- .makeIdentifierDisclosureNotifier();
+ .makeIdentifierDisclosureNotifier(any(CellularNetworkSecuritySafetySource.class));
doReturn(mNullCipherNotifier)
.when(mTelephonyComponentFactory)
.makeNullCipherNotifier();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
index 378df4b..0c7342e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
@@ -28,6 +28,7 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.PersistableBundle;
+import android.provider.Telephony;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -42,6 +43,7 @@
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.util.List;
+import java.util.Set;
public class ApnSettingTest extends TelephonyTest {
@@ -206,12 +208,16 @@
final String[] dummyStringArr = new String[] {"dummy"};
final InetAddress dummyProxyAddress = InetAddress.getByAddress(new byte[]{0, 0, 0, 0});
final Uri dummyUri = Uri.parse("www.google.com");
+
+ final Set<String> excludedFields = Set.of("mEditedStatus");
+
// base apn
ApnSetting baseApn = createApnSetting(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT);
Field[] fields = ApnSetting.class.getDeclaredFields();
for (Field f : fields) {
int modifiers = f.getModifiers();
- if (Modifier.isStatic(modifiers) || !Modifier.isFinal(modifiers)) {
+ if (Modifier.isStatic(modifiers) || !Modifier.isFinal(modifiers)
+ || excludedFields.contains(f.getName())) {
continue;
}
f.setAccessible(true);
@@ -414,4 +420,33 @@
// InfrastructureBitmask value set to '1(cellular)'
assertEquals(infrastructureBitmask, apn2.getInfrastructureBitmask());
}
+
+ @Test
+ public void testEditedStatus() {
+ ApnSetting apn = new ApnSetting.Builder()
+ .setId(1234)
+ .setOperatorNumeric("310260")
+ .setEntryName("mms")
+ .setApnName("mms")
+ .setApnTypeBitmask(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE))
+ .setEditedStatus(Telephony.Carriers.USER_EDITED)
+ .build();
+ assertEquals(Telephony.Carriers.USER_EDITED, apn.getEditedStatus());
+
+ ApnSetting apn2 = new ApnSetting.Builder()
+ .setId(1234)
+ .setOperatorNumeric("310260")
+ .setEntryName("mms")
+ .setApnName("mms")
+ .setApnTypeBitmask(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT)
+ .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE))
+ .setEditedStatus(Telephony.Carriers.CARRIER_EDITED)
+ .build();
+
+ // The edited status should not affect equals
+ assertEquals(apn, apn2);
+ }
}
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 5b91aee..50ee5c8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -20,10 +20,15 @@
import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_DATA_SETTINGS_CHANGED;
+import static com.google.common.truth.Truth.assertThat;
+
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.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -32,6 +37,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.AlarmManager;
import android.app.NotificationManager;
import android.content.Context;
import android.net.NetworkCapabilities;
@@ -59,6 +65,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Map;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class AutoDataSwitchControllerTest extends TelephonyTest {
@@ -66,7 +74,7 @@
private static final int EVENT_DISPLAY_INFO_CHANGED = 2;
private static final int EVENT_EVALUATE_AUTO_SWITCH = 3;
private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 4;
- private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 5;
+ private static final int EVENT_STABILITY_CHECK_PASSED = 5;
private static final int PHONE_1 = 0;
private static final int SUB_1 = 1;
@@ -79,12 +87,14 @@
private boolean mIsNonTerrestrialNetwork = false;
// Mocked
private AutoDataSwitchController.AutoDataSwitchControllerCallback mMockedPhoneSwitcherCallback;
+ private AlarmManager mMockedAlarmManager;
// Real
private TelephonyDisplayInfo mGoodTelephonyDisplayInfo;
private TelephonyDisplayInfo mBadTelephonyDisplayInfo;
private int mDefaultDataSub;
private AutoDataSwitchController mAutoDataSwitchControllerUT;
+ private Map<Integer, AlarmManager.OnAlarmListener> mEventsToAlarmListener;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
@@ -94,6 +104,7 @@
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false /*roaming*/);
mMockedPhoneSwitcherCallback =
mock(AutoDataSwitchController.AutoDataSwitchControllerCallback.class);
+ mMockedAlarmManager = mock(AlarmManager.class);
doReturn(PHONE_1).when(mPhone).getPhoneId();
doReturn(SUB_1).when(mPhone).getSubId();
@@ -147,6 +158,8 @@
doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
doReturn(10000L).when(mDataConfigManager)
.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
+ doReturn(120000L).when(mDataConfigManager)
+ .getAutoDataSwitchPerformanceStabilityTimeThreshold();
doReturn(MAX_RETRY).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry();
doReturn(SCORE_TOLERANCE).when(mDataConfigManager).getAutoDataSwitchScoreTolerance();
doAnswer(invocation -> {
@@ -166,6 +179,11 @@
mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);
+ replaceInstance(AutoDataSwitchController.class, "mAlarmManager",
+ mAutoDataSwitchControllerUT, mMockedAlarmManager);
+ mEventsToAlarmListener = getPrivateField(mAutoDataSwitchControllerUT,
+ "mEventsToAlarmListener", Map.class);
+
doReturn(true).when(mFeatureFlags).autoSwitchAllowRoaming();
doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
}
@@ -356,6 +374,7 @@
@Test
public void testCancelSwitch_onPrimary_rat_signalStrength() {
+ doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
// 4.1.1 Display info and signal strength on secondary phone became bad,
// but primary is still OOS, so still switch to the secondary.
prepareIdealUsesNonDdsCondition();
@@ -371,30 +390,39 @@
// but primary become service, then don't switch.
prepareIdealUsesNonDdsCondition();
processAllFutureMessages();
- clearInvocations(mMockedPhoneSwitcherCallback);
+ clearInvocations(mMockedPhoneSwitcherCallback, mMockedAlarmManager);
serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_MODERATE);
processAllFutureMessages();
- verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+ verify(mMockedPhoneSwitcherCallback, atLeastOnce())
+ .onRequireCancelAnyPendingAutoSwitchValidation();
+ verify(mMockedAlarmManager, atLeastOnce()).cancel(mEventsToAlarmListener.get(
+ EVENT_STABILITY_CHECK_PASSED));
// 4.2 Display info on default phone became good just as the secondary
prepareIdealUsesNonDdsCondition();
processAllFutureMessages();
- clearInvocations(mMockedPhoneSwitcherCallback);
+ clearInvocations(mMockedPhoneSwitcherCallback, mMockedAlarmManager);
serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
displayInfoChanged(PHONE_1, mGoodTelephonyDisplayInfo);
processAllFutureMessages();
- verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+ verify(mMockedPhoneSwitcherCallback, atLeastOnce())
+ .onRequireCancelAnyPendingAutoSwitchValidation();
+ verify(mMockedAlarmManager, atLeastOnce()).cancel(mEventsToAlarmListener.get(
+ EVENT_STABILITY_CHECK_PASSED));
// 4.3 Signal strength on default phone became just as good as the secondary
prepareIdealUsesNonDdsCondition();
processAllFutureMessages();
- clearInvocations(mMockedPhoneSwitcherCallback);
+ clearInvocations(mMockedPhoneSwitcherCallback, mMockedAlarmManager);
serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
processAllFutureMessages();
- verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+ verify(mMockedPhoneSwitcherCallback, atLeastOnce())
+ .onRequireCancelAnyPendingAutoSwitchValidation();
+ verify(mMockedAlarmManager, atLeastOnce()).cancel(mEventsToAlarmListener.get(
+ EVENT_STABILITY_CHECK_PASSED));
}
@Test
@@ -459,16 +487,18 @@
@Test
public void testOnNonDdsSwitchBackToPrimary_rat_signalStrength() {
doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
- prepareIdealUsesNonDdsCondition();
// 4.1 Display info and signal strength on secondary phone became bad just as the default
- // Expect no switch since both phone has the same score.
+ // Expect switch back since both phone has the same score.
serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
processAllFutureMessages();
- verify(mMockedPhoneSwitcherCallback, never()).onRequireValidation(anyInt(), anyBoolean());
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ true/*needValidation*/);
clearInvocations(mMockedPhoneSwitcherCallback);
prepareIdealUsesNonDdsCondition();
@@ -504,7 +534,7 @@
}
@Test
- public void testStabilityCheckOverride() {
+ public void testStabilityCheckOverride_basic() {
// Starting stability check for switching to non-DDS
prepareIdealUsesNonDdsCondition();
processAllMessages();
@@ -515,9 +545,6 @@
// Display info and signal strength on secondary phone became worse than the default.
// Expect to switch back, and it should override the previous stability check
serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
- signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
- displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
- signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
// process all messages include the delayed message
processAllFutureMessages();
@@ -528,6 +555,25 @@
}
@Test
+ public void testStabilityCheckOverride_uses_rat_signalStrength() {
+ doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
+ // Switching due to availability first.
+ prepareIdealUsesNonDdsCondition();
+
+ // Verify stability check pending with short timer.
+ verify(mMockedPhoneSwitcherCallback, never()).onRequireValidation(anyInt(), anyBoolean());
+
+ // Switching due to performance now, should override to use long timer.
+ serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+
+ // Verify stability check pending with long timer.
+ assertThat(mAutoDataSwitchControllerUT.hasMessages(EVENT_STABILITY_CHECK_PASSED)).isFalse();
+ verify(mMockedAlarmManager).setExact(anyInt(), anyLong(), anyString(),
+ eq(mEventsToAlarmListener.get(
+ EVENT_STABILITY_CHECK_PASSED)), any());
+ }
+
+ @Test
public void testValidationFailedRetry() {
prepareIdealUsesNonDdsCondition();
@@ -643,6 +689,7 @@
@Test
public void testRatSignalStrengthSkipEvaluation() {
// Verify the secondary phone is OOS and its score(0) is too low to justify the evaluation
+ clearInvocations(mMockedPhoneSwitcherCallback);
displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
processAllFutureMessages();
verify(mMockedPhoneSwitcherCallback, never())
@@ -726,9 +773,19 @@
Message msg = mAutoDataSwitchControllerUT.obtainMessage(EVENT_SERVICE_STATE_CHANGED);
msg.obj = new AsyncResult(phoneId, null, null);
mAutoDataSwitchControllerUT.sendMessage(msg);
+ processAllMessages();
}
private void setDefaultDataSubId(int defaultDataSub) {
mDefaultDataSub = defaultDataSub;
doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
}
+
+ @Override
+ public void processAllFutureMessages() {
+ if (mFeatureFlags.autoDataSwitchRatSs()
+ && mEventsToAlarmListener.containsKey(EVENT_STABILITY_CHECK_PASSED)) {
+ mEventsToAlarmListener.get(EVENT_STABILITY_CHECK_PASSED).onAlarm();
+ }
+ super.processAllFutureMessages();
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataEvaluationTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataEvaluationTest.java
index 6d4c0c1..8ce0894 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataEvaluationTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataEvaluationTest.java
@@ -58,6 +58,14 @@
mDataEvaluationUT.removeDataDisallowedReason(
DataEvaluation.DataDisallowedReason.DATA_DISABLED);
assertThat(mDataEvaluationUT.getDataDisallowedReasons().size()).isEqualTo(1);
+
+ mDataEvaluationUT.addDataDisallowedReason(
+ DataEvaluation.DataDisallowedReason.DATA_LIMIT_REACHED);
+ assertThat(mDataEvaluationUT.getDataDisallowedReasons().size()).isEqualTo(2);
+
+ mDataEvaluationUT.removeDataDisallowedReason(
+ DataEvaluation.DataDisallowedReason.ROAMING_DISABLED);
+ assertThat(mDataEvaluationUT.getDataDisallowedReasons().size()).isEqualTo(1);
}
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 14bad95..00e6680 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -648,8 +648,26 @@
private void serviceStateChanged(@NetworkType int networkType,
@RegistrationState int dataRegState, @RegistrationState int voiceRegState,
@RegistrationState int iwlanRegState, DataSpecificRegistrationInfo dsri) {
+ boolean isEmergencyOnly = false;
+ if (dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED) {
+ isEmergencyOnly = true;
+ }
ServiceState ss = createSS(networkType, networkType, dataRegState, voiceRegState,
- iwlanRegState, dsri);
+ iwlanRegState, dsri, isEmergencyOnly);
+
+ doReturn(ss).when(mSST).getServiceState();
+ doReturn(ss).when(mPhone).getServiceState();
+
+ mDataNetworkControllerUT.obtainMessage(17/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
+ processAllMessages();
+ }
+
+ private void serviceStateChanged(@NetworkType int networkType,
+ @RegistrationState int dataRegState, @RegistrationState int voiceRegState,
+ @RegistrationState int iwlanRegState, DataSpecificRegistrationInfo dsri,
+ boolean isEmergencyOnly) {
+ ServiceState ss = createSS(networkType, networkType, dataRegState, voiceRegState,
+ iwlanRegState, dsri, isEmergencyOnly);
doReturn(ss).when(mSST).getServiceState();
doReturn(ss).when(mPhone).getServiceState();
@@ -661,7 +679,8 @@
private ServiceState createSS(@NetworkType int dataNetworkType,
@NetworkType int voiceNetworkType,
@RegistrationState int dataRegState, @RegistrationState int voiceRegState,
- @RegistrationState int iwlanRegState, DataSpecificRegistrationInfo dsri) {
+ @RegistrationState int iwlanRegState, DataSpecificRegistrationInfo dsri,
+ boolean isEmergencyOnly) {
if (dsri == null) {
dsri = new DataSpecificRegistrationInfo.Builder(8)
.setNrAvailable(true)
@@ -682,6 +701,7 @@
.setDataSpecificInfo(dsri)
.setIsNonTerrestrialNetwork(mIsNonTerrestrialNetwork)
.setAvailableServices(mCarrierSupportedSatelliteServices)
+ .setEmergencyOnly(isEmergencyOnly)
.build());
ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
@@ -691,6 +711,7 @@
.setDomain(NetworkRegistrationInfo.DOMAIN_PS)
.setIsNonTerrestrialNetwork(mIsNonTerrestrialNetwork)
.setAvailableServices(mCarrierSupportedSatelliteServices)
+ .setEmergencyOnly(isEmergencyOnly)
.build());
ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
@@ -698,6 +719,7 @@
.setAccessNetworkTechnology(voiceNetworkType)
.setRegistrationState(voiceRegState)
.setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+ .setEmergencyOnly(isEmergencyOnly)
.build());
ss.setDataRoamingFromRegistration(dataRegState
@@ -808,6 +830,12 @@
mCarrierConfig.putIntArray(CarrierConfigManager
.KEY_CAPABILITIES_EXEMPT_FROM_SINGLE_DC_CHECK_INT_ARRAY,
new int[]{NetworkCapabilities.NET_CAPABILITY_IMS});
+ mCarrierConfig.putBooleanArray(
+ CarrierConfigManager.KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY,
+ new boolean[] {false, false, true, false, false}
+ );
+ mCarrierConfig.putLongArray(CarrierConfigManager.KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY,
+ new long[] {180000, 180000, 180000, 180000});
mCarrierConfig.putLongArray(CarrierConfigManager.KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY,
new long[] {100, 100, 100, 100});
@@ -818,6 +846,8 @@
mContextFixture.putResource(com.android.internal.R.string.config_bandwidthEstimateSource,
"bandwidth_estimator");
+ mContextFixture.putBooleanResource(com.android.internal.R.bool
+ .config_honor_data_retry_timer_for_emergency_network, true);
mContextFixture.putIntResource(com.android.internal.R.integer
.config_delay_for_ims_dereg_millis, 3000);
mContextFixture.putBooleanResource(com.android.internal.R.bool
@@ -2125,6 +2155,53 @@
}
@Test
+ public void testEmergencyRequestWithThrottling() throws Exception {
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, /* data */
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, /* voice */
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, /* iwlan */
+ null, false);
+ mDataNetworkControllerUT.getDataRetryManager()
+ .registerCallback(mMockedDataRetryManagerCallback);
+
+ setFailedSetupDataResponse(mMockedWwanDataServiceManager, DataFailCause.PROTOCOL_ERRORS,
+ 10000, false);
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_EIMS));
+ processAllMessages();
+
+ // There should be only one attempt, and no retry should happen because the second one
+ // was throttled.
+ verify(mMockedWwanDataServiceManager, times(1)).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+ any(), any(), anyBoolean(), any(Message.class));
+
+ ArgumentCaptor<List<ThrottleStatus>> throttleStatusCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mMockedDataRetryManagerCallback)
+ .onThrottleStatusChanged(throttleStatusCaptor.capture());
+ assertThat(throttleStatusCaptor.getValue()).hasSize(1);
+ ThrottleStatus throttleStatus = throttleStatusCaptor.getValue().get(0);
+ assertThat(throttleStatus.getApnType()).isEqualTo(ApnSetting.TYPE_EMERGENCY);
+ assertThat(throttleStatus.getRetryType())
+ .isEqualTo(ThrottleStatus.RETRY_TYPE_NEW_CONNECTION);
+ assertThat(throttleStatus.getTransportType())
+ .isEqualTo(AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ Mockito.reset(mMockedWwanDataServiceManager);
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_DENIED, /* data */
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, /* voice */
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, /* iwlan */
+ null, true);
+
+ // No retry should happen because the second one was throttled.
+ verify(mMockedWwanDataServiceManager, never()).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+ any(), any(), anyBoolean(), any(Message.class));
+ }
+
+ @Test
public void testHandoverRuleFromString() {
HandoverRule handoverRule = new HandoverRule("source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
+ "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed");
@@ -4681,7 +4758,7 @@
TelephonyManager.NETWORK_TYPE_1xRTT /* voice RAT */,
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING ,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
- NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, null);
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING, null, true);
doReturn(ss).when(mSST).getServiceState();
mDataNetworkControllerUT.obtainMessage(EVENT_SERVICE_STATE_CHANGED).sendToTarget();
mDataNetworkControllerUT.removeNetworkRequest(request);
@@ -4842,6 +4919,9 @@
@Test
public void testNetworkOnProvisioningProfileClass_WithFlagEnabled() throws Exception {
when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+ // Allowed data limit Unlimited
+ mContextFixture.putIntResource(com.android.internal.R.integer
+ .config_esim_bootstrap_data_limit_bytes, -1);
doReturn(new SubscriptionInfoInternal.Builder().setId(1)
.setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
.when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
@@ -4869,6 +4949,65 @@
}
@Test
+ public void testSetUpPdn_WithBootStrapDataLimit_Zero() throws Exception {
+ when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+ // Allowed data limit set as zero
+ doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ processAllMessages();
+ // With current consumed bytes is zero, same as allowed limit, data_limit_reached
+ // disallowed reason is met
+ verifyConnectedNetworkHasNoDataProfile(mEsimBootstrapDataProfile);
+
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL));
+ processAllMessages();
+ // New network request also meets with data limit reached disallowed reason
+ verifyConnectedNetworkHasNoDataProfile(mEsimBootstrapImsProfile);
+ }
+
+ @Test
+ public void testSetUpPdn_WithBootStrapDataLimit_Unlimited() throws Exception {
+ when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+ // Allowed data limit
+ mContextFixture.putIntResource(com.android.internal.R.integer
+ .config_esim_bootstrap_data_limit_bytes, -1/*unlimited*/);
+ doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+ .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
+ .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ processAllMessages();
+ // With allowed data limit unlimited, connection is allowed
+ verifyConnectedNetworkHasDataProfile(mEsimBootstrapDataProfile);
+
+ mDataNetworkControllerUT.addNetworkRequest(
+ createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL));
+ setSuccessfulSetupDataResponse(mMockedDataServiceManagers
+ .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), 2);
+ processAllMessages();
+ // With allowed data limit unlimited, connection is allowed
+ verifyConnectedNetworkHasDataProfile(mEsimBootstrapImsProfile);
+
+ // Both internet and IMS should be retained after network re-evaluation
+ mDataNetworkControllerUT.obtainMessage(16 /*EVENT_REEVALUATE_EXISTING_DATA_NETWORKS*/,
+ DataEvaluation.DataEvaluationReason.CHECK_DATA_USAGE).sendToTarget();
+ processAllMessages();
+ // With allowed data limit unlimited, connection is allowed
+ verifyConnectedNetworkHasDataProfile(mEsimBootstrapDataProfile);
+ verifyConnectedNetworkHasDataProfile(mEsimBootstrapImsProfile);
+ }
+
+ @Test
public void testNetworkOnNonProvisioningProfileClass_WithFlagEnabled() throws Exception {
when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
doReturn(new SubscriptionInfoInternal.Builder().setId(1)
@@ -4894,6 +5033,9 @@
public void testNtnNetworkOnProvisioningProfileClass_WithFlagEnabled() throws Exception {
when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+ // Allowed data limit Unlimited
+ mContextFixture.putIntResource(com.android.internal.R.integer
+ .config_esim_bootstrap_data_limit_bytes, -1);
doReturn(new SubscriptionInfoInternal.Builder().setId(1)
.setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
.when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index 66444da..62fbebc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -68,6 +68,7 @@
import android.telephony.data.EpsQos;
import android.telephony.data.NetworkSliceInfo;
import android.telephony.data.Qos;
+import android.telephony.data.QosBearerSession;
import android.telephony.data.TrafficDescriptor;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -2260,4 +2261,44 @@
assertThat(mDataNetworkUT.getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)).isTrue();
}
+
+ @Test
+ public void testQosSessionsChanged() throws Exception {
+ createImsDataNetwork(false/*isMmtel*/);
+ List<QosBearerSession> newQosSessions =
+ List.of(new QosBearerSession(1, mDefaultQos, Collections.emptyList()));
+ DataCallResponse response = new DataCallResponse.Builder()
+ .setCause(0)
+ .setRetryDurationMillis(-1L)
+ .setId(123)
+ .setLinkStatus(DataCallResponse.LINK_STATUS_ACTIVE)
+ .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
+ .setInterfaceName("ifname")
+ .setAddresses(Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress(IPV4_ADDRESS), 32),
+ new LinkAddress(IPV6_ADDRESS + "/64")))
+ .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
+ InetAddresses.parseNumericAddress("fd00:976a::9")))
+ .setGatewayAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("10.0.2.15"),
+ InetAddresses.parseNumericAddress("fe80::2")))
+ .setPcscfAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
+ InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
+ InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
+ .setMtuV4(1234)
+ .setPduSessionId(1)
+ .setQosBearerSessions(newQosSessions)
+ .setTrafficDescriptors(Collections.emptyList())
+ .setDefaultQos(mDefaultQos)
+ .build();
+
+ // Qos sessions list changed
+ mDataNetworkUT.obtainMessage(8/*EVENT_DATA_STATE_CHANGED*/,
+ new AsyncResult(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ List.of(response), null)).sendToTarget();
+ processAllMessages();
+
+ verify(mDataNetworkCallback).onQosSessionsChanged(newQosSessions);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index e556cb8..44d207d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -139,7 +139,8 @@
Telephony.Carriers.SKIP_464XLAT,
Telephony.Carriers.ALWAYS_ON,
Telephony.Carriers.INFRASTRUCTURE_BITMASK,
- Telephony.Carriers.ESIM_BOOTSTRAP_PROVISIONING
+ Telephony.Carriers.ESIM_BOOTSTRAP_PROVISIONING,
+ Telephony.Carriers.EDITED_STATUS
};
private int mPreferredApnSet = 0;
@@ -180,7 +181,8 @@
-1, // skip_464xlat
0, // always_on
1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
// default internet data profile for RAT CDMA, to test update preferred data profile
new Object[]{
@@ -216,8 +218,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
2, // id
@@ -252,8 +255,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
3, // id
@@ -288,8 +292,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
4, // id
@@ -325,8 +330,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
// This APN entry is created to test de-duping.
new Object[]{
@@ -363,8 +369,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
6, // id
@@ -400,8 +407,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
7, // id
@@ -437,8 +445,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
8, // id
@@ -474,8 +483,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 0 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
9, // id
@@ -512,10 +522,11 @@
-1, // skip_464xlat
0, // always_on
2, // INFRASTRUCTURE_SATELLITE
- 0 // esim_bootstrap_provisioning
+ 0, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
- 10, // id
+ 10, // id
PLMN, // numeric
ESIM_BOOTSTRAP_PROVISIONING_APN, // name
ESIM_BOOTSTRAP_PROVISIONING_APN, // apn
@@ -527,7 +538,7 @@
"", // user
"", // password
-1, // authtype
- "default,supl", // types
+ "default,supl", // types
"IPV4V6", // protocol
"IPV4V6", // roaming_protocol
1, // carrier_enabled
@@ -548,8 +559,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_CELLULAR
- 1 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_CELLULAR
+ 1, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
11, // id
@@ -585,8 +597,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 1, // INFRASTRUCTURE_SATELLITE
- 1 // esim_bootstrap_provisioning
+ 1, // INFRASTRUCTURE_SATELLITE
+ 1, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
12, // id
@@ -622,14 +635,15 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 2, // INFRASTRUCTURE_SATELLITE
- 1 // esim_bootstrap_provisioning
+ 2, // INFRASTRUCTURE_SATELLITE
+ 1, // esim_bootstrap_provisioning
+ 0 // UNEDITED
},
new Object[]{
- 13, // id
+ 13, // id
PLMN, // numeric
- RCS_APN1, // name
- RCS_APN1, // apn
+ RCS_APN1, // name
+ RCS_APN1, // apn
"", // proxy
"", // port
"", // mmsc
@@ -659,8 +673,9 @@
-1, // carrier_id
-1, // skip_464xlat
0, // always_on
- 2, // INFRASTRUCTURE_SATELLITE
- 1 // esim_bootstrap_provisioning
+ 2, // INFRASTRUCTURE_SATELLITE
+ 1, // esim_bootstrap_provisioning
+ 0 // UNEDITED
}
);
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 cd7aadc..47f8ce2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
@@ -27,19 +27,23 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.net.Uri;
import android.os.AsyncResult;
import android.os.Handler;
+import android.os.Message;
import android.os.RemoteException;
+import android.telecom.PhoneAccount;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.DomainSelectionService;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsReasonInfo;
import android.testing.AndroidTestingRunner;
@@ -171,7 +175,8 @@
IWwanSelectorResultCallback resultCallback =
Mockito.mock(IWwanSelectorResultCallback.class);
- wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback);
+ wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+ false, resultCallback);
processAllMessages();
ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
@@ -190,12 +195,13 @@
assertNotNull(handler);
- EmergencyRegResult regResult =
- new EmergencyRegResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
+ EmergencyRegistrationResult regResult =
+ new EmergencyRegistrationResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
handler.sendMessage(handler.obtainMessage(event, new AsyncResult(null, regResult, null)));
processAllMessages();
verify(resultCallback).onComplete(eq(regResult));
+ verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any());
}
@Test
@@ -221,8 +227,8 @@
assertNotNull(wwanCallback);
- wwanCallback.onRequestEmergencyNetworkScan(new int[] { },
- SCAN_TYPE_NO_PREFERENCE, Mockito.mock(IWwanSelectorResultCallback.class));
+ wwanCallback.onRequestEmergencyNetworkScan(new int[] { }, SCAN_TYPE_NO_PREFERENCE,
+ false, Mockito.mock(IWwanSelectorResultCallback.class));
processAllMessages();
verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
@@ -235,6 +241,196 @@
}
@Test
+ public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanWithResetScan()
+ 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,
+ true, resultCallback);
+ processAllMessages();
+
+ ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+ verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
+
+ verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+ verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+ Message msg = msgCaptor.getValue();
+
+ assertNotNull(msg);
+
+ AsyncResult unused = AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> eventCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ verify(mPhone).registerForEmergencyNetworkScan(
+ handlerCaptor.capture(), eventCaptor.capture(), any());
+
+ int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+ verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+ eq(scanType), any());
+
+ Handler handler = handlerCaptor.getValue();
+ int event = eventCaptor.getValue();
+
+ assertNotNull(handler);
+
+ EmergencyRegistrationResult regResult =
+ new EmergencyRegistrationResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
+ handler.sendMessage(handler.obtainMessage(event, new AsyncResult(null, regResult, null)));
+ processAllMessages();
+
+ verify(resultCallback).onComplete(eq(regResult));
+ }
+
+ @Test
+ public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanWithResetScanDoneAndCancel()
+ 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,
+ true, resultCallback);
+ processAllMessages();
+
+ ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+ verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
+ verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+ verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+ Message msg = msgCaptor.getValue();
+
+ assertNotNull(msg);
+
+ // Reset completes.
+ AsyncResult unused = AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Verify that scan is requested.
+ verify(mPhone).registerForEmergencyNetworkScan(any(), anyInt(), any());
+ verify(mPhone).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+ // Cancele scan after reset completes.
+ wwanCallback.onCancel();
+ processAllMessages();
+
+ // Verify scan request is canceled.
+ verify(mPhone).cancelEmergencyNetworkScan(eq(false), any());
+ verify(mPhone, times(2)).cancelEmergencyNetworkScan(anyBoolean(), any());
+ }
+
+ @Test
+ public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanWithResetScanAndCancel()
+ 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,
+ true, resultCallback);
+ processAllMessages();
+
+ ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+ verify(mPhone).cancelEmergencyNetworkScan(eq(true), msgCaptor.capture());
+ verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+ verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
+
+ Message msg = msgCaptor.getValue();
+
+ assertNotNull(msg);
+
+ // Canceled before reset completes.
+ wwanCallback.onCancel();
+ processAllMessages();
+
+ // Verify there is no additional cancel.
+ verify(mPhone, times(1)).cancelEmergencyNetworkScan(anyBoolean(), any());
+
+ // Reset completes
+ AsyncResult unused = AsyncResult.forMessage(msg);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Verify there is no scan request after reset completes.
+ verify(mPhone, times(0)).registerForEmergencyNetworkScan(any(), anyInt(), any());
+ verify(mPhone, times(0)).triggerEmergencyNetworkScan(any(), anyInt(), any());
+ }
+
+ @Test
@SmallTest
public void testDomainSelectorCancelSelection() throws Exception {
mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
@@ -255,7 +451,7 @@
mDsc.cancelSelection();
- verify(domainSelector).cancelSelection();
+ verify(domainSelector).finishSelection();
}
@Test
@@ -393,7 +589,8 @@
Mockito.mock(IWwanSelectorResultCallback.class);
// 1st scan request from remote service
- wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback);
+ wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+ false, resultCallback);
processAllMessages();
ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
@@ -429,15 +626,16 @@
// 2nd scan request
IWwanSelectorResultCallback resultCallback2 =
Mockito.mock(IWwanSelectorResultCallback.class);
- wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback2);
+ wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+ false, resultCallback2);
processAllMessages();
// Verify that triggerEmergencyNetworkScan isn't called
verify(mPhone, times(1)).triggerEmergencyNetworkScan(any(), anyInt(), any());
// Result received
- EmergencyRegResult regResult =
- new EmergencyRegResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
+ EmergencyRegistrationResult regResult =
+ new EmergencyRegistrationResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
handler.sendMessage(handler.obtainMessage(event, new AsyncResult(null, regResult, null)));
processAllMessages();
@@ -476,7 +674,8 @@
Mockito.mock(IWwanSelectorResultCallback.class);
// 1st scan request from remote service
- wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback);
+ wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+ false, resultCallback);
processAllMessages();
ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
@@ -500,8 +699,8 @@
verify(mDomainSelectionController, times(1)).selectDomain(eq(attr), eq(transportCallback));
// Result received
- EmergencyRegResult regResult =
- new EmergencyRegResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
+ EmergencyRegistrationResult regResult =
+ new EmergencyRegistrationResult(UTRAN, 0, 0, true, false, 0, 0, "", "", "");
handler.sendMessage(handler.obtainMessage(event, new AsyncResult(null, regResult, null)));
processAllMessages();
@@ -517,7 +716,8 @@
// 2nd scan request
IWwanSelectorResultCallback resultCallback2 =
Mockito.mock(IWwanSelectorResultCallback.class);
- wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType, resultCallback2);
+ wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+ false, resultCallback2);
processAllMessages();
// Verify that triggerEmergencyNetworkScan is called
@@ -525,7 +725,7 @@
// Result received
regResult =
- new EmergencyRegResult(EUTRAN, 0, 0, true, false, 0, 0, "", "", "");
+ new EmergencyRegistrationResult(EUTRAN, 0, 0, true, false, 0, 0, "", "", "");
handler.sendMessage(handler.obtainMessage(event, new AsyncResult(null, regResult, null)));
processAllMessages();
@@ -577,6 +777,7 @@
boolean isEmergency, DomainSelectionController controller) throws Exception {
DomainSelectionConnection dsc = new DomainSelectionConnection(phone,
selectorType, isEmergency, controller);
+ dsc.setTestMode(true);
replaceInstance(DomainSelectionConnection.class, "mLooper",
dsc, mTestableLooper.getLooper());
return dsc;
@@ -585,7 +786,7 @@
private DomainSelectionService.SelectionAttributes getSelectionAttributes(
int slotId, int subId, int selectorType, boolean isEmergency,
boolean exited, int callFailCause, String callId, String number,
- ImsReasonInfo imsReasonInfo, EmergencyRegResult regResult) {
+ ImsReasonInfo imsReasonInfo, EmergencyRegistrationResult regResult) {
DomainSelectionService.SelectionAttributes.Builder builder =
new DomainSelectionService.SelectionAttributes.Builder(
slotId, subId, selectorType)
@@ -594,9 +795,11 @@
.setCsDisconnectCause(callFailCause);
if (callId != null) builder.setCallId(callId);
- if (number != null) builder.setNumber(number);
+ if (number != null) {
+ builder.setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
+ }
if (imsReasonInfo != null) builder.setPsDisconnectCause(imsReasonInfo);
- if (regResult != null) builder.setEmergencyRegResult(regResult);
+ if (regResult != null) builder.setEmergencyRegistrationResult(regResult);
return builder.build();
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
index 76de9c9..f893c35 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
@@ -42,7 +42,7 @@
import android.os.Message;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.DomainSelectionService;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.data.ApnSetting;
import android.testing.AndroidTestingRunner;
@@ -95,6 +95,7 @@
doReturn(mAnm).when(mPhone).getAccessNetworksManager();
mEcDsc = new EmergencyCallDomainSelectionConnection(mPhone,
mDomainSelectionController, mEmergencyStateTracker);
+ mEcDsc.setTestMode(true);
replaceInstance(DomainSelectionConnection.class, "mLooper",
mEcDsc, mTestableLooper.getLooper());
mTransportCallback = mEcDsc.getTransportSelectorCallback();
@@ -113,7 +114,7 @@
replaceInstance(EmergencyCallDomainSelectionConnection.class,
"mEmergencyStateTracker", mEcDsc, mEmergencyStateTracker);
- EmergencyRegResult regResult = new EmergencyRegResult(
+ EmergencyRegistrationResult regResult = new EmergencyRegistrationResult(
EUTRAN, REGISTRATION_STATE_UNKNOWN,
NetworkRegistrationInfo.DOMAIN_PS,
true, false, 0, 0, "", "", "");
@@ -121,7 +122,7 @@
DomainSelectionService.SelectionAttributes attr =
EmergencyCallDomainSelectionConnection.getSelectionAttributes(
mPhone.getPhoneId(), mPhone.getSubId(), false,
- TELECOM_CALL_ID1, "911", 0, null, regResult);
+ TELECOM_CALL_ID1, "911", false, 0, null, regResult);
CompletableFuture<Integer> future =
mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
@@ -162,7 +163,7 @@
replaceInstance(EmergencyCallDomainSelectionConnection.class,
"mEmergencyStateTracker", mEcDsc, mEmergencyStateTracker);
- EmergencyRegResult regResult = new EmergencyRegResult(
+ EmergencyRegistrationResult regResult = new EmergencyRegistrationResult(
UTRAN, REGISTRATION_STATE_UNKNOWN,
NetworkRegistrationInfo.DOMAIN_CS,
true, false, 0, 0, "", "", "");
@@ -170,7 +171,7 @@
DomainSelectionService.SelectionAttributes attr =
EmergencyCallDomainSelectionConnection.getSelectionAttributes(
mPhone.getPhoneId(), mPhone.getSubId(), false,
- TELECOM_CALL_ID1, "911", 0, null, regResult);
+ TELECOM_CALL_ID1, "911", false, 0, null, regResult);
CompletableFuture<Integer> future =
mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
@@ -184,7 +185,7 @@
IWwanSelectorCallback wwanCallback = onWwanSelected(mTransportCallback);
assertFalse(future.isDone());
- verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ verify(mEmergencyStateTracker).onEmergencyTransportChangedAndWait(
eq(EmergencyStateTracker.EMERGENCY_TYPE_CALL), eq(MODE_EMERGENCY_WWAN));
wwanCallback.onDomainSelected(DOMAIN_CS, false);
@@ -200,7 +201,7 @@
replaceInstance(EmergencyCallDomainSelectionConnection.class,
"mEmergencyStateTracker", mEcDsc, mEmergencyStateTracker);
- EmergencyRegResult regResult = new EmergencyRegResult(
+ EmergencyRegistrationResult regResult = new EmergencyRegistrationResult(
EUTRAN, REGISTRATION_STATE_UNKNOWN,
NetworkRegistrationInfo.DOMAIN_PS,
true, true, 0, 0, "", "", "");
@@ -208,7 +209,7 @@
DomainSelectionService.SelectionAttributes attr =
EmergencyCallDomainSelectionConnection.getSelectionAttributes(
mPhone.getPhoneId(), mPhone.getSubId(), false,
- TELECOM_CALL_ID1, "911", 0, null, regResult);
+ TELECOM_CALL_ID1, "911", false, 0, null, regResult);
CompletableFuture<Integer> future =
mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
@@ -222,7 +223,7 @@
IWwanSelectorCallback wwanCallback = onWwanSelected(mTransportCallback);
assertFalse(future.isDone());
- verify(mEmergencyStateTracker).onEmergencyTransportChanged(
+ verify(mEmergencyStateTracker).onEmergencyTransportChangedAndWait(
eq(EmergencyStateTracker.EMERGENCY_TYPE_CALL), eq(MODE_EMERGENCY_WWAN));
wwanCallback.onDomainSelected(DOMAIN_PS, true);
@@ -234,7 +235,7 @@
@Test
@SmallTest
public void testOnSelectionTerminated() throws Exception {
- EmergencyRegResult regResult = new EmergencyRegResult(
+ EmergencyRegistrationResult regResult = new EmergencyRegistrationResult(
EUTRAN, REGISTRATION_STATE_UNKNOWN,
NetworkRegistrationInfo.DOMAIN_PS,
true, true, 0, 0, "", "", "");
@@ -242,7 +243,7 @@
DomainSelectionService.SelectionAttributes attr =
EmergencyCallDomainSelectionConnection.getSelectionAttributes(
mPhone.getPhoneId(), mPhone.getSubId(), false,
- TELECOM_CALL_ID1, "911", 0, null, regResult);
+ TELECOM_CALL_ID1, "911", false, 0, null, regResult);
mEcDsc.createEmergencyConnection(attr, mConnectionCallback);
mTransportCallback.onSelectionTerminated(ERROR_UNSPECIFIED);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java
index c25aeb9..4f63be0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencySmsDomainSelectionConnectionTest.java
@@ -444,7 +444,7 @@
assertFalse(future.isDone());
verify(mAnm).unregisterForQualifiedNetworksChanged(any(Handler.class));
- verify(mDomainSelector).cancelSelection();
+ verify(mDomainSelector).finishSelection();
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java
index f05943f..72d8524 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnectionTest.java
@@ -188,10 +188,10 @@
NormalCallDomainSelectionConnection.getSelectionAttributes(1, 2,
TELECOM_CALL_ID1, "123", false, 10, imsReasonInfo);
- assertEquals(1, attributes.getSlotId());
- assertEquals(2, attributes.getSubId());
+ assertEquals(1, attributes.getSlotIndex());
+ assertEquals(2, attributes.getSubscriptionId());
assertEquals(TELECOM_CALL_ID1, attributes.getCallId());
- assertEquals("123", attributes.getNumber());
+ assertEquals("123", attributes.getAddress().getSchemeSpecificPart());
assertEquals(false, attributes.isVideoCall());
assertEquals(false, attributes.isEmergency());
assertEquals(SELECTOR_TYPE_CALLING, attributes.getSelectorType());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS b/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
index b9112be..2a76770 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
@@ -6,3 +6,4 @@
mkoon@google.com
seheele@google.com
radhikaagrawal@google.com
+jdyou@google.com
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java
index 05291e2..5799dd8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/SmsDomainSelectionConnectionTest.java
@@ -205,7 +205,7 @@
mDsConnection.finishSelection();
- verify(mDomainSelector).cancelSelection();
+ verify(mDomainSelector).finishSelection();
}
private void setUpTestableLooper() throws Exception {
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 27b53ad..105f2bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -23,6 +23,7 @@
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;
@@ -52,9 +53,11 @@
import android.os.Message;
import android.os.UserHandle;
import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
@@ -90,12 +93,10 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class EmergencyStateTrackerTest extends TelephonyTest {
- private static final String TEST_CALL_ID = "TC@TEST1";
- private static final String TEST_CALL_ID_2 = "TC@TEST2";
private static final String TEST_SMS_ID = "1111";
private static final String TEST_SMS_ID_2 = "2222";
private static final long TEST_ECM_EXIT_TIMEOUT_MS = 500;
- private static final EmergencyRegResult E_REG_RESULT = new EmergencyRegResult(
+ private static final EmergencyRegistrationResult E_REG_RESULT = new EmergencyRegistrationResult(
EUTRAN, REGISTRATION_STATE_HOME, DOMAIN_CS_PS, true, true, 0, 1, "001", "01", "US");
@Mock EmergencyStateTracker.PhoneFactoryProxy mPhoneFactoryProxy;
@@ -103,6 +104,8 @@
@Mock EmergencyStateTracker.TelephonyManagerProxy mTelephonyManagerProxy;
@Mock PhoneSwitcher mPhoneSwitcher;
@Mock RadioOnHelper mRadioOnHelper;
+ @Mock android.telecom.Connection mTestConnection1;
+ @Mock android.telecom.Connection mTestConnection2;
@Before
public void setUp() throws Exception {
@@ -151,22 +154,93 @@
false /* isRadioOn */);
setConfigForDdsSwitch(testPhone, null,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150");
+ ServiceState ss = mock(ServiceState.class);
+ doReturn(ss).when(mSST).getServiceState();
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .build();
+ doReturn(nri).when(ss).getNetworkRegistrationInfo(anyInt(), anyInt());
// Spy is used to capture consumer in delayDialForDdsSwitch
EmergencyStateTracker spyEst = spy(emergencyStateTracker);
- CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone, TEST_CALL_ID,
- false);
+ CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone,
+ mTestConnection1, 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(0));
- // isOkToCall() should return true once radio is on
+ eq(false), eq(DEFAULT_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));
when(mSST.isRadioOn()).thenReturn(true);
- assertTrue(callback.getValue()
+ assertFalse(callback.getValue()
.isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+ nri = new NetworkRegistrationInfo.Builder()
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setRegistrationState(REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(nri).when(ss).getNetworkRegistrationInfo(anyInt(), anyInt());
+
+ assertTrue(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
+ // Once radio on is complete, trigger delay dial
+ callback.getValue().onComplete(null, true);
+ ArgumentCaptor<Consumer<Boolean>> completeConsumer = ArgumentCaptor
+ .forClass(Consumer.class);
+ verify(spyEst).switchDdsDelayed(eq(testPhone), completeConsumer.capture());
+ verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(testPhone.getPhoneId()),
+ eq(150) /* extensionTime */, any());
+ // After dds switch completes successfully, set emergency mode
+ completeConsumer.getValue().accept(true);
+ verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any());
+ }
+
+ /**
+ * Test that the EmergencyStateTracker turns on radio, performs a DDS switch and sets emergency
+ * mode switch when we are not roaming and the carrier only supports SUPL over the data plane.
+ */
+ @Test
+ @SmallTest
+ public void startEmergencyCall_radioOff_turnOnRadioTimeoutSwitchDdsAndSetEmergencyMode() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+ // Create test Phones and set radio off
+ Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+ false /* isRadioOn */);
+ setConfigForDdsSwitch(testPhone, null,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150");
+ ServiceState ss = mock(ServiceState.class);
+ doReturn(ss).when(mSST).getServiceState();
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .build();
+ doReturn(nri).when(ss).getNetworkRegistrationInfo(anyInt(), anyInt());
+ // Spy is used to capture consumer in delayDialForDdsSwitch
+ EmergencyStateTracker spyEst = spy(emergencyStateTracker);
+ CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone,
+ mTestConnection1, 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));
+ // onTimeout should return true when radion on
+ assertFalse(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+ assertFalse(callback.getValue()
+ .onTimeout(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+ when(mSST.isRadioOn()).thenReturn(true);
+
+ assertFalse(callback.getValue()
+ .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+ assertTrue(callback.getValue()
+ .onTimeout(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
// Once radio on is complete, trigger delay dial
callback.getValue().onComplete(null, true);
ArgumentCaptor<Consumer<Boolean>> completeConsumer = ArgumentCaptor
@@ -193,13 +267,13 @@
false /* isRadioOn */);
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, 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(0));
+ eq(false), eq(DEFAULT_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);
@@ -228,8 +302,8 @@
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150");
// Spy is used to capture consumer in delayDialForDdsSwitch
EmergencyStateTracker spyEst = spy(emergencyStateTracker);
- CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone, TEST_CALL_ID,
- false);
+ CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone,
+ mTestConnection1, false);
// startEmergencyCall should trigger radio on
ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
@@ -270,7 +344,7 @@
when(mSatelliteController.isSatelliteEnabled()).thenReturn(true);
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// startEmergencyCall should trigger satellite modem off
ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
@@ -300,7 +374,7 @@
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK, "0");
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Radio already on so shouldn't trigger this
verify(mRadioOnHelper, never()).triggerRadioOnAndListen(any(), anyBoolean(), any(),
@@ -325,7 +399,7 @@
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "0");
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// non-DDS supports SUPL, so no DDS switch
verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
@@ -347,7 +421,7 @@
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "0");
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Is roaming, so no DDS switch
verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
@@ -374,7 +448,7 @@
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK, "0");
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Verify DDS switch
verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /* phoneId */,
@@ -402,7 +476,7 @@
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK, "0");
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Verify DDS switch
verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /* phoneId */,
@@ -411,7 +485,7 @@
/**
* Test that once EmergencyStateTracker handler receives set emergency mode done message it sets
- * IsInEmergencyCall to true, sets LastEmergencyRegResult and completes future with
+ * IsInEmergencyCall to true, sets LastEmergencyRegistrationResult and completes future with
* DisconnectCause.NOT_DISCONNECTED.
*/
@Test
@@ -425,7 +499,7 @@
setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
// Call startEmergencyCall() to set testPhone
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Verify future completes with DisconnectCause.NOT_DISCONNECTED
CompletableFuture<Void> unused = future.thenAccept((result) -> {
assertEquals((Integer) result, (Integer) DisconnectCause.NOT_DISCONNECTED);
@@ -435,7 +509,7 @@
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyCall());
- assertTrue(emergencyStateTracker.getEmergencyRegResult().equals(E_REG_RESULT));
+ assertTrue(emergencyStateTracker.getEmergencyRegistrationResult().equals(E_REG_RESULT));
verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
}
@@ -455,13 +529,13 @@
setUpAsyncResultForExitEmergencyMode(testPhone);
// Call startEmergencyCall() to set testPhone
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
assertFalse(emergencyStateTracker.isInEmergencyCall());
@@ -482,15 +556,15 @@
/* isRadioOn= */ true);
// Call startEmergencyCall() to set testPhone
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// set domain
emergencyStateTracker.onEmergencyCallDomainUpdated(PhoneConstants.PHONE_TYPE_IMS,
- TEST_CALL_ID);
+ mTestConnection1);
// End call to enter ECM
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
// Make sure CS ECBM is true
assertTrue(emergencyStateTracker.isInEcm());
@@ -512,15 +586,15 @@
/* isRadioOn= */ true);
// Call startEmergencyCall() to set testPhone
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// set domain
emergencyStateTracker.onEmergencyCallDomainUpdated(PhoneConstants.PHONE_TYPE_CDMA,
- TEST_CALL_ID);
+ mTestConnection1);
// End call to enter ECM
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
// Make sure IMS ECBM is true
assertTrue(emergencyStateTracker.isInEcm());
@@ -544,15 +618,15 @@
doReturn(PhoneConstants.PHONE_TYPE_GSM).when(testPhone).getPhoneType();
// Call startEmergencyCall() to set testPhone
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// set domain
emergencyStateTracker.onEmergencyCallDomainUpdated(PhoneConstants.PHONE_TYPE_CDMA,
- TEST_CALL_ID);
+ mTestConnection1);
// End call to enter ECM
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEcm());
assertFalse(emergencyStateTracker.isInCdmaEcm());
@@ -572,7 +646,7 @@
/* isRadioOn= */ true );
// Call startEmergencyCall() to set testPhone
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
emergencyStateTracker.onEmergencyTransportChanged(
EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN);
@@ -581,6 +655,27 @@
}
/**
+ * Test that onEmergencyTransportChangedAndWait sets the new emergency mode.
+ */
+ @Test
+ @SmallTest
+ public void onEmergencyTransportChangedAndWait_setEmergencyMode() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ // Create test Phones
+ Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // Call startEmergencyCall() to set testPhone
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+ mTestConnection1, false);
+
+ emergencyStateTracker.onEmergencyTransportChangedAndWait(
+ EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN);
+
+ verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any());
+ }
+
+ /**
* Test that after endCall() is called, EmergencyStateTracker will enter ECM if the call was
* ACTIVE and send related intents.
*/
@@ -595,15 +690,15 @@
/* isRadioOn= */ true);
// Start emergency call then enter ECM
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, true);
assertFalse(emergencyStateTracker.isInEcm());
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEcm());
// Verify intents are sent that ECM is entered
@@ -630,13 +725,13 @@
/* isRadioOn= */ true);
// Start emergency call then enter ECM
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Call does not reach ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.IDLE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.IDLE, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertFalse(emergencyStateTracker.isInEcm());
}
@@ -657,15 +752,15 @@
setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
setUpAsyncResultForExitEmergencyMode(testPhone);
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
processAllMessages();
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEcm());
@@ -692,17 +787,17 @@
setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
setUpAsyncResultForExitEmergencyMode(testPhone);
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+ PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
processAllMessages();
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEcm());
@@ -748,22 +843,22 @@
verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+ PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
processAllMessages();
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEcm());
@@ -792,7 +887,7 @@
replaceInstance(EmergencyStateTracker.class, "mContext", emergencyStateTracker, mContext);
// dial on the same slot
- unused = emergencyStateTracker.startEmergencyCall(testPhone, TEST_CALL_ID, false);
+ unused = emergencyStateTracker.startEmergencyCall(testPhone, mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -824,22 +919,22 @@
verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+ PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
processAllMessages();
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEcm());
@@ -872,7 +967,7 @@
replaceInstance(EmergencyStateTracker.class, "mContext", emergencyStateTracker, mContext);
// dial on the other slot
- unused = emergencyStateTracker.startEmergencyCall(phone1, TEST_CALL_ID, false);
+ unused = emergencyStateTracker.startEmergencyCall(phone1, mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -902,15 +997,15 @@
setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
setUpAsyncResultForExitEmergencyMode(testPhone);
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
processAllMessages();
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEcm());
@@ -939,16 +1034,16 @@
setUpAsyncResultForExitEmergencyMode(testPhone);
// Start emergency call then enter ECM
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+ PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
// End call to enter ECM
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
// verify ecbm states are correct
@@ -984,7 +1079,7 @@
Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
/* isRadioOn= */ true);
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> {
@@ -1004,15 +1099,13 @@
Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
/* isRadioOn= */ true);
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, true);
+ mTestConnection1, true);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
- assertTrue(emergencyStateTracker.isInEmergencyCall());
- // Expect: DisconnectCause#NOT_DISCONNECTED.
- assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
- Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
- verify(phone0, never()).setEmergencyMode(anyInt(), any(Message.class));
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ assertFalse(future.isDone());
+ verify(phone0).setEmergencyMode(anyInt(), any(Message.class));
}
@Test
@@ -1025,18 +1118,18 @@
setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
// First active call
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Second starting call
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID_2, false);
+ mTestConnection2, false);
// Returns DisconnectCause#NOT_DISCONNECTED immediately.
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
@@ -1055,22 +1148,22 @@
// First active call
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
assertTrue(emergencyStateTracker.isInEcm());
// Second emergency call started.
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID_2, false);
+ mTestConnection2, false);
// Returns DisconnectCause#NOT_DISCONNECTED immediately.
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
@@ -1088,7 +1181,7 @@
// First emergency call
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -1098,7 +1191,7 @@
// Second emergency call
Phone phone1 = getPhone(1);
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone1,
- TEST_CALL_ID_2, false);
+ mTestConnection2, false);
// Returns DisconnectCause#ERROR_UNSPECIFIED immediately.
assertEquals(future.getNow(DisconnectCause.NOT_DISCONNECTED),
@@ -1117,7 +1210,7 @@
// First active call
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -1126,15 +1219,15 @@
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
assertTrue(emergencyStateTracker.isInEcm());
assertFalse(emergencyStateTracker.isInEmergencyCall());
// Second emergency call started.
- future = emergencyStateTracker.startEmergencyCall(phone0, TEST_CALL_ID_2, false);
+ future = emergencyStateTracker.startEmergencyCall(phone0, mTestConnection2, false);
assertTrue(emergencyStateTracker.isInEmergencyMode());
// Returns DisconnectCause#NOT_DISCONNECTED immediately.
@@ -1143,7 +1236,7 @@
emergencyStateTracker.onEmergencyTransportChanged(
EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WLAN);
- emergencyStateTracker.endCall(TEST_CALL_ID_2);
+ emergencyStateTracker.endCall(mTestConnection2);
processAllMessages();
// At this time, ECM is still running so still in ECM.
@@ -1163,7 +1256,7 @@
/* isRadioOn= */ true);
// Call startEmergencyCall() to set testPhone
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
// Set emergency transport
emergencyStateTracker.onEmergencyTransportChanged(
@@ -1171,12 +1264,12 @@
// Set call properties
emergencyStateTracker.onEmergencyCallPropertiesChanged(
- android.telecom.Connection.PROPERTY_WIFI, TEST_CALL_ID);
+ android.telecom.Connection.PROPERTY_WIFI, mTestConnection1);
verify(testPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any());
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
verify(testPhone, times(1)).cancelEmergencyNetworkScan(eq(true), any());
}
@@ -1196,7 +1289,7 @@
assertTrue(emergencyStateTracker.isInEmergencyMode());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- assertTrue(emergencyStateTracker.getEmergencyRegResult().equals(E_REG_RESULT));
+ assertTrue(emergencyStateTracker.getEmergencyRegistrationResult().equals(E_REG_RESULT));
// Expect: DisconnectCause#NOT_DISCONNECTED.
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
@@ -1217,7 +1310,7 @@
assertTrue(emergencyStateTracker.isInEmergencyMode());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- assertTrue(emergencyStateTracker.getEmergencyRegResult().equals(E_REG_RESULT));
+ assertTrue(emergencyStateTracker.getEmergencyRegistrationResult().equals(E_REG_RESULT));
// Expect: DisconnectCause#NOT_DISCONNECTED.
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
@@ -1251,7 +1344,7 @@
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
- assertTrue(emergencyStateTracker.getEmergencyRegResult().equals(E_REG_RESULT));
+ assertTrue(emergencyStateTracker.getEmergencyRegistrationResult().equals(E_REG_RESULT));
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WLAN), any(Message.class));
}
@@ -1327,14 +1420,14 @@
setEcmSupportedConfig(phone0, true);
// Emergency call is ended and the emergency callback is entered.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEcm());
assertFalse(emergencyStateTracker.isInEmergencyCall());
@@ -1385,14 +1478,14 @@
setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
// Emergency call is in active.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Emergency SMS is being started.
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
@@ -1412,7 +1505,7 @@
/* isRadioOn= */ true);
// Emergency call is in progress.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -1457,7 +1550,7 @@
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
// Emergency call is being started.
- future = emergencyStateTracker.startEmergencyCall(phone0, TEST_CALL_ID, false);
+ future = emergencyStateTracker.startEmergencyCall(phone0, mTestConnection1, false);
processAllMessages();
verify(phone0).exitEmergencyMode(any(Message.class));
@@ -1488,7 +1581,7 @@
// Emergency call is being started.
CompletableFuture<Integer> callFuture = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
assertFalse(smsFuture.isDone());
assertFalse(callFuture.isDone());
@@ -1500,7 +1593,7 @@
processAllMessages();
// Exit emergency mode and set the emergency mode again by the call when the exit result
- // is received for obtaining the latest EmergencyRegResult.
+ // is received for obtaining the latest EmergencyRegistrationResult.
verify(phone0).exitEmergencyMode(any(Message.class));
ArgumentCaptor<Message> callCaptor = ArgumentCaptor.forClass(Message.class);
verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), callCaptor.capture());
@@ -1530,14 +1623,14 @@
setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
// Emergency call is in active.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Emergency SMS is being started using the different phone.
Phone phone1 = getPhone(1);
@@ -1571,7 +1664,7 @@
// Emergency call is being started using the different phone.
Phone phone1 = getPhone(1);
setUpAsyncResultForSetEmergencyMode(phone1, E_REG_RESULT);
- future = emergencyStateTracker.startEmergencyCall(phone1, TEST_CALL_ID, false);
+ future = emergencyStateTracker.startEmergencyCall(phone1, mTestConnection1, false);
processAllMessages();
verify(phone0).exitEmergencyMode(any(Message.class));
@@ -1604,7 +1697,7 @@
// Emergency call is being started using the different phone.
Phone phone1 = getPhone(1);
CompletableFuture<Integer> callFuture = emergencyStateTracker.startEmergencyCall(phone1,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
assertFalse(smsFuture.isDone());
assertFalse(callFuture.isDone());
@@ -1616,7 +1709,7 @@
processAllMessages();
// Exit emergency mode and set the emergency mode again by the call when the exit result
- // is received for obtaining the latest EmergencyRegResult.
+ // is received for obtaining the latest EmergencyRegistrationResult.
verify(phone0).exitEmergencyMode(any(Message.class));
ArgumentCaptor<Message> callCaptor = ArgumentCaptor.forClass(Message.class);
verify(phone1).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), callCaptor.capture());
@@ -1648,14 +1741,14 @@
setEcmSupportedConfig(phone0, false);
// Emergency call is in active.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Emergency SMS is being started.
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
@@ -1665,7 +1758,7 @@
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -1689,14 +1782,14 @@
setEcmSupportedConfig(phone0, false);
// Emergency call is in active.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Emergency SMS is being started.
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
@@ -1710,7 +1803,7 @@
assertTrue(emergencyStateTracker.isInEmergencyMode());
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
assertFalse(emergencyStateTracker.isInEmergencyMode());
@@ -1730,14 +1823,14 @@
setEcmSupportedConfig(phone0, true);
// Emergency call is in active.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Emergency SMS is being started.
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
@@ -1747,7 +1840,7 @@
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEcm());
@@ -1782,14 +1875,14 @@
setEcmSupportedConfig(phone0, true);
// Emergency call is in active.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Emergency SMS is being started.
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
@@ -1799,7 +1892,7 @@
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -1838,14 +1931,14 @@
setEcmSupportedConfig(phone0, true);
// Emergency call is in active.
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
// Emergency SMS is being started.
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
@@ -1859,7 +1952,7 @@
assertTrue(emergencyStateTracker.isInEmergencyMode());
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -1888,7 +1981,7 @@
EmergencyStateTracker testEst = setupEmergencyStateTracker(
false /* isSuplDdsSwitchRequiredForEmergencyCall */);
- assertNotNull(testEst.startEmergencyCall(phone, TEST_CALL_ID, false));
+ assertNotNull(testEst.startEmergencyCall(phone, mTestConnection1, false));
ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
@@ -1973,17 +2066,17 @@
setUpAsyncResultForExitEmergencyMode(testPhone);
// Start emergency call then enter ECM
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+ PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
// End call to enter ECM
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
// verify ecbm states are correct
@@ -1993,7 +2086,7 @@
// 2nd call while in emergency callback mode
unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
emergencyStateTracker.onEmergencyTransportChanged(
EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN);
processAllMessages();
@@ -2011,11 +2104,11 @@
verify(testPhone).notifyEcbmTimerReset(eq(Boolean.TRUE));
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+ PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
// End call to enter ECM
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
// verify ecbm states are correct
@@ -2056,17 +2149,17 @@
setUpAsyncResultForExitEmergencyMode(testPhone);
// Start emergency call then enter ECM
CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
processAllMessages();
// Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+ PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
// Set ecm as supported
setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
// End call to enter ECM
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
// verify ecbm states are correct
@@ -2076,7 +2169,7 @@
// 2nd call while in emergency callback mode
unused = emergencyStateTracker.startEmergencyCall(testPhone,
- TEST_CALL_ID, false);
+ mTestConnection1, false);
emergencyStateTracker.onEmergencyTransportChanged(
EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN);
processAllMessages();
@@ -2094,7 +2187,7 @@
verify(testPhone).notifyEcbmTimerReset(eq(Boolean.TRUE));
// End call to return to ECM
- emergencyStateTracker.endCall(TEST_CALL_ID);
+ emergencyStateTracker.endCall(mTestConnection1);
processAllMessages();
// verify ecbm states are correct
@@ -2191,7 +2284,8 @@
.putString(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, esExtensionSec);
}
- private void setUpAsyncResultForSetEmergencyMode(Phone phone, EmergencyRegResult regResult) {
+ private void setUpAsyncResultForSetEmergencyMode(Phone phone,
+ EmergencyRegistrationResult regResult) {
doAnswer((invocation) -> {
Object[] args = invocation.getArguments();
final Message msg = (Message) args[1];
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccConnectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccConnectorTest.java
index d4850c8..ca4576f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccConnectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccConnectorTest.java
@@ -40,6 +40,7 @@
import android.os.test.TestLooper;
import android.service.euicc.EuiccService;
import android.service.euicc.IEuiccService;
+import android.service.euicc.IGetAvailableMemoryInBytesCallback;
import android.service.euicc.IGetEidCallback;
import androidx.test.runner.AndroidJUnit4;
@@ -70,6 +71,7 @@
private static final int CARD_ID = 15;
private static final int PORT_INDEX = 0;
+ private static final long AVAILABLE_MEMORY = 123L;
@Before
public void setUp() throws Exception {
@@ -136,6 +138,31 @@
}
@Test
+ public void testInitialState_forAvailableMemory_commandRejected() {
+ prepareEuiccApp(
+ false /* hasPermission */,
+ false /* requiresBindPermission */,
+ false /* hasPriority */);
+ mConnector = new EuiccConnector(mContext, mLooper.getLooper());
+ final AtomicBoolean called = new AtomicBoolean(false);
+ mConnector.getAvailableMemoryInBytes(
+ CARD_ID,
+ new EuiccConnector.GetAvailableMemoryInBytesCommandCallback() {
+ @Override
+ public void onGetAvailableMemoryInBytesComplete(long availableMemoryInBytes) {
+ fail("Command should have failed");
+ }
+
+ @Override
+ public void onEuiccServiceUnavailable() {
+ assertTrue("Callback called twice", called.compareAndSet(false, true));
+ }
+ });
+ mLooper.dispatchAll();
+ assertTrue(called.get());
+ }
+
+ @Test
public void testInitialState_switchCommandRejected() {
prepareEuiccApp(false /* hasPermission */, false /* requiresBindPermission */,
false /* hasPriority */);
@@ -236,6 +263,47 @@
}
@Test
+ public void testCommandDispatch_forAvailableMemory_success() throws Exception {
+ prepareEuiccApp(
+ true /* hasPermission */,
+ true /* requiresBindPermission */,
+ true /* hasPriority */);
+ mConnector = new EuiccConnector(mContext, mLooper.getLooper());
+ doAnswer(
+ new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Exception {
+ IGetAvailableMemoryInBytesCallback callback =
+ invocation.getArgument(1);
+ callback.onSuccess(AVAILABLE_MEMORY);
+ return null;
+ }
+ })
+ .when(mEuiccService)
+ .getAvailableMemoryInBytes(
+ anyInt(), Mockito.<IGetAvailableMemoryInBytesCallback>any());
+ final AtomicReference<Long> availableMemoryInBytesRef = new AtomicReference<>();
+ mConnector.getAvailableMemoryInBytes(
+ CARD_ID,
+ new EuiccConnector.GetAvailableMemoryInBytesCommandCallback() {
+ @Override
+ public void onGetAvailableMemoryInBytesComplete(long availableMemoryInBytes) {
+ if (availableMemoryInBytesRef.get() != null) {
+ fail("Callback called twice");
+ }
+ availableMemoryInBytesRef.set(availableMemoryInBytes);
+ }
+
+ @Override
+ public void onEuiccServiceUnavailable() {
+ fail("Command should have succeeded");
+ }
+ });
+ mLooper.dispatchAll();
+ assertEquals(AVAILABLE_MEMORY, availableMemoryInBytesRef.get().longValue());
+ }
+
+ @Test
public void testCommandDispatch_remoteException() throws Exception {
prepareEuiccApp(true /* hasPermission */, true /* requiresBindPermission */,
true /* hasPriority */);
@@ -259,6 +327,35 @@
}
@Test
+ public void testCommandDispatch_forAvailableMemory_remoteException() throws Exception {
+ prepareEuiccApp(
+ true /* hasPermission */,
+ true /* requiresBindPermission */,
+ true /* hasPriority */);
+ mConnector = new EuiccConnector(mContext, mLooper.getLooper());
+ doThrow(new RemoteException("failure"))
+ .when(mEuiccService)
+ .getAvailableMemoryInBytes(
+ anyInt(), Mockito.<IGetAvailableMemoryInBytesCallback>any());
+ final AtomicBoolean called = new AtomicBoolean(false);
+ mConnector.getAvailableMemoryInBytes(
+ CARD_ID,
+ new EuiccConnector.GetAvailableMemoryInBytesCommandCallback() {
+ @Override
+ public void onGetAvailableMemoryInBytesComplete(long availableMemoryInBytes) {
+ fail("Command should have failed");
+ }
+
+ @Override
+ public void onEuiccServiceUnavailable() {
+ assertTrue("Callback called twice", called.compareAndSet(false, true));
+ }
+ });
+ mLooper.dispatchAll();
+ assertTrue(called.get());
+ }
+
+ @Test
public void testCommandDispatch_processDied() throws Exception {
// Kick off the asynchronous command.
prepareEuiccApp(true /* hasPermission */, true /* requiresBindPermission */,
@@ -288,6 +385,39 @@
}
@Test
+ public void testCommandDispatch_forAvailableMemory_processDied() throws Exception {
+ // Kick off the asynchronous command.
+ prepareEuiccApp(
+ true /* hasPermission */,
+ true /* requiresBindPermission */,
+ true /* hasPriority */);
+ mConnector = new EuiccConnector(mContext, mLooper.getLooper());
+ final AtomicBoolean called = new AtomicBoolean(false);
+ mConnector.getAvailableMemoryInBytes(
+ CARD_ID,
+ new EuiccConnector.GetAvailableMemoryInBytesCommandCallback() {
+ @Override
+ public void onGetAvailableMemoryInBytesComplete(long availableMemoryInBytes) {
+ fail("Unexpected command success callback");
+ }
+
+ @Override
+ public void onEuiccServiceUnavailable() {
+ assertTrue("Callback called twice", called.compareAndSet(false, true));
+ }
+ });
+ mLooper.dispatchAll();
+ assertFalse(called.get());
+
+ // Now, pretend the remote process died.
+ mConnector.onServiceDisconnected(null /* name */);
+ mLooper.dispatchAll();
+
+ // Callback should have been called.
+ assertTrue(called.get());
+ }
+
+ @Test
public void testLinger() throws Exception {
prepareEuiccApp(true /* hasPermission */, true /* requiresBindPermission */,
true /* hasPriority */);
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 a493247..d5ce447 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -41,6 +41,7 @@
import android.Manifest;
import android.annotation.Nullable;
import android.app.PendingIntent;
+import android.app.admin.flags.Flags;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.content.Intent;
@@ -49,6 +50,8 @@
import android.content.pm.Signature;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.euicc.DownloadSubscriptionResult;
import android.service.euicc.EuiccService;
@@ -94,11 +97,15 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class EuiccControllerTest extends TelephonyTest {
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final DownloadableSubscription SUBSCRIPTION =
DownloadableSubscription.forActivationCode("abcde");
@@ -134,6 +141,7 @@
private static final String ICC_ID = "54321";
private static final int CARD_ID = 25;
private static final int REMOVABLE_CARD_ID = 26;
+ private static final long AVAILABLE_MEMORY = 123L;
// Mocked classes
private EuiccConnector mMockConnector;
@@ -188,6 +196,19 @@
}
@Override
+ public void refreshSubscriptionsAndSendResult(
+ PendingIntent callbackIntent,
+ int resultCode,
+ Intent extrasIntent,
+ boolean isCallerAdmin,
+ String callingPackage,
+ int cardId,
+ Set<Integer> subscriptions) {
+ mCalledRefreshSubscriptionsAndSendResult = true;
+ sendResult(callbackIntent, resultCode, extrasIntent);
+ }
+
+ @Override
public void sendOtaStatusChangedBroadcast() {
++mNumOtaStatusChanged;
}
@@ -257,6 +278,73 @@
}
@Test(expected = SecurityException.class)
+ public void testGetAvailableMemoryInBytes_noPrivileges() throws Exception {
+ setGetAvailableMemoryInBytesPermissions(
+ false /* hasPhoneState */,
+ false /* hasPhoneStatePrivileged */,
+ false /* hasCarrierPrivileges */);
+ callGetAvailableMemoryInBytes(true /* success */, AVAILABLE_MEMORY, CARD_ID);
+ }
+
+ @Test
+ public void testGetAvailableMemoryInBytes_withPhoneState() throws Exception {
+ setGetAvailableMemoryInBytesPermissions(
+ true /* hasPhoneState */,
+ false /* hasPhoneStatePrivileged */,
+ false /* hasCarrierPrivileges */);
+ assertEquals(
+ AVAILABLE_MEMORY,
+ callGetAvailableMemoryInBytes(true /* success */, AVAILABLE_MEMORY, CARD_ID));
+ }
+
+ @Test
+ public void testGetAvailableMemoryInBytes_withPhoneStatePrivileged() throws Exception {
+ setGetAvailableMemoryInBytesPermissions(
+ false /* hasPhoneState */,
+ true /* hasPhoneStatePrivileged */,
+ false /* hasCarrierPrivileges */);
+ assertEquals(
+ AVAILABLE_MEMORY,
+ callGetAvailableMemoryInBytes(true /* success */, AVAILABLE_MEMORY, CARD_ID));
+ }
+
+ @Test
+ public void testGetAvailableMemoryInBytes_withCarrierPrivileges() throws Exception {
+ setGetAvailableMemoryInBytesPermissions(
+ false /* hasPhoneState */,
+ false /* hasPhoneStatePrivileged */,
+ true /* hasCarrierPrivileges */);
+ assertEquals(
+ AVAILABLE_MEMORY,
+ callGetAvailableMemoryInBytes(true /* success */, AVAILABLE_MEMORY, CARD_ID));
+ }
+
+ @Test
+ public void testGetAvailableMemoryInBytes_failure() throws Exception {
+ setGetAvailableMemoryInBytesPermissions(
+ true /* hasPhoneState */,
+ false /* hasPhoneStatePrivileged */,
+ false /* hasCarrierPrivileges */);
+ assertEquals(
+ EuiccManager.EUICC_MEMORY_FIELD_UNAVAILABLE,
+ callGetAvailableMemoryInBytes(false /* success */, AVAILABLE_MEMORY, CARD_ID));
+ }
+
+ @Test
+ public void testGetAvailableMemoryInBytes_unsupportedCardId() throws Exception {
+ setGetAvailableMemoryInBytesPermissions(
+ false /* hasPhoneState */,
+ false /* hasPhoneStatePrivileged */,
+ true /* hasCarrierPrivileges */);
+ assertEquals(
+ AVAILABLE_MEMORY,
+ callGetAvailableMemoryInBytes(
+ true /* success */,
+ AVAILABLE_MEMORY,
+ TelephonyManager.UNSUPPORTED_CARD_ID));
+ }
+
+ @Test(expected = SecurityException.class)
public void testGetOtaStatus_noPrivileges() {
setHasWriteEmbeddedPermission(false /* hasPermission */);
callGetOtaStatus(true /* success */, 1 /* status */);
@@ -748,6 +836,132 @@
}
@Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_noAdminPermission()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasWriteEmbeddedPermission(false);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(false);
+ setUpUiccSlotData();
+ GetDownloadableSubscriptionMetadataResult result =
+ new GetDownloadableSubscriptionMetadataResult(
+ EuiccService.RESULT_OK, SUBSCRIPTION_WITH_METADATA);
+ prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = PACKAGE_NAME;
+ when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
+ setCanManageSubscriptionOnTargetSim(false /* isTargetEuicc */, false /* hasPrivileges */);
+
+ callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
+ 12345, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR,
+ 0 /* detailedCode */);
+ verify(mMockConnector, never()).downloadSubscription(anyInt(), anyInt(),
+ any(), anyBoolean(), anyBoolean(), any(), any());
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_adminPermission()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+ setHasWriteEmbeddedPermission(false);
+
+ callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
+ EuiccService.RESULT_OK, 0 /* resolvableError */, "whatever" /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
+ assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult);
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_adminPermission_usingSwitchAfterDownload()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasWriteEmbeddedPermission(false);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+ setUpUiccSlotData();
+ GetDownloadableSubscriptionMetadataResult result =
+ new GetDownloadableSubscriptionMetadataResult(
+ EuiccService.RESULT_OK, SUBSCRIPTION_WITH_METADATA);
+ prepareGetDownloadableSubscriptionMetadataCall(true /* complete */, result);
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = PACKAGE_NAME;
+ when(mPackageManager.getPackageInfo(eq(PACKAGE_NAME), anyInt())).thenReturn(pi);
+ setCanManageSubscriptionOnTargetSim(false /* isTargetEuicc */, false /* hasPrivileges */);
+
+ callDownloadSubscription(SUBSCRIPTION, true /* switchAfterDownload */, true /* complete */,
+ 12345, 0 /* resolvableError */, PACKAGE_NAME /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR,
+ 0 /* detailedCode */);
+ verify(mMockConnector, never()).downloadSubscription(anyInt(), anyInt(),
+ any(), anyBoolean(), anyBoolean(), any(), any());
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_onlyAdminManagedAllowed_callerNotAdmin_throws()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(false);
+ setHasWriteEmbeddedPermission(true);
+ doReturn(true)
+ .when(mUserManager)
+ .hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY);
+
+ assertThrows(SecurityException.class,
+ () ->
+ callDownloadSubscription(
+ SUBSCRIPTION,
+ false /* switchAfterDownload */,
+ true /* complete */,
+ EuiccService.RESULT_OK,
+ 0 /* resolvableError */,
+ "whatever" /* callingPackage */));
+ assertFalse(mController.mCalledRefreshSubscriptionsAndSendResult);
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_onlyAdminManagedAllowed_callerNotAdmin_disabled_success()
+ throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(false);
+ setHasWriteEmbeddedPermission(true);
+ doReturn(true)
+ .when(mUserManager)
+ .hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY);
+
+ callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
+ EuiccService.RESULT_OK, 0 /* resolvableError */, "whatever" /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
+ assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult);
+ }
+
+ @Test
+ @DisableCompatChanges({EuiccManager.SHOULD_RESOLVE_PORT_INDEX_FOR_APPS})
+ public void testDownloadSubscription_onlyAdminManagedAllowed_callerIsAdmin_success()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+ setHasWriteEmbeddedPermission(false);
+ doReturn(true)
+ .when(mUserManager)
+ .hasUserRestriction(UserManager.DISALLOW_SIM_GLOBALLY);
+
+ callDownloadSubscription(SUBSCRIPTION, false /* switchAfterDownload */, true /* complete */,
+ EuiccService.RESULT_OK, 0 /* resolvableError */, "whatever" /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
+ assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult);
+ }
+
+ @Test
public void testDeleteSubscription_noSuchSubscription() throws Exception {
setHasWriteEmbeddedPermission(true);
callDeleteSubscription(
@@ -791,6 +1005,82 @@
assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult);
}
+
+ @Test
+ public void testDeleteSubscription_adminOwned_success() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasWriteEmbeddedPermission(false);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(true);
+ String callingPackage = "whatever";
+ SubscriptionInfo subInfo1 = new SubscriptionInfo.Builder()
+ .setId(SUBSCRIPTION_ID)
+ .setEmbedded(true)
+ .setIccId(ICC_ID)
+ .setCardId(CARD_ID)
+ .setPortIndex(TelephonyManager.DEFAULT_PORT_INDEX)
+ .setGroupOwner(callingPackage)
+ .build();
+ ArrayList<SubscriptionInfo> subInfos = new ArrayList<>(Arrays.asList(subInfo1));
+ when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(subInfos);
+
+ callDeleteSubscription(
+ SUBSCRIPTION_ID, ICC_ID, true /* complete */,
+ 0 /* result */, callingPackage /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK,
+ 0 /* detailedCode */);
+ }
+
+ @Test
+ public void testDeleteSubscription_adminOwned_featureDisabled_success() throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasWriteEmbeddedPermission(true);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(false);
+ String callingPackage = "whatever";
+ SubscriptionInfo subInfo1 = new SubscriptionInfo.Builder()
+ .setId(SUBSCRIPTION_ID)
+ .setEmbedded(true)
+ .setIccId(ICC_ID)
+ .setCardId(CARD_ID)
+ .setPortIndex(TelephonyManager.DEFAULT_PORT_INDEX)
+ .setGroupOwner(callingPackage)
+ .build();
+ ArrayList<SubscriptionInfo> subInfos = new ArrayList<>(Arrays.asList(subInfo1));
+ when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(subInfos);
+
+ callDeleteSubscription(
+ SUBSCRIPTION_ID, ICC_ID, true /* complete */,
+ 0 /* result */, callingPackage /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK,
+ 0 /* detailedCode */);
+ }
+
+ @Test
+ public void testDeleteSubscription_adminOwned_noPermissions_error() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
+ setHasWriteEmbeddedPermission(false);
+ setHasManageDevicePolicyManagedSubscriptionsPermission(false);
+ String callingPackage = "whatever";
+ SubscriptionInfo subInfo1 = new SubscriptionInfo.Builder()
+ .setId(SUBSCRIPTION_ID)
+ .setEmbedded(true)
+ .setIccId(ICC_ID)
+ .setCardId(CARD_ID)
+ .setPortIndex(TelephonyManager.DEFAULT_PORT_INDEX)
+ .setGroupOwner(callingPackage)
+ .build();
+ ArrayList<SubscriptionInfo> subInfos = new ArrayList<>(Arrays.asList(subInfo1));
+ when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(subInfos);
+
+ callDeleteSubscription(
+ SUBSCRIPTION_ID, ICC_ID, true /* complete */,
+ 0 /* result */, callingPackage /* callingPackage */);
+
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR,
+ 0 /* detailedCode */);
+ }
+
@Test
public void testGetResolvedPortIndexForSubscriptionSwitchWithOutMEP() throws Exception {
setUpUiccSlotData();
@@ -1495,6 +1785,25 @@
setHasCarrierPrivilegesOnActiveSubscription(hasCarrierPrivileges);
}
+ private void setGetAvailableMemoryInBytesPermissions(
+ boolean hasPhoneState, boolean hasPhoneStatePrivileged, boolean hasCarrierPrivileges)
+ throws Exception {
+ doReturn(
+ hasPhoneState
+ ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED)
+ .when(mContext)
+ .checkCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+ doReturn(
+ hasPhoneStatePrivileged
+ ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED)
+ .when(mContext)
+ .checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ when(mTelephonyManager.getPhoneCount()).thenReturn(1);
+ setHasCarrierPrivilegesOnActiveSubscription(hasCarrierPrivileges);
+ }
+
private void setHasWriteEmbeddedPermission(boolean hasPermission) {
doReturn(hasPermission
? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED)
@@ -1502,6 +1811,15 @@
.checkCallingOrSelfPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
}
+ private void setHasManageDevicePolicyManagedSubscriptionsPermission(boolean hasPermission) {
+ doReturn(hasPermission
+ ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED)
+ .when(mContext)
+ .checkCallingOrSelfPermission(
+ Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS);
+ }
+
+
private void setHasMasterClearPermission(boolean hasPermission) {
Stubber stubber = hasPermission ? doNothing() : doThrow(new SecurityException());
stubber.when(mContext).enforceCallingPermission(
@@ -1618,6 +1936,29 @@
return mController.getEid(cardId, PACKAGE_NAME);
}
+ private long callGetAvailableMemoryInBytes(
+ final boolean success, final long availableMemoryInBytes, int cardId) {
+ doAnswer(
+ new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Exception {
+ EuiccConnector.GetAvailableMemoryInBytesCommandCallback cb =
+ invocation.getArgument(1 /* resultCallback */);
+ if (success) {
+ cb.onGetAvailableMemoryInBytesComplete(availableMemoryInBytes);
+ } else {
+ cb.onEuiccServiceUnavailable();
+ }
+ return null;
+ }
+ })
+ .when(mMockConnector)
+ .getAvailableMemoryInBytes(
+ anyInt(),
+ Mockito.<EuiccConnector.GetAvailableMemoryInBytesCommandCallback>any());
+ return mController.getAvailableMemoryInBytes(cardId, PACKAGE_NAME);
+ }
+
private int callGetOtaStatus(final boolean success, final int status) {
doAnswer(new Answer<Void>() {
@Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 6496efb..14cff4b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -1603,6 +1603,17 @@
assertEquals(2, copiedDialArgs.eccCategory);
}
+ @Test
+ @SmallTest
+ public void testCanMakeWifiCall() {
+ mImsPhoneUT.setServiceState(ServiceState.STATE_IN_SERVICE);
+ mImsPhoneUT.setImsRegistered(true);
+ doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).when(mImsCT)
+ .getImsRegistrationTech();
+
+ assertTrue(mImsPhoneUT.canMakeWifiCall());
+ }
+
private ServiceState getServiceStateDataAndVoice(int rat, int regState, boolean isRoaming) {
ServiceState ss = new ServiceState();
ss.setStateOutOfService();
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 c992f4d..475c90b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -160,6 +160,8 @@
private CellularServiceState[] mServiceStates;
// IMS registrations for slot 0 and 1
+ private ImsRegistrationStats mImsRegStatsUnregisteredLte0;
+ private ImsRegistrationStats mImsRegStatsRegisteringLte0;
private ImsRegistrationStats mImsRegistrationStatsLte0;
private ImsRegistrationStats mImsRegistrationStatsWifi0;
private ImsRegistrationStats mImsRegistrationStatsLte1;
@@ -504,6 +506,42 @@
mServiceState5Proto
};
+ // IMS over LTE on slot 0, unregistered for 5 seconds at the registered state
+ mImsRegStatsUnregisteredLte0 = new ImsRegistrationStats();
+ mImsRegStatsUnregisteredLte0.carrierId = CARRIER1_ID;
+ mImsRegStatsUnregisteredLte0.simSlotIndex = 0;
+ mImsRegStatsUnregisteredLte0.rat = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegStatsUnregisteredLte0.registeredMillis = 0;
+ mImsRegStatsUnregisteredLte0.voiceCapableMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.voiceAvailableMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.smsCapableMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.smsAvailableMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.videoCapableMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.videoAvailableMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.utCapableMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.utAvailableMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.registeringMillis = 0;
+ mImsRegStatsUnregisteredLte0.unregisteredMillis = 5000L;
+ mImsRegStatsUnregisteredLte0.registeredTimes = 0;
+
+ // IMS over LTE on slot 0, registering for 5 seconds at the registered state
+ mImsRegStatsRegisteringLte0 = new ImsRegistrationStats();
+ mImsRegStatsRegisteringLte0.carrierId = CARRIER1_ID;
+ mImsRegStatsRegisteringLte0.simSlotIndex = 0;
+ mImsRegStatsRegisteringLte0.rat = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegStatsRegisteringLte0.registeredMillis = 0;
+ mImsRegStatsRegisteringLte0.voiceCapableMillis = 5000L;
+ mImsRegStatsRegisteringLte0.voiceAvailableMillis = 5000L;
+ mImsRegStatsRegisteringLte0.smsCapableMillis = 5000L;
+ mImsRegStatsRegisteringLte0.smsAvailableMillis = 5000L;
+ mImsRegStatsRegisteringLte0.videoCapableMillis = 5000L;
+ mImsRegStatsRegisteringLte0.videoAvailableMillis = 5000L;
+ mImsRegStatsRegisteringLte0.utCapableMillis = 5000L;
+ mImsRegStatsRegisteringLte0.utAvailableMillis = 5000L;
+ mImsRegStatsRegisteringLte0.registeringMillis = 5000L;
+ mImsRegStatsRegisteringLte0.unregisteredMillis = 0;
+ mImsRegStatsRegisteringLte0.registeredTimes = 1;
+
// IMS over LTE on slot 0, registered for 5 seconds
mImsRegistrationStatsLte0 = new ImsRegistrationStats();
mImsRegistrationStatsLte0.carrierId = CARRIER1_ID;
@@ -518,6 +556,9 @@
mImsRegistrationStatsLte0.videoAvailableMillis = 5000L;
mImsRegistrationStatsLte0.utCapableMillis = 5000L;
mImsRegistrationStatsLte0.utAvailableMillis = 5000L;
+ mImsRegistrationStatsLte0.registeringMillis = 0;
+ mImsRegistrationStatsLte0.unregisteredMillis = 0;
+ mImsRegistrationStatsLte0.registeredTimes = 0;
// IMS over WiFi on slot 0, registered for 10 seconds (voice only)
mImsRegistrationStatsWifi0 = new ImsRegistrationStats();
@@ -542,6 +583,9 @@
mImsRegistrationStatsLte1.videoAvailableMillis = 20000L;
mImsRegistrationStatsLte1.utCapableMillis = 20000L;
mImsRegistrationStatsLte1.utAvailableMillis = 20000L;
+ mImsRegistrationStatsLte1.registeringMillis = 0;
+ mImsRegistrationStatsLte1.unregisteredMillis = 0;
+ mImsRegistrationStatsLte1.registeredTimes = 0;
// IMS terminations on LTE
mImsRegistrationTerminationLte = new ImsRegistrationTermination();
@@ -567,8 +611,13 @@
mImsRegistrationStats =
new ImsRegistrationStats[] {
- mImsRegistrationStatsLte0, mImsRegistrationStatsWifi0, mImsRegistrationStatsLte1
+ mImsRegStatsUnregisteredLte0,
+ mImsRegStatsRegisteringLte0,
+ mImsRegistrationStatsLte0,
+ mImsRegistrationStatsWifi0,
+ mImsRegistrationStatsLte1
};
+
mImsRegistrationTerminations =
new ImsRegistrationTermination[] {
mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi
@@ -1144,6 +1193,8 @@
mServiceState5Proto = null;
mServiceSwitches = null;
mServiceStates = null;
+ mImsRegStatsUnregisteredLte0 = null;
+ mImsRegStatsRegisteringLte0 = null;
mImsRegistrationStatsLte0 = null;
mImsRegistrationStatsWifi0 = null;
mImsRegistrationStatsLte1 = null;
@@ -1753,7 +1804,7 @@
mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegistrationStatsLte0));
mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
- // Service state and service switch should be added successfully
+ // mImsRegistrationStatsLte0 should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
assertProtoArrayEquals(new ImsRegistrationStats[] {mImsRegistrationStatsLte0}, regStats);
@@ -1769,7 +1820,7 @@
mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegistrationStatsWifi0));
mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
- // Service state and service switch should be added successfully
+ // mImsRegistrationStatsLte0 and mImsRegistrationStatsWifi0 should be added successfully
verifyCurrentStateSavedToFileOnce();
ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
assertProtoArrayEqualsIgnoringOrder(
@@ -1779,18 +1830,34 @@
@Test
@SmallTest
- public void addImsRegistrationStats_updateExistingEntries() throws Exception {
- createTestFile(START_TIME_MILLIS);
- ImsRegistrationStats newImsRegistrationStatsLte0 = copyOf(mImsRegistrationStatsLte0);
+ public void addImsRegistrationStats_withExistingRegisteringEntries() throws Exception {
+ createEmptyTestFile();
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
-
- mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegistrationStatsLte0));
+ mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegStatsRegisteringLte0));
mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
- // mImsRegistrationStatsLte0's durations should be doubled
+ // mImsRegStatsRegisteringLte0's info should be added successfully
verifyCurrentStateSavedToFileOnce();
- ImsRegistrationStats[] serviceStates = mPersistAtomsStorage.getImsRegistrationStats(0L);
- newImsRegistrationStatsLte0.registeredMillis *= 2;
+ ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationStats[] {mImsRegStatsRegisteringLte0},
+ regStats);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ ImsRegistrationStats newImsRegistrationStatsLte0 = copyOf(mImsRegStatsUnregisteredLte0);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegStatsUnregisteredLte0));
+ mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
+
+ // mImsRegStatsUnregisteredLte0's durations should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ newImsRegistrationStatsLte0.unregisteredMillis *= 2;
newImsRegistrationStatsLte0.voiceCapableMillis *= 2;
newImsRegistrationStatsLte0.voiceAvailableMillis *= 2;
newImsRegistrationStatsLte0.smsCapableMillis *= 2;
@@ -1802,10 +1869,12 @@
assertProtoArrayEqualsIgnoringOrder(
new ImsRegistrationStats[] {
newImsRegistrationStatsLte0,
+ mImsRegStatsRegisteringLte0,
+ mImsRegistrationStatsLte0,
mImsRegistrationStatsWifi0,
mImsRegistrationStatsLte1
},
- serviceStates);
+ regStats);
}
@Test
@@ -1834,6 +1903,39 @@
@Test
@SmallTest
+ public void addImsRegistrationStats_withExistingDurationEntries() throws Exception {
+ createEmptyTestFile();
+ ImsRegistrationStats newImsRegStatsLte0 = copyOf(mImsRegistrationStatsLte0);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegStatsUnregisteredLte0));
+ mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegStatsRegisteringLte0));
+ mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegistrationStatsLte0));
+ mPersistAtomsStorage.incTimeMillis(DAY_IN_MILLIS);
+
+ // UnregisteredMillis, registeringMillis and registeredTimes should be added successfully
+ // capable and available durations should be tripled
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ newImsRegStatsLte0.unregisteredMillis += newImsRegStatsLte0.registeredMillis;
+ newImsRegStatsLte0.registeringMillis += newImsRegStatsLte0.registeredMillis;
+ newImsRegStatsLte0.registeredTimes += 1;
+ newImsRegStatsLte0.voiceCapableMillis *= 3;
+ newImsRegStatsLte0.voiceAvailableMillis *= 3;
+ newImsRegStatsLte0.smsCapableMillis *= 3;
+ newImsRegStatsLte0.smsAvailableMillis *= 3;
+ newImsRegStatsLte0.videoCapableMillis *= 3;
+ newImsRegStatsLte0.videoAvailableMillis *= 3;
+ newImsRegStatsLte0.utCapableMillis *= 3;
+ newImsRegStatsLte0.utAvailableMillis *= 3;
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationStats[] {
+ newImsRegStatsLte0
+ },
+ regStats);
+ }
+
+ @Test
+ @SmallTest
public void addImsRegistrationTermination_emptyProto() throws Exception {
createEmptyTestFile();
@@ -1943,7 +2045,11 @@
// pull timestamp should be updated and saved
assertProtoArrayEqualsIgnoringOrder(
new ImsRegistrationStats[] {
- mImsRegistrationStatsLte0, mImsRegistrationStatsWifi0, mImsRegistrationStatsLte1
+ mImsRegStatsUnregisteredLte0,
+ mImsRegStatsRegisteringLte0,
+ mImsRegistrationStatsLte0,
+ mImsRegistrationStatsWifi0,
+ mImsRegistrationStatsLte1
},
stats1);
assertProtoArrayEquals(new ImsRegistrationStats[0], stats2);
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 fd5f6ab..37ff69a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.satellite;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
+
import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +31,7 @@
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.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
@@ -82,6 +85,10 @@
private static final int DATAGRAM_TYPE2 = SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING;
private static final String TEST_MESSAGE = "This is a test datagram message";
private static final long TEST_EXPIRE_TIMER_SATELLITE_ALIGN = TimeUnit.SECONDS.toMillis(1);
+ private static final int TEST_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMEOUT_MILLIS =
+ (int) TimeUnit.SECONDS.toMillis(180);
+ private static final long TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS =
+ TimeUnit.SECONDS.toMillis(60);
private DatagramDispatcher mDatagramDispatcherUT;
private TestDatagramDispatcher mTestDemoModeDatagramDispatcher;
@@ -155,7 +162,7 @@
}
@Test
- public void testSendSatelliteDatagram_usingSatelliteModemInterface_success() throws Exception {
+ public void testSendSatelliteDatagram_success() throws Exception {
doAnswer(invocation -> {
Message message = (Message) invocation.getArguments()[3];
@@ -168,7 +175,7 @@
doReturn(true).when(mMockDatagramController)
.needsWaitingForSatelliteConnected();
when(mMockDatagramController.getDatagramWaitTimeForConnectedState())
- .thenReturn(DatagramController.DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT);
+ .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
mResultListener.clear();
mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
@@ -221,7 +228,7 @@
mInOrder.verify(mMockDatagramController).getDatagramWaitTimeForConnectedState();
assertTrue(mDatagramDispatcherUT.isDatagramWaitForConnectedStateTimerStarted());
- moveTimeForward(DatagramController.DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT);
+ moveTimeForward(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
processAllMessages();
verifyZeroInteractions(mMockSatelliteModemInterface);
mInOrder.verify(mMockDatagramController)
@@ -267,7 +274,87 @@
}
@Test
- public void testSendSatelliteDatagram_usingSatelliteModemInterface_failure() throws Exception {
+ public void testSendSatelliteDatagram_timeout() throws Exception {
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[3];
+
+ mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+
+ // DatagramDispatcher should ignore the second EVENT_SEND_SATELLITE_DATAGRAM_DONE
+ mDatagramDispatcherUT.obtainMessage(2 /*EVENT_SEND_SATELLITE_DATAGRAM_DONE*/,
+ new AsyncResult(message.obj, null, null))
+ .sendToTarget();
+
+ return null;
+ }).when(mMockSatelliteModemInterface).sendSatelliteDatagram(any(SatelliteDatagram.class),
+ anyBoolean(), anyBoolean(), any(Message.class));
+ doReturn(false).when(mMockDatagramController)
+ .needsWaitingForSatelliteConnected();
+ when(mMockDatagramController.getDatagramWaitTimeForConnectedState())
+ .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
+ mContextFixture.putIntResource(
+ R.integer.config_wait_for_datagram_sending_response_timeout_millis,
+ TEST_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMEOUT_MILLIS);
+ mResultListener.clear();
+
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+ processAllMessages();
+ mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
+ mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS), eq(0),
+ eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+ verifyNoMoreInteractions(mMockDatagramController);
+ verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
+ any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
+
+ clearInvocations(mMockSatelliteModemInterface);
+ clearInvocations(mMockDatagramController);
+ mResultListener.clear();
+
+ // No response for the send request from modem
+ doNothing().when(mMockSatelliteModemInterface).sendSatelliteDatagram(
+ any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+
+ mDatagramDispatcherUT.sendSatelliteDatagram(SUB_ID, DATAGRAM_TYPE1, mDatagram,
+ true, mResultListener::offer);
+ processAllMessages();
+ mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
+ mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING), eq(1),
+ eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED), eq(1),
+ eq(SATELLITE_RESULT_MODEM_TIMEOUT));
+ mInOrder.verify(mMockDatagramController)
+ .updateSendStatus(eq(SUB_ID),
+ eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE), eq(0),
+ eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+ verifyNoMoreInteractions(mMockDatagramController);
+ verify(mMockSatelliteModemInterface, times(1)).sendSatelliteDatagram(
+ any(SatelliteDatagram.class), anyBoolean(), anyBoolean(), any(Message.class));
+ verify(mMockSatelliteModemInterface).abortSendingSatelliteDatagrams(any(Message.class));
+ assertThat(mResultListener.peek()).isEqualTo(SATELLITE_RESULT_MODEM_TIMEOUT);
+ }
+
+ @Test
+ public void testSendSatelliteDatagram_failure() throws Exception {
doAnswer(invocation -> {
Message message = (Message) invocation.getArguments()[3];
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 0e16e25..3a42881 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -78,6 +78,8 @@
private static final int SUB_ID = 0;
private static final String TEST_MESSAGE = "This is a test datagram message";
private static final long TEST_EXPIRE_TIMER_SATELLITE_ALIGN = TimeUnit.SECONDS.toMillis(1);
+ private static final long TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS =
+ TimeUnit.SECONDS.toMillis(60);
private DatagramReceiver mDatagramReceiverUT;
private DatagramReceiver.SatelliteDatagramListenerHandler mSatelliteDatagramListenerHandler;
@@ -163,7 +165,7 @@
}).when(mMockSatelliteModemInterface).pollPendingSatelliteDatagrams(any(Message.class));
doReturn(true).when(mMockDatagramController).needsWaitingForSatelliteConnected();
when(mMockDatagramController.getDatagramWaitTimeForConnectedState())
- .thenReturn(DatagramController.DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT);
+ .thenReturn(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
mResultListener.clear();
mDatagramReceiverUT.pollPendingSatelliteDatagrams(SUB_ID, mResultListener::offer);
@@ -201,7 +203,7 @@
verifyZeroInteractions(mMockSatelliteModemInterface);
assertTrue(mDatagramReceiverUT.isDatagramWaitForConnectedStateTimerStarted());
- moveTimeForward(DatagramController.DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT);
+ moveTimeForward(TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS);
processAllMessages();
mInOrder.verify(mMockDatagramController).updateReceiveStatus(eq(SUB_ID),
eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED), eq(0),
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 f7483d9..431b4cc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -18,6 +18,7 @@
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.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;
@@ -45,6 +46,7 @@
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE;
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
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_NOT_AUTHORIZED;
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NO_RESOURCES;
@@ -83,6 +85,7 @@
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncResult;
@@ -131,6 +134,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -159,7 +163,11 @@
private static final String TEST_NEXT_SATELLITE_TOKEN = "TEST_NEXT_SATELLITE_TOKEN";
private static final String[] EMPTY_STRING_ARRAY = {};
private static final List<String> EMPTY_STRING_LIST = new ArrayList<>();
+ private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY =
+ "satellite_system_notification_done_key";
private static final int[] ACTIVE_SUB_IDS = {SUB_ID};
+ private static final int TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS =
+ (int) TimeUnit.SECONDS.toMillis(60);
private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
mCarrierConfigChangedListenerList = new ArrayList<>();
@@ -176,6 +184,7 @@
@Mock private ProvisionMetricsStats mMockProvisionMetricsStats;
@Mock private SessionMetricsStats mMockSessionMetricsStats;
@Mock private SubscriptionManagerService mMockSubscriptionManagerService;
+ @Mock private NotificationManager mMockNotificationManager;
private List<Integer> mIIntegerConsumerResults = new ArrayList<>();
@Mock private ISatelliteTransmissionUpdateCallback mStartTransmissionUpdateCallback;
@Mock private ISatelliteTransmissionUpdateCallback mStopTransmissionUpdateCallback;
@@ -460,6 +469,9 @@
mContextFixture.putStringArrayResource(
R.array.config_satellite_providers,
EMPTY_STRING_ARRAY);
+ mContextFixture.putIntResource(
+ R.integer.config_wait_for_satellite_enabling_response_timeout_millis,
+ TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS);
doReturn(ACTIVE_SUB_IDS).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
mCarrierConfigBundle = mContextFixture.getCarrierConfigBundle();
@@ -503,6 +515,13 @@
doNothing().when(mMockProvisionMetricsStats).reportProvisionMetrics();
doNothing().when(mMockControllerMetricsStats).reportDeprovisionCount(anyInt());
when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+ doReturn(mSST).when(mPhone).getServiceStateTracker();
+ doReturn(mSST).when(mPhone2).getServiceStateTracker();
+ doReturn(mServiceState).when(mSST).getServiceState();
+ doReturn(Context.NOTIFICATION_SERVICE).when(mContext).getSystemServiceName(
+ NotificationManager.class);
+ doReturn(mMockNotificationManager).when(mContext).getSystemService(
+ Context.NOTIFICATION_SERVICE);
mSatelliteControllerUT =
new TestSatelliteController(mContext, Looper.myLooper(), mFeatureFlags);
verify(mMockSatelliteModemInterface).registerForSatelliteProvisionStateChanged(
@@ -2869,6 +2888,241 @@
assertEquals(true, satelliteEnabledPerCarrier.get(SUB_ID1));
}
+ @Test
+ public void testUpdateRestrictReasonForEntitlementPerCarrier() throws Exception {
+ logd("testUpdateRestrictReasonForEntitlementPerCarrier");
+ when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+ // Verify that the entitlement restriction reason is added before the entitlement query,
+ // When the Satellite entitlement status value read from DB is disabled.
+ doReturn("").when(mContext).getOpPackageName();
+ doReturn("").when(mContext).getAttributionTag();
+ doReturn("0").when(mMockSubscriptionManagerService).getSubscriptionProperty(anyInt(),
+ eq(SATELLITE_ENTITLEMENT_STATUS), anyString(), anyString());
+ doReturn(new ArrayList<>()).when(
+ mMockSubscriptionManagerService).getSatelliteEntitlementPlmnList(anyInt());
+ mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+ true);
+ mCarrierConfigBundle.putBoolean(
+ CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_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();
+ Set<Integer> restrictionSet =
+ mSatelliteControllerUT.getAttachRestrictionReasonsForCarrier(SUB_ID);
+ assertEquals(1, restrictionSet.size());
+ assertTrue(restrictionSet.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT));
+ }
+
+ @Test
+ public void testUpdateEntitlementPlmnListPerCarrier() throws Exception {
+ logd("testUpdateEntitlementPlmnListPerCarrier");
+ when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+ // If the Satellite entitlement plmn list read from the DB is empty and carrier config
+ // plmn list also is empty , check whether an empty list is returned when calling
+ // getSatellitePlmnsForCarrier before the entitlement query.
+ doReturn(new ArrayList<>()).when(
+ mMockSubscriptionManagerService).getSatelliteEntitlementPlmnList(anyInt());
+ replaceInstance(SatelliteController.class, "mEntitlementPlmnListPerCarrier",
+ mSatelliteControllerUT, new SparseArray<>());
+ replaceInstance(SatelliteController.class, "mSatelliteServicesSupportedByCarriers",
+ mSatelliteControllerUT, new HashMap<>());
+ mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+ true);
+ mCarrierConfigBundle.putBoolean(
+ CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_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();
+
+ assertEquals(new ArrayList<>(), mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID));
+
+ // If the Satellite entitlement plmn list read from the DB is valid and carrier config
+ // plmn list is empty, check whether valid entitlement plmn list is returned
+ // when calling getSatellitePlmnsForCarrier before the entitlement query.
+ replaceInstance(SatelliteController.class, "mEntitlementPlmnListPerCarrier",
+ mSatelliteControllerUT, new SparseArray<>());
+ List<String> expectedSatelliteEntitlementPlmnList = Arrays.asList("123456,12560");
+ doReturn(expectedSatelliteEntitlementPlmnList).when(
+ mMockSubscriptionManagerService).getSatelliteEntitlementPlmnList(anyInt());
+ for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+ : mCarrierConfigChangedListenerList) {
+ pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+ /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+ );
+ }
+ processAllMessages();
+
+ assertEquals(expectedSatelliteEntitlementPlmnList,
+ mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID));
+
+ // If the Satellite entitlement plmn list read from the DB is valid and carrier config
+ // plmn list is valid, check whether valid entitlement plmn list is returned when
+ // calling getSatellitePlmnsForCarrier before the entitlement query.
+ replaceInstance(SatelliteController.class, "mEntitlementPlmnListPerCarrier",
+ mSatelliteControllerUT, new SparseArray<>());
+ PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle();
+ List<String> carrierConfigPlmnList = Arrays.asList("00102", "00103", "00105");
+ carrierSupportedSatelliteServicesPerProvider.putIntArray(
+ carrierConfigPlmnList.get(0), new int[]{2});
+ carrierSupportedSatelliteServicesPerProvider.putIntArray(
+ carrierConfigPlmnList.get(1), new int[]{1, 3});
+ carrierSupportedSatelliteServicesPerProvider.putIntArray(
+ carrierConfigPlmnList.get(2), new int[]{2});
+ mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager
+ .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+ carrierSupportedSatelliteServicesPerProvider);
+ for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+ : mCarrierConfigChangedListenerList) {
+ pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+ /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+ );
+ }
+ processAllMessages();
+
+ assertEquals(expectedSatelliteEntitlementPlmnList,
+ mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID));
+
+ // If the Satellite entitlement plmn list read from the DB is empty and carrier config
+ // plmn list is valid, check whether valid carrier config plmn list is returned when
+ // calling getSatellitePlmnsForCarrier before the entitlement query.
+ replaceInstance(SatelliteController.class, "mEntitlementPlmnListPerCarrier",
+ mSatelliteControllerUT, new SparseArray<>());
+ doReturn(new ArrayList<>()).when(
+ mMockSubscriptionManagerService).getSatelliteEntitlementPlmnList(anyInt());
+ for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+ : mCarrierConfigChangedListenerList) {
+ pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+ /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+ );
+ }
+ processAllMessages();
+
+ assertEquals(carrierConfigPlmnList.stream().sorted().toList(),
+ mSatelliteControllerUT.getSatellitePlmnsForCarrier(
+ SUB_ID).stream().sorted().toList());
+ }
+
+ @Test
+ public void testHandleEventServiceStateChanged() throws Exception {
+ when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+ // Do nothing when the satellite is not connected
+ doReturn(false).when(mServiceState).isUsingNonTerrestrialNetwork();
+ sendServiceStateChangedEvent();
+ processAllMessages();
+ assertEquals(false,
+ mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
+ verify(mMockNotificationManager, never()).notifyAsUser(anyString(), anyInt(), any(), any());
+
+ // Check sending a system notification when the satellite is connected
+ doReturn(true).when(mServiceState).isUsingNonTerrestrialNetwork();
+ sendServiceStateChangedEvent();
+ processAllMessages();
+ verify(mMockNotificationManager, times(1)).notifyAsUser(anyString(), anyInt(), any(),
+ any());
+ assertEquals(true,
+ mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
+
+ // Check don't display again after displayed already a system notification.
+ sendServiceStateChangedEvent();
+ processAllMessages();
+ verify(mMockNotificationManager, times(1)).notifyAsUser(anyString(), anyInt(), any(),
+ any());
+ }
+
+ @Test
+ public void testRequestSatelliteEnabled_timeout() {
+ mIsSatelliteEnabledSemaphore.drainPermits();
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+ verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+ sendProvisionedStateChangedEvent(true, null);
+ processAllMessages();
+ verifySatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+
+ // Successfully disable satellite
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_RESULT_SUCCESS);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+
+ // Time out to enable satellite
+ ArgumentCaptor<Message> enableSatelliteResponse = ArgumentCaptor.forClass(Message.class);
+ mIIntegerConsumerResults.clear();
+ setUpNoResponseForRequestSatelliteEnabled(true, false);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertFalse(waitForIIntegerConsumerResult(1));
+ verify(mMockSatelliteModemInterface).requestSatelliteEnabled(eq(true), eq(false),
+ enableSatelliteResponse.capture());
+
+ clearInvocations(mMockSatelliteModemInterface);
+ moveTimeForward(TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_RESULT_MODEM_TIMEOUT, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockSatelliteModemInterface).requestSatelliteEnabled(eq(false), eq(false), any(
+ Message.class));
+ verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+
+ // Send the response for the above request to enable satellite. SatelliteController should
+ // ignore the event
+ Message response = enableSatelliteResponse.getValue();
+ AsyncResult.forMessage(response, null, null);
+ response.sendToTarget();
+ processAllMessages();
+ verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+
+ // Successfully enable satellite
+ mIIntegerConsumerResults.clear();
+ setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_RESULT_SUCCESS);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
+
+ // Time out to disable satellite
+ ArgumentCaptor<Message> disableSatelliteResponse = ArgumentCaptor.forClass(Message.class);
+ mIIntegerConsumerResults.clear();
+ clearInvocations(mMockSatelliteModemInterface);
+ setUpNoResponseForRequestSatelliteEnabled(false, false);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
+ processAllMessages();
+ assertFalse(waitForIIntegerConsumerResult(1));
+ verify(mMockSatelliteModemInterface).requestSatelliteEnabled(eq(false), eq(false),
+ disableSatelliteResponse.capture());
+
+ clearInvocations(mMockSatelliteModemInterface);
+ moveTimeForward(TEST_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMEOUT_MILLIS);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_RESULT_MODEM_TIMEOUT, (long) mIIntegerConsumerResults.get(0));
+ verify(mMockSatelliteModemInterface, never()).requestSatelliteEnabled(anyBoolean(),
+ anyBoolean(), any(Message.class));
+ verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+
+ // Send the response for the above request to disable satellite. SatelliteController should
+ // ignore the event
+ response = disableSatelliteResponse.getValue();
+ AsyncResult.forMessage(response, null, null);
+ response.sendToTarget();
+ processAllMessages();
+ verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+ }
+
private void resetSatelliteControllerUTEnabledState() {
logd("resetSatelliteControllerUTEnabledState");
setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
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 6d3bb4e..a1c2cfc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -18,6 +18,7 @@
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;
@@ -571,6 +572,35 @@
SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
clearInvocations(mMockDatagramController);
+ // Start receiving datagrams
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+ SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS);
+ processAllMessages();
+
+ // SatelliteSessionController should move to TRANSFERRING state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+ assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+ verify(mMockDatagramController).onSatelliteModemStateChanged(
+ SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+ clearInvocations(mMockDatagramController);
+
+ // Receiving datagrams is successful and done.
+ mTestSatelliteSessionController.onDatagramTransferStateChanged(
+ SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+ processAllMessages();
+
+ // SatelliteSessionController should move to CONNECTED state.
+ assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+ SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+ assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+ assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+ verify(mMockDatagramController).onSatelliteModemStateChanged(
+ SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+ clearInvocations(mMockDatagramController);
+
// Wait for timeout
moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
index 582c658..8841c7a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
@@ -20,13 +20,24 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.telephony.CellularIdentifierDisclosure;
import com.android.internal.telephony.TestExecutorService;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.InOrder;
import java.util.concurrent.TimeUnit;
@@ -38,6 +49,9 @@
private static final int SUB_ID_1 = 1;
private static final int SUB_ID_2 = 2;
private CellularIdentifierDisclosure mDislosure;
+ private CellularNetworkSecuritySafetySource mSafetySource;
+ private Context mContext;
+ private InOrder mInOrder;
@Before
public void setUp() {
@@ -47,33 +61,41 @@
CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
"001001",
false);
+ mSafetySource = mock(CellularNetworkSecuritySafetySource.class);
+ mInOrder = inOrder(mSafetySource);
}
@Test
public void testInitializeDisabled() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
assertFalse(notifier.isEnabled());
+ verify(mSafetySource, never()).setIdentifierDisclosureIssueEnabled(any(), anyBoolean());
}
@Test
public void testDisableAddDisclosureNop() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
assertFalse(notifier.isEnabled());
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
+ verify(mSafetySource, never())
+ .setIdentifierDisclosure(any(), anyInt(), anyInt(), any(), any());
}
@Test
public void testAddDisclosureEmergencyNop() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
CellularIdentifierDisclosure emergencyDisclosure =
new CellularIdentifierDisclosure(
CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
@@ -81,116 +103,161 @@
"001001",
true);
- notifier.enable();
- notifier.addDisclosure(SUB_ID_1, emergencyDisclosure);
+ notifier.enable(mContext);
+ notifier.addDisclosure(mContext, SUB_ID_1, emergencyDisclosure);
assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
+ verify(mSafetySource, never())
+ .setIdentifierDisclosure(any(), anyInt(), anyInt(), any(), any());
}
@Test
public void testAddDisclosureCountIncrements() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
- notifier.enable();
+ notifier.enable(mContext);
for (int i = 0; i < 3; i++) {
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
}
assertEquals(3, notifier.getCurrentDisclosureCount(SUB_ID_1));
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(3), any(), any());
}
@Test
public void testSingleDisclosureStartAndEndTimesAreEqual() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
- notifier.enable();
+ notifier.enable(mContext);
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
assertEquals(1, notifier.getCurrentDisclosureCount(SUB_ID_1));
assertTrue(notifier.getFirstOpen(SUB_ID_1).equals(notifier.getCurrentEnd(SUB_ID_1)));
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
}
@Test
public void testMultipleDisclosuresTimeWindows() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
- notifier.enable();
+ notifier.enable(mContext);
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
assertEquals(2, notifier.getCurrentDisclosureCount(SUB_ID_1));
assertTrue(notifier.getFirstOpen(SUB_ID_1).isBefore(notifier.getCurrentEnd(SUB_ID_1)));
+ verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
}
@Test
public void testAddDisclosureThenWindowClose() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
// One round of disclosures
- notifier.enable();
- notifier.addDisclosure(SUB_ID_1, mDislosure);
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.enable(mContext);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
assertEquals(2, notifier.getCurrentDisclosureCount(SUB_ID_1));
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
// Window close should reset the counter
executor.advanceTime(WINDOW_CLOSE_ADVANCE_MILLIS);
assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
// A new disclosure should increment as normal
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
assertEquals(1, notifier.getCurrentDisclosureCount(SUB_ID_1));
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
}
@Test
public void testDisableClosesWindow() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
// One round of disclosures
- notifier.enable();
- notifier.addDisclosure(SUB_ID_1, mDislosure);
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.enable(mContext);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
assertEquals(2, notifier.getCurrentDisclosureCount(SUB_ID_1));
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
- notifier.disable();
+ notifier.disable(mContext);
assertFalse(notifier.isEnabled());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosureIssueEnabled(any(), eq(false));
// We're disabled now so no disclosures should open the disclosure window
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
+ mInOrder.verifyNoMoreInteractions();
}
@Test
public void testMultipleSubIdsTrackedIndependently() {
TestExecutorService executor = new TestExecutorService();
CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(executor, 15, TimeUnit.MINUTES);
+ new CellularIdentifierDisclosureNotifier(
+ executor, 15, TimeUnit.MINUTES, mSafetySource);
- notifier.enable();
+ notifier.enable(mContext);
for (int i = 0; i < 3; i++) {
- notifier.addDisclosure(SUB_ID_1, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
}
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(1), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(3), any(), any());
for (int i = 0; i < 4; i++) {
- notifier.addDisclosure(SUB_ID_2, mDislosure);
+ notifier.addDisclosure(mContext, SUB_ID_2, mDislosure);
}
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_2), eq(1), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_2), eq(2), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_2), eq(3), any(), any());
+ mInOrder.verify(mSafetySource, times(1))
+ .setIdentifierDisclosure(any(), eq(SUB_ID_2), eq(4), any(), any());
assertEquals(3, notifier.getCurrentDisclosureCount(SUB_ID_1));
assertEquals(4, notifier.getCurrentDisclosureCount(SUB_ID_2));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java b/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java
new file mode 100644
index 0000000..76118c4
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/CellularNetworkSecuritySafetySourceTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 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.security;
+
+import static com.android.internal.telephony.security.CellularNetworkSecuritySafetySource.NULL_CIPHER_STATE_ENCRYPTED;
+import static com.android.internal.telephony.security.CellularNetworkSecuritySafetySource.NULL_CIPHER_STATE_NOTIFY_ENCRYPTED;
+import static com.android.internal.telephony.security.CellularNetworkSecuritySafetySource.NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.safetycenter.SafetySourceData;
+import android.util.Singleton;
+
+import com.android.internal.R;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.TestApplication;
+import com.android.internal.telephony.security.CellularNetworkSecuritySafetySource.SafetyCenterManagerWrapper;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.time.Instant;
+
+public final class CellularNetworkSecuritySafetySourceTest extends TelephonyTest {
+
+ private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ // unmock ActivityManager to be able to register receiver, create real PendingIntents.
+ restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
+ restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
+
+ SubscriptionInfoInternal info0 = new SubscriptionInfoInternal.Builder()
+ .setId(0)
+ .setDisplayName("fake_name0")
+ .build();
+ doReturn(info0).when(mSubscriptionManagerService).getSubscriptionInfoInternal(eq(0));
+ SubscriptionInfoInternal info1 = new SubscriptionInfoInternal.Builder()
+ .setId(1)
+ .setDisplayName("fake_name1")
+ .build();
+ doReturn(info1).when(mSubscriptionManagerService).getSubscriptionInfoInternal(eq(1));
+
+ mContextFixture.putResource(R.string.scCellularNetworkSecurityTitle, "fake");
+ mContextFixture.putResource(R.string.scCellularNetworkSecuritySummary, "fake");
+ mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedTitle, "fake %1$s");
+ mContextFixture.putResource(R.string.scNullCipherIssueNonEncryptedSummary, "fake");
+ mContextFixture.putResource(R.string.scNullCipherIssueEncryptedTitle, "fake %1$s");
+ mContextFixture.putResource(R.string.scNullCipherIssueEncryptedSummary, "fake");
+ mContextFixture.putResource(R.string.scIdentifierDisclosureIssueTitle, "fake");
+ mContextFixture.putResource(
+ R.string.scIdentifierDisclosureIssueSummary, "fake %1$d %2$tr %3$tr %4$s");
+ mContextFixture.putResource(R.string.scNullCipherIssueActionSettings, "fake");
+ mContextFixture.putResource(R.string.scNullCipherIssueActionLearnMore, "fake");
+
+ mSafetyCenterManagerWrapper = mock(SafetyCenterManagerWrapper.class);
+ doAnswer(inv -> getActivityPendingIntent(inv.getArgument(1)))
+ .when(mSafetyCenterManagerWrapper)
+ .getActivityPendingIntent(any(Context.class), any(Intent.class));
+
+ mSafetySource = new CellularNetworkSecuritySafetySource(mSafetyCenterManagerWrapper);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private PendingIntent getActivityPendingIntent(Intent intent) {
+ Context context = TestApplication.getAppContext();
+ return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+ }
+
+ @Test
+ public void disableNullCipherIssue_nullData() {
+ mSafetySource.setNullCipherIssueEnabled(mContext, false);
+
+ verify(mSafetyCenterManagerWrapper, times(1)).setSafetySourceData(isNull());
+ }
+
+ @Test
+ public void enableNullCipherIssue_statusWithoutIssues() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setNullCipherIssueEnabled(mContext, true);
+
+ verify(mSafetyCenterManagerWrapper, times(1)).setSafetySourceData(data.capture());
+ assertThat(data.getValue().getStatus()).isNotNull();
+ assertThat(data.getValue().getIssues()).isEmpty();
+ }
+
+ @Test
+ public void setNullCipherState_encrypted_statusWithoutIssue() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setNullCipherIssueEnabled(mContext, true);
+ mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_ENCRYPTED);
+
+ verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+ assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+ assertThat(data.getAllValues().get(1).getIssues()).isEmpty();
+ }
+
+ @Test
+ public void setNullCipherState_notifyEncrypted_statusWithIssue() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setNullCipherIssueEnabled(mContext, true);
+ mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_ENCRYPTED);
+
+ verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+ assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+ assertThat(data.getAllValues().get(1).getIssues()).hasSize(1);
+ }
+
+ @Test
+ public void setNullCipherState_notifyNonEncrypted_statusWithIssue() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setNullCipherIssueEnabled(mContext, true);
+ mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+
+ verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+ assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+ assertThat(data.getAllValues().get(1).getIssues()).hasSize(1);
+ }
+
+ @Test
+ public void setNullCipherState_multipleNonEncrypted_statusWithTwoIssues() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setNullCipherIssueEnabled(mContext, true);
+ mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+ mSafetySource.setNullCipherState(mContext, 1, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+
+ verify(mSafetyCenterManagerWrapper, times(3)).setSafetySourceData(data.capture());
+ assertThat(data.getAllValues().get(2).getStatus()).isNotNull();
+ assertThat(data.getAllValues().get(2).getIssues()).hasSize(2);
+ }
+
+ @Test
+ public void disableIdentifierDisclosueIssue_nullData() {
+ // We must first enable before disabling, since a standalone call to disable may result in
+ // a no-op when the default for a new notifier is to be disabled.
+ mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+ mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, false);
+
+ verify(mSafetyCenterManagerWrapper, times(1)).setSafetySourceData(isNull());
+ }
+
+ @Test
+ public void enableIdentifierDisclosureIssue_enableTwice() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+ mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+ mSafetySource.setIdentifierDisclosure(mContext, 0, 12, Instant.now(), Instant.now());
+ mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+
+ // Two invocations because the initial enablement and the subsequent disclosure result in
+ // updates to safety center
+ verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+ // When we're enabled, enabling again should not clear our issue list.
+ assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+ assertThat(data.getAllValues().get(1).getIssues()).hasSize(1);
+ }
+
+ @Test
+ public void enableIdentifierDisclosueIssue_statusWithoutIssues() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+
+ verify(mSafetyCenterManagerWrapper, times(1)).setSafetySourceData(data.capture());
+ assertThat(data.getValue().getStatus()).isNotNull();
+ assertThat(data.getValue().getIssues()).isEmpty();
+ }
+
+ @Test
+ public void setIdentifierDisclosure_singleDisclosure_statusWithIssue() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+ mSafetySource.setIdentifierDisclosure(mContext, 0, 12, Instant.now(), Instant.now());
+
+ verify(mSafetyCenterManagerWrapper, times(2)).setSafetySourceData(data.capture());
+ assertThat(data.getAllValues().get(1).getStatus()).isNotNull();
+ assertThat(data.getAllValues().get(1).getIssues()).hasSize(1);
+ }
+
+ @Test
+ public void setIdentifierDisclosure_multipleDisclosures_statusWithTwoIssues() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+ mSafetySource.setIdentifierDisclosure(mContext, 0, 12, Instant.now(), Instant.now());
+ mSafetySource.setIdentifierDisclosure(mContext, 1, 3, Instant.now(), Instant.now());
+
+ verify(mSafetyCenterManagerWrapper, times(3)).setSafetySourceData(data.capture());
+ assertThat(data.getAllValues().get(2).getStatus()).isNotNull();
+ assertThat(data.getAllValues().get(2).getIssues()).hasSize(2);
+ }
+
+ @Test
+ public void multipleIssuesKinds_statusWithTwoIssues() {
+ ArgumentCaptor<SafetySourceData> data = ArgumentCaptor.forClass(SafetySourceData.class);
+
+ mSafetySource.setNullCipherIssueEnabled(mContext, true);
+ mSafetySource.setNullCipherState(mContext, 0, NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED);
+ mSafetySource.setIdentifierDisclosureIssueEnabled(mContext, true);
+ mSafetySource.setIdentifierDisclosure(mContext, 0, 12, Instant.now(), Instant.now());
+
+ verify(mSafetyCenterManagerWrapper, times(4)).setSafetySourceData(data.capture());
+ assertThat(data.getAllValues().get(3).getStatus()).isNotNull();
+ assertThat(data.getAllValues().get(3).getIssues()).hasSize(2);
+ }
+}
\ No newline at end of file
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 1a89c15..7339e42 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -128,12 +128,19 @@
SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK;
static final int FAKE_SERVICE_CAPABILITIES_2 =
SubscriptionManager.SERVICE_CAPABILITY_SMS_BITMASK;
+ static final int FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED = 1;
+ static final int FAKE_SATELLITE_ENTITLEMENT_STATUS_DISABLED = 0;
+ static final String FAKE_SATELLITE_ENTITLEMENT_PLMNS1 = "123123,12310";
+ static final String FAKE_SATELLITE_ENTITLEMENT_PLMNS2 = "";
static final String FAKE_MAC_ADDRESS1 = "DC:E5:5B:38:7D:40";
static final String FAKE_MAC_ADDRESS2 = "DC:B5:4F:47:F3:4C";
private FeatureFlags mFeatureFlags;
+ static final int FAKE_TRANSFER_STATUS_TRANSFERRED_OUT = 1;
+ static final int FAKE_TRANSFER_STATUS_CONVERTED = 2;
+
static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO1 =
new SubscriptionInfoInternal.Builder()
.setId(1)
@@ -204,6 +211,9 @@
.setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_NTN_DISABLED)
.setGroupDisabled(false)
.setServiceCapabilities(FAKE_SERVICE_CAPABILITIES_1)
+ .setTransferStatus(FAKE_TRANSFER_STATUS_TRANSFERRED_OUT)
+ .setSatelliteEntitlementStatus(FAKE_SATELLITE_ENTITLEMENT_STATUS_DISABLED)
+ .setSatelliteEntitlementPlmns(FAKE_SATELLITE_ENTITLEMENT_PLMNS2)
.build();
static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO2 =
@@ -276,6 +286,9 @@
.setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_NTN_ENABLED)
.setGroupDisabled(false)
.setServiceCapabilities(FAKE_SERVICE_CAPABILITIES_2)
+ .setTransferStatus(FAKE_TRANSFER_STATUS_CONVERTED)
+ .setSatelliteEntitlementStatus(FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED)
+ .setSatelliteEntitlementPlmns(FAKE_SATELLITE_ENTITLEMENT_PLMNS1)
.build();
private SubscriptionDatabaseManager mDatabaseManagerUT;
@@ -436,6 +449,7 @@
doReturn(2).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID2));
when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
when(mFeatureFlags.dataOnlyCellularService()).thenReturn(true);
+ when(mFeatureFlags.supportPsimToEsimConversion()).thenReturn(true);
mDatabaseManagerUT = new SubscriptionDatabaseManager(mContext, Looper.myLooper(),
mFeatureFlags, mSubscriptionDatabaseManagerCallback);
logd("SubscriptionDatabaseManagerTest -Setup!");
@@ -706,7 +720,10 @@
assertThrows(IllegalArgumentException.class,
() -> mDatabaseManagerUT.setNumber(1, FAKE_PHONE_NUMBER2));
- SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ // Prevent the carrier number from overriding the display number
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(
+ new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1)
+ .setNumberFromCarrier("").build());
mDatabaseManagerUT.setNumber(subInfo.getSubscriptionId(), FAKE_PHONE_NUMBER2);
processAllMessages();
@@ -2239,4 +2256,105 @@
mDatabaseManagerUT.getSubscriptionInfoInternal(1).getServiceCapabilities())
.isEqualTo(FAKE_SERVICE_CAPABILITIES_1);
}
+
+ @Test
+ public void testSetTransferStatus() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setTransferStatus(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ FAKE_TRANSFER_STATUS_TRANSFERRED_OUT));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setTransferStatus(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ FAKE_TRANSFER_STATUS_TRANSFERRED_OUT);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setTransferStatus(FAKE_TRANSFER_STATUS_TRANSFERRED_OUT)
+ .build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(1)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ SimInfo.COLUMN_TRANSFER_STATUS)).isEqualTo(FAKE_TRANSFER_STATUS_TRANSFERRED_OUT);
+
+ mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ SimInfo.COLUMN_TRANSFER_STATUS, FAKE_TRANSFER_STATUS_CONVERTED);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId()).getTransferStatus())
+ .isEqualTo(FAKE_TRANSFER_STATUS_CONVERTED);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId()).getTransferStatus())
+ .isNotEqualTo(FAKE_TRANSFER_STATUS_TRANSFERRED_OUT);
+ }
+
+ @Test
+ public void testUpdateSatelliteEntitlementStatus() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setSatelliteEntitlementStatus(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setSatelliteEntitlementStatus(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setSatelliteEntitlementStatus(FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED)
+ .build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS))
+ .isEqualTo(FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED);
+
+ mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_STATUS,
+ FAKE_SATELLITE_ENTITLEMENT_STATUS_DISABLED);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId())
+ .getSatelliteEntitlementStatus())
+ .isEqualTo(FAKE_SATELLITE_ENTITLEMENT_STATUS_DISABLED);
+ }
+
+ @Test
+ public void testUpdateSatelliteEntitlementPlmns() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setSatelliteEntitlementPlmns(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ FAKE_SATELLITE_ENTITLEMENT_PLMNS1));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setSatelliteEntitlementPlmns(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ FAKE_SATELLITE_ENTITLEMENT_PLMNS1);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo)
+ .setSatelliteEntitlementPlmns(FAKE_SATELLITE_ENTITLEMENT_PLMNS1)
+ .build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS))
+ .isEqualTo(FAKE_SATELLITE_ENTITLEMENT_PLMNS1);
+
+ mDatabaseManagerUT.setSubscriptionProperty(FAKE_SUBSCRIPTION_INFO1.getSubscriptionId(),
+ SimInfo.COLUMN_SATELLITE_ENTITLEMENT_PLMNS,
+ FAKE_SATELLITE_ENTITLEMENT_PLMNS2);
+ assertThat(mDatabaseManagerUT.getSubscriptionInfoInternal(
+ FAKE_SUBSCRIPTION_INFO1.getSubscriptionId())
+ .getSatelliteEntitlementPlmns())
+ .isEqualTo(FAKE_SATELLITE_ENTITLEMENT_PLMNS2);
+ }
}
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 d9addd1..6e2c5bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
@@ -115,6 +115,13 @@
.setOnlyNonTerrestrialNetwork(1)
.setServiceCapabilities(
SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK)
+ .setTransferStatus(1)
+ .setSatelliteEntitlementStatus(
+ SubscriptionDatabaseManagerTest
+ .FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED)
+ .setSatelliteEntitlementPlmns(
+ SubscriptionDatabaseManagerTest
+ .FAKE_SATELLITE_ENTITLEMENT_PLMNS1)
.build();
private final SubscriptionInfoInternal mSubInfoNull =
@@ -141,6 +148,7 @@
.setRcsConfig(new byte[0])
.setAllowedNetworkTypesForReasons("")
.setDeviceToDeviceStatusSharingContacts("")
+ .setTransferStatus(1)
.build();
@Rule
@@ -237,6 +245,13 @@
assertThat(mSubInfo.getOnlyNonTerrestrialNetwork()).isEqualTo(1);
assertThat(mSubInfo.getServiceCapabilities()).isEqualTo(
SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK);
+ assertThat(mSubInfo.getTransferStatus()).isEqualTo(1);
+ assertThat(mSubInfo.getSatelliteEntitlementStatus())
+ .isEqualTo(SubscriptionDatabaseManagerTest
+ .FAKE_SATELLITE_ENTITLEMENT_STATUS_ENABLED);
+ assertThat(mSubInfo.getSatelliteEntitlementPlmns())
+ .isEqualTo(SubscriptionDatabaseManagerTest
+ .FAKE_SATELLITE_ENTITLEMENT_PLMNS1);
}
@Test
@@ -303,6 +318,7 @@
assertThat(subInfo.isOnlyNonTerrestrialNetwork()).isTrue();
assertThat(subInfo.getServiceCapabilities()).isEqualTo(
Set.of(SubscriptionManager.SERVICE_CAPABILITY_DATA));
+ assertThat(mSubInfo.getTransferStatus()).isEqualTo(1);
}
@Test
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 0aeaad3..defa730 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -353,6 +353,11 @@
assertThat(subInfo.getSimSlotIndex()).isEqualTo(0);
assertThat(subInfo.getSubscriptionType()).isEqualTo(
SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+
+ // Invalid slot index should trigger IllegalArgumentException
+ assertThrows(IllegalArgumentException.class,
+ () -> mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
+ 2, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM));
}
@Test
@@ -414,6 +419,23 @@
}
@Test
+ public void testSetAdminOwned() {
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+ mSubscriptionManagerServiceUT.addSubInfo(FAKE_ICCID1, FAKE_CARRIER_NAME1,
+ 0, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ processAllMessages();
+ String groupOwner = "test";
+
+ mSubscriptionManagerServiceUT.setGroupOwner(1, groupOwner);
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getGroupOwner()).isEqualTo(groupOwner);
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ }
+
+ @Test
@DisableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
public void testSetPhoneNumber() {
doReturn(false).when(mFlags).enforceTelephonyFeatureMapping();
@@ -1589,7 +1611,9 @@
@Test
public void testSetDisplayNumber() {
- insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+ insertSubscription(new SubscriptionInfoInternal.Builder(FAKE_SUBSCRIPTION_INFO1)
+ .setNumberFromCarrier("")
+ .build());
// Should fail without MODIFY_PHONE_STATE
assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
@@ -2146,6 +2170,39 @@
}
@Test
+ public void testGetNumberWithCarrierNumber() {
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
+
+ // Should fail without MODIFY_PHONE_STATE
+ assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
+ .setDisplayNumber(FAKE_PHONE_NUMBER2, 1));
+
+ mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ mSubscriptionManagerServiceUT.setDisplayNumber(FAKE_PHONE_NUMBER2, 1);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getNumber()).isEqualTo(FAKE_PHONE_NUMBER1);
+ Mockito.clearInvocations(mMockedSubscriptionManagerServiceCallback);
+
+ setCarrierPrivilegesForSubId(true, 1);
+ mSubscriptionManagerServiceUT.setPhoneNumber(1,
+ SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, "",
+ CALLING_PACKAGE, CALLING_FEATURE);
+ processAllMessages();
+ verify(mMockedSubscriptionManagerServiceCallback).onSubscriptionChanged(eq(1));
+ setCarrierPrivilegesForSubId(false, 1);
+
+ subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1);
+ assertThat(subInfo).isNotNull();
+ assertThat(subInfo.getNumber()).isEqualTo(FAKE_PHONE_NUMBER2);
+ }
+
+ @Test
public void testGetNonAccessibleFields() throws Exception {
insertSubscription(FAKE_SUBSCRIPTION_INFO1);