Merge "Dump state of CarrierServiceBindHelper in UiccController.dump" into main
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 3beb016..6334803 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -1,10 +1,13 @@
package: "com.android.internal.telephony.flags"
flag {
- name: "auto_switch_allow_roaming"
+ name: "auto_data_switch_allow_roaming"
namespace: "telephony"
description: "Allow using roaming network as target if user allows it from settings."
- bug: "306488039"
+ bug: "287132491"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -117,4 +120,11 @@
namespace: "telephony"
description: "This flag is for internal implementation to handle reconnect request from QNS in telephony FWK."
bug: "319520561"
+}
+
+flag {
+ name: "dsrs_diagnostics_enabled"
+ namespace: "telephony"
+ description: "Enable DSRS diagnostics."
+ bug: "319601607"
}
\ No newline at end of file
diff --git a/flags/ims.aconfig b/flags/ims.aconfig
index d09259e..b2cc904 100644
--- a/flags/ims.aconfig
+++ b/flags/ims.aconfig
@@ -62,3 +62,10 @@
description: "This flag updates roaming state to set wfc mode"
bug:"317298331"
}
+
+flag {
+ name: "enable_sip_subscribe_retry"
+ namespace: "telephony"
+ description: "This flag controls whether framework supports SIP subscribe retry or not"
+ bug:"297023230"
+}
diff --git a/flags/telephony.aconfig b/flags/telephony.aconfig
index d59b249..9ef70b1 100644
--- a/flags/telephony.aconfig
+++ b/flags/telephony.aconfig
@@ -26,4 +26,18 @@
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
+}
+
+flag {
+ name: "minimal_telephony_cdm_check"
+ namespace: "telephony"
+ description: "This flag disables Calling/Data/Messaging features if their respective feature is missing"
+ bug: "310710841"
+}
+
+flag {
+ name: "minimal_telephony_managers_conditional_on_features"
+ namespace: "telephony"
+ description: "This flag enables checking for telephony features before initializing corresponding managers"
+ bug: "310710841"
+}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 5517bc6..9113514 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.AsyncResult;
import android.os.Build;
import android.os.Bundle;
@@ -159,6 +160,12 @@
public GsmCdmaCallTracker(GsmCdmaPhone phone, FeatureFlags featureFlags) {
super(featureFlags);
+ if (mFeatureFlags.minimalTelephonyCdmCheck()
+ && !phone.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ throw new UnsupportedOperationException("GsmCdmaCallTracker requires calling");
+ }
+
this.mPhone = phone;
mCi = phone.mCi;
mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
@@ -1492,7 +1499,7 @@
switch (msg.what) {
case EVENT_POLL_CALLS_RESULT:
- Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
+ if (DBG_POLL) Rlog.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received");
if (msg == mLastRelevantPoll) {
if (DBG_POLL) log(
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index aca759b..de7ebd6 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -42,6 +42,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.database.SQLException;
import android.hardware.radio.modem.ImeiInfo;
import android.net.Uri;
@@ -370,9 +371,11 @@
SignalStrengthController.class.getName()).makeSignalStrengthController(this);
mSST = mTelephonyComponentFactory.inject(ServiceStateTracker.class.getName())
.makeServiceStateTracker(this, this.mCi, featureFlags);
- mEmergencyNumberTracker = mTelephonyComponentFactory
- .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
- this, this.mCi);
+ if (hasCalling()) {
+ mEmergencyNumberTracker = mTelephonyComponentFactory
+ .inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
+ this, this.mCi, mFeatureFlags);
+ }
mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName())
.makeDeviceStateMonitor(this, mFeatureFlags);
@@ -412,9 +415,11 @@
mCallWaitingController = new CallWaitingController(this);
- loadTtyMode();
+ if (hasCalling()) {
+ loadTtyMode();
- CallManager.getInstance().registerPhone(this);
+ CallManager.getInstance().registerPhone(this);
+ }
mSubscriptionsChangedListener =
new SubscriptionManager.OnSubscriptionsChangedListener() {
@@ -464,13 +469,21 @@
}
};
+ private boolean hasCalling() {
+ if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+ return mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING);
+ }
+
private void initOnce(CommandsInterface ci) {
if (ci instanceof SimulatedRadioControl) {
mSimulatedRadioControl = (SimulatedRadioControl) ci;
}
- mCT = mTelephonyComponentFactory.inject(GsmCdmaCallTracker.class.getName())
- .makeGsmCdmaCallTracker(this, mFeatureFlags);
+ if (hasCalling()) {
+ mCT = mTelephonyComponentFactory.inject(GsmCdmaCallTracker.class.getName())
+ .makeGsmCdmaCallTracker(this, mFeatureFlags);
+ }
mIccPhoneBookIntManager = mTelephonyComponentFactory
.inject(IccPhoneBookInterfaceManager.class.getName())
.makeIccPhoneBookInterfaceManager(this);
@@ -558,7 +571,7 @@
mNullCipherNotifier =
mTelephonyComponentFactory
.inject(NullCipherNotifier.class.getName())
- .makeNullCipherNotifier();
+ .makeNullCipherNotifier(mSafetySource);
mCi.registerForSecurityAlgorithmUpdates(
this, EVENT_SECURITY_ALGORITHM_UPDATE, null);
}
@@ -693,7 +706,7 @@
unregisterForIccRecordEvents();
registerForIccRecordEvents();
- mCT.updatePhoneType();
+ if (mCT != null) mCT.updatePhoneType();
int radioState = mCi.getRadioState();
if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
@@ -753,6 +766,8 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public PhoneConstants.State getState() {
+ if (!hasCalling()) return PhoneConstants.State.IDLE;
+
if (mImsPhone != null) {
PhoneConstants.State imsState = mImsPhone.getState();
if (imsState != PhoneConstants.State.IDLE) {
@@ -837,6 +852,7 @@
@Override
public boolean isDataSuspended() {
+ if (mCT == null) return false;
return mCT.mState != PhoneConstants.State.IDLE && !mSST.isConcurrentVoiceAndDataAllowed();
}
@@ -884,7 +900,7 @@
@Override
public boolean isInEmergencyCall() {
- if (isPhoneTypeGsm()) {
+ if (!hasCalling() || isPhoneTypeGsm()) {
return false;
} else {
return mCT.isInEmergencyCall();
@@ -893,7 +909,7 @@
@Override
protected void setIsInEmergencyCall() {
- if (!isPhoneTypeGsm()) {
+ if (!hasCalling() && !isPhoneTypeGsm()) {
mCT.setIsInEmergencyCall();
}
}
@@ -985,6 +1001,7 @@
@Override
public void acceptCall(int videoState) throws CallStateException {
+ if (!hasCalling()) throw new CallStateException();
Phone imsPhone = mImsPhone;
if ( imsPhone != null && imsPhone.getRingingCall().isRinging() ) {
imsPhone.acceptCall(videoState);
@@ -995,6 +1012,7 @@
@Override
public void rejectCall() throws CallStateException {
+ if (!hasCalling()) throw new CallStateException();
mCT.rejectCall();
}
@@ -1025,6 +1043,7 @@
@Override
public boolean canConference() {
+ if (!hasCalling()) return false;
if (mImsPhone != null && mImsPhone.canConference()) {
return true;
}
@@ -1075,12 +1094,13 @@
@Override
public void clearDisconnected() {
+ if (!hasCalling()) return;
mCT.clearDisconnected();
}
@Override
public boolean canTransfer() {
- if (isPhoneTypeGsm()) {
+ if (hasCalling() && isPhoneTypeGsm()) {
return mCT.canTransfer();
} else {
loge("canTransfer: not possible in CDMA");
@@ -1090,7 +1110,7 @@
@Override
public void explicitCallTransfer() {
- if (isPhoneTypeGsm()) {
+ if (hasCalling() && isPhoneTypeGsm()) {
mCT.explicitCallTransfer();
} else {
loge("explicitCallTransfer: not possible in CDMA");
@@ -1104,11 +1124,13 @@
@Override
public GsmCdmaCall getBackgroundCall() {
+ if (!hasCalling()) return null;
return mCT.mBackgroundCall;
}
@Override
public Call getRingingCall() {
+ if (!hasCalling()) return null;
Phone imsPhone = mImsPhone;
// It returns the ringing call of ImsPhone if the ringing call of GSMPhone isn't ringing.
// In CallManager.registerPhone(), it always registers ringing call of ImsPhone, because
@@ -1184,7 +1206,7 @@
private boolean handleCallDeflectionIncallSupplementaryService(
String dialString) {
- if (dialString.length() > 1) {
+ if (!hasCalling() || dialString.length() > 1) {
return false;
}
@@ -1209,7 +1231,7 @@
private boolean handleCallWaitingIncallSupplementaryService(String dialString) {
int len = dialString.length();
- if (len > 2) {
+ if (!hasCalling() || len > 2) {
return false;
}
@@ -1429,6 +1451,9 @@
@Override
public Connection dial(String dialString, @NonNull DialArgs dialArgs,
Consumer<Phone> chosenPhoneConsumer) throws CallStateException {
+ if (!hasCalling()) {
+ throw new CallStateException("Calling feature is not supported!");
+ }
if (!isPhoneTypeGsm() && dialArgs.uusInfo != null) {
throw new CallStateException("Sending UUS information NOT supported in CDMA!");
}
@@ -2148,7 +2173,9 @@
@Override
public int getEmergencyNumberDbVersion() {
- return getEmergencyNumberTracker().getEmergencyNumberDbVersion();
+ EmergencyNumberTracker tracker = getEmergencyNumberTracker();
+ if (tracker == null) return -1;
+ return tracker.getEmergencyNumberDbVersion();
}
@Override
@@ -3134,6 +3161,8 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void syncClirSetting() {
+ if (!hasCalling()) return;
+
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
migrateClirSettingIfNeeded(sp);
@@ -3339,8 +3368,10 @@
if (b != null) {
updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(b);
updateCdmaRoamingSettingsAfterCarrierConfigChanged(b);
- updateNrSettingsAfterCarrierConfigChanged(b);
- updateVoNrSettings(b);
+ if (hasCalling()) {
+ updateNrSettingsAfterCarrierConfigChanged(b);
+ updateVoNrSettings(b);
+ }
updateSsOverCdmaSupported(b);
updateCarrierN1ModeSupported(b);
} else {
@@ -3738,7 +3769,7 @@
&& mNullCipherNotifier != null) {
ar = (AsyncResult) msg.obj;
SecurityAlgorithmUpdate update = (SecurityAlgorithmUpdate) ar.result;
- mNullCipherNotifier.onSecurityAlgorithmUpdate(getPhoneId(), update);
+ mNullCipherNotifier.onSecurityAlgorithmUpdate(mContext, getSubId(), update);
}
break;
@@ -4912,6 +4943,7 @@
* Handler of RIL Voice Radio Technology changed event.
*/
private void onVoiceRegStateOrRatChanged(int vrs, int vrat) {
+ if (!hasCalling()) return;
logd("onVoiceRegStateOrRatChanged");
mCT.dispatchCsCallRadioTech(getCsCallRadioTech(vrs, vrat));
}
@@ -5113,6 +5145,8 @@
* Load the current TTY mode in GsmCdmaPhone based on Telecom and UI settings.
*/
private void loadTtyMode() {
+ if (!hasCalling()) return;
+
int ttyMode = TelecomManager.TTY_MODE_OFF;
TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
if (telecomManager != null) {
@@ -5392,9 +5426,9 @@
// enable/disable API.
if (mFeatureFlags.enableModemCipherTransparencyUnsolEvents()) {
if (prefEnabled) {
- mNullCipherNotifier.enable();
+ mNullCipherNotifier.enable(mContext);
} else {
- mNullCipherNotifier.disable();
+ mNullCipherNotifier.disable(mContext);
}
} else {
logi(
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 4e4d55d..4146c24 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -203,12 +203,12 @@
mTrackers.remove(token);
mPhone.notifySmsSent(tracker.mDestAddress);
mSmsDispatchersController.notifySmsSentToEmergencyStateTracker(
- tracker.mDestAddress, tracker.mMessageId);
+ tracker.mDestAddress, tracker.mMessageId, true);
break;
case ImsSmsImplBase.SEND_STATUS_ERROR:
tracker.onFailed(mContext, reason, networkReasonCode);
mTrackers.remove(token);
- notifySmsSentFailedToEmergencyStateTracker(tracker);
+ notifySmsSentFailedToEmergencyStateTracker(tracker, true);
break;
case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
int maxRetryCountOverIms = getMaxRetryCountOverIms();
@@ -227,7 +227,7 @@
} else {
tracker.onFailed(mContext, reason, networkReasonCode);
mTrackers.remove(token);
- notifySmsSentFailedToEmergencyStateTracker(tracker);
+ notifySmsSentFailedToEmergencyStateTracker(tracker, true);
}
break;
case ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK:
@@ -304,6 +304,11 @@
switch (result) {
case Intents.RESULT_SMS_HANDLED:
mappedResult = ImsSmsImplBase.DELIVER_STATUS_OK;
+ if (message != null) {
+ mSmsDispatchersController
+ .notifySmsReceivedViaImsToEmergencyStateTracker(
+ message.getOriginatingAddress());
+ }
break;
case Intents.RESULT_SMS_OUT_OF_MEMORY:
mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_NO_MEMORY;
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 8488ab0..aaeba23 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -35,6 +35,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
@@ -52,6 +53,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.util.ArrayUtils;
@@ -122,6 +124,7 @@
protected final Context mContext;
private final SubscriptionManagerService mSubscriptionManagerService;
+ private final @NonNull FeatureFlags mFeatureFlags;
// Keep a record of active primary (non-opportunistic) subscription list.
@NonNull private List<Integer> mPrimarySubList = new ArrayList<>();
@@ -201,10 +204,11 @@
/**
* Init instance of MultiSimSettingController.
*/
- public static MultiSimSettingController init(Context context) {
+ public static MultiSimSettingController init(Context context,
+ @NonNull FeatureFlags featureFlags) {
synchronized (MultiSimSettingController.class) {
if (sInstance == null) {
- sInstance = new MultiSimSettingController(context);
+ sInstance = new MultiSimSettingController(context, featureFlags);
} else {
Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
}
@@ -213,9 +217,10 @@
}
@VisibleForTesting
- public MultiSimSettingController(Context context) {
+ public MultiSimSettingController(Context context, @NonNull FeatureFlags featureFlags) {
mContext = context;
mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+ mFeatureFlags = featureFlags;
// Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change.
TelephonyManager telephonyManager = ((TelephonyManager) mContext.getSystemService(
@@ -239,6 +244,24 @@
onCarrierConfigChanged(slotIndex, subId));
}
+ private boolean hasCalling() {
+ if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+ return mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING);
+ }
+
+ private boolean hasData() {
+ if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+ return mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_DATA);
+ }
+
+ private boolean hasMessaging() {
+ if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+ return mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_MESSAGING);
+ }
+
/**
* Notify MOBILE_DATA of a subscription is changed.
*/
@@ -606,35 +629,43 @@
|| mActiveModemCount == 1)) {
int subId = mPrimarySubList.get(0);
if (DBG) log("updateDefaultValues: to only primary sub " + subId);
- mSubscriptionManagerService.setDefaultDataSubId(subId);
- mSubscriptionManagerService.setDefaultVoiceSubId(subId);
- mSubscriptionManagerService.setDefaultSmsSubId(subId);
+ if (hasData()) mSubscriptionManagerService.setDefaultDataSubId(subId);
+ if (hasCalling()) mSubscriptionManagerService.setDefaultVoiceSubId(subId);
+ if (hasMessaging()) mSubscriptionManagerService.setDefaultSmsSubId(subId);
sendDefaultSubConfirmedNotification(subId);
return;
}
if (DBG) log("updateDefaultValues: records: " + mPrimarySubList);
- boolean dataSelected, voiceSelected, smsSelected;
+ boolean dataSelected = false;
+ boolean voiceSelected = false;
+ boolean smsSelected = false;
- // Update default data subscription.
- if (DBG) log("updateDefaultValues: Update default data subscription");
- dataSelected = updateDefaultValue(mPrimarySubList,
- mSubscriptionManagerService.getDefaultDataSubId(),
- mSubscriptionManagerService::setDefaultDataSubId);
+ if (hasData()) {
+ // Update default data subscription.
+ if (DBG) log("updateDefaultValues: Update default data subscription");
+ dataSelected = updateDefaultValue(mPrimarySubList,
+ mSubscriptionManagerService.getDefaultDataSubId(),
+ mSubscriptionManagerService::setDefaultDataSubId);
+ }
- // Update default voice subscription.
- if (DBG) log("updateDefaultValues: Update default voice subscription");
- voiceSelected = updateDefaultValue(mPrimarySubList,
- mSubscriptionManagerService.getDefaultVoiceSubId(),
- mSubscriptionManagerService::setDefaultVoiceSubId);
+ if (hasCalling()) {
+ // Update default voice subscription.
+ if (DBG) log("updateDefaultValues: Update default voice subscription");
+ voiceSelected = updateDefaultValue(mPrimarySubList,
+ mSubscriptionManagerService.getDefaultVoiceSubId(),
+ mSubscriptionManagerService::setDefaultVoiceSubId);
+ }
- // Update default sms subscription.
- if (DBG) log("updateDefaultValues: Update default sms subscription");
- smsSelected = updateDefaultValue(mPrimarySubList,
- mSubscriptionManagerService.getDefaultSmsSubId(),
- mSubscriptionManagerService::setDefaultSmsSubId,
- mIsAskEverytimeSupportedForSms);
+ if (hasMessaging()) {
+ // Update default sms subscription.
+ if (DBG) log("updateDefaultValues: Update default sms subscription");
+ smsSelected = updateDefaultValue(mPrimarySubList,
+ mSubscriptionManagerService.getDefaultSmsSubId(),
+ mSubscriptionManagerService::setDefaultSmsSubId,
+ mIsAskEverytimeSupportedForSms);
+ }
boolean autoFallbackEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_voice_data_sms_auto_fallback);
@@ -1023,11 +1054,11 @@
int autoDefaultSubId = primarySubList.get(0);
- if ((primarySubList.size() == 1) && !smsSelected) {
+ if (hasMessaging() && (primarySubList.size() == 1) && !smsSelected) {
mSubscriptionManagerService.setDefaultSmsSubId(autoDefaultSubId);
}
- if ((primarySubList.size() == 1) && !voiceSelected) {
+ if (hasCalling() && (primarySubList.size() == 1) && !voiceSelected) {
mSubscriptionManagerService.setDefaultVoiceSubId(autoDefaultSubId);
}
@@ -1036,13 +1067,15 @@
log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId
+ " next active subId " + autoDefaultSubId);
- // If earlier user selected DDS is now available, set that as DDS subId.
- if (primarySubList.contains(userPrefDataSubId)
- && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId)
- && (defaultDataSubId != userPrefDataSubId)) {
- mSubscriptionManagerService.setDefaultDataSubId(userPrefDataSubId);
- } else if (!dataSelected) {
- mSubscriptionManagerService.setDefaultDataSubId(autoDefaultSubId);
+ if (hasData()) {
+ // If earlier user selected DDS is now available, set that as DDS subId.
+ if (primarySubList.contains(userPrefDataSubId)
+ && SubscriptionManager.isValidSubscriptionId(userPrefDataSubId)
+ && (defaultDataSubId != userPrefDataSubId)) {
+ mSubscriptionManagerService.setDefaultDataSubId(userPrefDataSubId);
+ } else if (!dataSelected) {
+ mSubscriptionManagerService.setDefaultDataSubId(autoDefaultSubId);
+ }
}
if (DBG) {
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index b9ad388..67ca1e1 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -26,6 +26,7 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
import android.telephony.CarrierConfigManager;
@@ -194,6 +195,7 @@
private boolean mIsPhysicalChannelConfigOn;
private boolean mIsPrimaryTimerActive;
private boolean mIsSecondaryTimerActive;
+ private long mSecondaryTimerExpireTimestamp;
private boolean mIsTimerResetEnabledForLegacyStateRrcIdle;
/** Carrier config to reset timers when mccmnc changes */
private boolean mIsTimerResetEnabledOnPlmnChanges;
@@ -220,6 +222,7 @@
// Cached copies below to prevent race conditions
@NonNull private ServiceState mServiceState;
+ /** Used to track link status to be DORMANT or ACTIVE */
@Nullable private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
// Ratchet physical channel config fields to prevent 5G/5G+ flickering
@@ -378,6 +381,9 @@
createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
updatePhysicalChannelConfigs(
mPhone.getServiceStateTracker().getPhysicalChannelConfigList());
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
+ }
}
private void createTimerRules(String icons, String timers, String secondaryTimers) {
@@ -663,6 +669,7 @@
case EVENT_SECONDARY_TIMER_EXPIRED:
if (DBG) log("Secondary timer expired for state: " + mSecondaryTimerState);
mIsSecondaryTimerActive = false;
+ mSecondaryTimerExpireTimestamp = 0;
mSecondaryTimerState = "";
updateTimers();
mLastShownNrDueToAdvancedBand = false;
@@ -1035,11 +1042,15 @@
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
- // Check NR advanced in case NR advanced bands were added
- if (isNrAdvanced()) {
- transitionTo(mNrConnectedAdvancedState);
- } else if (isPhysicalLinkActive()) {
- transitionWithTimerTo(mNrConnectedState);
+ if (isPhysicalLinkActive()) {
+ if (isNrAdvanced()) {
+ transitionTo(mNrConnectedAdvancedState);
+ } else {
+ transitionWithTimerTo(mNrConnectedState);
+ }
+ } else {
+ // Update in case the override network type changed
+ updateOverrideNetworkType();
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
@@ -1113,11 +1124,10 @@
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
- // Check NR advanced in case NR advanced bands were added
- if (isNrAdvanced()) {
- transitionTo(mNrConnectedAdvancedState);
- } else if (!isPhysicalLinkActive() && mFeatureFlags.supportNrSaRrcIdle()) {
+ if (!isPhysicalLinkActive() && mFeatureFlags.supportNrSaRrcIdle()) {
transitionWithTimerTo(mNrIdleState);
+ } else if (isNrAdvanced()) {
+ transitionTo(mNrConnectedAdvancedState);
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
@@ -1203,11 +1213,10 @@
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
- // Check NR advanced in case NR advanced bands were removed
- if (!isNrAdvanced()) {
- transitionWithTimerTo(isPhysicalLinkActive()
- || !mFeatureFlags.supportNrSaRrcIdle()
- ? mNrConnectedState : mNrIdleState);
+ if (!isPhysicalLinkActive() && mFeatureFlags.supportNrSaRrcIdle()) {
+ transitionWithTimerTo(mNrIdleState);
+ } else if (!isNrAdvanced()) {
+ transitionWithTimerTo(mNrConnectedState);
}
break;
case EVENT_PHYSICAL_LINK_STATUS_CHANGED:
@@ -1246,6 +1255,8 @@
private void updatePhysicalChannelConfigs(List<PhysicalChannelConfig> physicalChannelConfigs) {
boolean isPccListEmpty = physicalChannelConfigs == null || physicalChannelConfigs.isEmpty();
if (isPccListEmpty && isUsingPhysicalChannelConfigForRrcDetection()) {
+ // Clear mPrimaryCellChangedWhileIdle to allow later potential one-off PCI change.
+ // Update link status to be DORMANT, but keep ratcheted bands.
log("Physical channel configs updated: not updating PCC fields for empty PCC list "
+ "indicating RRC idle.");
mPrimaryCellChangedWhileIdle = false;
@@ -1299,12 +1310,13 @@
} else {
if (mFeatureFlags.supportNrSaRrcIdle() && mDoesPccListIndicateIdle
&& isUsingPhysicalChannelConfigForRrcDetection()
- && !mPrimaryCellChangedWhileIdle && isTimerActiveForRrcIdle()
+ && !mPrimaryCellChangedWhileIdle
&& !isNrAdvancedForPccFields(nrBandwidths, nrBands)) {
- log("Allow primary cell change during RRC idle timer without changing state: "
+ log("Allow primary cell change once during RRC idle without changing state: "
+ mLastAnchorNrCellId + " -> " + anchorNrCellId);
mPrimaryCellChangedWhileIdle = true;
mLastAnchorNrCellId = anchorNrCellId;
+ reduceSecondaryTimerIfNeeded();
return;
}
if (mRatchetPccFieldsForSameAnchorNrCell) {
@@ -1325,17 +1337,45 @@
}
}
+ /**
+ * Called when PCI change, specifically during idle state.
+ */
+ private void reduceSecondaryTimerIfNeeded() {
+ if (!mIsSecondaryTimerActive || mNrAdvancedBandsSecondaryTimer <= 0) return;
+ // Secondary timer is active, so we must have a valid secondary rule right now.
+ OverrideTimerRule secondaryRule = mOverrideTimerRules.get(mPrimaryTimerState);
+ if (secondaryRule != null) {
+ int secondaryDuration = secondaryRule.getSecondaryTimer(mSecondaryTimerState);
+ long durationMillis = secondaryDuration * 1000L;
+ if ((mSecondaryTimerExpireTimestamp - SystemClock.uptimeMillis()) > durationMillis) {
+ if (DBG) log("Due to PCI change, reduce the secondary timer to " + durationMillis);
+ removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
+ sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, mSecondaryTimerState,
+ durationMillis);
+ }
+ } else {
+ loge("!! Secondary timer is active, but found no rule for " + mPrimaryTimerState);
+ }
+ }
+
private void transitionWithTimerTo(IState destState) {
String destName = destState.getName();
- if (DBG) log("Transition with primary timer from " + mPreviousState + " to " + destName);
- OverrideTimerRule rule = mOverrideTimerRules.get(mPreviousState);
- if (!mIsDeviceIdleMode && rule != null && rule.getTimer(destName) > 0) {
- int duration = rule.getTimer(destName);
- if (DBG) log(duration + "s primary timer started for state: " + mPreviousState);
- mPrimaryTimerState = mPreviousState;
- mPreviousState = getCurrentState().getName();
- mIsPrimaryTimerActive = true;
- sendMessageDelayed(EVENT_PRIMARY_TIMER_EXPIRED, destState, duration * 1000L);
+ if (mIsPrimaryTimerActive) {
+ log("Transition without timer from " + getCurrentState().getName() + " to " + destName
+ + " due to existing " + mPrimaryTimerState + " primary timer.");
+ } else {
+ if (DBG) {
+ log("Transition with primary timer from " + mPreviousState + " to " + destName);
+ }
+ OverrideTimerRule rule = mOverrideTimerRules.get(mPreviousState);
+ if (!mIsDeviceIdleMode && rule != null && rule.getTimer(destName) > 0) {
+ int duration = rule.getTimer(destName);
+ if (DBG) log(duration + "s primary timer started for state: " + mPreviousState);
+ mPrimaryTimerState = mPreviousState;
+ mPreviousState = getCurrentState().getName();
+ mIsPrimaryTimerActive = true;
+ sendMessageDelayed(EVENT_PRIMARY_TIMER_EXPIRED, destState, duration * 1000L);
+ }
}
transitionTo(destState);
}
@@ -1357,7 +1397,9 @@
mSecondaryTimerState = currentName;
mPreviousState = currentName;
mIsSecondaryTimerActive = true;
- sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, destState, duration * 1000L);
+ long durationMillis = duration * 1000L;
+ mSecondaryTimerExpireTimestamp = SystemClock.uptimeMillis() + durationMillis;
+ sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, destState, durationMillis);
}
mIsPrimaryTimerActive = false;
transitionTo(getCurrentState());
@@ -1367,14 +1409,12 @@
int dataRat = getDataNetworkType();
IState transitionState;
if (dataRat == TelephonyManager.NETWORK_TYPE_NR || (isLte(dataRat) && isNrConnected())) {
- if (isNrAdvanced()) {
+ if (!isPhysicalLinkActive() && mFeatureFlags.supportNrSaRrcIdle()) {
+ transitionState = mNrIdleState;
+ } else if (isNrAdvanced()) {
transitionState = mNrConnectedAdvancedState;
} else {
- if (isPhysicalLinkActive() || !mFeatureFlags.supportNrSaRrcIdle()) {
- transitionState = mNrConnectedState;
- } else {
- transitionState = mNrIdleState;
- }
+ transitionState = mNrConnectedState;
}
} else if (isLte(dataRat) && isNrNotRestricted()) {
if (isPhysicalLinkActive()) {
@@ -1403,13 +1443,11 @@
String currentState = getCurrentState().getName();
- if (mIsPrimaryTimerActive && getOverrideNetworkType() == getCurrentOverrideNetworkType()
- && getDataNetworkType()
- == mDisplayInfoController.getTelephonyDisplayInfo().getNetworkType()) {
- // remove primary timer if device goes back to the original icon
+ if (mIsPrimaryTimerActive && mPrimaryTimerState.equals(currentState)) {
+ // remove primary timer if device goes back to the original state
if (DBG) {
- log("Remove primary timer since icon of primary state and current icon equal: "
- + mPrimaryTimerState);
+ log("Remove primary timer since primary timer state ("
+ + mPrimaryTimerState + ") was reestablished.");
}
removeMessages(EVENT_PRIMARY_TIMER_EXPIRED);
mIsPrimaryTimerActive = false;
@@ -1426,6 +1464,7 @@
}
removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
mIsSecondaryTimerActive = false;
+ mSecondaryTimerExpireTimestamp = 0;
mSecondaryTimerState = "";
transitionToCurrentState();
return;
@@ -1435,10 +1474,11 @@
if (currentState.equals(STATE_CONNECTED_NR_ADVANCED)) {
if (DBG) log("Reset timers since state is NR_ADVANCED.");
resetAllTimers();
- } else if (currentState.equals(STATE_CONNECTED)
+ } else if ((currentState.equals(STATE_CONNECTED)
+ || currentState.equals(STATE_CONNECTED_RRC_IDLE))
&& !mPrimaryTimerState.equals(STATE_CONNECTED_NR_ADVANCED)
&& !mSecondaryTimerState.equals(STATE_CONNECTED_NR_ADVANCED)) {
- if (DBG) log("Reset non-NR_ADVANCED timers since state is NR_CONNECTED");
+ if (DBG) log("Reset non-NR advanced timers since state is NR connected/idle");
resetAllTimers();
} else {
int rat = getDataNetworkType();
@@ -1455,24 +1495,13 @@
removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
mIsPrimaryTimerActive = false;
mIsSecondaryTimerActive = false;
+ mSecondaryTimerExpireTimestamp = 0;
mPrimaryTimerState = "";
mSecondaryTimerState = "";
mLastShownNrDueToAdvancedBand = false;
}
- private boolean isTimerActiveForRrcIdle() {
- if (mIsPrimaryTimerActive) {
- return mPrimaryTimerState.equals(STATE_CONNECTED_RRC_IDLE)
- || mPrimaryTimerState.equals(STATE_NOT_RESTRICTED_RRC_IDLE);
- } else if (mIsSecondaryTimerActive) {
- return mSecondaryTimerState.equals(STATE_CONNECTED_RRC_IDLE)
- || mSecondaryTimerState.equals(STATE_NOT_RESTRICTED_RRC_IDLE);
- } else {
- return false;
- }
- }
-
/**
* Private class defining timer rules between states to prevent flickering. These rules are
* created in {@link #parseCarrierConfigs()} based on various carrier configs.
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index c5bc428..803fb19 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -212,7 +212,7 @@
Looper.myLooper(), featureFlags);
TelephonyComponentFactory.getInstance().inject(MultiSimSettingController.class.
- getName()).initMultiSimSettingController(context);
+ getName()).initMultiSimSettingController(context, featureFlags);
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_EUICC)) {
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 5177adb..8b3be1e 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -31,6 +31,7 @@
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.radio.V1_0.IRadio;
import android.hardware.radio.V1_0.RadioError;
import android.hardware.radio.V1_0.RadioIndicationType;
@@ -271,6 +272,13 @@
static final String[] HIDL_SERVICE_NAME = {"slot1", "slot2", "slot3"};
+ private static final Map<String, Integer> FEATURES_TO_SERVICES = Map.ofEntries(
+ Map.entry(PackageManager.FEATURE_TELEPHONY_CALLING, HAL_SERVICE_VOICE),
+ Map.entry(PackageManager.FEATURE_TELEPHONY_DATA, HAL_SERVICE_DATA),
+ Map.entry(PackageManager.FEATURE_TELEPHONY_MESSAGING, HAL_SERVICE_MESSAGING),
+ Map.entry(PackageManager.FEATURE_TELEPHONY_IMS, HAL_SERVICE_IMS)
+ );
+
public static List<TelephonyHistogram> getTelephonyRILTimingHistograms() {
List<TelephonyHistogram> list;
synchronized (sRilTimeHistograms) {
@@ -621,6 +629,7 @@
if (mDisabledRadioServices.get(HAL_SERVICE_RADIO).contains(mPhoneId)) {
riljLoge("getRadioProxy: mRadioProxy for " + HIDL_SERVICE_NAME[mPhoneId]
+ " is disabled");
+ return null;
} else {
try {
mRadioProxy = android.hardware.radio.V1_6.IRadio.getService(
@@ -662,6 +671,7 @@
mDisabledRadioServices.get(HAL_SERVICE_RADIO).add(mPhoneId);
riljLoge("getRadioProxy: set mRadioProxy for "
+ HIDL_SERVICE_NAME[mPhoneId] + " as disabled");
+ return null;
}
}
} catch (RemoteException e) {
@@ -718,6 +728,9 @@
public synchronized RadioServiceProxy getRadioServiceProxy(int service) {
if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return mServiceProxies.get(service);
if ((service >= HAL_SERVICE_IMS) && !isRadioServiceSupported(service)) {
+ riljLogw("getRadioServiceProxy: " + serviceToString(service) + " for "
+ + HIDL_SERVICE_NAME[mPhoneId] + " is not supported\n"
+ + android.util.Log.getStackTraceString(new RuntimeException()));
return mServiceProxies.get(service);
}
if (!mIsCellularSupported) {
@@ -733,7 +746,9 @@
try {
if (mMockModem == null && mDisabledRadioServices.get(service).contains(mPhoneId)) {
riljLoge("getRadioServiceProxy: " + serviceToString(service) + " for "
- + HIDL_SERVICE_NAME[mPhoneId] + " is disabled");
+ + HIDL_SERVICE_NAME[mPhoneId] + " is disabled\n"
+ + android.util.Log.getStackTraceString(new RuntimeException()));
+ return null;
} else {
IBinder binder;
switch (service) {
@@ -944,7 +959,8 @@
mDisabledRadioServices.get(service).add(mPhoneId);
mHalVersion.put(service, RADIO_HAL_VERSION_UNKNOWN);
riljLoge("getRadioServiceProxy: set " + serviceToString(service) + " for "
- + HIDL_SERVICE_NAME[mPhoneId] + " as disabled");
+ + HIDL_SERVICE_NAME[mPhoneId] + " as disabled\n"
+ + android.util.Log.getStackTraceString(new RuntimeException()));
}
}
} catch (RemoteException e) {
@@ -1094,9 +1110,16 @@
tdc.registerRIL(this);
}
+ validateFeatureFlags();
+
// Set radio callback; needed to set RadioIndication callback (should be done after
// wakelock stuff is initialized above as callbacks are received on separate binder threads)
for (int service = MIN_SERVICE_IDX; service <= MAX_SERVICE_IDX; service++) {
+ if (!isRadioServiceSupported(service)) {
+ riljLog("Not initializing " + serviceToString(service) + " (not supported)");
+ continue;
+ }
+
if (service == HAL_SERVICE_RADIO) {
getRadioProxy();
} else {
@@ -1161,6 +1184,26 @@
return false;
}
+ private void validateFeatureFlags() {
+ PackageManager pm = mContext.getPackageManager();
+ for (var entry : FEATURES_TO_SERVICES.entrySet()) {
+ String feature = entry.getKey();
+ int service = entry.getValue();
+
+ boolean hasFeature = pm.hasSystemFeature(feature);
+ boolean hasService = isRadioServiceSupported(service);
+
+ if (hasFeature && !hasService) {
+ riljLoge("Feature " + feature + " is declared, but service "
+ + serviceToString(service) + " is missing");
+ }
+ if (!hasFeature && hasService) {
+ riljLoge("Service " + serviceToString(service) + " is available, but feature "
+ + feature + " is not declared");
+ }
+ }
+ }
+
private boolean isRadioBugDetectionEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ENABLE_RADIO_BUG_DETECTION, 1) != 0;
diff --git a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
index bab4d12..4d9196e 100644
--- a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
+++ b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
@@ -86,6 +86,13 @@
register();
}
+ private void requestCapabilities() {
+ if (mRadioInterfaceCapabilities != null) return;
+
+ mRadioConfig.getHalDeviceCapabilities(obtainMessage(
+ EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE));
+ }
+
/**
* Gets the radio interface capabilities for the device
*/
@@ -95,8 +102,7 @@
// Only incur cost of synchronization block if mRadioInterfaceCapabilities isn't null
synchronized (mLockRadioInterfaceCapabilities) {
if (mRadioInterfaceCapabilities == null) {
- mRadioConfig.getHalDeviceCapabilities(
- obtainMessage(EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE));
+ requestCapabilities();
try {
if (Looper.myLooper() != getLooper()) {
mLockRadioInterfaceCapabilities.wait(2000);
@@ -158,7 +164,7 @@
switch (msg.what) {
case Phone.EVENT_RADIO_AVAILABLE:
case Phone.EVENT_RADIO_ON:
- getCapabilities();
+ requestCapabilities();
break;
case EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE:
setupCapabilities((AsyncResult) msg.obj);
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 04f5c08..498535b 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -1013,10 +1013,12 @@
* Notifies the {@link SmsDispatchersController} that sending MO SMS is failed.
*
* @param tracker holds the SMS message to be sent
+ * @param isOverIms a flag specifying whether SMS is sent via IMS or not
*/
- protected void notifySmsSentFailedToEmergencyStateTracker(SmsTracker tracker) {
+ protected void notifySmsSentFailedToEmergencyStateTracker(SmsTracker tracker,
+ boolean isOverIms) {
mSmsDispatchersController.notifySmsSentFailedToEmergencyStateTracker(
- tracker.mDestAddress, tracker.mMessageId);
+ tracker.mDestAddress, tracker.mMessageId, isOverIms);
}
/**
@@ -1052,7 +1054,7 @@
tracker.onSent(mContext);
mPhone.notifySmsSent(tracker.mDestAddress);
mSmsDispatchersController.notifySmsSentToEmergencyStateTracker(
- tracker.mDestAddress, tracker.mMessageId);
+ tracker.mDestAddress, tracker.mMessageId, false);
mPhone.getSmsStats().onOutgoingSms(
tracker.mImsRetry > 0 /* isOverIms */,
@@ -1103,7 +1105,7 @@
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE);
- notifySmsSentFailedToEmergencyStateTracker(tracker);
+ notifySmsSentFailedToEmergencyStateTracker(tracker, false);
mPhone.getSmsStats().onOutgoingSms(
tracker.mImsRetry > 0 /* isOverIms */,
SmsConstants.FORMAT_3GPP2.equals(getFormat()),
@@ -1164,7 +1166,7 @@
} else {
int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE;
tracker.onFailed(mContext, error, errorCode);
- notifySmsSentFailedToEmergencyStateTracker(tracker);
+ notifySmsSentFailedToEmergencyStateTracker(tracker, false);
mPhone.getSmsStats().onOutgoingSms(
tracker.mImsRetry > 0 /* isOverIms */,
SmsConstants.FORMAT_3GPP2.equals(getFormat()),
@@ -2389,7 +2391,7 @@
int errorCode) {
for (SmsTracker tracker : trackers) {
tracker.onFailed(mContext, error, errorCode);
- notifySmsSentFailedToEmergencyStateTracker(tracker);
+ notifySmsSentFailedToEmergencyStateTracker(tracker, false);
}
if (trackers.length > 0) {
// This error occurs before the SMS is sent. Make an assumption if it would have
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 8795840..cc287f8 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -109,6 +109,9 @@
/** Called when AP domain selection is abnormally terminated. */
private static final int EVENT_DOMAIN_SELECTION_TERMINATED_ABNORMALLY = 20;
+ /** Called when MT SMS is received via IMS. */
+ private static final int EVENT_SMS_RECEIVED_VIA_IMS = 21;
+
/** Delete any partial message segments after being IN_SERVICE for 1 day. */
private static final long PARTIAL_SEGMENT_WAIT_DURATION = (long) (60 * 60 * 1000) * 24;
/** Constant for invalid time */
@@ -487,8 +490,10 @@
String destAddr = (String) args.arg1;
Long messageId = (Long) args.arg2;
Boolean success = (Boolean) args.arg3;
+ Boolean isOverIms = (Boolean) args.arg4;
try {
- handleSmsSentCompletedUsingDomainSelection(destAddr, messageId, success);
+ handleSmsSentCompletedUsingDomainSelection(
+ destAddr, messageId, success, isOverIms);
} finally {
args.recycle();
}
@@ -499,6 +504,10 @@
(DomainSelectionConnectionHolder) msg.obj);
break;
}
+ case EVENT_SMS_RECEIVED_VIA_IMS: {
+ handleSmsReceivedViaIms((String) msg.obj);
+ break;
+ }
default:
if (isCdmaMo()) {
mCdmaDispatcher.handleMessage(msg);
@@ -808,7 +817,7 @@
Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
notifySmsSentFailedToEmergencyStateTracker(
- tracker.mDestAddress, tracker.mMessageId);
+ tracker.mDestAddress, tracker.mMessageId, !retryUsingImsService);
return;
}
String scAddr = (String) map.get("scAddr");
@@ -817,7 +826,7 @@
Rlog.e(TAG, "sendRetrySms failed due to null destAddr");
tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
notifySmsSentFailedToEmergencyStateTracker(
- tracker.mDestAddress, tracker.mMessageId);
+ tracker.mDestAddress, tracker.mMessageId, !retryUsingImsService);
return;
}
@@ -859,7 +868,7 @@
+ "destPort: %s", scAddr, map.get("destPort")));
tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
notifySmsSentFailedToEmergencyStateTracker(
- tracker.mDestAddress, tracker.mMessageId);
+ tracker.mDestAddress, tracker.mMessageId, !retryUsingImsService);
return;
}
// replace old smsc and pdu with newly encoded ones
@@ -1147,13 +1156,16 @@
* @param destAddr The destination address for SMS.
* @param messageId The message id for SMS.
* @param success A flag specifying whether MO SMS is successfully sent or not.
+ * @param isOverIms A flag specifying whether MO SMS is sent over IMS or not.
*/
private void handleSmsSentCompletedUsingDomainSelection(@NonNull String destAddr,
- long messageId, boolean success) {
+ long messageId, boolean success, boolean isOverIms) {
if (mEmergencyStateTracker != null) {
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
if (tm.isEmergencyNumber(destAddr)) {
- mEmergencyStateTracker.endSms(String.valueOf(messageId), success);
+ mEmergencyStateTracker.endSms(String.valueOf(messageId), success,
+ isOverIms ? NetworkRegistrationInfo.DOMAIN_PS
+ : NetworkRegistrationInfo.DOMAIN_CS);
}
}
}
@@ -1161,13 +1173,15 @@
/**
* Called when MO SMS is successfully sent.
*/
- protected void notifySmsSentToEmergencyStateTracker(@NonNull String destAddr, long messageId) {
+ protected void notifySmsSentToEmergencyStateTracker(@NonNull String destAddr, long messageId,
+ boolean isOverIms) {
if (isSmsDomainSelectionEnabled()) {
// Run on main thread for interworking with EmergencyStateTracker.
SomeArgs args = SomeArgs.obtain();
args.arg1 = destAddr;
args.arg2 = Long.valueOf(messageId);
args.arg3 = Boolean.TRUE;
+ args.arg4 = Boolean.valueOf(isOverIms);
sendMessage(obtainMessage(EVENT_SMS_SENT_COMPLETED_USING_DOMAIN_SELECTION, args));
}
}
@@ -1176,17 +1190,42 @@
* Called when sending MO SMS is failed.
*/
protected void notifySmsSentFailedToEmergencyStateTracker(@NonNull String destAddr,
- long messageId) {
+ long messageId, boolean isOverIms) {
if (isSmsDomainSelectionEnabled()) {
// Run on main thread for interworking with EmergencyStateTracker.
SomeArgs args = SomeArgs.obtain();
args.arg1 = destAddr;
args.arg2 = Long.valueOf(messageId);
args.arg3 = Boolean.FALSE;
+ args.arg4 = Boolean.valueOf(isOverIms);
sendMessage(obtainMessage(EVENT_SMS_SENT_COMPLETED_USING_DOMAIN_SELECTION, args));
}
}
+ /**
+ * Called when MT SMS is received via IMS.
+ *
+ * @param origAddr The originating address of MT SMS.
+ */
+ private void handleSmsReceivedViaIms(@Nullable String origAddr) {
+ if (mEmergencyStateTracker != null) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ if (origAddr != null && tm.isEmergencyNumber(origAddr)) {
+ mEmergencyStateTracker.onEmergencySmsReceived();
+ }
+ }
+ }
+
+ /**
+ * Called when MT SMS is received via IMS.
+ */
+ protected void notifySmsReceivedViaImsToEmergencyStateTracker(@Nullable String origAddr) {
+ if (isSmsDomainSelectionEnabled()) {
+ // Run on main thread for interworking with EmergencyStateTracker.
+ sendMessage(obtainMessage(EVENT_SMS_RECEIVED_VIA_IMS, origAddr));
+ }
+ }
+
private boolean isTestEmergencyNumber(String number) {
try {
TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index f5aa074..5da4b12 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -305,8 +305,9 @@
/**
* Create a new EmergencyNumberTracker.
*/
- public EmergencyNumberTracker makeEmergencyNumberTracker(Phone phone, CommandsInterface ci) {
- return new EmergencyNumberTracker(phone, ci);
+ public EmergencyNumberTracker makeEmergencyNumberTracker(Phone phone, CommandsInterface ci,
+ @NonNull FeatureFlags featureFlags) {
+ return new EmergencyNumberTracker(phone, ci, featureFlags);
}
private static final boolean USE_NEW_NITZ_STATE_MACHINE = true;
@@ -507,8 +508,9 @@
* @param c The context.
* @return The multi sim settings controller instance.
*/
- public MultiSimSettingController initMultiSimSettingController(Context c) {
- return MultiSimSettingController.init(c);
+ public MultiSimSettingController initMultiSimSettingController(Context c,
+ @NonNull FeatureFlags featureFlags) {
+ return MultiSimSettingController.init(c, featureFlags);
}
/**
@@ -571,9 +573,11 @@
* @return The data settings manager instance.
*/
public @NonNull DataSettingsManager makeDataSettingsManager(@NonNull Phone phone,
- @NonNull DataNetworkController dataNetworkController, @NonNull Looper looper,
+ @NonNull DataNetworkController dataNetworkController,
+ @NonNull FeatureFlags featureFlags, @NonNull Looper looper,
@NonNull DataSettingsManager.DataSettingsManagerCallback callback) {
- return new DataSettingsManager(phone, dataNetworkController, looper, callback);
+ return new DataSettingsManager(phone, dataNetworkController, featureFlags, looper,
+ callback);
}
/** Create CellularNetworkSecuritySafetySource. */
@@ -589,7 +593,8 @@
}
/** Create NullCipherNotifier. */
- public NullCipherNotifier makeNullCipherNotifier() {
- return NullCipherNotifier.getInstance();
+ public NullCipherNotifier makeNullCipherNotifier(
+ CellularNetworkSecuritySafetySource safetySource) {
+ return NullCipherNotifier.getInstance(safetySource);
}
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 2119003..f40b6d6 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -129,7 +129,7 @@
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE);
- notifySmsSentFailedToEmergencyStateTracker(tracker);
+ notifySmsSentFailedToEmergencyStateTracker(tracker, false);
return;
}
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
index 02c459a..343bb0b 100644
--- a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
+++ b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
@@ -188,6 +188,12 @@
* even if ping test fails.
*/
private boolean mRequirePingTestBeforeSwitch = true;
+ /**
+ * TODO: remove after V.
+ * To indicate whether allow using roaming nDDS if user enabled its roaming when the DDS is not
+ * usable(OOS or disabled roaming)
+ */
+ private boolean mAllowNddsRoamning = true;
/** The count of consecutive auto switch validation failure **/
private int mAutoSwitchValidationFailedCount = 0;
/**
@@ -444,6 +450,7 @@
DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
mScoreTolerance = dataConfig.getAutoDataSwitchScoreTolerance();
mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired();
+ mAllowNddsRoamning = dataConfig.doesAutoDataSwitchAllowRoaming();
mAutoDataSwitchAvailabilityStabilityTimeThreshold =
dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
mAutoDataSwitchPerformanceStabilityTimeThreshold =
@@ -694,7 +701,7 @@
boolean isForPerformance = false;
boolean needValidation = true;
- if (sFeatureFlags.autoSwitchAllowRoaming()) {
+ if (isNddsRoamingEnabled()) {
if (mDefaultNetworkIsOnNonCellular) {
debugMessage.append(", back to default as default network")
.append(" is active on nonCellular transport");
@@ -820,7 +827,7 @@
return invalidResult;
}
- if (sFeatureFlags.autoSwitchAllowRoaming()) {
+ if (isNddsRoamingEnabled()) {
// check whether primary and secondary signal status are worth switching
if (!isRatSignalStrengthBasedSwitchEnabled()
&& isHomeService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
@@ -842,7 +849,7 @@
Phone secondaryDataPhone = null;
PhoneSignalStatus candidatePhoneStatus = mPhonesSignalStatus[phoneId];
- if (sFeatureFlags.autoSwitchAllowRoaming()) {
+ if (isNddsRoamingEnabled()) {
PhoneSignalStatus.UsableState currentUsableState =
mPhonesSignalStatus[defaultPhoneId].getUsableState();
PhoneSignalStatus.UsableState candidateUsableState =
@@ -919,6 +926,13 @@
}
/**
+ * @return {@code true} If the feature of switching to roaming non DDS is enabled.
+ */
+ private boolean isNddsRoamingEnabled() {
+ return sFeatureFlags.autoDataSwitchAllowRoaming() && mAllowNddsRoamning;
+ }
+
+ /**
* Called when the current environment suits auto data switch.
* Start pre-switch validation if the current environment suits auto data switch for
* {@link #mAutoDataSwitchAvailabilityStabilityTimeThreshold} MS.
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index 90743f8..0e06dad 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -1051,6 +1051,15 @@
}
/**
+ * TODO: remove after V.
+ * @return To indicate whether allow using roaming nDDS if user enabled its roaming when the DDS
+ * is not usable(OOS or disabled roaming)
+ */
+ public boolean doesAutoDataSwitchAllowRoaming() {
+ return mResources.getBoolean(com.android.internal.R.bool.auto_data_switch_allow_roaming);
+ }
+
+ /**
* @return The maximum number of retries when a validation for switching failed.
*/
public int getAutoDataSwitchValidationMaxRetry() {
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index c1e6155..0dbbc5c 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -1180,12 +1180,14 @@
mPhone.getServiceStateTracker().registerForCssIndicatorChanged(
getHandler(), EVENT_CSS_INDICATOR_CHANGED, null);
- mPhone.getCallTracker().registerForVoiceCallStarted(
- getHandler(), EVENT_VOICE_CALL_STARTED, null);
- mPhone.getCallTracker().registerForVoiceCallEnded(
- getHandler(), EVENT_VOICE_CALL_ENDED, null);
+ if (mPhone.getCallTracker() != null) {
+ mPhone.getCallTracker().registerForVoiceCallStarted(
+ getHandler(), EVENT_VOICE_CALL_STARTED, null);
+ mPhone.getCallTracker().registerForVoiceCallEnded(
+ getHandler(), EVENT_VOICE_CALL_ENDED, null);
+ }
// Check null for devices not supporting FEATURE_TELEPHONY_IMS.
- if (mPhone.getImsPhone() != null) {
+ if (mPhone.getImsPhone() != null && mPhone.getImsPhone().getCallTracker() != null) {
mPhone.getImsPhone().getCallTracker().registerForVoiceCallStarted(
getHandler(), EVENT_VOICE_CALL_STARTED, null);
mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
@@ -1223,12 +1225,14 @@
}
// Check null for devices not supporting FEATURE_TELEPHONY_IMS.
- if (mPhone.getImsPhone() != null) {
+ if (mPhone.getImsPhone() != null && mPhone.getImsPhone().getCallTracker() != null) {
mPhone.getImsPhone().getCallTracker().unregisterForVoiceCallStarted(getHandler());
mPhone.getImsPhone().getCallTracker().unregisterForVoiceCallEnded(getHandler());
}
- mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
- mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
+ if (mPhone.getCallTracker() != null) {
+ mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
+ mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
+ }
mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler());
TelephonyManager tm = mPhone.getContext().getSystemService(TelephonyManager.class);
@@ -1407,7 +1411,14 @@
} else {
loge("Failed to allocate PDU session id. e=" + ar.exception);
}
- setupData();
+ //Check whether all network requests were removed before setupData.
+ if (!mAttachedNetworkRequestList.isEmpty()) {
+ setupData();
+ } else {
+ mRetryDelayMillis = DataCallResponse.RETRY_DURATION_UNDEFINED;
+ mFailCause = DataFailCause.NO_RETRY_FAILURE;
+ transitionTo(mDisconnectedState);
+ }
break;
case EVENT_SETUP_DATA_NETWORK_RESPONSE:
int resultCode = msg.arg1;
@@ -1536,7 +1547,7 @@
// For requests that can't be satisfied anymore, we need to put them back to the
// unsatisfied pool. If none of network requests can be satisfied, then there is no
// need to mark network agent connected. Just silently deactivate the data network.
- if (mAttachedNetworkRequestList.size() == 0) {
+ if (mAttachedNetworkRequestList.isEmpty()) {
log("Tear down the network since there is no live network request.");
// Directly call onTearDown here. Calling tearDown will cause deadlock because
// EVENT_TEAR_DOWN_NETWORK is deferred until state machine enters connected
@@ -2507,7 +2518,8 @@
newSuspendedState = true;
// Check voice/data concurrency.
} else if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()
- && mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ && mTransport == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
+ && mPhone.getCallTracker() != null) {
newSuspendedState = mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE;
}
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 3ca28a3..70d3b23 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicyManager;
@@ -426,6 +427,12 @@
}
};
+ private boolean hasCalling() {
+ if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+ return mPhone.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING);
+ }
+
/**
* The sorted network request list by priority. The highest priority network request stays at
* the head of the list. The highest priority is 100, the lowest is 0.
@@ -861,7 +868,7 @@
mDataSettingsManager = TelephonyComponentFactory.getInstance().inject(
DataSettingsManager.class.getName())
- .makeDataSettingsManager(mPhone, this, looper,
+ .makeDataSettingsManager(mPhone, this, mFeatureFlags, looper,
new DataSettingsManagerCallback(this::post) {
@Override
public void onDataEnabledChanged(boolean enabled,
@@ -921,7 +928,7 @@
}
});
mDataStallRecoveryManager = new DataStallRecoveryManager(mPhone, this, mDataServiceManagers
- .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), looper,
+ .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), mFeatureFlags, looper,
new DataStallRecoveryManagerCallback(this::post) {
@Override
public void onDataStallReestablishInternet() {
@@ -1045,16 +1052,18 @@
}
}, this::post);
- // Register for call ended event for voice/data concurrent not supported case. It is
- // intended to only listen for events from the same phone as most of the telephony modules
- // are designed as per-SIM basis. For DSDS call ended on non-DDS sub, the frameworks relies
- // on service state on DDS sub change from out-of-service to in-service to trigger data
- // retry.
- mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_VOICE_CALL_ENDED, null);
- // Check null for devices not supporting FEATURE_TELEPHONY_IMS.
- if (mPhone.getImsPhone() != null) {
- mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
- this, EVENT_VOICE_CALL_ENDED, null);
+ if (hasCalling()) {
+ // Register for call ended event for voice/data concurrent not supported case. It is
+ // intended to only listen for events from the same phone as most of the telephony
+ // modules are designed as per-SIM basis. For DSDS call ended on non-DDS sub, the
+ // frameworks relies on service state on DDS sub change from out-of-service to
+ // in-service to trigger data retry.
+ mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_VOICE_CALL_ENDED, null);
+ // Check null for devices not supporting FEATURE_TELEPHONY_IMS.
+ if (mPhone.getImsPhone() != null) {
+ mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
+ this, EVENT_VOICE_CALL_ENDED, null);
+ }
}
mPhone.mCi.registerForSlicingConfigChanged(this, EVENT_SLICE_CONFIG_CHANGED, null);
mPhone.mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
@@ -1569,7 +1578,7 @@
}
// Check CS call state and see if concurrent voice/data is allowed.
- if (mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE
+ if (hasCalling() && mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE
&& !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
evaluation.addDataDisallowedReason(
DataDisallowedReason.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index e54f6d3..51e5b7d 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -47,6 +48,7 @@
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SettingsObserver;
import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.metrics.DeviceTelephonyPropertiesStats;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
@@ -57,6 +59,7 @@
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -88,6 +91,7 @@
private static final int EVENT_INITIALIZE = 11;
private final Phone mPhone;
+ private final @NonNull FeatureFlags mFeatureFlags;
private final ContentResolver mResolver;
private final SettingsObserver mSettingsObserver;
private final String mLogTag;
@@ -178,10 +182,12 @@
* @param callback Data settings manager callback.
*/
public DataSettingsManager(@NonNull Phone phone,
- @NonNull DataNetworkController dataNetworkController, @NonNull Looper looper,
+ @NonNull DataNetworkController dataNetworkController,
+ @NonNull FeatureFlags featureFlags, @NonNull Looper looper,
@NonNull DataSettingsManagerCallback callback) {
super(looper);
mPhone = phone;
+ mFeatureFlags = Objects.requireNonNull(featureFlags);
mLogTag = "DSMGR-" + mPhone.getPhoneId();
log("DataSettingsManager created.");
mSubId = mPhone.getSubId();
@@ -264,6 +270,12 @@
}
}
+ private boolean hasCalling() {
+ if (!mFeatureFlags.minimalTelephonyCdmCheck()) return true;
+ return mPhone.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING);
+ }
+
/**
* Called when needed to register for all events that data network controller is interested.
*/
@@ -281,9 +293,12 @@
mSettingsObserver.observe(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
EVENT_PROVISIONING_DATA_ENABLED_CHANGED);
- mPhone.getCallTracker().registerForVoiceCallStarted(this, EVENT_CALL_STATE_CHANGED, null);
- mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_CALL_STATE_CHANGED, null);
- if (mPhone.getImsPhone() != null) {
+ if (hasCalling()) {
+ mPhone.getCallTracker().registerForVoiceCallStarted(this, EVENT_CALL_STATE_CHANGED,
+ null);
+ mPhone.getCallTracker().registerForVoiceCallEnded(this, EVENT_CALL_STATE_CHANGED, null);
+ }
+ if (hasCalling() && mPhone.getImsPhone() != null) {
mPhone.getImsPhone().getCallTracker().registerForVoiceCallStarted(
this, EVENT_CALL_STATE_CHANGED, null);
mPhone.getImsPhone().getCallTracker().registerForVoiceCallEnded(
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index 893509c..ee8890a 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -48,6 +48,7 @@
import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.metrics.DataStallRecoveryStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.telephony.Rlog;
@@ -153,6 +154,7 @@
private final @NonNull Phone mPhone;
private final @NonNull String mLogTag;
private final @NonNull LocalLog mLocalLog = new LocalLog(128);
+ private final @NonNull FeatureFlags mFeatureFlags;
/** Data network controller */
private final @NonNull DataNetworkController mDataNetworkController;
@@ -196,7 +198,10 @@
private boolean mIsInternetNetworkConnected;
/** The durations for current recovery action */
private @ElapsedRealtimeLong long mTimeElapsedOfCurrentAction;
-
+ /** Tracks the total number of validation duration a data stall */
+ private int mValidationCount;
+ /** Tracks the number of validation for current action during a data stall */
+ private int mActionValidationCount;
/** The array for the timers between recovery actions. */
private @NonNull long[] mDataStallRecoveryDelayMillisArray;
/** The boolean array for the flags. They are used to skip the recovery actions if needed. */
@@ -253,6 +258,7 @@
* @param phone The phone instance.
* @param dataNetworkController Data network controller
* @param dataServiceManager The WWAN data service manager.
+ * @param featureFlags The feature flag.
* @param looper The looper to be used by the handler. Currently the handler thread is the phone
* process's main thread.
* @param callback Callback to notify data network controller for data stall events.
@@ -261,6 +267,7 @@
@NonNull Phone phone,
@NonNull DataNetworkController dataNetworkController,
@NonNull DataServiceManager dataServiceManager,
+ @NonNull FeatureFlags featureFlags,
@NonNull Looper looper,
@NonNull DataStallRecoveryManagerCallback callback) {
super(looper);
@@ -269,6 +276,7 @@
log("DataStallRecoveryManager created.");
mDataNetworkController = dataNetworkController;
mWwanDataServiceManager = dataServiceManager;
+ mFeatureFlags = featureFlags;
mDataConfigManager = mDataNetworkController.getDataConfigManager();
mDataNetworkController
.getDataSettingsManager()
@@ -288,7 +296,7 @@
registerAllEvents();
- mStats = new DataStallRecoveryStats(mPhone, dataNetworkController);
+ mStats = new DataStallRecoveryStats(mPhone, mFeatureFlags, dataNetworkController);
}
/** Register for all events that data stall monitor is interested. */
@@ -546,6 +554,8 @@
mTimeLastRecoveryStartMs = 0;
mLastAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
mRecoveryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
+ mValidationCount = 0;
+ mActionValidationCount = 0;
}
/**
@@ -556,8 +566,16 @@
private void onInternetValidationStatusChanged(@ValidationStatus int status) {
logl("onInternetValidationStatusChanged: " + DataUtils.validationStatusToString(status));
final boolean isValid = status == NetworkAgent.VALIDATION_STATUS_VALID;
+ if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
+ mValidationCount += 1;
+ mActionValidationCount += 1;
+ }
setNetworkValidationState(isValid);
if (isValid) {
+ if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
+ // Broadcast intent that data stall recovered.
+ broadcastDataStallDetected(getRecoveryAction());
+ }
reset();
} else if (isRecoveryNeeded(true)) {
// Set the network as invalid, because recovery is needed
@@ -596,6 +614,10 @@
*/
@VisibleForTesting
public void setRecoveryAction(@RecoveryAction int action) {
+ // Reset the validation count for action change
+ if (mFeatureFlags.dsrsDiagnosticsEnabled() && mRecoveryAction != action) {
+ mActionValidationCount = 0;
+ }
mRecoveryAction = action;
// Check if the mobile data enabled is TRUE, it means that the mobile data setting changed
@@ -674,13 +696,16 @@
final boolean isRecovered = !mDataStalled;
final int duration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
final @RecoveredReason int reason = getRecoveredReason(mIsValidNetwork);
- final boolean isFirstValidationOfAction = false;
final int durationOfAction = (int) getDurationOfCurrentRecoveryMs();
+ if (mFeatureFlags.dsrsDiagnosticsEnabled()) {
+ log("mValidationCount=" + mValidationCount
+ + ", mActionValidationCount=" + mActionValidationCount);
+ }
// Get the bundled DSRS stats.
Bundle bundle = mStats.getDataStallRecoveryMetricsData(
- recoveryAction, isRecovered, duration, reason, isFirstValidationOfAction,
- durationOfAction);
+ recoveryAction, isRecovered, duration, reason, mValidationCount,
+ mActionValidationCount, durationOfAction);
// Put the bundled stats extras on the intent.
intent.putExtra("EXTRA_DSRS_STATS_BUNDLE", bundle);
diff --git a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
index cb4ddab..66b977d 100644
--- a/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java
@@ -37,10 +37,12 @@
import android.telephony.DomainSelectionService;
import android.telephony.EmergencyRegistrationResult;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDisconnectCause;
import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsReasonInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CallFailCause;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.data.AccessNetworksManager;
import com.android.internal.telephony.data.AccessNetworksManager.QualifiedNetworks;
@@ -215,6 +217,19 @@
@NonNull String callId, @NonNull String number, boolean isTest,
int callFailCause, @Nullable ImsReasonInfo imsReasonInfo,
@Nullable EmergencyRegistrationResult emergencyRegResult) {
+
+ int preciseDisconnectCause = callFailCause;
+ switch (callFailCause) {
+ case CallFailCause.IMS_EMERGENCY_TEMP_FAILURE:
+ preciseDisconnectCause = PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE;
+ break;
+ case CallFailCause.IMS_EMERGENCY_PERM_FAILURE:
+ preciseDisconnectCause = PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
+ break;
+ default:
+ break;
+ }
+
DomainSelectionService.SelectionAttributes.Builder builder =
new DomainSelectionService.SelectionAttributes.Builder(
slotId, subId, SELECTOR_TYPE_CALLING)
@@ -223,7 +238,7 @@
.setExitedFromAirplaneMode(exited)
.setCallId(callId)
.setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null))
- .setCsDisconnectCause(callFailCause);
+ .setCsDisconnectCause(preciseDisconnectCause);
if (imsReasonInfo != null) builder.setPsDisconnectCause(imsReasonInfo);
if (emergencyRegResult != null) builder.setEmergencyRegistrationResult(emergencyRegResult);
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index e2418c5..02dd613 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -16,10 +16,12 @@
package com.android.internal.telephony.emergency;
+import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Environment;
@@ -48,6 +50,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.metrics.EmergencyNumberStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.PersistAtomsProto;
@@ -102,6 +105,7 @@
private final CommandsInterface mCi;
private final Phone mPhone;
+ private final @NonNull FeatureFlags mFeatureFlags;
private int mPhoneId;
private String mCountryIso;
private String mLastKnownEmergencyCountryIso = "";
@@ -173,10 +177,20 @@
}
};
- public EmergencyNumberTracker(Phone phone, CommandsInterface ci) {
+ public EmergencyNumberTracker(Phone phone, CommandsInterface ci,
+ @NonNull FeatureFlags featureFlags) {
+ Context ctx = phone.getContext();
+
mPhone = phone;
mCi = ci;
- mResources = mPhone.getContext().getResources();
+ mFeatureFlags = featureFlags;
+ mResources = ctx.getResources();
+
+ if (mFeatureFlags.minimalTelephonyCdmCheck()
+ && !ctx.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ throw new UnsupportedOperationException("EmergencyNumberTracker requires calling");
+ }
if (mPhone != null) {
mPhoneId = phone.getPhoneId();
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index a35cccf..c4d5355 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -46,12 +46,9 @@
import android.telephony.DisconnectCause;
import android.telephony.EmergencyRegistrationResult;
import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,7 +68,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -92,7 +88,6 @@
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;
@@ -132,9 +127,6 @@
private final Runnable mExitEcmRunnable = this::exitEmergencyCallbackMode;
// Tracks emergency calls by callId that have reached {@link Call.State#ACTIVE}.
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 connection to handle a second emergency call
private android.telecom.Connection mOngoingConnection;
@@ -152,6 +144,9 @@
private Phone mSmsPhone;
private CompletableFuture<Integer> mSmsEmergencyModeFuture;
private boolean mIsTestEmergencyNumberForSms;
+ // For tracking the emergency SMS callback mode.
+ private boolean mIsInScbm;
+ private boolean mIsEmergencySmsStartedDuringScbm;
private CompletableFuture<Boolean> mEmergencyTransportChangedFuture;
@@ -182,29 +177,6 @@
}
};
- /**
- * TelephonyCallback used to monitor whether ePDN on cellular network is disconnected or not.
- */
- private final class PreciseDataConnectionStateListener extends TelephonyCallback implements
- TelephonyCallback.PreciseDataConnectionStateListener {
- @Override
- public void onPreciseDataConnectionStateChanged(
- @NonNull PreciseDataConnectionState dataConnectionState) {
- ApnSetting apnSetting = dataConnectionState.getApnSetting();
- if ((apnSetting == null)
- || ((apnSetting.getApnTypeBitmask() | ApnSetting.TYPE_EMERGENCY) == 0)
- || (dataConnectionState.getTransportType()
- != AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) {
- return;
- }
- int state = dataConnectionState.getState();
- Rlog.d(TAG, "onPreciseDataConnectionStateChanged ePDN state=" + state);
- if (state == TelephonyManager.DATA_DISCONNECTED) exitEmergencyModeIfDelayed();
- }
- }
-
- private PreciseDataConnectionStateListener mDataConnectionStateListener;
-
/** PhoneFactory Dependencies for testing. */
@VisibleForTesting
public interface PhoneFactoryProxy {
@@ -228,8 +200,6 @@
@VisibleForTesting
public interface TelephonyManagerProxy {
int getPhoneCount();
- void registerTelephonyCallback(int subId, Executor executor, TelephonyCallback callback);
- void unregisterTelephonyCallback(TelephonyCallback callback);
}
private final TelephonyManagerProxy mTelephonyManagerProxy;
@@ -245,18 +215,6 @@
public int getPhoneCount() {
return mTelephonyManager.getActiveModemCount();
}
-
- @Override
- public void registerTelephonyCallback(int subId,
- Executor executor, TelephonyCallback callback) {
- TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId);
- tm.registerTelephonyCallback(executor, callback);
- }
-
- @Override
- public void unregisterTelephonyCallback(TelephonyCallback callback) {
- mTelephonyManager.unregisterTelephonyCallback(callback);
- }
}
/**
@@ -268,15 +226,13 @@
}
@VisibleForTesting
- public static final int MSG_SET_EMERGENCY_MODE = 1;
+ public static final int MSG_SET_EMERGENCY_MODE_DONE = 1;
@VisibleForTesting
- public static final int MSG_EXIT_EMERGENCY_MODE = 2;
+ public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 2;
@VisibleForTesting
- public static final int MSG_SET_EMERGENCY_MODE_DONE = 3;
- @VisibleForTesting
- public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 4;
- @VisibleForTesting
- public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 5;
+ public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 3;
+ /** A message which is used to automatically exit from SCBM after a period of time. */
+ private static final int MSG_EXIT_SCBM = 4;
private class MyHandler extends Handler {
@@ -312,38 +268,46 @@
// Case 1) When the emergency call is setting the emergency mode and
// the emergency SMS is being sent, completes the SMS future also.
// Case 2) When the emergency SMS is setting the emergency mode and
- // the emergency call is beint started, the SMS request is cancelled and
+ // the emergency call is being started, the SMS request is cancelled and
// the call request will be handled.
if (mSmsPhone != null) {
completeEmergencyMode(EMERGENCY_TYPE_SMS);
}
} else if (emergencyType == EMERGENCY_TYPE_SMS) {
if (mPhone != null && mSmsPhone != null) {
- // Clear call phone temporarily to exit the emergency mode
- // if the emergency call is started.
if (mIsEmergencyCallStartedDuringEmergencySms) {
- Phone phone = mPhone;
- mPhone = null;
- exitEmergencyMode(mSmsPhone, emergencyType, false);
- // Restore call phone for further use.
- mPhone = phone;
-
- if (!isSamePhone(mPhone, mSmsPhone)) {
- completeEmergencyMode(emergencyType,
- DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED);
+ if (!isSamePhone(mPhone, mSmsPhone) || !isInScbm()) {
+ // Clear call phone temporarily to exit the emergency mode
+ // if the emergency call is started.
+ Phone phone = mPhone;
+ mPhone = null;
+ exitEmergencyMode(mSmsPhone, emergencyType);
+ // Restore call phone for further use.
+ mPhone = phone;
+ if (!isSamePhone(mPhone, mSmsPhone)) {
+ completeEmergencyMode(emergencyType,
+ DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED);
+ exitEmergencySmsCallbackMode();
+ }
+ } else {
+ completeEmergencyMode(emergencyType);
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ exitEmergencySmsCallbackMode();
+ turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL,
+ mIsTestEmergencyNumber);
}
} else {
completeEmergencyMode(emergencyType);
}
- break;
} else {
completeEmergencyMode(emergencyType);
- }
- if (mIsEmergencyCallStartedDuringEmergencySms) {
- mIsEmergencyCallStartedDuringEmergencySms = false;
- turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL,
- mIsTestEmergencyNumber);
+ if (mIsEmergencyCallStartedDuringEmergencySms) {
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ exitEmergencySmsCallbackMode();
+ turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL,
+ mIsTestEmergencyNumber);
+ }
}
}
break;
@@ -366,6 +330,10 @@
mIsEmergencyCallStartedDuringEmergencySms = false;
turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL,
mIsTestEmergencyNumber);
+ } else if (mIsEmergencySmsStartedDuringScbm) {
+ mIsEmergencySmsStartedDuringScbm = false;
+ setEmergencyMode(mSmsPhone, emergencyType,
+ MODE_EMERGENCY_WWAN, MSG_SET_EMERGENCY_MODE_DONE);
}
}
break;
@@ -378,30 +346,35 @@
setEmergencyModeInProgress(false);
// When the emergency callback mode is in progress and the emergency SMS is
// started, it needs to be completed here for the emergency SMS.
- if (mSmsPhone != null) {
- completeEmergencyMode(EMERGENCY_TYPE_SMS);
+ if (emergencyType == EMERGENCY_TYPE_CALL) {
+ if (mSmsPhone != null) {
+ completeEmergencyMode(EMERGENCY_TYPE_SMS);
+ }
+ } else if (emergencyType == EMERGENCY_TYPE_SMS) {
+ // When the emergency SMS callback mode is in progress on other phone and
+ // the emergency call was started, needs to exit the emergency mode first.
+ if (mIsEmergencyCallStartedDuringEmergencySms) {
+ final Phone smsPhone = mSmsPhone;
+ exitEmergencySmsCallbackMode();
+
+ if (mPhone != null && smsPhone != null
+ && !isSamePhone(mPhone, smsPhone)) {
+ Phone phone = mPhone;
+ mPhone = null;
+ exitEmergencyMode(smsPhone, emergencyType);
+ // Restore call phone for further use.
+ mPhone = phone;
+ } else {
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL,
+ mIsTestEmergencyNumber);
+ }
+ }
}
break;
}
- case MSG_EXIT_EMERGENCY_MODE: {
- Rlog.v(TAG, "MSG_EXIT_EMERGENCY_MODE");
- exitEmergencyModeIfDelayed();
- break;
- }
- case MSG_SET_EMERGENCY_MODE: {
- AsyncResult ar = (AsyncResult) msg.obj;
- Integer emergencyType = (Integer) ar.userObj;
- Rlog.v(TAG, "MSG_SET_EMERGENCY_MODE for "
- + emergencyTypeToString(emergencyType) + ", " + mEmergencyMode);
- // Should be reached here only when starting a new emergency service
- // while exiting emergency callback mode on the other slot.
- if (mEmergencyMode != MODE_EMERGENCY_WWAN) return;
- final Phone phone = (mPhone != null) ? mPhone : mSmsPhone;
- if (phone != null) {
- mWasEmergencyModeSetOnModem = true;
- phone.setEmergencyMode(MODE_EMERGENCY_WWAN,
- mHandler.obtainMessage(MSG_SET_EMERGENCY_MODE_DONE, emergencyType));
- }
+ case MSG_EXIT_SCBM: {
+ exitEmergencySmsCallbackModeAndEmergencyMode();
break;
}
default:
@@ -522,19 +495,26 @@
// Case1) When 2nd emergency call is initiated during an active call on the same phone.
// Case2) While the device is in ECBM, an emergency call is initiated on the same phone.
if (isSamePhone(mPhone, phone) && (!mActiveEmergencyCalls.isEmpty() || isInEcm())) {
+ exitEmergencySmsCallbackMode();
mOngoingConnection = c;
mIsTestEmergencyNumber = isTestEmergencyNumber;
- // Ensure that domain selector requests scan.
- mLastEmergencyRegistrationResult = new EmergencyRegistrationResult(
- AccessNetworkConstants.AccessNetworkType.UNKNOWN,
- NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN,
- NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", "");
if (isInEcm()) {
// Remove pending exit ECM runnable.
mHandler.removeCallbacks(mExitEcmRunnable);
releaseWakeLock();
((GsmCdmaPhone) mPhone).notifyEcbmTimerReset(Boolean.TRUE);
+
+ mOngoingCallProperties = 0;
+ mCallEmergencyModeFuture = new CompletableFuture<>();
+ setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN,
+ MSG_SET_EMERGENCY_MODE_DONE);
+ return mCallEmergencyModeFuture;
}
+ // Ensure that domain selector requests scan.
+ mLastEmergencyRegistrationResult = new EmergencyRegistrationResult(
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN,
+ NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", "");
return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED);
}
@@ -553,13 +533,25 @@
// exit the emergency mode when receiving the result of setting the emergency mode and
// the emergency mode for this call will be restarted after the exit complete.
if (isInEmergencyMode() && !isEmergencyModeInProgress()) {
- exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, false);
+ if (!isSamePhone(mSmsPhone, phone)) {
+ exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+ } else {
+ // If the device is already in the emergency mode on the same phone,
+ // the general emergency call procedure can be immediately performed.
+ // And, if the emergency PDN is already connected, then we need to keep
+ // this PDN active while initating the emergency call.
+ mIsEmergencyCallStartedDuringEmergencySms = false;
+ }
+
+ exitEmergencySmsCallbackMode();
}
- mPhone = phone;
- mOngoingConnection = c;
- mIsTestEmergencyNumber = isTestEmergencyNumber;
- return mCallEmergencyModeFuture;
+ if (mIsEmergencyCallStartedDuringEmergencySms) {
+ mPhone = phone;
+ mOngoingConnection = c;
+ mIsTestEmergencyNumber = isTestEmergencyNumber;
+ return mCallEmergencyModeFuture;
+ }
}
mPhone = phone;
@@ -587,7 +579,7 @@
}
if (wasActive && mActiveEmergencyCalls.isEmpty()
- && isEmergencyCallbackModeSupported()) {
+ && isEmergencyCallbackModeSupported(mPhone)) {
enterEmergencyCallbackMode();
if (mOngoingConnection == null) {
@@ -604,7 +596,12 @@
enterEmergencyCallbackMode();
}
} else {
- exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, false);
+ if (isInScbm()) {
+ setIsInEmergencyCall(false);
+ setEmergencyCallbackMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+ } else {
+ exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL);
+ }
clearEmergencyCallInfo();
}
}
@@ -635,8 +632,20 @@
// 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);
+ if (mEmergencyMode != MODE_EMERGENCY_WWAN) {
+ setEmergencyMode(phone, emergencyType, MODE_EMERGENCY_WWAN,
+ MSG_SET_EMERGENCY_MODE_DONE);
+ } else {
+ // Ensure that domain selector requests the network scan.
+ mLastEmergencyRegistrationResult = new EmergencyRegistrationResult(
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN,
+ NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", "");
+ if (emergencyType == EMERGENCY_TYPE_CALL) {
+ setIsInEmergencyCall(true);
+ }
+ completeEmergencyMode(emergencyType);
+ }
});
}
@@ -673,26 +682,33 @@
return;
}
- synchronized (mLock) {
- unregisterForDataConnectionStateChanges();
- if (mPhoneToExit != null) {
- if (emergencyType != EMERGENCY_TYPE_CALL) {
- setIsInEmergencyCall(false);
- }
- mOnEcmExitCompleteRunnable = null;
- if (mPhoneToExit != phone) {
- // Exit emergency mode on the other phone first,
- // then set emergency mode on the given phone.
- mPhoneToExit.exitEmergencyMode(
- mHandler.obtainMessage(MSG_SET_EMERGENCY_MODE,
- Integer.valueOf(emergencyType)));
- mPhoneToExit = null;
- return;
- }
- mPhoneToExit = null;
+ mWasEmergencyModeSetOnModem = true;
+ phone.setEmergencyMode(mode, m);
+ }
+
+ /**
+ * Sets the emergency callback mode on modem.
+ *
+ * @param phone the {@code Phone} to set the emergency mode on modem.
+ * @param emergencyType the emergency type to identify an emergency call or SMS.
+ */
+ private void setEmergencyCallbackMode(Phone phone, @EmergencyType int emergencyType) {
+ boolean needToSetCallbackMode = false;
+
+ if (emergencyType == EMERGENCY_TYPE_CALL) {
+ needToSetCallbackMode = true;
+ } else if (emergencyType == EMERGENCY_TYPE_SMS) {
+ // Ensure that no emergency call is in progress.
+ if (mActiveEmergencyCalls.isEmpty() && mOngoingConnection == null
+ && mOngoingEmergencySmsIds.isEmpty()) {
+ needToSetCallbackMode = true;
}
- mWasEmergencyModeSetOnModem = true;
- phone.setEmergencyMode(mode, m);
+ }
+
+ if (needToSetCallbackMode) {
+ // Set emergency mode on modem.
+ setEmergencyMode(phone, emergencyType, MODE_EMERGENCY_CALLBACK,
+ MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
}
}
@@ -766,10 +782,8 @@
*
* @param phone the {@code Phone} to exit the emergency mode.
* @param emergencyType the emergency type to identify an emergency call or SMS.
- * @param waitForPdnDisconnect indicates whether it shall wait for the disconnection of ePDN.
*/
- private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType,
- boolean waitForPdnDisconnect) {
+ private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType) {
Rlog.i(TAG, "exitEmergencyMode for " + emergencyTypeToString(emergencyType));
if (emergencyType == EMERGENCY_TYPE_CALL) {
@@ -805,23 +819,8 @@
return;
}
- synchronized (mLock) {
- mWasEmergencyModeSetOnModem = false;
- if (waitForPdnDisconnect) {
- registerForDataConnectionStateChanges(phone);
- mPhoneToExit = phone;
- if (mPdnDisconnectionTimeoutMs > 0) {
- // To avoid waiting for the disconnection indefinitely.
- mHandler.sendEmptyMessageDelayed(MSG_EXIT_EMERGENCY_MODE,
- mPdnDisconnectionTimeoutMs);
- }
- return;
- } else {
- unregisterForDataConnectionStateChanges();
- mPhoneToExit = null;
- }
- phone.exitEmergencyMode(m);
- }
+ mWasEmergencyModeSetOnModem = false;
+ phone.exitEmergencyMode(m);
}
/** Returns last {@link EmergencyRegistrationResult} as set by {@code setEmergencyMode()}. */
@@ -985,12 +984,8 @@
* Handles the radio power off request.
*/
public void onCellularRadioPowerOffRequested() {
- synchronized (mLock) {
- if (isInEcm()) {
- exitEmergencyCallbackMode(null);
- }
- exitEmergencyModeIfDelayed();
- }
+ exitEmergencySmsCallbackModeAndEmergencyMode();
+ exitEmergencyCallbackMode();
}
private static boolean isVoWiFi(int properties) {
@@ -1000,14 +995,16 @@
/**
* Returns {@code true} if device and carrier support emergency callback mode.
+ *
+ * @param phone The {@link Phone} instance to be checked.
*/
@VisibleForTesting
- public boolean isEmergencyCallbackModeSupported() {
- int subId = mPhone.getSubId();
+ public boolean isEmergencyCallbackModeSupported(Phone phone) {
+ int subId = phone.getSubId();
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
// If there is no SIM, refer to the saved last carrier configuration with valid
// subscription.
- int phoneId = mPhone.getPhoneId();
+ int phoneId = phone.getPhoneId();
Boolean savedConfig = mNoSimEcbmSupported.get(Integer.valueOf(phoneId));
if (savedConfig == null) {
// Exceptional case such as with poor boot performance.
@@ -1054,9 +1051,7 @@
((GsmCdmaPhone) mPhone).notifyEcbmTimerReset(Boolean.FALSE);
}
- // Set emergency mode on modem.
- setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK,
- MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
+ setEmergencyCallbackMode(mPhone, EMERGENCY_TYPE_CALL);
// Post this runnable so we will automatically exit if no one invokes
// exitEmergencyCallbackMode() directly.
@@ -1091,9 +1086,7 @@
gsmCdmaPhone.notifyEmergencyCallRegistrants(false);
// Exit emergency mode on modem.
- // b/299866883: Wait for the disconnection of ePDN before calling exitEmergencyMode.
- exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL,
- mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_PS);
+ exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL);
}
mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN;
@@ -1182,7 +1175,8 @@
*/
public CompletableFuture<Integer> startEmergencySms(@NonNull Phone phone, @NonNull String smsId,
boolean isTestEmergencyNumber) {
- Rlog.i(TAG, "startEmergencySms: phoneId=" + phone.getPhoneId() + ", smsId=" + smsId);
+ Rlog.i(TAG, "startEmergencySms: phoneId=" + phone.getPhoneId() + ", smsId=" + smsId
+ + ", scbm=" + isInScbm());
// When an emergency call is in progress, it checks whether an emergency call is already in
// progress on the different phone.
@@ -1191,17 +1185,29 @@
return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED);
}
- // When an emergency SMS is in progress, it checks whether an emergency SMS is already in
- // progress on the different phone.
+ boolean exitScbmInOtherPhone = false;
+ boolean smsStartedInScbm = isInScbm();
+
+ // When an emergency SMS is in progress, it checks whether an emergency SMS is already
+ // in progress on the different phone.
if (mSmsPhone != null && !isSamePhone(mSmsPhone, phone)) {
- Rlog.e(TAG, "Emergency SMS is in progress on the other slot.");
- return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED);
+ if (smsStartedInScbm) {
+ // When other phone is in the emergency SMS callback mode, we need to stop the
+ // emergency SMS callback mode first.
+ exitScbmInOtherPhone = true;
+ mIsEmergencySmsStartedDuringScbm = true;
+ exitEmergencySmsCallbackModeAndEmergencyMode();
+ } else {
+ Rlog.e(TAG, "Emergency SMS is in progress on the other slot.");
+ return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED);
+ }
}
- // When the previous emergency SMS is not completed yet,
+ // When the previous emergency SMS is not completed yet and the device is not in SCBM,
// this new request will not be allowed.
- if (mSmsPhone != null && isInEmergencyMode() && isEmergencyModeInProgress()) {
- Rlog.e(TAG, "Existing emergency SMS is in progress.");
+ if (mSmsPhone != null && isInEmergencyMode() && isEmergencyModeInProgress()
+ && !smsStartedInScbm) {
+ Rlog.e(TAG, "Existing emergency SMS is in progress and not in SCBM.");
return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED);
}
@@ -1209,17 +1215,31 @@
mIsTestEmergencyNumberForSms = isTestEmergencyNumber;
mOngoingEmergencySmsIds.add(smsId);
- // When the emergency mode is already set by the previous emergency call or SMS,
- // completes the future immediately.
- if (isInEmergencyMode() && !isEmergencyModeInProgress()) {
- return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED);
+ if (smsStartedInScbm) {
+ // When the device is in SCBM and emergency SMS is being sent,
+ // completes the future immediately.
+ if (!exitScbmInOtherPhone) {
+ // The emergency SMS is allowed and returns the success result.
+ return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED);
+ }
+
+ mSmsEmergencyModeFuture = new CompletableFuture<>();
+ } else {
+ // When the emergency mode is already set by the previous emergency call or SMS,
+ // completes the future immediately.
+ if (isInEmergencyMode() && !isEmergencyModeInProgress()) {
+ // The emergency SMS is allowed and returns the success result.
+ return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED);
+ }
+
+ mSmsEmergencyModeFuture = new CompletableFuture<>();
+
+ if (!isInEmergencyMode()) {
+ setEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN,
+ MSG_SET_EMERGENCY_MODE_DONE);
+ }
}
- mSmsEmergencyModeFuture = new CompletableFuture<>();
- if (!isInEmergencyMode()) {
- setEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN,
- MSG_SET_EMERGENCY_MODE_DONE);
- }
return mSmsEmergencyModeFuture;
}
@@ -1230,35 +1250,142 @@
* @param smsId the SMS id on which to end the emergency SMS.
* @param success the flag specifying whether an emergency SMS is successfully sent or not.
* {@code true} if SMS is successfully sent, {@code false} otherwise.
+ * @param domain the domain that MO SMS was sent.
*/
- public void endSms(@NonNull String smsId, boolean success) {
+ public void endSms(@NonNull String smsId, boolean success,
+ @NetworkRegistrationInfo.Domain int domain) {
mOngoingEmergencySmsIds.remove(smsId);
// If the outgoing emergency SMSs are empty, we can try to exit the emergency mode.
if (mOngoingEmergencySmsIds.isEmpty()) {
+ mSmsEmergencyModeFuture = null;
+ mIsEmergencySmsStartedDuringScbm = false;
+
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() && mOngoingConnection == null) {
- setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_CALLBACK,
- MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
+ setEmergencyCallbackMode(mPhone, EMERGENCY_TYPE_CALL);
}
- } else {
- exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, false);
}
- clearEmergencySmsInfo();
+ // If SCBM supports, SCBM will be entered here regardless of ECBM state.
+ if (success && domain == NetworkRegistrationInfo.DOMAIN_PS
+ && (isInScbm() || isEmergencyCallbackModeSupported(mSmsPhone))) {
+ enterEmergencySmsCallbackMode();
+ } else if (isInScbm()) {
+ // Sets the emergency mode to CALLBACK without re-initiating SCBM timer.
+ setEmergencyCallbackMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+ } else {
+ exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+ clearEmergencySmsInfo();
+ }
+ }
+ }
+
+ /**
+ * Called when emergency SMS is received from the network.
+ */
+ public void onEmergencySmsReceived() {
+ if (isInScbm()) {
+ Rlog.d(TAG, "Emergency SMS received, re-initiate SCBM timer");
+ // Reinitiate the SCBM timer when receiving emergency SMS while in SCBM.
+ enterEmergencySmsCallbackMode();
}
}
private void clearEmergencySmsInfo() {
mOngoingEmergencySmsIds.clear();
+ mIsEmergencySmsStartedDuringScbm = false;
mIsTestEmergencyNumberForSms = false;
mSmsEmergencyModeFuture = null;
mSmsPhone = null;
}
/**
+ * Returns {@code true} if currently in emergency SMS callback mode.
+ */
+ public boolean isInScbm() {
+ return mIsInScbm;
+ }
+
+ /**
+ * Sets the emergency SMS callback mode state.
+ *
+ * @param isInScbm {@code true} if currently in emergency SMS callback mode,
+ * {@code false} otherwise.
+ */
+ private void setIsInScbm(boolean isInScbm) {
+ mIsInScbm = isInScbm;
+ }
+
+ /**
+ * Enters the emergency SMS callback mode.
+ */
+ private void enterEmergencySmsCallbackMode() {
+ Rlog.d(TAG, "enter SCBM while " + (isInScbm() ? "in" : "not in") + " SCBM");
+ // Remove pending message if present.
+ mHandler.removeMessages(MSG_EXIT_SCBM);
+
+ if (!isInScbm()) {
+ setIsInScbm(true);
+ }
+
+ setEmergencyCallbackMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+
+ // At the moment, the default SCBM timer value will be used with the same value
+ // that is configured for emergency callback mode.
+ int delayInMillis = Long.valueOf(mEcmExitTimeoutMs).intValue();
+ int subId = mSmsPhone.getSubId();
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ delayInMillis = getConfig(subId,
+ CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT,
+ delayInMillis);
+ if (delayInMillis == 0) {
+ delayInMillis = Long.valueOf(mEcmExitTimeoutMs).intValue();
+ }
+ }
+ // Post the message so we will automatically exit if no one invokes
+ // exitEmergencySmsCallbackModeAndEmergencyMode() directly.
+ mHandler.sendEmptyMessageDelayed(MSG_EXIT_SCBM, delayInMillis);
+ }
+
+ /**
+ * Exits emergency SMS callback mode and emergency mode if the device is in SCBM and
+ * the emergency mode is in CALLBACK.
+ */
+ private void exitEmergencySmsCallbackModeAndEmergencyMode() {
+ Rlog.d(TAG, "exit SCBM and emergency mode");
+ final Phone smsPhone = mSmsPhone;
+ boolean wasInScbm = isInScbm();
+ exitEmergencySmsCallbackMode();
+
+ // The emergency mode needs to be checked to ensure that there is no ongoing emergency SMS.
+ if (wasInScbm && mOngoingEmergencySmsIds.isEmpty()) {
+ // Exit emergency mode on modem.
+ exitEmergencyMode(smsPhone, EMERGENCY_TYPE_SMS);
+ }
+ }
+
+ /**
+ * Exits emergency SMS callback mode.
+ */
+ private void exitEmergencySmsCallbackMode() {
+ // Remove pending message if present.
+ mHandler.removeMessages(MSG_EXIT_SCBM);
+
+ if (isInScbm()) {
+ Rlog.i(TAG, "exit SCBM");
+ setIsInScbm(false);
+ }
+
+ if (mOngoingEmergencySmsIds.isEmpty()) {
+ mIsTestEmergencyNumberForSms = false;
+ mSmsPhone = null;
+ }
+ }
+
+ /**
* Returns {@code true} if any phones from PhoneFactory have radio on.
*/
private boolean isRadioOn() {
@@ -1596,49 +1723,4 @@
Rlog.i(TAG, "updateNoSimEcbmSupported preference updated slotIndex=" + slotIndex
+ ", supported=" + carrierConfig);
}
-
- /** For test purpose only */
- @VisibleForTesting
- public void setPdnDisconnectionTimeoutMs(int timeout) {
- mPdnDisconnectionTimeoutMs = timeout;
- }
-
- private void exitEmergencyModeIfDelayed() {
- synchronized (mLock) {
- if (mPhoneToExit != null) {
- unregisterForDataConnectionStateChanges();
- mPhoneToExit.exitEmergencyMode(
- mHandler.obtainMessage(MSG_EXIT_EMERGENCY_MODE_DONE,
- Integer.valueOf(EMERGENCY_TYPE_CALL)));
- mPhoneToExit = null;
- }
- }
- }
-
- /**
- * Registers for changes to data connection state.
- */
- private void registerForDataConnectionStateChanges(Phone phone) {
- if ((mDataConnectionStateListener != null) || (phone == null)) {
- return;
- }
- Rlog.i(TAG, "registerForDataConnectionStateChanges");
-
- mDataConnectionStateListener = new PreciseDataConnectionStateListener();
- mTelephonyManagerProxy.registerTelephonyCallback(phone.getSubId(),
- mHandler::post, mDataConnectionStateListener);
- }
-
- /**
- * Unregisters for changes to data connection state.
- */
- private void unregisterForDataConnectionStateChanges() {
- if (mDataConnectionStateListener == null) {
- return;
- }
- Rlog.i(TAG, "unregisterForDataConnectionStateChanges");
-
- mTelephonyManagerProxy.unregisterTelephonyCallback(mDataConnectionStateListener);
- mDataConnectionStateListener = null;
- }
}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index e2f95da..18b4b14 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -1331,8 +1331,13 @@
SubscriptionInfo subscriptionInfo =
mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
slot.getPhoneIdFromPortIndex(portIndex));
- if (subscriptionInfo == null || subscriptionInfo.isOpportunistic()) {
- // If the port is active and empty/opportunistic, return the portIndex.
+ if (subscriptionInfo == null
+ || subscriptionInfo.isOpportunistic()
+ || (mFeatureFlags.esimBootstrapProvisioningFlag()
+ && subscriptionInfo.getProfileClass()
+ == SubscriptionManager.PROFILE_CLASS_PROVISIONING)) {
+ // If the port is active and has empty/opportunistic/provisioning
+ // profiles then return the portIndex.
return portIndex;
}
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index dae808a..e5afbeb 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -171,7 +171,7 @@
if(mPhone.getServiceState().getRilDataRadioTechnology()
!= ServiceState.RIL_RADIO_TECHNOLOGY_NR) {
tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE);
- notifySmsSentFailedToEmergencyStateTracker(tracker);
+ notifySmsSentFailedToEmergencyStateTracker(tracker, false);
return;
}
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index b5a052d..dcb3b20 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -3580,14 +3580,13 @@
ImsPhoneConnection conn = findConnection(imsCall);
// Since onCallInitiating and onCallProgressing reset mPendingMO,
// we can't depend on mPendingMO.
- if ((reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
- || reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED
- || reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED)
- && conn != null) {
+ if (conn != null) {
logi("onCallStartFailed eccCategory=" + eccCategory);
- if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
- || reasonInfo.getExtraCode()
- == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY) {
+ int reason = reasonInfo.getCode();
+ int extraCode = reasonInfo.getExtraCode();
+ if ((reason == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+ && extraCode == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY)
+ || (reason == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL)) {
conn.setNonDetectableEmergencyCallInfo(eccCategory);
}
conn.setImsReasonInfo(reasonInfo);
diff --git a/src/java/com/android/internal/telephony/metrics/CellularSecurityTransparencyStats.java b/src/java/com/android/internal/telephony/metrics/CellularSecurityTransparencyStats.java
new file mode 100644
index 0000000..f77474f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/CellularSecurityTransparencyStats.java
@@ -0,0 +1,86 @@
+/*
+ * 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.metrics;
+
+import android.telephony.CellularIdentifierDisclosure;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.telephony.Rlog;
+
+/**
+ * Facilitates writing stats relating to cellular transparency features. Delegates the actual
+ * writing of stats out to {@link TelephonyStatsLog}.
+ */
+public class CellularSecurityTransparencyStats {
+
+ private static final String LOG_TAG = "CellularSecurityTransparencyStats";
+ private static final String LOG_DESCRIPTOR_SIM_MCC = "SIM MCC";
+ private static final String LOG_DESCRIPTOR_SIM_MNC = "SIM MNC";
+ private static final String LOG_DESCRIPTOR_DISCLOSURE_MCC = "disclosure MCC";
+ private static final String LOG_DESCRIPTOR_DISCLOSURE_MNC = "disclosure MNC";
+ private static final int DEFAULT_PLMN_PART = -1;
+
+ /**
+ * Log an identifier disclosure to be written out to {@link TelephonyStatsLog}
+ */
+ public void logIdentifierDisclosure(CellularIdentifierDisclosure disclosure, String simMcc,
+ String simMnc, boolean notificationsEnabled) {
+
+ int mcc = parsePlmnPartOrDefault(simMcc, LOG_DESCRIPTOR_SIM_MCC);
+ int mnc = parsePlmnPartOrDefault(simMnc, LOG_DESCRIPTOR_SIM_MNC);
+
+ int disclosureMcc = DEFAULT_PLMN_PART;
+ int disclosureMnc = DEFAULT_PLMN_PART;
+ String plmn = disclosure.getPlmn();
+ if (plmn != null) {
+ String[] plmnParts = plmn.split("-");
+ if (plmnParts.length == 2) {
+ disclosureMcc = parsePlmnPartOrDefault(plmnParts[0], LOG_DESCRIPTOR_DISCLOSURE_MCC);
+ disclosureMnc = parsePlmnPartOrDefault(plmnParts[1], LOG_DESCRIPTOR_DISCLOSURE_MNC);
+ }
+ }
+
+ writeIdentifierDisclosure(mcc, mnc, disclosureMcc, disclosureMnc,
+ disclosure.getCellularIdentifier(), disclosure.getNasProtocolMessage(),
+ disclosure.isEmergency(), notificationsEnabled);
+
+ }
+
+ private int parsePlmnPartOrDefault(String input, String logDescriptor) {
+ try {
+ return Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ Rlog.d(LOG_TAG, "Failed to parse " + logDescriptor + ": " + input);
+ }
+
+ return DEFAULT_PLMN_PART;
+ }
+
+ /**
+ * Write identifier disclosure data out to {@link TelephonyStatsLog}. This method is split
+ * out to enable testing, since {@link TelephonyStatsLog} is a final static class.
+ */
+ @VisibleForTesting
+ public void writeIdentifierDisclosure(int mcc, int mnc, int disclosureMcc, int disclosureMnc,
+ int identifier, int protocolMessage, boolean isEmergency,
+ boolean areNotificationsEnabled) {
+ TelephonyStatsLog.write(TelephonyStatsLog.CELLULAR_IDENTIFIER_DISCLOSED, mcc, mnc,
+ disclosureMcc, disclosureMnc, identifier, protocolMessage, isEmergency,
+ areNotificationsEnabled);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
index 387495e..cd5b7d6 100644
--- a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -16,12 +16,27 @@
package com.android.internal.telephony.metrics;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;
+
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.CellSignalStrength;
@@ -30,6 +45,7 @@
import android.telephony.TelephonyManager;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataCallResponse.LinkStatus;
+import android.text.TextUtils;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
@@ -38,11 +54,14 @@
import com.android.internal.telephony.data.DataNetworkController;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataStallRecoveryManager;
+import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.telephony.Rlog;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
/**
* Generates metrics related to data stall recovery events per phone ID for the pushed atom.
@@ -57,18 +76,31 @@
private static final String TAG = "DSRS-";
+ private static final int UNSET_DIAGNOSTIC_STATE = -1;
+
+ private static final long REFRESH_DURATION_IN_MILLIS = TimeUnit.MINUTES.toMillis(3);
+
// Handler to upload metrics.
private final @NonNull Handler mHandler;
private final @NonNull String mTag;
private final @NonNull Phone mPhone;
+ private final @NonNull TelephonyManager mTelephonyManager;
+ private final @NonNull FeatureFlags mFeatureFlags;
+
+ // Flag to control the DSRS diagnostics
+ private final boolean mIsDsrsDiagnosticsEnabled;
// The interface name of the internet network.
private @Nullable String mIfaceName = null;
/* Metrics and stats data variables */
+ // Record metrics refresh time in milliseconds to decide whether to refresh data again
+ @ElapsedRealtimeLong
+ private long mMetricsReflashTime = 0L;
private int mPhoneId = 0;
private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+ private int mConvertedMccMnc = -1;
private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
private int mBand = 0;
// The RAT used for data (including IWLAN).
@@ -88,17 +120,33 @@
private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
+ // Connectivity diagnostics states
+ private int mNetworkProbesResult = UNSET_DIAGNOSTIC_STATE;
+ private int mNetworkProbesType = UNSET_DIAGNOSTIC_STATE;
+ private int mNetworkValidationResult = UNSET_DIAGNOSTIC_STATE;
+ private int mTcpMetricsCollectionPeriodMillis = UNSET_DIAGNOSTIC_STATE;
+ private int mTcpPacketFailRate = UNSET_DIAGNOSTIC_STATE;
+ private int mDnsConsecutiveTimeouts = UNSET_DIAGNOSTIC_STATE;
+
+ private ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager = null;
+ private ConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback = null;
+ private static final Executor INLINE_EXECUTOR = x -> x.run();
+
/**
* Constructs a new instance of {@link DataStallRecoveryStats}.
*/
- public DataStallRecoveryStats(@NonNull final Phone phone,
+ public DataStallRecoveryStats(
+ @NonNull final Phone phone,
+ @NonNull FeatureFlags featureFlags,
@NonNull final DataNetworkController dataNetworkController) {
mTag = TAG + phone.getPhoneId();
mPhone = phone;
+ mFeatureFlags = featureFlags;
HandlerThread handlerThread = new HandlerThread(mTag + "-thread");
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper());
+ mTelephonyManager = mPhone.getContext().getSystemService(TelephonyManager.class);
dataNetworkController.registerDataNetworkControllerCallback(
new DataNetworkControllerCallback(mHandler::post) {
@@ -117,6 +165,45 @@
mInternetLinkStatus = status;
}
});
+
+ mIsDsrsDiagnosticsEnabled = mFeatureFlags.dsrsDiagnosticsEnabled();
+ if (mIsDsrsDiagnosticsEnabled) {
+ try {
+ // Register ConnectivityDiagnosticsCallback to get diagnostics states
+ mConnectivityDiagnosticsManager =
+ mPhone.getContext().getSystemService(ConnectivityDiagnosticsManager.class);
+ mConnectivityDiagnosticsCallback = new ConnectivityDiagnosticsCallback() {
+ @Override
+ public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
+ PersistableBundle bundle = report.getAdditionalInfo();
+ mNetworkProbesResult = bundle.getInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK);
+ mNetworkProbesType = bundle.getInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK);
+ mNetworkValidationResult = bundle.getInt(KEY_NETWORK_VALIDATION_RESULT);
+ }
+
+ @Override
+ public void onDataStallSuspected(@NonNull DataStallReport report) {
+ PersistableBundle bundle = report.getStallDetails();
+ mTcpMetricsCollectionPeriodMillis =
+ bundle.getInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS);
+ mTcpPacketFailRate = bundle.getInt(KEY_TCP_PACKET_FAIL_RATE);
+ mDnsConsecutiveTimeouts = bundle.getInt(KEY_DNS_CONSECUTIVE_TIMEOUTS);
+ }
+ };
+ mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback(
+ new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .build(),
+ INLINE_EXECUTOR,
+ mConnectivityDiagnosticsCallback
+ );
+ } catch (Exception e) {
+ mConnectivityDiagnosticsManager = null;
+ mConnectivityDiagnosticsCallback = null;
+ }
+ }
}
/**
@@ -194,10 +281,26 @@
*/
private void refreshMetricsData() {
logd("Refreshes the metrics data.");
+ // Update the metrics reflash time
+ mMetricsReflashTime = SystemClock.elapsedRealtime();
// Update phone id/carrier id and signal strength
mPhoneId = mPhone.getPhoneId() + 1;
mCarrierId = mPhone.getCarrierId();
mSignalStrength = mPhone.getSignalStrength().getLevel();
+ if (mIsDsrsDiagnosticsEnabled) {
+ // Get the MCCMNC and convert it to an int
+ String networkOperator = mTelephonyManager.getNetworkOperator();
+ if (!TextUtils.isEmpty(networkOperator)) {
+ try {
+ mConvertedMccMnc = Integer.parseInt(networkOperator);
+ } catch (NumberFormatException e) {
+ loge("Invalid MCCMNC format: " + networkOperator);
+ mConvertedMccMnc = -1;
+ }
+ } else {
+ mConvertedMccMnc = -1;
+ }
+ }
// Update the bandwidth.
updateBandwidths();
@@ -308,7 +411,8 @@
* @param isRecovered Whether the data stall has been recovered.
* @param duration The duration from data stall occurred in milliseconds.
* @param reason The reason for the recovery.
- * @param isFirstValidation Whether this is the first validation after recovery.
+ * @param validationCount The total number of validation duration a data stall.
+ * @param actionValidationCount The number of validation for current action during a data stall
* @param durationOfAction The duration of the current action in milliseconds.
*/
public Bundle getDataStallRecoveryMetricsData(
@@ -316,28 +420,75 @@
boolean isRecovered,
int duration,
@DataStallRecoveryManager.RecoveredReason int reason,
- boolean isFirstValidation,
+ int validationCount,
+ int actionValidationCount,
int durationOfAction) {
+
+ if (mIsDsrsDiagnosticsEnabled) {
+ // Refresh data if the data has not been updated within 3 minutes
+ final long refreshDuration = SystemClock.elapsedRealtime() - mMetricsReflashTime;
+ if (refreshDuration > REFRESH_DURATION_IN_MILLIS) {
+ // Refreshes the metrics data.
+ try {
+ refreshMetricsData();
+ } catch (Exception e) {
+ loge("The metrics data cannot be refreshed.", e);
+ }
+ }
+ }
+
Bundle bundle = new Bundle();
- bundle.putInt("Action", action);
- bundle.putBoolean("IsRecovered", isRecovered);
- bundle.putInt("Duration", duration);
- bundle.putInt("Reason", reason);
- bundle.putBoolean("IsFirstValidation", isFirstValidation);
- bundle.putInt("DurationOfAction", durationOfAction);
- bundle.putInt("PhoneId", mPhoneId);
- bundle.putInt("CarrierId", mCarrierId);
- bundle.putInt("SignalStrength", mSignalStrength);
- bundle.putInt("Band", mBand);
- bundle.putInt("Rat", mRat);
- bundle.putBoolean("IsOpportunistic", mIsOpportunistic);
- bundle.putBoolean("IsMultiSim", mIsMultiSim);
- bundle.putInt("NetworkRegState", mNetworkRegState);
- bundle.putInt("OtherSignalStrength", mOtherSignalStrength);
- bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState);
- bundle.putInt("InternetLinkStatus", mInternetLinkStatus);
- bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps);
- bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps);
+
+ if (mIsDsrsDiagnosticsEnabled) {
+ bundle.putInt("Action", action);
+ bundle.putInt("IsRecovered", isRecovered ? 1 : 0);
+ bundle.putInt("Duration", duration);
+ bundle.putInt("Reason", reason);
+ bundle.putInt("DurationOfAction", durationOfAction);
+ bundle.putInt("ValidationCount", validationCount);
+ bundle.putInt("ActionValidationCount", actionValidationCount);
+ bundle.putInt("PhoneId", mPhoneId);
+ bundle.putInt("CarrierId", mCarrierId);
+ bundle.putInt("MccMnc", mConvertedMccMnc);
+ bundle.putInt("SignalStrength", mSignalStrength);
+ bundle.putInt("Band", mBand);
+ bundle.putInt("Rat", mRat);
+ bundle.putInt("IsOpportunistic", mIsOpportunistic ? 1 : 0);
+ bundle.putInt("IsMultiSim", mIsMultiSim ? 1 : 0);
+ bundle.putInt("NetworkRegState", mNetworkRegState);
+ bundle.putInt("OtherSignalStrength", mOtherSignalStrength);
+ bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState);
+ bundle.putInt("InternetLinkStatus", mInternetLinkStatus);
+ bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps);
+ bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps);
+ bundle.putInt("NetworkProbesResult", mNetworkProbesResult);
+ bundle.putInt("NetworkProbesType", mNetworkProbesType);
+ bundle.putInt("NetworkValidationResult", mNetworkValidationResult);
+ bundle.putInt("TcpMetricsCollectionPeriodMillis", mTcpMetricsCollectionPeriodMillis);
+ bundle.putInt("TcpPacketFailRate", mTcpPacketFailRate);
+ bundle.putInt("DnsConsecutiveTimeouts", mDnsConsecutiveTimeouts);
+ } else {
+ bundle.putInt("Action", action);
+ bundle.putBoolean("IsRecovered", isRecovered);
+ bundle.putInt("Duration", duration);
+ bundle.putInt("Reason", reason);
+ bundle.putBoolean("IsFirstValidation", validationCount == 1);
+ bundle.putInt("DurationOfAction", durationOfAction);
+ bundle.putInt("PhoneId", mPhoneId);
+ bundle.putInt("CarrierId", mCarrierId);
+ bundle.putInt("SignalStrength", mSignalStrength);
+ bundle.putInt("Band", mBand);
+ bundle.putInt("Rat", mRat);
+ bundle.putBoolean("IsOpportunistic", mIsOpportunistic);
+ bundle.putBoolean("IsMultiSim", mIsMultiSim);
+ bundle.putInt("NetworkRegState", mNetworkRegState);
+ bundle.putInt("OtherSignalStrength", mOtherSignalStrength);
+ bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState);
+ bundle.putInt("InternetLinkStatus", mInternetLinkStatus);
+ bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps);
+ bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps);
+ }
+
return bundle;
}
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index a315f1e..856045f 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -138,14 +138,6 @@
private static final long MIN_COOLDOWN_MILLIS =
DBG ? 10L * MILLIS_PER_SECOND : 23L * MILLIS_PER_HOUR;
- /**
- * Sets atom pull cool down to 4 minutes for userdebug build.
- *
- * <p>Applies to certain atoms: CellularServiceState.
- */
- private static final long CELL_SERVICE_MIN_COOLDOWN_MILLIS =
- DBG ? 10L * MILLIS_PER_SECOND :
- IS_DEBUGGABLE ? 4L * MILLIS_PER_MINUTE : 23L * MILLIS_PER_HOUR;
/**
* Buckets with less than these many calls will be dropped.
@@ -166,6 +158,13 @@
private static final long CELL_SERVICE_DURATION_BUCKET_MILLIS =
DBG || IS_DEBUGGABLE ? 2L * MILLIS_PER_SECOND : 5L * MILLIS_PER_MINUTE;
+ /**
+ * Sets atom pull cool down to 4 minutes for userdebug build, 5 hours for user build.
+ *
+ * <p>Applies to certain atoms: CellularServiceState, DataCallSession,
+ * ImsRegistrationTermination.
+ */
+ private final long mPowerCorrelatedMinCooldownMillis;
private final PersistAtomsStorage mStorage;
private final DeviceStateHelper mDeviceStateHelper;
private final StatsManager mStatsManager;
@@ -234,6 +233,9 @@
mAirplaneModeStats = new AirplaneModeStats(context);
mDefaultNetworkMonitor = new DefaultNetworkMonitor(context, featureFlags);
+ mPowerCorrelatedMinCooldownMillis = DBG ? 10L * MILLIS_PER_SECOND :
+ IS_DEBUGGABLE ? 4L * MILLIS_PER_MINUTE : (long) context.getResources().getInteger(
+ com.android.internal.R.integer.config_metrics_pull_cooldown_millis);
}
/**
@@ -516,7 +518,8 @@
private int pullDataCallSession(List<StatsEvent> data) {
// Include ongoing data call segments
concludeDataCallSessionStats();
- DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS);
+ DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(
+ mPowerCorrelatedMinCooldownMillis);
if (dataCallSessions != null) {
Arrays.stream(dataCallSessions)
.forEach(dataCall -> data.add(buildStatsEvent(dataCall)));
@@ -544,8 +547,8 @@
private int pullCellularServiceState(List<StatsEvent> data) {
// Include the latest durations
concludeServiceStateStats();
- CellularServiceState[] persistAtoms =
- mStorage.getCellularServiceStates(CELL_SERVICE_MIN_COOLDOWN_MILLIS);
+ CellularServiceState[] persistAtoms = mStorage.getCellularServiceStates(
+ mPowerCorrelatedMinCooldownMillis);
if (persistAtoms != null) {
// list is already shuffled when instances were inserted
Arrays.stream(persistAtoms)
@@ -573,8 +576,8 @@
}
private int pullImsRegistrationTermination(List<StatsEvent> data) {
- ImsRegistrationTermination[] persistAtoms =
- mStorage.getImsRegistrationTerminations(MIN_COOLDOWN_MILLIS);
+ ImsRegistrationTermination[] persistAtoms = mStorage.getImsRegistrationTerminations(
+ mPowerCorrelatedMinCooldownMillis);
if (persistAtoms != null) {
// list is already shuffled when instances were inserted
Arrays.stream(persistAtoms)
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 55db6d2..92d9372 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -93,6 +93,7 @@
import android.telephony.satellite.SatelliteDatagram;
import android.telephony.satellite.SatelliteManager;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.uwb.UwbManager;
@@ -2518,6 +2519,12 @@
logd("getSatellitePlmnsForCarrier: carrierEnabledSatelliteFlag is disabled");
return new ArrayList<>();
}
+
+ if (!isSatelliteSupportedViaCarrier(subId)) {
+ logd("Satellite for carrier is not supported.");
+ return new ArrayList<>();
+ }
+
synchronized (mSupportedSatelliteServicesLock) {
return mMergedPlmnListPerCarrier.get(subId, new ArrayList<>()).stream().toList();
}
@@ -2596,21 +2603,21 @@
}
/**
- * @return {@code true} if any subscription on the device is connected to satellite,
- * {@code false} otherwise.
+ * @return {@code Pair<true, subscription ID>} if any subscription on the device is connected to
+ * satellite, {@code Pair<false, null>} otherwise.
*/
- private boolean isUsingNonTerrestrialNetworkViaCarrier() {
+ private Pair<Boolean, Integer> isUsingNonTerrestrialNetworkViaCarrier() {
if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
logd("isUsingNonTerrestrialNetwork: carrierEnabledSatelliteFlag is disabled");
- return false;
+ return new Pair<>(false, null);
}
for (Phone phone : PhoneFactory.getPhones()) {
ServiceState serviceState = phone.getServiceState();
if (serviceState != null && serviceState.isUsingNonTerrestrialNetwork()) {
- return true;
+ return new Pair<>(true, phone.getSubId());
}
}
- return false;
+ return new Pair<>(false, null);
}
/**
@@ -2624,7 +2631,7 @@
+ " is disabled");
return false;
}
- if (isUsingNonTerrestrialNetworkViaCarrier()) {
+ if (isUsingNonTerrestrialNetworkViaCarrier().first) {
return true;
}
for (Phone phone : PhoneFactory.getPhones()) {
@@ -2872,6 +2879,7 @@
* @return true if satellite is provisioned on the given subscription else return false.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @Nullable
protected Boolean isSatelliteViaOemProvisioned() {
synchronized (mSatelliteViaOemProvisionLock) {
if (mOverriddenIsSatelliteViaOemProvisioned != null) {
@@ -3995,7 +4003,13 @@
}
private void determineSystemNotification() {
- if (isUsingNonTerrestrialNetworkViaCarrier()) {
+ if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+ logd("determineSystemNotification: carrierEnabledSatelliteFlag is disabled");
+ return;
+ }
+
+ Pair<Boolean, Integer> isNtn = isUsingNonTerrestrialNetworkViaCarrier();
+ if (isNtn.first) {
if (mSharedPreferences == null) {
try {
mSharedPreferences = mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
@@ -4005,18 +4019,18 @@
}
}
if (mSharedPreferences == null) {
- loge("handleEventServiceStateChanged: Cannot get default shared preferences");
+ loge("determineSystemNotification: Cannot get default shared preferences");
return;
}
if (!mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false)) {
- showSatelliteSystemNotification();
+ showSatelliteSystemNotification(isNtn.second);
mSharedPreferences.edit().putBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY,
true).apply();
}
}
}
- private void showSatelliteSystemNotification() {
+ private void showSatelliteSystemNotification(int subId) {
logd("showSatelliteSystemNotification");
final NotificationChannel notificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
@@ -4054,6 +4068,7 @@
// Add action to invoke Satellite setting activity in Settings.
Intent intentSatelliteSetting = new Intent(ACTION_SATELLITE_SETTING);
+ intentSatelliteSetting.putExtra("sub_id", subId);
PendingIntent pendingIntentSatelliteSetting = PendingIntent.getActivity(mContext, 0,
intentSatelliteSetting, PendingIntent.FLAG_IMMUTABLE);
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index 149b054..c491476 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -309,8 +309,14 @@
}
}
- private boolean isSatelliteViaOemAvailable() {
- return mSatelliteController.isSatelliteViaOemProvisioned();
+ /**
+ * Check if satellite is available via OEM
+ * @return {@code true} if satellite is provisioned via OEM else return {@code false}
+ */
+ @VisibleForTesting
+ public boolean isSatelliteViaOemAvailable() {
+ Boolean satelliteProvisioned = mSatelliteController.isSatelliteViaOemProvisioned();
+ return satelliteProvisioned != null ? satelliteProvisioned : false;
}
private boolean isSatelliteViaCarrierAvailable() {
diff --git a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
index 4540b8a..8591e86 100644
--- a/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
+++ b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
@@ -21,6 +21,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.CellularSecurityTransparencyStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.telephony.Rlog;
import java.time.Instant;
@@ -62,13 +65,13 @@
// This object should only be accessed from within the thread of mSerializedWorkQueue. Access
// outside of that thread would require additional synchronization.
private Map<Integer, DisclosureWindow> mWindows;
+ private SubscriptionManagerService mSubscriptionManagerService;
+ private CellularSecurityTransparencyStats mCellularSecurityTransparencyStats;
public CellularIdentifierDisclosureNotifier(CellularNetworkSecuritySafetySource safetySource) {
- this(
- Executors.newSingleThreadScheduledExecutor(),
- DEFAULT_WINDOW_CLOSE_DURATION_IN_MINUTES,
- TimeUnit.MINUTES,
- safetySource);
+ this(Executors.newSingleThreadScheduledExecutor(), DEFAULT_WINDOW_CLOSE_DURATION_IN_MINUTES,
+ TimeUnit.MINUTES, safetySource, SubscriptionManagerService.getInstance(),
+ new CellularSecurityTransparencyStats());
}
/**
@@ -83,12 +86,16 @@
ScheduledExecutorService notificationQueue,
long windowCloseDuration,
TimeUnit windowCloseUnit,
- CellularNetworkSecuritySafetySource safetySource) {
+ CellularNetworkSecuritySafetySource safetySource,
+ SubscriptionManagerService subscriptionManagerService,
+ CellularSecurityTransparencyStats cellularSecurityTransparencyStats) {
mSerializedWorkQueue = notificationQueue;
mWindowCloseDuration = windowCloseDuration;
mWindowCloseUnit = windowCloseUnit;
mWindows = new HashMap<>();
mSafetySource = safetySource;
+ mSubscriptionManagerService = subscriptionManagerService;
+ mCellularSecurityTransparencyStats = cellularSecurityTransparencyStats;
}
/**
@@ -98,6 +105,8 @@
public void addDisclosure(Context context, int subId, CellularIdentifierDisclosure disclosure) {
Rlog.d(TAG, "Identifier disclosure reported: " + disclosure);
+ logDisclosure(subId, disclosure);
+
synchronized (mEnabledLock) {
if (!mEnabled) {
Rlog.d(TAG, "Skipping disclosure because notifier was disabled.");
@@ -123,6 +132,31 @@
} // end mEnabledLock
}
+ private void logDisclosure(int subId, CellularIdentifierDisclosure disclosure) {
+ try {
+ mSerializedWorkQueue.execute(runLogDisclosure(subId, disclosure));
+ } catch (RejectedExecutionException e) {
+ Rlog.e(TAG, "Failed to schedule runLogDisclosure: " + e.getMessage());
+ }
+ }
+
+ private Runnable runLogDisclosure(int subId,
+ CellularIdentifierDisclosure disclosure) {
+ return () -> {
+ SubscriptionInfoInternal subInfo =
+ mSubscriptionManagerService.getSubscriptionInfoInternal(subId);
+ String mcc = null;
+ String mnc = null;
+ if (subInfo != null) {
+ mcc = subInfo.getMcc();
+ mnc = subInfo.getMnc();
+ }
+
+ mCellularSecurityTransparencyStats.logIdentifierDisclosure(disclosure, mcc, mnc,
+ isEnabled());
+ };
+ }
+
/**
* Re-enable if previously disabled. This means that {@code addDisclsoure} will start tracking
* disclosures again and potentially emitting notifications.
diff --git a/src/java/com/android/internal/telephony/security/NullCipherNotifier.java b/src/java/com/android/internal/telephony/security/NullCipherNotifier.java
index 0ab8299..3ece701 100644
--- a/src/java/com/android/internal/telephony/security/NullCipherNotifier.java
+++ b/src/java/com/android/internal/telephony/security/NullCipherNotifier.java
@@ -16,10 +16,29 @@
package com.android.internal.telephony.security;
-import android.telephony.SecurityAlgorithmUpdate;
+import static android.telephony.SecurityAlgorithmUpdate.SECURITY_ALGORITHM_UNKNOWN;
+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 android.annotation.IntDef;
+import android.content.Context;
+import android.telephony.SecurityAlgorithmUpdate;
+import android.telephony.SecurityAlgorithmUpdate.ConnectionEvent;
+import android.telephony.SecurityAlgorithmUpdate.SecurityAlgorithm;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.telephony.Rlog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+
/**
* Encapsulates logic to emit notifications to the user that a null cipher is in use. A null cipher
* is one that does not attempt to implement any encryption.
@@ -27,44 +46,84 @@
* <p>This class will either emit notifications through SafetyCenterManager if SafetyCenter exists
* on a device, or it will emit system notifications otherwise.
*
+ * TODO(b/319662115): handle radio availability, no service, SIM removal, etc.
+ *
* @hide
*/
public class NullCipherNotifier {
-
private static final String TAG = "NullCipherNotifier";
private static NullCipherNotifier sInstance;
+ private final CellularNetworkSecuritySafetySource mSafetySource;
+ private final HashMap<Integer, SubscriptionState> mSubscriptionState = new HashMap<>();
+
+ private final Object mEnabledLock = new Object();
+ @GuardedBy("mEnabledLock")
private boolean mEnabled = false;
+ // This is a single threaded executor. This is important because we want to ensure certain
+ // events are strictly serialized.
+ private ScheduledExecutorService mSerializedWorkQueue;
+
/**
* Gets a singleton NullCipherNotifier.
*/
- public static synchronized NullCipherNotifier getInstance() {
+ public static synchronized NullCipherNotifier getInstance(
+ CellularNetworkSecuritySafetySource safetySource) {
if (sInstance == null) {
- sInstance = new NullCipherNotifier();
+ sInstance = new NullCipherNotifier(
+ Executors.newSingleThreadScheduledExecutor(),
+ safetySource);
}
return sInstance;
}
- private NullCipherNotifier() {}
+ @VisibleForTesting
+ public NullCipherNotifier(
+ ScheduledExecutorService notificationQueue,
+ CellularNetworkSecuritySafetySource safetySource) {
+ mSerializedWorkQueue = notificationQueue;
+ mSafetySource = safetySource;
+ }
/**
* Adds a security algorithm update. If appropriate, this will trigger a user notification.
*/
- public void onSecurityAlgorithmUpdate(int phoneId, SecurityAlgorithmUpdate update) {
- // TODO (b/315005938) this is a stub method for now. Logic
- // for tracking disclosures and emitting notifications will flow
- // from here.
- Rlog.d(TAG, "Security algorithm update: phoneId = " + phoneId + " " + update);
+ public void onSecurityAlgorithmUpdate(
+ Context context, int subId, SecurityAlgorithmUpdate update) {
+ Rlog.d(TAG, "Security algorithm update: subId = " + subId + " " + update);
+
+ if (shouldIgnoreUpdate(update)) {
+ return;
+ }
+
+ try {
+ mSerializedWorkQueue.execute(() -> {
+ SubscriptionState subState = mSubscriptionState.get(subId);
+ if (subState == null) {
+ subState = new SubscriptionState();
+ mSubscriptionState.put(subId, subState);
+ }
+
+ @CellularNetworkSecuritySafetySource.NullCipherState int nullCipherState =
+ subState.update(update);
+ mSafetySource.setNullCipherState(context, subId, nullCipherState);
+ });
+ } catch (RejectedExecutionException e) {
+ Rlog.e(TAG, "Failed to schedule onEnableNotifier: " + e.getMessage());
+ }
}
/**
* Enables null cipher notification; {@code onSecurityAlgorithmUpdate} will start handling
* security algorithm updates and send notifications to the user when required.
*/
- public void enable() {
- Rlog.d(TAG, "enabled");
- mEnabled = true;
+ public void enable(Context context) {
+ synchronized (mEnabledLock) {
+ Rlog.d(TAG, "enabled");
+ mEnabled = true;
+ scheduleOnEnabled(context, true);
+ }
}
/**
@@ -73,12 +132,180 @@
* {@code onSecurityAlgorithmUpdate} is called while in a disabled state, security algorithm
* updates will be dropped.
*/
- public void disable() {
- Rlog.d(TAG, "disabled");
- mEnabled = false;
+ public void disable(Context context) {
+ synchronized (mEnabledLock) {
+ Rlog.d(TAG, "disabled");
+ mEnabled = false;
+ scheduleOnEnabled(context, false);
+ }
}
+ /** Checks whether the null cipher notification is enabled. */
public boolean isEnabled() {
- return mEnabled;
+ synchronized (mEnabledLock) {
+ return mEnabled;
+ }
}
+
+ private void scheduleOnEnabled(Context context, boolean enabled) {
+ try {
+ mSerializedWorkQueue.execute(() -> {
+ Rlog.i(TAG, "On enable notifier. Enable value: " + enabled);
+ mSafetySource.setNullCipherIssueEnabled(context, enabled);
+ });
+ } catch (RejectedExecutionException e) {
+ Rlog.e(TAG, "Failed to schedule onEnableNotifier: " + e.getMessage());
+ }
+
+ }
+
+ /** Returns whether the update should be dropped and the monitoring state left unchanged. */
+ private static boolean shouldIgnoreUpdate(SecurityAlgorithmUpdate update) {
+ // Ignore emergencies.
+ if (update.isUnprotectedEmergency()) {
+ return true;
+ }
+
+ switch (update.getConnectionEvent()) {
+ // Ignore non-link layer protocols. Monitoring is only looking for data exposed
+ // over-the-air so only the link layer protocols are tracked. Higher-level protocols can
+ // protect data further into the network but that is out of scope.
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_SIP:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_RTP:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_VONR_SIP:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_VONR_RTP:
+ // Ignore emergencies.
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_SIP_SOS:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_RTP_SOS:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_VONR_SIP_SOS:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_VONR_RTP_SOS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Determines whether an algorithm does not attempt to implement any encryption and is therefore
+ * considered a null cipher.
+ *
+ * <p>Only the algorithms known to be null ciphers are classified as such. Explicitly unknown
+ * algorithms, or algorithms that are unknown by means of values added to newer HALs, are
+ * assumed not to be null ciphers.
+ */
+ private static boolean isNullCipher(@SecurityAlgorithm int algorithm) {
+ switch (algorithm) {
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_A50:
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_GEA0:
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_UEA0:
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA0:
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_NEA0:
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_IMS_NULL:
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_SIP_NULL:
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_SRTP_NULL:
+ case SecurityAlgorithmUpdate.SECURITY_ALGORITHM_OTHER:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /** The state of network connections for a subscription. */
+ private static final class SubscriptionState {
+ private @NetworkClass int mActiveNetworkClass = NETWORK_CLASS_UNKNOWN;
+ private final HashMap<Integer, ConnectionState> mState = new HashMap<>();
+
+ private @CellularNetworkSecuritySafetySource.NullCipherState int
+ update(SecurityAlgorithmUpdate update) {
+ boolean fromNullCipherState = hasNullCipher();
+
+ @NetworkClass int networkClass = getNetworkClass(update.getConnectionEvent());
+ if (networkClass != mActiveNetworkClass || networkClass == NETWORK_CLASS_UNKNOWN) {
+ mState.clear();
+ mActiveNetworkClass = networkClass;
+ }
+
+ ConnectionState fromState =
+ mState.getOrDefault(update.getConnectionEvent(), ConnectionState.UNKNOWN);
+ ConnectionState toState = new ConnectionState(
+ update.getEncryption() == SECURITY_ALGORITHM_UNKNOWN
+ ? fromState.getEncryption()
+ : update.getEncryption(),
+ update.getIntegrity() == SECURITY_ALGORITHM_UNKNOWN
+ ? fromState.getIntegrity()
+ : update.getIntegrity());
+ mState.put(update.getConnectionEvent(), toState);
+
+ if (hasNullCipher()) {
+ return NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED;
+ }
+ if (!fromNullCipherState || mActiveNetworkClass == NETWORK_CLASS_UNKNOWN) {
+ return NULL_CIPHER_STATE_ENCRYPTED;
+ }
+ return NULL_CIPHER_STATE_NOTIFY_ENCRYPTED;
+ }
+
+ private boolean hasNullCipher() {
+ return mState.values().stream().anyMatch(ConnectionState::hasNullCipher);
+ }
+
+ private static final int NETWORK_CLASS_UNKNOWN = 0;
+ private static final int NETWORK_CLASS_2G = 2;
+ private static final int NETWORK_CLASS_3G = 3;
+ private static final int NETWORK_CLASS_4G = 4;
+ private static final int NETWORK_CLASS_5G = 5;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"NETWORK_CLASS_"}, value = {NETWORK_CLASS_UNKNOWN,
+ NETWORK_CLASS_2G, NETWORK_CLASS_3G, NETWORK_CLASS_4G,
+ NETWORK_CLASS_5G})
+ private @interface NetworkClass {}
+
+ private static @NetworkClass int getNetworkClass(
+ @ConnectionEvent int connectionEvent) {
+ switch (connectionEvent) {
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_CS_SIGNALLING_GSM:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_PS_SIGNALLING_GPRS:
+ return NETWORK_CLASS_2G;
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_CS_SIGNALLING_3G:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_PS_SIGNALLING_3G:
+ return NETWORK_CLASS_3G;
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_NAS_SIGNALLING_LTE:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_LTE:
+ return NETWORK_CLASS_4G;
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_NAS_SIGNALLING_5G:
+ case SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_5G:
+ return NETWORK_CLASS_5G;
+ default:
+ return NETWORK_CLASS_UNKNOWN;
+ }
+ }
+ }
+
+ /** The state of security algorithms for a network connection. */
+ private static final class ConnectionState {
+ private static final ConnectionState UNKNOWN =
+ new ConnectionState(SECURITY_ALGORITHM_UNKNOWN, SECURITY_ALGORITHM_UNKNOWN);
+
+ private final @SecurityAlgorithm int mEncryption;
+ private final @SecurityAlgorithm int mIntegrity;
+
+ private ConnectionState(
+ @SecurityAlgorithm int encryption, @SecurityAlgorithm int integrity) {
+ mEncryption = encryption;
+ mIntegrity = integrity;
+ }
+
+ private @SecurityAlgorithm int getEncryption() {
+ return mEncryption;
+ }
+
+ private @SecurityAlgorithm int getIntegrity() {
+ return mIntegrity;
+ }
+
+ private boolean hasNullCipher() {
+ return isNullCipher(mEncryption) || isNullCipher(mIntegrity);
+ }
+ };
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 0457971..a22f075 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -1290,6 +1290,11 @@
return -1;
}
+ if (mUiccApplications[index] == null) {
+ loge("App index " + index + " is invalid");
+ return -1;
+ }
+
if (mUiccApplications[index].getType() != expectedAppType
&& mUiccApplications[index].getType() != altExpectedAppType) {
loge("App index " + index + " is invalid since it's not "
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index e493a18..4fce070 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -3014,7 +3014,8 @@
new AsyncResult(null, update, null)));
processAllMessages();
- verify(mNullCipherNotifier, times(1)).onSecurityAlgorithmUpdate(eq(0), eq(update));
+ verify(mNullCipherNotifier, times(1))
+ .onSecurityAlgorithmUpdate(eq(mContext), eq(0), eq(update));
}
@Test
@@ -3067,7 +3068,7 @@
setNullCipherNotificationPreferenceEnabled(true);
phoneUT.handleNullCipherNotificationPreferenceChanged();
- verify(mNullCipherNotifier, times(1)).enable();
+ verify(mNullCipherNotifier, times(1)).enable(eq(mContext));
verify(mMockCi, times(1)).setSecurityAlgorithmsUpdatedEnabled(eq(true),
any(Message.class));
}
@@ -3081,7 +3082,7 @@
setNullCipherNotificationPreferenceEnabled(false);
phoneUT.handleNullCipherNotificationPreferenceChanged();
- verify(mNullCipherNotifier, times(1)).disable();
+ verify(mNullCipherNotifier, times(1)).disable(eq(mContext));
verify(mMockCi, times(1)).setSecurityAlgorithmsUpdatedEnabled(eq(false),
any(Message.class));
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
index 776715c..41fd45a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
@@ -16,10 +16,12 @@
package com.android.internal.telephony;
+import android.content.pm.PackageManager;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.telephony.gsm.SmsMessage;
import com.android.internal.util.HexDump;
@@ -28,6 +30,12 @@
public class GsmSmsTest extends AndroidTestCase {
+ private boolean hasMessaging() {
+ final PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext()
+ .getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING);
+ }
+
@SmallTest
public void testAddressing() throws Exception {
String pdu = "07914151551512f2040B916105551511f100006060605130308A04D4F29C0E";
@@ -258,8 +266,8 @@
@SmallTest
public void testFragmentText() throws Exception {
- boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() ==
- TelephonyManager.PHONE_TYPE_GSM);
+ boolean isGsmPhoneWithMessaging = (TelephonyManager.getDefault().getPhoneType()
+ == TelephonyManager.PHONE_TYPE_GSM) && hasMessaging();
// Valid 160 character 7-bit text.
String text = "123456789012345678901234567890123456789012345678901234567890" +
@@ -271,7 +279,7 @@
assertEquals(1, ted.codeUnitSize);
assertEquals(0, ted.languageTable);
assertEquals(0, ted.languageShiftTable);
- if (isGsmPhone) {
+ if (isGsmPhoneWithMessaging) {
ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
assertEquals(1, fragments.size());
}
@@ -286,7 +294,7 @@
assertEquals(1, ted.codeUnitSize);
assertEquals(0, ted.languageTable);
assertEquals(0, ted.languageShiftTable);
- if (isGsmPhone) {
+ if (isGsmPhoneWithMessaging) {
ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
assertEquals(2, fragments.size());
assertEquals(text, fragments.get(0) + fragments.get(1));
@@ -297,8 +305,8 @@
@SmallTest
public void testFragmentTurkishText() throws Exception {
- boolean isGsmPhone = (TelephonyManager.getDefault().getPhoneType() ==
- TelephonyManager.PHONE_TYPE_GSM);
+ boolean isGsmPhoneWithMessaging = (TelephonyManager.getDefault().getPhoneType()
+ == TelephonyManager.PHONE_TYPE_GSM) && hasMessaging();
int[] oldTables = GsmAlphabet.getEnabledSingleShiftTables();
int[] turkishTable = { 1 };
@@ -313,7 +321,7 @@
assertEquals(1, ted.codeUnitSize);
assertEquals(0, ted.languageTable);
assertEquals(1, ted.languageShiftTable);
- if (isGsmPhone) {
+ if (isGsmPhoneWithMessaging) {
ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
assertEquals(1, fragments.size());
assertEquals(text, fragments.get(0));
@@ -329,7 +337,7 @@
assertEquals(1, ted.codeUnitSize);
assertEquals(0, ted.languageTable);
assertEquals(1, ted.languageShiftTable);
- if (isGsmPhone) {
+ if (isGsmPhoneWithMessaging) {
ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
assertEquals(2, fragments.size());
assertEquals(text, fragments.get(0) + fragments.get(1));
@@ -347,7 +355,7 @@
assertEquals(1, ted.codeUnitSize);
assertEquals(0, ted.languageTable);
assertEquals(1, ted.languageShiftTable);
- if (isGsmPhone) {
+ if (isGsmPhoneWithMessaging) {
ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text);
assertEquals(3, fragments.size());
assertEquals(text, fragments.get(0) + fragments.get(1) + fragments.get(2));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index 4c68e26..a7e9604 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -255,7 +255,7 @@
// Capture listener to emulate the carrier config change notification used later
ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
- mMultiSimSettingControllerUT = new MultiSimSettingController(mContext);
+ mMultiSimSettingControllerUT = new MultiSimSettingController(mContext, mFeatureFlags);
processAllMessages();
verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
listenerArgumentCaptor.capture());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index 1ea989a..e08abd9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -1250,6 +1250,79 @@
}
@Test
+ public void testPrimaryTimerPrimaryCellChangeNrIdle() throws Exception {
+ doReturn(true).when(mFeatureFlags).supportNrSaRrcIdle();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ ArrayList<PhysicalChannelConfig> physicalChannelConfigs = new ArrayList<>();
+ 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");
+ sendCarrierConfigChanged();
+
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // should trigger 10 second primary timer
+ 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());
+
+ // change PCI during connected_rrc_idle
+ physicalChannelConfigs.add(new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(2)
+ .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_rrc_idle", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // change PCI for the second time during connected_rrc_idle
+ physicalChannelConfigs.add(new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(3)
+ .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());
+
+ // primary timer expires
+ moveTimeForward(10 * 1000);
+ processAllMessages();
+
+ assertEquals("connected", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
+ }
+
+ @Test
public void testSecondaryTimerExpire() throws Exception {
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
@@ -1477,7 +1550,7 @@
mNetworkTypeController.getOverrideNetworkType());
assertTrue(mNetworkTypeController.areAnyTimersActive());
- // switch to connected_rrc_idle before primary timer expires
+ // empty PCC, switch to connected_rrc_idle before primary timer expires
physicalChannelConfigs.clear();
mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
new AsyncResult(null, physicalChannelConfigs, null));
@@ -1498,16 +1571,40 @@
mNetworkTypeController.getOverrideNetworkType());
assertTrue(mNetworkTypeController.areAnyTimersActive());
- // 5 seconds passed during connected_mmwave -> connected_rrc_idle secondary timer
- moveTimeForward(5 * 1000);
+ // Verify secondary timer is still active after 6 seconds passed during
+ // connected_mmwave -> connected_rrc_idle secondary timer, should still keep the primary
+ // state icon.
+ moveTimeForward((5 + 1) * 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);
+ @Test
+ public void testSecondaryTimerAdvanceBandReduceOnPciChange() throws Exception {
+ // The advance band secondary timer has been running for 6 seconds, 20 - 6 seconds are left.
+ testSecondaryTimerAdvanceBand();
+
+ // PCI changed from 1 to 2 for the first while the timer is running.
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+ new AsyncResult(null, List.of(
+ new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(2)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .build()), null));
+ processAllMessages();
+
+ // Verify the first PCI change is exempted from triggering state change.
+ assertEquals("connected_rrc_idle", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // Verify the timer has been reduced from 20 - 6s(advance band) to 5s(regular).
+ moveTimeForward(5 * 1000);
processAllMessages();
assertEquals("connected_rrc_idle", getCurrentState().getName());
@@ -1695,22 +1792,6 @@
// should trigger 10 second 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
- physicalChannelConfigs.clear();
mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
new AsyncResult(null, physicalChannelConfigs, null));
processAllMessages();
@@ -1745,6 +1826,22 @@
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
assertTrue(mNetworkTypeController.areAnyTimersActive());
+
+ // primary cell changes again
+ physicalChannelConfigs.clear();
+ physicalChannelConfigs.add(new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(3)
+ .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_NSA,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.areAnyTimersActive());
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 3fa4fd1..70e3d6b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -110,13 +110,18 @@
callingPkg, persistMessage, priority, expectMore, validityPeriod, messageId);
}
- public void testNotifySmsSentToEmergencyStateTracker(String destAddr, long messageId) {
- notifySmsSentToEmergencyStateTracker(destAddr, messageId);
+ public void testNotifySmsSentToEmergencyStateTracker(String destAddr, long messageId,
+ boolean isOverIms) {
+ notifySmsSentToEmergencyStateTracker(destAddr, messageId, isOverIms);
}
public void testNotifySmsSentFailedToEmergencyStateTracker(String destAddr,
- long messageId) {
- notifySmsSentFailedToEmergencyStateTracker(destAddr, messageId);
+ long messageId, boolean isOverIms) {
+ notifySmsSentFailedToEmergencyStateTracker(destAddr, messageId, isOverIms);
+ }
+
+ public void testNotifySmsReceivedViaImsToEmergencyStateTracker(String origAddr) {
+ notifySmsReceivedViaImsToEmergencyStateTracker(origAddr);
}
}
@@ -498,15 +503,30 @@
@Test
@SmallTest
- public void testNotifySmsSentToEmergencyStateTracker() throws Exception {
+ public void testNotifySmsSentToEmergencyStateTrackerOnDomainCs() throws Exception {
setUpDomainSelectionEnabled(true);
setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
- mSmsDispatchersController.testNotifySmsSentToEmergencyStateTracker("911", 1L);
+ mSmsDispatchersController.testNotifySmsSentToEmergencyStateTracker("911", 1L, false);
processAllMessages();
verify(mTelephonyManager).isEmergencyNumber(eq("911"));
- verify(mEmergencyStateTracker).endSms(eq("1"), eq(true));
+ verify(mEmergencyStateTracker)
+ .endSms(eq("1"), eq(true), eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifySmsSentToEmergencyStateTrackerOnDomainPs() throws Exception {
+ setUpDomainSelectionEnabled(true);
+ setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
+
+ mSmsDispatchersController.testNotifySmsSentToEmergencyStateTracker("911", 1L, true);
+ processAllMessages();
+
+ verify(mTelephonyManager).isEmergencyNumber(eq("911"));
+ verify(mEmergencyStateTracker)
+ .endSms(eq("1"), eq(true), eq(NetworkRegistrationInfo.DOMAIN_PS));
}
@Test
@@ -515,11 +535,11 @@
setUpDomainSelectionEnabled(true);
setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
- mSmsDispatchersController.testNotifySmsSentToEmergencyStateTracker("1234", 1L);
+ mSmsDispatchersController.testNotifySmsSentToEmergencyStateTracker("1234", 1L, true);
processAllMessages();
verify(mTelephonyManager).isEmergencyNumber(eq("1234"));
- verify(mEmergencyStateTracker, never()).endSms(anyString(), anyBoolean());
+ verify(mEmergencyStateTracker, never()).endSms(anyString(), anyBoolean(), anyInt());
}
@Test
@@ -527,22 +547,37 @@
public void testNotifySmsSentToEmergencyStateTrackerWithoutEmergencyStateTracker()
throws Exception {
setUpDomainSelectionEnabled(true);
- mSmsDispatchersController.testNotifySmsSentToEmergencyStateTracker("911", 1L);
+ mSmsDispatchersController.testNotifySmsSentToEmergencyStateTracker("911", 1L, true);
verify(mTelephonyManager, never()).isEmergencyNumber(anyString());
}
@Test
@SmallTest
- public void testNotifySmsSentFailedToEmergencyStateTracker() throws Exception {
+ public void testNotifySmsSentFailedToEmergencyStateTrackerOnDomainCs() throws Exception {
setUpDomainSelectionEnabled(true);
setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
- mSmsDispatchersController.testNotifySmsSentFailedToEmergencyStateTracker("911", 1L);
+ mSmsDispatchersController.testNotifySmsSentFailedToEmergencyStateTracker("911", 1L, false);
processAllMessages();
verify(mTelephonyManager).isEmergencyNumber(eq("911"));
- verify(mEmergencyStateTracker).endSms(eq("1"), eq(false));
+ verify(mEmergencyStateTracker)
+ .endSms(eq("1"), eq(false), eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifySmsSentFailedToEmergencyStateTrackerOnDomainPs() throws Exception {
+ setUpDomainSelectionEnabled(true);
+ setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
+
+ mSmsDispatchersController.testNotifySmsSentFailedToEmergencyStateTracker("911", 1L, true);
+ processAllMessages();
+
+ verify(mTelephonyManager).isEmergencyNumber(eq("911"));
+ verify(mEmergencyStateTracker)
+ .endSms(eq("1"), eq(false), eq(NetworkRegistrationInfo.DOMAIN_PS));
}
@Test
@@ -552,11 +587,38 @@
setUpDomainSelectionEnabled(true);
setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
- mSmsDispatchersController.testNotifySmsSentFailedToEmergencyStateTracker("1234", 1L);
+ mSmsDispatchersController.testNotifySmsSentFailedToEmergencyStateTracker("1234", 1L, true);
processAllMessages();
verify(mTelephonyManager).isEmergencyNumber(eq("1234"));
- verify(mEmergencyStateTracker, never()).endSms(anyString(), anyBoolean());
+ verify(mEmergencyStateTracker, never()).endSms(anyString(), anyBoolean(), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifySmsReceivedViaImsToEmergencyStateTracker() throws Exception {
+ setUpDomainSelectionEnabled(true);
+ setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
+
+ mSmsDispatchersController.testNotifySmsReceivedViaImsToEmergencyStateTracker("911");
+ processAllMessages();
+
+ verify(mTelephonyManager).isEmergencyNumber(eq("911"));
+ verify(mEmergencyStateTracker).onEmergencySmsReceived();
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifySmsReceivedViaImsToEmergencyStateTrackerWithNonEmergencyNumber()
+ throws Exception {
+ setUpDomainSelectionEnabled(true);
+ setUpEmergencyStateTracker(DisconnectCause.NOT_DISCONNECTED);
+
+ mSmsDispatchersController.testNotifySmsReceivedViaImsToEmergencyStateTracker("1234");
+ processAllMessages();
+
+ verify(mTelephonyManager).isEmergencyNumber(eq("1234"));
+ verify(mEmergencyStateTracker, never()).onEmergencySmsReceived();
}
@Test
@@ -781,7 +843,7 @@
mSmsDispatchersController, mEmergencyStateTracker);
when(mEmergencyStateTracker.startEmergencySms(any(Phone.class), anyString(), anyBoolean()))
.thenReturn(mEmergencySmsFuture);
- doNothing().when(mEmergencyStateTracker).endSms(anyString(), anyBoolean());
+ doNothing().when(mEmergencyStateTracker).endSms(anyString(), anyBoolean(), anyInt());
mEmergencySmsFuture.complete(result);
when(mTelephonyManager.isEmergencyNumber(eq("911"))).thenReturn(true);
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index a6e06e3..673acbc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -577,6 +577,8 @@
mDomainSelectionResolver = Mockito.mock(DomainSelectionResolver.class);
mNullCipherNotifier = Mockito.mock(NullCipherNotifier.class);
+ doReturn(true).when(mFeatureFlags).minimalTelephonyCdmCheck();
+
TelephonyManager.disableServiceHandleCaching();
PropertyInvalidatedCache.disableForTestMode();
// For testing do not allow Log.WTF as it can cause test process to crash
@@ -640,7 +642,7 @@
nullable(CommandsInterface.class), nullable(FeatureFlags.class));
doReturn(mEmergencyNumberTracker).when(mTelephonyComponentFactory)
.makeEmergencyNumberTracker(nullable(Phone.class),
- nullable(CommandsInterface.class));
+ nullable(CommandsInterface.class), any(FeatureFlags.class));
doReturn(getTestEmergencyNumber()).when(mEmergencyNumberTracker)
.getEmergencyNumber(any());
doReturn(mUiccProfile).when(mTelephonyComponentFactory)
@@ -696,7 +698,7 @@
.makeIdentifierDisclosureNotifier(any(CellularNetworkSecuritySafetySource.class));
doReturn(mNullCipherNotifier)
.when(mTelephonyComponentFactory)
- .makeNullCipherNotifier();
+ .makeNullCipherNotifier(any(CellularNetworkSecuritySafetySource.class));
//mPhone
doReturn(mContext).when(mPhone).getContext();
@@ -821,6 +823,12 @@
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
doReturn(true).when(mTelephonyManager).isDataCapable();
+ mContextFixture.addSystemFeature(PackageManager.FEATURE_TELECOM);
+ mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING);
+ mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_DATA);
+ mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_EUICC);
+ mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING);
+
doReturn(TelephonyManager.PHONE_TYPE_GSM).when(mTelephonyManager).getPhoneType();
doReturn(mServiceState).when(mSST).getServiceState();
doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
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 50ee5c8..ddbe9c0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -156,6 +156,7 @@
// Change data config
doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
+ doReturn(true).when(mDataConfigManager).doesAutoDataSwitchAllowRoaming();
doReturn(10000L).when(mDataConfigManager)
.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
doReturn(120000L).when(mDataConfigManager)
@@ -184,7 +185,7 @@
mEventsToAlarmListener = getPrivateField(mAutoDataSwitchControllerUT,
"mEventsToAlarmListener", Map.class);
- doReturn(true).when(mFeatureFlags).autoSwitchAllowRoaming();
+ doReturn(true).when(mFeatureFlags).autoDataSwitchAllowRoaming();
doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
}
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 00e6680..9423551 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -872,7 +872,7 @@
mMockSubInfo = Mockito.mock(SubscriptionInfo.class);
mFeatureFlags = Mockito.mock(FeatureFlags.class);
when(mTelephonyComponentFactory.makeDataSettingsManager(any(Phone.class),
- any(DataNetworkController.class), any(Looper.class),
+ any(DataNetworkController.class), any(FeatureFlags.class), any(Looper.class),
any(DataSettingsManager.DataSettingsManagerCallback.class))).thenCallRealMethod();
doReturn(mMockedImsMmTelManager).when(mMockedImsManager).getImsMmTelManager(anyInt());
doReturn(mMockedImsRcsManager).when(mMockedImsManager).getImsRcsManager(anyInt());
@@ -1857,7 +1857,8 @@
boolean isDataEnabled = mDataNetworkControllerUT.getDataSettingsManager().isDataEnabled();
doReturn(mDataNetworkControllerUT.getDataSettingsManager())
.when(mPhone).getDataSettingsManager();
- MultiSimSettingController controller = Mockito.spy(new MultiSimSettingController(mContext));
+ MultiSimSettingController controller = Mockito.spy(new MultiSimSettingController(mContext,
+ mFeatureFlags));
doReturn(true).when(controller).isCarrierConfigLoadedForAllSub();
replaceInstance(MultiSimSettingController.class, "sInstance", null, controller);
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 62fbebc..ebfc41a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -85,6 +86,7 @@
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
import com.android.internal.telephony.metrics.DataCallSessionStats;
+import com.android.internal.telephony.test.SimulatedCommands;
import org.junit.After;
import org.junit.Before;
@@ -949,7 +951,6 @@
eq(DataService.REQUEST_REASON_SHUTDOWN), any(Message.class));
}
-
@Test
public void testCreateDataNetworkOnIwlan() throws Exception {
doReturn(mIwlanNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
@@ -1171,6 +1172,59 @@
}
@Test
+ public void testNetworkRequestDetachedBeforePduSessionIdAllocated() throws Exception {
+ doReturn(mIwlanNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ eq(NetworkRegistrationInfo.DOMAIN_PS),
+ eq(AccessNetworkConstants.TRANSPORT_TYPE_WLAN));
+ doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mAccessNetworksManager)
+ .getPreferredTransportByNetworkCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ NetworkRequestList networkRequestList = new NetworkRequestList();
+ TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+ .build(), mPhone);
+ networkRequestList.add(networkRequest);
+
+ SimulatedCommands simulatedCommands2 = mock(SimulatedCommands.class);
+ mPhone.mCi = simulatedCommands2;
+ mDataNetworkUT = new DataNetwork(mPhone, mFeatureFlags, Looper.myLooper(),
+ mDataServiceManagers, mImsDataProfile, networkRequestList,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN, DataAllowedReason.NORMAL,
+ mDataNetworkCallback);
+ replaceInstance(DataNetwork.class, "mDataCallSessionStats",
+ mDataNetworkUT, mDataCallSessionStats);
+ processAllMessages();
+ // Capture the message for allocatePduSessionId response. We want to delay it.
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(simulatedCommands2).allocatePduSessionId(messageCaptor.capture());
+ // Detach attached network request.
+ mDataNetworkUT.detachNetworkRequest(networkRequest, false);
+ processAllMessages();
+
+ // Pass response msg for the PDU session ID allocation.
+ Message msg = messageCaptor.getValue();
+ AsyncResult.forMessage(msg, 1, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Check setupDataCall was not called.
+ verify(mMockedWlanDataServiceManager, never()).setupDataCall(eq(AccessNetworkType.IWLAN),
+ eq(mImsDataProfile), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), nullable(LinkProperties.class),
+ eq(1), nullable(NetworkSliceInfo.class),
+ any(TrafficDescriptor.class), eq(true), any(Message.class));
+
+ // Check state changed to DISCONNECTED from CONNECTING
+ ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor =
+ ArgumentCaptor.forClass(PreciseDataConnectionState.class);
+ verify(mPhone, times(2)).notifyDataConnection(pdcsCaptor.capture());
+ List<PreciseDataConnectionState> pdcsList = pdcsCaptor.getAllValues();
+ assertThat(pdcsList.get(0).getState()).isEqualTo(TelephonyManager.DATA_CONNECTING);
+ assertThat(pdcsList.get(1).getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTED);
+ }
+
+ @Test
public void testAdminAndOwnerUids() throws Exception {
doReturn(ADMIN_UID2).when(mCarrierPrivilegesTracker).getCarrierServicePackageUid();
setupDataNetwork();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
index fc1bf0d..3f18a3a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
@@ -77,7 +77,7 @@
.when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
mDataSettingsManagerUT = new DataSettingsManager(mPhone, mDataNetworkController,
- Looper.myLooper(), mMockedDataSettingsManagerCallback);
+ mFeatureFlags, Looper.myLooper(), mMockedDataSettingsManagerCallback);
logd("DataSettingsManagerTest -Setup!");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
index e508e5b..9f8b8ad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
@@ -58,6 +58,8 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class DataStallRecoveryManagerTest extends TelephonyTest {
+ private static final String KEY_IS_DSRS_DIAGNOSTICS_ENABLED =
+ "is_dsrs_diagnostics_enabled";
private FakeContentResolver mFakeContentResolver;
// Mocked classes
@@ -89,6 +91,7 @@
Field field = DataStallRecoveryManager.class.getDeclaredField("mPredictWaitingMillis");
field.setAccessible(true);
+ doReturn(true).when(mFeatureFlags).dsrsDiagnosticsEnabled();
mFakeContentResolver = new FakeContentResolver();
doReturn(mFakeContentResolver).when(mContext).getContentResolver();
// Set the global settings for action enabled state and duration to
@@ -120,6 +123,7 @@
mPhone,
mDataNetworkController,
mMockedWwanDataServiceManager,
+ mFeatureFlags,
mTestableLooper.getLooper(),
mDataStallRecoveryManagerCallback);
@@ -429,6 +433,7 @@
@Test
public void testSendDSRMData() throws Exception {
ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+ boolean isDsrsDiagnosticsEnabled = mFeatureFlags.dsrsDiagnosticsEnabled();
logd("Set phone status to normal status.");
sendOnInternetDataNetworkCallback(true);
@@ -460,8 +465,13 @@
logd(bundle.toString());
int size = bundle.size();
logd("bundle size is " + size);
- // Check if bundle size is 19
- assertThat(size).isEqualTo(19);
+ if (isDsrsDiagnosticsEnabled) {
+ // Check if bundle size is 27
+ assertThat(size).isEqualTo(27);
+ } else {
+ // Check if bundle size is 19
+ assertThat(size).isEqualTo(19);
+ }
}
}
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 f893c35..4a433c2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnectionTest.java
@@ -44,12 +44,14 @@
import android.telephony.DomainSelectionService;
import android.telephony.EmergencyRegistrationResult;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDisconnectCause;
import android.telephony.data.ApnSetting;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.internal.telephony.CallFailCause;
import com.android.internal.telephony.ITransportSelectorCallback;
import com.android.internal.telephony.ITransportSelectorResultCallback;
import com.android.internal.telephony.IWwanSelectorCallback;
@@ -258,6 +260,39 @@
verify(mAnm).unregisterForQualifiedNetworksChanged(any());
}
+ @Test
+ @SmallTest
+ public void testGetSelectionAttributesEmergencyTempOrPermFailure() {
+ DomainSelectionService.SelectionAttributes attr =
+ EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), false,
+ TELECOM_CALL_ID1, "911", false,
+ CallFailCause.IMS_EMERGENCY_TEMP_FAILURE, null, null);
+
+ assertEquals(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE, attr.getCsDisconnectCause());
+
+ attr = EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), false,
+ TELECOM_CALL_ID1, "911", false,
+ CallFailCause.IMS_EMERGENCY_PERM_FAILURE, null, null);
+
+ assertEquals(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE, attr.getCsDisconnectCause());
+
+ attr = EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), false,
+ TELECOM_CALL_ID1, "911", false,
+ CallFailCause.EMERGENCY_TEMP_FAILURE, null, null);
+
+ assertEquals(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE, attr.getCsDisconnectCause());
+
+ attr = EmergencyCallDomainSelectionConnection.getSelectionAttributes(
+ mPhone.getPhoneId(), mPhone.getSubId(), false,
+ TELECOM_CALL_ID1, "911", false,
+ CallFailCause.EMERGENCY_PERM_FAILURE, null, null);
+
+ assertEquals(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE, attr.getCsDisconnectCause());
+ }
+
private IWwanSelectorCallback onWwanSelected(ITransportSelectorCallback transportCallback)
throws Exception {
ITransportSelectorResultCallback cb = Mockito.mock(ITransportSelectorResultCallback.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
index c47eb3b..10dbfea 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
@@ -51,7 +51,6 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyTest;
import com.google.i18n.phonenumbers.ShortNumberInfo;
@@ -61,6 +60,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import java.io.BufferedInputStream;
@@ -139,7 +139,7 @@
mShortNumberInfo = mock(ShortNumberInfo.class);
mCarrierConfigManagerMock = mock(CarrierConfigManager.class);
- mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext());
+ mContext = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
mMockContext = mock(Context.class);
mResources = mock(Resources.class);
@@ -151,9 +151,14 @@
doReturn(1).when(mPhone2).getPhoneId();
doReturn(SUB_ID_PHONE_2).when(mPhone2).getSubId();
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(mPackageManager).when(mMockContext).getPackageManager();
+
initializeEmergencyNumberListTestSamples();
- mEmergencyNumberTrackerMock = new EmergencyNumberTracker(mPhone, mSimulatedCommands);
- mEmergencyNumberTrackerMock2 = new EmergencyNumberTracker(mPhone2, mSimulatedCommands);
+ mEmergencyNumberTrackerMock = new EmergencyNumberTracker(mPhone, mSimulatedCommands,
+ mFeatureFlags);
+ mEmergencyNumberTrackerMock2 = new EmergencyNumberTracker(mPhone2, mSimulatedCommands,
+ mFeatureFlags);
doReturn(mEmergencyNumberTrackerMock2).when(mPhone2).getEmergencyNumberTracker();
mEmergencyNumberTrackerMock.DBG = true;
@@ -171,10 +176,13 @@
public void tearDown() throws Exception {
// Set back to single sim mode
setSinglePhone();
- Path target = Paths.get(mLocalDownloadDirectory.getPath(), EMERGENCY_NUMBER_DB_OTA_FILE);
- Files.deleteIfExists(target);
- mLocalDownloadDirectory.delete();
- mLocalDownloadDirectory = null;
+ if (mLocalDownloadDirectory != null) {
+ Path target = Paths.get(mLocalDownloadDirectory.getPath(),
+ EMERGENCY_NUMBER_DB_OTA_FILE);
+ Files.deleteIfExists(target);
+ mLocalDownloadDirectory.delete();
+ mLocalDownloadDirectory = null;
+ }
mEmergencyNumberTrackerMock = null;
mEmergencyNumberTrackerMock2 = null;
mEmergencyNumberListTestSample.clear();
@@ -361,12 +369,12 @@
@Test
public void testRegistrationForCountryChangeIntent() throws Exception {
EmergencyNumberTracker localEmergencyNumberTracker;
- Context spyContext = spy(mContext);
- doReturn(spyContext).when(mPhone).getContext();
+ Mockito.clearInvocations(mContext);
ArgumentCaptor<IntentFilter> intentCaptor = ArgumentCaptor.forClass(IntentFilter.class);
- localEmergencyNumberTracker = new EmergencyNumberTracker(mPhone, mSimulatedCommands);
- verify(spyContext, times(1)).registerReceiver(any(), intentCaptor.capture());
+ localEmergencyNumberTracker = new EmergencyNumberTracker(mPhone, mSimulatedCommands,
+ mFeatureFlags);
+ verify(mContext, times(1)).registerReceiver(any(), intentCaptor.capture());
IntentFilter ifilter = intentCaptor.getValue();
assertTrue(ifilter.hasAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED));
}
@@ -543,7 +551,7 @@
com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
- mPhone, mSimulatedCommands);
+ mPhone, mSimulatedCommands, mFeatureFlags);
emergencyNumberTrackerMock.sendMessage(
emergencyNumberTrackerMock.obtainMessage(
1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
@@ -616,7 +624,7 @@
com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
- mPhone, mSimulatedCommands);
+ mPhone, mSimulatedCommands, mFeatureFlags);
emergencyNumberTrackerMock.sendMessage(
emergencyNumberTrackerMock.obtainMessage(
1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
@@ -720,7 +728,7 @@
com.android.internal.R.bool.ignore_emergency_number_routing_from_db);
EmergencyNumberTracker emergencyNumberTrackerMock = new EmergencyNumberTracker(
- mPhone, mSimulatedCommands);
+ mPhone, mSimulatedCommands, mFeatureFlags);
emergencyNumberTrackerMock.sendMessage(
emergencyNumberTrackerMock.obtainMessage(
1 /* EVENT_UNSOL_EMERGENCY_NUMBER_LIST */,
@@ -843,7 +851,7 @@
ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
EmergencyNumberTracker localEmergencyNumberTracker =
- new EmergencyNumberTracker(mPhone, mSimulatedCommands);
+ new EmergencyNumberTracker(mPhone, mSimulatedCommands, mFeatureFlags);
verify(mCarrierConfigManagerMock)
.registerCarrierConfigChangeListener(any(), listenerArgumentCaptor.capture());
CarrierConfigManager.CarrierConfigChangeListener carrierConfigChangeListener =
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 105f2bf..3eb7659 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -17,7 +17,9 @@
package com.android.internal.telephony.emergency;
import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+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.REGISTRATION_STATE_HOME;
import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK;
@@ -60,7 +62,6 @@
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -95,7 +96,7 @@
public class EmergencyStateTrackerTest extends TelephonyTest {
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 int TEST_ECM_EXIT_TIMEOUT_MS = 500;
private static final EmergencyRegistrationResult E_REG_RESULT = new EmergencyRegistrationResult(
EUTRAN, REGISTRATION_STATE_HOME, DOMAIN_CS_PS, true, true, 0, 1, "001", "01", "US");
@@ -772,216 +773,6 @@
}
/**
- * Test that once endCall() for IMS call is called and we enter ECM, then we exit ECM
- * after the specified timeout.
- */
- @Test
- @SmallTest
- public void endCall_entersEcm_thenExitsEcmAfterTimeoutImsCall() throws Exception {
- // Setup EmergencyStateTracker
- EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
- /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
- // Create test Phone
- Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
- /* isRadioOn= */ true);
- setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
- setUpAsyncResultForExitEmergencyMode(testPhone);
- CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- mTestConnection1, false);
- // Set call to ACTIVE
- emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
- emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
- // Set ecm as supported
- setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
-
- processAllMessages();
-
- emergencyStateTracker.endCall(mTestConnection1);
-
- assertTrue(emergencyStateTracker.isInEcm());
-
- Context mockContext = mock(Context.class);
- replaceInstance(EmergencyStateTracker.class, "mContext",
- emergencyStateTracker, mockContext);
- processAllFutureMessages();
-
- ArgumentCaptor<TelephonyCallback> callbackCaptor =
- ArgumentCaptor.forClass(TelephonyCallback.class);
-
- verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
- any(), callbackCaptor.capture());
-
- TelephonyCallback callback = callbackCaptor.getValue();
-
- assertNotNull(callback);
-
- // Verify exitEmergencyMode() is called after timeout
- verify(testPhone).exitEmergencyMode(any(Message.class));
- assertFalse(emergencyStateTracker.isInEmergencyMode());
- assertFalse(emergencyStateTracker.isInEcm());
- verify(mTelephonyManagerProxy).unregisterTelephonyCallback(eq(callback));
- }
-
- /**
- * Test that startEmergencyCall() is called right after exiting ECM on the same slot.
- */
- @Test
- @SmallTest
- public void exitEcm_thenDialEmergencyCallOnTheSameSlotRightAfter() throws Exception {
- // Setup EmergencyStateTracker
- EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
- /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
- emergencyStateTracker.setPdnDisconnectionTimeoutMs(0);
- // Create test Phone
- Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
- /* isRadioOn= */ true);
- setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
- setUpAsyncResultForExitEmergencyMode(testPhone);
-
- verify(testPhone, times(0)).setEmergencyMode(anyInt(), any(Message.class));
- verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
-
- CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- 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, mTestConnection1);
- emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
- // Set ecm as supported
- setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
-
- processAllMessages();
-
- emergencyStateTracker.endCall(mTestConnection1);
-
- assertTrue(emergencyStateTracker.isInEcm());
-
- Context mockContext = mock(Context.class);
- replaceInstance(EmergencyStateTracker.class, "mContext",
- emergencyStateTracker, mockContext);
- processAllFutureMessages();
-
- ArgumentCaptor<TelephonyCallback> callbackCaptor =
- ArgumentCaptor.forClass(TelephonyCallback.class);
-
- verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
- any(), callbackCaptor.capture());
-
- TelephonyCallback callback = callbackCaptor.getValue();
-
- assertNotNull(callback);
- assertFalse(emergencyStateTracker.isInEmergencyMode());
- assertFalse(emergencyStateTracker.isInEcm());
- verify(mTelephonyManagerProxy, times(0)).unregisterTelephonyCallback(eq(callback));
- verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
- any(Message.class));
- verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
-
- replaceInstance(EmergencyStateTracker.class, "mContext", emergencyStateTracker, mContext);
-
- // dial on the same slot
- unused = emergencyStateTracker.startEmergencyCall(testPhone, mTestConnection1, false);
- processAllMessages();
-
- assertTrue(emergencyStateTracker.isInEmergencyMode());
- assertFalse(emergencyStateTracker.isInEcm());
- verify(mTelephonyManagerProxy, times(1)).unregisterTelephonyCallback(eq(callback));
- verify(testPhone, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
- any(Message.class));
- verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
- }
-
- /**
- * Test that startEmergencyCall() is called right after exiting ECM on the other slot.
- */
- @Test
- @SmallTest
- public void exitEcm_thenDialEmergencyCallOnTheOtherSlotRightAfter() throws Exception {
- // Setup EmergencyStateTracker
- EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
- /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
- emergencyStateTracker.setPdnDisconnectionTimeoutMs(0);
- // Create test Phone
- Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
- /* isRadioOn= */ true);
- setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
- setUpAsyncResultForExitEmergencyMode(testPhone);
-
- verify(testPhone, times(0)).setEmergencyMode(anyInt(), any(Message.class));
- verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
-
- CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
- 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, mTestConnection1);
- emergencyStateTracker.onEmergencyCallDomainUpdated(
- PhoneConstants.PHONE_TYPE_IMS, mTestConnection1);
- // Set ecm as supported
- setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
-
- processAllMessages();
-
- emergencyStateTracker.endCall(mTestConnection1);
-
- assertTrue(emergencyStateTracker.isInEcm());
-
- Context mockContext = mock(Context.class);
- replaceInstance(EmergencyStateTracker.class, "mContext",
- emergencyStateTracker, mockContext);
- processAllFutureMessages();
-
- ArgumentCaptor<TelephonyCallback> callbackCaptor =
- ArgumentCaptor.forClass(TelephonyCallback.class);
-
- verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
- any(), callbackCaptor.capture());
-
- TelephonyCallback callback = callbackCaptor.getValue();
-
- assertNotNull(callback);
- assertFalse(emergencyStateTracker.isInEmergencyMode());
- assertFalse(emergencyStateTracker.isInEcm());
- verify(mTelephonyManagerProxy, times(0)).unregisterTelephonyCallback(eq(callback));
- verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
- any(Message.class));
- verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
-
- Phone phone1 = getPhone(1);
- verify(phone1, times(0)).setEmergencyMode(anyInt(), any(Message.class));
- verify(phone1, times(0)).exitEmergencyMode(any(Message.class));
-
- replaceInstance(EmergencyStateTracker.class, "mContext", emergencyStateTracker, mContext);
-
- // dial on the other slot
- unused = emergencyStateTracker.startEmergencyCall(phone1, mTestConnection1, false);
- processAllMessages();
-
- assertTrue(emergencyStateTracker.isInEmergencyMode());
- assertFalse(emergencyStateTracker.isInEcm());
- verify(mTelephonyManagerProxy, times(1)).unregisterTelephonyCallback(eq(callback));
- verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
- any(Message.class));
- verify(testPhone, times(1)).exitEmergencyMode(any(Message.class));
- verify(phone1, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
- verify(phone1, times(0)).exitEmergencyMode(any(Message.class));
- }
-
- /**
* Test that once endCall() is called and we enter ECM, then we exit ECM when turning on
* airplane mode.
*/
@@ -1160,14 +951,16 @@
processAllMessages();
assertTrue(emergencyStateTracker.isInEcm());
+ verify(phone0, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ verify(phone0, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
// Second emergency call started.
CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
mTestConnection2, false);
- // Returns DisconnectCause#NOT_DISCONNECTED immediately.
- assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
- Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ assertFalse(future.isDone());
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ verify(phone0, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
}
@Test
@@ -1230,9 +1023,7 @@
future = emergencyStateTracker.startEmergencyCall(phone0, mTestConnection2, false);
assertTrue(emergencyStateTracker.isInEmergencyMode());
- // Returns DisconnectCause#NOT_DISCONNECTED immediately.
- assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
- Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ assertFalse(future.isDone());
emergencyStateTracker.onEmergencyTransportChanged(
EmergencyStateTracker.EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WLAN);
@@ -1297,7 +1088,7 @@
@Test
@SmallTest
- public void testEndSms() {
+ public void testEndSmsAndExitEmergencyMode() {
EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
/* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
@@ -1315,10 +1106,105 @@
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
- emergencyStateTracker.endSms(TEST_SMS_ID, true);
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_CS);
verify(phone0).exitEmergencyMode(any(Message.class));
assertFalse(emergencyStateTracker.isInEmergencyMode());
+ // CS domain doesn't support SCBM.
+ assertFalse(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
+ public void testEndSmsAndEnterEmergencySmsCallbackMode() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ assertTrue(emergencyStateTracker.getEmergencyRegistrationResult().equals(E_REG_RESULT));
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
+
+ verify(mCarrierConfigManager).getConfigForSubId(anyInt(),
+ eq(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT));
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
+ public void testEndSmsWithSmsSentFailureWhileInScbm() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpScbm(phone0, emergencyStateTracker);
+ processAllMessages();
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID_2, false);
+ processAllMessages();
+
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ assertTrue(emergencyStateTracker.isInScbm());
+
+ // Set emergency transport
+ emergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN);
+
+ // When MO SMS fails while in SCBM.
+ emergencyStateTracker.endSms(TEST_SMS_ID_2, false, DOMAIN_PS);
+
+ verify(mCarrierConfigManager).getConfigForSubId(anyInt(),
+ eq(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT));
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
+ public void testEndSmsWithSmsSentSuccessWhileInScbm() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpScbm(phone0, emergencyStateTracker);
+ processAllMessages();
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID_2, false);
+ processAllMessages();
+
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ assertTrue(emergencyStateTracker.isInScbm());
+
+ // Set emergency transport
+ emergencyStateTracker.onEmergencyTransportChanged(
+ EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN);
+
+ // When MO SMS is successfully sent while in SCBM.
+ emergencyStateTracker.endSms(TEST_SMS_ID_2, true, DOMAIN_PS);
+
+ verify(mCarrierConfigManager, times(2)).getConfigForSubId(anyInt(),
+ eq(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT));
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInScbm());
}
@Test
@@ -1449,6 +1335,24 @@
@Test
@SmallTest
+ public void testStartEmergencySmsWhileInScbm() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpScbm(phone0, emergencyStateTracker);
+
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID_2, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ verify(phone0, never()).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
public void testStartEmergencySmsUsingDifferentPhone() {
EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
/* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
@@ -1470,6 +1374,36 @@
@Test
@SmallTest
+ public void testStartEmergencySmsWhileInScbmOnDifferentPhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ false,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setUpScbm(phone0, emergencyStateTracker);
+ processAllMessages();
+
+ Phone phone1 = getPhone(1);
+ setUpAsyncResultForSetEmergencyMode(phone1, E_REG_RESULT);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone1,
+ TEST_SMS_ID_2, false);
+
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ // Waits for exiting emergency mode on other phone.
+ assertFalse(future.isDone());
+
+ processAllMessages();
+
+ verify(phone1).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ processAllMessages();
+
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ }
+
+ @Test
+ @SmallTest
public void testStartEmergencyCallActiveAndSmsOnSamePhone() {
EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
/* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
@@ -1532,6 +1466,185 @@
@Test
@SmallTest
+ public void testStartEmergencyCallWhileCallActiveAndInScbmOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ // Emergency call is in active.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
+ mTestConnection1, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ // Expect: DisconnectCause#NOT_DISCONNECTED
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
+
+ // Emergency SMS is successfully sent and SCBM entered.
+ setUpScbm(phone0, emergencyStateTracker);
+ processAllMessages();
+
+ future = emergencyStateTracker.startEmergencyCall(phone0, mTestConnection2, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ // Ensure that SCBM is exited.
+ assertFalse(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallWhileInEcmAndScbmOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setEcmSupportedConfig(phone0, true);
+ // Emergency call is in active.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
+ mTestConnection1, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ // Expect: DisconnectCause#NOT_DISCONNECTED
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
+ emergencyStateTracker.endCall(mTestConnection1);
+
+ assertTrue(emergencyStateTracker.isInEcm());
+
+ // Emergency SMS is successfully sent and SCBM entered.
+ setUpScbm(phone0, emergencyStateTracker);
+ processAllMessages();
+
+ future = emergencyStateTracker.startEmergencyCall(phone0, mTestConnection2, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEcm());
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ // Ensure that SCBM is exited.
+ assertFalse(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallWhileScbmInProgressOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // Emergency SMS is successfully sent and SCBM entered.
+ setUpScbm(phone0, emergencyStateTracker);
+
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // Emergency call is started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
+ mTestConnection1, false);
+ processAllMessages();
+
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ // Ensure that SCBM is exited.
+ assertFalse(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallWhileInScbmOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ // Emergency SMS is successfully sent and SCBM entered.
+ setUpScbm(phone0, emergencyStateTracker);
+ processAllMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // Emergency call is started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone0,
+ mTestConnection1, false);
+ processAllMessages();
+
+ verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ // Ensure that SCBM is exited.
+ assertFalse(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallAndEnterScbmAndEndCallOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setEcmSupportedConfig(phone0, true);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ mTestConnection1, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ assertFalse(emergencyStateTracker.isInScbm());
+
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
+
+ assertTrue(emergencyStateTracker.isInScbm());
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+
+ setEcmSupportedConfig(phone0, false);
+ emergencyStateTracker.endCall(mTestConnection1);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ assertTrue(emergencyStateTracker.isInScbm());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ }
+
+ @Test
+ @SmallTest
public void testStartEmergencySmsActiveAndCallOnSamePhone() {
EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
/* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
@@ -1553,8 +1666,7 @@
future = emergencyStateTracker.startEmergencyCall(phone0, mTestConnection1, false);
processAllMessages();
- verify(phone0).exitEmergencyMode(any(Message.class));
- verify(phone0, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEmergencyCall());
// Expect: DisconnectCause#NOT_DISCONNECTED.
@@ -1615,6 +1727,48 @@
@Test
@SmallTest
+ public void testStartEmergencySmsInProgressAndStartEmergencyCallAndEndSmsOnSamePhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ // Emergency SMS is in progress.
+ CompletableFuture<Integer> smsFuture = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ ArgumentCaptor<Message> smsCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), smsCaptor.capture());
+
+ // Emergency call is being started.
+ CompletableFuture<Integer> callFuture = emergencyStateTracker.startEmergencyCall(phone0,
+ mTestConnection1, false);
+
+ assertFalse(smsFuture.isDone());
+ assertFalse(callFuture.isDone());
+
+ emergencyStateTracker.endSms(TEST_SMS_ID, false, DOMAIN_PS);
+
+ // Response message for setEmergencyMode by SMS.
+ Message msg = smsCaptor.getValue();
+ AsyncResult.forMessage(msg, E_REG_RESULT, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Response message for setEmergencyMode by call.
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ processAllMessages();
+
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(callFuture.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ }
+
+ @Test
+ @SmallTest
public void testStartEmergencyCallAndSmsOnDifferentPhone() {
EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
/* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
@@ -1731,6 +1885,73 @@
@Test
@SmallTest
+ public void testStartEmergencyCallWhileScbmInProgressOnDifferentPhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ // Emergency SMS is successfully sent and SCBM entered.
+ setUpScbm(phone0, emergencyStateTracker);
+
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // Emergency call is being started using the different phone.
+ Phone phone1 = getPhone(1);
+ setUpAsyncResultForSetEmergencyMode(phone1, E_REG_RESULT);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone1,
+ mTestConnection1, false);
+ processAllMessages();
+
+ // Exit emergency mode and set the emergency mode again by the call when the exit result
+ // is received for obtaining the latest EmergencyRegResult.
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ verify(phone1).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ // Ensure that SCBM is exited.
+ assertFalse(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
+ public void testStartEmergencyCallWhileInScbmOnDifferentPhone() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ // Emergency SMS is successfully sent and SCBM entered.
+ setUpScbm(phone0, emergencyStateTracker);
+ processAllMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ // Emergency call is being started using the different phone.
+ Phone phone1 = getPhone(1);
+ setUpAsyncResultForSetEmergencyMode(phone1, E_REG_RESULT);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(phone1,
+ mTestConnection1, false);
+ processAllMessages();
+
+ // Exit emergency mode and set the emergency mode again by the call when the exit result
+ // is received for obtaining the latest EmergencyRegResult.
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ verify(phone1).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+ // Ensure that SCBM is exited.
+ assertFalse(emergencyStateTracker.isInScbm());
+ }
+
+ @Test
+ @SmallTest
public void testExitEmergencyModeCallAndSmsOnSamePhone() {
EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
/* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
@@ -1762,7 +1983,7 @@
assertTrue(emergencyStateTracker.isInEmergencyMode());
- emergencyStateTracker.endSms(TEST_SMS_ID, true);
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
processAllMessages();
assertFalse(emergencyStateTracker.isInEmergencyMode());
@@ -1799,7 +2020,7 @@
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
- emergencyStateTracker.endSms(TEST_SMS_ID, true);
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -1846,7 +2067,7 @@
assertTrue(emergencyStateTracker.isInEcm());
assertFalse(emergencyStateTracker.isInEmergencyCall());
- emergencyStateTracker.endSms(TEST_SMS_ID, true);
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
processAllMessages();
verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
@@ -1901,7 +2122,7 @@
emergencyStateTracker.onEmergencyTransportChanged(
EmergencyStateTracker.EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN);
- emergencyStateTracker.endSms(TEST_SMS_ID, true);
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
processAllMessages();
// Enter emergency callback mode and emergency mode changed by SMS end.
@@ -1909,13 +2130,15 @@
assertTrue(emergencyStateTracker.isInEmergencyMode());
assertTrue(emergencyStateTracker.isInEcm());
assertFalse(emergencyStateTracker.isInEmergencyCall());
+ assertTrue(emergencyStateTracker.isInScbm());
- // ECM timeout.
+ // ECM/SCBM timeout.
processAllFutureMessages();
assertFalse(emergencyStateTracker.isInEmergencyMode());
assertFalse(emergencyStateTracker.isInEcm());
assertFalse(emergencyStateTracker.isInEmergencyCall());
+ assertFalse(emergencyStateTracker.isInScbm());
verify(phone0).exitEmergencyMode(any(Message.class));
}
@@ -1948,7 +2171,7 @@
assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
- emergencyStateTracker.endSms(TEST_SMS_ID, true);
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
assertTrue(emergencyStateTracker.isInEmergencyMode());
@@ -1970,6 +2193,130 @@
@Test
@SmallTest
+ public void testExitEmergencyModeWhenExitingEcmAndScbm() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setEcmSupportedConfig(phone0, true);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ mTestConnection1, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endCall(mTestConnection1);
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ setScbmTimerValue(phone0, TEST_ECM_EXIT_TIMEOUT_MS + 100);
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
+ processAllMessages();
+
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ assertTrue(emergencyStateTracker.isInScbm());
+
+ // ECM timeout.
+ moveTimeForward(TEST_ECM_EXIT_TIMEOUT_MS + 50);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ assertTrue(emergencyStateTracker.isInScbm());
+
+ // SCBM timeout.
+ processAllFutureMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInScbm());
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testExitEmergencyModeWhenExitingScbmAndEcm() {
+ EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+ /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+ Phone phone0 = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+ /* isRadioOn= */ true);
+ setUpAsyncResultForSetEmergencyMode(phone0, E_REG_RESULT);
+ setUpAsyncResultForExitEmergencyMode(phone0);
+ setEcmSupportedConfig(phone0, true);
+ // Emergency call is in active.
+ CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(phone0,
+ mTestConnection1, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEmergencyCall());
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, mTestConnection1);
+
+ // Emergency SMS is being started.
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone0,
+ TEST_SMS_ID, false);
+
+ // Returns DisconnectCause#NOT_DISCONNECTED immediately.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ emergencyStateTracker.endCall(mTestConnection1);
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+
+ setScbmTimerValue(phone0, TEST_ECM_EXIT_TIMEOUT_MS - 100);
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
+ processAllMessages();
+
+ verify(phone0).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ assertTrue(emergencyStateTracker.isInScbm());
+
+ // SCBM timeout.
+ moveTimeForward(TEST_ECM_EXIT_TIMEOUT_MS - 50);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInEcm());
+ assertFalse(emergencyStateTracker.isInEmergencyCall());
+ assertFalse(emergencyStateTracker.isInScbm());
+
+ // ECM timeout.
+ processAllFutureMessages();
+
+ assertFalse(emergencyStateTracker.isInEmergencyMode());
+ assertFalse(emergencyStateTracker.isInEcm());
+ verify(phone0).exitEmergencyMode(any(Message.class));
+ }
+
+ @Test
+ @SmallTest
public void testSaveKeyEmergencyCallbackModeSupportedBool() {
mContextFixture.getCarrierConfigBundle().putBoolean(
CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
@@ -1995,14 +2342,14 @@
listenerArgumentCaptor.getAllValues().get(0);
// Verify carrier config for valid subscription
- assertTrue(testEst.isEmergencyCallbackModeSupported());
+ assertTrue(testEst.isEmergencyCallbackModeSupported(phone));
// SIM removed
when(phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
setEcmSupportedConfig(phone, false);
// Verify default config for invalid subscription
- assertFalse(testEst.isEmergencyCallbackModeSupported());
+ assertFalse(testEst.isEmergencyCallbackModeSupported(phone));
// Insert SIM again
when(phone.getSubId()).thenReturn(1);
@@ -2023,7 +2370,7 @@
TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
// Verify saved config for valid subscription
- assertTrue(testEst.isEmergencyCallbackModeSupported());
+ assertTrue(testEst.isEmergencyCallbackModeSupported(phone));
// Insert SIM again, but emergency callback mode not supported
when(phone.getSubId()).thenReturn(1);
@@ -2035,7 +2382,7 @@
TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
// Verify carrier config for valid subscription
- assertFalse(testEst.isEmergencyCallbackModeSupported());
+ assertFalse(testEst.isEmergencyCallbackModeSupported(phone));
// SIM removed again
when(phone.getSubId()).thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -2047,7 +2394,7 @@
TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
// Verify saved config for valid subscription
- assertFalse(testEst.isEmergencyCallbackModeSupported());
+ assertFalse(testEst.isEmergencyCallbackModeSupported(phone));
}
/**
@@ -2263,24 +2610,20 @@
}
private void setEcmSupportedConfig(Phone phone, boolean ecmSupported) {
- CarrierConfigManager cfgManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- cfgManager.getConfigForSubId(phone.getSubId()).putBoolean(
+ mCarrierConfigManager.getConfigForSubId(phone.getSubId()).putBoolean(
CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL,
ecmSupported);
}
private void setConfigForDdsSwitch(Phone phone, String[] roaminPlmns,
int suplEmergencyModeType, String esExtensionSec) {
- CarrierConfigManager cfgManager = (CarrierConfigManager) mContext
- .getSystemService(Context.CARRIER_CONFIG_SERVICE);
- cfgManager.getConfigForSubId(phone.getSubId()).putStringArray(
+ mCarrierConfigManager.getConfigForSubId(phone.getSubId()).putStringArray(
CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
roaminPlmns);
- cfgManager.getConfigForSubId(phone.getSubId()).putInt(
+ mCarrierConfigManager.getConfigForSubId(phone.getSubId()).putInt(
CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
suplEmergencyModeType);
- cfgManager.getConfigForSubId(phone.getSubId())
+ mCarrierConfigManager.getConfigForSubId(phone.getSubId())
.putString(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, esExtensionSec);
}
@@ -2304,4 +2647,36 @@
return null;
}).when(phone).exitEmergencyMode(any(Message.class));
}
+
+ private void setUpScbm(Phone phone, EmergencyStateTracker emergencyStateTracker) {
+ setUpAsyncResultForSetEmergencyMode(phone, E_REG_RESULT);
+ setEcmSupportedConfig(phone, true);
+ CompletableFuture<Integer> future = emergencyStateTracker.startEmergencySms(phone,
+ TEST_SMS_ID, false);
+ processAllMessages();
+
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ verify(phone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+
+ assertTrue(emergencyStateTracker.getEmergencyRegistrationResult().equals(E_REG_RESULT));
+ // Expect: DisconnectCause#NOT_DISCONNECTED.
+ assertEquals(future.getNow(DisconnectCause.ERROR_UNSPECIFIED),
+ Integer.valueOf(DisconnectCause.NOT_DISCONNECTED));
+
+ // Expect: entering SCBM.
+ emergencyStateTracker.endSms(TEST_SMS_ID, true, DOMAIN_PS);
+
+ verify(mCarrierConfigManager).getConfigForSubId(anyInt(),
+ eq(CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT));
+ if (!emergencyStateTracker.isInEcm() && !emergencyStateTracker.isInEmergencyCall()) {
+ verify(phone).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK), any(Message.class));
+ }
+ assertTrue(emergencyStateTracker.isInEmergencyMode());
+ assertTrue(emergencyStateTracker.isInScbm());
+ }
+
+ private void setScbmTimerValue(Phone phone, int millis) {
+ mCarrierConfigManager.getConfigForSubId(phone.getSubId()).putInt(
+ CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT, millis);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 79b1ada..6056112 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -2497,6 +2497,34 @@
}
@Test
+ @SmallTest
+ public void testDomainSelectionEmergencyPermFailure() {
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ startOutgoingCall();
+ ImsPhoneConnection c = mCTUT.mForegroundCall.getFirstConnection();
+ mImsCallListener.onCallStartFailed(mSecondImsCall,
+ new ImsReasonInfo(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE, -1));
+ processAllMessages();
+ assertNotNull(c.getImsReasonInfo());
+ assertEquals(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE,
+ c.getImsReasonInfo().getCode());
+ }
+
+ @Test
+ @SmallTest
+ public void testDomainSelectionEmergencyTempFailure() {
+ doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+ startOutgoingCall();
+ ImsPhoneConnection c = mCTUT.mForegroundCall.getFirstConnection();
+ mImsCallListener.onCallStartFailed(mSecondImsCall,
+ new ImsReasonInfo(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE, -1));
+ processAllMessages();
+ assertNotNull(c.getImsReasonInfo());
+ assertEquals(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE,
+ c.getImsReasonInfo().getCode());
+ }
+
+ @Test
public void testUpdateImsCallStatusIncoming() throws Exception {
// Incoming call
ImsPhoneConnection connection = setupRingingConnection();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/CellularSecurityTransparencyStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/CellularSecurityTransparencyStatsTest.java
new file mode 100644
index 0000000..2149de0
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/CellularSecurityTransparencyStatsTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.metrics;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.CellularIdentifierDisclosure;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class CellularSecurityTransparencyStatsTest {
+
+ private CellularSecurityTransparencyStats mCellularSecurityStats;
+
+ @Before
+ public void setUp() throws Exception {
+ mCellularSecurityStats = spy(new CellularSecurityTransparencyStats());
+ }
+
+ @Test
+ public void testLogIdentifierDisclosure_NullSimPlmn() {
+ CellularIdentifierDisclosure disclosure = new CellularIdentifierDisclosure(
+ CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+ CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI, "123-456", true);
+
+ mCellularSecurityStats.logIdentifierDisclosure(disclosure, null, null, true);
+
+ verify(mCellularSecurityStats).writeIdentifierDisclosure(-1, -1, 123, 456,
+ CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+ CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, true,
+ true);
+ }
+
+ @Test
+ public void testLogIdentifierDisclosure_badSimPlmn() {
+ CellularIdentifierDisclosure disclosure = new CellularIdentifierDisclosure(
+ CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+ CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI, "123-456", true);
+
+ mCellularSecurityStats.logIdentifierDisclosure(disclosure, "INCORRECTLY", "FORMATTED",
+ true);
+
+ verify(mCellularSecurityStats).writeIdentifierDisclosure(-1, -1, 123, 456,
+ CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+ CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, true,
+ true);
+ }
+
+ @Test
+ public void testLogIdentifierDisclosure_badDisclosurePlmn() {
+ CellularIdentifierDisclosure disclosure = new CellularIdentifierDisclosure(
+ CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+ CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI, "INCORRECT", true);
+
+ mCellularSecurityStats.logIdentifierDisclosure(disclosure, "123", "456", true);
+
+ verify(mCellularSecurityStats).writeIdentifierDisclosure(123, 456, -1, -1,
+ CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+ CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, true,
+ true);
+ }
+
+ @Test
+ public void testLogIdentifierDisclosure_expectedGoodData() {
+ CellularIdentifierDisclosure disclosure = new CellularIdentifierDisclosure(
+ CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+ CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI, "999-666", true);
+
+ mCellularSecurityStats.logIdentifierDisclosure(disclosure, "123", "456", true);
+
+ verify(mCellularSecurityStats).writeIdentifierDisclosure(123, 456, 999, 666,
+ CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+ CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, true,
+ true);
+
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index 0426737..35f1bb7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -70,8 +70,8 @@
.setCoolDownMillis(24L * 3600L * 1000L)
.build();
private static final long MIN_COOLDOWN_MILLIS = 23L * 3600L * 1000L;
- private static final long CELL_SERVICE_MIN_COOLDOWN_MILLIS =
- IS_DEBUGGABLE ? 4L * 60L * 1000L : MIN_COOLDOWN_MILLIS;
+ private static final long POWER_CORRELATED_MIN_COOLDOWN_MILLIS =
+ IS_DEBUGGABLE ? 4L * 60L * 1000L : 5L * 3600L * 1000L;
private static final long MIN_CALLS_PER_BUCKET = 5L;
// NOTE: these fields are currently 32-bit internally and padded to 64-bit by TelephonyManager
@@ -402,6 +402,9 @@
@SmallTest
public void onPullAtom_cellularServiceState_tooFrequent() throws Exception {
doReturn(null).when(mPersistAtomsStorage).getCellularServiceStates(anyLong());
+ mContextFixture.putIntResource(
+ com.android.internal.R.integer.config_metrics_pull_cooldown_millis,
+ (int) POWER_CORRELATED_MIN_COOLDOWN_MILLIS);
List<StatsEvent> actualAtoms = new ArrayList<>();
int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
@@ -409,7 +412,7 @@
assertThat(actualAtoms).hasSize(0);
assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
verify(mPersistAtomsStorage, times(1)).getCellularServiceStates(
- eq(CELL_SERVICE_MIN_COOLDOWN_MILLIS));
+ eq(POWER_CORRELATED_MIN_COOLDOWN_MILLIS));
verifyNoMoreInteractions(mPersistAtomsStorage);
}
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 f1a8de9..aae6a2f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -1736,6 +1736,8 @@
// Trigger carrier config changed with carrierEnabledSatelliteFlag enabled
when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+ mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+ true);
for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
: mCarrierConfigChangedListenerList) {
pair.first.execute(() -> pair.second.onCarrierConfigChanged(
@@ -2723,8 +2725,8 @@
verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(),
eq(plmnListPerCarrier), eq(allSatellitePlmnList), any(Message.class));
- // If the PlmnListPerCarrier and the overlay config plmn list are exist verify passing
- // the modem.
+ // If the PlmnListPerCarrier and the overlay config plmn list are exist but
+ // KEY_SATELLITE_ATTACH_SUPPORTED_BOOL is false, verify passing to the modem.
entitlementPlmnList = Arrays.stream(new String[]{"00101", "00102", "00103"}).toList();
overlayConfigPlmnList =
Arrays.stream(new String[]{"00101", "00102", "00104"}).toList();
@@ -2735,6 +2737,22 @@
entitlementPlmnList, mIIntegerConsumer);
plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID);
+ assertEquals(new ArrayList<>(), plmnListPerCarrier);
+ allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
+ entitlementPlmnList, overlayConfigPlmnList);
+ verify(mMockSatelliteModemInterface, times(1)).setSatellitePlmn(anyInt(),
+ eq(entitlementPlmnList), eq(allSatellitePlmnList), any(Message.class));
+
+ // If the PlmnListPerCarrier and the overlay config plmn list are exist and
+ // KEY_SATELLITE_ATTACH_SUPPORTED_BOOL is true verify passing the modem.
+ reset(mMockSatelliteModemInterface);
+ mCarrierConfigBundle.putBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+ true);
+
+ mSatelliteControllerUT.onSatelliteEntitlementStatusUpdated(SUB_ID, true,
+ entitlementPlmnList, mIIntegerConsumer);
+
+ plmnListPerCarrier = mSatelliteControllerUT.getSatellitePlmnsForCarrier(SUB_ID);
allSatellitePlmnList = SatelliteServiceUtils.mergeStrLists(
plmnListPerCarrier, overlayConfigPlmnList);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
index 20b33eb..d12828a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -514,6 +514,24 @@
assertEquals(0, testSOSMessageRecommender.getCountOfTimerStarted());
}
+ @Test
+ public void testIsSatelliteViaOemAvailable() {
+ Boolean originalIsSatelliteViaOemProvisioned =
+ mTestSatelliteController.mIsSatelliteViaOemProvisioned;
+
+ mTestSatelliteController.mIsSatelliteViaOemProvisioned = null;
+ assertFalse(mTestSOSMessageRecommender.isSatelliteViaOemAvailable());
+
+ mTestSatelliteController.mIsSatelliteViaOemProvisioned = true;
+ assertTrue(mTestSOSMessageRecommender.isSatelliteViaOemAvailable());
+
+ mTestSatelliteController.mIsSatelliteViaOemProvisioned = false;
+ assertFalse(mTestSOSMessageRecommender.isSatelliteViaOemAvailable());
+
+ mTestSatelliteController.mIsSatelliteViaOemProvisioned =
+ originalIsSatelliteViaOemProvisioned;
+ }
+
private void testStopTrackingCallBeforeTimeout(
@Connection.ConnectionState int connectionState) {
mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
@@ -614,7 +632,7 @@
mProvisionStateChangedCallbacks;
private int mRegisterForSatelliteProvisionStateChangedCalls = 0;
private int mUnregisterForSatelliteProvisionStateChangedCalls = 0;
- private boolean mIsSatelliteViaOemProvisioned = true;
+ private Boolean mIsSatelliteViaOemProvisioned = true;
private boolean mIsSatelliteConnectedViaCarrierWithinHysteresisTime = true;
public boolean isOemEnabledSatelliteSupported = true;
public boolean isCarrierEnabledSatelliteSupported = true;
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 8841c7a..aba8164 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifierTest.java
@@ -29,12 +29,17 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.telephony.CellularIdentifierDisclosure;
import com.android.internal.telephony.TestExecutorService;
+import com.android.internal.telephony.metrics.CellularSecurityTransparencyStats;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
@@ -50,6 +55,9 @@
private static final int SUB_ID_2 = 2;
private CellularIdentifierDisclosure mDislosure;
private CellularNetworkSecuritySafetySource mSafetySource;
+ private CellularSecurityTransparencyStats mStats;
+ private TestExecutorService mExecutor;
+ private SubscriptionManagerService mSubscriptionManagerService;
private Context mContext;
private InOrder mInOrder;
@@ -62,15 +70,16 @@
"001001",
false);
mSafetySource = mock(CellularNetworkSecuritySafetySource.class);
+ mStats = mock(CellularSecurityTransparencyStats.class);
+ mExecutor = new TestExecutorService();
+ mSubscriptionManagerService = mock(SubscriptionManagerService.class);
+ mContext = mock(Context.class);
mInOrder = inOrder(mSafetySource);
}
@Test
public void testInitializeDisabled() {
- TestExecutorService executor = new TestExecutorService();
- CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(
- executor, 15, TimeUnit.MINUTES, mSafetySource);
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
assertFalse(notifier.isEnabled());
verify(mSafetySource, never()).setIdentifierDisclosureIssueEnabled(any(), anyBoolean());
@@ -78,10 +87,7 @@
@Test
public void testDisableAddDisclosureNop() {
- TestExecutorService executor = new TestExecutorService();
- CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(
- executor, 15, TimeUnit.MINUTES, mSafetySource);
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
assertFalse(notifier.isEnabled());
notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
@@ -92,10 +98,8 @@
@Test
public void testAddDisclosureEmergencyNop() {
- TestExecutorService executor = new TestExecutorService();
- CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(
- executor, 15, TimeUnit.MINUTES, mSafetySource);
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
+
CellularIdentifierDisclosure emergencyDisclosure =
new CellularIdentifierDisclosure(
CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
@@ -113,11 +117,7 @@
@Test
public void testAddDisclosureCountIncrements() {
- TestExecutorService executor = new TestExecutorService();
- CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(
- executor, 15, TimeUnit.MINUTES, mSafetySource);
-
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
notifier.enable(mContext);
for (int i = 0; i < 3; i++) {
@@ -135,28 +135,20 @@
@Test
public void testSingleDisclosureStartAndEndTimesAreEqual() {
- TestExecutorService executor = new TestExecutorService();
- CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(
- executor, 15, TimeUnit.MINUTES, mSafetySource);
-
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
notifier.enable(mContext);
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)));
+ Assert.assertEquals(notifier.getFirstOpen(SUB_ID_1), 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, mSafetySource);
-
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
notifier.enable(mContext);
notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
@@ -175,10 +167,7 @@
@Test
public void testAddDisclosureThenWindowClose() {
- TestExecutorService executor = new TestExecutorService();
- CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(
- executor, 15, TimeUnit.MINUTES, mSafetySource);
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
// One round of disclosures
notifier.enable(mContext);
@@ -191,7 +180,7 @@
.setIdentifierDisclosure(any(), eq(SUB_ID_1), eq(2), any(), any());
// Window close should reset the counter
- executor.advanceTime(WINDOW_CLOSE_ADVANCE_MILLIS);
+ mExecutor.advanceTime(WINDOW_CLOSE_ADVANCE_MILLIS);
assertEquals(0, notifier.getCurrentDisclosureCount(SUB_ID_1));
// A new disclosure should increment as normal
@@ -203,10 +192,7 @@
@Test
public void testDisableClosesWindow() {
- TestExecutorService executor = new TestExecutorService();
- CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(
- executor, 15, TimeUnit.MINUTES, mSafetySource);
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
// One round of disclosures
notifier.enable(mContext);
@@ -231,10 +217,7 @@
@Test
public void testMultipleSubIdsTrackedIndependently() {
- TestExecutorService executor = new TestExecutorService();
- CellularIdentifierDisclosureNotifier notifier =
- new CellularIdentifierDisclosureNotifier(
- executor, 15, TimeUnit.MINUTES, mSafetySource);
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
notifier.enable(mContext);
for (int i = 0; i < 3; i++) {
@@ -262,4 +245,27 @@
assertEquals(3, notifier.getCurrentDisclosureCount(SUB_ID_1));
assertEquals(4, notifier.getCurrentDisclosureCount(SUB_ID_2));
}
+
+ @Test
+ public void testLogDisclsoure() {
+ String mcc = "100";
+ String mnc = "200";
+
+ CellularIdentifierDisclosureNotifier notifier = getNotifier();
+ SubscriptionInfoInternal subInfoMock = mock(SubscriptionInfoInternal.class);
+ when(mSubscriptionManagerService.getSubscriptionInfoInternal(SUB_ID_1)).thenReturn(
+ subInfoMock);
+ when(subInfoMock.getMcc()).thenReturn(mcc);
+ when(subInfoMock.getMnc()).thenReturn(mnc);
+
+ notifier.addDisclosure(mContext, SUB_ID_1, mDislosure);
+
+ verify(mStats, times(1)).logIdentifierDisclosure(mDislosure, mcc, mnc,
+ mDislosure.isEmergency());
+ }
+
+ private CellularIdentifierDisclosureNotifier getNotifier() {
+ return new CellularIdentifierDisclosureNotifier(mExecutor, 15, TimeUnit.MINUTES,
+ mSafetySource, mSubscriptionManagerService, mStats);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/security/NullCipherNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/security/NullCipherNotifierTest.java
new file mode 100644
index 0000000..0bb7b76
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/NullCipherNotifierTest.java
@@ -0,0 +1,377 @@
+/*
+ * 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.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.eq;
+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.SecurityAlgorithmUpdate;
+
+import com.android.internal.telephony.TestExecutorService;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+public class NullCipherNotifierTest {
+
+ private static final int SUB_ID = 3425;
+ private static final List<Integer> NON_TRANSPORT_LAYER_EVENTS =
+ List.of(SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_SIP,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_SIP_SOS,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_RTP,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_VOLTE_RTP_SOS,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_VONR_SIP,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_VONR_SIP_SOS,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_VONR_RTP,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_VONR_RTP_SOS);
+ private static final List<Integer> TRANSPORT_LAYER_EVENTS =
+ List.of(SecurityAlgorithmUpdate.CONNECTION_EVENT_CS_SIGNALLING_GSM,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_PS_SIGNALLING_GPRS,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_CS_SIGNALLING_3G,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_PS_SIGNALLING_3G,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_NAS_SIGNALLING_LTE,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_LTE,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_NAS_SIGNALLING_5G,
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_5G);
+ private static final List<Integer> NULL_CIPHERS =
+ List.of(SecurityAlgorithmUpdate.SECURITY_ALGORITHM_A50,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_GEA0,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_UEA0,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA0,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_NEA0,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_IMS_NULL,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_SIP_NULL,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_SRTP_NULL,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_OTHER);
+ private static final List<Integer> NON_NULL_CIPHERS =
+ List.of(SecurityAlgorithmUpdate.SECURITY_ALGORITHM_A51,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_A52,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_A53,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_A54,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_GEA1,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_GEA2,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_GEA3,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_GEA4,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_GEA5,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_UEA1,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_UEA2,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA1,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA3,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_NEA1,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_NEA2,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_NEA3,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_SIP_NO_IPSEC_CONFIG,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_AES_GCM,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_AES_GMAC,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_AES_CBC,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_DES_EDE3_CBC,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_AES_EDE3_CBC,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_MD5_96,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_RTP,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_SRTP_AES_COUNTER,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_SRTP_AES_F8,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_SRTP_HMAC_SHA1,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_ENCR_AES_GCM_16,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_ENCR_AES_CBC,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_AUTH_HMAC_SHA2_256_128,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_ORYX);
+
+ private CellularNetworkSecuritySafetySource mSafetySource;
+ private Context mContext;
+ private TestExecutorService mExecutor = new TestExecutorService();
+
+ @Before
+ public void setUp() {
+ mSafetySource = mock(CellularNetworkSecuritySafetySource.class);
+ }
+
+ @Test
+ public void initializeNotifier_notifierAndSafetySourceDisabled() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ assertThat(notifier.isEnabled()).isFalse();
+ verify(mSafetySource, never()).setNullCipherIssueEnabled(any(), anyBoolean());
+ }
+
+ @Test
+ public void enable_enablesSafetySource() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ notifier.enable(mContext);
+
+ assertThat(notifier.isEnabled()).isTrue();
+ verify(mSafetySource, times(1)).setNullCipherIssueEnabled(eq(mContext), eq(true));
+ }
+
+ @Test
+ public void disable_disablesSafetySource() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+ notifier.enable(mContext);
+
+ notifier.disable(mContext);
+
+ assertThat(notifier.isEnabled()).isFalse();
+ verify(mSafetySource, times(1)).setNullCipherIssueEnabled(eq(mContext), eq(false));
+ }
+
+ @Test
+ public void onSecurityAlgorithmUpdate_enabled_unprotectedEmergency_noSafetySourceUpdate() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ SecurityAlgorithmUpdate.CONNECTION_EVENT_AS_SIGNALLING_5G,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+ /* isUnprotectedEmergency= */ true));
+
+ verify(mSafetySource, never()).setNullCipherState(any(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void onSecurityAlgorithmUpdate_enabled_nonTransportLayerEvent_noSafetySourceUpdate() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ for (int connectionEvent : NON_TRANSPORT_LAYER_EVENTS) {
+ clearInvocations(mSafetySource);
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+ /* isUnprotectedEmergency= */ false));
+
+ verify(mSafetySource, never().description("Connection event: " + connectionEvent))
+ .setNullCipherState(any(), anyInt(), anyInt());
+ }
+ }
+
+ @Test
+ public void onUpdate_enabled_transportLayerEvent_encryptionNullCipher_notifyNonEncrypted() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
+ for (int encryptionAlgorithm : NULL_CIPHERS) {
+ clearInvocations(mSafetySource);
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ encryptionAlgorithm,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+ /* isUnprotectedEmergency= */ false));
+
+ verify(
+ mSafetySource,
+ times(1).description(
+ "Connection event: " + connectionEvent
+ + " Encryption algorithm: " + encryptionAlgorithm))
+ .setNullCipherState(
+ eq(mContext),
+ eq(SUB_ID),
+ eq(NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED));
+ }
+ }
+ }
+
+ @Test
+ public void onUpdate_enabled_transportLayerEvent_integrityNullCipher_notifyNonEncrypted() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
+ for (int integrityAlgorithm : NULL_CIPHERS) {
+ clearInvocations(mSafetySource);
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+ integrityAlgorithm,
+ /* isUnprotectedEmergency= */ false));
+
+ verify(
+ mSafetySource,
+ times(1).description(
+ "Connection event: " + connectionEvent
+ + " Integrity algorithm: " + integrityAlgorithm))
+ .setNullCipherState(
+ eq(mContext),
+ eq(SUB_ID),
+ eq(NULL_CIPHER_STATE_NOTIFY_NON_ENCRYPTED));
+ }
+ }
+ }
+
+ @Test
+ public void onUpdate_enabled_transportLayerEvent_encryptionNonNullCipher_encrypted() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
+ for (int encryptionAlgorithm : NON_NULL_CIPHERS) {
+ clearInvocations(mSafetySource);
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ encryptionAlgorithm,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+ /* isUnprotectedEmergency= */ false));
+
+ verify(
+ mSafetySource,
+ times(1).description(
+ "Connection event: " + connectionEvent
+ + " Encryption algorithm: " + encryptionAlgorithm))
+ .setNullCipherState(
+ eq(mContext),
+ eq(SUB_ID),
+ eq(NULL_CIPHER_STATE_ENCRYPTED));
+ }
+ }
+ }
+
+ @Test
+ public void onUpdate_enabled_transportLayerEvent_integrityNonNullCipher_encrypted() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
+ for (int integrityAlgorithm : NON_NULL_CIPHERS) {
+ clearInvocations(mSafetySource);
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+ integrityAlgorithm,
+ /* isUnprotectedEmergency= */ false));
+
+ verify(
+ mSafetySource,
+ times(1).description(
+ "Connection event: " + connectionEvent
+ + " Integrity algorithm: " + integrityAlgorithm))
+ .setNullCipherState(
+ eq(mContext),
+ eq(SUB_ID),
+ eq(NULL_CIPHER_STATE_ENCRYPTED));
+ }
+ }
+ }
+
+ @Test
+ public void onUpdate_enabled_transportLayerEvent_encryptionNonNullCipher_notifyEncrypted() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+
+ for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
+ for (int encryptionAlgorithm : NON_NULL_CIPHERS) {
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ NULL_CIPHERS.get(0),
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+ /* isUnprotectedEmergency= */ false));
+
+ clearInvocations(mSafetySource);
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ encryptionAlgorithm,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_HMAC_SHA1_96,
+ /* isUnprotectedEmergency= */ false));
+
+ verify(
+ mSafetySource,
+ times(1).description(
+ "Connection event: " + connectionEvent
+ + " Encryption algorithm: " + encryptionAlgorithm))
+ .setNullCipherState(
+ eq(mContext),
+ eq(SUB_ID),
+ eq(NULL_CIPHER_STATE_NOTIFY_ENCRYPTED));
+ }
+ }
+ }
+
+ @Test
+ public void onUpdate_enabled_transportLayerEvent_integrityNonNullCipher_notifyEncrypted() {
+ NullCipherNotifier notifier = new NullCipherNotifier(mExecutor, mSafetySource);
+ notifier.enable(mContext);
+
+ for (int connectionEvent : TRANSPORT_LAYER_EVENTS) {
+ for (int integrityAlgorithm : NON_NULL_CIPHERS) {
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+ NULL_CIPHERS.get(0),
+ /* isUnprotectedEmergency= */ false));
+
+ clearInvocations(mSafetySource);
+ notifier.onSecurityAlgorithmUpdate(
+ mContext,
+ SUB_ID,
+ new SecurityAlgorithmUpdate(
+ connectionEvent,
+ SecurityAlgorithmUpdate.SECURITY_ALGORITHM_EEA2,
+ integrityAlgorithm,
+ /* isUnprotectedEmergency= */ false));
+
+ verify(
+ mSafetySource,
+ times(1).description(
+ "Connection event: " + connectionEvent
+ + " Integrity algorithm: " + integrityAlgorithm))
+ .setNullCipherState(
+ eq(mContext),
+ eq(SUB_ID),
+ eq(NULL_CIPHER_STATE_NOTIFY_ENCRYPTED));
+ }
+ }
+ }
+}