aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/internal/telephony
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/android/internal/telephony')
-rw-r--r--src/java/com/android/internal/telephony/GsmCdmaCallTracker.java9
-rw-r--r--src/java/com/android/internal/telephony/GsmCdmaPhone.java76
-rw-r--r--src/java/com/android/internal/telephony/ImsSmsDispatcher.java11
-rw-r--r--src/java/com/android/internal/telephony/MultiSimSettingController.java97
-rw-r--r--src/java/com/android/internal/telephony/NetworkTypeController.java133
-rw-r--r--src/java/com/android/internal/telephony/PhoneConfigurationManager.java3
-rw-r--r--src/java/com/android/internal/telephony/PhoneFactory.java2
-rw-r--r--src/java/com/android/internal/telephony/RIL.java47
-rw-r--r--src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java12
-rw-r--r--src/java/com/android/internal/telephony/SMSDispatcher.java14
-rw-r--r--src/java/com/android/internal/telephony/ServiceStateTracker.java2
-rw-r--r--src/java/com/android/internal/telephony/SmsDispatchersController.java55
-rw-r--r--src/java/com/android/internal/telephony/TelephonyComponentFactory.java21
-rw-r--r--src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java2
-rw-r--r--src/java/com/android/internal/telephony/configupdate/ConfigParser.java100
-rw-r--r--src/java/com/android/internal/telephony/configupdate/ConfigProviderAdaptor.java54
-rw-r--r--src/java/com/android/internal/telephony/configupdate/TelephonyConfigUpdateInstallReceiver.java200
-rw-r--r--src/java/com/android/internal/telephony/data/AccessNetworksManager.java42
-rw-r--r--src/java/com/android/internal/telephony/data/AutoDataSwitchController.java20
-rw-r--r--src/java/com/android/internal/telephony/data/DataConfigManager.java9
-rw-r--r--src/java/com/android/internal/telephony/data/DataNetwork.java36
-rw-r--r--src/java/com/android/internal/telephony/data/DataNetworkController.java60
-rw-r--r--src/java/com/android/internal/telephony/data/DataSettingsManager.java23
-rw-r--r--src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java35
-rw-r--r--src/java/com/android/internal/telephony/domainselection/EmergencyCallDomainSelectionConnection.java20
-rw-r--r--src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java18
-rw-r--r--src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java534
-rw-r--r--src/java/com/android/internal/telephony/euicc/EuiccConnector.java17
-rw-r--r--src/java/com/android/internal/telephony/euicc/EuiccController.java46
-rw-r--r--src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java2
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java13
-rw-r--r--src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java195
-rw-r--r--src/java/com/android/internal/telephony/satellite/SatelliteConfig.java233
-rw-r--r--src/java/com/android/internal/telephony/satellite/SatelliteConfigParser.java94
-rw-r--r--src/java/com/android/internal/telephony/satellite/SatelliteController.java153
-rw-r--r--src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java10
-rw-r--r--src/java/com/android/internal/telephony/security/NullCipherNotifier.java259
-rw-r--r--src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java7
38 files changed, 2174 insertions, 490 deletions
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 5517bc6f4e..9113514c75 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.BroadcastReceiver;
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 class GsmCdmaCallTracker extends CallTracker {
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 @@ public class GsmCdmaCallTracker extends CallTracker {
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 aca759b703..de7ebd6514 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.Context;
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 @@ public class GsmCdmaPhone extends Phone {
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 @@ public class GsmCdmaPhone extends Phone {
mCallWaitingController = new CallWaitingController(this);
- loadTtyMode();
+ if (hasCalling()) {
+ loadTtyMode();
- CallManager.getInstance().registerPhone(this);
+ CallManager.getInstance().registerPhone(this);
+ }
mSubscriptionsChangedListener =
new SubscriptionManager.OnSubscriptionsChangedListener() {
@@ -464,13 +469,21 @@ public class GsmCdmaPhone extends Phone {
}
};
+ 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 @@ public class GsmCdmaPhone extends Phone {
mNullCipherNotifier =
mTelephonyComponentFactory
.inject(NullCipherNotifier.class.getName())
- .makeNullCipherNotifier();
+ .makeNullCipherNotifier(mSafetySource);
mCi.registerForSecurityAlgorithmUpdates(
this, EVENT_SECURITY_ALGORITHM_UPDATE, null);
}
@@ -693,7 +706,7 @@ public class GsmCdmaPhone extends Phone {
unregisterForIccRecordEvents();
registerForIccRecordEvents();
- mCT.updatePhoneType();
+ if (mCT != null) mCT.updatePhoneType();
int radioState = mCi.getRadioState();
if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
@@ -753,6 +766,8 @@ public class GsmCdmaPhone extends Phone {
@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 @@ public class GsmCdmaPhone extends Phone {
@Override
public boolean isDataSuspended() {
+ if (mCT == null) return false;
return mCT.mState != PhoneConstants.State.IDLE && !mSST.isConcurrentVoiceAndDataAllowed();
}
@@ -884,7 +900,7 @@ public class GsmCdmaPhone extends Phone {
@Override
public boolean isInEmergencyCall() {
- if (isPhoneTypeGsm()) {
+ if (!hasCalling() || isPhoneTypeGsm()) {
return false;
} else {
return mCT.isInEmergencyCall();
@@ -893,7 +909,7 @@ public class GsmCdmaPhone extends Phone {
@Override
protected void setIsInEmergencyCall() {
- if (!isPhoneTypeGsm()) {
+ if (!hasCalling() && !isPhoneTypeGsm()) {
mCT.setIsInEmergencyCall();
}
}
@@ -985,6 +1001,7 @@ public class GsmCdmaPhone extends Phone {
@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 @@ public class GsmCdmaPhone extends Phone {
@Override
public void rejectCall() throws CallStateException {
+ if (!hasCalling()) throw new CallStateException();
mCT.rejectCall();
}
@@ -1025,6 +1043,7 @@ public class GsmCdmaPhone extends Phone {
@Override
public boolean canConference() {
+ if (!hasCalling()) return false;
if (mImsPhone != null && mImsPhone.canConference()) {
return true;
}
@@ -1075,12 +1094,13 @@ public class GsmCdmaPhone extends Phone {
@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 @@ public class GsmCdmaPhone extends Phone {
@Override
public void explicitCallTransfer() {
- if (isPhoneTypeGsm()) {
+ if (hasCalling() && isPhoneTypeGsm()) {
mCT.explicitCallTransfer();
} else {
loge("explicitCallTransfer: not possible in CDMA");
@@ -1104,11 +1124,13 @@ public class GsmCdmaPhone extends Phone {
@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 @@ public class GsmCdmaPhone extends Phone {
private boolean handleCallDeflectionIncallSupplementaryService(
String dialString) {
- if (dialString.length() > 1) {
+ if (!hasCalling() || dialString.length() > 1) {
return false;
}
@@ -1209,7 +1231,7 @@ public class GsmCdmaPhone extends Phone {
private boolean handleCallWaitingIncallSupplementaryService(String dialString) {
int len = dialString.length();
- if (len > 2) {
+ if (!hasCalling() || len > 2) {
return false;
}
@@ -1429,6 +1451,9 @@ public class GsmCdmaPhone extends Phone {
@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 @@ public class GsmCdmaPhone extends Phone {
@Override
public int getEmergencyNumberDbVersion() {
- return getEmergencyNumberTracker().getEmergencyNumberDbVersion();
+ EmergencyNumberTracker tracker = getEmergencyNumberTracker();
+ if (tracker == null) return -1;
+ return tracker.getEmergencyNumberDbVersion();
}
@Override
@@ -3134,6 +3161,8 @@ public class GsmCdmaPhone extends Phone {
*/
@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 @@ public class GsmCdmaPhone extends Phone {
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 @@ public class GsmCdmaPhone extends Phone {
&& 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 @@ public class GsmCdmaPhone extends Phone {
* 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 @@ public class GsmCdmaPhone extends Phone {
* 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 @@ public class GsmCdmaPhone extends Phone {
// 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 4e4d55d34b..4146c245dc 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -203,12 +203,12 @@ public class ImsSmsDispatcher extends SMSDispatcher {
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 @@ public class ImsSmsDispatcher extends SMSDispatcher {
} 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 @@ public class ImsSmsDispatcher extends SMSDispatcher {
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 8488ab0359..aaeba23622 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.annotation.NonNull;
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 android.util.Log;
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 @@ public class MultiSimSettingController extends Handler {
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 @@ public class MultiSimSettingController extends Handler {
/**
* 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 @@ public class MultiSimSettingController extends Handler {
}
@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 @@ public class MultiSimSettingController extends Handler {
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 @@ public class MultiSimSettingController extends Handler {
|| 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 @@ public class MultiSimSettingController extends Handler {
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 @@ public class MultiSimSettingController extends Handler {
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 b9ad388bbf..67ca1e1aaf 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.AsyncResult;
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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
// 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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
} 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 @@ public class NetworkTypeController extends StateMachine {
}
}
+ /**
+ * 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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
}
removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
mIsSecondaryTimerActive = false;
+ mSecondaryTimerExpireTimestamp = 0;
mSecondaryTimerState = "";
transitionToCurrentState();
return;
@@ -1435,10 +1474,11 @@ public class NetworkTypeController extends StateMachine {
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 @@ public class NetworkTypeController extends StateMachine {
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/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 49e1e387fd..ef96d892c0 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -560,7 +560,8 @@ public class PhoneConfigurationManager {
}
public int getNumberOfModemsWithSimultaneousVoiceConnections() {
- return getStaticPhoneCapability().getMaxActiveVoiceSubscriptions();
+ return maybeOverrideMaxActiveVoiceSubscriptions(mStaticCapability)
+ .getMaxActiveVoiceSubscriptions();
}
public boolean isVirtualDsdaEnabled() {
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index c5bc4283ec..803fb19185 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -212,7 +212,7 @@ public class PhoneFactory {
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 5177adb3b9..8b3be1ea84 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.NonNull;
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 @@ public class RIL extends BaseCommands implements CommandsInterface {
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 @@ public class RIL extends BaseCommands implements CommandsInterface {
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 @@ public class RIL extends BaseCommands implements CommandsInterface {
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 class RIL extends BaseCommands implements CommandsInterface {
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 @@ public class RIL extends BaseCommands implements CommandsInterface {
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 @@ public class RIL extends BaseCommands implements CommandsInterface {
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 @@ public class RIL extends BaseCommands implements CommandsInterface {
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 @@ public class RIL extends BaseCommands implements CommandsInterface {
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 bab4d12185..4d9196e469 100644
--- a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
+++ b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
@@ -86,6 +86,13 @@ public class RadioInterfaceCapabilityController extends Handler {
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 @@ public class RadioInterfaceCapabilityController extends Handler {
// 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 @@ public class RadioInterfaceCapabilityController extends Handler {
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 04f5c083ba..498535bc4e 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -1013,10 +1013,12 @@ public abstract class SMSDispatcher extends Handler {
* 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 @@ public abstract class SMSDispatcher extends Handler {
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 @@ public abstract class SMSDispatcher extends Handler {
// 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 @@ public abstract class SMSDispatcher extends Handler {
} 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 @@ public abstract class SMSDispatcher extends Handler {
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/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 88d541c38f..ba6479ed03 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -739,7 +739,7 @@ public class ServiceStateTracker extends Handler {
mAccessNetworksManagerCallback = new AccessNetworksManagerCallback(this::post) {
@Override
- public void onPreferredTransportChanged(int networkCapability) {
+ public void onPreferredTransportChanged(int networkCapability, boolean forceReconnect) {
// Check if preferred on IWLAN was changed in ServiceState.
boolean isIwlanPreferred = mAccessNetworksManager.isAnyApnOnIwlan();
if (mSS.isIwlanPreferred() != isIwlanPreferred) {
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 8795840e9a..cc287f8055 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -109,6 +109,9 @@ public class SmsDispatchersController extends Handler {
/** 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 @@ public class SmsDispatchersController extends Handler {
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 @@ public class SmsDispatchersController extends Handler {
(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 @@ public class SmsDispatchersController extends Handler {
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 @@ public class SmsDispatchersController extends Handler {
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 @@ public class SmsDispatchersController extends Handler {
+ "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 @@ public class SmsDispatchersController extends Handler {
* @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 @@ public class SmsDispatchersController extends Handler {
/**
* 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 @@ public class SmsDispatchersController extends Handler {
* 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 f5aa0746c5..5da4b12b5a 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -305,8 +305,9 @@ public class TelephonyComponentFactory {
/**
* 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 @@ public class TelephonyComponentFactory {
* @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 @@ public class TelephonyComponentFactory {
* @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 @@ public class TelephonyComponentFactory {
}
/** 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 2119003d14..f40b6d6c0a 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 @@ public class CdmaSMSDispatcher extends SMSDispatcher {
// 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/configupdate/ConfigParser.java b/src/java/com/android/internal/telephony/configupdate/ConfigParser.java
new file mode 100644
index 0000000000..5c3ac866df
--- /dev/null
+++ b/src/java/com/android/internal/telephony/configupdate/ConfigParser.java
@@ -0,0 +1,100 @@
+/*
+ * 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.configupdate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class ConfigParser<T> {
+
+ public static final int VERSION_UNKNOWN = -1;
+
+ protected int mVersion = VERSION_UNKNOWN;
+
+ protected T mConfig;
+
+ /**
+ * Constructs a parser from the raw data
+ *
+ * @param data the config data
+ */
+ public ConfigParser(@Nullable byte[] data) {
+ parseData(data);
+ }
+
+ /**
+ * Constructs a parser from the input stream
+ *
+ * @param input the input stream of the config
+ * @return the instance of the ConfigParser
+ */
+ public ConfigParser(@NonNull InputStream input) throws IOException {
+ parseData(input.readAllBytes());
+ }
+
+ /**
+ * Constructs a parser from the input stream
+ *
+ * @param file the input file of the config
+ * @return the instance of the ConfigParser
+ */
+ public ConfigParser(@NonNull File file) throws IOException {
+ try (FileInputStream fis = new FileInputStream(file)) {
+ parseData(fis.readAllBytes());
+ }
+ }
+
+ /**
+ * Get the version of the config
+ *
+ * @return the version of the config if it is defined, or VERSION_UNKNOWN
+ */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Get the config
+ *
+ * @return the config
+ */
+ public @Nullable T getConfig() {
+ return mConfig;
+ }
+
+ /**
+ * Get the sub config raw data by id
+ *
+ * @param id the identifier of the sub config
+ * @return the raw data of the sub config
+ */
+ public @Nullable byte[] getData(String id) {
+ return null;
+ }
+
+ /**
+ * Parse the config data
+ *
+ * @param data the config data
+ */
+ protected abstract void parseData(@Nullable byte[] data);
+}
diff --git a/src/java/com/android/internal/telephony/configupdate/ConfigProviderAdaptor.java b/src/java/com/android/internal/telephony/configupdate/ConfigProviderAdaptor.java
new file mode 100644
index 0000000000..1344534daf
--- /dev/null
+++ b/src/java/com/android/internal/telephony/configupdate/ConfigProviderAdaptor.java
@@ -0,0 +1,54 @@
+/*
+ * 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.configupdate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+
+public interface ConfigProviderAdaptor {
+
+ String DOMAIN_SATELLITE = "satellite";
+
+ /**
+ * Get the config from the provider
+ */
+ @Nullable ConfigParser getConfigParser(String domain);
+
+ class Callback {
+ /**
+ * The callback when the config is changed
+ * @param config the config is changed
+ */
+ public void onChanged(@Nullable ConfigParser config) {}
+ }
+
+ /**
+ * Register the callback to monitor the config change
+ * @param executor The executor to execute the callback.
+ * @param callback the callback to monitor the config change
+ */
+ void registerCallback(@NonNull Executor executor, @NonNull Callback callback);
+
+ /**
+ * Unregister the callback
+ * @param callback the callback to be unregistered
+ */
+ void unregisterCallback(@NonNull Callback callback);
+}
diff --git a/src/java/com/android/internal/telephony/configupdate/TelephonyConfigUpdateInstallReceiver.java b/src/java/com/android/internal/telephony/configupdate/TelephonyConfigUpdateInstallReceiver.java
new file mode 100644
index 0000000000..0db7844c8d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/configupdate/TelephonyConfigUpdateInstallReceiver.java
@@ -0,0 +1,200 @@
+/*
+ * 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.configupdate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.satellite.SatelliteConfigParser;
+import com.android.server.updates.ConfigUpdateInstallReceiver;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+public class TelephonyConfigUpdateInstallReceiver extends ConfigUpdateInstallReceiver implements
+ ConfigProviderAdaptor {
+
+ private static final String TAG = "TelephonyConfigUpdateInstallReceiver";
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected static final String UPDATE_DIR = "/data/misc/telephonyconfig";
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected static final String UPDATE_CONTENT_PATH = "telephony_config.pb";
+ protected static final String UPDATE_METADATA_PATH = "metadata/";
+ public static final String VERSION = "version";
+
+ private ConcurrentHashMap<Executor, Callback> mCallbackHashMap = new ConcurrentHashMap<>();
+ @NonNull
+ private final Object mConfigParserLock = new Object();
+ @GuardedBy("mConfigParserLock")
+ private ConfigParser mConfigParser;
+
+
+ public static TelephonyConfigUpdateInstallReceiver sReceiverAdaptorInstance =
+ new TelephonyConfigUpdateInstallReceiver();
+
+ /**
+ * @return The singleton instance of TelephonyConfigUpdateInstallReceiver
+ */
+ @NonNull
+ public static TelephonyConfigUpdateInstallReceiver getInstance() {
+ return sReceiverAdaptorInstance;
+ }
+
+ public TelephonyConfigUpdateInstallReceiver() {
+ super(UPDATE_DIR, UPDATE_CONTENT_PATH, UPDATE_METADATA_PATH, VERSION);
+ }
+
+ /**
+ * @return byte array type of config data protobuffer file
+ */
+ @Nullable
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public byte[] getCurrentContent() {
+ try {
+ return IoUtils.readFileAsByteArray(updateContent.getCanonicalPath());
+ } catch (IOException e) {
+ Slog.i(TAG, "Failed to read current content, assuming first update!");
+ return null;
+ }
+ }
+
+ @Override
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public void postInstall(Context context, Intent intent) {
+ Log.d(TAG, "Telephony config is updated in file partition");
+ ConfigParser updatedConfigParser = getNewConfigParser(DOMAIN_SATELLITE,
+ getCurrentContent());
+
+ if (updatedConfigParser == null) {
+ Log.d(TAG, "updatedConfigParser is null");
+ return;
+ }
+
+ boolean isParserChanged = false;
+
+ synchronized (getInstance().mConfigParserLock) {
+ if (getInstance().mConfigParser == null) {
+ getInstance().mConfigParser = updatedConfigParser;
+ isParserChanged = true;
+ } else {
+ int updatedVersion = updatedConfigParser.mVersion;
+ int previousVersion = getInstance().mConfigParser.mVersion;
+ Log.d(TAG, "previous version is " + previousVersion + " | updated version is "
+ + updatedVersion);
+ if (updatedVersion > previousVersion) {
+ getInstance().mConfigParser = updatedConfigParser;
+ isParserChanged = true;
+ }
+ }
+ }
+
+ if (isParserChanged) {
+ if (getInstance().mCallbackHashMap.keySet().isEmpty()) {
+ Log.d(TAG, "mCallbackHashMap.keySet().isEmpty");
+ return;
+ }
+ Iterator<Executor> iterator =
+ getInstance().mCallbackHashMap.keySet().iterator();
+ while (iterator.hasNext()) {
+ Executor executor = iterator.next();
+ getInstance().mCallbackHashMap.get(executor).onChanged(
+ updatedConfigParser);
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public ConfigParser getConfigParser(String domain) {
+ Log.d(TAG, "getConfigParser");
+ synchronized (getInstance().mConfigParserLock) {
+ if (getInstance().mConfigParser == null) {
+ Log.d(TAG, "CreateNewConfigParser with domain " + domain);
+ getInstance().mConfigParser = getNewConfigParser(domain, getCurrentContent());
+ }
+ return getInstance().mConfigParser;
+ }
+ }
+
+ @Override
+ public void registerCallback(@NonNull Executor executor, @NonNull Callback callback) {
+ mCallbackHashMap.put(executor, callback);
+ }
+
+ @Override
+ public void unregisterCallback(@NonNull Callback callback) {
+ Iterator<Executor> iterator = mCallbackHashMap.keySet().iterator();
+ while (iterator.hasNext()) {
+ Executor executor = iterator.next();
+ if (mCallbackHashMap.get(executor) == callback) {
+ mCallbackHashMap.remove(executor);
+ break;
+ }
+ }
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public File getUpdateDir() {
+ return getInstance().updateDir;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public File getUpdateContent() {
+ return getInstance().updateContent;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public ConcurrentHashMap<Executor, Callback> getCallbackMap() {
+ return getInstance().mCallbackHashMap;
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public void setCallbackMap(ConcurrentHashMap<Executor, Callback> map) {
+ getInstance().mCallbackHashMap = map;
+ }
+
+ /**
+ * @param data byte array type of config data
+ * @return when data is null, return null otherwise return ConfigParser
+ */
+ @Nullable
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public ConfigParser getNewConfigParser(String domain, @Nullable byte[] data) {
+ if (data == null) {
+ Log.d(TAG, "content data is null");
+ return null;
+ }
+ switch (domain) {
+ case DOMAIN_SATELLITE:
+ return new SatelliteConfigParser(data);
+ default:
+ Log.e(TAG, "DOMAIN should be specified");
+ return null;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
index a657ecd8cf..3d3fbe959c 100644
--- a/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/data/AccessNetworksManager.java
@@ -234,6 +234,11 @@ public class AccessNetworksManager extends Handler {
.mapToObj(AccessNetworkType::toString).collect(Collectors.joining(","))
+ "]");
+ handleQualifiedNetworksChanged(apnTypes, qualifiedNetworkTypes, false);
+ }
+
+ private void handleQualifiedNetworksChanged(
+ int apnTypes, int[] qualifiedNetworkTypes, boolean forceReconnect) {
if (Arrays.stream(qualifiedNetworkTypes).anyMatch(accessNetwork
-> !DataUtils.isValidAccessNetwork(accessNetwork))) {
loge("Invalid access networks " + Arrays.toString(qualifiedNetworkTypes));
@@ -274,8 +279,9 @@ public class AccessNetworksManager extends Handler {
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
mAccessNetworksManagerCallbacks.forEach(callback ->
callback.invokeFromExecutor(() ->
- callback.onPreferredTransportChanged(DataUtils
- .apnTypeToNetworkCapability(apnType))));
+ callback.onPreferredTransportChanged(
+ DataUtils.apnTypeToNetworkCapability(apnType),
+ forceReconnect)));
}
} else {
mAvailableNetworks.put(apnType, qualifiedNetworkTypes);
@@ -297,7 +303,7 @@ public class AccessNetworksManager extends Handler {
}
if (!qualifiedNetworksList.isEmpty()) {
- setPreferredTransports(qualifiedNetworksList);
+ setPreferredTransports(qualifiedNetworksList, forceReconnect);
mQualifiedNetworksChangedRegistrants.notifyResult(qualifiedNetworksList);
}
}
@@ -338,6 +344,17 @@ public class AccessNetworksManager extends Handler {
}
});
}
+
+ @Override
+ public void onReconnectQualifedNetworkType(int apnTypes, int qualifiedNetworkType) {
+ if (mFeatureFlags.reconnectQualifiedNetwork()) {
+ log("onReconnectQualifedNetworkType: apnTypes = ["
+ + ApnSetting.getApnTypesStringFromBitmask(apnTypes)
+ + "], networks = [" + AccessNetworkType.toString(qualifiedNetworkType)
+ + "]");
+ handleQualifiedNetworksChanged(apnTypes, new int[]{qualifiedNetworkType}, true);
+ }
+ }
}
private void onEmergencyDataNetworkPreferredTransportChanged(
@@ -371,8 +388,10 @@ public class AccessNetworksManager extends Handler {
* Called when preferred transport changed.
*
* @param networkCapability The network capability.
+ * @param forceReconnect whether enforce reconnection to the preferred transport type.
*/
- public abstract void onPreferredTransportChanged(@NetCapability int networkCapability);
+ public abstract void onPreferredTransportChanged(
+ @NetCapability int networkCapability, boolean forceReconnect);
}
/**
@@ -610,7 +629,8 @@ public class AccessNetworksManager extends Handler {
: AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
}
- private void setPreferredTransports(@NonNull List<QualifiedNetworks> networksList) {
+ private void setPreferredTransports(
+ @NonNull List<QualifiedNetworks> networksList, boolean forceReconnect) {
for (QualifiedNetworks networks : networksList) {
if (networks.qualifiedNetworks.length > 0) {
int transport = getTransportFromAccessNetwork(networks.qualifiedNetworks[0]);
@@ -618,11 +638,13 @@ public class AccessNetworksManager extends Handler {
mPreferredTransports.put(networks.apnType, transport);
mAccessNetworksManagerCallbacks.forEach(callback ->
callback.invokeFromExecutor(() ->
- callback.onPreferredTransportChanged(DataUtils
- .apnTypeToNetworkCapability(networks.apnType))));
+ callback.onPreferredTransportChanged(
+ DataUtils.apnTypeToNetworkCapability(networks.apnType),
+ forceReconnect)));
logl("setPreferredTransports: apnType="
+ ApnSetting.getApnTypeString(networks.apnType) + ", transport="
- + AccessNetworkConstants.transportTypeToString(transport));
+ + AccessNetworkConstants.transportTypeToString(transport)
+ + (forceReconnect ? ", forceReconnect:true" : ""));
}
}
}
@@ -643,7 +665,7 @@ public class AccessNetworksManager extends Handler {
* Get the preferred transport by network capability.
*
* @param networkCapability The network capability. (Note that only APN-type capabilities are
- * supported.
+ * supported.)
* @return The preferred transport.
*/
public @TransportType int getPreferredTransportByNetworkCapability(
@@ -662,7 +684,7 @@ public class AccessNetworksManager extends Handler {
* @return {@code true} if there is any APN is on IWLAN, otherwise {@code false}.
*/
public boolean isAnyApnOnIwlan() {
- for (int apnType : AccessNetworksManager.SUPPORTED_APN_TYPES) {
+ for (int apnType : SUPPORTED_APN_TYPES) {
if (getPreferredTransport(apnType) == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
return true;
}
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
index 02c459a2b2..343bb0b2ed 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 @@ public class AutoDataSwitchController extends Handler {
* 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 @@ public class AutoDataSwitchController extends Handler {
DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
mScoreTolerance = dataConfig.getAutoDataSwitchScoreTolerance();
mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired();
+ mAllowNddsRoamning = dataConfig.doesAutoDataSwitchAllowRoaming();
mAutoDataSwitchAvailabilityStabilityTimeThreshold =
dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
mAutoDataSwitchPerformanceStabilityTimeThreshold =
@@ -694,7 +701,7 @@ public class AutoDataSwitchController extends Handler {
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 @@ public class AutoDataSwitchController extends Handler {
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 @@ public class AutoDataSwitchController extends Handler {
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 @@ public class AutoDataSwitchController extends Handler {
}
/**
+ * @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 90743f85d8..0e06dad972 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 @@ public class DataConfigManager extends Handler {
}
/**
+ * 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 c1e61c6f08..0dbbc5c024 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 @@ public class DataNetwork extends StateMachine {
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(
@@ -1198,7 +1200,7 @@ public class DataNetwork extends StateMachine {
getHandler()::post) {
@Override
public void onPreferredTransportChanged(
- @NetCapability int networkCapability) {
+ @NetCapability int networkCapability, boolean forceReconnect) {
if (networkCapability == NetworkCapabilities.NET_CAPABILITY_MMS) {
log("MMS preference changed.");
updateNetworkCapabilities();
@@ -1223,12 +1225,14 @@ public class DataNetwork extends StateMachine {
}
// 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 @@ public class DataNetwork extends StateMachine {
} 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 @@ public class DataNetwork extends StateMachine {
// 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 @@ public class DataNetwork extends StateMachine {
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 2fb23a738e..70d3b23142 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.BroadcastReceiver;
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 @@ public class DataNetworkController extends Handler {
}
};
+ 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 @@ public class DataNetworkController extends Handler {
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 @@ public class DataNetworkController extends Handler {
}
});
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() {
@@ -975,13 +982,16 @@ public class DataNetworkController extends Handler {
mAccessNetworksManager.registerCallback(new AccessNetworksManagerCallback(this::post) {
@Override
- public void onPreferredTransportChanged(@NetCapability int capability) {
+ public void onPreferredTransportChanged(
+ @NetCapability int capability, boolean forceReconnect) {
int preferredTransport = mAccessNetworksManager
.getPreferredTransportByNetworkCapability(capability);
logl("onPreferredTransportChanged: "
+ DataUtils.networkCapabilityToString(capability) + " preferred on "
- + AccessNetworkConstants.transportTypeToString(preferredTransport));
- DataNetworkController.this.onEvaluatePreferredTransport(capability);
+ + AccessNetworkConstants.transportTypeToString(preferredTransport)
+ + (forceReconnect ? "forceReconnect:true" : ""));
+
+ DataNetworkController.this.onEvaluatePreferredTransport(capability, forceReconnect);
if (!hasMessages(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS)) {
sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
DataEvaluationReason.PREFERRED_TRANSPORT_CHANGED));
@@ -1042,16 +1052,18 @@ public class DataNetworkController extends Handler {
}
}, 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);
@@ -1165,7 +1177,7 @@ public class DataNetworkController extends Handler {
}
break;
case EVENT_EVALUATE_PREFERRED_TRANSPORT:
- onEvaluatePreferredTransport(msg.arg1);
+ onEvaluatePreferredTransport(msg.arg1, msg.arg2 != 0 /* forceReconnect */);
break;
case EVENT_SUBSCRIPTION_PLANS_CHANGED:
SubscriptionPlan[] plans = (SubscriptionPlan[]) msg.obj;
@@ -1566,7 +1578,7 @@ public class DataNetworkController extends Handler {
}
// 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);
@@ -3359,8 +3371,12 @@ public class DataNetworkController extends Handler {
* Called when needed to evaluate the preferred transport for certain capability.
*
* @param capability The network capability to evaluate.
+ * @param forceReconnect indicates whether enforce reconnection to move to the preferred
+ * transport type.
+ *
*/
- private void onEvaluatePreferredTransport(@NetCapability int capability) {
+ private void onEvaluatePreferredTransport(
+ @NetCapability int capability, boolean forceReconnect) {
int preferredTransport = mAccessNetworksManager
.getPreferredTransportByNetworkCapability(capability);
log("onEvaluatePreferredTransport: " + DataUtils.networkCapabilityToString(capability)
@@ -3384,7 +3400,13 @@ public class DataNetworkController extends Handler {
continue;
}
- tryHandoverDataNetwork(dataNetwork, preferredTransport, null/*handoverRetryEntry*/);
+ if (forceReconnect) {
+ tearDownGracefully(
+ dataNetwork, DataNetwork.TEAR_DOWN_REASON_HANDOVER_NOT_ALLOWED);
+ } else {
+ tryHandoverDataNetwork(
+ dataNetwork, preferredTransport, null/*handoverRetryEntry*/);
+ }
}
}
}
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index e54f6d382e..51e5b7d8f1 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.CallbackExecutor;
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.PhoneConstants;
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.FileDescriptor;
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 @@ public class DataSettingsManager extends Handler {
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 @@ public class DataSettingsManager extends Handler {
* @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 @@ public class DataSettingsManager extends Handler {
}
}
+ 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 @@ public class DataSettingsManager extends Handler {
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 893509cc98..ee8890aebd 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.PhoneConstants;
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 @@ public class DataStallRecoveryManager extends Handler {
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 @@ public class DataStallRecoveryManager extends Handler {
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 @@ public class DataStallRecoveryManager extends Handler {
* @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 @@ public class DataStallRecoveryManager extends Handler {
@NonNull Phone phone,
@NonNull DataNetworkController dataNetworkController,
@NonNull DataServiceManager dataServiceManager,
+ @NonNull FeatureFlags featureFlags,
@NonNull Looper looper,
@NonNull DataStallRecoveryManagerCallback callback) {
super(looper);
@@ -269,6 +276,7 @@ public class DataStallRecoveryManager extends Handler {
log("DataStallRecoveryManager created.");
mDataNetworkController = dataNetworkController;
mWwanDataServiceManager = dataServiceManager;
+ mFeatureFlags = featureFlags;
mDataConfigManager = mDataNetworkController.getDataConfigManager();
mDataNetworkController
.getDataSettingsManager()
@@ -288,7 +296,7 @@ public class DataStallRecoveryManager extends Handler {
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 @@ public class DataStallRecoveryManager extends Handler {
mTimeLastRecoveryStartMs = 0;
mLastAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
mRecoveryAction = RECOVERY_ACTION_GET_DATA_CALL_LIST;
+ mValidationCount = 0;
+ mActionValidationCount = 0;
}
/**
@@ -556,8 +566,16 @@ public class DataStallRecoveryManager extends Handler {
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 @@ public class DataStallRecoveryManager extends Handler {
*/
@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 @@ public class DataStallRecoveryManager extends Handler {
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 e0e0354552..66b977d7ca 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.Annotation.NetCapability;
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;
@@ -162,7 +164,8 @@ public class EmergencyCallDomainSelectionConnection extends DomainSelectionConne
private AccessNetworksManager.AccessNetworksManagerCallback mPreferredTransportCallback =
new AccessNetworksManager.AccessNetworksManagerCallback(Runnable::run) {
@Override
- public void onPreferredTransportChanged(@NetCapability int capability) {
+ public void onPreferredTransportChanged(
+ @NetCapability int capability, boolean forceReconnect) {
}
};
@@ -214,6 +217,19 @@ public class EmergencyCallDomainSelectionConnection extends DomainSelectionConne
@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)
@@ -222,7 +238,7 @@ public class EmergencyCallDomainSelectionConnection extends DomainSelectionConne
.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 e2418c5acd..02dd613b88 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.Phone;
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 @@ public class EmergencyNumberTracker extends Handler {
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 class EmergencyNumberTracker extends Handler {
}
};
- 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 a35cccff09..c4d5355734 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.CarrierConfigManager;
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.Arrays;
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 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
}
};
- /**
- * 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 @@ public class EmergencyStateTracker {
@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 class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
}
@VisibleForTesting
- public static final int MSG_SET_EMERGENCY_MODE = 1;
- @VisibleForTesting
- public static final int MSG_EXIT_EMERGENCY_MODE = 2;
- @VisibleForTesting
- public static final int MSG_SET_EMERGENCY_MODE_DONE = 3;
+ public static final int MSG_SET_EMERGENCY_MODE_DONE = 1;
@VisibleForTesting
- public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 4;
+ public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 2;
@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 @@ public class EmergencyStateTracker {
// 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 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
// 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 @@ public class EmergencyStateTracker {
// 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 @@ public class EmergencyStateTracker {
}
if (wasActive && mActiveEmergencyCalls.isEmpty()
- && isEmergencyCallbackModeSupported()) {
+ && isEmergencyCallbackModeSupported(mPhone)) {
enterEmergencyCallbackMode();
if (mOngoingConnection == null) {
@@ -604,7 +596,12 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
// 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 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
*
* @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 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
* 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 @@ public class EmergencyStateTracker {
/**
* 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 @@ public class EmergencyStateTracker {
((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 @@ public class EmergencyStateTracker {
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 class EmergencyStateTracker {
*/
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 @@ public class EmergencyStateTracker {
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 @@ public class EmergencyStateTracker {
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<>();
- if (!isInEmergencyMode()) {
- setEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN,
- MSG_SET_EMERGENCY_MODE_DONE);
+ 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);
+ }
}
+
return mSmsEmergencyModeFuture;
}
@@ -1230,35 +1250,142 @@ public class EmergencyStateTracker {
* @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);
}
+ }
+
+ // 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, false);
+ exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+ clearEmergencySmsInfo();
}
+ }
+ }
- 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 @@ public class EmergencyStateTracker {
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/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index 3c444efdda..4e773f3c4c 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -215,6 +215,11 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
public interface GetAvailableMemoryInBytesCommandCallback extends BaseEuiccCommandCallback {
/** Called when the available memory in bytes lookup has completed. */
void onGetAvailableMemoryInBytesComplete(long availableMemoryInBytes);
+ /**
+ * Called when the connected LPA does not implement
+ * EuiccService#onGetAvailableMemoryInBytes(int).
+ */
+ void onUnsupportedOperationExceptionComplete(String message);
}
/** Callback class for {@link #getOtaStatus}. */
@@ -789,6 +794,18 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
onCommandEnd(callback);
});
}
+
+ @Override
+ public void onUnsupportedOperationException(
+ String message) {
+ sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
+ ((GetAvailableMemoryInBytesCommandCallback)
+ callback)
+ .onUnsupportedOperationExceptionComplete(
+ message);
+ onCommandEnd(callback);
+ });
+ }
});
break;
}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 6dd6f657c8..18b4b149d7 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 @@ public class EuiccController extends IEuiccController.Stub {
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;
}
}
@@ -1888,10 +1893,12 @@ public class EuiccController extends IEuiccController.Stub {
return awaitResult(latch, eidRef);
}
- private long blockingGetAvailableMemoryInBytesFromEuiccService(int cardId) {
+ private long blockingGetAvailableMemoryInBytesFromEuiccService(int cardId)
+ throws UnsupportedOperationException {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Long> memoryRef =
new AtomicReference<>(EuiccManager.EUICC_MEMORY_FIELD_UNAVAILABLE);
+ AtomicReference<Exception> exceptionRef = new AtomicReference();
mConnector.getAvailableMemoryInBytes(
cardId,
new EuiccConnector.GetAvailableMemoryInBytesCommandCallback() {
@@ -1902,11 +1909,24 @@ public class EuiccController extends IEuiccController.Stub {
}
@Override
+ public void onUnsupportedOperationExceptionComplete(String message) {
+ exceptionRef.set(new UnsupportedOperationException(message));
+ latch.countDown();
+ }
+
+ @Override
public void onEuiccServiceUnavailable() {
latch.countDown();
}
});
- return awaitResult(latch, memoryRef);
+ try {
+ return awaitResultOrException(latch, memoryRef, exceptionRef);
+ } catch (UnsupportedOperationException uoe) {
+ throw uoe;
+ } catch (Exception e) {
+ // Other type of exceptions are not expected here but re-throw in case that happens.
+ throw new UnsupportedOperationException(e);
+ }
}
private @OtaStatus int blockingGetOtaStatusFromEuiccService(int cardId) {
@@ -1956,6 +1976,24 @@ public class EuiccController extends IEuiccController.Stub {
return resultRef.get();
}
+ private static <T> T awaitResultOrException(
+ CountDownLatch latch,
+ AtomicReference<T> resultRef,
+ AtomicReference<Exception> resultException)
+ throws Exception {
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ if (resultException.get() != null) {
+ throw resultException.get();
+ }
+
+ return resultRef.get();
+ }
+
// Returns whether the caller has carrier privilege on the given subscription.
private boolean checkCarrierPrivilegeInMetadata(DownloadableSubscription subscription,
String callingPackage) {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index dae808adb5..e5afbeb9a3 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 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
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 b5a052d518..dcb3b207af 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 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
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/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
index 387495e109..cd5b7d61c6 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.ServiceState;
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.DataNetwork;
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 @@ public class DataStallRecoveryStats {
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 @@ public class DataStallRecoveryStats {
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 @@ public class DataStallRecoveryStats {
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 @@ public class DataStallRecoveryStats {
*/
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 @@ public class DataStallRecoveryStats {
* @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 @@ public class DataStallRecoveryStats {
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/satellite/SatelliteConfig.java b/src/java/com/android/internal/telephony/satellite/SatelliteConfig.java
new file mode 100644
index 0000000000..8d7e723184
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteConfig.java
@@ -0,0 +1,233 @@
+/*
+ * 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.satellite;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.satellite.nano.SatelliteConfigData;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * SatelliteConfig is utility class for satellite.
+ * It is obtained through the getConfig() at the SatelliteConfigParser.
+ */
+public class SatelliteConfig {
+
+ private static final String TAG = "SatelliteConfig";
+ private static final String SATELLITE_DIR_NAME = "satellite";
+ private static final String S2_CELL_FILE_NAME = "s2_cell_file";
+ private int mVersion;
+ private Map<Integer, Map<String, Set<Integer>>> mSupportedServicesPerCarrier;
+ private List<String> mSatelliteRegionCountryCodes;
+ private Boolean mIsSatelliteRegionAllowed;
+ private Path mSatS2FilePath;
+ private SatelliteConfigData.SatelliteConfigProto mConfigData;
+
+ public SatelliteConfig(SatelliteConfigData.SatelliteConfigProto configData) {
+ mConfigData = configData;
+ mVersion = mConfigData.version;
+ mSupportedServicesPerCarrier = getCarrierSupportedSatelliteServices();
+ mSatelliteRegionCountryCodes = List.of(
+ mConfigData.deviceSatelliteRegion.countryCodes);
+ mIsSatelliteRegionAllowed = mConfigData.deviceSatelliteRegion.isAllowed;
+ mSatS2FilePath = null;
+
+ Log.d(TAG, "mVersion:" + mVersion + " | "
+ + "mSupportedServicesPerCarrier:" + mSupportedServicesPerCarrier + " | "
+ + "mSatelliteRegionCountryCodes:" + mSatelliteRegionCountryCodes + " | "
+ + "mIsSatelliteRegionAllowed:" + mIsSatelliteRegionAllowed + " | "
+ + "s2CellFile size:" + mConfigData.deviceSatelliteRegion.s2CellFile.length);
+ }
+
+ /**
+ * @return a Map data with carrier_id, plmns and allowed_services.
+ */
+ private Map<Integer, Map<String, Set<Integer>>> getCarrierSupportedSatelliteServices() {
+ SatelliteConfigData.CarrierSupportedSatelliteServicesProto[] satelliteServices =
+ mConfigData.carrierSupportedSatelliteServices;
+ Map<Integer, Map<String, Set<Integer>>> carrierToServicesMap = new HashMap<>();
+ for (SatelliteConfigData.CarrierSupportedSatelliteServicesProto carrierProto :
+ satelliteServices) {
+ SatelliteConfigData.SatelliteProviderCapabilityProto[] satelliteCapabilities =
+ carrierProto.supportedSatelliteProviderCapabilities;
+ Map<String, Set<Integer>> satelliteCapabilityMap = new HashMap<>();
+ for (SatelliteConfigData.SatelliteProviderCapabilityProto capabilityProto :
+ satelliteCapabilities) {
+ String carrierPlmn = capabilityProto.carrierPlmn;
+ Set<Integer> allowedServices = new HashSet<>();
+ for (int service : capabilityProto.allowedServices) {
+ allowedServices.add(service);
+ }
+ satelliteCapabilityMap.put(carrierPlmn, allowedServices);
+ }
+ carrierToServicesMap.put(carrierProto.carrierId, satelliteCapabilityMap);
+ }
+ return carrierToServicesMap;
+ }
+
+ /**
+ * Get satellite plmns for carrier
+ *
+ * @param carrierId the carrier identifier.
+ * @return Plmns corresponding to carrier identifier.
+ */
+ @NonNull
+ public List<String> getAllSatellitePlmnsForCarrier(int carrierId) {
+ if (mSupportedServicesPerCarrier != null) {
+ Map<String, Set<Integer>> satelliteCapabilitiesMap = mSupportedServicesPerCarrier.get(
+ carrierId);
+ if (satelliteCapabilitiesMap != null) {
+ return new ArrayList<>(satelliteCapabilitiesMap.keySet());
+ }
+ }
+ Log.d(TAG, "getAllSatellitePlmnsForCarrier : mConfigData is null or no config data");
+ return new ArrayList<>();
+ }
+
+ /**
+ * Get supported satellite services of all providers for a carrier.
+ * The format of the return value - Key: PLMN, Value: Set of supported satellite services.
+ *
+ * @param carrierId the carrier identifier.
+ * @return all supported satellite services for a carrier
+ */
+ @NonNull
+ public Map<String, Set<Integer>> getSupportedSatelliteServices(int carrierId) {
+ if (mSupportedServicesPerCarrier != null) {
+ Map<String, Set<Integer>> satelliteCapaMap =
+ mSupportedServicesPerCarrier.get(carrierId);
+ if (satelliteCapaMap != null) {
+ return satelliteCapaMap;
+ } else {
+ Log.d(TAG, "No supported services found for carrier=" + carrierId);
+ }
+ } else {
+ Log.d(TAG, "mSupportedServicesPerCarrier is null");
+ }
+ return new HashMap<>();
+ }
+
+ /**
+ * @return satellite region country codes
+ */
+ @NonNull
+ public List<String> getDeviceSatelliteCountryCodes() {
+ if (mSatelliteRegionCountryCodes != null) {
+ return mSatelliteRegionCountryCodes;
+ }
+ Log.d(TAG, "getDeviceSatelliteCountryCodes : mConfigData is null or no config data");
+ return new ArrayList<>();
+ }
+
+ /**
+ * @return satellite access allow value, if there is no config data then it returns null.
+ */
+ @Nullable
+ public Boolean isSatelliteDataForAllowedRegion() {
+ if (mIsSatelliteRegionAllowed == null) {
+ Log.d(TAG, "getIsSatelliteRegionAllowed : mConfigData is null or no config data");
+ }
+ return mIsSatelliteRegionAllowed;
+ }
+
+
+ /**
+ * @param context the Context
+ * @return satellite s2_cell_file path
+ */
+ @Nullable
+ public Path getSatelliteS2CellFile(@Nullable Context context) {
+ if (context == null) {
+ Log.d(TAG, "getSatelliteS2CellFile : context is null");
+ return null;
+ }
+
+ if (isFileExist(mSatS2FilePath)) {
+ Log.d(TAG, "File mSatS2FilePath is already exist");
+ return mSatS2FilePath;
+ }
+
+ if (mConfigData != null && mConfigData.deviceSatelliteRegion != null) {
+ mSatS2FilePath = copySatS2FileToPhoneDirectory(context,
+ mConfigData.deviceSatelliteRegion.s2CellFile);
+ return mSatS2FilePath;
+ }
+ Log.d(TAG, "getSatelliteS2CellFile :"
+ + "mConfigData is null or mConfigData.deviceSatelliteRegion is null");
+ return null;
+ }
+
+ /**
+ * @param context the Context
+ * @param byteArrayFile byte array type of protobuffer config data
+ * @return the satellite_cell_file path
+ */
+ @Nullable
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public Path copySatS2FileToPhoneDirectory(@Nullable Context context,
+ @Nullable byte[] byteArrayFile) {
+
+ if (context == null || byteArrayFile == null) {
+ Log.d(TAG, "copySatS2FileToPhoneDirectory : context or byteArrayFile are null");
+ return null;
+ }
+
+ File satS2FileDir = context.getDir(SATELLITE_DIR_NAME, Context.MODE_PRIVATE);
+ if (!satS2FileDir.exists()) {
+ satS2FileDir.mkdirs();
+ }
+
+ Path targetSatS2FilePath = satS2FileDir.toPath().resolve(S2_CELL_FILE_NAME);
+ try {
+ InputStream inputStream = new ByteArrayInputStream(byteArrayFile);
+ if (inputStream == null) {
+ Log.d(TAG, "copySatS2FileToPhoneDirectory: Resource=" + S2_CELL_FILE_NAME
+ + " not found");
+ } else {
+ Files.copy(inputStream, targetSatS2FilePath, StandardCopyOption.REPLACE_EXISTING);
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "copySatS2FileToPhoneDirectory: ex=" + ex);
+ }
+ return targetSatS2FilePath;
+ }
+
+ /**
+ * @return {@code true} if the SatS2File is already existed and {@code false} otherwise.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public boolean isFileExist(Path filePath) {
+ return Files.exists(filePath);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteConfigParser.java b/src/java/com/android/internal/telephony/satellite/SatelliteConfigParser.java
new file mode 100644
index 0000000000..4ff1880ba5
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteConfigParser.java
@@ -0,0 +1,94 @@
+/*
+ * 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.satellite;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import com.android.internal.telephony.configupdate.ConfigParser;
+import com.android.internal.telephony.satellite.nano.SatelliteConfigData;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * SatelliteConfigParser parses the config data and create SatelliteConfig.
+ * The config data is located at "/data/misc/telephonyconfig/telephony_config.pb".
+ * It is obtained through the getConfigParser() at the TelephonyConfigUpdateInstallReceiver.
+ */
+public class SatelliteConfigParser extends ConfigParser<SatelliteConfig> {
+ private static final String TAG = "SatelliteConfigParser";
+
+ /**
+ * Create an instance of SatelliteConfigParser with byte array data.
+ *
+ * @param data the config data formatted as byte array.
+ */
+ public SatelliteConfigParser(@Nullable byte[] data) {
+ super(data);
+ }
+
+ /**
+ * Create an instance of SatelliteConfigParser with InputStream data.
+ *
+ * @param input the config data formatted as InputStream.
+ */
+ public SatelliteConfigParser(@NonNull InputStream input)
+ throws IOException {
+ super(input);
+ }
+
+ /**
+ * Create an instance of SatelliteConfigParser with File data.
+ *
+ * @param file the config data formatted as File.
+ */
+ public SatelliteConfigParser(@NonNull File file) throws IOException {
+ super(file);
+ }
+
+ @Override
+ protected void parseData(@Nullable byte[] data) {
+ boolean parseError = false;
+ try {
+ if (data == null) {
+ Log.d(TAG, "config data is null");
+ return;
+ }
+ SatelliteConfigData.TelephonyConfigProto telephonyConfigData =
+ SatelliteConfigData.TelephonyConfigProto.parseFrom(data);
+ if (telephonyConfigData == null || telephonyConfigData.satellite == null) {
+ Log.e(TAG, "telephonyConfigData or telephonyConfigData.satellite is null");
+ return;
+ }
+ mVersion = telephonyConfigData.satellite.version;
+ mConfig = new SatelliteConfig(telephonyConfigData.satellite);
+ Log.d(TAG, "SatelliteConfig is created");
+ } catch (Exception e) {
+ parseError = true;
+ Log.e(TAG, "Parse Error : " + e.getMessage());
+ } finally {
+ if (parseError) {
+ mVersion = VERSION_UNKNOWN;
+ mConfig = null;
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 0d3973d127..2c12939380 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -34,6 +34,8 @@ import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODE
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
+import static com.android.internal.telephony.configupdate.ConfigProviderAdaptor.DOMAIN_SATELLITE;
+
import android.annotation.ArrayRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -66,6 +68,8 @@ import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.Registrant;
+import android.os.RegistrantList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceSpecificException;
@@ -89,6 +93,7 @@ import android.telephony.satellite.SatelliteCapabilities;
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;
@@ -101,6 +106,9 @@ import com.android.internal.telephony.DeviceStateMonitor;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.configupdate.ConfigParser;
+import com.android.internal.telephony.configupdate.ConfigProviderAdaptor;
+import com.android.internal.telephony.configupdate.TelephonyConfigUpdateInstallReceiver;
import com.android.internal.telephony.flags.FeatureFlags;
import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
@@ -118,6 +126,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
@@ -187,6 +196,7 @@ public class SatelliteController extends Handler {
private static final int EVENT_SERVICE_STATE_CHANGED = 37;
private static final int EVENT_SATELLITE_CAPABILITIES_CHANGED = 38;
private static final int EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT = 39;
+ private static final int EVENT_SATELLITE_CONFIG_DATA_UPDATED = 40;
@NonNull private static SatelliteController sInstance;
@NonNull private final Context mContext;
@@ -298,6 +308,7 @@ public class SatelliteController extends Handler {
@NonNull private final CarrierConfigManager mCarrierConfigManager;
@NonNull private final CarrierConfigManager.CarrierConfigChangeListener
mCarrierConfigChangeListener;
+ @NonNull private final ConfigProviderAdaptor.Callback mConfigDataUpdatedCallback;
@NonNull private final Object mCarrierConfigArrayLock = new Object();
@GuardedBy("mCarrierConfigArrayLock")
@NonNull private final SparseArray<PersistableBundle> mCarrierConfigArray = new SparseArray<>();
@@ -365,6 +376,8 @@ public class SatelliteController extends Handler {
private static final String NOTIFICATION_CHANNEL = "satelliteChannel";
private static final String NOTIFICATION_CHANNEL_ID = "satellite";
+ private final RegistrantList mSatelliteConfigUpdateChangedRegistrants = new RegistrantList();
+
/**
* @return The singleton instance of SatelliteController.
*/
@@ -454,12 +467,64 @@ public class SatelliteController extends Handler {
handleCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
mCarrierConfigManager.registerCarrierConfigChangeListener(
new HandlerExecutor(new Handler(looper)), mCarrierConfigChangeListener);
+
+ mConfigDataUpdatedCallback = new ConfigProviderAdaptor.Callback() {
+ @Override
+ public void onChanged(@Nullable ConfigParser config) {
+ SatelliteControllerHandlerRequest request =
+ new SatelliteControllerHandlerRequest(true,
+ SatelliteServiceUtils.getPhone());
+ sendRequestAsync(EVENT_SATELLITE_CONFIG_DATA_UPDATED, request, null);
+ }
+ };
+ TelephonyConfigUpdateInstallReceiver.getInstance()
+ .registerCallback(Executors.newSingleThreadExecutor(), mConfigDataUpdatedCallback);
+
mDSM.registerForSignalStrengthReportDecision(this, CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING,
null);
loadSatelliteSharedPreferences();
mWaitTimeForSatelliteEnablingResponse = getWaitForSatelliteEnablingResponseTimeoutMillis();
}
+ /**
+ * Register a callback to get a updated satellite config data.
+ * @param h Handler to notify
+ * @param what msg.what when the message is delivered
+ * @param obj AsyncResult.userObj when the message is delivered
+ */
+ public void registerForConfigUpdateChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mSatelliteConfigUpdateChangedRegistrants.add(r);
+ }
+
+ /**
+ * Unregister a callback to get a updated satellite config data.
+ * @param h Handler to notify
+ */
+ public void unregisterForConfigUpdateChanged(Handler h) {
+ mSatelliteConfigUpdateChangedRegistrants.remove(h);
+ }
+
+ /**
+ * Get satelliteConfig from SatelliteConfigParser
+ */
+ public SatelliteConfig getSatelliteConfig() {
+ if (getSatelliteConfigParser() == null) {
+ Log.d(TAG, "getSatelliteConfigParser() is not ready");
+ return null;
+ }
+ return (SatelliteConfig) getSatelliteConfigParser().getConfig();
+ }
+
+ /**
+ * Get SatelliteConfigParser from TelephonyConfigUpdateInstallReceiver
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public SatelliteConfigParser getSatelliteConfigParser() {
+ return (SatelliteConfigParser) TelephonyConfigUpdateInstallReceiver
+ .getInstance().getConfigParser(DOMAIN_SATELLITE);
+ }
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
protected void initializeSatelliteModeRadios() {
if (mContentResolver != null) {
@@ -1287,6 +1352,12 @@ public class SatelliteController extends Handler {
break;
}
+ case EVENT_SATELLITE_CONFIG_DATA_UPDATED: {
+ handleEventConfigDataUpdated();
+ mSatelliteConfigUpdateChangedRegistrants.notifyRegistrants();
+ break;
+ }
+
default:
Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
msg.what);
@@ -1294,6 +1365,19 @@ public class SatelliteController extends Handler {
}
}
+ private void handleEventConfigDataUpdated() {
+ updateSupportedSatelliteServicesForActiveSubscriptions();
+ int[] activeSubIds = mSubscriptionManagerService.getActiveSubIdList(true);
+ if (activeSubIds != null) {
+ for (int subId : activeSubIds) {
+ processNewCarrierConfigData(subId);
+ }
+ } else {
+ loge("updateSupportedSatelliteServicesForActiveSubscriptions: "
+ + "activeSubIds is null");
+ }
+ }
+
private void notifyRequester(SatelliteControllerHandlerRequest request) {
synchronized (request) {
request.notifyAll();
@@ -2513,21 +2597,21 @@ public class SatelliteController extends Handler {
}
/**
- * @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);
}
/**
@@ -2541,7 +2625,7 @@ public class SatelliteController extends Handler {
+ " is disabled");
return false;
}
- if (isUsingNonTerrestrialNetworkViaCarrier()) {
+ if (isUsingNonTerrestrialNetworkViaCarrier().first) {
return true;
}
for (Phone phone : PhoneFactory.getPhones()) {
@@ -2789,6 +2873,7 @@ public class SatelliteController extends Handler {
* @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) {
@@ -3193,9 +3278,22 @@ public class SatelliteController extends Handler {
return;
}
+ SatelliteConfig satelliteConfig = getSatelliteConfig();
+ if (satelliteConfig != null) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ int carrierId = tm.createForSubscriptionId(subId).getSimCarrierId();
+ List<String> plmnList = satelliteConfig.getAllSatellitePlmnsForCarrier(carrierId);
+ if (!plmnList.isEmpty()) {
+ logd("mMergedPlmnListPerCarrier is updated by ConfigUpdater : " + plmnList);
+ mMergedPlmnListPerCarrier.put(subId, plmnList);
+ return;
+ }
+ }
+
if (mSatelliteServicesSupportedByCarriers.containsKey(subId)) {
carrierPlmnList =
mSatelliteServicesSupportedByCarriers.get(subId).keySet().stream().toList();
+ logd("mMergedPlmnListPerCarrier is updated by carrier config");
} else {
carrierPlmnList = new ArrayList<>();
}
@@ -3205,10 +3303,31 @@ public class SatelliteController extends Handler {
}
private void updateSupportedSatelliteServices(int subId) {
+ logd("updateSupportedSatelliteServices with subId " + subId);
synchronized (mSupportedSatelliteServicesLock) {
+ SatelliteConfig satelliteConfig = getSatelliteConfig();
+
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ int carrierId = tm.createForSubscriptionId(subId).getSimCarrierId();
+
+ if (satelliteConfig != null) {
+ Map<String, Set<Integer>> supportedServicesPerPlmn =
+ satelliteConfig.getSupportedSatelliteServices(carrierId);
+ if (!supportedServicesPerPlmn.isEmpty()) {
+ mSatelliteServicesSupportedByCarriers.put(subId, supportedServicesPerPlmn);
+ logd("updateSupportedSatelliteServices using ConfigUpdater, "
+ + "supportedServicesPerPlmn = " + supportedServicesPerPlmn);
+ updatePlmnListPerCarrier(subId);
+ return;
+ } else {
+ logd("supportedServicesPerPlmn is empty");
+ }
+ }
+
mSatelliteServicesSupportedByCarriers.put(
subId, readSupportedSatelliteServicesFromCarrierConfig(subId));
updatePlmnListPerCarrier(subId);
+ logd("updateSupportedSatelliteServices using carrier config");
}
}
@@ -3262,8 +3381,11 @@ public class SatelliteController extends Handler {
updateCarrierConfig(subId);
updateEntitlementPlmnListPerCarrier(subId);
updateSupportedSatelliteServicesForActiveSubscriptions();
- configureSatellitePlmnForCarrier(subId);
+ processNewCarrierConfigData(subId);
+ }
+ private void processNewCarrierConfigData(int subId) {
+ configureSatellitePlmnForCarrier(subId);
synchronized (mIsSatelliteEnabledLock) {
mSatelliteAttachRestrictionForCarrierArray.clear();
mIsSatelliteAttachEnabledForCarrierArrayPerSub.clear();
@@ -3875,7 +3997,13 @@ public class SatelliteController extends Handler {
}
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,
@@ -3885,18 +4013,18 @@ public class SatelliteController extends Handler {
}
}
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,
@@ -3934,6 +4062,7 @@ public class SatelliteController extends Handler {
// 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 149b0543a5..c491476534 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 @@ public class SatelliteSOSMessageRecommender extends Handler {
}
}
- 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/NullCipherNotifier.java b/src/java/com/android/internal/telephony/security/NullCipherNotifier.java
index 0ab82992ba..3ece701874 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 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 @@ import com.android.telephony.Rlog;
* <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 @@ public class NullCipherNotifier {
* {@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/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
index a2ebf4ed42..82af4e80e4 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -641,7 +641,8 @@ public class SubscriptionInfoInternal {
* @return the number of this subscription.
*/
public String getNumber() {
- return mNumber;
+ if (TextUtils.isEmpty(mNumberFromCarrier)) return mNumber;
+ return mNumberFromCarrier;
}
/**
@@ -1260,7 +1261,7 @@ public class SubscriptionInfoInternal {
.setCarrierName(mCarrierName)
.setDisplayNameSource(mDisplayNameSource)
.setIconTint(mIconTint)
- .setNumber(mNumber)
+ .setNumber(getNumber())
.setDataRoaming(mDataRoaming)
.setMcc(mMcc)
.setMnc(mMnc)
@@ -1308,7 +1309,7 @@ public class SubscriptionInfoInternal {
+ " displayNameSource="
+ SubscriptionManager.displayNameSourceToString(mDisplayNameSource)
+ " iconTint=" + mIconTint
- + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mNumber)
+ + " number=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, getNumber())
+ " dataRoaming=" + mDataRoaming
+ " mcc=" + mMcc
+ " mnc=" + mMnc