aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-02-03 01:21:28 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-02-03 01:21:28 +0000
commit047282935d5a8dfdcf927678f12a73a88672ad73 (patch)
tree6380b6381d6dd6d235bed4cd13da8d1ef76e3051
parent70d24d166a775963569477c351a307e86f5d5f7d (diff)
parent2a748c579ebd165c829efbe4f82abe0280327d81 (diff)
downloadtelephony-047282935d5a8dfdcf927678f12a73a88672ad73.tar.gz
Merge "Snap for 11400057 from 7f28fd57b20e663a2ad91259c72aa521a91e4694 to simpleperf-release" into simpleperf-release
-rw-r--r--OWNERS2
-rw-r--r--proto/src/persist_atoms.proto4
-rw-r--r--src/java/com/android/internal/telephony/CarrierServiceStateTracker.java64
-rw-r--r--src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java39
-rw-r--r--src/java/com/android/internal/telephony/GbaManager.java7
-rw-r--r--src/java/com/android/internal/telephony/GsmCdmaPhone.java23
-rw-r--r--src/java/com/android/internal/telephony/ImsSmsDispatcher.java1
-rw-r--r--src/java/com/android/internal/telephony/LocaleTracker.java8
-rw-r--r--src/java/com/android/internal/telephony/MultiSimSettingController.java47
-rw-r--r--src/java/com/android/internal/telephony/NetworkTypeController.java220
-rw-r--r--src/java/com/android/internal/telephony/Phone.java24
-rw-r--r--src/java/com/android/internal/telephony/PhoneConfigurationManager.java41
-rw-r--r--src/java/com/android/internal/telephony/RILUtils.java27
-rw-r--r--src/java/com/android/internal/telephony/ServiceStateTracker.java37
-rw-r--r--src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java6
-rw-r--r--src/java/com/android/internal/telephony/data/AutoDataSwitchController.java702
-rw-r--r--src/java/com/android/internal/telephony/data/DataConfigManager.java113
-rw-r--r--src/java/com/android/internal/telephony/data/DataNetwork.java28
-rw-r--r--src/java/com/android/internal/telephony/data/DataNetworkController.java21
-rw-r--r--src/java/com/android/internal/telephony/data/DataProfileManager.java30
-rw-r--r--src/java/com/android/internal/telephony/data/DataRetryManager.java6
-rw-r--r--src/java/com/android/internal/telephony/data/DataServiceManager.java3
-rw-r--r--src/java/com/android/internal/telephony/data/DataSettingsManager.java27
-rw-r--r--src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java209
-rw-r--r--src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java15
-rw-r--r--src/java/com/android/internal/telephony/data/PhoneSwitcher.java442
-rw-r--r--src/java/com/android/internal/telephony/domainselection/OWNERS9
-rw-r--r--src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java17
-rw-r--r--src/java/com/android/internal/telephony/euicc/EuiccController.java13
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhone.java82
-rwxr-xr-x[-rw-r--r--]src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java7
-rw-r--r--src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java3
-rw-r--r--src/java/com/android/internal/telephony/metrics/DataConnectionStateTracker.java189
-rw-r--r--src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java414
-rw-r--r--src/java/com/android/internal/telephony/metrics/MetricsCollector.java8
-rw-r--r--src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java4
-rw-r--r--src/java/com/android/internal/telephony/metrics/RcsStats.java7
-rw-r--r--src/java/com/android/internal/telephony/metrics/ServiceStateStats.java67
-rw-r--r--src/java/com/android/internal/telephony/metrics/SmsStats.java2
-rw-r--r--src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java68
-rw-r--r--src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java59
-rw-r--r--src/java/com/android/internal/telephony/satellite/PointingAppController.java18
-rw-r--r--src/java/com/android/internal/telephony/satellite/SatelliteController.java350
-rw-r--r--src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java18
-rw-r--r--src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java128
-rw-r--r--src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java16
-rw-r--r--src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java31
-rw-r--r--src/java/com/android/internal/telephony/uicc/UiccPkcs15.java13
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java39
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java24
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java9
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java15
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java33
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java13
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java242
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java14
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java67
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java3
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java380
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java49
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java115
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java70
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java64
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java47
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java153
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java323
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS9
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java62
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java259
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java374
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java137
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java577
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java208
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java9
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java198
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java16
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java68
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java4
80 files changed, 6118 insertions, 1136 deletions
diff --git a/OWNERS b/OWNERS
index 7e572000ec..ff1a04e0bd 100644
--- a/OWNERS
+++ b/OWNERS
@@ -18,4 +18,4 @@ tnd@google.com
xiaotonj@google.com
# Domain Selection code is co-owned, adding additional owners for this code
-per-file EmergencyStateTracker*=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com
+per-file EmergencyStateTracker*=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,jdyou@google.com
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 61e44a3c8f..2e569f042c 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -275,6 +275,8 @@ message VoiceCallSession {
optional int32 call_duration = 32;
optional int32 last_known_rat = 33;
optional int32 fold_state = 34;
+ optional int64 rat_switch_count_after_connected = 35;
+ optional bool handover_in_progress = 36;
// Internal use only
optional int64 setup_begin_millis = 10001;
@@ -378,6 +380,8 @@ message CellularServiceState {
optional bool is_emergency_only = 10;
optional bool is_internet_pdn_up = 11;
optional int32 fold_state = 12;
+ optional bool override_voice_service = 13;
+ optional bool isDataEnabled = 14;
// Internal use only
optional int64 last_used_millis = 10001;
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 6b44998db3..93f5ab01e7 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -116,14 +116,15 @@ public class CarrierServiceStateTracker extends Handler {
mPhone.getContext(),
mPhone.getSubId(),
CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT,
- CarrierConfigManager
- .KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
+ CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT,
+ CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL);
if (b.isEmpty()) return;
for (Map.Entry<Integer, NotificationType> entry :
mNotificationTypeMap.entrySet()) {
NotificationType notificationType = entry.getValue();
notificationType.setDelay(b);
+ notificationType.setEnabled(b);
}
handleConfigChanges();
});
@@ -430,6 +431,18 @@ public class CarrierServiceStateTracker extends Handler {
void setDelay(PersistableBundle bundle);
/**
+ * Checks whether this Notification is enabled.
+ * @return {@code true} if this Notification is enabled, false otherwise
+ */
+ boolean isEnabled();
+
+ /**
+ * Sets whether this Notification is enabled. If disabled, it will not build notification.
+ * @param bundle PersistableBundle
+ */
+ void setEnabled(PersistableBundle bundle);
+
+ /**
* returns notification type id.
**/
int getTypeId();
@@ -458,6 +471,7 @@ public class CarrierServiceStateTracker extends Handler {
private final int mTypeId;
private int mDelay = UNINITIALIZED_DELAY_VALUE;
+ private boolean mEnabled = false;
PrefNetworkNotification(int typeId) {
this.mTypeId = typeId;
@@ -480,6 +494,28 @@ public class CarrierServiceStateTracker extends Handler {
return mDelay;
}
+ /**
+ * Checks whether this Notification is enabled.
+ * @return {@code true} if this Notification is enabled, false otherwise
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Sets whether this Notification is enabled. If disabled, it will not build notification.
+ * @param bundle PersistableBundle
+ */
+ public void setEnabled(PersistableBundle bundle) {
+ if (bundle == null) {
+ Rlog.e(LOG_TAG, "bundle is null");
+ return;
+ }
+ mEnabled = !bundle.getBoolean(
+ CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL);
+ Rlog.i(LOG_TAG, "reading enabled notification pref network: " + mEnabled);
+ }
+
public int getTypeId() {
return mTypeId;
}
@@ -497,10 +533,10 @@ public class CarrierServiceStateTracker extends Handler {
*/
public boolean sendMessage() {
Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: "
- + "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode()
- + "," + mSST.isRadioOn());
- if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode()
- || isRadioOffOrAirplaneMode()) {
+ + "," + mEnabled + "," + isPhoneStillRegistered() + "," + mDelay
+ + "," + isGlobalMode() + "," + mSST.isRadioOn());
+ if (!mEnabled || mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered()
+ || isGlobalMode() || isRadioOffOrAirplaneMode()) {
return false;
}
return true;
@@ -559,6 +595,22 @@ public class CarrierServiceStateTracker extends Handler {
return mDelay;
}
+ /**
+ * Checks whether this Notification is enabled.
+ * @return {@code true} if this Notification is enabled, false otherwise
+ */
+ public boolean isEnabled() {
+ return true;
+ }
+
+ /**
+ * Sets whether this Notification is enabled. If disabled, it will not build notification.
+ * @param bundle PersistableBundle
+ */
+ public void setEnabled(PersistableBundle bundle) {
+ // always allowed. There is no config to hide notifications.
+ }
+
public int getTypeId() {
return mTypeId;
}
diff --git a/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java b/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java
index 82d44096a8..7e8663a837 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java
@@ -26,6 +26,8 @@ import android.telephony.CellBroadcastIdRange;
import android.telephony.SmsCbMessage;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
@@ -33,6 +35,8 @@ import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -59,6 +63,7 @@ public final class CellBroadcastConfigTracker extends StateMachine {
// Cache of current cell broadcast id ranges of 3gpp2
private List<CellBroadcastIdRange> mCbRanges3gpp2 = new CopyOnWriteArrayList<>();
private Phone mPhone;
+ private final LocalLog mLocalLog = new LocalLog(128);
@VisibleForTesting
public int mSubId;
@VisibleForTesting
@@ -106,8 +111,7 @@ public final class CellBroadcastConfigTracker extends StateMachine {
@Override
public String toString() {
return "Request[mCbRangesRequest3gpp = " + mCbRangesRequest3gpp + ", "
- + "mCbRangesRequest3gpp2 = " + mCbRangesRequest3gpp2 + ", "
- + "mCallback = " + mCallback + "]";
+ + "mCbRangesRequest3gpp2 = " + mCbRangesRequest3gpp2 + "]";
}
}
@@ -175,6 +179,9 @@ public final class CellBroadcastConfigTracker extends StateMachine {
Request request = (Request) msg.obj;
if (DBG) {
logd("IdleState handle EVENT_REQUEST with request:" + request);
+ mLocalLog.log("IdleState handle EVENT_REQUEST with request:" + request
+ + ", mCbRanges3gpp:" + mCbRanges3gpp
+ + ", mCbRanges3gpp2:" + mCbRanges3gpp2);
}
if (!mCbRanges3gpp.equals(request.get3gppRanges())) {
// set gsm config if the config is changed
@@ -229,6 +236,7 @@ public final class CellBroadcastConfigTracker extends StateMachine {
transitionTo(mGsmActivatingState);
} else {
logd("Failed to set gsm config");
+ mLocalLog.log("GsmConfiguringState Failed to set gsm config:" + request);
request.getCallback().accept(
TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG);
// transit to idle state on the failure case
@@ -265,6 +273,8 @@ public final class CellBroadcastConfigTracker extends StateMachine {
if (DBG) {
logd("GsmActivatingState handle EVENT_ACTIVATION_DONE with request:"
+ request);
+ mLocalLog.log("GsmActivatingState EVENT_ACTIVATION_DONE, exception:"
+ + ar.exception + ", request:" + request);
}
if (ar.exception == null) {
mCbRanges3gpp = request.get3gppRanges();
@@ -326,6 +336,7 @@ public final class CellBroadcastConfigTracker extends StateMachine {
transitionTo(mCdmaActivatingState);
} else {
logd("Failed to set cdma config");
+ mLocalLog.log("CdmaConfiguringState Failed to set cdma config:" + request);
request.getCallback().accept(
TelephonyManager.CELL_BROADCAST_RESULT_FAIL_CONFIG);
// transit to idle state on the failure case
@@ -362,6 +373,8 @@ public final class CellBroadcastConfigTracker extends StateMachine {
if (DBG) {
logd("CdmaActivatingState handle EVENT_ACTIVATION_DONE with request:"
+ request);
+ mLocalLog.log("CdmaActivatingState EVENT_ACTIVATION_DONE, exception:"
+ + ar.exception + ", request:" + request);
}
if (ar.exception == null) {
mCbRanges3gpp2 = request.get3gpp2Ranges();
@@ -531,4 +544,26 @@ public final class CellBroadcastConfigTracker extends StateMachine {
mPhone.mCi.setCdmaBroadcastActivation(activate, response);
}
}
+
+ /**
+ * Dump the state of CellBroadcastConfigTracker
+ *
+ * @param fd File descriptor
+ * @param printWriter Print writer
+ * @param args Arguments
+ */
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println(CellBroadcastConfigTracker.class.getSimpleName()
+ + "-" + mPhone.getPhoneId() + ":");
+ pw.increaseIndent();
+ pw.println("Current mCbRanges3gpp:" + mCbRanges3gpp);
+ pw.println("Current mCbRanges3gpp2:" + mCbRanges3gpp2);
+ pw.decreaseIndent();
+
+ pw.println("Local logs:");
+ pw.increaseIndent();
+ mLocalLog.dump(fd, pw, args);
+ pw.decreaseIndent();
+ }
}
diff --git a/src/java/com/android/internal/telephony/GbaManager.java b/src/java/com/android/internal/telephony/GbaManager.java
index b1db1ac353..7c5f636d2b 100644
--- a/src/java/com/android/internal/telephony/GbaManager.java
+++ b/src/java/com/android/internal/telephony/GbaManager.java
@@ -40,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.metrics.RcsStats;
import com.android.telephony.Rlog;
+import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
@@ -155,7 +156,11 @@ public class GbaManager {
public synchronized void unlinkToDeath() {
if (mBinder != null) {
- mBinder.unlinkToDeath(this, 0);
+ try {
+ mBinder.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ // do nothing
+ }
mBinder = null;
}
}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 5eae06112c..6e2601e635 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -249,6 +249,7 @@ public class GsmCdmaPhone extends Phone {
private String mImeiSv;
private String mVmNumber;
private int mImeiType = IMEI_TYPE_UNKNOWN;
+ private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
@VisibleForTesting
public CellBroadcastConfigTracker mCellBroadcastConfigTracker =
@@ -426,9 +427,9 @@ public class GsmCdmaPhone extends Phone {
if (mPhoneId == intent.getIntExtra(
SubscriptionManager.EXTRA_SLOT_INDEX,
SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
- int simState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
+ mSimState = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
TelephonyManager.SIM_STATE_UNKNOWN);
- if (simState == TelephonyManager.SIM_STATE_LOADED
+ if (mSimState == TelephonyManager.SIM_STATE_LOADED
&& currentSlotSubIdChanged()) {
setNetworkSelectionModeAutomatic(null);
}
@@ -680,6 +681,7 @@ public class GsmCdmaPhone extends Phone {
mTelecomVoiceServiceStateOverride = newOverride;
if (changed && mSST != null) {
mSST.onTelecomVoiceServiceStateOverrideChanged();
+ mSST.getServiceStateStats().onVoiceServiceStateOverrideChanged(hasService);
}
}
@@ -3521,13 +3523,14 @@ public class GsmCdmaPhone extends Phone {
case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE:
logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE");
ar = (AsyncResult) msg.obj;
+ // Only test for a success here in order to flip the support flag.
+ // Testing for the negative case, e.g. REQUEST_NOT_SUPPORTED, is insufficient
+ // because the modem or the RIL could still return exceptions for temporary
+ // failures even when the feature is unsupported.
if (ar == null || ar.exception == null) {
mIsNullCipherAndIntegritySupported = true;
return;
}
- CommandException.Error error = ((CommandException) ar.exception).getCommandError();
- mIsNullCipherAndIntegritySupported = !error.equals(
- CommandException.Error.REQUEST_NOT_SUPPORTED);
break;
case EVENT_IMS_DEREGISTRATION_TRIGGERED:
@@ -4499,6 +4502,12 @@ public class GsmCdmaPhone extends Phone {
e.printStackTrace();
}
pw.flush();
+ try {
+ mCellBroadcastConfigTracker.dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.flush();
}
@Override
@@ -5017,6 +5026,10 @@ public class GsmCdmaPhone extends Phone {
return;
}
+ if (mSimState != TelephonyManager.SIM_STATE_LOADED) {
+ return;
+ }
+
if (config == null) {
loge("didn't get the vonr_enabled_bool from the carrier config.");
return;
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 90885fe6e3..9b116b4228 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -657,7 +657,6 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@VisibleForTesting
public void fallbackToPstn(SmsTracker tracker) {
- tracker.mMessageRef = nextMessageRef();
mSmsDispatchersController.sendRetrySms(tracker);
}
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index de854fa7ca..076b00197f 100644
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -372,7 +372,10 @@ public class LocaleTracker extends Handler {
*/
public void updateOperatorNumeric(String operatorNumeric) {
if (TextUtils.isEmpty(operatorNumeric)) {
- sendMessageDelayed(obtainMessage(EVENT_OPERATOR_LOST), SERVICE_OPERATOR_LOST_DELAY_MS);
+ if (!hasMessages(EVENT_OPERATOR_LOST)) {
+ sendMessageDelayed(obtainMessage(EVENT_OPERATOR_LOST),
+ SERVICE_OPERATOR_LOST_DELAY_MS);
+ }
} else {
removeMessages(EVENT_OPERATOR_LOST);
updateOperatorNumericImmediate(operatorNumeric);
@@ -528,6 +531,9 @@ public class LocaleTracker extends Handler {
if (!mPhone.isRadioOn()) {
countryIso = "";
countryIsoDebugInfo = "radio off";
+
+ // clear cell infos, we don't know where the next network to camp on.
+ mCellInfoList = null;
}
log("updateLocale: countryIso = " + countryIso
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 0acae4b56d..d6b09303be 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -83,7 +83,8 @@ public class MultiSimSettingController extends Handler {
private static final int EVENT_SUBSCRIPTION_INFO_CHANGED = 4;
private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED = 5;
private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6;
- private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 8;
+ @VisibleForTesting
+ public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 8;
@VisibleForTesting
public static final int EVENT_RADIO_STATE_CHANGED = 9;
@@ -150,6 +151,8 @@ public class MultiSimSettingController extends Handler {
// The number of existing DataSettingsControllerCallback
private int mCallbacksCount;
+ /** The number of active modem count. */
+ private int mActiveModemCount;
private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub";
@@ -163,15 +166,13 @@ public class MultiSimSettingController extends Handler {
}
@Override
- public void onDataEnabledChanged(boolean enabled,
- @TelephonyManager.DataEnabledChangedReason int reason, String callingPackage) {
+ public void onUserDataEnabledChanged(boolean enabled, @NonNull String callingPackage) {
int subId = mPhone.getSubId();
- // notifyUserDataEnabled if the change is called from external and reason is
- // DATA_ENABLED_REASON_USER
+ // only notifyUserDataEnabled if the change is called from external to avoid
+ // setUserDataEnabledForGroup infinite loop
if (SubscriptionManager.isValidSubscriptionId(subId)
- && reason == TelephonyManager.DATA_ENABLED_REASON_USER
&& !getInstance().mContext.getOpPackageName().equals(callingPackage)) {
- getInstance().notifyUserDataEnabled(mPhone.getSubId(), enabled);
+ getInstance().notifyUserDataEnabled(subId, enabled);
}
}
@@ -217,11 +218,14 @@ public class MultiSimSettingController extends Handler {
mSubscriptionManagerService = SubscriptionManagerService.getInstance();
// Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change.
- final int phoneCount = ((TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE)).getSupportedModemCount();
+ TelephonyManager telephonyManager = ((TelephonyManager) mContext.getSystemService(
+ TelephonyManager.class));
+ final int phoneCount = telephonyManager.getSupportedModemCount();
mCarrierConfigLoadedSubIds = new int[phoneCount];
Arrays.fill(mCarrierConfigLoadedSubIds, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mActiveModemCount = telephonyManager.getActiveModemCount();
+
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
@@ -316,9 +320,14 @@ public class MultiSimSettingController extends Handler {
onMultiSimConfigChanged(activeModems);
break;
case EVENT_RADIO_STATE_CHANGED:
- for (Phone phone : PhoneFactory.getPhones()) {
- if (phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
- if (DBG) log("Radio unavailable. Clearing sub info initialized flag.");
+ for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) {
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null && phone.mCi.getRadioState()
+ == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
+ if (DBG) {
+ log("Radio unavailable on phone " + phoneId
+ + ", clearing sub info initialized flag");
+ }
mSubInfoInitialized = false;
break;
}
@@ -453,6 +462,8 @@ public class MultiSimSettingController extends Handler {
}
private void onMultiSimConfigChanged(int activeModems) {
+ mActiveModemCount = activeModems;
+ log("onMultiSimConfigChanged: current ActiveModemCount=" + mActiveModemCount);
// Clear mCarrierConfigLoadedSubIds. Other actions will responds to active
// subscription change.
for (int phoneId = activeModems; phoneId < mCarrierConfigLoadedSubIds.length; phoneId++) {
@@ -601,8 +612,7 @@ public class MultiSimSettingController extends Handler {
// opportunistic subscription active (activeSubInfos.size() > 1), we automatically
// set the primary to be default SIM and return.
if (mPrimarySubList.size() == 1 && (change != PRIMARY_SUB_REMOVED
- || ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
- .getActiveModemCount() == 1)) {
+ || mActiveModemCount == 1)) {
int subId = mPrimarySubList.get(0);
if (DBG) log("updateDefaultValues: to only primary sub " + subId);
mSubscriptionManagerService.setDefaultDataSubId(subId);
@@ -1058,10 +1068,11 @@ public class MultiSimSettingController extends Handler {
}
private boolean isRadioAvailableOnAllSubs() {
- for (Phone phone : PhoneFactory.getPhones()) {
- if ((phone.mCi != null &&
- phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) ||
- phone.isShuttingDown()) {
+ for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) {
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null
+ && (phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE
+ || phone.isShuttingDown())) {
return false;
}
}
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index beebf2206e..d1c83590d3 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -29,6 +29,7 @@ import android.os.PowerManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
@@ -40,7 +41,6 @@ import android.text.TextUtils;
import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
import com.android.internal.telephony.data.DataUtils;
-import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.util.IState;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
@@ -52,9 +52,11 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
@@ -105,7 +107,7 @@ public class NetworkTypeController extends StateMachine {
/** Event for preferred network mode changed. */
private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 10;
/** Event for physical channel configs changed. */
- private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 11;
+ private static final int EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED = 11;
/** Event for device idle mode changed, when device goes to deep sleep and pauses all timers. */
private static final int EVENT_DEVICE_IDLE_MODE_CHANGED = 12;
@@ -123,13 +125,13 @@ public class NetworkTypeController extends StateMachine {
sEvents[EVENT_RADIO_OFF_OR_UNAVAILABLE] = "EVENT_RADIO_OFF_OR_UNAVAILABLE";
sEvents[EVENT_PREFERRED_NETWORK_MODE_CHANGED] = "EVENT_PREFERRED_NETWORK_MODE_CHANGED";
sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE";
- sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED";
+ sEvents[EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED";
sEvents[EVENT_DEVICE_IDLE_MODE_CHANGED] = "EVENT_DEVICE_IDLE_MODE_CHANGED";
}
- private final @NonNull Phone mPhone;
- private final @NonNull DisplayInfoController mDisplayInfoController;
- private final @NonNull BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @NonNull private final Phone mPhone;
+ @NonNull private final DisplayInfoController mDisplayInfoController;
+ @NonNull private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
@@ -140,7 +142,7 @@ public class NetworkTypeController extends StateMachine {
}
};
- private final @NonNull CarrierConfigManager.CarrierConfigChangeListener
+ @NonNull private final CarrierConfigManager.CarrierConfigChangeListener
mCarrierConfigChangeListener =
new CarrierConfigManager.CarrierConfigChangeListener() {
@Override
@@ -153,9 +155,9 @@ public class NetworkTypeController extends StateMachine {
}
};
- private @NonNull Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
- private @NonNull String mLteEnhancedPattern = "";
- private @Annotation.OverrideNetworkType int mOverrideNetworkType;
+ @NonNull private Map<String, OverrideTimerRule> mOverrideTimerRules = new HashMap<>();
+ @NonNull private String mLteEnhancedPattern = "";
+ @Annotation.OverrideNetworkType private int mOverrideNetworkType;
private boolean mIsPhysicalChannelConfigOn;
private boolean mIsPrimaryTimerActive;
private boolean mIsSecondaryTimerActive;
@@ -163,11 +165,12 @@ public class NetworkTypeController extends StateMachine {
private int mLtePlusThresholdBandwidth;
private int mNrAdvancedThresholdBandwidth;
private boolean mIncludeLteForNrAdvancedThresholdBandwidth;
- private @NonNull int[] mAdditionalNrAdvancedBandsList;
- private @NonNull String mPrimaryTimerState;
- private @NonNull String mSecondaryTimerState;
- private @NonNull String mPreviousState;
- private @LinkStatus int mPhysicalLinkStatus;
+ private boolean mRatchetPccFieldsForSameAnchorNrCell;
+ @NonNull private final Set<Integer> mAdditionalNrAdvancedBands = new HashSet<>();
+ @NonNull private String mPrimaryTimerState;
+ @NonNull private String mSecondaryTimerState;
+ @NonNull private String mPreviousState;
+ @LinkStatus private int mPhysicalLinkStatus;
private boolean mIsPhysicalChannelConfig16Supported;
private boolean mIsNrAdvancedAllowedByPco = false;
private int mNrAdvancedCapablePcoId = 0;
@@ -175,12 +178,17 @@ public class NetworkTypeController extends StateMachine {
private boolean mEnableNrAdvancedWhileRoaming = true;
private boolean mIsDeviceIdleMode = false;
- private @Nullable DataNetworkControllerCallback mNrAdvancedCapableByPcoChangedCallback = null;
- private @Nullable DataNetworkControllerCallback mNrPhysicalLinkStatusChangedCallback = null;
+ @Nullable private DataNetworkControllerCallback mNrAdvancedCapableByPcoChangedCallback = null;
+ @Nullable private DataNetworkControllerCallback mNrPhysicalLinkStatusChangedCallback = null;
// Cached copies below to prevent race conditions
- private @NonNull ServiceState mServiceState;
- private @Nullable List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+ @NonNull private ServiceState mServiceState;
+ @Nullable private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+
+ // Ratchet physical channel config fields to prevent 5G/5G+ flickering
+ @NonNull private Set<Integer> mRatchetedNrBands = new HashSet<>();
+ private int mRatchetedNrBandwidths = 0;
+ private int mLastAnchorNrCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
/**
* NetworkTypeController constructor.
@@ -246,7 +254,7 @@ public class NetworkTypeController extends StateMachine {
mPhone.registerForPreferredNetworkTypeChanged(getHandler(),
EVENT_PREFERRED_NETWORK_MODE_CHANGED, null);
mPhone.registerForPhysicalChannelConfig(getHandler(),
- EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, null);
+ EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED, null);
mPhone.getServiceStateTracker().registerForServiceStateChanged(getHandler(),
EVENT_SERVICE_STATE_CHANGED, null);
mIsPhysicalChannelConfig16Supported = mPhone.getContext().getSystemService(
@@ -293,10 +301,16 @@ public class NetworkTypeController extends StateMachine {
CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT);
mIncludeLteForNrAdvancedThresholdBandwidth = config.getBoolean(
CarrierConfigManager.KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL);
+ mRatchetPccFieldsForSameAnchorNrCell = config.getBoolean(
+ CarrierConfigManager.KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL);
mEnableNrAdvancedWhileRoaming = config.getBoolean(
CarrierConfigManager.KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL);
- mAdditionalNrAdvancedBandsList = config.getIntArray(
+ mAdditionalNrAdvancedBands.clear();
+ int[] additionalNrAdvancedBands = config.getIntArray(
CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY);
+ if (additionalNrAdvancedBands != null) {
+ Arrays.stream(additionalNrAdvancedBands).forEach(mAdditionalNrAdvancedBands::add);
+ }
mNrAdvancedCapablePcoId = config.getInt(
CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
if (mNrAdvancedCapablePcoId > 0 && mNrAdvancedCapableByPcoChangedCallback == null) {
@@ -343,6 +357,7 @@ public class NetworkTypeController extends StateMachine {
String overrideSecondaryTimerRule = config.getString(
CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
+ updatePhysicalChannelConfigs();
}
private void createTimerRules(String icons, String timers, String secondaryTimers) {
@@ -553,9 +568,9 @@ public class NetworkTypeController extends StateMachine {
quit();
break;
case EVENT_INITIALIZE:
- // The reason that we do it here is because some of the works below requires
- // other modules (e.g. DataNetworkController, ServiceStateTracker), which is not
- // created yet when NetworkTypeController is created.
+ // The reason that we do it here is that the work below requires other modules
+ // (e.g. DataNetworkController, ServiceStateTracker), which are not created
+ // when NetworkTypeController is created.
registerForAllEvents();
parseCarrierConfigs();
break;
@@ -579,6 +594,9 @@ public class NetworkTypeController extends StateMachine {
log("Reset timers since physical channel config indications are off.");
}
resetAllTimers();
+ mRatchetedNrBands.clear();
+ mRatchetedNrBandwidths = 0;
+ mLastAnchorNrCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
}
transitionToCurrentState();
break;
@@ -609,10 +627,8 @@ public class NetworkTypeController extends StateMachine {
resetAllTimers();
transitionToCurrentState();
break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- mPhysicalChannelConfigs =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
- if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
+ updatePhysicalChannelConfigs();
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
@@ -689,10 +705,8 @@ public class NetworkTypeController extends StateMachine {
}
mIsNrRestricted = isNrRestricted();
break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- mPhysicalChannelConfigs =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
- if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
+ updatePhysicalChannelConfigs();
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
if (mIsTimerResetEnabledForLegacyStateRrcIdle && !isPhysicalLinkActive()) {
@@ -770,10 +784,8 @@ public class NetworkTypeController extends StateMachine {
}
}
break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- mPhysicalChannelConfigs =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
- if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
+ updatePhysicalChannelConfigs();
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
if (isPhysicalLinkActive()) {
@@ -854,10 +866,8 @@ public class NetworkTypeController extends StateMachine {
}
}
break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- mPhysicalChannelConfigs =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
- if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
+ updatePhysicalChannelConfigs();
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
if (!isPhysicalLinkActive()) {
@@ -935,10 +945,8 @@ public class NetworkTypeController extends StateMachine {
transitionWithTimerTo(mLegacyState);
}
break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- mPhysicalChannelConfigs =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
- if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
+ updatePhysicalChannelConfigs();
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
@@ -1016,10 +1024,8 @@ public class NetworkTypeController extends StateMachine {
transitionWithTimerTo(mLegacyState);
}
break;
- case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
- mPhysicalChannelConfigs =
- mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
- if (DBG) log("Physical channel configs updated: " + mPhysicalChannelConfigs);
+ case EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED:
+ updatePhysicalChannelConfigs();
if (isUsingPhysicalChannelConfigForRrcDetection()) {
mPhysicalLinkStatus = getPhysicalLinkStatusFromPhysicalChannelConfig();
}
@@ -1050,6 +1056,77 @@ public class NetworkTypeController extends StateMachine {
private final NrConnectedAdvancedState mNrConnectedAdvancedState =
new NrConnectedAdvancedState();
+ private void updatePhysicalChannelConfigs() {
+ List<PhysicalChannelConfig> physicalChannelConfigs =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ boolean isPccListEmpty = physicalChannelConfigs == null || physicalChannelConfigs.isEmpty();
+ if (isPccListEmpty && isUsingPhysicalChannelConfigForRrcDetection()) {
+ log("Physical channel configs updated: not updating PCC fields for empty PCC list "
+ + "indicating RRC idle.");
+ mPhysicalChannelConfigs = physicalChannelConfigs;
+ return;
+ }
+
+ int anchorNrCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
+ int anchorLteCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
+ int nrBandwidths = 0;
+ Set<Integer> nrBands = new HashSet<>();
+ if (physicalChannelConfigs != null) {
+ for (PhysicalChannelConfig config : physicalChannelConfigs) {
+ if (config.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR) {
+ if (config.getConnectionStatus() == CellInfo.CONNECTION_PRIMARY_SERVING
+ && anchorNrCellId == PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN) {
+ anchorNrCellId = config.getPhysicalCellId();
+ }
+ nrBandwidths += config.getCellBandwidthDownlinkKhz();
+ nrBands.add(config.getBand());
+ } else if (config.getNetworkType() == TelephonyManager.NETWORK_TYPE_LTE) {
+ if (config.getConnectionStatus() == CellInfo.CONNECTION_PRIMARY_SERVING
+ && anchorLteCellId == PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN) {
+ anchorLteCellId = config.getPhysicalCellId();
+ }
+ if (mIncludeLteForNrAdvancedThresholdBandwidth) {
+ nrBandwidths += config.getCellBandwidthDownlinkKhz();
+ }
+ }
+ }
+ }
+
+ // Update anchor NR cell from anchor LTE cell for NR NSA
+ if (anchorNrCellId == PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN
+ && anchorLteCellId != PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN) {
+ anchorNrCellId = anchorLteCellId;
+ }
+
+ if (anchorNrCellId == PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN) {
+ if (!isPccListEmpty) {
+ log("Ignoring physical channel config fields without an anchor NR cell, "
+ + "either due to LTE-only configs or an unspecified cell ID.");
+ }
+ mRatchetedNrBandwidths = 0;
+ mRatchetedNrBands.clear();
+ } else if (anchorNrCellId == mLastAnchorNrCellId && mRatchetPccFieldsForSameAnchorNrCell) {
+ log("Ratchet physical channel config fields since anchor NR cell is the same.");
+ mRatchetedNrBandwidths = Math.max(mRatchetedNrBandwidths, nrBandwidths);
+ mRatchetedNrBands.addAll(nrBands);
+ } else {
+ if (mRatchetPccFieldsForSameAnchorNrCell) {
+ log("Not ratcheting physical channel config fields since anchor NR cell changed: "
+ + mLastAnchorNrCellId + " -> " + anchorNrCellId);
+ }
+ mRatchetedNrBandwidths = nrBandwidths;
+ mRatchetedNrBands = nrBands;
+ }
+
+ mLastAnchorNrCellId = anchorNrCellId;
+ mPhysicalChannelConfigs = physicalChannelConfigs;
+ if (DBG) {
+ log("Physical channel configs updated: anchorNrCell=" + mLastAnchorNrCellId
+ + ", nrBandwidths=" + mRatchetedNrBandwidths + ", nrBands=" + mRatchetedNrBands
+ + ", configs=" + mPhysicalChannelConfigs);
+ }
+ }
+
private void transitionWithTimerTo(IState destState) {
String destName = destState.getName();
if (DBG) log("Transition with primary timer from " + mPreviousState + " to " + destName);
@@ -1275,29 +1352,25 @@ public class NetworkTypeController extends StateMachine {
// Check PCO requirement. For carriers using PCO to indicate whether the data connection is
// NR advanced capable, mNrAdvancedCapablePcoId should be configured to non-zero.
if (mNrAdvancedCapablePcoId > 0 && !mIsNrAdvancedAllowedByPco) {
+ if (DBG) log("isNrAdvanced: not allowed by PCO for PCO ID " + mNrAdvancedCapablePcoId);
return false;
}
// Check if NR advanced is enabled when the device is roaming. Some carriers disable it
// while the device is roaming.
if (mServiceState.getDataRoaming() && !mEnableNrAdvancedWhileRoaming) {
+ if (DBG) log("isNrAdvanced: false because NR advanced is unavailable while roaming.");
return false;
}
- int bandwidths = 0;
- if (mPhone.getServiceStateTracker().getPhysicalChannelConfigList() != null) {
- bandwidths = mPhone.getServiceStateTracker().getPhysicalChannelConfigList()
- .stream()
- .filter(config -> mIncludeLteForNrAdvancedThresholdBandwidth
- || config.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR)
- .map(PhysicalChannelConfig::getCellBandwidthDownlinkKhz)
- .mapToInt(Integer::intValue)
- .sum();
- }
-
// Check if meeting minimum bandwidth requirement. For most carriers, there is no minimum
// bandwidth requirement and mNrAdvancedThresholdBandwidth is 0.
- if (mNrAdvancedThresholdBandwidth > 0 && bandwidths < mNrAdvancedThresholdBandwidth) {
+ if (mNrAdvancedThresholdBandwidth > 0
+ && mRatchetedNrBandwidths < mNrAdvancedThresholdBandwidth) {
+ if (DBG) {
+ log("isNrAdvanced: false because bandwidths=" + mRatchetedNrBandwidths
+ + " does not meet the threshold=" + mNrAdvancedThresholdBandwidth);
+ }
return false;
}
@@ -1311,17 +1384,17 @@ public class NetworkTypeController extends StateMachine {
}
private boolean isAdditionalNrAdvancedBand() {
- if (ArrayUtils.isEmpty(mAdditionalNrAdvancedBandsList)
- || mPhysicalChannelConfigs == null) {
- return false;
- }
- for (PhysicalChannelConfig item : mPhysicalChannelConfigs) {
- if (item.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR
- && ArrayUtils.contains(mAdditionalNrAdvancedBandsList, item.getBand())) {
- return true;
+ if (mAdditionalNrAdvancedBands.isEmpty() || mRatchetedNrBands.isEmpty()) {
+ if (DBG && !mAdditionalNrAdvancedBands.isEmpty()) {
+ // Only log if mAdditionalNrAdvancedBands is empty to prevent log spam
+ log("isAdditionalNrAdvancedBand: false because bands are empty; configs="
+ + mAdditionalNrAdvancedBands + ", bands=" + mRatchetedNrBands);
}
+ return false;
}
- return false;
+ Set<Integer> intersection = new HashSet<>(mAdditionalNrAdvancedBands);
+ intersection.retainAll(mRatchetedNrBands);
+ return !intersection.isEmpty();
}
private boolean isLte(int rat) {
@@ -1388,8 +1461,13 @@ public class NetworkTypeController extends StateMachine {
+ mIsTimerResetEnabledForLegacyStateRrcIdle);
pw.println("mLtePlusThresholdBandwidth=" + mLtePlusThresholdBandwidth);
pw.println("mNrAdvancedThresholdBandwidth=" + mNrAdvancedThresholdBandwidth);
- pw.println("mAdditionalNrAdvancedBandsList="
- + Arrays.toString(mAdditionalNrAdvancedBandsList));
+ pw.println("mIncludeLteForNrAdvancedThresholdBandwidth="
+ + mIncludeLteForNrAdvancedThresholdBandwidth);
+ pw.println("mRatchetPccFieldsForSameAnchorNrCell=" + mRatchetPccFieldsForSameAnchorNrCell);
+ pw.println("mRatchetedNrBandwidths=" + mRatchetedNrBandwidths);
+ pw.println("mAdditionalNrAdvancedBandsList=" + mAdditionalNrAdvancedBands);
+ pw.println("mRatchetedNrBands=" + mRatchetedNrBands);
+ pw.println("mLastAnchorNrCellId=" + mLastAnchorNrCellId);
pw.println("mPrimaryTimerState=" + mPrimaryTimerState);
pw.println("mSecondaryTimerState=" + mSecondaryTimerState);
pw.println("mPreviousState=" + mPreviousState);
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 4e62d20eb2..0f4f528b72 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -715,6 +715,28 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
+ * Determines if the carrier prefers to use an in service sim for a normal routed emergency
+ * call.
+ * @return true when carrier config
+ * {@link CarrierConfigManager#KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL}
+ * is true.
+ */
+ public boolean shouldPreferInServiceSimForNormalRoutedEmergencyCall() {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle b = configManager.getConfigForSubId(getSubId(), CarrierConfigManager
+ .KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL);
+ if (b != null) {
+ return b.getBoolean(CarrierConfigManager
+ .KEY_PREFER_IN_SERVICE_SIM_FOR_NORMAL_ROUTED_EMERGENCY_CALLS_BOOL,
+ false);
+ } else {
+ // Default value set in CarrierConfigManager
+ return false;
+ }
+ }
+
+ /**
* When overridden the derived class needs to call
* super.handleMessage(msg) so this method has a
* a chance to process the message.
@@ -2264,7 +2286,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
*
* @return Current signal strength as SignalStrength
*/
- public SignalStrength getSignalStrength() {
+ public @NonNull SignalStrength getSignalStrength() {
SignalStrengthController ssc = getSignalStrengthController();
if (ssc == null) {
return new SignalStrength();
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 8b9582419e..06ab58448a 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -28,10 +28,12 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.RegistrantList;
import android.os.SystemProperties;
+import android.provider.DeviceConfig;
import android.sysprop.TelephonyProperties;
import android.telephony.PhoneCapability;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -46,18 +48,21 @@ import java.util.NoSuchElementException;
* This class manages phone's configuration which defines the potential capability (static) of the
* phone and its current activated capability (current).
* It gets and monitors static and current phone capability from the modem; send broadcast
- * if they change, and and sends commands to modem to enable or disable phones.
+ * if they change, and sends commands to modem to enable or disable phones.
*/
public class PhoneConfigurationManager {
public static final String DSDA = "dsda";
public static final String DSDS = "dsds";
public static final String TSTS = "tsts";
public static final String SSSS = "";
+ /** DeviceConfig key for whether Virtual DSDA is enabled. */
+ private static final String KEY_ENABLE_VIRTUAL_DSDA = "enable_virtual_dsda";
private static final String LOG_TAG = "PhoneCfgMgr";
private static final int EVENT_SWITCH_DSDS_CONFIG_DONE = 100;
private static final int EVENT_GET_MODEM_STATUS = 101;
private static final int EVENT_GET_MODEM_STATUS_DONE = 102;
private static final int EVENT_GET_PHONE_CAPABILITY_DONE = 103;
+ private static final int EVENT_DEVICE_CONFIG_CHANGED = 104;
private static PhoneConfigurationManager sInstance = null;
private final Context mContext;
@@ -70,6 +75,11 @@ public class PhoneConfigurationManager {
private final Map<Integer, Boolean> mPhoneStatusMap;
private MockableInterface mMi = new MockableInterface();
private TelephonyManager mTelephonyManager;
+ /**
+ * True if 'Virtual DSDA' i.e., in-call IMS connectivity on both subs with only single logical
+ * modem, is enabled.
+ */
+ private boolean mVirtualDsdaEnabled;
private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList();
private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
@@ -102,6 +112,16 @@ public class PhoneConfigurationManager {
mRadioConfig = RadioConfig.getInstance();
mHandler = new ConfigManagerHandler();
mPhoneStatusMap = new HashMap<>();
+ mVirtualDsdaEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_VIRTUAL_DSDA, false);
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_TELEPHONY, Runnable::run,
+ properties -> {
+ if (TextUtils.equals(DeviceConfig.NAMESPACE_TELEPHONY,
+ properties.getNamespace())) {
+ mHandler.sendEmptyMessage(EVENT_DEVICE_CONFIG_CHANGED);
+ }
+ });
notifyCapabilityChanged();
@@ -127,10 +147,7 @@ public class PhoneConfigurationManager {
// If virtual DSDA is enabled for this UE, then updates maxActiveVoiceSubscriptions to 2.
private PhoneCapability maybeUpdateMaxActiveVoiceSubscriptions(
final PhoneCapability staticCapability) {
- boolean enableVirtualDsda = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enable_virtual_dsda);
-
- if (staticCapability.getLogicalModemList().size() > 1 && enableVirtualDsda) {
+ if (staticCapability.getLogicalModemList().size() > 1 && mVirtualDsdaEnabled) {
return new PhoneCapability.Builder(staticCapability)
.setMaxActiveVoiceSubscriptions(2)
.build();
@@ -197,13 +214,22 @@ public class PhoneConfigurationManager {
ar = (AsyncResult) msg.obj;
if (ar != null && ar.exception == null) {
mStaticCapability = (PhoneCapability) ar.result;
- mStaticCapability =
- maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability);
notifyCapabilityChanged();
} else {
log(msg.what + " failure. Not getting phone capability." + ar.exception);
}
break;
+ case EVENT_DEVICE_CONFIG_CHANGED:
+ boolean isVirtualDsdaEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_VIRTUAL_DSDA, false);
+ if (isVirtualDsdaEnabled != mVirtualDsdaEnabled) {
+ log("EVENT_DEVICE_CONFIG_CHANGED: from " + mVirtualDsdaEnabled + " to "
+ + isVirtualDsdaEnabled);
+ mVirtualDsdaEnabled = isVirtualDsdaEnabled;
+ }
+ break;
+ default:
+ log("Unknown event: " + msg.what);
}
}
}
@@ -317,6 +343,7 @@ public class PhoneConfigurationManager {
mHandler, EVENT_GET_PHONE_CAPABILITY_DONE);
mRadioConfig.getPhoneCapability(callback);
}
+ mStaticCapability = maybeUpdateMaxActiveVoiceSubscriptions(mStaticCapability);
log("getStaticPhoneCapability: mStaticCapability " + mStaticCapability);
return mStaticCapability;
}
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index 9db186fbf8..ae8d033939 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -288,6 +288,7 @@ import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.radio.data.SliceInfo;
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -989,8 +990,9 @@ public class RILUtils {
* @param dp Data profile
* @return The converted DataProfileInfo
*/
- public static android.hardware.radio.data.DataProfileInfo convertToHalDataProfile(
+ public static android.hardware.radio.data.DataProfileInfo convertToHalDataProfile(@Nullable
DataProfile dp) {
+ if (dp == null) return null;
android.hardware.radio.data.DataProfileInfo dpi =
new android.hardware.radio.data.DataProfileInfo();
@@ -3722,7 +3724,8 @@ public class RILUtils {
private static NetworkSliceInfo convertHalSliceInfo(android.hardware.radio.V1_6.SliceInfo si) {
NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder()
.setSliceServiceType(si.sst)
- .setMappedHplmnSliceServiceType(si.mappedHplmnSst);
+ .setMappedHplmnSliceServiceType(si.mappedHplmnSst)
+ .setStatus(convertHalSliceStatus(si.status));
if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
builder.setSliceDifferentiator(si.sliceDifferentiator)
.setMappedHplmnSliceDifferentiator(si.mappedHplmnSD);
@@ -3733,7 +3736,8 @@ public class RILUtils {
private static NetworkSliceInfo convertHalSliceInfo(android.hardware.radio.data.SliceInfo si) {
NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder()
.setSliceServiceType(si.sliceServiceType)
- .setMappedHplmnSliceServiceType(si.mappedHplmnSst);
+ .setMappedHplmnSliceServiceType(si.mappedHplmnSst)
+ .setStatus(convertHalSliceStatus(si.status));
if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
builder.setSliceDifferentiator(si.sliceDifferentiator)
.setMappedHplmnSliceDifferentiator(si.mappedHplmnSd);
@@ -3741,6 +3745,23 @@ public class RILUtils {
return builder.build();
}
+ @NetworkSliceInfo.SliceStatus private static int convertHalSliceStatus(byte status) {
+ switch (status) {
+ case SliceInfo.STATUS_CONFIGURED:
+ return NetworkSliceInfo.SLICE_STATUS_CONFIGURED;
+ case SliceInfo.STATUS_ALLOWED:
+ return NetworkSliceInfo.SLICE_STATUS_ALLOWED;
+ case SliceInfo.STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN:
+ return NetworkSliceInfo.SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN;
+ case SliceInfo.STATUS_REJECTED_NOT_AVAILABLE_IN_REG_AREA:
+ return NetworkSliceInfo.SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA;
+ case SliceInfo.STATUS_DEFAULT_CONFIGURED:
+ return NetworkSliceInfo.SLICE_STATUS_DEFAULT_CONFIGURED;
+ default:
+ return NetworkSliceInfo.SLICE_STATUS_UNKNOWN;
+ }
+ }
+
private static TrafficDescriptor convertHalTrafficDescriptor(
android.hardware.radio.V1_6.TrafficDescriptor td) throws IllegalArgumentException {
String dnn = td.dnn.getDiscriminator()
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 50eea7f695..8a4967085f 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -97,6 +97,7 @@ import com.android.internal.telephony.data.DataNetworkController.DataNetworkCont
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.metrics.ServiceStateStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.satellite.NtnCapabilityResolver;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
@@ -404,6 +405,11 @@ public class ServiceStateTracker extends Handler {
// state in case our service state was never broadcasted (we don't notify
// service states when the subId is invalid)
mPhone.notifyServiceStateChanged(mPhone.getServiceState());
+ // On SubscriptionId changed from invalid to valid sub id, create
+ // ServiceStateProvider with valid sub id entry. Note: PollStateDone can update
+ // the DB again,for the SubID with any change detected at poll state request
+ log("Update SS information on moving from invalid to valid sub id");
+ updateServiceStateToDb(mPhone.getServiceState());
}
boolean restoreSelection = !context.getResources().getBoolean(
@@ -2932,8 +2938,8 @@ public class ServiceStateTracker extends Handler {
// Force display no service
final boolean forceDisplayNoService = shouldForceDisplayNoService() && !mIsSimReady;
- if (!forceDisplayNoService && Phone.isEmergencyCallOnly()) {
- // No service but emergency call allowed
+ if (!forceDisplayNoService && (mEmergencyOnly || Phone.isEmergencyCallOnly())) {
+ // The slot is emc only or the slot is masked as oos due to device is emc only
plmn = Resources.getSystem()
.getText(com.android.internal.R.string.emergency_calls_only).toString();
} else {
@@ -3419,6 +3425,7 @@ public class ServiceStateTracker extends Handler {
updateNrFrequencyRangeFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
updateNrStateFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
+ updateNtnCapability();
if (TelephonyUtils.IS_DEBUGGABLE && mPhone.getTelephonyTester() != null) {
mPhone.getTelephonyTester().overrideServiceState(mNewSS);
@@ -3759,10 +3766,7 @@ public class ServiceStateTracker extends Handler {
mPhone.notifyServiceStateChanged(mPhone.getServiceState());
}
- // insert into ServiceStateProvider. This will trigger apps to wake through JobScheduler
- mPhone.getContext().getContentResolver()
- .insert(getUriForSubscriptionId(mPhone.getSubId()),
- getContentValuesForServiceState(mSS));
+ updateServiceStateToDb(mPhone.getServiceState());
TelephonyMetrics.getInstance().writeServiceStateChanged(mPhone.getPhoneId(), mSS);
mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
@@ -3892,6 +3896,16 @@ public class ServiceStateTracker extends Handler {
}
}
+ /**
+ * Insert SS information into ServiceStateProvider DB table for a sub id.
+ * This will trigger apps to wake through JobScheduler
+ */
+ private void updateServiceStateToDb(ServiceState serviceState) {
+ mPhone.getContext().getContentResolver()
+ .insert(getUriForSubscriptionId(mPhone.getSubId()),
+ getContentValuesForServiceState(serviceState));
+ }
+
private String getOperatorNameFromEri() {
String eriText = null;
if (mPhone.isPhoneTypeCdma()) {
@@ -5538,6 +5552,17 @@ public class ServiceStateTracker extends Handler {
}
}
+ private void updateNtnCapability() {
+ for (NetworkRegistrationInfo nri : mNewSS.getNetworkRegistrationInfoListForTransportType(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) {
+ NtnCapabilityResolver.resolveNtnCapability(nri, mSubId);
+ if (nri.isNonTerrestrialNetwork()) {
+ // Replace the existing NRI with the updated NRI.
+ mNewSS.addNetworkRegistrationInfo(nri);
+ }
+ }
+ }
+
/**
* Check if device is non-roaming and always on home network.
*
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
index 0ae1b5ce79..4b5eebc6e2 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -452,10 +452,12 @@ public class CarrierDisplayNameResolver {
boolean forceDisplayNoService =
mPhone.getServiceStateTracker().shouldForceDisplayNoService() && !isSimReady;
ServiceState ss = getServiceState();
+ // The slot is emc only or oos but the device is emc only.
+ boolean isEmcOnly = ss.isEmergencyOnly() || Phone.isEmergencyCallOnly();
if (ss.getState() == ServiceState.STATE_POWER_OFF && !forceDisplayNoService
- && !Phone.isEmergencyCallOnly()) {
+ && !isEmcOnly) {
plmn = null;
- } else if (forceDisplayNoService || !Phone.isEmergencyCallOnly()) {
+ } else if (forceDisplayNoService || !isEmcOnly) {
plmn = mContext.getResources().getString(
com.android.internal.R.string.lockscreen_carrier_default);
} else {
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
new file mode 100644
index 0000000000..87591deebc
--- /dev/null
+++ b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2023 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.data;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.telephony.SubscriptionManager.DEFAULT_PHONE_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkCapabilities;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.telephony.util.NotificationChannelController;
+import com.android.telephony.Rlog;
+
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * Recommend a data phone to use based on its availability.
+ */
+public class AutoDataSwitchController extends Handler {
+ /** Registration state changed. */
+ public static final int EVALUATION_REASON_REGISTRATION_STATE_CHANGED = 1;
+ /** Telephony Display Info changed. */
+ public static final int EVALUATION_REASON_DISPLAY_INFO_CHANGED = 2;
+ /** Signal Strength changed. */
+ public static final int EVALUATION_REASON_SIGNAL_STRENGTH_CHANGED = 3;
+ /** Default network capabilities changed or lost. */
+ public static final int EVALUATION_REASON_DEFAULT_NETWORK_CHANGED = 4;
+ /** Data enabled settings changed. */
+ public static final int EVALUATION_REASON_DATA_SETTINGS_CHANGED = 5;
+ /** Retry due to previous validation failed. */
+ public static final int EVALUATION_REASON_RETRY_VALIDATION = 6;
+ /** Sim loaded which means slot mapping became available. */
+ public static final int EVALUATION_REASON_SIM_LOADED = 7;
+ /** Voice call ended. */
+ public static final int EVALUATION_REASON_VOICE_CALL_END = 8;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "EVALUATION_REASON_",
+ value = {EVALUATION_REASON_REGISTRATION_STATE_CHANGED,
+ EVALUATION_REASON_DISPLAY_INFO_CHANGED,
+ EVALUATION_REASON_SIGNAL_STRENGTH_CHANGED,
+ EVALUATION_REASON_DEFAULT_NETWORK_CHANGED,
+ EVALUATION_REASON_DATA_SETTINGS_CHANGED,
+ EVALUATION_REASON_RETRY_VALIDATION,
+ EVALUATION_REASON_SIM_LOADED,
+ EVALUATION_REASON_VOICE_CALL_END})
+ public @interface AutoDataSwitchEvaluationReason {}
+
+ private static final String LOG_TAG = "ADSC";
+
+ /** Event for service state changed. */
+ private static final int EVENT_SERVICE_STATE_CHANGED = 1;
+ /** Event for display info changed. This is for getting 5G NSA or mmwave information. */
+ private static final int EVENT_DISPLAY_INFO_CHANGED = 2;
+ /** Event for evaluate auto data switch opportunity. */
+ private static final int EVENT_EVALUATE_AUTO_SWITCH = 3;
+ /** Event for signal strength changed. */
+ private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 4;
+ /** Event indicates the switch state is stable, proceed to validation as the next step. */
+ private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 5;
+
+ /** Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS} */
+ private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+ /**
+ * When starting this activity, this extra can also be specified to supply a Bundle of arguments
+ * to pass to that fragment when it is instantiated during the initial creation of the activity.
+ */
+ private static final String SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS =
+ ":settings:show_fragment_args";
+ /** The resource ID of the auto data switch fragment in settings. **/
+ private static final String AUTO_DATA_SWITCH_SETTING_R_ID = "auto_data_switch";
+ /** Notification tag **/
+ private static final String AUTO_DATA_SWITCH_NOTIFICATION_TAG = "auto_data_switch";
+ /** Notification ID **/
+ private static final int AUTO_DATA_SWITCH_NOTIFICATION_ID = 1;
+
+ private final @NonNull LocalLog mLocalLog = new LocalLog(128);
+ private final @NonNull Context mContext;
+ private final @NonNull SubscriptionManagerService mSubscriptionManagerService;
+ private final @NonNull PhoneSwitcher mPhoneSwitcher;
+ private final @NonNull AutoDataSwitchControllerCallback mPhoneSwitcherCallback;
+ private boolean mDefaultNetworkIsOnNonCellular = false;
+ /** {@code true} if we've displayed the notification the first time auto switch occurs **/
+ private boolean mDisplayedNotification = false;
+ /**
+ * Time threshold in ms to define a internet connection status to be stable(e.g. out of service,
+ * in service, wifi is the default active network.etc), while -1 indicates auto switch
+ * feature disabled.
+ */
+ private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1;
+ /**
+ * {@code true} if requires ping test before switching preferred data modem; otherwise, switch
+ * even if ping test fails.
+ */
+ private boolean mRequirePingTestBeforeSwitch = true;
+ /** The count of consecutive auto switch validation failure **/
+ private int mAutoSwitchValidationFailedCount = 0;
+ /**
+ * The maximum number of retries when a validation for switching failed.
+ */
+ private int mAutoDataSwitchValidationMaxRetry;
+
+ private @NonNull PhoneSignalStatus[] mPhonesSignalStatus;
+
+ /**
+ * To track the signal status of a phone in order to evaluate whether it's a good candidate to
+ * switch to.
+ */
+ private static class PhoneSignalStatus {
+ private @NonNull Phone mPhone;
+ private @NetworkRegistrationInfo.RegistrationState int mDataRegState =
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+ private @NonNull TelephonyDisplayInfo mDisplayInfo;
+ private @NonNull SignalStrength mSignalStrength;
+
+ private int mScore;
+
+ private PhoneSignalStatus(@NonNull Phone phone) {
+ this.mPhone = phone;
+ this.mDisplayInfo = phone.getDisplayInfoController().getTelephonyDisplayInfo();
+ this.mSignalStrength = phone.getSignalStrength();
+ }
+ private int updateScore() {
+ // TODO: score = inservice? dcm.getscore() : 0
+ return mScore;
+ }
+ @Override
+ public String toString() {
+ return "{phoneId=" + mPhone.getPhoneId()
+ + " score=" + mScore + " dataRegState="
+ + NetworkRegistrationInfo.registrationStateToString(mDataRegState)
+ + " display=" + mDisplayInfo + " signalStrength=" + mSignalStrength.getLevel()
+ + "}";
+
+ }
+ }
+
+ /**
+ * This is the callback used for listening events from {@link AutoDataSwitchController}.
+ */
+ public abstract static class AutoDataSwitchControllerCallback {
+ /**
+ * Called when a target data phone is recommended by the controller.
+ * @param targetPhoneId The target phone Id.
+ * @param needValidation {@code true} if need a ping test to pass before switching.
+ */
+ public abstract void onRequireValidation(int targetPhoneId, boolean needValidation);
+
+ /**
+ * Called when a target data phone is demanded by the controller.
+ * @param targetPhoneId The target phone Id.
+ * @param reason The reason for the demand.
+ */
+ public abstract void onRequireImmediatelySwitchToPhone(int targetPhoneId,
+ @AutoDataSwitchEvaluationReason int reason);
+
+ /**
+ * Called when the controller asks to cancel any pending validation attempts because the
+ * environment is no longer suited for switching.
+ */
+ public abstract void onRequireCancelAnyPendingAutoSwitchValidation();
+ }
+
+ /**
+ * @param context Context.
+ * @param looper Main looper.
+ * @param phoneSwitcher Phone switcher.
+ * @param phoneSwitcherCallback Callback for phone switcher to execute.
+ */
+ public AutoDataSwitchController(@NonNull Context context, @NonNull Looper looper,
+ @NonNull PhoneSwitcher phoneSwitcher,
+ @NonNull AutoDataSwitchControllerCallback phoneSwitcherCallback) {
+ super(looper);
+ mContext = context;
+ mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+ mPhoneSwitcher = phoneSwitcher;
+ mPhoneSwitcherCallback = phoneSwitcherCallback;
+ readDeviceResourceConfig();
+ int numActiveModems = PhoneFactory.getPhones().length;
+ mPhonesSignalStatus = new PhoneSignalStatus[numActiveModems];
+ for (int phoneId = 0; phoneId < numActiveModems; phoneId++) {
+ registerAllEventsForPhone(phoneId);
+ }
+ }
+
+ /**
+ * Called when active modem count changed, update all tracking events.
+ * @param numActiveModems The current number of active modems.
+ */
+ public synchronized void onMultiSimConfigChanged(int numActiveModems) {
+ int oldActiveModems = mPhonesSignalStatus.length;
+ if (oldActiveModems == numActiveModems) return;
+ // Dual -> Single
+ for (int phoneId = numActiveModems; phoneId < oldActiveModems; phoneId++) {
+ Phone phone = mPhonesSignalStatus[phoneId].mPhone;
+ phone.getDisplayInfoController().unregisterForTelephonyDisplayInfoChanged(this);
+ phone.getSignalStrengthController().unregisterForSignalStrengthChanged(this);
+ phone.getServiceStateTracker().unregisterForServiceStateChanged(this);
+ }
+ mPhonesSignalStatus = Arrays.copyOf(mPhonesSignalStatus, numActiveModems);
+ // Signal -> Dual
+ for (int phoneId = oldActiveModems; phoneId < numActiveModems; phoneId++) {
+ registerAllEventsForPhone(phoneId);
+ }
+ }
+
+ /**
+ * Register all tracking events for a phone.
+ * @param phoneId The phone to register for all events.
+ */
+ private void registerAllEventsForPhone(int phoneId) {
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ mPhonesSignalStatus[phoneId] = new PhoneSignalStatus(phone);
+ phone.getDisplayInfoController().registerForTelephonyDisplayInfoChanged(
+ this, EVENT_DISPLAY_INFO_CHANGED, phoneId);
+ phone.getSignalStrengthController().registerForSignalStrengthChanged(
+ this, EVENT_SIGNAL_STRENGTH_CHANGED, phoneId);
+ phone.getServiceStateTracker().registerForServiceStateChanged(this,
+ EVENT_SERVICE_STATE_CHANGED, phoneId);
+ } else {
+ loge("Unexpected null phone " + phoneId + " when register all events");
+ }
+ }
+
+ /**
+ * Read the default device config from any default phone because the resource config are per
+ * device. No need to register callback for the same reason.
+ */
+ private void readDeviceResourceConfig() {
+ Phone phone = PhoneFactory.getDefaultPhone();
+ DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
+ mRequirePingTestBeforeSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired();
+ mAutoDataSwitchAvailabilityStabilityTimeThreshold =
+ dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
+ mAutoDataSwitchValidationMaxRetry =
+ dataConfig.getAutoDataSwitchValidationMaxRetry();
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ AsyncResult ar;
+ int phoneId;
+ switch (msg.what) {
+ case EVENT_SERVICE_STATE_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ phoneId = (int) ar.userObj;
+ onRegistrationStateChanged(phoneId);
+ break;
+ case EVENT_DISPLAY_INFO_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ phoneId = (int) ar.userObj;
+ onDisplayInfoChanged(phoneId);
+ break;
+ case EVENT_EVALUATE_AUTO_SWITCH:
+ int reason = (int) msg.obj;
+ onEvaluateAutoDataSwitch(reason);
+ break;
+ case EVENT_MEETS_AUTO_DATA_SWITCH_STATE:
+ int targetPhoneId = msg.arg1;
+ boolean needValidation = (boolean) msg.obj;
+ log("require validation on phone " + targetPhoneId
+ + (needValidation ? "" : " no") + " need to pass");
+ mPhoneSwitcherCallback.onRequireValidation(targetPhoneId, needValidation);
+ break;
+ default:
+ loge("Unexpected event " + msg.what);
+ }
+ }
+
+ /**
+ * Called when registration state changed.
+ */
+ private void onRegistrationStateChanged(int phoneId) {
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ int oldRegState = mPhonesSignalStatus[phoneId].mDataRegState;
+ int newRegState = phone.getServiceState()
+ .getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .getRegistrationState();
+ if (newRegState != oldRegState) {
+ mPhonesSignalStatus[phoneId].mDataRegState = newRegState;
+ log("onRegistrationStateChanged: phone " + phoneId + " "
+ + NetworkRegistrationInfo.registrationStateToString(oldRegState)
+ + " -> "
+ + NetworkRegistrationInfo.registrationStateToString(newRegState));
+ evaluateAutoDataSwitch(EVALUATION_REASON_REGISTRATION_STATE_CHANGED);
+ } else {
+ log("onRegistrationStateChanged: no change.");
+ }
+ } else {
+ loge("Unexpected null phone " + phoneId + " upon its registration state changed");
+ }
+ }
+
+ /**
+ * @return {@code true} if the phone state is considered in service.
+ */
+ private boolean isInService(@NetworkRegistrationInfo.RegistrationState int dataRegState) {
+ return dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME
+ || dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+ }
+
+ /**
+ * Called when {@link TelephonyDisplayInfo} changed. This can happen when network types or
+ * override network types (5G NSA, 5G MMWAVE) change.
+ */
+ private void onDisplayInfoChanged(int phoneId) {
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ TelephonyDisplayInfo displayInfo = phone.getDisplayInfoController()
+ .getTelephonyDisplayInfo();
+ //TODO(b/260928808)
+ log("onDisplayInfoChanged:" + displayInfo);
+ } else {
+ loge("Unexpected null phone " + phoneId + " upon its display info changed");
+ }
+ }
+
+ /**
+ * Schedule for auto data switch evaluation.
+ * @param reason The reason for the evaluation.
+ */
+ public void evaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) {
+ long delayMs = reason == EVALUATION_REASON_RETRY_VALIDATION
+ ? mAutoDataSwitchAvailabilityStabilityTimeThreshold
+ << mAutoSwitchValidationFailedCount
+ : 0;
+ if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
+ sendMessageDelayed(obtainMessage(EVENT_EVALUATE_AUTO_SWITCH, reason), delayMs);
+ }
+ }
+
+ /**
+ * Evaluate for auto data switch opportunity.
+ * If suitable to switch, check that the suitable state is stable(or switch immediately if user
+ * turned off settings).
+ * @param reason The reason for the evaluation.
+ */
+ private void onEvaluateAutoDataSwitch(@AutoDataSwitchEvaluationReason int reason) {
+ // auto data switch feature is disabled.
+ if (mAutoDataSwitchAvailabilityStabilityTimeThreshold < 0) return;
+ int defaultDataSubId = mSubscriptionManagerService.getDefaultDataSubId();
+ // check is valid DSDS
+ if (!isActiveSubId(defaultDataSubId) || mSubscriptionManagerService
+ .getActiveSubIdList(true).length <= 1) {
+ return;
+ }
+ Phone defaultDataPhone = PhoneFactory.getPhone(mSubscriptionManagerService.getPhoneId(
+ defaultDataSubId));
+ if (defaultDataPhone == null) {
+ loge("onEvaluateAutoDataSwitch: cannot find the phone associated with default data"
+ + " subscription " + defaultDataSubId);
+ return;
+ }
+ int defaultDataPhoneId = defaultDataPhone.getPhoneId();
+ int preferredPhoneId = mPhoneSwitcher.getPreferredDataPhoneId();
+ log("onEvaluateAutoDataSwitch: defaultPhoneId: " + defaultDataPhoneId
+ + " preferredPhoneId: " + preferredPhoneId
+ + " reason: " + evaluationReasonToString(reason));
+ if (preferredPhoneId == defaultDataPhoneId) {
+ // on default data sub
+ int candidatePhoneId = getSwitchCandidatePhoneId(defaultDataPhoneId);
+ if (candidatePhoneId != INVALID_PHONE_INDEX) {
+ startStabilityCheck(candidatePhoneId, mRequirePingTestBeforeSwitch);
+ } else {
+ cancelAnyPendingSwitch();
+ }
+ } else {
+ // on backup data sub
+ Phone backupDataPhone = PhoneFactory.getPhone(preferredPhoneId);
+ if (backupDataPhone == null) {
+ loge("onEvaluateAutoDataSwitch: Unexpected null phone " + preferredPhoneId
+ + " as the current active data phone");
+ return;
+ }
+
+ if (!defaultDataPhone.isUserDataEnabled() || !backupDataPhone.isDataAllowed()) {
+ // immediately switch back if user disabled setting changes
+ mPhoneSwitcherCallback.onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
+ EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ return;
+ }
+
+ if (mDefaultNetworkIsOnNonCellular) {
+ log("onEvaluateAutoDataSwitch: Default network is active on nonCellular transport");
+ startStabilityCheck(DEFAULT_PHONE_INDEX, false);
+ return;
+ }
+
+ if (mPhonesSignalStatus[preferredPhoneId].mDataRegState
+ != NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
+ // backup phone lost its HOME registration
+ startStabilityCheck(DEFAULT_PHONE_INDEX, false);
+ return;
+ }
+
+ if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) {
+ // default phone is back to service
+ startStabilityCheck(DEFAULT_PHONE_INDEX, mRequirePingTestBeforeSwitch);
+ return;
+ }
+
+ // cancel any previous attempts of switching back to default phone
+ cancelAnyPendingSwitch();
+ }
+ }
+
+ /**
+ * Called when consider switching from primary default data sub to another data sub.
+ * @return the target subId if a suitable candidate is found, otherwise return
+ * {@link SubscriptionManager#INVALID_PHONE_INDEX}
+ */
+ private int getSwitchCandidatePhoneId(int defaultPhoneId) {
+ Phone defaultDataPhone = PhoneFactory.getPhone(defaultPhoneId);
+ if (defaultDataPhone == null) {
+ log("getSwitchCandidatePhoneId: no sim loaded");
+ return INVALID_PHONE_INDEX;
+ }
+
+ if (!defaultDataPhone.isUserDataEnabled()) {
+ log("getSwitchCandidatePhoneId: user disabled data");
+ return INVALID_PHONE_INDEX;
+ }
+
+ if (mDefaultNetworkIsOnNonCellular) {
+ // Exists other active default transport
+ log("getSwitchCandidatePhoneId: Default network is active on non-cellular transport");
+ return INVALID_PHONE_INDEX;
+ }
+
+ // check whether primary and secondary signal status are worth switching
+ if (isInService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
+ log("getSwitchCandidatePhoneId: DDS is in service");
+ return INVALID_PHONE_INDEX;
+ }
+ for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
+ if (phoneId != defaultPhoneId) {
+ // the alternative phone must have HOME availability
+ if (mPhonesSignalStatus[phoneId].mDataRegState
+ == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
+ log("getSwitchCandidatePhoneId: found phone " + phoneId
+ + " in HOME service");
+ Phone secondaryDataPhone = PhoneFactory.getPhone(phoneId);
+ if (secondaryDataPhone != null && // check auto switch feature enabled
+ secondaryDataPhone.isDataAllowed()) {
+ return phoneId;
+ }
+ }
+ }
+ }
+ return INVALID_PHONE_INDEX;
+ }
+
+ /**
+ * 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.
+ * @param targetPhoneId the target phone Id.
+ * @param needValidation {@code true} if validation is needed.
+ */
+ private void startStabilityCheck(int targetPhoneId, boolean needValidation) {
+ log("startAutoDataSwitchStabilityCheck: targetPhoneId=" + targetPhoneId
+ + " needValidation=" + needValidation);
+ if (!hasMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, needValidation)) {
+ sendMessageDelayed(obtainMessage(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, targetPhoneId,
+ 0/*placeholder*/,
+ needValidation),
+ mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+ }
+ }
+
+ /** Auto data switch evaluation reason to string. */
+ public static @NonNull String evaluationReasonToString(
+ @AutoDataSwitchEvaluationReason int reason) {
+ switch (reason) {
+ case EVALUATION_REASON_REGISTRATION_STATE_CHANGED: return "REGISTRATION_STATE_CHANGED";
+ case EVALUATION_REASON_DISPLAY_INFO_CHANGED: return "DISPLAY_INFO_CHANGED";
+ case EVALUATION_REASON_SIGNAL_STRENGTH_CHANGED: return "SIGNAL_STRENGTH_CHANGED";
+ case EVALUATION_REASON_DEFAULT_NETWORK_CHANGED: return "DEFAULT_NETWORK_CHANGED";
+ case EVALUATION_REASON_DATA_SETTINGS_CHANGED: return "DATA_SETTINGS_CHANGED";
+ case EVALUATION_REASON_RETRY_VALIDATION: return "RETRY_VALIDATION";
+ case EVALUATION_REASON_SIM_LOADED: return "SIM_LOADED";
+ case EVALUATION_REASON_VOICE_CALL_END: return "VOICE_CALL_END";
+ }
+ return "Unknown(" + reason + ")";
+ }
+
+ /** @return {@code true} if the sub is active. */
+ private boolean isActiveSubId(int subId) {
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerService
+ .getSubscriptionInfoInternal(subId);
+ return subInfo != null && subInfo.isActive();
+ }
+
+ /**
+ * Called when default network capabilities changed. If default network is active on
+ * non-cellular, switch back to the default data phone. If default network is lost, try to find
+ * another sub to switch to.
+ * @param networkCapabilities {@code null} indicates default network lost.
+ */
+ public void updateDefaultNetworkCapabilities(
+ @Nullable NetworkCapabilities networkCapabilities) {
+ if (networkCapabilities != null) {
+ // Exists default network
+ mDefaultNetworkIsOnNonCellular = !networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
+ if (mDefaultNetworkIsOnNonCellular
+ && isActiveSubId(mPhoneSwitcher.getAutoSelectedDataSubId())) {
+ log("default network is active on non cellular, switch back to default");
+ evaluateAutoDataSwitch(EVALUATION_REASON_DEFAULT_NETWORK_CHANGED);
+ }
+ } else {
+ log("default network is lost, try to find another active sub to switch to");
+ mDefaultNetworkIsOnNonCellular = false;
+ evaluateAutoDataSwitch(EVALUATION_REASON_DEFAULT_NETWORK_CHANGED);
+ }
+ }
+
+ /**
+ * Cancel any auto switch attempts when the current environment is not suitable for auto switch.
+ */
+ private void cancelAnyPendingSwitch() {
+ resetFailedCount();
+ removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE);
+ mPhoneSwitcherCallback.onRequireCancelAnyPendingAutoSwitchValidation();
+ }
+
+ /**
+ * Display a notification the first time auto data switch occurs.
+ * @param phoneId The phone Id of the current preferred phone.
+ * @param isDueToAutoSwitch {@code true} if the switch was due to auto data switch feature.
+ */
+ public void displayAutoDataSwitchNotification(int phoneId, boolean isDueToAutoSwitch) {
+ NotificationManager notificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ if (mDisplayedNotification) {
+ // cancel posted notification if any exist
+ log("displayAutoDataSwitchNotification: canceling any notifications for phone "
+ + phoneId);
+ notificationManager.cancel(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
+ AUTO_DATA_SWITCH_NOTIFICATION_ID);
+ return;
+ }
+ // proceed only the first time auto data switch occurs, which includes data during call
+ if (!isDueToAutoSwitch) {
+ return;
+ }
+ SubscriptionInfo subInfo = mSubscriptionManagerService
+ .getSubscriptionInfo(mSubscriptionManagerService.getSubId(phoneId));
+ if (subInfo == null || subInfo.isOpportunistic()) {
+ loge("displayAutoDataSwitchNotification: phoneId="
+ + phoneId + " unexpected subInfo " + subInfo);
+ return;
+ }
+ int subId = subInfo.getSubscriptionId();
+ logl("displayAutoDataSwitchNotification: display for subId=" + subId);
+ // "Mobile network settings" screen / dialog
+ Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+ final Bundle fragmentArgs = new Bundle();
+ // Special contract for Settings to highlight permission row
+ fragmentArgs.putString(SETTINGS_EXTRA_FRAGMENT_ARG_KEY, AUTO_DATA_SWITCH_SETTING_R_ID);
+ intent.putExtra(Settings.EXTRA_SUB_ID, subId);
+ intent.putExtra(SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
+ PendingIntent contentIntent = PendingIntent.getActivity(
+ mContext, subId, intent, PendingIntent.FLAG_IMMUTABLE);
+
+ CharSequence activeCarrierName = subInfo.getDisplayName();
+ CharSequence contentTitle = mContext.getString(
+ com.android.internal.R.string.auto_data_switch_title, activeCarrierName);
+ CharSequence contentText = mContext.getText(
+ com.android.internal.R.string.auto_data_switch_content);
+
+ final Notification notif = new Notification.Builder(mContext)
+ .setContentTitle(contentTitle)
+ .setContentText(contentText)
+ .setSmallIcon(android.R.drawable.stat_sys_warning)
+ .setColor(mContext.getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
+ .setContentIntent(contentIntent)
+ .setStyle(new Notification.BigTextStyle().bigText(contentText))
+ .build();
+ notificationManager.notify(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
+ AUTO_DATA_SWITCH_NOTIFICATION_ID, notif);
+ mDisplayedNotification = true;
+ }
+
+ /** Enable future switch retry again. Called when switch condition changed. */
+ public void resetFailedCount() {
+ mAutoSwitchValidationFailedCount = 0;
+ }
+
+ /**
+ * Called when skipped switch due to validation failed. Schedule retry to switch again.
+ */
+ public void evaluateRetryOnValidationFailed() {
+ if (mAutoSwitchValidationFailedCount < mAutoDataSwitchValidationMaxRetry) {
+ evaluateAutoDataSwitch(EVALUATION_REASON_RETRY_VALIDATION);
+ mAutoSwitchValidationFailedCount++;
+ } else {
+ logl("evaluateRetryOnValidationFailed: reached max auto switch retry count "
+ + mAutoDataSwitchValidationMaxRetry);
+ mAutoSwitchValidationFailedCount = 0;
+ }
+ }
+
+ /**
+ * Log debug messages.
+ * @param s debug messages
+ */
+ private void log(@NonNull String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+
+ /**
+ * Log error messages.
+ * @param s error messages
+ */
+ private void loge(@NonNull String s) {
+ Rlog.e(LOG_TAG, s);
+ }
+
+ /**
+ * Log debug messages and also log into the local log.
+ * @param s debug messages
+ */
+ private void logl(@NonNull String s) {
+ log(s);
+ mLocalLog.log(s);
+ }
+
+ /**
+ * Dump the state of DataNetworkController
+ *
+ * @param fd File descriptor
+ * @param printWriter Print writer
+ * @param args Arguments
+ */
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("AutoDataSwitchController:");
+ pw.increaseIndent();
+ pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry
+ + " mAutoSwitchValidationFailedCount=" + mAutoSwitchValidationFailedCount);
+ pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeSwitch);
+ pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold="
+ + mAutoDataSwitchAvailabilityStabilityTimeThreshold);
+ pw.increaseIndent();
+ for (PhoneSignalStatus status: mPhonesSignalStatus) {
+ pw.println(status);
+ }
+ pw.decreaseIndent();
+ mLocalLog.dump(fd, pw, args);
+ pw.decreaseIndent();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/data/DataConfigManager.java b/src/java/com/android/internal/telephony/data/DataConfigManager.java
index 78450a81ed..f7fe4ad0f8 100644
--- a/src/java/com/android/internal/telephony/data/DataConfigManager.java
+++ b/src/java/com/android/internal/telephony/data/DataConfigManager.java
@@ -32,6 +32,7 @@ import android.telephony.Annotation.NetCapability;
import android.telephony.Annotation.NetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
@@ -199,8 +200,6 @@ public class DataConfigManager extends Handler {
"anomaly_setup_data_call_failure";
/** DeviceConfig key of anomaly report threshold for frequent network-unwanted call. */
private static final String KEY_ANOMALY_NETWORK_UNWANTED = "anomaly_network_unwanted";
- /** DeviceConfig key of anomaly report threshold for frequent change of preferred network. */
- private static final String KEY_ANOMALY_QNS_CHANGE_NETWORK = "anomaly_qns_change_network";
/** DeviceConfig key of anomaly report threshold for invalid QNS params. */
private static final String KEY_ANOMALY_QNS_PARAM = "anomaly_qns_param";
/** DeviceConfig key of anomaly report threshold for DataNetwork stuck in connecting state. */
@@ -214,13 +213,8 @@ public class DataConfigManager extends Handler {
"anomaly_network_handover_timeout";
/** DeviceConfig key of anomaly report: True for enabling APN config invalidity detection */
private static final String KEY_ANOMALY_APN_CONFIG_ENABLED = "anomaly_apn_config_enabled";
- /** DeviceConfig key of the time threshold in ms for defining a network status to be stable. **/
- private static final String KEY_AUTO_DATA_SWITCH_AVAILABILITY_STABILITY_TIME_THRESHOLD =
- "auto_data_switch_availability_stability_time_threshold";
- /** DeviceConfig key of the maximum number of retries when a validation for switching failed.**/
- private static final String KEY_AUTO_DATA_SWITCH_VALIDATION_MAX_RETRY =
- "auto_data_switch_validation_max_retry";
-
+ /** Invalid auto data switch score. */
+ private static final int INVALID_AUTO_DATA_SWITCH_SCORE = -1;
/** Anomaly report thresholds for frequent setup data call failure. */
private EventFrequency mSetupDataCallAnomalyReportThreshold;
@@ -301,6 +295,12 @@ public class DataConfigManager extends Handler {
private @NonNull final List<HandoverRule> mHandoverRuleList = new ArrayList<>();
/** {@code True} keep IMS network in case of moving to non VOPS area; {@code false} otherwise.*/
private boolean mShouldKeepNetworkUpInNonVops = false;
+ /**
+ * A map of network types to the estimated downlink values by signal strength 0 - 4 for that
+ * network type
+ */
+ private @NonNull final @DataConfigNetworkType Map<String, int[]>
+ mAutoDataSwitchNetworkTypeSignalMap = new ConcurrentHashMap<>();
/**
* Constructor
@@ -452,6 +452,7 @@ public class DataConfigManager extends Handler {
updateBandwidths();
updateTcpBuffers();
updateHandoverRules();
+ updateAutoDataSwitchConfig();
log("Carrier config updated. Config is " + (isConfigCarrierSpecific() ? "" : "not ")
+ "carrier specific.");
@@ -918,6 +919,84 @@ public class DataConfigManager extends Handler {
}
/**
+ * Update the network type and signal strength score table for auto data switch decisions.
+ */
+ private void updateAutoDataSwitchConfig() {
+ synchronized (this) {
+ mAutoDataSwitchNetworkTypeSignalMap.clear();
+ final PersistableBundle table = mCarrierConfig.getPersistableBundle(
+ CarrierConfigManager.KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE);
+ String[] networkTypeKeys = {
+ DATA_CONFIG_NETWORK_TYPE_GPRS,
+ DATA_CONFIG_NETWORK_TYPE_EDGE,
+ DATA_CONFIG_NETWORK_TYPE_UMTS,
+ DATA_CONFIG_NETWORK_TYPE_CDMA,
+ DATA_CONFIG_NETWORK_TYPE_1xRTT,
+ DATA_CONFIG_NETWORK_TYPE_EVDO_0,
+ DATA_CONFIG_NETWORK_TYPE_EVDO_A,
+ DATA_CONFIG_NETWORK_TYPE_HSDPA,
+ DATA_CONFIG_NETWORK_TYPE_HSUPA,
+ DATA_CONFIG_NETWORK_TYPE_HSPA,
+ DATA_CONFIG_NETWORK_TYPE_EVDO_B,
+ DATA_CONFIG_NETWORK_TYPE_EHRPD,
+ DATA_CONFIG_NETWORK_TYPE_IDEN,
+ DATA_CONFIG_NETWORK_TYPE_LTE,
+ DATA_CONFIG_NETWORK_TYPE_HSPAP,
+ DATA_CONFIG_NETWORK_TYPE_GSM,
+ DATA_CONFIG_NETWORK_TYPE_TD_SCDMA,
+ DATA_CONFIG_NETWORK_TYPE_NR_NSA,
+ DATA_CONFIG_NETWORK_TYPE_NR_NSA_MMWAVE,
+ DATA_CONFIG_NETWORK_TYPE_NR_SA,
+ DATA_CONFIG_NETWORK_TYPE_NR_SA_MMWAVE
+ };
+ if (table != null) {
+ for (String networkType : networkTypeKeys) {
+ int[] scores = table.getIntArray(networkType);
+ if (scores != null
+ && scores.length == SignalStrength.NUM_SIGNAL_STRENGTH_BINS) {
+ for (int i = 0; i < scores.length; i++) {
+ if (scores[i] < 0) {
+ loge("Auto switch score must not < 0 for network type "
+ + networkType);
+ break;
+ }
+ if (i == scores.length - 1) {
+ mAutoDataSwitchNetworkTypeSignalMap.put(networkType, scores);
+ }
+ }
+ } else {
+ loge("Auto switch score table should specify "
+ + SignalStrength.NUM_SIGNAL_STRENGTH_BINS
+ + " signal strength for network type " + networkType);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @param displayInfo The displayed network info.
+ * @param signalStrength The signal strength.
+ * @return Score base on network type and signal strength to inform auto data switch decision.
+ */
+ public int getAutoDataSwitchScore(@NonNull TelephonyDisplayInfo displayInfo,
+ @NonNull SignalStrength signalStrength) {
+ int[] scores = mAutoDataSwitchNetworkTypeSignalMap.get(
+ getDataConfigNetworkType(displayInfo));
+ return scores != null ? scores[signalStrength.getLevel()] : INVALID_AUTO_DATA_SWITCH_SCORE;
+ }
+
+ /**
+ * @return The tolerated gap of score for auto data switch decision, larger than which the
+ * device will switch to the SIM with higher score. If 0, the device always switch to the higher
+ * score SIM. If < 0, the network type and signal strength based auto switch is disabled.
+ */
+ public int getAutoDataSwitchScoreTolerance() {
+ return mResources.getInteger(com.android.internal.R.integer
+ .auto_data_switch_score_tolerance);
+ }
+
+ /**
* @return The maximum number of retries when a validation for switching failed.
*/
public int getAutoDataSwitchValidationMaxRetry() {
@@ -1264,6 +1343,15 @@ public class DataConfigManager extends Handler {
}
/**
+ * @return {@code true} if allow sending null data profile to ask modem to clear the initial
+ * attach data profile.
+ */
+ public boolean allowClearInitialAttachDataProfile() {
+ return mResources.getBoolean(
+ com.android.internal.R.bool.allow_clear_initial_attach_data_profile);
+ }
+
+ /**
* Log debug messages.
* @param s debug messages
*/
@@ -1315,9 +1403,15 @@ public class DataConfigManager extends Handler {
pw.println("mNetworkDisconnectingTimeout=" + mNetworkDisconnectingTimeout);
pw.println("mNetworkHandoverTimeout=" + mNetworkHandoverTimeout);
pw.println("mIsApnConfigAnomalyReportEnabled=" + mIsApnConfigAnomalyReportEnabled);
+ pw.println("Auto data switch:");
+ pw.increaseIndent();
+ pw.println("getAutoDataSwitchScoreTolerance=" + getAutoDataSwitchScoreTolerance());
+ mAutoDataSwitchNetworkTypeSignalMap.forEach((key, value) -> pw.println(key + ":"
+ + Arrays.toString(value)));
pw.println("getAutoDataSwitchAvailabilityStabilityTimeThreshold="
+ getAutoDataSwitchAvailabilityStabilityTimeThreshold());
pw.println("getAutoDataSwitchValidationMaxRetry=" + getAutoDataSwitchValidationMaxRetry());
+ pw.decreaseIndent();
pw.println("Metered APN types=" + mMeteredApnTypes.stream()
.map(ApnSetting::getApnTypeString).collect(Collectors.joining(",")));
pw.println("Roaming metered APN types=" + mRoamingMeteredApnTypes.stream()
@@ -1359,6 +1453,7 @@ public class DataConfigManager extends Handler {
pw.println("isEnhancedIwlanHandoverCheckEnabled=" + isEnhancedIwlanHandoverCheckEnabled());
pw.println("isTetheringProfileDisabledForRoaming="
+ isTetheringProfileDisabledForRoaming());
+ pw.println("allowClearInitialAttachDataProfile=" + allowClearInitialAttachDataProfile());
pw.decreaseIndent();
}
}
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index d533933bf2..6ba251b430 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -664,6 +664,11 @@ public class DataNetwork extends StateMachine {
*/
private @NetworkType int mLastKnownDataNetworkType;
+ /**
+ * The last known roaming state of this data network.
+ */
+ private boolean mLastKnownRoamingState;
+
/** The reason that why setting up this data network is allowed. */
private @NonNull DataAllowedReason mDataAllowedReason;
@@ -923,6 +928,7 @@ public class DataNetwork extends StateMachine {
}
mTransport = transport;
mLastKnownDataNetworkType = getDataNetworkType();
+ mLastKnownRoamingState = mPhone.getServiceState().getDataRoamingFromRegistration();
mDataAllowedReason = dataAllowedReason;
dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
mAttachedNetworkRequestList.addAll(networkRequestList);
@@ -1136,6 +1142,15 @@ public class DataNetwork extends StateMachine {
if (networkType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
mLastKnownDataNetworkType = networkType;
}
+ NetworkRegistrationInfo nri =
+ mPhone.getServiceState()
+ .getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (nri != null && nri.isInService()) {
+ mLastKnownRoamingState = nri.getNetworkRegistrationState()
+ == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+ }
updateSuspendState();
updateNetworkCapabilities();
break;
@@ -2224,7 +2239,11 @@ public class DataNetwork extends StateMachine {
TrafficDescriptor trafficDescriptor = mDataProfile.getTrafficDescriptor();
final boolean matchAllRuleAllowed = trafficDescriptor == null
- || !TextUtils.isEmpty(trafficDescriptor.getDataNetworkName());
+ || !TextUtils.isEmpty(trafficDescriptor.getDataNetworkName())
+ // Both OsAppId and APN name are null. This helps for modem to handle when we
+ // are on 5G or LTE with URSP support in falling back to default network.
+ || (TextUtils.isEmpty(trafficDescriptor.getDataNetworkName())
+ && trafficDescriptor.getOsAppId() == null);
int accessNetwork = DataUtils.networkTypeToAccessNetworkType(dataNetworkType);
@@ -3391,6 +3410,13 @@ public class DataNetwork extends StateMachine {
}
/**
+ * @return The last known roaming state of this data network.
+ */
+ public boolean getLastKnownRoamingState() {
+ return mLastKnownRoamingState;
+ }
+
+ /**
* @return The PCO data received from the network.
*/
public @NonNull Map<Integer, PcoData> getPcoData() {
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 2ee1ffd3fb..f9e7510612 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -1983,21 +1983,31 @@ public class DataNetworkController extends Handler {
}
int sourceAccessNetwork = DataUtils.networkTypeToAccessNetworkType(
sourceNetworkType);
-
+ NetworkRegistrationInfo nri = mServiceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ boolean isWwanInService = false;
+ if (nri != null && nri.isInService()) {
+ isWwanInService = true;
+ }
+ // If WWAN is inService, use the real roaming state reported by modem instead of
+ // using the overridden roaming state, otherwise get last known roaming state stored
+ // in data network.
+ boolean isRoaming = isWwanInService ? mServiceState.getDataRoamingFromRegistration()
+ : dataNetwork.getLastKnownRoamingState();
int targetAccessNetwork = DataUtils.networkTypeToAccessNetworkType(
getDataNetworkType(DataUtils.getTargetTransport(dataNetwork.getTransport())));
NetworkCapabilities capabilities = dataNetwork.getNetworkCapabilities();
log("evaluateDataNetworkHandover: "
+ "source=" + AccessNetworkType.toString(sourceAccessNetwork)
+ ", target=" + AccessNetworkType.toString(targetAccessNetwork)
+ + ", roaming=" + isRoaming
+ ", ServiceState=" + mServiceState
+ ", capabilities=" + capabilities);
// Matching the rules by the configured order. Bail out if find first matching rule.
for (HandoverRule rule : handoverRules) {
- // Check if the rule is only for roaming and we are not roaming. Use the real
- // roaming state reported by modem instead of using the overridden roaming state.
- if (rule.isOnlyForRoaming && !mServiceState.getDataRoamingFromRegistration()) {
+ // Check if the rule is only for roaming and we are not roaming.
+ if (rule.isOnlyForRoaming && !isRoaming) {
// If the rule is for roaming only, and the device is not roaming, then bypass
// this rule.
continue;
@@ -3378,7 +3388,8 @@ public class DataNetworkController extends Handler {
if (oldPsNri == null
|| oldPsNri.getAccessNetworkTechnology() != newPsNri.getAccessNetworkTechnology()
- || (!oldPsNri.isInService() && newPsNri.isInService())) {
+ || (!oldPsNri.isInService() && newPsNri.isInService())
+ || (oldPsNri.isRoaming() && !newPsNri.isRoaming())) {
return true;
}
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index 893ec4174b..0878ccf48a 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -78,13 +78,6 @@ public class DataProfileManager extends Handler {
private final String mLogTag;
private final LocalLog mLocalLog = new LocalLog(128);
- /**
- * Should only be used by update updateDataProfiles() to indicate whether resend IA to modem
- * regardless whether IA changed.
- **/
- private final boolean FORCED_UPDATE_IA = true;
- private final boolean ONLY_UPDATE_IA_IF_CHANGED = false;
-
/** Data network controller. */
private final @NonNull DataNetworkController mDataNetworkController;
@@ -202,11 +195,12 @@ public class DataProfileManager extends Handler {
switch (msg.what) {
case EVENT_SIM_REFRESH:
log("Update data profiles due to SIM refresh.");
- updateDataProfiles(FORCED_UPDATE_IA);
+ updateDataProfiles(!mDataConfigManager.allowClearInitialAttachDataProfile()
+ /*force update IA*/);
break;
case EVENT_APN_DATABASE_CHANGED:
log("Update data profiles due to APN db updated.");
- updateDataProfiles(ONLY_UPDATE_IA_IF_CHANGED);
+ updateDataProfiles(false/*force update IA*/);
break;
default:
loge("Unexpected event " + msg);
@@ -219,9 +213,8 @@ public class DataProfileManager extends Handler {
*/
private void onCarrierConfigUpdated() {
log("Update data profiles due to carrier config updated.");
- updateDataProfiles(FORCED_UPDATE_IA);
-
- //TODO: more works needed to be done here.
+ updateDataProfiles(!mDataConfigManager.allowClearInitialAttachDataProfile()
+ /*force update IA*/);
}
/**
@@ -258,7 +251,8 @@ public class DataProfileManager extends Handler {
* Update all data profiles, including preferred data profile, and initial attach data profile.
* Also send those profiles down to the modem if needed.
*
- * @param forceUpdateIa If {@code true}, we should always send IA again to modem.
+ * @param forceUpdateIa If {@code true}, we should always send initial attach data profile again
+ * to modem.
*/
private void updateDataProfiles(boolean forceUpdateIa) {
List<DataProfile> profiles = new ArrayList<>();
@@ -444,7 +438,7 @@ public class DataProfileManager extends Handler {
if (defaultProfile == null || defaultProfile.equals(mPreferredDataProfile)) return;
// Save the preferred data profile into database.
setPreferredDataProfile(defaultProfile);
- updateDataProfiles(ONLY_UPDATE_IA_IF_CHANGED);
+ updateDataProfiles(false/*force update IA*/);
}
/**
@@ -567,7 +561,8 @@ public class DataProfileManager extends Handler {
* attach. In this case, exception can be configured through
* {@link CarrierConfigManager#KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY}.
*
- * @param forceUpdateIa If {@code true}, we should always send IA again to modem.
+ * @param forceUpdateIa If {@code true}, we should always send initial attach data profile again
+ * to modem.
*/
private void updateInitialAttachDataProfileAtModem(boolean forceUpdateIa) {
DataProfile initialAttachDataProfile = null;
@@ -589,9 +584,8 @@ public class DataProfileManager extends Handler {
mInitialAttachDataProfile = initialAttachDataProfile;
logl("Initial attach data profile updated as " + mInitialAttachDataProfile
+ " or forceUpdateIa= " + forceUpdateIa);
- // TODO: Push the null data profile to modem on new AIDL HAL. Modem should clear the IA
- // APN, tracking for U b/227579876, now using forceUpdateIa which always push to modem
- if (mInitialAttachDataProfile != null) {
+ if (mInitialAttachDataProfile != null || mDataConfigManager
+ .allowClearInitialAttachDataProfile()) {
mWwanDataServiceManager.setInitialAttachApn(mInitialAttachDataProfile,
mPhone.getServiceState().getDataRoamingFromRegistration(), null);
}
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java
index b6ad101693..314668973b 100644
--- a/src/java/com/android/internal/telephony/data/DataRetryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -1469,9 +1469,9 @@ public class DataRetryManager extends Handler {
} else {
Intent intent = new Intent(ACTION_RETRY);
intent.putExtra(ACTION_RETRY_EXTRA_HASHCODE, dataRetryEntry.hashCode());
- // No need to wake up the device at the exact time, the retry can wait util next time
- // the device wake up to save power.
- mAlarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
+ // No need to wake up the device, the retry can wait util next time the device wake up
+ // to save power.
+ mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
dataRetryEntry.retryElapsedTime,
PendingIntent.getBroadcast(mPhone.getContext(),
dataRetryEntry.hashCode() /*Unique identifier of this retry attempt*/,
diff --git a/src/java/com/android/internal/telephony/data/DataServiceManager.java b/src/java/com/android/internal/telephony/data/DataServiceManager.java
index 2733aff3ae..bb0e8c3cb2 100644
--- a/src/java/com/android/internal/telephony/data/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/data/DataServiceManager.java
@@ -809,11 +809,12 @@ public class DataServiceManager extends Handler {
* Set an APN to initial attach network.
*
* @param dataProfile Data profile used for data network setup. See {@link DataProfile}.
+ * {@code null} to clear any previous data profiles.
* @param isRoaming True if the device is data roaming.
* @param onCompleteMessage The result message for this request. Null if the client does not
* care about the result.
*/
- public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+ public void setInitialAttachApn(@Nullable DataProfile dataProfile, boolean isRoaming,
Message onCompleteMessage) {
if (DBG) log("setInitialAttachApn");
if (!mBound) {
diff --git a/src/java/com/android/internal/telephony/data/DataSettingsManager.java b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
index 5178ae4831..f8365bbef5 100644
--- a/src/java/com/android/internal/telephony/data/DataSettingsManager.java
+++ b/src/java/com/android/internal/telephony/data/DataSettingsManager.java
@@ -130,6 +130,14 @@ public class DataSettingsManager extends Handler {
}
/**
+ * Called when user data enabled state changed.
+ *
+ * @param enabled {@code true} indicates user mobile data is enabled.
+ * @param callingPackage The package that changed the data enabled state.
+ */
+ public void onUserDataEnabledChanged(boolean enabled, @NonNull String callingPackage) {}
+
+ /**
* Called when overall data enabled state changed.
*
* @param enabled {@code true} indicates mobile data is enabled.
@@ -289,6 +297,23 @@ public class DataSettingsManager extends Handler {
}
}
}, this::post);
+ // some overall mobile data override policy depend on whether DDS is user data enabled.
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (phone.getPhoneId() != mPhone.getPhoneId()) {
+ phone.getDataSettingsManager().registerCallback(new DataSettingsManagerCallback(
+ this::post) {
+ @Override
+ public void onUserDataEnabledChanged(boolean enabled,
+ @NonNull String callingPackage) {
+ log("phone" + phone.getPhoneId() + " onUserDataEnabledChanged "
+ + enabled + " by " + callingPackage
+ + ", reevaluating mobile data policies");
+ DataSettingsManager.this.updateDataEnabledAndNotify(
+ TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
+ }
+ });
+ }
+ }
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_UNKNOWN);
}
@@ -416,6 +441,8 @@ public class DataSettingsManager extends Handler {
if (changed) {
logl("UserDataEnabled changed to " + enabled);
mPhone.notifyUserMobileDataStateChanged(enabled);
+ mDataSettingsManagerCallbacks.forEach(callback -> callback.invokeFromExecutor(
+ () -> callback.onUserDataEnabledChanged(enabled, callingPackage)));
updateDataEnabledAndNotify(TelephonyManager.DATA_ENABLED_REASON_USER, callingPackage);
}
}
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index 6c6f06455d..375b2503a7 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -23,7 +23,10 @@ import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Intent;
+import android.database.ContentObserver;
import android.net.NetworkAgent;
+import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -34,6 +37,7 @@ import android.telephony.Annotation.ValidationStatus;
import android.telephony.CellSignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
@@ -127,12 +131,24 @@ public class DataStallRecoveryManager extends Handler {
/** The data stall recovered by user (Mobile Data Power off/on). */
private static final int RECOVERED_REASON_USER = 3;
+ /** The number of milliseconds to wait for the DSRM prediction to complete. */
+ private static final int DSRM_PREDICT_WAITING_MILLIS = 1000;
+
+ /** Event for send data stall broadcast. */
+ private static final int EVENT_SEND_DATA_STALL_BROADCAST = 1;
+
/** Event for triggering recovery action. */
private static final int EVENT_DO_RECOVERY = 2;
/** Event for radio state changed. */
private static final int EVENT_RADIO_STATE_CHANGED = 3;
+ /** Event for recovery actions changed. */
+ private static final int EVENT_CONTENT_DSRM_ENABLED_ACTIONS_CHANGED = 4;
+
+ /** Event for duration milliseconds changed. */
+ private static final int EVENT_CONTENT_DSRM_DURATION_MILLIS_CHANGED = 5;
+
private final @NonNull Phone mPhone;
private final @NonNull String mLogTag;
private final @NonNull LocalLog mLocalLog = new LocalLog(128);
@@ -185,8 +201,30 @@ public class DataStallRecoveryManager extends Handler {
/** The boolean array for the flags. They are used to skip the recovery actions if needed. */
private @NonNull boolean[] mSkipRecoveryActionArray;
+ /**
+ * The content URI for the DSRM recovery actions.
+ *
+ * @see Settings.Global#DSRM_ENABLED_ACTIONS
+ */
+ private static final Uri CONTENT_URL_DSRM_ENABLED_ACTIONS = Settings.Global.getUriFor(
+ Settings.Global.DSRM_ENABLED_ACTIONS);
+
+ /**
+ * The content URI for the DSRM duration milliseconds.
+ *
+ * @see Settings.Global#DSRM_DURATION_MILLIS
+ */
+ private static final Uri CONTENT_URL_DSRM_DURATION_MILLIS = Settings.Global.getUriFor(
+ Settings.Global.DSRM_DURATION_MILLIS);
+
+
private DataStallRecoveryManagerCallback mDataStallRecoveryManagerCallback;
+ private final DataStallRecoveryStats mStats;
+
+ /** The number of milliseconds to wait for the DSRM prediction to complete. */
+ private @ElapsedRealtimeLong long mPredictWaitingMillis = 0L;
+
/**
* The data stall recovery manager callback. Note this is only used for passing information
* internally in the data stack, should not be used externally.
@@ -248,6 +286,8 @@ public class DataStallRecoveryManager extends Handler {
updateDataStallRecoveryConfigs();
registerAllEvents();
+
+ mStats = new DataStallRecoveryStats(mPhone, dataNetworkController);
}
/** Register for all events that data stall monitor is interested. */
@@ -280,12 +320,33 @@ public class DataStallRecoveryManager extends Handler {
}
});
mPhone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
+
+ // Register for DSRM global setting changes.
+ mPhone.getContext().getContentResolver().registerContentObserver(
+ CONTENT_URL_DSRM_ENABLED_ACTIONS, false, mContentObserver);
+ mPhone.getContext().getContentResolver().registerContentObserver(
+ CONTENT_URL_DSRM_DURATION_MILLIS, false, mContentObserver);
+
}
@Override
public void handleMessage(Message msg) {
logv("handleMessage = " + msg);
switch (msg.what) {
+ case EVENT_SEND_DATA_STALL_BROADCAST:
+ mRecoveryTriggered = true;
+ // Verify that DSRM needs to process the recovery action
+ if (!isRecoveryNeeded(false)) {
+ cancelNetworkCheckTimer();
+ startNetworkCheckTimer(getRecoveryAction());
+ return;
+ }
+ // Broadcast intent that data stall has been detected.
+ broadcastDataStallDetected(getRecoveryAction());
+ // Schedule the message to to wait for the predicted value.
+ sendMessageDelayed(
+ obtainMessage(EVENT_DO_RECOVERY), mPredictWaitingMillis);
+ break;
case EVENT_DO_RECOVERY:
doRecovery();
break;
@@ -302,16 +363,119 @@ public class DataStallRecoveryManager extends Handler {
}
}
break;
+ case EVENT_CONTENT_DSRM_ENABLED_ACTIONS_CHANGED:
+ updateGlobalConfigActions();
+ break;
+ case EVENT_CONTENT_DSRM_DURATION_MILLIS_CHANGED:
+ updateGlobalConfigDurations();
+ break;
default:
loge("Unexpected message = " + msg);
break;
}
}
+ private final ContentObserver mContentObserver = new ContentObserver(this) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ if (CONTENT_URL_DSRM_ENABLED_ACTIONS.equals(uri)) {
+ log("onChange URI: " + uri);
+ sendMessage(obtainMessage(EVENT_CONTENT_DSRM_ENABLED_ACTIONS_CHANGED));
+ } else if (CONTENT_URL_DSRM_DURATION_MILLIS.equals(uri)) {
+ log("onChange URI: " + uri);
+ sendMessage(obtainMessage(EVENT_CONTENT_DSRM_DURATION_MILLIS_CHANGED));
+ }
+ }
+ };
+
+
+ @VisibleForTesting
+ public ContentObserver getContentObserver() {
+ return mContentObserver;
+ }
+
+ /**
+ * Updates the skip recovery action array based on DSRM global configuration actions.
+ *
+ * @see Settings.Global#DSRM_ENABLED_ACTIONS
+ */
+ private void updateGlobalConfigActions() {
+ String enabledActions = Settings.Global.getString(
+ mPhone.getContext().getContentResolver(),
+ Settings.Global.DSRM_ENABLED_ACTIONS);
+
+ if (!TextUtils.isEmpty(enabledActions)) {
+ String[] splitEnabledActions = enabledActions.split(",");
+ boolean[] enabledActionsArray = new boolean[splitEnabledActions.length];
+ for (int i = 0; i < enabledActionsArray.length; i++) {
+ enabledActionsArray[i] = Boolean.parseBoolean(splitEnabledActions[i].trim());
+ }
+
+ int minLength = Math.min(enabledActionsArray.length,
+ mSkipRecoveryActionArray.length);
+
+ // Update the skip recovery action array.
+ for (int i = 0; i < minLength; i++) {
+ mSkipRecoveryActionArray[i] = !enabledActionsArray[i];
+ }
+ log("SkipRecoveryAction: "
+ + Arrays.toString(mSkipRecoveryActionArray));
+ mPredictWaitingMillis = DSRM_PREDICT_WAITING_MILLIS;
+ } else {
+ mPredictWaitingMillis = 0;
+ log("Enabled actions is null");
+ }
+ }
+
+ /**
+ * Updates the duration millis array based on DSRM global configuration durations.
+ *
+ * @see Settings.Global#DSRM_DURATION_MILLIS
+ */
+ private void updateGlobalConfigDurations() {
+ String durationMillis = Settings.Global.getString(
+ mPhone.getContext().getContentResolver(),
+ Settings.Global.DSRM_DURATION_MILLIS);
+
+ if (!TextUtils.isEmpty(durationMillis)) {
+ String[] splitDurationMillis = durationMillis.split(",");
+ long[] durationMillisArray = new long[splitDurationMillis.length];
+ for (int i = 0; i < durationMillisArray.length; i++) {
+ try {
+ durationMillisArray[i] = Long.parseLong(splitDurationMillis[i].trim());
+ } catch (NumberFormatException e) {
+ mPredictWaitingMillis = 0;
+ loge("Parsing duration millis error");
+ return;
+ }
+ }
+
+ int minLength = Math.min(durationMillisArray.length,
+ mDataStallRecoveryDelayMillisArray.length);
+
+ // Copy the values from the durationMillisArray array to the
+ // mDataStallRecoveryDelayMillisArray array.
+ for (int i = 0; i < minLength; i++) {
+ mDataStallRecoveryDelayMillisArray[i] = durationMillisArray[i];
+ }
+ log("DataStallRecoveryDelayMillis: "
+ + Arrays.toString(mDataStallRecoveryDelayMillisArray));
+ mPredictWaitingMillis = DSRM_PREDICT_WAITING_MILLIS;
+ } else {
+ mPredictWaitingMillis = 0;
+ log("Duration millis is null");
+ }
+ }
+
/** Update the data stall recovery configs from DataConfigManager. */
private void updateDataStallRecoveryConfigs() {
mDataStallRecoveryDelayMillisArray = mDataConfigManager.getDataStallRecoveryDelayMillis();
mSkipRecoveryActionArray = mDataConfigManager.getDataStallRecoveryShouldSkipArray();
+
+ //Update Global settings
+ updateGlobalConfigActions();
+ updateGlobalConfigDurations();
}
/**
@@ -320,7 +484,8 @@ public class DataStallRecoveryManager extends Handler {
* @param recoveryAction The recovery action to query.
* @return the delay in milliseconds for the specific recovery action.
*/
- private long getDataStallRecoveryDelayMillis(@RecoveryAction int recoveryAction) {
+ @VisibleForTesting
+ public long getDataStallRecoveryDelayMillis(@RecoveryAction int recoveryAction) {
return mDataStallRecoveryDelayMillisArray[recoveryAction];
}
@@ -330,7 +495,8 @@ public class DataStallRecoveryManager extends Handler {
* @param recoveryAction The recovery action.
* @return {@code true} if the action needs to be skipped.
*/
- private boolean shouldSkipRecoveryAction(@RecoveryAction int recoveryAction) {
+ @VisibleForTesting
+ public boolean shouldSkipRecoveryAction(@RecoveryAction int recoveryAction) {
return mSkipRecoveryActionArray[recoveryAction];
}
@@ -385,7 +551,7 @@ public class DataStallRecoveryManager extends Handler {
mIsValidNetwork = false;
log("trigger data stall recovery");
mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
- sendMessage(obtainMessage(EVENT_DO_RECOVERY));
+ sendMessage(obtainMessage(EVENT_SEND_DATA_STALL_BROADCAST));
}
}
@@ -490,6 +656,22 @@ public class DataStallRecoveryManager extends Handler {
Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction);
+
+ // Get the information for DSRS state
+ 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();
+
+ // Get the bundled DSRS stats.
+ Bundle bundle = mStats.getDataStallRecoveryMetricsData(
+ recoveryAction, isRecovered, duration, reason, isFirstValidationOfAction,
+ durationOfAction);
+
+ // Put the bundled stats extras on the intent.
+ intent.putExtra("EXTRA_DSRS_STATS_BUNDLE", bundle);
+
mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
}
@@ -531,7 +713,8 @@ public class DataStallRecoveryManager extends Handler {
mNetworkCheckTimerStarted = true;
mTimeLastRecoveryStartMs = SystemClock.elapsedRealtime();
sendMessageDelayed(
- obtainMessage(EVENT_DO_RECOVERY), getDataStallRecoveryDelayMillis(action));
+ obtainMessage(EVENT_SEND_DATA_STALL_BROADCAST),
+ getDataStallRecoveryDelayMillis(action));
}
}
@@ -540,7 +723,7 @@ public class DataStallRecoveryManager extends Handler {
log("cancelNetworkCheckTimer()");
if (mNetworkCheckTimerStarted) {
mNetworkCheckTimerStarted = false;
- removeMessages(EVENT_DO_RECOVERY);
+ removeMessages(EVENT_SEND_DATA_STALL_BROADCAST);
}
}
@@ -645,8 +828,9 @@ public class DataStallRecoveryManager extends Handler {
&& !mIsAttemptedAllSteps)
|| mLastAction == RECOVERY_ACTION_RESET_MODEM)
? (int) getDurationOfCurrentRecoveryMs() : 0;
- DataStallRecoveryStats.onDataStallEvent(
- mLastAction, mPhone, isValid, timeDuration, reason,
+
+ mStats.uploadMetrics(
+ mLastAction, isValid, timeDuration, reason,
isFirstValidationAfterDoRecovery, timeDurationOfCurrentAction);
logl(
"data stall: "
@@ -696,22 +880,12 @@ public class DataStallRecoveryManager extends Handler {
private void doRecovery() {
@RecoveryAction final int recoveryAction = getRecoveryAction();
final int signalStrength = mPhone.getSignalStrength().getLevel();
- mRecoveryTriggered = true;
-
- // DSRM used sendMessageDelayed to process the next event EVENT_DO_RECOVERY, so it need
- // to check the condition if DSRM need to process the recovery action.
- if (!isRecoveryNeeded(false)) {
- cancelNetworkCheckTimer();
- startNetworkCheckTimer(recoveryAction);
- return;
- }
TelephonyMetrics.getInstance()
.writeSignalStrengthEvent(mPhone.getPhoneId(), signalStrength);
TelephonyMetrics.getInstance().writeDataStallEvent(mPhone.getPhoneId(), recoveryAction);
mLastAction = recoveryAction;
mLastActionReported = false;
- broadcastDataStallDetected(recoveryAction);
mNetworkCheckTimerStarted = false;
mTimeElapsedOfCurrentAction = SystemClock.elapsedRealtime();
@@ -873,6 +1047,7 @@ public class DataStallRecoveryManager extends Handler {
pw.println(
"mMobileDataChangedToEnabledDuringDataStall="
+ mMobileDataChangedToEnabledDuringDataStall);
+ pw.println("mPredictWaitingMillis=" + mPredictWaitingMillis);
pw.println(
"DataStallRecoveryDelayMillisArray="
+ Arrays.toString(mDataStallRecoveryDelayMillisArray));
diff --git a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
index 5ed12aadec..de8c48c74f 100644
--- a/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
+++ b/src/java/com/android/internal/telephony/data/LinkBandwidthEstimator.java
@@ -315,6 +315,8 @@ public class LinkBandwidthEstimator extends Handler {
registerNrStateFrequencyChange();
mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(AccessNetworkConstants
.TRANSPORT_TYPE_WWAN, this, MSG_DATA_REG_STATE_OR_RAT_CHANGED, null);
+ mPhone.getSignalStrengthController().registerForSignalStrengthChanged(this,
+ MSG_SIGNAL_STRENGTH_CHANGED, null);
}
@Override
@@ -333,7 +335,7 @@ public class LinkBandwidthEstimator extends Handler {
handleDefaultNetworkChanged((NetworkCapabilities) msg.obj);
break;
case MSG_SIGNAL_STRENGTH_CHANGED:
- handleSignalStrengthChanged((SignalStrength) msg.obj);
+ handleSignalStrengthChanged();
break;
case MSG_NR_FREQUENCY_CHANGED:
// fall through
@@ -917,10 +919,8 @@ public class LinkBandwidthEstimator extends Handler {
() -> callback.onBandwidthChanged(linkBandwidthTxKps, linkBandwidthRxKps)));
}
- private void handleSignalStrengthChanged(SignalStrength signalStrength) {
- if (signalStrength == null) {
- return;
- }
+ private void handleSignalStrengthChanged() {
+ SignalStrength signalStrength = mPhone.getSignalStrength();
mSignalStrengthDbm = signalStrength.getDbm();
mSignalLevel = signalStrength.getLevel();
@@ -1099,13 +1099,8 @@ public class LinkBandwidthEstimator extends Handler {
}
private class TelephonyCallbackImpl extends TelephonyCallback implements
- TelephonyCallback.SignalStrengthsListener,
TelephonyCallback.ActiveDataSubscriptionIdListener {
@Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, signalStrength).sendToTarget();
- }
- @Override
public void onActiveDataSubscriptionIdChanged(int subId) {
obtainMessage(MSG_ACTIVE_PHONE_CHANGED, subId).sendToTarget();
}
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index d2fdf4abb9..13ccadcabe 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.data;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG;
+import static android.telephony.SubscriptionManager.DEFAULT_PHONE_INDEX;
import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -32,9 +33,6 @@ import static java.util.Arrays.copyOf;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -50,7 +48,6 @@ import android.net.NetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.os.AsyncResult;
import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -58,10 +55,7 @@ import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.RemoteException;
-import android.provider.Settings;
-import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
-import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionInfo;
@@ -79,6 +73,7 @@ import android.util.Log;
import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Call;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.ISetOpportunisticDataCallback;
import com.android.internal.telephony.IccCard;
@@ -96,7 +91,6 @@ import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDeman
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.telephony.subscription.SubscriptionManagerService.WatchedInt;
-import com.android.internal.telephony.util.NotificationChannelController;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -121,21 +115,6 @@ public class PhoneSwitcher extends Handler {
private static final String LOG_TAG = "PhoneSwitcher";
protected static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
- /** Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS} */
- private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
- /**
- * When starting this activity, this extra can also be specified to supply a Bundle of arguments
- * to pass to that fragment when it is instantiated during the initial creation of the activity.
- */
- private static final String SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS =
- ":settings:show_fragment_args";
- /** The res Id of the auto data switch fragment in settings. **/
- private static final String AUTO_DATA_SWITCH_SETTING_R_ID = "auto_data_switch";
- /** Notification tag **/
- private static final String AUTO_DATA_SWITCH_NOTIFICATION_TAG = "auto_data_switch";
- /** Notification ID **/
- private static final int AUTO_DATA_SWITCH_NOTIFICATION_ID = 1;
-
private static final int MODEM_COMMAND_RETRY_PERIOD_MS = 5000;
// After the emergency call ends, wait for a few seconds to see if we enter ECBM before starting
// the countdown to remove the emergency DDS override.
@@ -257,9 +236,6 @@ public class PhoneSwitcher extends Handler {
// its value will be DEFAULT_SUBSCRIPTION_ID.
private int mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
- /** The count of consecutive auto switch validation failure **/
- private int mAutoSwitchRetryFailedCount = 0;
-
// The phone ID that has an active voice call. If set, and its mobile data setting is on,
// it will become the mPreferredDataPhoneId.
protected int mPhoneIdInVoiceCall = SubscriptionManager.INVALID_PHONE_INDEX;
@@ -302,10 +278,10 @@ public class PhoneSwitcher extends Handler {
// ECBM, which is detected by EVENT_EMERGENCY_TOGGLE.
private static final int EVENT_PRECISE_CALL_STATE_CHANGED = 109;
private static final int EVENT_NETWORK_VALIDATION_DONE = 110;
- private static final int EVENT_EVALUATE_AUTO_SWITCH = 111;
+
private static final int EVENT_MODEM_COMMAND_DONE = 112;
private static final int EVENT_MODEM_COMMAND_RETRY = 113;
- private static final int EVENT_SERVICE_STATE_CHANGED = 114;
+
// An emergency call is about to be originated and requires the DDS to be overridden.
// Uses EVENT_PRECISE_CALL_STATE_CHANGED message to start countdown to finish override defined
// in mEmergencyOverride. If EVENT_PRECISE_CALL_STATE_CHANGED does not come in
@@ -318,7 +294,6 @@ public class PhoneSwitcher extends Handler {
private static final int EVENT_NETWORK_AVAILABLE = 118;
private static final int EVENT_PROCESS_SIM_STATE_CHANGE = 119;
private static final int EVENT_IMS_RADIO_TECH_CHANGED = 120;
- private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 121;
// List of events triggers re-evaluations
private static final String EVALUATION_REASON_RADIO_ON = "EVENT_RADIO_ON";
@@ -343,23 +318,9 @@ public class PhoneSwitcher extends Handler {
private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
- /**
- * {@code true} if requires ping test before switching preferred data modem; otherwise, switch
- * even if ping test fails.
- */
- private boolean mRequirePingTestBeforeDataSwitch = true;
-
- /**
- * Time threshold in ms to define a internet connection status to be stable(e.g. out of service,
- * in service, wifi is the default active network.etc), while -1 indicates auto switch
- * feature disabled.
- */
- private long mAutoDataSwitchAvailabilityStabilityTimeThreshold = -1;
-
- /**
- * The maximum number of retries when a validation for switching failed.
- */
- private int mAutoDataSwitchValidationMaxRetry;
+ private AutoDataSwitchController mAutoDataSwitchController;
+ private AutoDataSwitchController.AutoDataSwitchControllerCallback
+ mAutoDataSwitchCallback;
/** Data settings manager callback. Key is the phone id. */
private final @NonNull Map<Integer, DataSettingsManagerCallback> mDataSettingsManagerCallbacks =
@@ -368,12 +329,10 @@ public class PhoneSwitcher extends Handler {
private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
public int mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
- public boolean isDefaultNetworkOnCellular = false;
@Override
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {
if (networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
- isDefaultNetworkOnCellular = true;
if (SubscriptionManager.isValidSubscriptionId(mExpectedSubId)
&& mExpectedSubId == getSubIdFromNetworkSpecifier(
networkCapabilities.getNetworkSpecifier())) {
@@ -384,22 +343,13 @@ public class PhoneSwitcher extends Handler {
mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mSwitchReason = TelephonyEvent.DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
}
- } else {
- if (isDefaultNetworkOnCellular) {
- // non-cellular transport is active
- isDefaultNetworkOnCellular = false;
- log("default network is active on non cellular");
- evaluateIfAutoSwitchIsNeeded();
- }
}
+ mAutoDataSwitchController.updateDefaultNetworkCapabilities(networkCapabilities);
}
@Override
public void onLost(Network network) {
- // try find an active sub to switch to
- if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
- sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
- }
+ mAutoDataSwitchController.updateDefaultNetworkCapabilities(null);
}
}
@@ -551,8 +501,6 @@ public class PhoneSwitcher extends Handler {
}});
phone.getDataSettingsManager().registerCallback(
mDataSettingsManagerCallbacks.get(phoneId));
- phone.getServiceStateTracker().registerForServiceStateChanged(this,
- EVENT_SERVICE_STATE_CHANGED, phoneId);
registerForImsRadioTechChange(context, phoneId);
}
Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
@@ -563,8 +511,6 @@ public class PhoneSwitcher extends Handler {
PhoneFactory.getPhone(0).mCi.registerForOn(this, EVENT_RADIO_ON, null);
}
- readDeviceResourceConfig();
-
TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
context.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
telephonyRegistryManager.addOnSubscriptionsChangedListener(
@@ -573,6 +519,36 @@ public class PhoneSwitcher extends Handler {
mConnectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mAutoDataSwitchCallback = new AutoDataSwitchController.AutoDataSwitchControllerCallback() {
+ @Override
+ public void onRequireValidation(int targetPhoneId, boolean needValidation) {
+ int targetSubId = targetPhoneId == DEFAULT_PHONE_INDEX
+ ? DEFAULT_SUBSCRIPTION_ID
+ : mSubscriptionManagerService.getSubId(targetPhoneId);
+ PhoneSwitcher.this.validate(targetSubId, needValidation,
+ DataSwitch.Reason.DATA_SWITCH_REASON_AUTO, null);
+ }
+
+ @Override
+ public void onRequireImmediatelySwitchToPhone(int targetPhoneId,
+ @AutoDataSwitchController.AutoDataSwitchEvaluationReason int reason) {
+ PhoneSwitcher.this.mAutoSelectedDataSubId =
+ targetPhoneId == DEFAULT_PHONE_INDEX
+ ? DEFAULT_SUBSCRIPTION_ID
+ : mSubscriptionManagerService.getSubId(targetPhoneId);
+ PhoneSwitcher.this.evaluateIfImmediateDataSwitchIsNeeded(
+ AutoDataSwitchController.evaluationReasonToString(reason),
+ DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
+ }
+
+ @Override
+ public void onRequireCancelAnyPendingAutoSwitchValidation() {
+ PhoneSwitcher.this.cancelPendingAutoDataSwitchValidation();
+ }
+ };
+ mAutoDataSwitchController = new AutoDataSwitchController(context, looper, this,
+ mAutoDataSwitchCallback);
+
mContext.registerReceiver(mDefaultDataChangedReceiver,
new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED));
@@ -675,19 +651,6 @@ public class PhoneSwitcher extends Handler {
onEvaluate(REQUESTS_UNCHANGED, "subscription changed");
break;
}
- case EVENT_SERVICE_STATE_CHANGED: {
- AsyncResult ar = (AsyncResult) msg.obj;
- final int phoneId = (int) ar.userObj;
- onServiceStateChanged(phoneId);
- break;
- }
- case EVENT_MEETS_AUTO_DATA_SWITCH_STATE: {
- final int targetSubId = msg.arg1;
- final boolean needValidation = (boolean) msg.obj;
- validate(targetSubId, needValidation,
- DataSwitch.Reason.DATA_SWITCH_REASON_AUTO, null);
- break;
- }
case EVENT_PRIMARY_DATA_SUB_CHANGED: {
evaluateIfImmediateDataSwitchIsNeeded("primary data sub changed",
DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
@@ -718,9 +681,6 @@ public class PhoneSwitcher extends Handler {
onEvaluate(REQUESTS_CHANGED, "emergencyToggle");
break;
}
- case EVENT_EVALUATE_AUTO_SWITCH:
- evaluateIfAutoSwitchIsNeeded();
- break;
case EVENT_RADIO_CAPABILITY_CHANGED: {
final int phoneId = msg.arg1;
sendRilCommands(phoneId);
@@ -792,7 +752,8 @@ public class PhoneSwitcher extends Handler {
DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
if (!isAnyVoiceCallActiveOnDevice()) {
// consider auto switch on hang up all voice call
- evaluateIfAutoSwitchIsNeeded();
+ mAutoDataSwitchController.evaluateAutoDataSwitch(
+ AutoDataSwitchController.EVALUATION_REASON_VOICE_CALL_END);
}
break;
}
@@ -892,33 +853,19 @@ public class PhoneSwitcher extends Handler {
} else if (TelephonyManager.SIM_STATE_LOADED == simState) {
if (mCurrentDdsSwitchFailure.get(slotIndex).contains(
CommandException.Error.INVALID_SIM_STATE)
- && (TelephonyManager.SIM_STATE_LOADED == simState)
&& isSimApplicationReady(slotIndex)) {
sendRilCommands(slotIndex);
}
// SIM loaded after subscriptions slot mapping are done. Evaluate for auto
// data switch.
- sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+ mAutoDataSwitchController.evaluateAutoDataSwitch(
+ AutoDataSwitchController.EVALUATION_REASON_SIM_LOADED);
}
break;
}
}
}
- /**
- * Read the default device config from any default phone because the resource config are per
- * device. No need to register callback for the same reason.
- */
- private void readDeviceResourceConfig() {
- Phone phone = PhoneFactory.getDefaultPhone();
- DataConfigManager dataConfig = phone.getDataNetworkController().getDataConfigManager();
- mRequirePingTestBeforeDataSwitch = dataConfig.isPingTestBeforeAutoDataSwitchRequired();
- mAutoDataSwitchAvailabilityStabilityTimeThreshold =
- dataConfig.getAutoDataSwitchAvailabilityStabilityTimeThreshold();
- mAutoDataSwitchValidationMaxRetry =
- dataConfig.getAutoDataSwitchValidationMaxRetry();
- }
-
private synchronized void onMultiSimConfigChanged(int activeModemCount) {
// No change.
if (mActiveModemCount == activeModemCount) return;
@@ -958,13 +905,13 @@ public class PhoneSwitcher extends Handler {
});
phone.getDataSettingsManager().registerCallback(
mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
- phone.getServiceStateTracker().registerForServiceStateChanged(this,
- EVENT_SERVICE_STATE_CHANGED, phoneId);
Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
mCurrentDdsSwitchFailure.add(ddsFailure);
registerForImsRadioTechChange(mContext, phoneId);
}
+
+ mAutoDataSwitchController.onMultiSimConfigChanged(activeModemCount);
}
/**
@@ -973,14 +920,14 @@ public class PhoneSwitcher extends Handler {
* 2. OR user changed auto data switch feature
*/
private void onDataEnabledChanged() {
- logl("user changed data related settings");
if (isAnyVoiceCallActiveOnDevice()) {
// user changed data related settings during call, switch or turn off immediately
evaluateIfImmediateDataSwitchIsNeeded(
"user changed data settings during call",
DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
} else {
- evaluateIfAutoSwitchIsNeeded();
+ mAutoDataSwitchController.evaluateAutoDataSwitch(AutoDataSwitchController
+ .EVALUATION_REASON_DATA_SETTINGS_CHANGED);
}
}
@@ -1069,134 +1016,9 @@ public class PhoneSwitcher extends Handler {
}
/**
- * Called when service state changed.
- */
- private void onServiceStateChanged(int phoneId) {
- Phone phone = findPhoneById(phoneId);
- if (phone != null) {
- int newRegState = phone.getServiceState()
- .getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .getRegistrationState();
- if (newRegState != mPhoneStates[phoneId].dataRegState) {
- mPhoneStates[phoneId].dataRegState = newRegState;
- logl("onServiceStateChanged: phoneId:" + phoneId + " dataReg-> "
- + NetworkRegistrationInfo.registrationStateToString(newRegState));
- if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
- sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
- }
- }
- }
- }
-
- /**
- * Evaluate if auto switch is suitable at the moment.
- */
- private void evaluateIfAutoSwitchIsNeeded() {
- // auto data switch feature is disabled from server
- if (mAutoDataSwitchAvailabilityStabilityTimeThreshold < 0) return;
- // check is valid DSDS
- if (!isActiveSubId(mPrimaryDataSubId) || mSubscriptionManagerService
- .getActiveSubIdList(true).length <= 1) {
- return;
- }
-
- Phone primaryDataPhone = getPhoneBySubId(mPrimaryDataSubId);
- if (primaryDataPhone == null) {
- loge("evaluateIfAutoSwitchIsNeeded: cannot find primary data phone. subId="
- + mPrimaryDataSubId);
- return;
- }
-
- int primaryPhoneId = primaryDataPhone.getPhoneId();
- log("evaluateIfAutoSwitchIsNeeded: primaryPhoneId: " + primaryPhoneId
- + " preferredPhoneId: " + mPreferredDataPhoneId);
- Phone secondaryDataPhone;
-
- if (mPreferredDataPhoneId == primaryPhoneId) {
- // on primary data sub
-
- int candidateSubId = getAutoSwitchTargetSubIdIfExists();
- if (candidateSubId != INVALID_SUBSCRIPTION_ID) {
- startAutoDataSwitchStabilityCheck(candidateSubId, mRequirePingTestBeforeDataSwitch);
- } else {
- cancelPendingAutoDataSwitch();
- }
- } else if ((secondaryDataPhone = findPhoneById(mPreferredDataPhoneId)) != null) {
- // on secondary data sub
-
- if (!primaryDataPhone.isUserDataEnabled()
- || !secondaryDataPhone.isDataAllowed()) {
- // immediately switch back if user setting changes
- mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID;
- evaluateIfImmediateDataSwitchIsNeeded("User disabled data settings",
- DataSwitch.Reason.DATA_SWITCH_REASON_MANUAL);
- return;
- }
-
- NetworkCapabilities defaultNetworkCapabilities = mConnectivityManager
- .getNetworkCapabilities(mConnectivityManager.getActiveNetwork());
- if (defaultNetworkCapabilities != null && !defaultNetworkCapabilities
- .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
- log("evaluateIfAutoSwitchIsNeeded: "
- + "Default network is active on non-cellular transport");
- startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, false);
- return;
- }
-
- if (mPhoneStates[secondaryDataPhone.getPhoneId()].dataRegState
- != NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
- // secondary phone lost its HOME availability
- startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID, false);
- return;
- }
-
- if (isInService(mPhoneStates[primaryPhoneId])) {
- // primary becomes available
- startAutoDataSwitchStabilityCheck(DEFAULT_SUBSCRIPTION_ID,
- mRequirePingTestBeforeDataSwitch);
- return;
- }
-
- // cancel any previous attempts of switching back to primary
- cancelPendingAutoDataSwitch();
- }
- }
-
- /**
- * @param phoneState The phone state to check
- * @return {@code true} if the phone state is considered in service.
- */
- private boolean isInService(@NonNull PhoneState phoneState) {
- return phoneState.dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME
- || phoneState.dataRegState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
- }
-
- /**
- * 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.
- * @param targetSubId the target sub Id.
- * @param needValidation {@code true} if validation is needed.
- */
- private void startAutoDataSwitchStabilityCheck(int targetSubId, boolean needValidation) {
- log("startAutoDataSwitchStabilityCheck: targetSubId=" + targetSubId
- + " needValidation=" + needValidation);
- if (!hasMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, needValidation)) {
- sendMessageDelayed(obtainMessage(EVENT_MEETS_AUTO_DATA_SWITCH_STATE, targetSubId,
- 0/*placeholder*/,
- needValidation),
- mAutoDataSwitchAvailabilityStabilityTimeThreshold);
- }
- }
-
- /**
* Cancel any auto switch attempts when the current environment is not suitable for auto switch.
*/
- private void cancelPendingAutoDataSwitch() {
- mAutoSwitchRetryFailedCount = 0;
- removeMessages(EVENT_MEETS_AUTO_DATA_SWITCH_STATE);
+ private void cancelPendingAutoDataSwitchValidation() {
if (mValidator.isValidating()) {
mValidator.stopValidation();
@@ -1207,56 +1029,6 @@ public class PhoneSwitcher extends Handler {
}
}
- /**
- * Called when consider switching from primary default data sub to another data sub.
- * @return the target subId if a suitable candidate is found, otherwise return
- * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
- */
- private int getAutoSwitchTargetSubIdIfExists() {
- Phone primaryDataPhone = getPhoneBySubId(mPrimaryDataSubId);
- if (primaryDataPhone == null) {
- log("getAutoSwitchTargetSubId: no sim loaded");
- return INVALID_SUBSCRIPTION_ID;
- }
-
- int primaryPhoneId = primaryDataPhone.getPhoneId();
-
- if (!primaryDataPhone.isUserDataEnabled()) {
- log("getAutoSwitchTargetSubId: user disabled data");
- return INVALID_SUBSCRIPTION_ID;
- }
-
- NetworkCapabilities defaultNetworkCapabilities = mConnectivityManager
- .getNetworkCapabilities(mConnectivityManager.getActiveNetwork());
- if (defaultNetworkCapabilities != null && !defaultNetworkCapabilities
- .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
- // Exists other active default transport
- log("getAutoSwitchTargetSubId: Default network is active on non-cellular transport");
- return INVALID_SUBSCRIPTION_ID;
- }
-
- // check whether primary and secondary signal status worth switching
- if (isInService(mPhoneStates[primaryPhoneId])) {
- log("getAutoSwitchTargetSubId: primary is in service");
- return INVALID_SUBSCRIPTION_ID;
- }
- for (int phoneId = 0; phoneId < mPhoneStates.length; phoneId++) {
- if (phoneId != primaryPhoneId) {
- // the alternative phone must have HOME availability
- if (mPhoneStates[phoneId].dataRegState
- == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
- log("getAutoSwitchTargetSubId: found phone " + phoneId + " in HOME service");
- Phone secondaryDataPhone = findPhoneById(phoneId);
- if (secondaryDataPhone != null && // check auto switch feature enabled
- secondaryDataPhone.isDataAllowed()) {
- return secondaryDataPhone.getSubId();
- }
- }
- }
- }
- return INVALID_SUBSCRIPTION_ID;
- }
-
private TelephonyManager getTm() {
return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
@@ -1415,8 +1187,6 @@ public class PhoneSwitcher extends Handler {
protected static class PhoneState {
public volatile boolean active = false;
- public @NetworkRegistrationInfo.RegistrationState int dataRegState =
- NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
public long lastRequested = 0;
}
@@ -1733,18 +1503,22 @@ public class PhoneSwitcher extends Handler {
*/
private void validate(int subId, boolean needValidation, int switchReason,
@Nullable ISetOpportunisticDataCallback callback) {
- logl("Validate subId " + subId + " due to " + switchReasonToString(switchReason)
- + " needValidation=" + needValidation);
int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
? mPrimaryDataSubId : subId;
+ logl("Validate subId " + subId + " due to " + switchReasonToString(switchReason)
+ + " needValidation=" + needValidation + " subIdToValidate=" + subIdToValidate
+ + " mAutoSelectedDataSubId=" + mAutoSelectedDataSubId
+ + " mPreferredDataSubId=" + mPreferredDataSubId.get());
if (!isActiveSubId(subIdToValidate)) {
logl("Can't switch data to inactive subId " + subIdToValidate);
if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
// the default data sub is not selected yet, store the intent of switching to
// default subId once it becomes available.
mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+ sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
+ } else {
+ sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
}
- sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
return;
}
@@ -1836,14 +1610,12 @@ public class PhoneSwitcher extends Handler {
if (!isActiveSubId(subId)) {
logl("confirmSwitch: subId " + subId + " is no longer active");
resultForCallBack = SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION;
- mAutoSwitchRetryFailedCount = 0;
} else if (!confirm) {
resultForCallBack = SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
// retry for auto data switch validation failure
if (mLastSwitchPreferredDataReason == DataSwitch.Reason.DATA_SWITCH_REASON_AUTO) {
- scheduleAutoSwitchRetryEvaluation();
- mAutoSwitchRetryFailedCount++;
+ mAutoDataSwitchController.evaluateRetryOnValidationFailed();
}
} else {
if (subId == mPrimaryDataSubId) {
@@ -1852,7 +1624,7 @@ public class PhoneSwitcher extends Handler {
setAutoSelectedDataSubIdInternal(subId);
}
resultForCallBack = SET_OPPORTUNISTIC_SUB_SUCCESS;
- mAutoSwitchRetryFailedCount = 0;
+ mAutoDataSwitchController.resetFailedCount();
}
// Trigger callback if needed
@@ -1861,23 +1633,6 @@ public class PhoneSwitcher extends Handler {
mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
}
- /**
- * Schedule auto data switch evaluation retry if haven't reached the max retry count.
- */
- private void scheduleAutoSwitchRetryEvaluation() {
- if (mAutoSwitchRetryFailedCount < mAutoDataSwitchValidationMaxRetry) {
- if (!hasMessages(EVENT_EVALUATE_AUTO_SWITCH)) {
- sendMessageDelayed(obtainMessage(EVENT_EVALUATE_AUTO_SWITCH),
- mAutoDataSwitchAvailabilityStabilityTimeThreshold
- << mAutoSwitchRetryFailedCount);
- }
- } else {
- logl("scheduleAutoSwitchEvaluation: reached max auto switch retry count "
- + mAutoDataSwitchValidationMaxRetry);
- mAutoSwitchRetryFailedCount = 0;
- }
- }
-
private void onNetworkAvailable(int subId, Network network) {
log("onNetworkAvailable: on subId " + subId);
// Do nothing unless pending switch matches target subId and it doesn't require
@@ -1928,8 +1683,14 @@ public class PhoneSwitcher extends Handler {
}
// A phone in voice call might trigger data being switched to it.
+ // Exclude dialing to give modem time to process an EMC first before dealing with DDS switch
+ // Include alerting because modem RLF leads to delay in switch, so carrier required to
+ // switch in alerting phase.
+ // TODO: check ringing call for vDADA
return (!phone.getBackgroundCall().isIdle()
- || !phone.getForegroundCall().isIdle());
+ && phone.getBackgroundCall().getState() != Call.State.DIALING)
+ || (!phone.getForegroundCall().isIdle()
+ && phone.getForegroundCall().getState() != Call.State.DIALING);
}
private void updateHalCommandToUse() {
@@ -2062,8 +1823,7 @@ public class PhoneSwitcher extends Handler {
for (int i = 0; i < mActiveModemCount; i++) {
PhoneState ps = mPhoneStates[i];
c.setTimeInMillis(ps.lastRequested);
- pw.println("PhoneId(" + i + ") active=" + ps.active + ", dataRegState="
- + NetworkRegistrationInfo.registrationStateToString(ps.dataRegState)
+ pw.println("PhoneId(" + i + ") active=" + ps.active
+ ", lastRequest="
+ (ps.lastRequested == 0 ? "never" :
String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)));
@@ -2081,10 +1841,6 @@ public class PhoneSwitcher extends Handler {
pw.println("mActiveModemCount=" + mActiveModemCount);
pw.println("mPhoneIdInVoiceCall=" + mPhoneIdInVoiceCall);
pw.println("mCurrentDdsSwitchFailure=" + mCurrentDdsSwitchFailure);
- pw.println("mAutoDataSwitchAvailabilityStabilityTimeThreshold="
- + mAutoDataSwitchAvailabilityStabilityTimeThreshold);
- pw.println("mAutoDataSwitchValidationMaxRetry=" + mAutoDataSwitchValidationMaxRetry);
- pw.println("mRequirePingTestBeforeDataSwitch=" + mRequirePingTestBeforeDataSwitch);
pw.println("mLastSwitchPreferredDataReason="
+ switchReasonToString(mLastSwitchPreferredDataReason));
pw.println("mDisplayedAutoSwitchNotification=" + mDisplayedAutoSwitchNotification);
@@ -2092,6 +1848,7 @@ public class PhoneSwitcher extends Handler {
pw.increaseIndent();
mLocalLog.dump(fd, pw, args);
pw.decreaseIndent();
+ mAutoDataSwitchController.dump(fd, pw, args);
pw.decreaseIndent();
}
@@ -2130,72 +1887,15 @@ public class PhoneSwitcher extends Handler {
phoneId), MODEM_COMMAND_RETRY_PERIOD_MS);
return;
}
- if (commandSuccess) logl("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId);
+ if (commandSuccess) {
+ logl("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId);
+ mAutoDataSwitchController.displayAutoDataSwitchNotification(phoneId,
+ mLastSwitchPreferredDataReason == DataSwitch.Reason.DATA_SWITCH_REASON_AUTO);
+ }
mCurrentDdsSwitchFailure.get(phoneId).clear();
// Notify all registrants
mActivePhoneRegistrants.notifyRegistrants();
notifyPreferredDataSubIdChanged();
- displayAutoDataSwitchNotification();
- }
-
- /**
- * Display a notification the first time auto data switch occurs.
- */
- private void displayAutoDataSwitchNotification() {
- NotificationManager notificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (mDisplayedAutoSwitchNotification) {
- // cancel posted notification if any exist
- log("displayAutoDataSwitchNotification: canceling any notifications for subId "
- + mAutoSelectedDataSubId);
- notificationManager.cancel(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
- AUTO_DATA_SWITCH_NOTIFICATION_ID);
- return;
- }
- // proceed only the first time auto data switch occurs, which includes data during call
- if (mLastSwitchPreferredDataReason != DataSwitch.Reason.DATA_SWITCH_REASON_AUTO) {
- log("displayAutoDataSwitchNotification: Ignore DDS switch due to "
- + switchReasonToString(mLastSwitchPreferredDataReason));
- return;
- }
- SubscriptionInfo subInfo = mSubscriptionManagerService
- .getSubscriptionInfo(mAutoSelectedDataSubId);
- if (subInfo == null || subInfo.isOpportunistic()) {
- loge("displayAutoDataSwitchNotification: mAutoSelectedDataSubId="
- + mAutoSelectedDataSubId + " unexpected subInfo " + subInfo);
- return;
- }
- logl("displayAutoDataSwitchNotification: display for subId=" + mAutoSelectedDataSubId);
- // "Mobile network settings" screen / dialog
- Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
- final Bundle fragmentArgs = new Bundle();
- // Special contract for Settings to highlight permission row
- fragmentArgs.putString(SETTINGS_EXTRA_FRAGMENT_ARG_KEY, AUTO_DATA_SWITCH_SETTING_R_ID);
- intent.putExtra(Settings.EXTRA_SUB_ID, mAutoSelectedDataSubId);
- intent.putExtra(SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
- PendingIntent contentIntent = PendingIntent.getActivity(
- mContext, mAutoSelectedDataSubId, intent, PendingIntent.FLAG_IMMUTABLE);
-
- CharSequence activeCarrierName = subInfo.getDisplayName();
- CharSequence contentTitle = mContext.getString(
- com.android.internal.R.string.auto_data_switch_title, activeCarrierName);
- CharSequence contentText = mContext.getText(
- com.android.internal.R.string.auto_data_switch_content);
-
- final Notification notif = new Notification.Builder(mContext)
- .setContentTitle(contentTitle)
- .setContentText(contentText)
- .setSmallIcon(android.R.drawable.stat_sys_warning)
- .setColor(mContext.getResources().getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
- .setContentIntent(contentIntent)
- .setStyle(new Notification.BigTextStyle().bigText(contentText))
- .build();
- notificationManager.notify(AUTO_DATA_SWITCH_NOTIFICATION_TAG,
- AUTO_DATA_SWITCH_NOTIFICATION_ID, notif);
- mDisplayedAutoSwitchNotification = true;
}
private boolean isPhoneIdValidForRetry(int phoneId) {
diff --git a/src/java/com/android/internal/telephony/domainselection/OWNERS b/src/java/com/android/internal/telephony/domainselection/OWNERS
new file mode 100644
index 0000000000..2a7677001b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/OWNERS
@@ -0,0 +1,9 @@
+# automatically inherit owners from fw/opt/telephony
+
+hwangoo@google.com
+forestchoi@google.com
+avinashmp@google.com
+mkoon@google.com
+seheele@google.com
+radhikaagrawal@google.com
+jdyou@google.com
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 9b44001785..e2418c5acd 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -935,6 +935,23 @@ public class EmergencyNumberTracker extends Handler {
}
/**
+ * Get a list of the {@link EmergencyNumber}s that have the corresponding emergency number.
+ * Note: {@link #getEmergencyNumber(String)} assumes there is ONLY one record for a phone number
+ * when in reality there CAN be multiple instances if the same number is reported by the radio
+ * for a specific mcc and the emergency number database specifies the number without an mcc
+ * specified.
+ *
+ * @param emergencyNumber the emergency number to find.
+ * @return the list of emergency numbers matching.
+ */
+ public List<EmergencyNumber> getEmergencyNumbers(String emergencyNumber) {
+ final String toFind = PhoneNumberUtils.stripSeparators(emergencyNumber);
+ return getEmergencyNumberList().stream()
+ .filter(num -> num.getNumber().equals(toFind))
+ .toList();
+ }
+
+ /**
* Get the emergency service categories for the corresponding emergency number. The only
* trusted sources for the categories are the
* {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} and
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index a5b95c35bf..c8f3368d07 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -191,6 +191,19 @@ public class EuiccController extends IEuiccController.Stub {
boolean usePortIndex = resolutionIntent.getBooleanExtra(
EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, false);
resolutionExtras.putBoolean(EuiccService.EXTRA_RESOLUTION_USE_PORT_INDEX, usePortIndex);
+
+ if (!EuiccService.ACTION_RESOLVE_NO_PRIVILEGES.equals(resolutionIntent.getAction())
+ || !resolutionExtras.containsKey(EuiccService.EXTRA_RESOLUTION_PORT_INDEX)) {
+ // Port index resolution is requested only through the ACTION_RESOLVE_NO_PRIVILEGES
+ // action. Therefore, if the action is not ACTION_RESOLVE_NO_PRIVILEGES, use the
+ // port index from the resolution intent.
+ // (OR) If the action is ACTION_RESOLVE_NO_PRIVILEGES and resolutionExtras does not
+ // contain the EXTRA_RESOLUTION_PORT_INDEX key, retrieve the port index from
+ // resolutionIntent.
+ resolutionExtras.putInt(EuiccService.EXTRA_RESOLUTION_PORT_INDEX,
+ resolutionIntent.getIntExtra(EuiccService.EXTRA_RESOLUTION_PORT_INDEX,
+ TelephonyManager.DEFAULT_PORT_INDEX));
+ }
Log.i(TAG, " continueOperation portIndex: " + resolutionExtras.getInt(
EuiccService.EXTRA_RESOLUTION_PORT_INDEX) + " usePortIndex: " + usePortIndex);
op.continueOperation(cardId, resolutionExtras, callbackIntent);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 4b150cc533..3ab9fd7872 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -139,6 +139,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import java.util.stream.Stream;
/**
* {@hide}
@@ -2539,10 +2540,6 @@ public class ImsPhone extends ImsPhoneBase {
/** Sets the IMS phone number from IMS associated URIs, if any found. */
@VisibleForTesting
public void setPhoneNumberForSourceIms(Uri[] uris) {
- String phoneNumber = extractPhoneNumberFromAssociatedUris(uris);
- if (phoneNumber == null) {
- return;
- }
int subId = getSubId();
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
// Defending b/219080264:
@@ -2551,16 +2548,33 @@ public class ImsPhone extends ImsPhoneBase {
// IMS callbacks are sent back to telephony after SIM state changed.
return;
}
-
SubscriptionInfoInternal subInfo = mSubscriptionManagerService
.getSubscriptionInfoInternal(subId);
- if (subInfo != null) {
- phoneNumber = PhoneNumberUtils.formatNumberToE164(phoneNumber,
- subInfo.getCountryIso());
+ if (subInfo == null) {
+ loge("trigger setPhoneNumberForSourceIms, but subInfo is null");
+ return;
+ }
+ String subCountryIso = subInfo.getCountryIso();
+ String phoneNumber = extractPhoneNumberFromAssociatedUris(uris, /*isGlobalFormat*/true);
+ if (phoneNumber != null) {
+ phoneNumber = PhoneNumberUtils.formatNumberToE164(phoneNumber, subCountryIso);
if (phoneNumber == null) {
+ loge("format to E164 failed");
return;
}
mSubscriptionManagerService.setNumberFromIms(subId, phoneNumber);
+ } else if (isAllowNonGlobalNumberFormat()) {
+ // If carrier config has true for KEY_IGNORE_GLOBAL_PHONE_NUMBER_FORMAT_BOOL and
+ // P-Associated-Uri does not have global number,
+ // try to find phone number excluding '+' one more time.
+ phoneNumber = extractPhoneNumberFromAssociatedUris(uris, /*isGlobalFormat*/false);
+ if (phoneNumber == null) {
+ loge("extract phone number without '+' failed");
+ return;
+ }
+ mSubscriptionManagerService.setNumberFromIms(subId, phoneNumber);
+ } else {
+ logd("extract phone number failed");
}
}
@@ -2570,24 +2584,40 @@ public class ImsPhone extends ImsPhoneBase {
* <p>Associated URIs are public user identities, and phone number could be used:
* see 3GPP TS 24.229 5.4.1.2 and 3GPP TS 23.003 13.4. This algotihm look for the
* possible "global number" in E.164 format.
+ * <p>If true try finding phone number even if the P-Associated-Uri does not have global
+ * number format.
*/
- private static String extractPhoneNumberFromAssociatedUris(Uri[] uris) {
+ private static String extractPhoneNumberFromAssociatedUris(Uri[] uris, boolean isGlobalFormat) {
if (uris == null) {
return null;
}
- return Arrays.stream(uris)
+
+ Stream<String> intermediate = Arrays.stream(uris)
// Phone number is an opaque URI "tel:<phone-number>" or "sip:<phone-number>@<...>"
.filter(u -> u != null && u.isOpaque())
.filter(u -> "tel".equalsIgnoreCase(u.getScheme())
|| "sip".equalsIgnoreCase(u.getScheme()))
- .map(Uri::getSchemeSpecificPart)
- // "Global number" should be in E.164 format starting with "+" e.g. "+447539447777"
- .filter(ssp -> ssp != null && ssp.startsWith("+"))
- // Remove whatever after "@" for sip URI
- .map(ssp -> ssp.split("@")[0])
- // Returns the first winner
- .findFirst()
- .orElse(null);
+ .map(Uri::getSchemeSpecificPart);
+
+ if (isGlobalFormat) {
+ // "Global number" should be in E.164 format starting with "+" e.g. "+447539447777"
+ return intermediate.filter(ssp -> ssp != null && ssp.startsWith("+"))
+ // Remove whatever after "@" for sip URI
+ .map(ssp -> ssp.split("@")[0])
+ // Returns the first winner
+ .findFirst()
+ .orElse(null);
+ } else {
+ // non global number format
+ return intermediate.filter(ssp -> ssp != null)
+ // Remove whatever after "@" for sip URI
+ .map(ssp -> ssp.split("@")[0])
+ // regular expression, allow only number
+ .filter(ssp -> ssp.matches("^[0-9]+$"))
+ // Returns the first winner
+ .findFirst()
+ .orElse(null);
+ }
}
public IccRecords getIccRecords() {
@@ -2790,6 +2820,22 @@ public class ImsPhone extends ImsPhoneBase {
pw.flush();
}
+ private boolean isAllowNonGlobalNumberFormat() {
+ PersistableBundle persistableBundle = null;
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (carrierConfigManager != null) {
+ persistableBundle = carrierConfigManager.getConfigForSubId(getSubId(),
+ CarrierConfigManager.Ims.KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL);
+ }
+ if (persistableBundle != null) {
+ return persistableBundle.getBoolean(
+ CarrierConfigManager.Ims.KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL, false);
+ }
+
+ return false;
+ }
+
private void logi(String s) {
Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s);
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index b984d84e9e..104c925759 100644..100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -82,7 +82,7 @@ public class ImsPhoneConnection extends Connection implements
private ImsPhoneCall mParent;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsCall mImsCall;
- private Bundle mExtras = new Bundle();
+ private final Bundle mExtras = new Bundle();
private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1491,7 +1491,10 @@ public class ImsPhoneConnection extends Connection implements
* @return boolean: true if cross sim calling, false otherwise
*/
public boolean isCrossSimCall() {
- return mImsCall != null && mImsCall.isCrossSimCall();
+ if (mImsCall == null) {
+ return mExtras.getBoolean(ImsCallProfile.EXTRA_IS_CROSS_SIM_CALL);
+ }
+ return mImsCall.isCrossSimCall();
}
/**
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
index cfa16d0591..afb87dd8ae 100644
--- a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -24,6 +24,7 @@ import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.Annotation.NetworkType;
import android.telephony.DataFailCause;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -319,7 +320,7 @@ public class DataCallSessionStats {
ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
ServiceState serviceState =
serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
- return serviceState != null && serviceState.getRoaming();
+ return ServiceStateStats.isNetworkRoaming(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
}
private boolean getIsOpportunistic() {
diff --git a/src/java/com/android/internal/telephony/metrics/DataConnectionStateTracker.java b/src/java/com/android/internal/telephony/metrics/DataConnectionStateTracker.java
new file mode 100644
index 0000000000..c7ef625a42
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DataConnectionStateTracker.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * This Tracker is monitoring precise data connection states for each APNs which are used for IMS
+ * calling such as IMS and Emergency APN. It uses a SparseArray to track each SIM's connection
+ * state.
+ * The tracker is started by {@link VoiceCallSessionStats} and update the states to
+ * VoiceCallSessionStats directly.
+ */
+public class DataConnectionStateTracker {
+ private static final SparseArray<DataConnectionStateTracker> sDataConnectionStateTracker =
+ new SparseArray<>();
+ private final Executor mExecutor;
+ private Phone mPhone;
+ private int mSubId;
+ private HashMap<Integer, PreciseDataConnectionState> mLastPreciseDataConnectionState =
+ new HashMap<>();
+ private PreciseDataConnectionStateListenerImpl mDataConnectionStateListener;
+
+ private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener =
+ new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ if (mPhone == null) {
+ return;
+ }
+ int newSubId = mPhone.getSubId();
+ if (mSubId == newSubId
+ || newSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return;
+ }
+
+ unregisterTelephonyListener();
+ mSubId = newSubId;
+ registerTelephonyListener(mSubId);
+ }
+ };
+
+ private DataConnectionStateTracker() {
+ HandlerThread handlerThread =
+ new HandlerThread(DataConnectionStateTracker.class.getSimpleName());
+ handlerThread.start();
+ mExecutor = new HandlerExecutor(new Handler(handlerThread.getLooper()));
+ }
+
+ /** Getting or Creating DataConnectionStateTracker based on phoneId */
+ public static synchronized DataConnectionStateTracker getInstance(int phoneId) {
+ DataConnectionStateTracker dataConnectionStateTracker =
+ sDataConnectionStateTracker.get(phoneId);
+ if (dataConnectionStateTracker != null) {
+ return dataConnectionStateTracker;
+ }
+
+ dataConnectionStateTracker = new DataConnectionStateTracker();
+ sDataConnectionStateTracker.put(phoneId, dataConnectionStateTracker);
+ return dataConnectionStateTracker;
+ }
+
+ /** Starting to monitor the precise data connection states */
+ public void start(Phone phone) {
+ mPhone = phone;
+ mSubId = mPhone.getSubId();
+ registerTelephonyListener(mSubId);
+ SubscriptionManager mSubscriptionManager = mPhone.getContext()
+ .getSystemService(SubscriptionManager.class);
+ if (mSubscriptionManager != null) {
+ mSubscriptionManager
+ .addOnSubscriptionsChangedListener(mExecutor, mSubscriptionsChangedListener);
+ }
+ }
+
+ /** Stopping monitoring for the precise data connection states */
+ public void stop() {
+ if (mPhone == null) {
+ return;
+ }
+ SubscriptionManager mSubscriptionManager = mPhone.getContext()
+ .getSystemService(SubscriptionManager.class);
+ if (mSubscriptionManager != null) {
+ mSubscriptionManager
+ .removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener);
+ }
+ unregisterTelephonyListener();
+ mPhone = null;
+ mLastPreciseDataConnectionState.clear();
+ }
+
+ /** Returns data state of the last notified precise data connection state for apn type */
+ public int getDataState(int apnType) {
+ if (!mLastPreciseDataConnectionState.containsKey(apnType)) {
+ return TelephonyManager.DATA_UNKNOWN;
+ }
+ return mLastPreciseDataConnectionState.get(apnType).getState();
+ }
+
+ private void registerTelephonyListener(int subId) {
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return;
+ }
+ TelephonyManager telephonyManager =
+ mPhone.getContext().getSystemService(TelephonyManager.class);
+ if (telephonyManager != null) {
+ mDataConnectionStateListener = new PreciseDataConnectionStateListenerImpl(mExecutor);
+ mDataConnectionStateListener.register(telephonyManager.createForSubscriptionId(subId));
+ }
+ }
+
+ private void unregisterTelephonyListener() {
+ if (mDataConnectionStateListener != null) {
+ mDataConnectionStateListener.unregister();
+ mDataConnectionStateListener = null;
+ }
+ }
+
+ @VisibleForTesting
+ public void notifyDataConnectionStateChanged(PreciseDataConnectionState connectionState) {
+ List<Integer> apnTypes = connectionState.getApnSetting().getApnTypes();
+ if (apnTypes != null) {
+ for (int apnType : apnTypes) {
+ mLastPreciseDataConnectionState.put(apnType, connectionState);
+ }
+ }
+
+ mPhone.getVoiceCallSessionStats().onPreciseDataConnectionStateChanged(connectionState);
+ }
+
+ private class PreciseDataConnectionStateListenerImpl extends TelephonyCallback
+ implements TelephonyCallback.PreciseDataConnectionStateListener {
+ private final Executor mExecutor;
+ private TelephonyManager mTelephonyManager = null;
+
+ PreciseDataConnectionStateListenerImpl(Executor executor) {
+ mExecutor = executor;
+ }
+
+ public void register(TelephonyManager tm) {
+ if (tm == null) {
+ return;
+ }
+ mTelephonyManager = tm;
+ mTelephonyManager.registerTelephonyCallback(mExecutor, this);
+ }
+
+ public void unregister() {
+ if (mTelephonyManager != null) {
+ mTelephonyManager.unregisterTelephonyCallback(this);
+ mTelephonyManager = null;
+ }
+ }
+
+ @Override
+ public void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState connectionState) {
+ notifyDataConnectionStateChanged(connectionState);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
index 2f22196f45..0fd97ba25e 100644
--- a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -16,138 +16,376 @@
package com.android.internal.telephony.metrics;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.CellSignalStrength;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataCallResponse.LinkStatus;
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.TelephonyStatsLog;
+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.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.telephony.Rlog;
-/** Generates metrics related to data stall recovery events per phone ID for the pushed atom. */
+import java.util.List;
+
+/**
+ * Generates metrics related to data stall recovery events per phone ID for the pushed atom.
+ */
public class DataStallRecoveryStats {
+
/**
- * Create and push new atom when there is a data stall recovery event
- *
- * @param recoveryAction Data stall recovery action
- * @param phone
+ * Value indicating that link bandwidth is unspecified.
+ * Copied from {@code NetworkCapabilities#LINK_BANDWIDTH_UNSPECIFIED}
*/
+ private static final int LINK_BANDWIDTH_UNSPECIFIED = 0;
+
+ private static final String TAG = "DSRS-";
+
+ // Handler to upload metrics.
+ private final @NonNull Handler mHandler;
+
+ private final @NonNull String mTag;
+ private final @NonNull Phone mPhone;
+
+ // The interface name of the internet network.
+ private @Nullable String mIfaceName = null;
+
+ /* Metrics and stats data variables */
+ private int mPhoneId = 0;
+ private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+ private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ private int mBand = 0;
+ // The RAT used for data (including IWLAN).
+ private @NetworkType int mRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private boolean mIsOpportunistic = false;
+ private boolean mIsMultiSim = false;
+ private int mNetworkRegState = NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+ // Info of the other device in case of DSDS
+ private int mOtherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ private int mOtherNetworkRegState = NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+ // Link status of the data network
+ private @LinkStatus int mInternetLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN;
+
+ // The link bandwidth of the data network
+ private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
+ private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
- /* Since the Enum has been extended in Android T, we are mapping it to the correct number. */
- private static final int RECOVERY_ACTION_RADIO_RESTART_MAPPING = 3;
- private static final int RECOVERY_ACTION_RESET_MODEM_MAPPING = 4;
+ /**
+ * Constructs a new instance of {@link DataStallRecoveryStats}.
+ */
+ public DataStallRecoveryStats(@NonNull final Phone phone,
+ @NonNull final DataNetworkController dataNetworkController) {
+ mTag = TAG + phone.getPhoneId();
+ mPhone = phone;
+
+ HandlerThread handlerThread = new HandlerThread(mTag + "-thread");
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+
+ dataNetworkController.registerDataNetworkControllerCallback(
+ new DataNetworkControllerCallback(mHandler::post) {
+ @Override
+ public void onInternetDataNetworkConnected(
+ @NonNull List<DataNetwork> internetNetworks) {
+ for (DataNetwork dataNetwork : internetNetworks) {
+ mIfaceName = dataNetwork.getLinkProperties().getInterfaceName();
+ break;
+ }
+ }
+
+ @Override
+ public void onInternetDataNetworkDisconnected() {
+ mIfaceName = null;
+ }
+
+ @Override
+ public void onPhysicalLinkStatusChanged(@LinkStatus int status) {
+ mInternetLinkStatus = status;
+ }
+ });
+ }
/**
- * Called when data stall happened.
+ * Create and push new atom when there is a data stall recovery event.
*
- * @param recoveryAction The recovery action.
- * @param phone The phone instance.
- * @param isRecovered The data stall symptom recovered or not.
- * @param durationMillis The duration from data stall symptom occurred.
- * @param reason The recovered(data resume) reason.
- * @param isFirstValidation The validation status if it's the first come after recovery.
+ * @param action The recovery action.
+ * @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 durationOfAction The duration of the current action in milliseconds.
*/
- public static void onDataStallEvent(
- @DataStallRecoveryManager.RecoveryAction int recoveryAction,
- Phone phone,
+ public void uploadMetrics(
+ @DataStallRecoveryManager.RecoveryAction int action,
boolean isRecovered,
- int durationMillis,
+ int duration,
@DataStallRecoveryManager.RecoveredReason int reason,
boolean isFirstValidation,
- int durationMillisOfCurrentAction) {
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
- phone = phone.getDefaultPhone();
+ int durationOfAction) {
+
+ mHandler.post(() -> {
+ // Update data stall stats
+ log("Set recovery action to " + action);
+
+ // Refreshes the metrics data.
+ try {
+ refreshMetricsData();
+ } catch (Exception e) {
+ loge("The metrics data cannot be refreshed.", e);
+ return;
+ }
+
+ TelephonyStatsLog.write(
+ TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED,
+ mCarrierId,
+ mRat,
+ mSignalStrength,
+ action,
+ mIsOpportunistic,
+ mIsMultiSim,
+ mBand,
+ isRecovered,
+ duration,
+ reason,
+ mOtherSignalStrength,
+ mOtherNetworkRegState,
+ mNetworkRegState,
+ isFirstValidation,
+ mPhoneId,
+ durationOfAction,
+ mInternetLinkStatus,
+ mLinkUpBandwidthKbps,
+ mLinkDownBandwidthKbps);
+
+ log("Upload stats: "
+ + "Action:"
+ + action
+ + ", Recovered:"
+ + isRecovered
+ + ", Duration:"
+ + duration
+ + ", Reason:"
+ + reason
+ + ", First validation:"
+ + isFirstValidation
+ + ", Duration of action:"
+ + durationOfAction
+ + ", "
+ + this);
+ });
+ }
+
+ /**
+ * Refreshes the metrics data.
+ */
+ private void refreshMetricsData() {
+ logd("Refreshes the metrics data.");
+ // Update phone id/carrier id and signal strength
+ mPhoneId = mPhone.getPhoneId() + 1;
+ mCarrierId = mPhone.getCarrierId();
+ mSignalStrength = mPhone.getSignalStrength().getLevel();
+
+ // Update the bandwidth.
+ updateBandwidths();
+
+ // Update the RAT and band.
+ updateRatAndBand();
+
+ // Update the opportunistic state.
+ mIsOpportunistic = getIsOpportunistic(mPhone);
+
+ // Update the multi-SIM state.
+ mIsMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
+
+ // Update the network registration state.
+ updateNetworkRegState();
+
+ // Update the DSDS information.
+ updateDsdsInfo();
+ }
+
+ /**
+ * Updates the bandwidth for the current data network.
+ */
+ private void updateBandwidths() {
+ mLinkDownBandwidthKbps = mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
+
+ if (mIfaceName == null) {
+ loge("Interface name is null");
+ return;
+ }
+
+ DataNetworkController dataNetworkController = mPhone.getDataNetworkController();
+ if (dataNetworkController == null) {
+ loge("DataNetworkController is null");
+ return;
+ }
+
+ DataNetwork dataNetwork = dataNetworkController.getDataNetworkByInterface(mIfaceName);
+ if (dataNetwork == null) {
+ loge("DataNetwork is null");
+ return;
+ }
+ NetworkCapabilities networkCapabilities = dataNetwork.getNetworkCapabilities();
+ if (networkCapabilities == null) {
+ loge("NetworkCapabilities is null");
+ return;
}
- int carrierId = phone.getCarrierId();
- int rat = getRat(phone);
- int band =
- (rat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(phone);
- // the number returned here matches the SignalStrength enum we have
- int signalStrength = phone.getSignalStrength().getLevel();
- boolean isOpportunistic = getIsOpportunistic(phone);
- boolean isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
-
- if (recoveryAction == DataStallRecoveryManager.RECOVERY_ACTION_RADIO_RESTART) {
- recoveryAction = RECOVERY_ACTION_RADIO_RESTART_MAPPING;
- } else if (recoveryAction == DataStallRecoveryManager.RECOVERY_ACTION_RESET_MODEM) {
- recoveryAction = RECOVERY_ACTION_RESET_MODEM_MAPPING;
+ mLinkDownBandwidthKbps = networkCapabilities.getLinkDownstreamBandwidthKbps();
+ mLinkUpBandwidthKbps = networkCapabilities.getLinkUpstreamBandwidthKbps();
+ }
+
+ private void updateRatAndBand() {
+ mRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ mBand = 0;
+ ServiceState serviceState = mPhone.getServiceState();
+ if (serviceState == null) {
+ loge("ServiceState is null");
+ return;
+ }
+
+ mRat = serviceState.getDataNetworkType();
+ mBand =
+ (mRat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(mPhone);
+ }
+
+ private static boolean getIsOpportunistic(@NonNull Phone phone) {
+ SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+ .getSubscriptionInfoInternal(phone.getSubId());
+ return subInfo != null && subInfo.isOpportunistic();
+ }
+
+ private void updateNetworkRegState() {
+ mNetworkRegState = NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+
+ NetworkRegistrationInfo phoneRegInfo = mPhone.getServiceState()
+ .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (phoneRegInfo != null) {
+ mNetworkRegState = phoneRegInfo.getRegistrationState();
}
+ }
- // collect info of the other device in case of DSDS
- int otherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- // the number returned here matches the NetworkRegistrationState enum we have
- int otherNetworkRegState = NetworkRegistrationInfo
- .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+ private void updateDsdsInfo() {
+ mOtherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ mOtherNetworkRegState = NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
for (Phone otherPhone : PhoneFactory.getPhones()) {
- if (otherPhone.getPhoneId() == phone.getPhoneId()) continue;
+ if (otherPhone.getPhoneId() == mPhone.getPhoneId()) continue;
if (!getIsOpportunistic(otherPhone)) {
- otherSignalStrength = otherPhone.getSignalStrength().getLevel();
+ mOtherSignalStrength = otherPhone.getSignalStrength().getLevel();
NetworkRegistrationInfo regInfo = otherPhone.getServiceState()
.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
if (regInfo != null) {
- otherNetworkRegState = regInfo.getRegistrationState();
+ mOtherNetworkRegState = regInfo.getRegistrationState();
}
break;
}
}
+ }
- // the number returned here matches the NetworkRegistrationState enum we have
- int phoneNetworkRegState = NetworkRegistrationInfo
- .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
+ /**
+ * Return bundled data stall recovery metrics data.
+ *
+ * @param action The recovery action.
+ * @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 durationOfAction The duration of the current action in milliseconds.
+ */
+ public Bundle getDataStallRecoveryMetricsData(
+ @DataStallRecoveryManager.RecoveryAction int action,
+ boolean isRecovered,
+ int duration,
+ @DataStallRecoveryManager.RecoveredReason int reason,
+ boolean isFirstValidation,
+ int durationOfAction) {
+ 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);
+ return bundle;
+ }
- NetworkRegistrationInfo phoneRegInfo = phone.getServiceState()
- .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (phoneRegInfo != null) {
- phoneNetworkRegState = phoneRegInfo.getRegistrationState();
- }
+ private void log(@NonNull String s) {
+ Rlog.i(mTag, s);
+ }
- // reserve 0 for default value
- int phoneId = phone.getPhoneId() + 1;
-
- TelephonyStatsLog.write(
- TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED,
- carrierId,
- rat,
- signalStrength,
- recoveryAction,
- isOpportunistic,
- isMultiSim,
- band,
- isRecovered,
- durationMillis,
- reason,
- otherSignalStrength,
- otherNetworkRegState,
- phoneNetworkRegState,
- isFirstValidation,
- phoneId,
- durationMillisOfCurrentAction);
+ private void logd(@NonNull String s) {
+ Rlog.d(mTag, s);
}
- /** Returns the RAT used for data (including IWLAN). */
- private static @NetworkType int getRat(Phone phone) {
- ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
- ServiceState serviceState =
- serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
- return serviceState != null
- ? serviceState.getDataNetworkType()
- : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private void loge(@NonNull String s) {
+ Rlog.e(mTag, s);
}
- private static boolean getIsOpportunistic(Phone phone) {
- SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
- .getSubscriptionInfoInternal(phone.getSubId());
- return subInfo != null && subInfo.isOpportunistic();
+ private void loge(@NonNull String s, Throwable tr) {
+ Rlog.e(mTag, s, tr);
+ }
+
+ @Override
+ public String toString() {
+ return "DataStallRecoveryStats {"
+ + "Phone id:"
+ + mPhoneId
+ + ", Signal strength:"
+ + mSignalStrength
+ + ", Band:" + mBand
+ + ", RAT:" + mRat
+ + ", Opportunistic:"
+ + mIsOpportunistic
+ + ", Multi-SIM:"
+ + mIsMultiSim
+ + ", Network reg state:"
+ + mNetworkRegState
+ + ", Other signal strength:"
+ + mOtherSignalStrength
+ + ", Other network reg state:"
+ + mOtherNetworkRegState
+ + ", Link status:"
+ + mInternetLinkStatus
+ + ", Link down bandwidth:"
+ + mLinkDownBandwidthKbps
+ + ", Link up bandwidth:"
+ + mLinkUpBandwidthKbps
+ + "}";
}
}
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 5e00987911..3e49139cce 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -923,7 +923,9 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
roundAndConvertMillisToSeconds(state.totalTimeMillis),
state.isEmergencyOnly,
state.isInternetPdnUp,
- state.foldState);
+ state.foldState,
+ state.overrideVoiceService,
+ state.isDataEnabled);
}
private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) {
@@ -975,7 +977,9 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
session.isMultiparty,
session.callDuration,
session.lastKnownRat,
- session.foldState);
+ session.foldState,
+ session.ratSwitchCountAfterConnected,
+ session.handoverInProgress);
}
private static StatsEvent buildStatsEvent(IncomingSms sms) {
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 5a21bafd49..d495ca28c5 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -1709,7 +1709,9 @@ public class PersistAtomsStorage {
&& state.carrierId == key.carrierId
&& state.isEmergencyOnly == key.isEmergencyOnly
&& state.isInternetPdnUp == key.isInternetPdnUp
- && state.foldState == key.foldState) {
+ && state.foldState == key.foldState
+ && state.overrideVoiceService == key.overrideVoiceService
+ && state.isDataEnabled == key.isDataEnabled) {
return state;
}
}
diff --git a/src/java/com/android/internal/telephony/metrics/RcsStats.java b/src/java/com/android/internal/telephony/metrics/RcsStats.java
index 8d24defb2b..20b23f94cb 100644
--- a/src/java/com/android/internal/telephony/metrics/RcsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/RcsStats.java
@@ -1029,8 +1029,11 @@ public class RcsStats {
}
/** invalidated result when Request message is sent */
- public synchronized void invalidatedMessageResult(int subId, String sipMessageMethod,
- int sipMessageDirection, int messageError) {
+ public synchronized void invalidatedMessageResult(String callId, int subId,
+ String sipMessageMethod, int sipMessageDirection, int messageError) {
+ if (mSipMessage == null) {
+ mSipMessage = new SipMessageArray(sipMessageMethod, sipMessageDirection, callId);
+ }
mSipMessage.addSipMessageStat(subId, sipMessageMethod, 0,
sipMessageDirection, messageError);
}
diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
index b830cd00d2..b6563dd4db 100644
--- a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
@@ -15,6 +15,7 @@
*/
package com.android.internal.telephony.metrics;
+
import static android.telephony.TelephonyManager.DATA_CONNECTED;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
@@ -29,6 +30,7 @@ import android.telephony.AccessNetworkUtils;
import android.telephony.Annotation.NetworkType;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
+import android.telephony.ServiceState.RoamingType;
import android.telephony.TelephonyManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -44,6 +46,7 @@ import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceStat
import com.android.telephony.Rlog;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/** Tracks service state duration and switch metrics for each phone. */
@@ -52,6 +55,7 @@ public class ServiceStateStats extends DataNetworkControllerCallback {
private final AtomicReference<TimestampedServiceState> mLastState =
new AtomicReference<>(new TimestampedServiceState(null, 0L));
+ private final AtomicBoolean mOverrideVoiceService = new AtomicBoolean(false);
private final Phone mPhone;
private final PersistAtomsStorage mStorage;
private final DeviceStateHelper mDeviceStateHelper;
@@ -114,8 +118,10 @@ public class ServiceStateStats extends DataNetworkControllerCallback {
CellularServiceState newState = new CellularServiceState();
newState.voiceRat = getVoiceRat(mPhone, serviceState);
newState.dataRat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
- newState.voiceRoamingType = serviceState.getVoiceRoamingType();
- newState.dataRoamingType = serviceState.getDataRoamingType();
+ newState.voiceRoamingType =
+ getNetworkRoamingState(serviceState, NetworkRegistrationInfo.DOMAIN_CS);
+ newState.dataRoamingType =
+ getNetworkRoamingState(serviceState, NetworkRegistrationInfo.DOMAIN_PS);
newState.isEndc = isEndc(serviceState);
newState.simSlotIndex = mPhone.getPhoneId();
newState.isMultiSim = SimSlotState.isMultiSim();
@@ -123,6 +129,8 @@ public class ServiceStateStats extends DataNetworkControllerCallback {
newState.isEmergencyOnly = isEmergencyOnly(serviceState);
newState.isInternetPdnUp = isInternetPdnUp(mPhone);
newState.foldState = mDeviceStateHelper.getFoldState();
+ newState.overrideVoiceService = mOverrideVoiceService.get();
+ newState.isDataEnabled = mPhone.getDataSettingsManager().isDataEnabled();
TimestampedServiceState prevState =
mLastState.getAndSet(new TimestampedServiceState(newState, now));
addServiceStateAndSwitch(
@@ -150,6 +158,26 @@ public class ServiceStateStats extends DataNetworkControllerCallback {
}
}
+ /** Updates override state for voice service state when voice calling capability changes */
+ public void onVoiceServiceStateOverrideChanged(boolean override) {
+ if (override == mOverrideVoiceService.get()) {
+ return;
+ }
+ mOverrideVoiceService.set(override);
+ final long now = getTimeMillis();
+ TimestampedServiceState lastState =
+ mLastState.getAndUpdate(
+ state -> {
+ if (state.mServiceState == null) {
+ return new TimestampedServiceState(null, now);
+ }
+ CellularServiceState newServiceState = copyOf(state.mServiceState);
+ newServiceState.overrideVoiceService = mOverrideVoiceService.get();
+ return new TimestampedServiceState(newServiceState, now);
+ });
+ addServiceState(lastState, now);
+ }
+
private void addServiceState(TimestampedServiceState prevState, long now) {
addServiceStateAndSwitch(prevState, now, null);
}
@@ -271,6 +299,8 @@ public class ServiceStateStats extends DataNetworkControllerCallback {
copy.isEmergencyOnly = state.isEmergencyOnly;
copy.isInternetPdnUp = state.isInternetPdnUp;
copy.foldState = state.foldState;
+ copy.overrideVoiceService = state.overrideVoiceService;
+ copy.isDataEnabled = state.isDataEnabled;
return copy;
}
@@ -380,6 +410,39 @@ public class ServiceStateStats extends DataNetworkControllerCallback {
addServiceState(lastState, now);
}
+ private static @RoamingType int getNetworkRoamingState(
+ ServiceState ss, @NetworkRegistrationInfo.Domain int domain) {
+ final NetworkRegistrationInfo nri =
+ ss.getNetworkRegistrationInfo(domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (nri == null) {
+ // No registration for domain
+ return ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ }
+ @RoamingType int roamingType = nri.getRoamingType();
+ if (nri.isNetworkRoaming() && roamingType == ServiceState.ROAMING_TYPE_NOT_ROAMING) {
+ // Roaming is overridden, exact roaming type unknown.
+ return ServiceState.ROAMING_TYPE_UNKNOWN;
+ }
+ return roamingType;
+ }
+
+ /** Determines whether device is roaming, bypassing carrier overrides. */
+ public static boolean isNetworkRoaming(
+ ServiceState ss, @NetworkRegistrationInfo.Domain int domain) {
+ if (ss == null) {
+ return false;
+ }
+ final NetworkRegistrationInfo nri =
+ ss.getNetworkRegistrationInfo(domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ return nri != null && nri.isNetworkRoaming();
+ }
+
+ /** Determines whether device is roaming in any domain, bypassing carrier overrides. */
+ public static boolean isNetworkRoaming(ServiceState ss) {
+ return isNetworkRoaming(ss, NetworkRegistrationInfo.DOMAIN_CS)
+ || isNetworkRoaming(ss, NetworkRegistrationInfo.DOMAIN_PS);
+ }
+
@VisibleForTesting
protected long getTimeMillis() {
return SystemClock.elapsedRealtime();
diff --git a/src/java/com/android/internal/telephony/metrics/SmsStats.java b/src/java/com/android/internal/telephony/metrics/SmsStats.java
index 2f1e6a7182..949b72e57b 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsStats.java
@@ -386,7 +386,7 @@ public class SmsStats {
private boolean getIsRoaming() {
ServiceState serviceState = getServiceState();
- return serviceState != null ? serviceState.getRoaming() : false;
+ return ServiceStateStats.isNetworkRoaming(serviceState);
}
private int getCarrierId() {
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index ba07fa035a..5d8e027e09 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -47,8 +47,10 @@ import android.telephony.Annotation.NetworkType;
import android.telephony.AnomalyReporter;
import android.telephony.DisconnectCause;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.util.LongSparseArray;
@@ -163,6 +165,8 @@ public class VoiceCallSessionStats {
public VoiceCallSessionStats(int phoneId, Phone phone) {
mPhoneId = phoneId;
mPhone = phone;
+
+ DataConnectionStateTracker.getInstance(phoneId).start(phone);
}
/* CS calls */
@@ -395,6 +399,14 @@ public class VoiceCallSessionStats {
}
}
+ /** Updates internal states when IMS/Emergency PDN/PDU state changes */
+ public synchronized void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState connectionState) {
+ if (hasCalls()) {
+ updateVoiceCallSessionBearerState(connectionState);
+ }
+ }
+
/* internal */
/** Handles ringing MT call getting accepted. */
@@ -426,6 +438,7 @@ public class VoiceCallSessionStats {
int bearer = getBearer(conn);
ServiceState serviceState = getServiceState();
@NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, serviceState, bearer);
+ @VideoState int videoState = conn.getVideoState();
VoiceCallSession proto = new VoiceCallSession();
proto.bearerAtStart = bearer;
@@ -439,6 +452,7 @@ public class VoiceCallSessionStats {
proto.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
proto.ratAtEnd = rat;
proto.ratSwitchCount = 0L;
+ proto.ratSwitchCountAfterConnected = 0L;
proto.codecBitmask = 0L;
proto.simSlotIndex = mPhoneId;
proto.isMultiSim = SimSlotState.isMultiSim();
@@ -449,9 +463,11 @@ public class VoiceCallSessionStats {
proto.srvccCancellationCount = 0L;
proto.rttEnabled = false;
proto.isEmergency = conn.isEmergencyCall() || conn.isNetworkIdentifiedEmergencyCall();
- proto.isRoaming = serviceState != null ? serviceState.getVoiceRoaming() : false;
+ proto.isRoaming = ServiceStateStats.isNetworkRoaming(serviceState);
proto.isMultiparty = conn.isMultiparty();
proto.lastKnownRat = rat;
+ proto.videoEnabled = videoState != VideoProfile.STATE_AUDIO_ONLY ? true : false;
+ proto.handoverInProgress = isHandoverInProgress(bearer, proto.isEmergency);
// internal fields for tracking
if (getDirection(conn) == VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT) {
@@ -607,6 +623,9 @@ public class VoiceCallSessionStats {
private void updateRatAtEnd(VoiceCallSession proto, @NetworkType int rat) {
if (proto.ratAtEnd != rat) {
proto.ratSwitchCount++;
+ if (!proto.setupFailed) {
+ proto.ratSwitchCountAfterConnected++;
+ }
proto.ratAtEnd = rat;
if (rat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
proto.lastKnownRat = rat;
@@ -680,6 +699,17 @@ public class VoiceCallSessionStats {
return mPhone.getSignalStrength().getLevel();
}
+ private boolean isHandoverInProgress(int bearer, boolean isEmergency) {
+ // If the call is not IMS, the bearer will not be able to handover
+ if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
+ return false;
+ }
+
+ int apnType = isEmergency ? ApnSetting.TYPE_EMERGENCY : ApnSetting.TYPE_IMS;
+ int dataState = DataConnectionStateTracker.getInstance(mPhoneId).getDataState(apnType);
+ return dataState == TelephonyManager.DATA_HANDOVER_IN_PROGRESS;
+ }
+
/**
* This is a copy of ServiceStateStats.getVoiceRat(Phone, ServiceState, int) with minimum fix
* required for tracking EPSFB correctly.
@@ -922,4 +952,40 @@ public class VoiceCallSessionStats {
return map;
}
+
+ private void updateVoiceCallSessionBearerState(PreciseDataConnectionState connectionState) {
+ ApnSetting apnSetting = connectionState.getApnSetting();
+ if (apnSetting == null) {
+ return;
+ }
+
+ int apnTypes = apnSetting.getApnTypeBitmask();
+ if ((apnTypes & ApnSetting.TYPE_IMS) == 0
+ && (apnTypes & ApnSetting.TYPE_EMERGENCY) == 0) {
+ return;
+ }
+
+ for (int i = 0; i < mCallProtos.size(); i++) {
+ VoiceCallSession proto = mCallProtos.valueAt(i);
+ if (proto.bearerAtEnd == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
+ if (!proto.isEmergency && (apnTypes & ApnSetting.TYPE_IMS) != 0) {
+ updateHandoverState(proto, connectionState.getState());
+ }
+ if (proto.isEmergency && (apnTypes & ApnSetting.TYPE_EMERGENCY) != 0) {
+ updateHandoverState(proto, connectionState.getState());
+ }
+ }
+ }
+ }
+
+ private void updateHandoverState(VoiceCallSession proto, int dataState) {
+ switch (dataState) {
+ case TelephonyManager.DATA_HANDOVER_IN_PROGRESS:
+ proto.handoverInProgress = true;
+ break;
+ default:
+ // All other states are considered as not in handover
+ proto.handoverInProgress = false;
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
new file mode 100644
index 0000000000..5d4e5dd57f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/satellite/NtnCapabilityResolver.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 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.telephony.NetworkRegistrationInfo;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+
+import java.util.List;
+
+/**
+ * This utility class is responsible for resolving NTN capabilities of a
+ * {@link NetworkRegistrationInfo}.
+ */
+public class NtnCapabilityResolver {
+ private static final String TAG = "NtnCapabilityResolver";
+
+ /**
+ * Resolve NTN capability by updating the input NetworkRegistrationInfo to indicate whether
+ * connecting to a non-terrestrial network and the available services supported by the network.
+ *
+ * @param networkRegistrationInfo The NetworkRegistrationInfo of a network.
+ * @param subId The subscription ID associated with a phone.
+ */
+ public static void resolveNtnCapability(
+ @NonNull NetworkRegistrationInfo networkRegistrationInfo, int subId) {
+ SatelliteController satelliteController = SatelliteController.getInstance();
+ List<String> satellitePlmnList = satelliteController.getSatellitePlmnList();
+ String registeredPlmn = networkRegistrationInfo.getRegisteredPlmn();
+ for (String satellitePlmn : satellitePlmnList) {
+ if (TextUtils.equals(satellitePlmn, registeredPlmn)) {
+ logd("Registered to satellite PLMN " + satellitePlmn);
+ networkRegistrationInfo.setIsNonTerrestrialNetwork(true);
+ networkRegistrationInfo.setAvailableServices(
+ satelliteController.getSupportedSatelliteServices(subId, satellitePlmn));
+ break;
+ }
+ }
+ }
+
+ private static void logd(@NonNull String log) {
+ Rlog.d(TAG, log);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/satellite/PointingAppController.java b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
index f7f93cf441..9dba6f209c 100644
--- a/src/java/com/android/internal/telephony/satellite/PointingAppController.java
+++ b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
@@ -37,6 +37,7 @@ import android.telephony.satellite.SatelliteManager;
import android.text.TextUtils;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import java.util.ArrayList;
@@ -92,7 +93,8 @@ public class PointingAppController {
*
* @param context The Context for the PointingUIController.
*/
- private PointingAppController(@NonNull Context context) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ public PointingAppController(@NonNull Context context) {
mContext = context;
mStartedSatelliteTransmissionUpdates = false;
}
@@ -102,11 +104,21 @@ public class PointingAppController {
* transmission updates
* @param startedSatelliteTransmissionUpdates boolean to set the flag
*/
+ @VisibleForTesting
public void setStartedSatelliteTransmissionUpdates(
boolean startedSatelliteTransmissionUpdates) {
mStartedSatelliteTransmissionUpdates = startedSatelliteTransmissionUpdates;
}
+ /**
+ * Get the flag mStartedSatelliteTransmissionUpdates
+ * @return returns mStartedSatelliteTransmissionUpdates
+ */
+ @VisibleForTesting
+ public boolean getStartedSatelliteTransmissionUpdates() {
+ return mStartedSatelliteTransmissionUpdates;
+ }
+
private static final class DatagramTransferStateHandlerRequest {
public int datagramTransferState;
public int pendingCount;
@@ -128,6 +140,7 @@ public class PointingAppController {
public static final int EVENT_DATAGRAM_TRANSFER_STATE_CHANGED = 4;
private final ConcurrentHashMap<IBinder, ISatelliteTransmissionUpdateCallback> mListeners;
+
SatelliteTransmissionUpdateHandler(Looper looper) {
super(looper);
mListeners = new ConcurrentHashMap<>();
@@ -265,7 +278,6 @@ public class PointingAppController {
mSatelliteTransmissionUpdateHandlers.get(subId);
if (handler != null) {
handler.removeListener(callback);
-
if (handler.hasListeners()) {
result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
return;
@@ -321,9 +333,11 @@ public class PointingAppController {
/**
* Stop receiving satellite transmission updates.
+ * Reset the flag mStartedSatelliteTransmissionUpdates
* This can be called by the pointing UI when the user stops pointing to the satellite.
*/
public void stopSatelliteTransmissionUpdates(@NonNull Message message, @Nullable Phone phone) {
+ setStartedSatelliteTransmissionUpdates(false);
if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
SatelliteModemInterface.getInstance().stopSendingSatellitePointingInfo(message);
return;
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 5cd84446c7..957b152c25 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.satellite;
+import android.annotation.ArrayRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
@@ -25,23 +26,28 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.wifi.WifiManager;
import android.nfc.NfcAdapter;
-import android.nfc.NfcManager;
import android.os.AsyncResult;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.SystemProperties;
import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -53,8 +59,10 @@ import android.telephony.satellite.SatelliteCapabilities;
import android.telephony.satellite.SatelliteDatagram;
import android.telephony.satellite.SatelliteManager;
import android.util.Log;
+import android.util.SparseArray;
import android.uwb.UwbManager;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
@@ -63,10 +71,14 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import com.android.internal.util.FunctionalUtils;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
@@ -79,6 +91,8 @@ public class SatelliteController extends Handler {
private static final String TAG = "SatelliteController";
/** Whether enabling verbose debugging message or not. */
private static final boolean DBG = false;
+ private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+ private static final boolean DEBUG = !"user".equals(Build.TYPE);
/** File used to store shared preferences related to satellite. */
public static final String SATELLITE_SHARED_PREF = "satellite_shared_pref";
/** Value to pass for the setting key SATELLITE_MODE_ENABLED, enabled = 1, disabled = 0 */
@@ -153,6 +167,9 @@ public class SatelliteController extends Handler {
@GuardedBy("mSatelliteEnabledRequestLock")
private boolean mWaitingForRadioDisabled = false;
+ private boolean mWaitingForDisableSatelliteModemResponse = false;
+ private boolean mWaitingForSatelliteModemOff = false;
+
private final AtomicBoolean mRegisteredForProvisionStateChangedWithSatelliteService =
new AtomicBoolean(false);
private final AtomicBoolean mRegisteredForProvisionStateChangedWithPhone =
@@ -193,6 +210,22 @@ public class SatelliteController extends Handler {
private final Object mNeedsSatellitePointingLock = new Object();
@GuardedBy("mNeedsSatellitePointingLock")
private boolean mNeedsSatellitePointing = false;
+ /** Key: subId, value: (key: PLMN, value: set of
+ * {@link android.telephony.NetworkRegistrationInfo.ServiceType})
+ */
+ @GuardedBy("mSupportedSatelliteServicesLock")
+ @NonNull private final Map<Integer, Map<String, Set<Integer>>> mSupportedSatelliteServices =
+ new HashMap<>();
+ @NonNull private final Object mSupportedSatelliteServicesLock = new Object();
+ /** Key: PLMN, value: set of {@link android.telephony.NetworkRegistrationInfo.ServiceType} */
+ @NonNull private final Map<String, Set<Integer>> mSatelliteServicesSupportedByProviders;
+ @NonNull private final CarrierConfigManager mCarrierConfigManager;
+ @NonNull private final CarrierConfigManager.CarrierConfigChangeListener
+ mCarrierConfigChangeListener;
+ @NonNull private final Object mCarrierConfigArrayLock = new Object();
+ @GuardedBy("mCarrierConfigArrayLock")
+ @NonNull private final SparseArray<PersistableBundle> mCarrierConfigArray = new SparseArray<>();
+ @NonNull private final List<String> mSatellitePlmnList;
/**
* @return The singleton instance of SatelliteController.
@@ -260,6 +293,7 @@ public class SatelliteController extends Handler {
registerForPendingDatagramCount();
registerForSatelliteModemStateChanged();
mContentResolver = mContext.getContentResolver();
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
try {
mSharedPreferences = mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
@@ -281,6 +315,16 @@ public class SatelliteController extends Handler {
Settings.Global.getUriFor(Settings.Global.SATELLITE_MODE_RADIOS),
false, satelliteModeRadiosContentObserver);
}
+
+ mSatelliteServicesSupportedByProviders = readSupportedSatelliteServicesFromOverlayConfig();
+ mSatellitePlmnList =
+ mSatelliteServicesSupportedByProviders.keySet().stream().toList();
+ updateSupportedSatelliteServicesForActiveSubscriptions();
+ mCarrierConfigChangeListener =
+ (slotIndex, subId, carrierId, specificCarrierId) ->
+ handleCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
+ mCarrierConfigManager.registerCarrierConfigChangeListener(
+ new HandlerExecutor(new Handler(looper)), mCarrierConfigChangeListener);
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -418,44 +462,50 @@ public class SatelliteController extends Handler {
case BluetoothAdapter.ACTION_STATE_CHANGED:
int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
- logd("Bluetooth state updated to " + btState);
synchronized (mRadioStateLock) {
+ boolean currentBTStateEnabled = mBTStateEnabled;
if (btState == BluetoothAdapter.STATE_OFF) {
mBTStateEnabled = false;
evaluateToSendSatelliteEnabledSuccess();
} else if (btState == BluetoothAdapter.STATE_ON) {
mBTStateEnabled = true;
}
- logd("mBTStateEnabled: " + mBTStateEnabled);
+ if (currentBTStateEnabled != mBTStateEnabled) {
+ logd("mBTStateEnabled=" + mBTStateEnabled);
+ }
}
break;
case NfcAdapter.ACTION_ADAPTER_STATE_CHANGED:
int nfcState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, -1);
- logd("Nfc state updated to " + nfcState);
synchronized (mRadioStateLock) {
+ boolean currentNfcStateEnabled = mNfcStateEnabled;
if (nfcState == NfcAdapter.STATE_ON) {
mNfcStateEnabled = true;
} else if (nfcState == NfcAdapter.STATE_OFF) {
mNfcStateEnabled = false;
evaluateToSendSatelliteEnabledSuccess();
}
- logd("mNfcStateEnabled: " + mNfcStateEnabled);
+ if (currentNfcStateEnabled != mNfcStateEnabled) {
+ logd("mNfcStateEnabled=" + mNfcStateEnabled);
+ }
}
break;
case WifiManager.WIFI_STATE_CHANGED_ACTION:
int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN);
- logd("Wifi state updated to " + wifiState);
synchronized (mRadioStateLock) {
+ boolean currentWifiStateEnabled = mWifiStateEnabled;
if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
mWifiStateEnabled = true;
} else if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
mWifiStateEnabled = false;
evaluateToSendSatelliteEnabledSuccess();
}
- logd("mWifiStateEnabled: " + mWifiStateEnabled);
+ if (currentWifiStateEnabled != mWifiStateEnabled) {
+ logd("mWifiStateEnabled=" + mWifiStateEnabled);
+ }
}
break;
default:
@@ -686,17 +736,17 @@ public class SatelliteController extends Handler {
SatelliteManager.SATELLITE_ERROR_NONE);
}
}
- resetSatelliteEnabledRequest();
- setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
- setDemoModeEnabled(argument.enableDemoMode);
synchronized (mIsSatelliteEnabledLock) {
- mIsSatelliteEnabled = argument.enableSatellite;
+ if (!mWaitingForSatelliteModemOff) {
+ moveSatelliteToOffStateAndCleanUpResources(
+ SatelliteManager.SATELLITE_ERROR_NONE, argument.callback);
+ } else {
+ logd("Wait for satellite modem off before updating satellite"
+ + " modem state");
+ }
+ mWaitingForDisableSatelliteModemResponse = false;
}
- // If satellite is disabled, send success to callback immediately
- argument.callback.accept(error);
- updateSatelliteEnabledState(
- argument.enableSatellite, "EVENT_SET_SATELLITE_ENABLED_DONE");
}
} else {
synchronized (mSatelliteEnabledRequestLock) {
@@ -728,6 +778,9 @@ public class SatelliteController extends Handler {
.reportSessionMetrics();
} else {
mControllerMetricsStats.onSatelliteDisabled();
+ synchronized (mIsSatelliteEnabledLock) {
+ mWaitingForDisableSatelliteModemResponse = false;
+ }
}
break;
}
@@ -951,19 +1004,24 @@ public class SatelliteController extends Handler {
|| mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
mIsRadioOn = false;
logd("Radio State Changed to " + mCi.getRadioState());
- IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
- @Override
- public void accept(int result) {
- logd("RequestSatelliteEnabled: result=" + result);
- }
- };
- Phone phone = SatelliteServiceUtils.getPhone();
- Consumer<Integer> result = FunctionalUtils
- .ignoreRemoteException(errorCallback::accept);
- RequestSatelliteEnabledArgument message =
- new RequestSatelliteEnabledArgument(false, false, result);
- request = new SatelliteControllerHandlerRequest(message, phone);
- handleSatelliteEnabled(request);
+ if (isSatelliteEnabled()) {
+ IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ logd("RequestSatelliteEnabled: result=" + result);
+ }
+ };
+ Phone phone = SatelliteServiceUtils.getPhone();
+ Consumer<Integer> result = FunctionalUtils
+ .ignoreRemoteException(errorCallback::accept);
+ RequestSatelliteEnabledArgument message =
+ new RequestSatelliteEnabledArgument(false, false, result);
+ request = new SatelliteControllerHandlerRequest(message, phone);
+ handleSatelliteEnabled(request);
+ } else {
+ logd("EVENT_RADIO_STATE_CHANGED: Satellite modem is currently disabled."
+ + " Ignored the event");
+ }
} else {
mIsRadioOn = true;
if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
@@ -1155,13 +1213,13 @@ public class SatelliteController extends Handler {
if (mSatelliteEnabledRequest == null) {
mSatelliteEnabledRequest = request;
} else if (mSatelliteEnabledRequest.enableSatellite == request.enableSatellite) {
- logd("requestSatelliteEnabled enableSatellite: " + enableSatellite
+ logd("requestSatelliteEnabled enableSatellite: " + enableSatellite
+ " is already in progress.");
result.accept(SatelliteManager.SATELLITE_REQUEST_IN_PROGRESS);
return;
} else if (mSatelliteEnabledRequest.enableSatellite == false
&& request.enableSatellite == true) {
- logd("requestSatelliteEnabled enableSatellite: " + enableSatellite + " cannot be "
+ logd("requestSatelliteEnabled enableSatellite: " + enableSatellite + " cannot be "
+ "processed. Disable satellite is already in progress.");
result.accept(SatelliteManager.SATELLITE_ERROR);
return;
@@ -1763,33 +1821,24 @@ public class SatelliteController extends Handler {
* {@code false} otherwise.
*/
public boolean setSatelliteServicePackageName(@Nullable String servicePackageName) {
- boolean result = mSatelliteModemInterface.setSatelliteServicePackageName(
- servicePackageName);
- if (result) {
- logd("setSatelliteServicePackageName: Resetting cached states");
+ if (!isMockModemAllowed()) return false;
- // Cached states need to be cleared whenever switching satellite vendor services.
- synchronized (mIsSatelliteSupportedLock) {
- mIsSatelliteSupported = null;
- }
- synchronized (mIsSatelliteProvisionedLock) {
- mIsSatelliteProvisioned = null;
- }
- synchronized (mIsSatelliteEnabledLock) {
- mIsSatelliteEnabled = null;
- }
- synchronized (mSatelliteCapabilitiesLock) {
- mSatelliteCapabilities = null;
- }
- ResultReceiver receiver = new ResultReceiver(this) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- logd("requestIsSatelliteSupported: resultCode=" + resultCode);
- }
- };
- requestIsSatelliteSupported(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, receiver);
+ // Cached states need to be cleared whenever switching satellite vendor services.
+ logd("setSatelliteServicePackageName: Resetting cached states");
+ synchronized (mIsSatelliteSupportedLock) {
+ mIsSatelliteSupported = null;
+ }
+ synchronized (mIsSatelliteProvisionedLock) {
+ mIsSatelliteProvisioned = null;
+ }
+ synchronized (mIsSatelliteEnabledLock) {
+ mIsSatelliteEnabled = null;
}
- return result;
+ synchronized (mSatelliteCapabilitiesLock) {
+ mSatelliteCapabilities = null;
+ }
+ mSatelliteModemInterface.setSatelliteServicePackageName(servicePackageName);
+ return true;
}
/**
@@ -1891,6 +1940,40 @@ public class SatelliteController extends Handler {
}
/**
+ * @return The list of satellite PLMNs used for connecting to satellite networks.
+ */
+ @NonNull
+ public List<String> getSatellitePlmnList() {
+ return new ArrayList<>(mSatellitePlmnList);
+ }
+
+ /**
+ * @param subId Subscription ID.
+ * @param plmn The satellite roaming plmn.
+ * @return The list of services supported by the carrier associated with the {@code subId} for
+ * the satellite network {@code plmn}.
+ */
+ @NonNull
+ public List<Integer> getSupportedSatelliteServices(int subId, String plmn) {
+ synchronized (mSupportedSatelliteServicesLock) {
+ if (mSupportedSatelliteServices.containsKey(subId)) {
+ Map<String, Set<Integer>> supportedServices =
+ mSupportedSatelliteServices.get(subId);
+ if (supportedServices != null && supportedServices.containsKey(plmn)) {
+ return new ArrayList<>(supportedServices.get(plmn));
+ } else {
+ loge("getSupportedSatelliteServices: subId=" + subId + ", supportedServices "
+ + "does not contain key plmn=" + plmn);
+ }
+ } else {
+ loge("getSupportedSatelliteServices: mSupportedSatelliteServices does contain key"
+ + " subId=" + subId);
+ }
+ return new ArrayList<>();
+ }
+ }
+
+ /**
* If we have not successfully queried the satellite modem for its satellite service support,
* we will retry the query one more time. Otherwise, we will return the cached result.
*/
@@ -2045,13 +2128,23 @@ public class SatelliteController extends Handler {
private void handleSatelliteEnabled(SatelliteControllerHandlerRequest request) {
RequestSatelliteEnabledArgument argument =
(RequestSatelliteEnabledArgument) request.argument;
+ Phone phone = request.phone;
+
+ if (!argument.enableSatellite && (mSatelliteModemInterface.isSatelliteServiceSupported()
+ || phone != null)) {
+ synchronized (mIsSatelliteEnabledLock) {
+ mWaitingForDisableSatelliteModemResponse = true;
+ mWaitingForSatelliteModemOff = true;
+ }
+ }
+
Message onCompleted = obtainMessage(EVENT_SET_SATELLITE_ENABLED_DONE, request);
if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
mSatelliteModemInterface.requestSatelliteEnabled(argument.enableSatellite,
argument.enableDemoMode, onCompleted);
return;
}
- Phone phone = request.phone;
+
if (phone != null) {
phone.setSatellitePower(onCompleted, argument.enableSatellite);
} else {
@@ -2190,15 +2283,31 @@ public class SatelliteController extends Handler {
private void handleEventSatelliteModemStateChanged(
@SatelliteManager.SatelliteModemState int state) {
logd("handleEventSatelliteModemStateChanged: state=" + state);
- if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF
- || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
- setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
- setDemoModeEnabled(false);
- updateSatelliteEnabledState(
- false, "handleEventSatelliteModemStateChanged");
- cleanUpResources(state);
+ if (state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
+ || state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
+ synchronized (mIsSatelliteEnabledLock) {
+ if ((state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE)
+ || ((mIsSatelliteEnabled == null || isSatelliteEnabled())
+ && !mWaitingForDisableSatelliteModemResponse)) {
+ int error = (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF)
+ ? SatelliteManager.SATELLITE_ERROR_NONE
+ : SatelliteManager.SATELLITE_INVALID_MODEM_STATE;
+ Consumer<Integer> callback = null;
+ synchronized (mSatelliteEnabledRequestLock) {
+ if (mSatelliteEnabledRequest != null) {
+ callback = mSatelliteEnabledRequest.callback;
+ }
+ }
+ moveSatelliteToOffStateAndCleanUpResources(error, callback);
+ } else {
+ logd("Either waiting for the response of disabling satellite modem or the event"
+ + " should be ignored because isSatelliteEnabled="
+ + isSatelliteEnabled()
+ + ", mIsSatelliteEnabled=" + mIsSatelliteEnabled);
+ }
+ mWaitingForSatelliteModemOff = false;
+ }
}
-
mDatagramController.onSatelliteModemStateChanged(state);
}
@@ -2252,16 +2361,17 @@ public class SatelliteController extends Handler {
}
}
- private void cleanUpResources(@SatelliteManager.SatelliteModemState int state) {
- logd("cleanUpResources");
- if (state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
- synchronized (mSatelliteEnabledRequestLock) {
- if (mSatelliteEnabledRequest != null) {
- mSatelliteEnabledRequest.callback.accept(
- SatelliteManager.SATELLITE_INVALID_MODEM_STATE);
- }
- }
+ private void moveSatelliteToOffStateAndCleanUpResources(
+ @SatelliteManager.SatelliteError int error, @Nullable Consumer<Integer> callback) {
+ logd("moveSatelliteToOffStateAndCleanUpResources");
+ synchronized (mIsSatelliteEnabledLock) {
resetSatelliteEnabledRequest();
+ setDemoModeEnabled(false);
+ mIsSatelliteEnabled = false;
+ setSettingsKeyForSatelliteMode(SATELLITE_MODE_ENABLED_FALSE);
+ if (callback != null) callback.accept(error);
+ updateSatelliteEnabledState(
+ false, "moveSatelliteToOffStateAndCleanUpResources");
}
}
@@ -2270,6 +2380,100 @@ public class SatelliteController extends Handler {
mDatagramController.setDemoMode(mIsDemoModeEnabled);
}
+ private boolean isMockModemAllowed() {
+ return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
+ }
+
+ private void updateSupportedSatelliteServicesForActiveSubscriptions() {
+ synchronized (mSupportedSatelliteServicesLock) {
+ mSupportedSatelliteServices.clear();
+ int[] activeSubIds = SubscriptionManagerService.getInstance().getActiveSubIdList(true);
+ if (activeSubIds != null) {
+ for (int subId : activeSubIds) {
+ updateSupportedSatelliteServices(subId);
+ }
+ } else {
+ loge("updateSupportedSatelliteServicesForActiveSubscriptions: "
+ + "activeSubIds is null");
+ }
+ }
+ }
+
+ private void updateSupportedSatelliteServices(int subId) {
+ Map<String, Set<Integer>> carrierSupportedSatelliteServicesPerPlmn =
+ readSupportedSatelliteServicesFromCarrierConfig(subId);
+ synchronized (mSupportedSatelliteServicesLock) {
+ mSupportedSatelliteServices.put(subId,
+ SatelliteServiceUtils.mergeSupportedSatelliteServices(
+ mSatelliteServicesSupportedByProviders,
+ carrierSupportedSatelliteServicesPerPlmn));
+ }
+ }
+
+ @NonNull
+ private Map<String, Set<Integer>> readSupportedSatelliteServicesFromOverlayConfig() {
+ String[] supportedServices = readStringArrayFromOverlayConfig(
+ R.array.config_satellite_services_supported_by_providers);
+ return SatelliteServiceUtils.parseSupportedSatelliteServices(supportedServices);
+ }
+
+ @NonNull
+ private Map<String, Set<Integer>> readSupportedSatelliteServicesFromCarrierConfig(int subId) {
+ synchronized (mCarrierConfigArrayLock) {
+ PersistableBundle config = mCarrierConfigArray.get(subId);
+ if (config == null) {
+ config = getConfigForSubId(subId);
+ mCarrierConfigArray.put(subId, config);
+ }
+ return SatelliteServiceUtils.parseSupportedSatelliteServices(
+ config.getPersistableBundle(CarrierConfigManager
+ .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE));
+ }
+ }
+
+ @NonNull private PersistableBundle getConfigForSubId(int subId) {
+ PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId,
+ CarrierConfigManager
+ .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE);
+ if (config == null || config.isEmpty()) {
+ config = CarrierConfigManager.getDefaultConfig();
+ }
+ return config;
+ }
+
+ private void handleCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+ int specificCarrierId) {
+ logd("handleCarrierConfigChanged(): slotIndex(" + slotIndex + "), subId("
+ + subId + "), carrierId(" + carrierId + "), specificCarrierId("
+ + specificCarrierId + ")");
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return;
+ }
+
+ updateCarrierConfig(subId);
+ updateSupportedSatelliteServicesForActiveSubscriptions();
+ }
+
+ private void updateCarrierConfig(int subId) {
+ synchronized (mCarrierConfigArrayLock) {
+ mCarrierConfigArray.put(subId, getConfigForSubId(subId));
+ }
+ }
+
+ @NonNull
+ private String[] readStringArrayFromOverlayConfig(@ArrayRes int id) {
+ String[] strArray = null;
+ try {
+ strArray = mContext.getResources().getStringArray(id);
+ } catch (Resources.NotFoundException ex) {
+ loge("readStringArrayFromOverlayConfig: id= " + id + ", ex=" + ex);
+ }
+ if (strArray == null) {
+ strArray = new String[0];
+ }
+ return strArray;
+ }
+
private static void logd(@NonNull String log) {
Rlog.d(TAG, log);
}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 80c67b315a..ebf7780a49 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -24,14 +24,12 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.os.AsyncResult;
import android.os.Binder;
-import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RegistrantList;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.telephony.Rlog;
import android.telephony.satellite.SatelliteCapabilities;
import android.telephony.satellite.SatelliteDatagram;
@@ -57,8 +55,6 @@ import java.util.Arrays;
*/
public class SatelliteModemInterface {
private static final String TAG = "SatelliteModemInterface";
- private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
- private static final boolean DEBUG = !"user".equals(Build.TYPE);
private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds
private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute
private static final int REBIND_MULTIPLIER = 2;
@@ -1016,13 +1012,7 @@ public class SatelliteModemInterface {
* {@code false} otherwise.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public boolean setSatelliteServicePackageName(@Nullable String servicePackageName) {
- if (!shouldAllowModifyingSatelliteServicePackageName()) {
- loge("setSatelliteServicePackageName: modifying satellite service package name "
- + "is not allowed");
- return false;
- }
-
+ public void setSatelliteServicePackageName(@Nullable String servicePackageName) {
logd("setSatelliteServicePackageName: config_satellite_service_package is "
+ "updated, new packageName=" + servicePackageName);
mExponentialBackoff.stop();
@@ -1042,8 +1032,6 @@ public class SatelliteModemInterface {
mIsSatelliteServiceSupported = getSatelliteServiceSupport();
bindService();
mExponentialBackoff.start();
-
- return true;
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -1055,10 +1043,6 @@ public class SatelliteModemInterface {
message.sendToTarget();
}
- private boolean shouldAllowModifyingSatelliteServicePackageName() {
- return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
- }
-
private static void logd(@NonNull String log) {
Rlog.d(TAG, log);
}
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
index f11ca661de..151b69dd77 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -16,11 +16,16 @@
package com.android.internal.telephony.satellite;
+import static android.telephony.NetworkRegistrationInfo.FIRST_SERVICE_TYPE;
+import static android.telephony.NetworkRegistrationInfo.LAST_SERVICE_TYPE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.AsyncResult;
import android.os.Binder;
+import android.os.PersistableBundle;
+import android.telephony.NetworkRegistrationInfo;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.satellite.AntennaPosition;
@@ -40,7 +45,9 @@ import com.android.internal.telephony.subscription.SubscriptionManagerService;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -267,6 +274,127 @@ public class SatelliteServiceUtils {
}
/**
+ * Expected format of each input string in the array: "PLMN_1:service_1,service_2,..."
+ *
+ * @return The map of supported services with key: PLMN, value: set of services supported by
+ * the PLMN.
+ */
+ @NonNull
+ @NetworkRegistrationInfo.ServiceType
+ public static Map<String, Set<Integer>> parseSupportedSatelliteServices(
+ String[] supportedSatelliteServicesStrArray) {
+ Map<String, Set<Integer>> supportedServicesMap = new HashMap<>();
+ if (supportedSatelliteServicesStrArray == null
+ || supportedSatelliteServicesStrArray.length == 0) {
+ return supportedServicesMap;
+ }
+
+ for (String supportedServicesPerPlmnStr : supportedSatelliteServicesStrArray) {
+ String[] pairOfPlmnAndsupportedServicesStr =
+ supportedServicesPerPlmnStr.split(":");
+ if (pairOfPlmnAndsupportedServicesStr != null
+ && (pairOfPlmnAndsupportedServicesStr.length == 1
+ || pairOfPlmnAndsupportedServicesStr.length == 2)) {
+ String plmn = pairOfPlmnAndsupportedServicesStr[0];
+ Set<Integer> supportedServicesSet = new HashSet<>();
+ if (pairOfPlmnAndsupportedServicesStr.length == 2) {
+ String[] supportedServicesStrArray =
+ pairOfPlmnAndsupportedServicesStr[1].split(",");
+ for (String service : supportedServicesStrArray) {
+ try {
+ int serviceType = Integer.parseInt(service);
+ if (isServiceTypeValid(serviceType)) {
+ supportedServicesSet.add(serviceType);
+ } else {
+ loge("parseSupportedSatelliteServices: invalid serviceType="
+ + serviceType);
+ }
+ } catch (NumberFormatException e) {
+ loge("parseSupportedSatelliteServices: supportedServicesPerPlmnStr="
+ + supportedServicesPerPlmnStr + ", service=" + service
+ + ", e=" + e);
+ }
+ }
+ }
+ supportedServicesMap.put(plmn, supportedServicesSet);
+ } else {
+ loge("parseSupportedSatelliteServices: invalid format input, "
+ + "supportedServicesPerPlmnStr=" + supportedServicesPerPlmnStr);
+ }
+ }
+ return supportedServicesMap;
+ }
+
+ /**
+ * Expected format of the input dictionary bundle is:
+ * <ul>
+ * <li>Key: PLMN string.</li>
+ * <li>Value: A string with format "service_1,service_2,..."</li>
+ * </ul>
+ * @return The map of supported services with key: PLMN, value: set of services supported by
+ * the PLMN.
+ */
+ @NonNull
+ @NetworkRegistrationInfo.ServiceType
+ public static Map<String, Set<Integer>> parseSupportedSatelliteServices(
+ PersistableBundle supportedServicesBundle) {
+ Map<String, Set<Integer>> supportedServicesMap = new HashMap<>();
+ if (supportedServicesBundle == null || supportedServicesBundle.isEmpty()) {
+ return supportedServicesMap;
+ }
+
+ for (String plmn : supportedServicesBundle.keySet()) {
+ Set<Integer> supportedServicesSet = new HashSet<>();
+ for (int serviceType : supportedServicesBundle.getIntArray(plmn)) {
+ if (isServiceTypeValid(serviceType)) {
+ supportedServicesSet.add(serviceType);
+ } else {
+ loge("parseSupportedSatelliteServices: invalid service type=" + serviceType
+ + " for plmn=" + plmn);
+ }
+ }
+ supportedServicesMap.put(plmn, supportedServicesSet);
+ }
+ return supportedServicesMap;
+ }
+
+ /**
+ * For the PLMN that exists in both {@code providerSupportedServices} and
+ * {@code carrierSupportedServices}, the supported services will be the intersection of the two
+ * sets. For the PLMN that is present in {@code providerSupportedServices} but not in
+ * {@code carrierSupportedServices}, the provider supported services will be used. The rest
+ * will not be used.
+ *
+ * @param providerSupportedServices Satellite provider supported satellite services.
+ * @param carrierSupportedServices Carrier supported satellite services.
+ * @return The supported satellite services by the device for the corresponding carrier and the
+ * satellite provider.
+ */
+ @NonNull
+ @NetworkRegistrationInfo.ServiceType
+ public static Map<String, Set<Integer>> mergeSupportedSatelliteServices(
+ @NonNull @NetworkRegistrationInfo.ServiceType Map<String, Set<Integer>>
+ providerSupportedServices,
+ @NonNull @NetworkRegistrationInfo.ServiceType Map<String, Set<Integer>>
+ carrierSupportedServices) {
+ Map<String, Set<Integer>> supportedServicesMap = new HashMap<>();
+ for (Map.Entry<String, Set<Integer>> entry : providerSupportedServices.entrySet()) {
+ Set<Integer> supportedServices = new HashSet<>(entry.getValue());
+ if (carrierSupportedServices.containsKey(entry.getKey())) {
+ supportedServices.retainAll(carrierSupportedServices.get(entry.getKey()));
+ }
+ if (!supportedServices.isEmpty()) {
+ supportedServicesMap.put(entry.getKey(), supportedServices);
+ }
+ }
+ return supportedServicesMap;
+ }
+
+ private static boolean isServiceTypeValid(int serviceType) {
+ return (serviceType >= FIRST_SERVICE_TYPE && serviceType <= LAST_SERVICE_TYPE);
+ }
+
+ /**
* Return phone associated with phoneId 0.
*
* @return phone associated with phoneId 0 or {@code null} if it doesn't exist.
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
index b90dc5edf5..124437cd6b 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -2018,21 +2018,21 @@ public class SubscriptionDatabaseManager extends Handler {
}
/**
- * Reload the database from content provider to the cache.
+ * Reload the database from content provider to the cache. This must be a synchronous operation
+ * to prevent cache/database out-of-sync. Callers should be cautious to call this method because
+ * it might take longer time to complete.
*/
- public void reloadDatabase() {
- if (mAsyncMode) {
- post(this::loadDatabaseInternal);
- } else {
- loadDatabaseInternal();
- }
+ public void reloadDatabaseSync() {
+ logl("reloadDatabaseSync");
+ // Synchronously load the database into the cache.
+ loadDatabaseInternal();
}
/**
* Load the database from content provider to the cache.
*/
private void loadDatabaseInternal() {
- log("loadDatabaseInternal");
+ logl("loadDatabaseInternal");
try (Cursor cursor = mContext.getContentResolver().query(
SimInfo.CONTENT_URI, null, null, null, null)) {
mReadWriteLock.writeLock().lock();
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index a3377a277b..ba69d8a7c0 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -1069,6 +1069,8 @@ public class SubscriptionManagerService extends ISub.Stub {
int subId = insertSubscriptionInfo(embeddedProfile.getIccid(),
SubscriptionManager.INVALID_SIM_SLOT_INDEX,
null, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ mSubscriptionDatabaseManager.setDisplayName(subId, mContext.getResources()
+ .getString(R.string.default_card_name, subId));
subInfo = mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subId);
}
@@ -1116,6 +1118,7 @@ public class SubscriptionManagerService extends ISub.Stub {
// CARD_ID field should not contain the EID
if (cardId >= 0 && mUiccController.getCardIdForDefaultEuicc()
!= TelephonyManager.UNSUPPORTED_CARD_ID) {
+ builder.setCardId(cardId);
builder.setCardString(mUiccController.convertToCardString(cardId));
}
@@ -1345,6 +1348,8 @@ public class SubscriptionManagerService extends ISub.Stub {
// This is a new SIM card. Insert a new record.
subId = insertSubscriptionInfo(iccId, phoneId, null,
SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ mSubscriptionDatabaseManager.setDisplayName(subId,
+ mContext.getResources().getString(R.string.default_card_name, subId));
} else {
subId = subInfo.getSubscriptionId();
log("updateSubscription: Found existing subscription. subId= " + subId
@@ -1421,12 +1426,15 @@ public class SubscriptionManagerService extends ISub.Stub {
}
// Attempt to restore SIM specific settings when SIM is loaded.
- mContext.getContentResolver().call(
+ Bundle result = mContext.getContentResolver().call(
SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
iccId, null);
- log("Reload the database.");
- mSubscriptionDatabaseManager.reloadDatabase();
+ if (result != null && result.getBoolean(
+ SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_DATABASE_UPDATED)) {
+ logl("Sim specific settings changed the database.");
+ mSubscriptionDatabaseManager.reloadDatabaseSync();
+ }
}
log("updateSubscription: " + mSubscriptionDatabaseManager
@@ -3562,10 +3570,10 @@ public class SubscriptionManagerService extends ISub.Stub {
*
* @param subId the unique SubscriptionInfo index in database
* @return userHandle associated with this subscription
- * or {@code null} if subscription is not associated with any user.
+ * or {@code null} if subscription is not associated with any user
+ * or {code null} if subscripiton is not available on the device.
*
* @throws SecurityException if doesn't have required permission.
- * @throws IllegalArgumentException if {@code subId} is invalid.
*/
@Override
@Nullable
@@ -3578,8 +3586,7 @@ public class SubscriptionManagerService extends ISub.Stub {
SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
.getSubscriptionInfoInternal(subId);
if (subInfo == null) {
- throw new IllegalArgumentException("getSubscriptionUserHandle: Invalid subId: "
- + subId);
+ return null;
}
UserHandle userHandle = UserHandle.of(subInfo.getUserId());
@@ -3727,12 +3734,16 @@ public class SubscriptionManagerService extends ISub.Stub {
Bundle bundle = new Bundle();
bundle.putByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
logl("restoreAllSimSpecificSettingsFromBackup");
- mContext.getContentResolver().call(
+ Bundle result = mContext.getContentResolver().call(
SubscriptionManager.SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
null, bundle);
- // After restoring, we need to reload the content provider into the cache.
- mSubscriptionDatabaseManager.reloadDatabase();
+
+ if (result != null && result.getBoolean(
+ SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_DATABASE_UPDATED)) {
+ logl("Sim specific settings changed the database.");
+ mSubscriptionDatabaseManager.reloadDatabaseSync();
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
index be045c4c92..680af46ccc 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
@@ -106,14 +106,21 @@ public class UiccPkcs15 extends Handler {
mCallback.sendToTarget();
return;
}
-
+ IccIoResult response;
switch (msg.what) {
case EVENT_SELECT_FILE_DONE:
- readBinary();
+ response = (IccIoResult) ar.result;
+ if (response.getException() == null) {
+ readBinary();
+ } else {
+ log("Select file error : " + response.getException());
+ AsyncResult.forMessage(mCallback, null, response.getException());
+ mCallback.sendToTarget();
+ }
break;
case EVENT_READ_BINARY_DONE:
- IccIoResult response = (IccIoResult) ar.result;
+ response = (IccIoResult) ar.result;
String result = IccUtils.bytesToHexString(response.payload)
.toUpperCase(Locale.US);
log("IccIoResult: " + response + " payload: " + result);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index fadbff185c..dfb91a52b1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -16,12 +16,15 @@
package com.android.internal.telephony;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -97,6 +100,7 @@ public class CarrierServiceStateTrackerTest extends TelephonyTest {
private void setDefaultValues() {
mBundle.putInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, 0);
mBundle.putInt(CarrierConfigManager.KEY_EMERGENCY_NOTIFICATION_DELAY_INT, 0);
+ mBundle.putBoolean(CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
}
@After
@@ -232,4 +236,39 @@ public class CarrierServiceStateTrackerTest extends TelephonyTest {
verify(mNotificationManager, atLeast(2)).cancel(
CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG, SUB_ID);
}
+
+ @Test
+ public void testSetEnabledNotifications() {
+ logd(LOG_TAG + ":testSetEnabledNotifications()");
+
+ mBundle.putBoolean(CarrierConfigManager.KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, true);
+
+ Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
+ doReturn(mNotificationBuilder).when(mSpyCarrierSST).getNotificationBuilder(any());
+ doReturn(mNotificationManager).when(mSpyCarrierSST).getNotificationManager(any());
+ doReturn(true).when(mPhone).isWifiCallingEnabled(); // notifiable for emergency
+ mCarrierConfigChangeListener.onCarrierConfigChanged(0 /* slotIndex */, SUB_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
+ processAllMessages();
+
+ Map<Integer, CarrierServiceStateTracker.NotificationType> notificationTypeMap =
+ mCarrierSST.getNotificationTypeMap();
+ CarrierServiceStateTracker.NotificationType prefNetworkNotification =
+ notificationTypeMap.get(CarrierServiceStateTracker.NOTIFICATION_PREF_NETWORK);
+ CarrierServiceStateTracker.NotificationType emergencyNetworkNotification =
+ notificationTypeMap.get(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK);
+ assertFalse(prefNetworkNotification.isEnabled());
+ assertTrue(emergencyNetworkNotification.isEnabled());
+
+ verify(mNotificationManager, never()).notify(
+ eq(CarrierServiceStateTracker.PREF_NETWORK_NOTIFICATION_TAG),
+ eq(SUB_ID), isA(Notification.class));
+ verify(mNotificationManager, atLeast(1)).cancel(
+ CarrierServiceStateTracker.PREF_NETWORK_NOTIFICATION_TAG, SUB_ID);
+ verify(mNotificationManager, atLeast(1)).notify(
+ eq(CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG),
+ eq(SUB_ID), isA(Notification.class));
+ verify(mNotificationManager, never()).cancel(
+ CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG, SUB_ID);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 3c8db999fa..ea19b62625 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -783,7 +783,7 @@ public class ContextFixture implements TestFixture<Context> {
doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
doAnswer(invocation -> mNetworkId++).when(mNetwork).getNetId();
doReturn(mNetwork).when(mConnectivityManager).registerNetworkAgent(
- any(), any(), any(), any(), any(), any(), any(), anyInt());
+ any(), any(), any(), any(), any(), any(), anyInt());
doReturn(true).when(mEuiccManager).isEnabled();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index c5f20e39a4..465880aa7d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -2211,7 +2211,6 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
verify(mMockCi, times(1)).setNullCipherAndIntegrityEnabled(anyBoolean(),
any(Message.class));
- // Some ephemeral error occurred in the modem, but the feature was supported
mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE,
new AsyncResult(null, null,
new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED))));
@@ -2220,6 +2219,28 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
}
@Test
+ public void testHandleNullCipherAndIntegrityEnabled_radioUnavailable() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
+ TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, Boolean.TRUE.toString(),
+ false);
+ mPhoneUT.mCi = mMockCi;
+ assertFalse(mPhoneUT.isNullCipherAndIntegritySupported());
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_RADIO_AVAILABLE,
+ new AsyncResult(null, new int[]{ServiceState.RIL_RADIO_TECHNOLOGY_GSM}, null)));
+ processAllMessages();
+
+ verify(mMockCi, times(1)).setNullCipherAndIntegrityEnabled(anyBoolean(),
+ any(Message.class));
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE,
+ new AsyncResult(null, null,
+ new CommandException(CommandException.Error.RADIO_NOT_AVAILABLE))));
+ processAllMessages();
+ assertFalse(mPhoneUT.isNullCipherAndIntegritySupported());
+ }
+
+ @Test
public void testHandleNullCipherAndIntegrityEnabled_radioSupportsFeature() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CELLULAR_SECURITY,
TelephonyManager.PROPERTY_ENABLE_NULL_CIPHER_TOGGLE, Boolean.TRUE.toString(),
@@ -2234,7 +2255,6 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
verify(mMockCi, times(1)).setNullCipherAndIntegrityEnabled(anyBoolean(),
any(Message.class));
- // Some ephemeral error occurred in the modem, but the feature was supported
mPhoneUT.sendMessage(mPhoneUT.obtainMessage(EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE,
new AsyncResult(null, null, null)));
processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
index 1c44772d88..fe1404bcef 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
@@ -342,9 +342,6 @@ public class ImsSmsDispatcherTest extends TelephonyTest {
public void testFallbackGsmRetrywithMessageRef() throws Exception {
int token = mImsSmsDispatcher.mNextToken.get();
int messageRef = mImsSmsDispatcher.nextMessageRef();
- if (mImsSmsDispatcher.isMessageRefIncrementViaTelephony()) {
- messageRef += 1;
- }
when(mImsManager.getSmsFormat()).thenReturn(SmsMessage.FORMAT_3GPP);
when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
@@ -363,11 +360,7 @@ public class ImsSmsDispatcherTest extends TelephonyTest {
ArgumentCaptor<SMSDispatcher.SmsTracker> captor =
ArgumentCaptor.forClass(SMSDispatcher.SmsTracker.class);
verify(mSmsDispatchersController).sendRetrySms(captor.capture());
- if (mImsSmsDispatcher.isMessageRefIncrementViaTelephony()) {
- assertTrue(messageRef + 1 == captor.getValue().mMessageRef);
- } else {
- assertTrue(messageRef == captor.getValue().mMessageRef);
- }
+ assertTrue(messageRef == captor.getValue().mMessageRef);
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
index 19be558088..03b1cfddb8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -319,4 +319,19 @@ public class LocaleTrackerTest extends TelephonyTest {
mLocaleTracker.updateOperatorNumeric(TEST_CELL_MCC + FAKE_MNC);
verify(mNitzStateMachine, times(1)).handleCountryDetected("");
}
+
+ @Test
+ public void testClearCellInfoForLostOperator() {
+ doReturn(true).when(mPhone).isRadioOn();
+ sendServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+ processAllMessages();
+ assertTrue(mLocaleTracker.isTracking());
+ assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
+
+ // airplane mode + VoWiFI case
+ doReturn(false).when(mPhone).isRadioOn();
+ sendServiceState(ServiceState.STATE_POWER_OFF);
+ sendServiceState(ServiceState.STATE_IN_SERVICE);
+ assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index f4c19d974a..a41dbe15f7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.verify;
import android.content.Intent;
import android.content.res.Resources;
+import android.os.AsyncResult;
import android.os.HandlerThread;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -59,6 +60,7 @@ import androidx.test.InstrumentationRegistry;
import com.android.internal.telephony.data.DataSettingsManager;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.test.SimulatedCommands;
import org.junit.After;
import org.junit.Assert;
@@ -302,9 +304,18 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
@Test
public void testSubInfoChangeAfterRadioUnavailable() throws Exception {
+ int phone1SubId = 1;
+ int phone2SubId = 2;
+ // Mock DSDS, mock Phone 2
+ SimulatedCommands simulatedCommands2 = mock(SimulatedCommands.class);
+ mPhone2.mCi = simulatedCommands2;
+ doReturn(mDataSettingsManagerMock2).when(mPhone2).getDataSettingsManager();
+ mPhones = new Phone[]{mPhone, mPhone2};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ // Load carrier config for all subs
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
- sendCarrierConfigChanged(0, 1);
- sendCarrierConfigChanged(1, 2);
+ sendCarrierConfigChanged(0, phone1SubId);
+ sendCarrierConfigChanged(1, phone2SubId);
processAllMessages();
// Ensure all subscription loaded only updates state once
@@ -315,7 +326,22 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
verify(mSubscriptionManagerService, never()).setDefaultVoiceSubId(anyInt());
verify(mSubscriptionManagerService, never()).setDefaultSmsSubId(anyInt());
- // Notify radio unavailable.
+ // DSDS -> single active modem, radio available on phone 0 but unavailable on phone 1
+ doReturn(TelephonyManager.RADIO_POWER_UNAVAILABLE).when(simulatedCommands2).getRadioState();
+ markSubscriptionInactive(phone2SubId);
+ AsyncResult result = new AsyncResult(null, 1/*activeModemCount*/, null);
+ clearInvocations(mSubscriptionManagerService);
+ mMultiSimSettingControllerUT.obtainMessage(
+ MultiSimSettingController.EVENT_MULTI_SIM_CONFIG_CHANGED, result).sendToTarget();
+ mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
+ processAllMessages();
+
+ // Should still set defaults to the only remaining sub
+ verify(mSubscriptionManagerService).setDefaultDataSubId(phone1SubId);
+ verify(mSubscriptionManagerService).setDefaultVoiceSubId(phone1SubId);
+ verify(mSubscriptionManagerService).setDefaultSmsSubId(phone1SubId);
+
+ // Notify radio unavailable on all subs.
replaceInstance(BaseCommands.class, "mState", mSimulatedCommands,
TelephonyManager.RADIO_POWER_UNAVAILABLE);
mMultiSimSettingControllerUT.obtainMessage(
@@ -323,7 +349,6 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
// Mark all subs as inactive.
markSubscriptionInactive(1);
- markSubscriptionInactive(2);
clearInvocations(mSubscriptionManagerService);
// The below sub info change should be ignored.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java
index 8dc8ea710f..f5bdd27b6b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkRegistrationInfoTest.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import android.compat.testing.PlatformCompatChangeRule;
@@ -54,6 +56,7 @@ public class NetworkRegistrationInfoTest {
.setAvailableServices(Arrays.asList(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
.setCellIdentity(new CellIdentityLte())
.setRegisteredPlmn("12345")
+ .setIsNonTerrestrialNetwork(true)
.build();
Parcel p = Parcel.obtain();
@@ -70,6 +73,7 @@ public class NetworkRegistrationInfoTest {
public void testDefaultValues() {
NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder().build();
assertEquals("", nri.getRegisteredPlmn());
+ assertThat(nri.isNonTerrestrialNetwork()).isEqualTo(false);
}
@Test
@@ -77,6 +81,8 @@ public class NetworkRegistrationInfoTest {
public void testBuilder() {
assertEquals("12345", new NetworkRegistrationInfo.Builder()
.setRegisteredPlmn("12345").build().getRegisteredPlmn());
+ assertThat(new NetworkRegistrationInfo.Builder().setIsNonTerrestrialNetwork(true).build()
+ .isNonTerrestrialNetwork()).isEqualTo(true);
}
@Test
@@ -139,4 +145,11 @@ public class NetworkRegistrationInfoTest {
assertEquals(NetworkRegistrationInfo.REGISTRATION_STATE_EMERGENCY,
nri.getRegistrationState());
}
+
+ @Test
+ public void testSetIsNonTerrestrialNetwork() {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder().build();
+ nri.setIsNonTerrestrialNetwork(true);
+ assertThat(nri.isNonTerrestrialNetwork()).isEqualTo(true);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index 4b91207e31..7f15af8051 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -34,6 +34,7 @@ import android.os.Looper;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhysicalChannelConfig;
import android.telephony.RadioAccessFamily;
@@ -282,7 +283,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(false);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -330,7 +331,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(true);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_con", getCurrentState().getName());
@@ -386,7 +387,9 @@ public class NetworkTypeControllerTest extends TelephonyTest {
mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
new int[]{41});
PhysicalChannelConfig physicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(1)
.setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
.setBand(41)
.build();
List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
@@ -394,7 +397,36 @@ public class NetworkTypeControllerTest extends TelephonyTest {
doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
sendCarrierConfigChanged();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateNrConnectedMmwaveWithAdditionalBandAndNoMmwaveNrNsa()
+ throws Exception {
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
+ mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
+ new int[]{41});
+ PhysicalChannelConfig ltePhysicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(1)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .build();
+ PhysicalChannelConfig nrPhysicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setPhysicalCellId(2)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setCellConnectionStatus(CellInfo.CONNECTION_SECONDARY_SERVING)
+ .setBand(41)
+ .build();
+ List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+ lastPhysicalChannelConfigList.add(ltePhysicalChannelConfig);
+ lastPhysicalChannelConfigList.add(nrPhysicalChannelConfig);
+ doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
+ sendCarrierConfigChanged();
+
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
@@ -417,7 +449,6 @@ public class NetworkTypeControllerTest extends TelephonyTest {
doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
sendCarrierConfigChanged();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("connected", getCurrentState().getName());
@@ -535,6 +566,169 @@ public class NetworkTypeControllerTest extends TelephonyTest {
}
@Test
+ public void testEventPhysicalChannelConfigChangedWithRatcheting() throws Exception {
+ testTransitionToCurrentStateNrConnected();
+ mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
+ new int[]{41, 77});
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
+ mBundle.putBoolean(CarrierConfigManager.KEY_RATCHET_NR_ADVANCED_BANDWIDTH_IF_RRC_IDLE_BOOL,
+ true);
+ sendCarrierConfigChanged();
+
+ // Primary serving NR PCC with cell ID = 1, band = none, bandwidth = 200000
+ PhysicalChannelConfig pcc1 = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setPhysicalCellId(1)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .setCellBandwidthDownlinkKhz(19999)
+ .build();
+ // Secondary serving NR PCC with cell ID = 2, band = 41, bandwidth = 10000
+ PhysicalChannelConfig pcc2 = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setPhysicalCellId(2)
+ .setCellConnectionStatus(CellInfo.CONNECTION_SECONDARY_SERVING)
+ .setCellBandwidthDownlinkKhz(10000)
+ .setBand(41)
+ .build();
+ // Primary serving NR PCC with cell ID = 3, band = 77, bandwidth = 0
+ PhysicalChannelConfig pcc3 = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setPhysicalCellId(3)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .setBand(77)
+ .build();
+
+ List<PhysicalChannelConfig> physicalChannelConfigs = new ArrayList<>();
+ physicalChannelConfigs.add(pcc1);
+ physicalChannelConfigs.add(pcc2);
+ doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
+
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+
+ // bands and bandwidths should stay ratcheted even if an empty PCC list is sent
+ doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+
+ // bands and bandwidths should stay ratcheted as long as anchor NR cell is the same
+ physicalChannelConfigs.remove(pcc2);
+ doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+
+ // bands and bandwidths should no longer be ratcheted if anchor NR cell changes
+ // add pcc3 to front of list to ensure anchor NR cell changes from 1 -> 3
+ physicalChannelConfigs.add(0, pcc3);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected", getCurrentState().getName());
+
+ physicalChannelConfigs.add(pcc2);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ }
+
+ @Test
+ public void testEventPhysicalChannelConfigChangedWithoutRatcheting() throws Exception {
+ testTransitionToCurrentStateNrConnected();
+ mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
+ new int[]{41, 77});
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
+ sendCarrierConfigChanged();
+
+ // Primary serving NR PCC with cell ID = 1, band = none, bandwidth = 200000
+ PhysicalChannelConfig pcc1 = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setPhysicalCellId(1)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .setCellBandwidthDownlinkKhz(19999)
+ .build();
+ // Secondary serving NR PCC with cell ID = 2, band = 41, bandwidth = 10000
+ PhysicalChannelConfig pcc2 = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setPhysicalCellId(2)
+ .setCellConnectionStatus(CellInfo.CONNECTION_SECONDARY_SERVING)
+ .setCellBandwidthDownlinkKhz(10000)
+ .setBand(41)
+ .build();
+
+ List<PhysicalChannelConfig> physicalChannelConfigs = new ArrayList<>();
+ physicalChannelConfigs.add(pcc1);
+ physicalChannelConfigs.add(pcc2);
+ doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
+
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+
+ // bands and bandwidths should stay ratcheted even if an empty PCC list is sent
+ doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+
+ // bands and bandwidths should change if PCC list changes
+ physicalChannelConfigs.remove(pcc2);
+ doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected", getCurrentState().getName());
+ }
+
+ @Test
+ public void testEventPhysicalChannelConfigChangedUsingUserDataForRrc() throws Exception {
+ testTransitionToCurrentStateNrConnected();
+ mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
+ new int[]{41, 77});
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
+ mBundle.putBoolean(CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL,
+ true);
+ sendCarrierConfigChanged();
+
+ // Primary serving NR PCC with cell ID = 1, band = none, bandwidth = 200000
+ PhysicalChannelConfig pcc1 = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setPhysicalCellId(1)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .setCellBandwidthDownlinkKhz(19999)
+ .build();
+ // Secondary serving NR PCC with cell ID = 2, band = 41, bandwidth = 10000
+ PhysicalChannelConfig pcc2 = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setPhysicalCellId(2)
+ .setCellConnectionStatus(CellInfo.CONNECTION_SECONDARY_SERVING)
+ .setCellBandwidthDownlinkKhz(10000)
+ .setBand(41)
+ .build();
+
+ List<PhysicalChannelConfig> physicalChannelConfigs = new ArrayList<>();
+ physicalChannelConfigs.add(pcc1);
+ physicalChannelConfigs.add(pcc2);
+ doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
+
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+
+ // bands and bandwidths should not stay the same even if an empty PCC list is sent
+ doReturn(new ArrayList<>()).when(mSST).getPhysicalChannelConfigList();
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected", getCurrentState().getName());
+
+ // bands and bandwidths should change if PCC list changes
+ doReturn(physicalChannelConfigs).when(mSST).getPhysicalChannelConfigList();
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ }
+
+ @Test
public void testNrPhysicalChannelChangeFromNrConnectedMmwaveToLteConnected() throws Exception {
testTransitionToCurrentStateNrConnectedMmwave();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
@@ -556,7 +750,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
testTransitionToCurrentStateNrConnectedMmwave();
doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
setPhysicalLinkStatus(true);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
@@ -592,7 +786,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -620,7 +814,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
// LTE -> LTE+
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -648,7 +842,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
// LTE -> LTE+
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
@@ -676,7 +870,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
setPhysicalLinkStatus(false);
- mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED */);
+ mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */);
mNetworkTypeController.sendMessage(3 /* EVENT_SERVICE_STATE_CHANGED */);
processAllMessages();
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
@@ -1277,27 +1471,9 @@ public class NetworkTypeControllerTest extends TelephonyTest {
List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
lastPhysicalChannelConfigList.add(new PhysicalChannelConfig.Builder()
.setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
- .setCellBandwidthDownlinkKhz(20001)
- .build());
- doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
- mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
- sendCarrierConfigChanged();
-
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
- processAllMessages();
- assertEquals("connected_mmwave", getCurrentState().getName());
- }
-
- @Test
- public void testTransitionToCurrentStateNrConnectedWithHighBandwidthIncludingLte()
- throws Exception {
- assertEquals("DefaultState", getCurrentState().getName());
- doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
- doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
- List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
- lastPhysicalChannelConfigList.add(new PhysicalChannelConfig.Builder()
- .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
- .setCellBandwidthDownlinkKhz(20000)
+ .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+ .setPhysicalCellId(1)
+ .setCellBandwidthDownlinkKhz(19999)
.build());
lastPhysicalChannelConfigList.add(new PhysicalChannelConfig.Builder()
.setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
@@ -1305,13 +1481,13 @@ public class NetworkTypeControllerTest extends TelephonyTest {
.build());
doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 20000);
+ sendCarrierConfigChanged();
+ assertEquals("connected", getCurrentState().getName());
+
mBundle.putBoolean(
CarrierConfigManager.KEY_INCLUDE_LTE_FOR_NR_ADVANCED_THRESHOLD_BANDWIDTH_BOOL,
true);
sendCarrierConfigChanged();
-
- mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
- processAllMessages();
assertEquals("connected_mmwave", getCurrentState().getName());
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
index 500d69cb44..7efb886bc9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
@@ -18,6 +18,8 @@ package com.android.internal.telephony;
import static android.telephony.ServiceState.UNKNOWN_ID;
+import static com.google.common.truth.Truth.assertThat;
+
import android.os.Bundle;
import android.os.Parcel;
import android.telephony.AccessNetworkConstants;
@@ -444,6 +446,18 @@ public class ServiceStateTest extends TestCase {
assertEquals(UNKNOWN_ID, coarseLocationSanitizedSs.getCdmaNetworkId());
}
+ @SmallTest
+ public void testIsUsingNonTerrestrialNetwork() {
+ ServiceState ss = new ServiceState();
+ assertThat(ss.isUsingNonTerrestrialNetwork()).isEqualTo(false);
+
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setIsNonTerrestrialNetwork(true)
+ .build();
+ ss.addNetworkRegistrationInfo(nri);
+ assertThat(ss.isUsingNonTerrestrialNetwork()).isEqualTo(true);
+ }
+
private void assertCellIdentitiesSanitized(ServiceState ss) {
List<NetworkRegistrationInfo> networkRegistrationInfoList =
ss.getNetworkRegistrationInfoList();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 846b48e3f7..5f592d19e0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -16,6 +16,9 @@
package com.android.internal.telephony;
+import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY;
+import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_SMS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
@@ -96,6 +99,7 @@ import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.data.AccessNetworksManager;
import com.android.internal.telephony.data.DataNetworkController;
import com.android.internal.telephony.metrics.ServiceStateStats;
+import com.android.internal.telephony.satellite.SatelliteController;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
@@ -116,6 +120,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
public class ServiceStateTrackerTest extends TelephonyTest {
// Mocked classes
@@ -138,6 +143,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
private ServiceStateTracker sst;
private ServiceStateTrackerTestHandler mSSTTestHandler;
private PersistableBundle mBundle;
+ private SatelliteController mSatelliteController;
private static final int EVENT_REGISTERED_TO_NETWORK = 1;
private static final int EVENT_SUBSCRIPTION_INFO_READY = 2;
@@ -248,6 +254,11 @@ public class ServiceStateTrackerTest extends TelephonyTest {
mSubInfoInternal = new SubscriptionInfoInternal.Builder().setId(1).build();
mServiceStateStats = Mockito.mock(ServiceStateStats.class);
+ mSatelliteController = Mockito.mock(SatelliteController.class);
+ replaceInstance(SatelliteController.class, "sInstance", null,
+ mSatelliteController);
+ doReturn(new ArrayList<>()).when(mSatelliteController).getSatellitePlmnList();
+
mContextFixture.putResource(R.string.kg_text_message_separator, " \u2014 ");
doReturn(mSubInfoInternal).when(mSubscriptionManagerService)
@@ -2763,6 +2774,11 @@ public class ServiceStateTrackerTest extends TelephonyTest {
ss.setEmergencyOnly(true);
sst.mSS = ss;
+ // The other phone is in service
+ ss = new ServiceState();
+ doReturn(ss).when(mSST).getServiceState();
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mSST).getCombinedRegState(ss);
+
// update the spn
sst.updateSpnDisplay();
@@ -3208,4 +3224,55 @@ public class ServiceStateTrackerTest extends TelephonyTest {
assertTrue(sst.mSS.isIwlanPreferred());
}
+
+ @Test
+ public void testRegisterToSatellite() {
+ int[] satelliteSupportedServices = {SERVICE_TYPE_SMS, SERVICE_TYPE_EMERGENCY};
+ List<Integer> satelliteSupportedServiceList =
+ Arrays.stream(satelliteSupportedServices).boxed().collect(Collectors.toList());
+ CellIdentityGsm cellIdentity =
+ new CellIdentityGsm(0, 1, 900, 5, "101", "23", "test", "tst",
+ Collections.emptyList());
+ doReturn(Arrays.asList("10123")).when(mSatelliteController).getSatellitePlmnList();
+ doReturn(satelliteSupportedServiceList).when(mSatelliteController)
+ .getSupportedSatelliteServices(sst.mSubId, "10123");
+
+ assertFalse(sst.mSS.isUsingNonTerrestrialNetwork());
+
+ // Data registered to satellite roaming PLMN - "00101"
+ NetworkRegistrationInfo dataReg = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ 5, 16, 0, false, null, cellIdentity, getPlmnFromCellIdentity(cellIdentity),
+ 1, false, false, false, null);
+
+ // CS out of service
+ NetworkRegistrationInfo voiceReg = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ 0, 16, 0, true, null, cellIdentity, getPlmnFromCellIdentity(cellIdentity),
+ false, 0, 0, 0);
+
+ sst.mPollingContext[0] = 2;
+ // Update voice reg state to be in oos
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceReg, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ // Update data registered to satellite roaming PLMN
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, dataReg, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ assertTrue(sst.mSS.isUsingNonTerrestrialNetwork());
+ List<NetworkRegistrationInfo> nriList =
+ sst.mSS.getNetworkRegistrationInfoListForTransportType(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ assertEquals(2, nriList.size());
+ for (NetworkRegistrationInfo nri : nriList) {
+ assertTrue(Arrays.equals(satelliteSupportedServices, nri.getAvailableServices().stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+ }
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index b044814765..705bafdf75 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -219,6 +219,7 @@ public abstract class TelephonyTest {
protected UiccCardApplication mUiccCardApplication3gpp2;
protected UiccCardApplication mUiccCardApplicationIms;
protected SIMRecords mSimRecords;
+ protected SignalStrengthController mSignalStrengthController;
protected RuimRecords mRuimRecords;
protected IsimUiccRecords mIsimUiccRecords;
protected ProxyController mProxyController;
@@ -455,6 +456,7 @@ public abstract class TelephonyTest {
mUiccCardApplication3gpp2 = Mockito.mock(UiccCardApplication.class);
mUiccCardApplicationIms = Mockito.mock(UiccCardApplication.class);
mSimRecords = Mockito.mock(SIMRecords.class);
+ mSignalStrengthController = Mockito.mock(SignalStrengthController.class);
mRuimRecords = Mockito.mock(RuimRecords.class);
mIsimUiccRecords = Mockito.mock(IsimUiccRecords.class);
mProxyController = Mockito.mock(ProxyController.class);
@@ -636,6 +638,7 @@ public abstract class TelephonyTest {
doReturn(mSST).when(mPhone).getServiceStateTracker();
doReturn(mDeviceStateMonitor).when(mPhone).getDeviceStateMonitor();
doReturn(mDisplayInfoController).when(mPhone).getDisplayInfoController();
+ doReturn(mSignalStrengthController).when(mPhone).getSignalStrengthController();
doReturn(mEmergencyNumberTracker).when(mPhone).getEmergencyNumberTracker();
doReturn(mCarrierSignalAgent).when(mPhone).getCarrierSignalAgent();
doReturn(mCarrierActionAgent).when(mPhone).getCarrierActionAgent();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
new file mode 100644
index 0000000000..7ac3a170ad
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2023 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.data;
+
+import static android.telephony.SubscriptionManager.DEFAULT_PHONE_INDEX;
+
+import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_DATA_SETTINGS_CHANGED;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.net.NetworkCapabilities;
+import android.os.AsyncResult;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AutoDataSwitchControllerTest extends TelephonyTest {
+ private static final int EVENT_SERVICE_STATE_CHANGED = 1;
+ private static final int EVENT_DISPLAY_INFO_CHANGED = 2;
+ private static final int EVENT_EVALUATE_AUTO_SWITCH = 3;
+ private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 4;
+ private static final int EVENT_MEETS_AUTO_DATA_SWITCH_STATE = 5;
+
+ private static final int PHONE_1 = 0;
+ private static final int SUB_1 = 1;
+ private static final int PHONE_2 = 1;
+ private static final int SUB_2 = 2;
+ private static final int MAX_RETRY = 5;
+ // Mocked
+ private AutoDataSwitchController.AutoDataSwitchControllerCallback mMockedPhoneSwitcherCallback;
+
+ private int mDefaultDataSub;
+ private AutoDataSwitchController mAutoDataSwitchControllerUT;
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mMockedPhoneSwitcherCallback =
+ mock(AutoDataSwitchController.AutoDataSwitchControllerCallback.class);
+
+ doReturn(PHONE_1).when(mPhone).getPhoneId();
+ doReturn(SUB_1).when(mPhone).getSubId();
+
+ doReturn(PHONE_2).when(mPhone2).getPhoneId();
+ doReturn(SUB_2).when(mPhone2).getSubId();
+
+ doReturn(SUB_1).when(mSubscriptionManagerService).getSubId(PHONE_1);
+ doReturn(SUB_2).when(mSubscriptionManagerService).getSubId(PHONE_2);
+
+ mPhones = new Phone[]{mPhone, mPhone2};
+ for (Phone phone : mPhones) {
+ doReturn(mSST).when(phone).getServiceStateTracker();
+ doReturn(mDisplayInfoController).when(phone).getDisplayInfoController();
+ doReturn(mSignalStrengthController).when(phone).getSignalStrengthController();
+ doReturn(mSignalStrength).when(phone).getSignalStrength();
+ doAnswer(invocation -> phone.getSubId() == mDefaultDataSub)
+ .when(phone).isUserDataEnabled();
+ }
+ doReturn(new int[mPhones.length]).when(mSubscriptionManagerService)
+ .getActiveSubIdList(true);
+ doAnswer(invocation -> {
+ int subId = (int) invocation.getArguments()[0];
+
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) return null;
+
+ int slotIndex = subId == SUB_1 ? PHONE_1 : PHONE_2;
+ return new SubscriptionInfoInternal.Builder()
+ .setSimSlotIndex(slotIndex).setId(subId).build();
+ }).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+
+ // Change resource overlay
+ doReturn(true).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
+ doReturn(1L).when(mDataConfigManager)
+ .getAutoDataSwitchAvailabilityStabilityTimeThreshold();
+ doReturn(MAX_RETRY).when(mDataConfigManager).getAutoDataSwitchValidationMaxRetry();
+
+ setDefaultDataSubId(SUB_1);
+ doReturn(PHONE_1).when(mPhoneSwitcher).getPreferredDataPhoneId();
+
+ mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
+ mPhoneSwitcher, mMockedPhoneSwitcherCallback);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mAutoDataSwitchControllerUT = null;
+ super.tearDown();
+ }
+
+ @Test
+ public void testCancelSwitch_onPrimary() {
+ // 0. When all conditions met
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+
+ // Verify attempting to switch
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, true/*needValidation*/);
+
+ // 1. Service state becomes not ideal - primary is available again
+ serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+
+ // 2.1 User data disabled on primary SIM
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ doReturn(false).when(mPhone).isUserDataEnabled();
+ mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+
+ // 2.2 Auto switch feature is disabled
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ doReturn(false).when(mPhone2).isDataAllowed();
+ mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+
+ // 3.1 No default network
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI));
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+ }
+
+ @Test
+ public void testOnNonDdsSwitchBackToPrimary() {
+ doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+
+ prepareIdealUsesNonDdsCondition();
+ // 1.1 service state changes - primary becomes available again, require validation
+ serviceStateChanged(PHONE_1,
+ NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*need validate*/);
+ processAllFutureMessages();
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ true/*needValidation*/);
+
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ prepareIdealUsesNonDdsCondition();
+ // 1.2 service state changes - secondary becomes unavailable, NO need validation
+ serviceStateChanged(PHONE_1,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME/*need validate*/);
+ serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*no need*/);
+ processAllFutureMessages();
+ // The later validation requirement overrides the previous
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ false/*needValidation*/);
+
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ prepareIdealUsesNonDdsCondition();
+ // 2.1 User data disabled on primary SIM, no need validation
+ doReturn(false).when(mPhone).isUserDataEnabled();
+ mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
+ EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ prepareIdealUsesNonDdsCondition();
+ // 2.2 Auto switch feature is disabled, no need validation
+ clearInvocations(mCellularNetworkValidator);
+ doReturn(false).when(mPhone2).isDataAllowed();
+ mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireImmediatelySwitchToPhone(DEFAULT_PHONE_INDEX,
+ EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+
+ clearInvocations(mMockedPhoneSwitcherCallback);
+ prepareIdealUsesNonDdsCondition();
+ // 3.1 Default network is active on non-cellular transport
+ mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI));
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ false/*needValidation*/);
+ }
+
+ @Test
+ public void testCancelSwitch_onSecondary() {
+ doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+ prepareIdealUsesNonDdsCondition();
+
+ // attempts the switch back due to secondary becomes ROAMING
+ serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ false/*needValidation*/);
+
+ // cancel the switch back attempt due to secondary back to HOME
+ serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireCancelAnyPendingAutoSwitchValidation();
+ }
+
+ @Test
+ public void testValidationFailedRetry() {
+ prepareIdealUsesNonDdsCondition();
+
+ for (int i = 0; i < MAX_RETRY; i++) {
+ mAutoDataSwitchControllerUT.evaluateRetryOnValidationFailed();
+ processAllFutureMessages();
+ }
+ verify(mMockedPhoneSwitcherCallback, times(MAX_RETRY))
+ .onRequireValidation(PHONE_2, true /*need validation*/);
+ }
+
+ @Test
+ public void testExemptPingTest() {
+ // Change resource overlay
+ doReturn(false).when(mDataConfigManager)
+ .isPingTestBeforeAutoDataSwitchRequired();
+ mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
+ mPhoneSwitcher, mMockedPhoneSwitcherCallback);
+
+ //1. DDS -> nDDS, verify callback doesn't require validation
+ prepareIdealUsesNonDdsCondition();
+ processAllFutureMessages();
+
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, false/*needValidation*/);
+
+ //2. nDDS -> DDS, verify callback doesn't require validation
+ doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+ serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ processAllFutureMessages();
+ verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+ false/*needValidation*/);
+ }
+
+ @Test
+ public void testSetNotification() {
+ NotificationManager notificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ SubscriptionInfo mockedInfo = mock(SubscriptionInfo.class);
+ doReturn(false).when(mockedInfo).isOpportunistic();
+ doReturn(mockedInfo).when(mSubscriptionManagerService).getSubscriptionInfo(anyInt());
+
+ // First switch is not due to auto, so no notification.
+ mAutoDataSwitchControllerUT.displayAutoDataSwitchNotification(PHONE_2, false);
+ verify(mSubscriptionManagerService, never()).getSubscriptionInfo(SUB_2);
+
+ // Switch is due to auto, show notification.
+ mAutoDataSwitchControllerUT.displayAutoDataSwitchNotification(PHONE_2, true);
+ verify(notificationManager).notify(any(), anyInt(), any());
+ verify(mSubscriptionManagerService).getSubscriptionInfo(SUB_2);
+
+ // Switch is due to auto, but already shown notification, hide the notification.
+ mAutoDataSwitchControllerUT.displayAutoDataSwitchNotification(PHONE_2, true);
+ verify(notificationManager).cancel(any(), anyInt());
+ }
+
+ @Test
+ public void testMultiSimConfigChanged() {
+ // Test Dual -> Single
+ mAutoDataSwitchControllerUT.onMultiSimConfigChanged(1);
+
+ verify(mDisplayInfoController).unregisterForTelephonyDisplayInfoChanged(any());
+ verify(mSignalStrengthController).unregisterForSignalStrengthChanged(any());
+ verify(mSST).unregisterForServiceStateChanged(any());
+
+ clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST);
+ // Test Single -> Dual
+ mAutoDataSwitchControllerUT.onMultiSimConfigChanged(2);
+
+ verify(mDisplayInfoController).registerForTelephonyDisplayInfoChanged(any(),
+ eq(EVENT_DISPLAY_INFO_CHANGED), eq(PHONE_2));
+ verify(mSignalStrengthController).registerForSignalStrengthChanged(any(),
+ eq(EVENT_SIGNAL_STRENGTH_CHANGED), eq(PHONE_2));
+ verify(mSST).registerForServiceStateChanged(any(),
+ eq(EVENT_SERVICE_STATE_CHANGED), eq(PHONE_2));
+ }
+
+ /**
+ * Trigger conditions
+ * 1. service state changes
+ * 2. data setting changes
+ * - user toggle data
+ * - user toggle auto switch feature
+ * 3. default network changes
+ * - current network lost
+ * - network become active on non-cellular network
+ */
+ private void prepareIdealUsesNonDdsCondition() {
+ // 1. service state changes
+ serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ serviceStateChanged(PHONE_1, NetworkRegistrationInfo
+ .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+
+ // 2.1 User data enabled on primary SIM
+ doReturn(true).when(mPhone).isUserDataEnabled();
+
+ // 2.2 Auto switch feature is enabled
+ doReturn(true).when(mPhone2).isDataAllowed();
+
+ // 3.1 No default network
+ mAutoDataSwitchControllerUT.updateDefaultNetworkCapabilities(null /*networkCapabilities*/);
+ }
+
+ private void serviceStateChanged(int phoneId,
+ @NetworkRegistrationInfo.RegistrationState int dataRegState) {
+
+ ServiceState ss = new ServiceState();
+
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setRegistrationState(dataRegState)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .build());
+
+ ss.setDataRoamingFromRegistration(dataRegState
+ == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+
+ doReturn(ss).when(mPhones[phoneId]).getServiceState();
+
+ Message msg = mAutoDataSwitchControllerUT.obtainMessage(EVENT_SERVICE_STATE_CHANGED);
+ msg.obj = new AsyncResult(phoneId, null, null);
+ mAutoDataSwitchControllerUT.sendMessage(msg);
+ }
+ private void setDefaultDataSubId(int defaultDataSub) {
+ mDefaultDataSub = defaultDataSub;
+ doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
index f3128089aa..d4a0804b57 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataConfigManagerTest.java
@@ -20,10 +20,16 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.os.Looper;
import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -96,4 +102,47 @@ public class DataConfigManagerTest extends TelephonyTest {
assertThat(invalidFormat3.timeWindow).isEqualTo(defaultValue.timeWindow);
assertThat(invalidFormat3.eventNumOccurrence).isEqualTo(defaultValue.eventNumOccurrence);
}
+
+ @Test
+ public void testParseAutoDataSwitchScoreTable() {
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ int tolerance = 100;
+ PersistableBundle auto_data_switch_rat_signal_score_string_bundle = new PersistableBundle();
+ auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+ "NR_NSA_MMWAVE", new int[]{10000, 10227, 12488, 15017, 15278});
+ auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+ "LTE", new int[]{-3731, 5965, 8618, 11179, 13384});
+ mBundle.putPersistableBundle(
+ CarrierConfigManager.KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE,
+ auto_data_switch_rat_signal_score_string_bundle);
+
+ mContextFixture.putIntResource(com.android.internal.R.integer
+ .auto_data_switch_score_tolerance, tolerance);
+
+ mDataConfigManagerUT.sendEmptyMessage(1/*EVENT_CARRIER_CONFIG_CHANGED*/);
+ processAllMessages();
+
+ assertThat(mDataConfigManagerUT.getAutoDataSwitchScoreTolerance()).isEqualTo(tolerance);
+
+ // Verify NSA_MMWAVE
+ doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(signalStrength).getLevel();
+ assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo(
+ TelephonyManager.NETWORK_TYPE_LTE,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED, false/*isRoaming*/),
+ signalStrength)).isEqualTo(10227);
+ // Verify if entry contains any invalid negative scores, should yield -1.
+ doReturn(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN).when(signalStrength).getLevel();
+ assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo(
+ TelephonyManager.NETWORK_TYPE_LTE,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/),
+ signalStrength))
+ .isEqualTo(-1/*INVALID_AUTO_DATA_SWITCH_SCORE*/);
+ // Verify non-existent entry should yield -1
+ doReturn(SignalStrength.SIGNAL_STRENGTH_POOR).when(signalStrength).getLevel();
+ assertThat(mDataConfigManagerUT.getAutoDataSwitchScore(new TelephonyDisplayInfo(
+ TelephonyManager.NETWORK_TYPE_EDGE,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false/*isRoaming*/),
+ signalStrength))
+ .isEqualTo(-1/*INVALID_AUTO_DATA_SWITCH_SCORE*/);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index 808a15d1d9..d96bac49b8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -35,6 +35,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
@@ -62,6 +63,7 @@ import android.provider.Telephony;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.Annotation.NetCapability;
import android.telephony.Annotation.NetworkType;
@@ -1584,6 +1586,14 @@ public class DataNetworkControllerTest extends TelephonyTest {
// Verify data is torn down.
verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ // Registration is back to HOME.
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+ processAllFutureMessages();
+
+ // Verify data is restored.
+ verifyInternetConnected();
}
@Test
@@ -1630,13 +1640,27 @@ public class DataNetworkControllerTest extends TelephonyTest {
doReturn(true).when(controller).isCarrierConfigLoadedForAllSub();
replaceInstance(MultiSimSettingController.class, "sInstance", null, controller);
+ // Mock Data Overall data is always enabled due to auto data switch,
+ // verify the test shouldn't rely on the overall data status
+ doReturn(1).when(mPhone).getSubId();
+ doReturn(2).when(mSubscriptionManagerService).getDefaultDataSubId();
+ Phone phone2 = Mockito.mock(Phone.class);
+ phone2.mCi = mSimulatedCommands;
+ doReturn(true).when(phone2).isUserDataEnabled();
+ doReturn(mDataSettingsManager).when(phone2).getDataSettingsManager();
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, phone2});
+ mDataNetworkControllerUT.getDataSettingsManager().setMobileDataPolicy(TelephonyManager
+ .MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true);
+ processAllMessages();
+ clearInvocations(mPhone);
+
controller.notifyAllSubscriptionLoaded();
mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
TelephonyManager.DATA_ENABLED_REASON_USER, !isDataEnabled,
mContext.getOpPackageName());
processAllMessages();
- // Verify not to notify MultiSimSettingController
+ // Verify not to notify MultiSimSettingController due to internal calling package
verify(controller, never()).notifyUserDataEnabled(anyInt(), anyBoolean());
mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
@@ -1644,7 +1668,7 @@ public class DataNetworkControllerTest extends TelephonyTest {
mContext.getOpPackageName());
processAllMessages();
- // Verify not to notify MultiSimSettingController
+ // Verify not to notify MultiSimSettingController due to internal calling package
verify(controller, never()).notifyUserDataEnabled(anyInt(), anyBoolean());
mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
@@ -1655,6 +1679,7 @@ public class DataNetworkControllerTest extends TelephonyTest {
// Verify to notify MultiSimSettingController exactly 2 times
verify(controller, times(2)).notifyUserDataEnabled(anyInt(), anyBoolean());
+ verify(mPhone, never()).notifyDataEnabled(anyBoolean(), anyInt());
}
@Test
@@ -3208,6 +3233,14 @@ public class DataNetworkControllerTest extends TelephonyTest {
processAllMessages();
verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_DUN);
+
+ // User data disabled
+ mDataNetworkControllerUT.getDataSettingsManager().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false, mContext.getOpPackageName());
+ processAllMessages();
+
+ // Everything should be disconnected.
+ verifyAllDataDisconnected();
}
@Test
@@ -3869,6 +3902,84 @@ public class DataNetworkControllerTest extends TelephonyTest {
}
@Test
+ public void testHandoverDataNetworkRoamingOos() throws Exception {
+ testSetupImsDataNetwork();
+ // Configured handover is disallowed at Roaming.
+ mCarrierConfig.putStringArray(
+ CarrierConfigManager.KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY,
+ new String[]{
+ "source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN|UNKNOWN, "
+ + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, roaming=true, "
+ + "type=disallowed, capabilities=IMS"
+ });
+ carrierConfigChanged();
+ DataNetwork dataNetwork = getDataNetworks().get(0);
+ //Enter ROAMING
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ updateServiceStateForDatatNetwork(TelephonyManager.NETWORK_TYPE_LTE,
+ NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING, dataNetwork);
+ //OOS
+ serviceStateChanged(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
+ updateServiceStateForDatatNetwork(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+ dataNetwork);
+
+ updateTransport(NetworkCapabilities.NET_CAPABILITY_IMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+
+ // Verify IMS network was torn down on source first.
+ verify(mMockedWwanDataServiceManager).deactivateDataCall(anyInt(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+
+ // Verify that IWLAN is brought up again on IWLAN.
+ verify(mMockedWlanDataServiceManager).setupDataCall(anyInt(),
+ any(DataProfile.class), anyBoolean(), anyBoolean(),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(), anyBoolean(),
+ any(Message.class));
+
+ DataNetwork dataNetworkIwlan = getDataNetworks().get(0);
+ assertThat(dataNetworkIwlan.getTransport()).isEqualTo(
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ }
+
+ private void updateServiceStateForDatatNetwork(@Annotation.NetworkType int networkType,
+ @NetworkRegistrationInfo.RegistrationState int regState, DataNetwork dataNetwork) {
+ ServiceState ss = new ServiceState();
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setAccessNetworkTechnology(networkType)
+ .setRegistrationState(regState)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .setDataSpecificInfo(null)
+ .build());
+
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+ .build());
+
+ ss.addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setAccessNetworkTechnology(networkType)
+ .setRegistrationState(regState)
+ .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+ .build());
+ ss.setDataRoamingFromRegistration(regState
+ == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+ doReturn(ss).when(mSST).getServiceState();
+ doReturn(ss).when(mPhone).getServiceState();
+
+ if (dataNetwork != null) {
+ dataNetwork.obtainMessage(9/*EVENT_SERVICE_STATE_CHANGED*/).sendToTarget();
+ processAllMessages();
+ }
+ }
+
+ @Test
public void testHandoverDataNetworkSourceOosNoUnknownRule() throws Exception {
testSetupImsDataNetwork();
// Configured handover is allowed from OOS to 4G/5G/IWLAN.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index ed26b99253..41a714ca70 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -182,6 +182,28 @@ public class DataNetworkTest extends TelephonyTest {
"CBS", 1).getBytes()))
.build();
+ private final DataProfile m5gDataProfile = new DataProfile.Builder()
+ .setApnSetting(new ApnSetting.Builder()
+ .setId(2163)
+ .setOperatorNumeric("12345")
+ .setEntryName("fake_apn")
+ .setApnName(null /*empty name*/)
+ .setUser("user")
+ .setPassword("passwd")
+ .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL)
+ .setProtocol(ApnSetting.PROTOCOL_IPV6)
+ .setRoamingProtocol(ApnSetting.PROTOCOL_IP)
+ .setCarrierEnabled(true)
+ .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+ | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
+ .setProfileId(1234)
+ .setMaxConns(321)
+ .setWaitTime(456)
+ .setMaxConnsTime(789)
+ .build())
+ .setTrafficDescriptor(new TrafficDescriptor(null, null))
+ .build();
+
// Mocked classes
private DataNetworkCallback mDataNetworkCallback;
private DataCallSessionStats mDataCallSessionStats;
@@ -405,7 +427,7 @@ public class DataNetworkTest extends TelephonyTest {
verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
linkPropertiesCaptor.capture(), networkCapabilitiesCaptor.capture(), any(), any(),
- any(), anyInt());
+ anyInt());
// The very first link properties from telephony is an empty link properties. It will be
// updated later.
assertThat(linkPropertiesCaptor.getValue()).isEqualTo(new LinkProperties());
@@ -510,7 +532,7 @@ public class DataNetworkTest extends TelephonyTest {
verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
any(LinkProperties.class), networkCapabilitiesCaptor.capture(), any(), any(),
- any(), anyInt());
+ anyInt());
// Make sure the initial network capability has NOT_SUSPENDED
assertThat(networkCapabilitiesCaptor.getValue().hasCapability(
@@ -574,7 +596,7 @@ public class DataNetworkTest extends TelephonyTest {
// Agent re-created, so register should be called twice.
verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
any(LinkProperties.class), networkCapabilitiesCaptor.capture(), any(), any(),
- any(), anyInt());
+ anyInt());
// Make sure the 2nd network agent was created with NOT_SUSPENDED.
assertThat(networkCapabilitiesCaptor.getValue().hasCapability(
@@ -634,7 +656,7 @@ public class DataNetworkTest extends TelephonyTest {
verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
linkPropertiesCaptor.capture(), networkCapabilitiesCaptor.capture(), any(), any(),
- any(), anyInt());
+ anyInt());
// The very first link properties from telephony is an empty link properties. It will be
// updated later.
assertThat(linkPropertiesCaptor.getValue()).isEqualTo(new LinkProperties());
@@ -1265,8 +1287,8 @@ public class DataNetworkTest extends TelephonyTest {
.forClass(NetworkAgentConfig.class);
verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
- any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
- captor.capture(), anyInt());
+ any(LinkProperties.class), any(NetworkCapabilities.class), any(), captor.capture(),
+ anyInt());
NetworkAgentConfig networkAgentConfig = captor.getValue();
@@ -1349,7 +1371,7 @@ public class DataNetworkTest extends TelephonyTest {
// Agent re-created, so register should be called twice.
verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
- any(), anyInt());
+ anyInt());
assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)).isTrue();
@@ -1371,7 +1393,7 @@ public class DataNetworkTest extends TelephonyTest {
// Agent not re-created, so register should be called once.
verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
- any(), anyInt());
+ anyInt());
assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)).isTrue();
@@ -1540,7 +1562,7 @@ public class DataNetworkTest extends TelephonyTest {
// Agent re-created, so register should be called twice.
verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
linkPropertiesCaptor.capture(), any(NetworkCapabilities.class), any(), any(),
- any(), anyInt());
+ anyInt());
// The new agent should have the new IP address.
assertThat(linkPropertiesCaptor.getValue().getAllAddresses()).containsExactly(
InetAddresses.parseNumericAddress(IPV4_ADDRESS1),
@@ -1589,7 +1611,7 @@ public class DataNetworkTest extends TelephonyTest {
// Agent re-created, so register should be called twice.
verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
linkPropertiesCaptor.capture(), any(NetworkCapabilities.class), any(), any(),
- any(), anyInt());
+ anyInt());
// The new agent should have the new IP address.
assertThat(linkPropertiesCaptor.getValue().getAllAddresses()).containsExactly(
InetAddresses.parseNumericAddress(IPV6_ADDRESS1));
@@ -1680,7 +1702,7 @@ public class DataNetworkTest extends TelephonyTest {
// Agent should not be re-created, so register should be called ony once.
verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
- any(), anyInt());
+ anyInt());
// The network should have IPv6 address now
assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
@@ -1724,7 +1746,7 @@ public class DataNetworkTest extends TelephonyTest {
// Agent should not be re-created, so register should be called ony once.
verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
- any(), anyInt());
+ anyInt());
// The network should have IPv6 address now
assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
@@ -1769,7 +1791,7 @@ public class DataNetworkTest extends TelephonyTest {
// Agent should not be re-created, so register should be called ony once.
verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
- any(), anyInt());
+ anyInt());
// The network should have IPv6 address now
assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
@@ -1777,6 +1799,28 @@ public class DataNetworkTest extends TelephonyTest {
}
@Test
+ public void testPrivateNetwork() throws Exception {
+ NetworkRequestList networkRequestList = new NetworkRequestList();
+ networkRequestList.add(new TelephonyNetworkRequest(new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build(), mPhone));
+ mDataNetworkUT = new DataNetwork(mPhone, Looper.myLooper(), mDataServiceManagers,
+ m5gDataProfile, networkRequestList,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN, DataAllowedReason.NORMAL,
+ mDataNetworkCallback);
+ replaceInstance(DataNetwork.class, "mDataCallSessionStats",
+ mDataNetworkUT, mDataCallSessionStats);
+ processAllMessages();
+
+ verify(mMockedWwanDataServiceManager).setupDataCall(anyInt(),
+ eq(m5gDataProfile), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), nullable(LinkProperties.class),
+ eq(DataCallResponse.PDU_SESSION_ID_NOT_SET), nullable(NetworkSliceInfo.class),
+ // Verify matchAllRuleAllowed is flagged true
+ any(TrafficDescriptor.class), eq(true), any(Message.class));
+ }
+
+ @Test
public void testLinkStatusUpdate() throws Exception {
setupDataNetwork();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index e10c2a5a12..27271dfe56 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -873,8 +873,10 @@ public class DataProfileManagerTest extends TelephonyTest {
@Test
public void testSetInitialAttachDataProfileMultipleRequests() throws Exception {
+ // This test case only applies to legacy modem, see b/227579876
+ doReturn(false).when(mDataConfigManager).allowClearInitialAttachDataProfile();
+
// Test: Modem Cleared IA, should always send IA to modem
- // TODO(b/237444788): this case should be removed from U
mDataProfileManagerUT.obtainMessage(3 /* EVENT_SIM_REFRESH */).sendToTarget();
processAllMessages();
@@ -911,6 +913,14 @@ public class DataProfileManagerTest extends TelephonyTest {
@Test
public void testSimRemoval() {
+ // This test case applies to the latest modem, see b/227579876.
+ doReturn(true).when(mDataConfigManager).allowClearInitialAttachDataProfile();
+
+ // SIM inserted
+ mDataProfileManagerUT.obtainMessage(3 /* EVENT_SIM_REFRESH */).sendToTarget();
+ processAllMessages();
+
+ // SIM removed
Mockito.clearInvocations(mDataProfileManagerCallback);
changeSimStateTo(TelephonyManager.SIM_STATE_ABSENT);
mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
@@ -943,6 +953,58 @@ public class DataProfileManagerTest extends TelephonyTest {
dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
assertThat(dataProfile).isEqualTo(null);
+
+ // Verify null as initial attached data profile is sent to modem
+ verify(mMockedWwanDataServiceManager).setInitialAttachApn(null, false, null);
+ }
+
+ @Test
+ public void testSimRemovalLegacy() {
+ // This test case only applies to legacy modem, see b/227579876, where null IA won't be
+ // updated to modem
+ doReturn(false).when(mDataConfigManager).allowClearInitialAttachDataProfile();
+
+ // SIM inserted
+ mDataProfileManagerUT.obtainMessage(3 /* EVENT_SIM_REFRESH */).sendToTarget();
+ processAllMessages();
+
+ // SIM removed
+ Mockito.clearInvocations(mDataProfileManagerCallback);
+ mSimInserted = false;
+ mDataProfileManagerUT.obtainMessage(2 /*EVENT_APN_DATABASE_CHANGED*/).sendToTarget();
+ processAllMessages();
+
+ verify(mDataProfileManagerCallback).onDataProfilesChanged();
+
+ TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build(), mPhone);
+ DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+ assertThat(dataProfile).isNull();
+
+ // expect default EIMS when SIM absent
+ tnr = new TelephonyNetworkRequest(
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
+ .build(), mPhone);
+ dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+ assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo("sos");
+
+ // expect no default IMS when SIM absent
+ tnr = new TelephonyNetworkRequest(
+ new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+ .build(), mPhone);
+ dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+ tnr, TelephonyManager.NETWORK_TYPE_LTE, false);
+ assertThat(dataProfile).isEqualTo(null);
+
+ // Verify in legacy mode, null IA should NOT be sent to modem
+ verify(mMockedWwanDataServiceManager, Mockito.never())
+ .setInitialAttachApn(null, false, null);
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
index 84b3302ba0..103b189f7e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
@@ -795,7 +795,7 @@ public class DataRetryManagerTest extends TelephonyTest {
// Verify scheduled via Alarm Manager
ArgumentCaptor<PendingIntent> pendingIntentArgumentCaptor =
ArgumentCaptor.forClass(PendingIntent.class);
- verify(mAlarmManager).setAndAllowWhileIdle(anyInt(), anyLong(),
+ verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(),
pendingIntentArgumentCaptor.capture());
// Verify starts retry attempt after receiving intent
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
index f7525c1e52..20cb73a68e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataSettingsManagerTest.java
@@ -21,16 +21,22 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.os.Looper;
import android.os.PersistableBundle;
+import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
@@ -131,4 +137,45 @@ public class DataSettingsManagerTest extends TelephonyTest {
mDataSettingsManagerUT.setDefaultDataRoamingEnabled();
assertFalse(mDataSettingsManagerUT.isDataRoamingEnabled());
}
+
+ @Test
+ public void testUpdateDataEnabledAndNotifyOverride() throws Exception {
+ // Mock another DDS phone.
+ int ddsPhoneId = 1;
+ int ddsSubId = 2;
+ doReturn(ddsSubId).when(mSubscriptionManagerService).getDefaultDataSubId();
+ Phone phone2 = Mockito.mock(Phone.class);
+ doReturn(ddsPhoneId).when(phone2).getPhoneId();
+ doReturn(ddsSubId).when(phone2).getSubId();
+ doReturn(ddsPhoneId).when(mSubscriptionManagerService).getPhoneId(ddsSubId);
+ DataSettingsManager dataSettingsManager2 = Mockito.mock(DataSettingsManager.class);
+ doReturn(dataSettingsManager2).when(phone2).getDataSettingsManager();
+ mPhones = new Phone[] {mPhone, phone2};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ ArgumentCaptor<DataSettingsManagerCallback> callbackArgumentCaptor = ArgumentCaptor
+ .forClass(DataSettingsManagerCallback.class);
+
+ mDataSettingsManagerUT.sendEmptyMessage(11 /* EVENT_INITIALIZE */);
+ processAllMessages();
+
+ // Verify listening to user enabled status of other phones.
+ verify(dataSettingsManager2).registerCallback(callbackArgumentCaptor.capture());
+ DataSettingsManagerCallback callback = callbackArgumentCaptor.getValue();
+
+ // Mock the phone as nonDDS.
+ mDataSettingsManagerUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER, false, "");
+ processAllMessages();
+ clearInvocations(mPhone);
+
+ // Verify the override policy doesn't take effect because the DDS is user disabled.
+ mDataSettingsManagerUT.setMobileDataPolicy(
+ TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true);
+ processAllMessages();
+ verify(mPhone, never()).notifyDataEnabled(anyBoolean(), anyInt());
+
+ // Verify the override takes effect upon DDS user enabled.
+ doReturn(true).when(phone2).isUserDataEnabled();
+ callback.onUserDataEnabledChanged(true, "callingPackage");
+ verify(mPhone).notifyDataEnabled(true, TelephonyManager.DATA_ENABLED_REASON_OVERRIDE);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
index 35d3b9268a..22cdaae64f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataStallRecoveryManagerTest.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.data;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -25,9 +26,16 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.Intent;
+import android.database.ContentObserver;
import android.net.NetworkAgent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
import android.telephony.Annotation.ValidationStatus;
import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,21 +50,53 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class DataStallRecoveryManagerTest extends TelephonyTest {
+ private FakeContentResolver mFakeContentResolver;
+
// Mocked classes
private DataStallRecoveryManagerCallback mDataStallRecoveryManagerCallback;
private DataStallRecoveryManager mDataStallRecoveryManager;
+ /**
+ * The fake content resolver used to receive change event from global settings
+ * and notify observer of a change in content in DataStallRecoveryManager
+ */
+ private class FakeContentResolver extends MockContentResolver {
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer) {
+ super.notifyChange(uri, observer);
+ logd("onChanged(uri=" + uri + ")" + observer);
+ if (observer != null) {
+ observer.dispatchChange(false, uri);
+ } else {
+ mDataStallRecoveryManager.getContentObserver().dispatchChange(false, uri);
+ }
+ }
+ }
+
@Before
public void setUp() throws Exception {
logd("DataStallRecoveryManagerTest +Setup!");
super.setUp(getClass().getSimpleName());
+ Field field = DataStallRecoveryManager.class.getDeclaredField("mPredictWaitingMillis");
+ field.setAccessible(true);
+
+ mFakeContentResolver = new FakeContentResolver();
+ doReturn(mFakeContentResolver).when(mContext).getContentResolver();
+ // Set the global settings for action enabled state and duration to
+ // the default test values.
+ Settings.Global.putString(mFakeContentResolver, Settings.Global.DSRM_DURATION_MILLIS,
+ "100,100,100,100,0");
+ Settings.Global.putString(mFakeContentResolver, Settings.Global.DSRM_ENABLED_ACTIONS,
+ "true,true,false,true,true");
+
mDataStallRecoveryManagerCallback = mock(DataStallRecoveryManagerCallback.class);
mCarrierConfigManager = mPhone.getContext().getSystemService(CarrierConfigManager.class);
long[] dataStallRecoveryTimersArray = new long[] {100, 100, 100, 100};
@@ -81,11 +121,15 @@ public class DataStallRecoveryManagerTest extends TelephonyTest {
mMockedWwanDataServiceManager,
mTestableLooper.getLooper(),
mDataStallRecoveryManagerCallback);
+
+ field.set(mDataStallRecoveryManager, 0L);
+
logd("DataStallRecoveryManagerTest -Setup!");
}
@After
public void tearDown() throws Exception {
+ mFakeContentResolver = null;
mDataStallRecoveryManager = null;
super.tearDown();
}
@@ -93,22 +137,22 @@ public class DataStallRecoveryManagerTest extends TelephonyTest {
private void sendValidationStatusCallback(@ValidationStatus int status) {
ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
- verify(mDataNetworkController)
+ verify(mDataNetworkController, times(2))
.registerDataNetworkControllerCallback(
dataNetworkControllerCallbackCaptor.capture());
DataNetworkControllerCallback dataNetworkControllerCallback =
- dataNetworkControllerCallbackCaptor.getValue();
+ dataNetworkControllerCallbackCaptor.getAllValues().get(0);
dataNetworkControllerCallback.onInternetDataNetworkValidationStatusChanged(status);
}
private void sendOnInternetDataNetworkCallback(boolean isConnected) {
ArgumentCaptor<DataNetworkControllerCallback> dataNetworkControllerCallbackCaptor =
ArgumentCaptor.forClass(DataNetworkControllerCallback.class);
- verify(mDataNetworkController)
+ verify(mDataNetworkController, times(2))
.registerDataNetworkControllerCallback(
dataNetworkControllerCallbackCaptor.capture());
DataNetworkControllerCallback dataNetworkControllerCallback =
- dataNetworkControllerCallbackCaptor.getValue();
+ dataNetworkControllerCallbackCaptor.getAllValues().get(0);
if (isConnected) {
List<DataNetwork> dataprofile = new ArrayList<>();
@@ -363,4 +407,105 @@ public class DataStallRecoveryManagerTest extends TelephonyTest {
}
assertThat(mDataStallRecoveryManager.mDataStallStartMs != 0).isTrue();
}
+
+ /**
+ * Tests the DSRM process to send three intents for three action changes.
+ */
+ @Test
+ public void testSendDSRMData() throws Exception {
+ ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+
+ logd("Set phone status to normal status.");
+ sendOnInternetDataNetworkCallback(true);
+ doReturn(mSignalStrength).when(mPhone).getSignalStrength();
+ doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
+
+ // Set the expected behavior of the DataStallRecoveryManager.
+ logd("Start DSRM process, set action to 1");
+ mDataStallRecoveryManager.setRecoveryAction(1);
+ logd("Sending validation failed callback");
+ sendValidationStatusCallback(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ processAllFutureMessages();
+
+ logd("Verify that the DataStallRecoveryManager sends the expected intents.");
+ verify(mPhone.getContext(), times(3)).sendBroadcast(captorIntent.capture());
+ logd(captorIntent.getAllValues().toString());
+ for (int i = 0; i < captorIntent.getAllValues().size(); i++) {
+ Intent intent = captorIntent.getAllValues().get(i);
+ // Check and assert if intent is null
+ assertNotNull(intent);
+ // Check and assert if intent is not ACTION_DATA_STALL_DETECTED
+ assertThat(intent.getAction()).isEqualTo(
+ TelephonyManager.ACTION_DATA_STALL_DETECTED);
+ // Get the extra data
+ Bundle bundle = (Bundle) intent.getExtra("EXTRA_DSRS_STATS_BUNDLE");
+ // Check and assert if bundle is null
+ assertNotNull(bundle);
+ // Dump bundle data
+ logd(bundle.toString());
+ int size = bundle.size();
+ logd("bundle size is " + size);
+ // Check if bundle size is 19
+ assertThat(size).isEqualTo(19);
+ }
+ }
+
+ /**
+ * Tests update action enable state and duration from global settings.
+ */
+ @Test
+ public void testUpdateGlobalSettings() throws Exception {
+ Field field = DataStallRecoveryManager.class.getDeclaredField("mPredictWaitingMillis");
+ field.setAccessible(true);
+
+ // Set duration to 10000/20000/30000/40000
+ Settings.Global.putString(
+ mFakeContentResolver, Settings.Global.DSRM_DURATION_MILLIS,
+ "10000,20000,30000,40000,0");
+ // Send onChange event with Settings.Global.DSRM_DURATION_MILLIS to fake ContentResolver
+ mFakeContentResolver.notifyChange(
+ Settings.Global.getUriFor(Settings.Global.DSRM_DURATION_MILLIS), null);
+ processAllFutureMessages();
+ // Verify that the durations are correct values.
+ assertThat(mDataStallRecoveryManager.getDataStallRecoveryDelayMillis(0)).isEqualTo(10000L);
+ assertThat(mDataStallRecoveryManager.getDataStallRecoveryDelayMillis(1)).isEqualTo(20000L);
+ assertThat(mDataStallRecoveryManager.getDataStallRecoveryDelayMillis(2)).isEqualTo(30000L);
+ assertThat(mDataStallRecoveryManager.getDataStallRecoveryDelayMillis(3)).isEqualTo(40000L);
+
+ // Set action enable state to true/false/false/false/true
+ Settings.Global.putString(
+ mFakeContentResolver, Settings.Global.DSRM_ENABLED_ACTIONS,
+ "true,false,false,false,true");
+ // Send onChange event with Settings.Global.DSRM_ENABLED_ACTIONS to fake ContentResolver
+ mFakeContentResolver.notifyChange(
+ Settings.Global.getUriFor(Settings.Global.DSRM_ENABLED_ACTIONS), null);
+ processAllFutureMessages();
+ // Verify that the action enable state are correct values.
+ assertThat(mDataStallRecoveryManager.shouldSkipRecoveryAction(0)).isEqualTo(false);
+ assertThat(mDataStallRecoveryManager.shouldSkipRecoveryAction(1)).isEqualTo(true);
+ assertThat(mDataStallRecoveryManager.shouldSkipRecoveryAction(2)).isEqualTo(true);
+ assertThat(mDataStallRecoveryManager.shouldSkipRecoveryAction(3)).isEqualTo(true);
+ assertThat(mDataStallRecoveryManager.shouldSkipRecoveryAction(4)).isEqualTo(false);
+ // Check the predict waiting millis
+ assertThat(field.get(mDataStallRecoveryManager)).isEqualTo(1000L);
+ // Test predict waiting millis to rollback to 0 if there is no global duration and action
+ // Set duration to empty
+ Settings.Global.putString(
+ mFakeContentResolver, Settings.Global.DSRM_DURATION_MILLIS,
+ "");
+ // Send onChange event with Settings.Global.DSRM_DURATION_MILLIS to fake ContentResolver
+ mFakeContentResolver.notifyChange(
+ Settings.Global.getUriFor(Settings.Global.DSRM_DURATION_MILLIS), null);
+ processAllFutureMessages();
+ // Set action to empty
+ Settings.Global.putString(
+ mFakeContentResolver, Settings.Global.DSRM_ENABLED_ACTIONS,
+ "");
+ // Send onChange event with Settings.Global.DSRM_ENABLED_ACTIONS to fake ContentResolver
+ mFakeContentResolver.notifyChange(
+ Settings.Global.getUriFor(Settings.Global.DSRM_ENABLED_ACTIONS), null);
+ processAllFutureMessages();
+ // Check if predict waiting millis is 0
+ assertThat(field.get(mDataStallRecoveryManager)).isEqualTo(0L);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
index 0eba3fe96f..3fa5ba577f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -27,6 +27,7 @@ import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TE
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+import static com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_VOICE_CALL_END;
import static com.android.internal.telephony.data.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
import static org.junit.Assert.assertEquals;
@@ -65,6 +66,8 @@ import android.telephony.PhoneCapability;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -118,11 +121,13 @@ public class PhoneSwitcherTest extends TelephonyTest {
private GsmCdmaCall mInactiveCall;
private GsmCdmaCall mDialCall;
private GsmCdmaCall mIncomingCall;
+ private GsmCdmaCall mAlertingCall;
private ISetOpportunisticDataCallback mSetOpptDataCallback1;
private ISetOpportunisticDataCallback mSetOpptDataCallback2;
PhoneSwitcher.ImsRegTechProvider mMockImsRegTechProvider;
private SubscriptionInfo mSubscriptionInfo;
private ISub mMockedIsub;
+ private AutoDataSwitchController mAutoDataSwitchController;
private PhoneSwitcher mPhoneSwitcherUT;
private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
@@ -137,6 +142,10 @@ public class PhoneSwitcherTest extends TelephonyTest {
private int mActiveModemCount = 2;
private int mSupportedModemCount = 2;
private int mMaxDataAttachModemCount = 1;
+ private AutoDataSwitchController.AutoDataSwitchControllerCallback mAutoDataSwitchCallback;
+ private TelephonyDisplayInfo mTelephonyDisplayInfo = new TelephonyDisplayInfo(
+ TelephonyManager.NETWORK_TYPE_NR,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false);
@Before
public void setUp() throws Exception {
@@ -153,11 +162,13 @@ public class PhoneSwitcherTest extends TelephonyTest {
mInactiveCall = mock(GsmCdmaCall.class);
mDialCall = mock(GsmCdmaCall.class);
mIncomingCall = mock(GsmCdmaCall.class);
+ mAlertingCall = mock(GsmCdmaCall.class);
mSetOpptDataCallback1 = mock(ISetOpportunisticDataCallback.class);
mSetOpptDataCallback2 = mock(ISetOpportunisticDataCallback.class);
mMockImsRegTechProvider = mock(PhoneSwitcher.ImsRegTechProvider.class);
mSubscriptionInfo = mock(SubscriptionInfo.class);
mMockedIsub = mock(ISub.class);
+ mAutoDataSwitchController = mock(AutoDataSwitchController.class);
PhoneCapability phoneCapability = new PhoneCapability(1, 1, null, false, new int[0]);
doReturn(phoneCapability).when(mPhoneConfigurationManager).getCurrentPhoneCapability();
@@ -167,6 +178,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
doReturn(Call.State.HOLDING).when(mHoldingCall).getState();
doReturn(Call.State.DIALING).when(mDialCall).getState();
doReturn(Call.State.INCOMING).when(mIncomingCall).getState();
+ doReturn(Call.State.ALERTING).when(mAlertingCall).getState();
doReturn(true).when(mInactiveCall).isIdle();
doReturn(false).when(mActiveCall).isIdle();
@@ -179,6 +191,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
doReturn(mMockedIsub).when(mIBinder).queryLocalInterface(anyString());
doReturn(mPhone).when(mPhone).getImsPhone();
mServiceManagerMockedServices.put("isub", mIBinder);
+
+ doReturn(mTelephonyDisplayInfo).when(mDisplayInfoController).getTelephonyDisplayInfo();
}
@After
@@ -187,6 +201,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
mSubChangedListener = null;
mConnectivityManager = null;
mNetworkProviderMessenger = null;
+ mTelephonyDisplayInfo = null;
super.tearDown();
}
@@ -374,22 +389,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
}
/** Test Data Auto Switch **/
-
- /**
- * Trigger conditions
- * 1. service state changes
- * 2. data setting changes
- * - user toggle data
- * - user toggle auto switch feature
- * 3. default network changes
- * - current network lost
- * - network become active on non-cellular network
- * 4. subscription changes
- * - slot/sub mapping changes
- */
@Test
- @SmallTest
- public void testAutoDataSwitchCancelScenario_onPrimary() throws Exception {
+ public void testAutoDataSwitch_retry() throws Exception {
initialize();
// Phone 0 has sub 1, phone 1 has sub 2.
// Sub 1 is default data sub.
@@ -397,142 +398,28 @@ public class PhoneSwitcherTest extends TelephonyTest {
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(1);
- // 0. When all conditions met
- prepareIdealAutoSwitchCondition();
+ mAutoDataSwitchCallback.onRequireValidation(1/*Phone2*/, true);
processAllFutureMessages();
- // Verify attempting to switch
+ // Mock validation failed, expect retry attempt
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcherUT.mValidationCallback));
- doReturn(true).when(mCellularNetworkValidator).isValidating();
-
- // 1. Service state becomes not ideal - primary is available again
- clearInvocations(mCellularNetworkValidator);
- serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
- processAllFutureMessages();
-
- verify(mCellularNetworkValidator).stopValidation();
-
- // 2.1 User data disabled on primary SIM
- prepareIdealAutoSwitchCondition();
- processAllFutureMessages();
- clearInvocations(mCellularNetworkValidator);
- doReturn(false).when(mPhone).isUserDataEnabled();
- mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
- processAllFutureMessages();
-
- verify(mCellularNetworkValidator).stopValidation();
-
- // 2.2 Auto switch feature is disabled
- prepareIdealAutoSwitchCondition();
- processAllFutureMessages();
- clearInvocations(mCellularNetworkValidator);
- doReturn(false).when(mPhone2).isDataAllowed();
- mDataSettingsManagerCallbacks.get(1).onDataEnabledChanged(false, 123 , "");
- processAllFutureMessages();
-
- verify(mCellularNetworkValidator).stopValidation();
-
- // 3.1 No default network
- prepareIdealAutoSwitchCondition();
- processAllFutureMessages();
- clearInvocations(mCellularNetworkValidator);
- doReturn(new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI))
- .when(mConnectivityManager).getNetworkCapabilities(any());
- mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
- processAllFutureMessages();
-
- verify(mCellularNetworkValidator).stopValidation();
- }
-
- public void testAutoSwitchToSecondarySucceed() {
- prepareIdealAutoSwitchCondition();
- processAllFutureMessages();
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2/*Phone2*/);
processAllMessages();
- // Confirm auto switched to secondary sub Id 1/phone 0
- assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
- }
- @Test
- @SmallTest
- public void testAutoDataSwitch_switchBackToPrimary() throws Exception {
- initialize();
- // Phone 0 has sub 1, phone 1 has sub 2.
- // Sub 1 is default data sub.
- setSlotIndexToSubId(0, 1);
- setSlotIndexToSubId(1, 2);
- setDefaultDataSubId(1);
-
- testAutoSwitchToSecondarySucceed();
- // 1.1 service state changes - primary becomes available, need validation pass to switch
- serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
- processAllFutureMessages();
- verify(mCellularNetworkValidator).validate(eq(1), anyLong(), eq(false),
- eq(mPhoneSwitcherUT.mValidationCallback));
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
- processAllMessages();
-
- assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId()); // since validation failed
-
- serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
- processAllFutureMessages();
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
- processAllMessages();
-
- assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since validation passed
-
- testAutoSwitchToSecondarySucceed();
- // 1.2 service state changes - secondary becomes unavailable, NO need validation
- // The later validation requirement overrides the previous
- serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_HOME/*need validate*/);
- serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*no need*/);
- processAllFutureMessages();
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
- processAllMessages();
-
- assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
-
- testAutoSwitchToSecondarySucceed();
- // 2.1 User data disabled on primary SIM
- clearInvocations(mCellularNetworkValidator);
- doReturn(false).when(mPhone).isUserDataEnabled();
- mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
- processAllFutureMessages();
-
- assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
- verify(mCellularNetworkValidator, never()).validate(eq(1), anyLong(), eq(false),
- eq(mPhoneSwitcherUT.mValidationCallback));
-
- testAutoSwitchToSecondarySucceed();
- // 2.2 Auto switch feature is disabled
- clearInvocations(mCellularNetworkValidator);
- doReturn(false).when(mPhone2).isDataAllowed();
- mDataSettingsManagerCallbacks.get(0).onDataEnabledChanged(false, 123 , "");
- processAllFutureMessages();
+ verify(mAutoDataSwitchController).evaluateRetryOnValidationFailed();
- assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
- verify(mCellularNetworkValidator, never()).validate(eq(1), anyLong(), eq(false),
- eq(mPhoneSwitcherUT.mValidationCallback));
-
- testAutoSwitchToSecondarySucceed();
- // 3.1 Default network is active on non-cellular transport
- clearInvocations(mCellularNetworkValidator);
- doReturn(new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI))
- .when(mConnectivityManager).getNetworkCapabilities(any());
- mPhoneSwitcherUT.sendEmptyMessage(EVENT_EVALUATE_AUTO_SWITCH);
+ // Test clear failed count upon switch succeeded.
+ mAutoDataSwitchCallback.onRequireValidation(1/*Phone2*/, true);
processAllFutureMessages();
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2/*Phone2*/);
processAllMessages();
- assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // since no need validation
+ verify(mAutoDataSwitchController).resetFailedCount();
}
@Test
- @SmallTest
- public void testAutoDataSwitchCancel_onSecondary() throws Exception {
+ public void testAutoDataSwitch_setNotification() throws Exception {
initialize();
// Phone 0 has sub 1, phone 1 has sub 2.
// Sub 1 is default data sub.
@@ -540,121 +427,50 @@ public class PhoneSwitcherTest extends TelephonyTest {
setSlotIndexToSubId(1, 2);
setDefaultDataSubId(1);
- testAutoSwitchToSecondarySucceed();
- clearInvocations(mCellularNetworkValidator);
- // attempts the switch back due to secondary becomes ROAMING
- serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
- processAllFutureMessages();
-
- verify(mCellularNetworkValidator).validate(eq(1), anyLong(), eq(false),
- eq(mPhoneSwitcherUT.mValidationCallback));
- doReturn(true).when(mCellularNetworkValidator).isValidating();
-
- // cancel the switch back attempt due to secondary back to HOME
- serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
- processAllMessages();
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 1);
+ // Verify no notification check if switch failed.
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1/*phoneId*/,
+ null, new Throwable())).sendToTarget();
processAllMessages();
+ verify(mAutoDataSwitchController, never()).displayAutoDataSwitchNotification(
+ anyInt(), anyBoolean());
- assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
- verify(mCellularNetworkValidator).stopValidation();
- }
-
- @Test
- @SmallTest
- public void testAutoDataSwitch_retry() throws Exception {
- initialize();
- // Phone 0 has sub 1, phone 1 has sub 2.
- // Sub 1 is default data sub.
- setSlotIndexToSubId(0, 1);
- setSlotIndexToSubId(1, 2);
- setDefaultDataSubId(1);
-
- prepareIdealAutoSwitchCondition();
- processAllFutureMessages();
-
- // Verify attempting to switch
- verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
- eq(mPhoneSwitcherUT.mValidationCallback));
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
+ // Verify for switch not due to auto
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1/*phoneId*/,
+ null, null)).sendToTarget();
processAllMessages();
+ verify(mAutoDataSwitchController).displayAutoDataSwitchNotification(1/*phoneId*/, false);
- assertTrue(mPhoneSwitcherUT.hasMessages(EVENT_EVALUATE_AUTO_SWITCH));
-
- processAllFutureMessages();
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2);
- processAllFutureMessages();
-
- assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
- }
-
- @Test
- @SmallTest
- public void testAutoDataSwitch_setNotification() throws Exception {
- SubscriptionInfo mockedInfo = mock(SubscriptionInfo.class);
- doReturn(false).when(mockedInfo).isOpportunistic();
- doReturn(mockedInfo).when(mSubscriptionManagerService).getSubscriptionInfo(anyInt());
- initialize();
- // Phone 0 has sub 1, phone 1 has sub 2.
- // Sub 1 is default data sub.
- setSlotIndexToSubId(0, 1);
- setSlotIndexToSubId(1, 2);
- setDefaultDataSubId(1);
-
- testAutoSwitchToSecondarySucceed();
- clearInvocations(mSubscriptionManagerService);
- Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1, null, null))
- .sendToTarget();
+ // Verify for switch due to auto
+ mAutoDataSwitchCallback.onRequireValidation(1/*Phone2*/, false);
processAllMessages();
- verify(mSubscriptionManagerService).getSubscriptionInfo(2);
- // switch back to primary
- clearInvocations(mSubscriptionManagerService);
- Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(0, null, null))
- .sendToTarget();
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(true, 2/*Phone2*/);
+ Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1/*phoneId*/,
+ null, null)).sendToTarget();
processAllMessages();
- verify(mSubscriptionManagerService, never()).getSubscriptionInfo(1);
- Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, new AsyncResult(1, null, null))
- .sendToTarget();
- processAllMessages();
- verify(mSubscriptionManagerService, never()).getSubscriptionInfo(2);
+ verify(mAutoDataSwitchController).displayAutoDataSwitchNotification(1/*phoneId*/, true);
}
@Test
@SmallTest
public void testAutoDataSwitch_exemptPingTest() throws Exception {
initialize();
- // Change resource overlay
- doReturn(false).when(mDataConfigManager).isPingTestBeforeAutoDataSwitchRequired();
- mPhoneSwitcherUT = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper());
- processAllMessages();
// Phone 0 has sub 1, phone 1 has sub 2.
// Sub 1 is default data sub.
setSlotIndexToSubId(0, 1);
setSlotIndexToSubId(1, 2);
- setDefaultDataSubId(1);
- //1. Attempting to switch to nDDS, switch even if validation failed
- prepareIdealAutoSwitchCondition();
+ //Attempting to switch to nDDS, switch even if validation failed
+ mAutoDataSwitchCallback.onRequireValidation(1/*Phone2*/, false);
processAllFutureMessages();
verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcherUT.mValidationCallback));
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2);
+ mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 2/*Phone2*/);
processAllMessages();
assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId()); // switch succeeds
-
- //2. Attempting to switch back to DDS, switch even if validation failed
- serviceStateChanged(0, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
- processAllFutureMessages();
- verify(mCellularNetworkValidator).validate(eq(1), anyLong(), eq(false),
- eq(mPhoneSwitcherUT.mValidationCallback));
- mPhoneSwitcherUT.mValidationCallback.onValidationDone(false, 1);
- processAllMessages();
-
- assertEquals(1, mPhoneSwitcherUT.getActiveDataSubId()); // switch succeeds
}
/**
@@ -1096,13 +912,19 @@ public class PhoneSwitcherTest extends TelephonyTest {
// Phone 0 should be the default data phoneId.
assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
- // Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
- // trigger data switch.
+ // Dialing shouldn't trigger switch because we give modem time to deal with the dialing call
+ // first. Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled.
doReturn(mImsPhone).when(mPhone2).getImsPhone();
doReturn(true).when(mPhone2).isDataAllowed();
mockImsRegTech(1, REGISTRATION_TECH_LTE);
notifyPhoneAsInDial(mImsPhone);
+ // Phone1 should remain as the preferred data phone
+ assertEquals(0, mPhoneSwitcherUT.getPreferredDataPhoneId());
+
+ // Dialing -> Alert, should trigger phone switch
+ notifyPhoneAsAlerting(mImsPhone);
+
// Phone2 should be preferred data phone
assertEquals(1, mPhoneSwitcherUT.getPreferredDataPhoneId());
}
@@ -1200,7 +1022,6 @@ public class PhoneSwitcherTest extends TelephonyTest {
}
@Test
- @SmallTest
public void testNonDefaultDataPhoneInCall() throws Exception {
doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
initialize();
@@ -1250,11 +1071,9 @@ public class PhoneSwitcherTest extends TelephonyTest {
// Phone(DDS) call ended.
// Honor auto data switch's suggestion: if DDS is OOS, auto switch to Phone2(nDDS).
- serviceStateChanged(1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
- serviceStateChanged(0, NetworkRegistrationInfo
- .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING);
- doReturn(null).when(mConnectivityManager).getNetworkCapabilities(any());
notifyPhoneAsInactive(mPhone);
+ verify(mAutoDataSwitchController).evaluateAutoDataSwitch(EVALUATION_REASON_VOICE_CALL_END);
+ mAutoDataSwitchCallback.onRequireValidation(1 /*Phone2*/, true);
// verify immediately switch back to DDS upon call ends
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
@@ -1573,13 +1392,22 @@ public class PhoneSwitcherTest extends TelephonyTest {
// Switch to primary before a primary is selected/inactive.
setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
- SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, mSetOpptDataCallback1);
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID, false, mSetOpptDataCallback1);
processAllMessages();
assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
mPhoneSwitcherUT.getAutoSelectedDataSubId());
verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+ // Verify that the switch to default sub is successful
+ mPhoneSwitcherUT.trySetOpportunisticDataSubscription(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, mSetOpptDataCallback1);
+ processAllMessages();
+
+ assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+ mPhoneSwitcherUT.getAutoSelectedDataSubId());
+ verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_SUCCESS);
+
// once the primary is selected, it becomes the active sub.
setDefaultDataSubId(2);
assertEquals(2, mPhoneSwitcherUT.getActiveDataSubId());
@@ -1930,6 +1758,12 @@ public class PhoneSwitcherTest extends TelephonyTest {
processAllMessages();
}
+ private void notifyPhoneAsAlerting(Phone phone) {
+ doReturn(mAlertingCall).when(phone).getForegroundCall();
+ mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
+ processAllMessages();
+ }
+
private void notifyPhoneAsInIncomingCall(Phone phone) {
doReturn(mIncomingCall).when(phone).getForegroundCall();
mPhoneSwitcherUT.sendEmptyMessage(EVENT_PRECISE_CALL_STATE_CHANGED);
@@ -2042,6 +1876,13 @@ public class PhoneSwitcherTest extends TelephonyTest {
(Map<Integer, DataSettingsManager.DataSettingsManagerCallback>)
field.get(mPhoneSwitcherUT);
+ field = PhoneSwitcher.class.getDeclaredField("mAutoDataSwitchCallback");
+ field.setAccessible(true);
+ mAutoDataSwitchCallback = (AutoDataSwitchController.AutoDataSwitchControllerCallback)
+ field.get(mPhoneSwitcherUT);
+
+ replaceInstance(PhoneSwitcher.class, "mAutoDataSwitchController", mPhoneSwitcherUT,
+ mAutoDataSwitchController);
processAllMessages();
verify(mTelephonyRegistryManager).addOnSubscriptionsChangedListener(any(), any());
@@ -2058,6 +1899,9 @@ public class PhoneSwitcherTest extends TelephonyTest {
doReturn(true).when(mPhone2).isUserDataEnabled();
doReturn(mDataSettingsManager2).when(mPhone2).getDataSettingsManager();
doReturn(mSST2).when(mPhone2).getServiceStateTracker();
+ doReturn(mDisplayInfoController).when(mPhone2).getDisplayInfoController();
+ doReturn(mSignalStrengthController).when(mPhone2).getSignalStrengthController();
+ doReturn(mSignalStrength).when(mPhone2).getSignalStrength();
for (int i = 0; i < supportedModemCount; i++) {
mSlotIndexToSubId[i] = new int[1];
mSlotIndexToSubId[i][0] = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -2192,12 +2036,9 @@ public class PhoneSwitcherTest extends TelephonyTest {
private void setDefaultDataSubId(int defaultDataSub) {
mDefaultDataSub = defaultDataSub;
doReturn(mDefaultDataSub).when(mSubscriptionManagerService).getDefaultDataSubId();
- if (defaultDataSub == 1) {
- doReturn(true).when(mPhone).isUserDataEnabled();
- doReturn(false).when(mPhone2).isUserDataEnabled();
- } else {
- doReturn(false).when(mPhone).isUserDataEnabled();
- doReturn(true).when(mPhone2).isUserDataEnabled();
+ for (Phone phone : mPhones) {
+ doAnswer(invocation -> phone.getSubId() == mDefaultDataSub)
+ .when(phone).isUserDataEnabled();
}
sendDefaultDataSubChanged();
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS b/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
new file mode 100644
index 0000000000..2a7677001b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
@@ -0,0 +1,9 @@
+# automatically inherit owners from fw/opt/telephony
+
+hwangoo@google.com
+forestchoi@google.com
+avinashmp@google.com
+mkoon@google.com
+seheele@google.com
+radhikaagrawal@google.com
+jdyou@google.com
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index b74c8af2dc..cd9d9ad1b1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -47,6 +47,7 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -1049,6 +1050,67 @@ public class ImsPhoneTest extends TelephonyTest {
mContextFixture.addCallingOrSelfPermission("");
}
+ @Test
+ @SmallTest
+ public void testSetPhoneNumberForSourceIgnoreGlobalPhoneNumberFormat() {
+ // In reality the method under test runs in phone process so has MODIFY_PHONE_STATE
+ mContextFixture.addCallingOrSelfPermission(MODIFY_PHONE_STATE);
+ int subId = 1;
+ doReturn(subId).when(mPhone).getSubId();
+ doReturn(new SubscriptionInfoInternal.Builder().setId(subId).setSimSlotIndex(0)
+ .setCountryIso("gb").build()).when(mSubscriptionManagerService)
+ .getSubscriptionInfoInternal(subId);
+ // Set carrier config to ignore global phone number format
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(
+ CarrierConfigManager.Ims.KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL, true);
+ doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(eq(subId),
+ eq(CarrierConfigManager.Ims.KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL));
+
+ // 1. Two non-global phone number; 1st is set.
+ Uri[] associatedUris = new Uri[] {
+ Uri.parse("sip:01012345678@lte-uplus.co.kr"),
+ Uri.parse("tel:01012345678")
+ };
+ mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
+
+ verify(mSubscriptionManagerService).setNumberFromIms(subId, "01012345678");
+
+ // 2. 1st non-global phone number and 2nd global number; 2nd is set.
+ associatedUris = new Uri[] {
+ Uri.parse("sip:01012345678@lte-uplus.co.kr"),
+ Uri.parse("tel:+821012345678")
+ };
+ mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
+
+ verify(mSubscriptionManagerService).setNumberFromIms(subId, "+821012345678");
+
+ // 3. 1st sip-uri is not phone number and 2nd valid: 2nd is set.
+ associatedUris = new Uri[] {
+ Uri.parse("sip:john.doe@ims.x.com"),
+ Uri.parse("sip:01022223333@lte-uplus.co.kr"),
+ Uri.parse("tel:01022223333")
+ };
+ mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
+
+ verify(mSubscriptionManagerService).setNumberFromIms(subId, "01022223333");
+
+ clearInvocations(mSubscriptionManagerService);
+
+ // 4. Invalid phone number : not set.
+ associatedUris = new Uri[] {
+ Uri.parse("sip:john.doe@ims.x.com"),
+ Uri.parse("sip:hone3333@lte-uplus.co.kr"),
+ Uri.parse("tel:abcd1234555")
+ };
+ mImsPhoneUT.setPhoneNumberForSourceIms(associatedUris);
+
+ verify(mSubscriptionManagerService, never()).setNumberFromIms(anyInt(), anyString());
+
+ // Clean up
+ mContextFixture.addCallingOrSelfPermission("");
+ }
+
/**
* Verifies that valid radio technology is passed to RIL
* when IMS registration state changes to registered.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
index 7b66a52e40..f186f987f2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
@@ -30,6 +30,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -152,6 +153,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -324,6 +326,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -336,6 +339,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -373,6 +377,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
@@ -385,6 +390,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -416,6 +422,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -457,6 +464,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -495,6 +503,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = serviceStateCaptor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -507,6 +516,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, serviceSwitch.ratFrom);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratTo);
@@ -548,6 +558,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -559,6 +570,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(CARRIER1_ID, state.carrierId);
assertEquals(100L, state.totalTimeMillis);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -588,6 +600,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(0L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -629,6 +642,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -641,6 +655,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(2);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -653,6 +668,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(400L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(3);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -665,6 +681,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(800L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -710,6 +727,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
@@ -722,6 +740,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(5000L, state.totalTimeMillis);
assertEquals(true, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(2);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -734,6 +753,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -750,11 +770,31 @@ public class ServiceStateStatsTest extends TelephonyTest {
mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UMTS);
mockWwanCsRat(TelephonyManager.NETWORK_TYPE_UMTS);
doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mImsStats).getImsVoiceRadioTech();
- doReturn(ServiceState.ROAMING_TYPE_INTERNATIONAL).when(mServiceState).getVoiceRoamingType();
+ NetworkRegistrationInfo voiceNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ // This sets mNetworkRegistrationState
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+ .build();
+ voiceNri.setRoamingType(ServiceState.ROAMING_TYPE_INTERNATIONAL);
+ doReturn(voiceNri)
+ .when(mServiceState)
+ .getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
mServiceStateStats.onServiceStateChanged(mServiceState);
mServiceStateStats.incTimeMillis(200L);
// Voice and data roaming
- doReturn(ServiceState.ROAMING_TYPE_INTERNATIONAL).when(mServiceState).getDataRoamingType();
+ NetworkRegistrationInfo dataNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ // This sets mNetworkRegistrationState
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+ .build();
+ dataNri.setRoamingType(ServiceState.ROAMING_TYPE_INTERNATIONAL);
+ doReturn(dataNri)
+ .when(mServiceState)
+ .getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
mServiceStateStats.onServiceStateChanged(mServiceState);
mServiceStateStats.incTimeMillis(400L);
@@ -779,6 +819,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = serviceStateCaptor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
@@ -791,6 +832,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = serviceStateCaptor.getAllValues().get(2);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
@@ -803,6 +845,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(400L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
@@ -817,6 +860,76 @@ public class ServiceStateStatsTest extends TelephonyTest {
@Test
@SmallTest
+ public void onServiceStateChanged_roamingWithOverride() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // Voice roaming
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getDataNetworkType();
+ NetworkRegistrationInfo roamingNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ // This sets mNetworkRegistrationState
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+ .build();
+ roamingNri.setRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
+ doReturn(roamingNri).when(mServiceState)
+ .getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN));
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mImsStats).getImsVoiceRadioTech();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(400L);
+
+ // There should be 2 service states and 1 data service switch (LTE to UMTS)
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> serviceStateCaptor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ ArgumentCaptor<CellularDataServiceSwitch> serviceSwitchCaptor =
+ ArgumentCaptor.forClass(CellularDataServiceSwitch.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(
+ serviceStateCaptor.capture(), serviceSwitchCaptor.capture());
+ CellularServiceState state = serviceStateCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
+ state = serviceStateCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ // Atom should show roaming, despite type being unknown
+ assertEquals(ServiceState.ROAMING_TYPE_UNKNOWN, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_UNKNOWN, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(400L, state.totalTimeMillis);
+ assertEquals(false, state.isEmergencyOnly);
+ assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
+ CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
+ assertEquals(0, serviceSwitch.simSlotIndex);
+ assertFalse(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ assertNull(serviceSwitchCaptor.getAllValues().get(1)); // produced by conclude()
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
public void onServiceStateChanged_dualSim() throws Exception {
// Using default service state for LTE
// Only difference between the 2 slots is slot index
@@ -860,6 +973,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = serviceStateCaptor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -872,6 +986,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = serviceStateCaptor.getAllValues().get(2);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
@@ -884,6 +999,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = serviceStateCaptor.getAllValues().get(3);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
@@ -896,6 +1012,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
@@ -957,6 +1074,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(100L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
state = captor.getAllValues().get(1);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
@@ -969,6 +1087,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
assertEquals(200L, state.totalTimeMillis);
assertEquals(false, state.isEmergencyOnly);
assertEquals(true, state.isInternetPdnUp);
+ assertEquals(true, state.isDataEnabled);
verifyNoMoreInteractions(mPersistAtomsStorage);
}
@@ -1036,6 +1155,141 @@ public class ServiceStateStatsTest extends TelephonyTest {
verifyNoMoreInteractions(mPersistAtomsStorage);
}
+ @Test
+ @SmallTest
+ public void isNetworkRoaming_nullServiceState() throws Exception {
+ boolean result = ServiceStateStats.isNetworkRoaming(null);
+
+ assertEquals(false, result);
+ }
+
+ @Test
+ @SmallTest
+ public void isNetworkRoaming_notRoaming() throws Exception {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ // This sets mNetworkRegistrationState
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ nri.setRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
+ doReturn(nri).when(mServiceState).getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ doReturn(nri).when(mServiceState).getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ boolean result = ServiceStateStats.isNetworkRoaming(mServiceState);
+ boolean resultCs = ServiceStateStats.isNetworkRoaming(
+ mServiceState, NetworkRegistrationInfo.DOMAIN_CS);
+ boolean resultPs = ServiceStateStats.isNetworkRoaming(
+ mServiceState, NetworkRegistrationInfo.DOMAIN_PS);
+
+ assertEquals(false, result);
+ assertEquals(false, resultCs);
+ assertEquals(false, resultPs);
+ }
+
+ @Test
+ @SmallTest
+ public void isNetworkRoaming_csRoaming() throws Exception {
+ NetworkRegistrationInfo roamingNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ // This sets mNetworkRegistrationState
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+ .build();
+ roamingNri.setRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
+ doReturn(roamingNri).when(mServiceState).getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ boolean result = ServiceStateStats.isNetworkRoaming(mServiceState);
+ boolean resultCs = ServiceStateStats.isNetworkRoaming(
+ mServiceState, NetworkRegistrationInfo.DOMAIN_CS);
+ boolean resultPs = ServiceStateStats.isNetworkRoaming(
+ mServiceState, NetworkRegistrationInfo.DOMAIN_PS);
+
+ assertEquals(true, result);
+ assertEquals(true, resultCs);
+ assertEquals(false, resultPs);
+ }
+
+ @Test
+ @SmallTest
+ public void isNetworkRoaming_psRoaming() throws Exception {
+ NetworkRegistrationInfo roamingNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ // This sets mNetworkRegistrationState
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+ .build();
+ roamingNri.setRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
+ doReturn(roamingNri).when(mServiceState).getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ boolean result = ServiceStateStats.isNetworkRoaming(mServiceState);
+ boolean resultCs = ServiceStateStats.isNetworkRoaming(
+ mServiceState, NetworkRegistrationInfo.DOMAIN_CS);
+ boolean resultPs = ServiceStateStats.isNetworkRoaming(
+ mServiceState, NetworkRegistrationInfo.DOMAIN_PS);
+
+ assertEquals(true, result);
+ assertEquals(false, resultCs);
+ assertEquals(true, resultPs);
+ }
+
+ @Test
+ @SmallTest
+ public void isNetworkRoaming_bothRoaming() throws Exception {
+ NetworkRegistrationInfo roamingNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ // This sets mNetworkRegistrationState
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+ .build();
+ roamingNri.setRoamingType(ServiceState.ROAMING_TYPE_NOT_ROAMING);
+ doReturn(roamingNri).when(mServiceState).getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ doReturn(roamingNri).when(mServiceState).getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ boolean result = ServiceStateStats.isNetworkRoaming(mServiceState);
+ boolean resultCs = ServiceStateStats.isNetworkRoaming(
+ mServiceState, NetworkRegistrationInfo.DOMAIN_CS);
+ boolean resultPs = ServiceStateStats.isNetworkRoaming(
+ mServiceState, NetworkRegistrationInfo.DOMAIN_PS);
+
+ assertEquals(true, result);
+ assertEquals(true, resultCs);
+ assertEquals(true, resultPs);
+ }
+
+ @Test
+ @SmallTest
+ public void onVoiceServiceStateOverrideChanged_voiceCallingCapabilityChange() {
+ // Using default service state for LTE
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // Voice Calling registered
+ mServiceStateStats.onVoiceServiceStateOverrideChanged(true);
+ mServiceStateStats.incTimeMillis(100L);
+ // Voice Calling unregistered
+ mServiceStateStats.onVoiceServiceStateOverrideChanged(false);
+ mServiceStateStats.incTimeMillis(100L);
+ // Voice Calling unregistered again. Same state should not generate a new atom
+ mServiceStateStats.onVoiceServiceStateOverrideChanged(false);
+ mServiceStateStats.incTimeMillis(100L);
+
+ // There should be 3 service state updates
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(3))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(false, state.overrideVoiceService);
+ state = captor.getAllValues().get(1);
+ assertEquals(true, state.overrideVoiceService);
+ state = captor.getAllValues().get(2);
+ assertEquals(false, state.overrideVoiceService);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
private void mockWwanPsRat(@NetworkType int rat) {
mockWwanRat(
NetworkRegistrationInfo.DOMAIN_PS,
@@ -1088,6 +1342,7 @@ public class ServiceStateStatsTest extends TelephonyTest {
doReturn(1).when(mSecondPhone).getPhoneId();
doReturn(1).when(mUiccController).getSlotIdFromPhoneId(1);
doReturn(carrierId).when(mSecondPhone).getCarrierId();
+ doReturn(mDataSettingsManager).when(mSecondPhone).getDataSettingsManager();
doReturn(true).when(mPhysicalSlot1).isActive();
doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot1).getCardState();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index 2ca0b167e6..b358b6daa4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -44,13 +44,16 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.os.Looper;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.DisconnectCause;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDataConnectionState;
import android.telephony.PreciseDisconnectCause;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.test.suitebuilder.annotation.SmallTest;
@@ -155,19 +158,17 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
mCsCall1 = mock(GsmCdmaCall.class);
mImsCall0 = mock(ImsPhoneCall.class);
mImsCall1 = mock(ImsPhoneCall.class);
-
replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mSecondPhone});
doReturn(CARRIER_ID_SLOT_0).when(mPhone).getCarrierId();
- // mPhone's mSST/mServiceState has been set up by TelephonyTest
+ // mPhone's mContext/mSST/mServiceState has been set up by TelephonyTest
doReturn(CARRIER_ID_SLOT_1).when(mSecondPhone).getCarrierId();
+ doReturn(mContext).when(mSecondPhone).getContext();
doReturn(mSignalStrength).when(mSecondPhone).getSignalStrength();
doReturn(mSecondServiceStateTracker).when(mSecondPhone).getServiceStateTracker();
doReturn(mSecondServiceState).when(mSecondServiceStateTracker).getServiceState();
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UNKNOWN);
- doReturn(false).when(mServiceState).getVoiceRoaming();
setServiceState(mSecondServiceState, TelephonyManager.NETWORK_TYPE_UNKNOWN);
- doReturn(false).when(mSecondServiceState).getVoiceRoaming();
doReturn(true).when(mPhysicalSlot).isActive();
doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot).getCardState();
@@ -196,6 +197,10 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
doReturn(PhoneConstants.PHONE_TYPE_GSM).when(mGsmConnection1).getPhoneType();
doReturn(false).when(mGsmConnection1).isEmergencyCall();
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
mVoiceCallSessionStats0 = new TestableVoiceCallSessionStats(0, mPhone);
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
mVoiceCallSessionStats1 = new TestableVoiceCallSessionStats(1, mSecondPhone);
@@ -204,6 +209,8 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
@After
public void tearDown() throws Exception {
+ DataConnectionStateTracker.getInstance(0).stop();
+ DataConnectionStateTracker.getInstance(1).stop();
mVoiceCallSessionStats0 = null;
mVoiceCallSessionStats1 = null;
super.tearDown();
@@ -754,9 +761,17 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
@SmallTest
public void singleImsCall_roaming() {
setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ NetworkRegistrationInfo roamingNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ // This sets mNetworkRegistrationState
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING)
+ .build();
+ doReturn(roamingNri).when(mServiceState)
+ .getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
doReturn(mImsPhone).when(mPhone).getImsPhone();
- doReturn(true).when(mServiceState).getVoiceRoaming();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
doReturn(mImsCall0).when(mImsConnection0).getCall();
@@ -870,6 +885,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall.mainCodecQuality =
VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.ratSwitchCount = 2L;
+ expectedCall.ratSwitchCountAfterConnected = 2L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.bandAtEnd = 0;
expectedCall.callDuration =
@@ -947,6 +963,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall.mainCodecQuality =
VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.ratSwitchCount = 3L;
+ expectedCall.ratSwitchCountAfterConnected = 3L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.bandAtEnd = 0;
expectedCall.callDuration =
@@ -1139,6 +1156,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 1L;
+ expectedCall0.ratSwitchCountAfterConnected = 1L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
expectedCall0.bandAtEnd = 0;
expectedCall0.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
@@ -1161,6 +1179,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 2L;
+ expectedCall1.ratSwitchCountAfterConnected = 2L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall1.bandAtEnd = 0;
expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
@@ -1269,6 +1288,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 0;
expectedCall0.ratSwitchCount = 2L;
+ expectedCall0.ratSwitchCountAfterConnected = 2L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall0.bandAtEnd = 0;
expectedCall0.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
@@ -1291,6 +1311,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 1;
expectedCall1.ratSwitchCount = 1L;
+ expectedCall1.ratSwitchCountAfterConnected = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
expectedCall1.bandAtEnd = 0;
expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
@@ -1399,6 +1420,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 0L;
+ expectedCall0.ratSwitchCountAfterConnected = 0L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE;
// call 1 starts later, MT
doReturn(true).when(mImsConnection1).isIncoming();
@@ -1419,6 +1441,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 1L;
+ expectedCall1.ratSwitchCountAfterConnected = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
expectedCall1.bandAtEnd = 0;
expectedCall1.lastKnownRat = TelephonyManager.NETWORK_TYPE_HSPA;
@@ -1505,6 +1528,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall.setupDurationMillis = 5000;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.CALL_REJECTED;
expectedCall.ratSwitchCount = 1L;
+ expectedCall.ratSwitchCountAfterConnected = 0L;
expectedCall.setupFailed = true;
expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -1567,6 +1591,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.bandAtEnd = 0;
expectedCall.ratSwitchCount = 1L;
+ expectedCall.ratSwitchCountAfterConnected = 0L;
expectedCall.setupFailed = true;
expectedCall.setupDurationMillis = 13000;
expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
@@ -1626,6 +1651,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall.setupDurationMillis = 5000;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.ratSwitchCount = 1L;
+ expectedCall.ratSwitchCountAfterConnected = 0L;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
expectedCall.mainCodecQuality =
@@ -1805,6 +1831,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall.setupFailed = false;
expectedCall.srvccFailureCount = 2L;
expectedCall.ratSwitchCount = 1L;
+ expectedCall.ratSwitchCountAfterConnected = 1L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -1957,6 +1984,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall.srvccCompleted = true;
expectedCall.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
expectedCall.ratSwitchCount = 1L;
+ expectedCall.ratSwitchCountAfterConnected = 1L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
@@ -2055,6 +2083,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 1L;
+ expectedCall0.ratSwitchCountAfterConnected = 1L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall0.bandAtEnd = 0;
expectedCall0.srvccCompleted = true;
@@ -2082,6 +2111,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 1L;
+ expectedCall1.ratSwitchCountAfterConnected = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall1.bandAtEnd = 0;
expectedCall1.srvccCompleted = true;
@@ -2163,6 +2193,322 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
@Test
@SmallTest
+ public void singleCsCall_handover() {
+ doReturn(false).when(mGsmConnection0).isIncoming();
+ doReturn(2000L).when(mGsmConnection0).getCreateTime();
+ doReturn(mCsCall0).when(mGsmConnection0).getCall();
+ VoiceCallSession expectedCall =
+ makeSlot0CallProto(
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS,
+ VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ DisconnectCause.NORMAL);
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
+ expectedCall.setupDurationMillis = 5000;
+ expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
+ expectedCall.ratSwitchCount = 1L;
+ expectedCall.ratSwitchCountAfterConnected = 0L;
+ expectedCall.setupFailed = false;
+ expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.lastKnownRat = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.handoverInProgress = false;
+ VoiceCallRatUsage expectedRatUsageLte =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
+ VoiceCallRatUsage expectedRatUsageUmts =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 3000L, 12000L, 1L);
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+ mVoiceCallSessionStats0.setTimeMillis(2000L);
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(Call.State.DIALING).when(mCsCall0).getState();
+ doReturn(Call.State.DIALING).when(mGsmConnection0).getState();
+ doReturn(DisconnectCause.NOT_DISCONNECTED).when(mGsmConnection0).getDisconnectCause();
+ mVoiceCallSessionStats0.onRilDial(mGsmConnection0);
+ mVoiceCallSessionStats0.setTimeMillis(3000L);
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_UMTS);
+ mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
+ mVoiceCallSessionStats0.setTimeMillis(3100L);
+ mVoiceCallSessionStats0.onAudioCodecChanged(mGsmConnection0, DriverCall.AUDIO_QUALITY_AMR);
+ mVoiceCallSessionStats0.setTimeMillis(7000L);
+ doReturn(Call.State.ALERTING).when(mCsCall0).getState();
+ doReturn(Call.State.ALERTING).when(mGsmConnection0).getState();
+ mVoiceCallSessionStats0.onRilCallListChanged(List.of(mGsmConnection0));
+ mVoiceCallSessionStats0.setTimeMillis(10000L);
+ doReturn(Call.State.ACTIVE).when(mCsCall0).getState();
+ doReturn(Call.State.ACTIVE).when(mGsmConnection0).getState();
+ mVoiceCallSessionStats0.onRilCallListChanged(List.of(mGsmConnection0));
+ mVoiceCallSessionStats0.setTimeMillis(11000L);
+ // connection state changes for IMS APN shouldn't have impact on cs call
+ mVoiceCallSessionStats0.onPreciseDataConnectionStateChanged(
+ makePreciseDataConnectionState(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ TelephonyManager.DATA_HANDOVER_IN_PROGRESS, ApnSetting.TYPE_IMS));
+ mVoiceCallSessionStats0.setTimeMillis(12000L);
+ doReturn(DisconnectCause.NORMAL).when(mGsmConnection0).getDisconnectCause();
+ doReturn(PreciseDisconnectCause.NORMAL).when(mGsmConnection0).getPreciseDisconnectCause();
+ mVoiceCallSessionStats0.onRilCallListChanged(List.of(mGsmConnection0));
+
+ ArgumentCaptor<VoiceCallSession> callCaptor =
+ ArgumentCaptor.forClass(VoiceCallSession.class);
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ assertProtoEquals(expectedCall, callCaptor.getValue());
+ assertSortedProtoArrayEquals(
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ ratUsage.get());
+ }
+
+ @Test
+ @SmallTest
+ public void singleCall_callStartedDuringHandover() {
+ // set last IMS APN connection state with handover in progress
+ DataConnectionStateTracker.getInstance(0).notifyDataConnectionStateChanged(
+ makePreciseDataConnectionState(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ TelephonyManager.DATA_HANDOVER_IN_PROGRESS, ApnSetting.TYPE_IMS));
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(true).when(mImsConnection0).isIncoming();
+ doReturn(2000L).when(mImsConnection0).getCreateTime();
+ doReturn(mImsCall0).when(mImsConnection0).getCall();
+ doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ VoiceCallSession expectedCall =
+ makeSlot0CallProto(
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+ VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ ImsReasonInfo.CODE_USER_TERMINATED);
+ expectedCall.setupDurationMillis = 80;
+ expectedCall.setupFailed = false;
+ expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.handoverInProgress = true;
+ VoiceCallRatUsage expectedRatUsage =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+ mVoiceCallSessionStats0.setTimeMillis(2000L);
+ doReturn(Call.State.INCOMING).when(mImsCall0).getState();
+ doReturn(Call.State.INCOMING).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onImsCallReceived(mImsConnection0);
+ mVoiceCallSessionStats0.setTimeMillis(2100L);
+ mVoiceCallSessionStats0.onAudioCodecChanged(
+ mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_AMR);
+ mVoiceCallSessionStats0.setTimeMillis(2200L);
+ mVoiceCallSessionStats0.onImsAcceptCall(List.of(mImsConnection0));
+ mVoiceCallSessionStats0.setTimeMillis(2280L);
+ doReturn(Call.State.ACTIVE).when(mImsCall0).getState();
+ doReturn(Call.State.ACTIVE).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+ mVoiceCallSessionStats0.setTimeMillis(12000L);
+ mVoiceCallSessionStats0.onImsCallTerminated(
+ mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED, 0));
+
+ ArgumentCaptor<VoiceCallSession> callCaptor =
+ ArgumentCaptor.forClass(VoiceCallSession.class);
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ assertProtoEquals(expectedCall, callCaptor.getValue());
+ assertThat(ratUsage.get()).hasLength(1);
+ assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+ }
+
+ @Test
+ @SmallTest
+ public void singleCall_callTerminatedDuringHandover() {
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(true).when(mImsConnection0).isIncoming();
+ doReturn(2000L).when(mImsConnection0).getCreateTime();
+ doReturn(mImsCall0).when(mImsConnection0).getCall();
+ doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ VoiceCallSession expectedCall =
+ makeSlot0CallProto(
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+ VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ ImsReasonInfo.CODE_USER_TERMINATED);
+ expectedCall.setupDurationMillis = 80;
+ expectedCall.setupFailed = false;
+ expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.handoverInProgress = true;
+ VoiceCallRatUsage expectedRatUsage =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+ mVoiceCallSessionStats0.setTimeMillis(2000L);
+ doReturn(Call.State.INCOMING).when(mImsCall0).getState();
+ doReturn(Call.State.INCOMING).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onImsCallReceived(mImsConnection0);
+ mVoiceCallSessionStats0.setTimeMillis(2100L);
+ mVoiceCallSessionStats0.onAudioCodecChanged(
+ mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_AMR);
+ mVoiceCallSessionStats0.setTimeMillis(2200L);
+ mVoiceCallSessionStats0.onImsAcceptCall(List.of(mImsConnection0));
+ mVoiceCallSessionStats0.setTimeMillis(2280L);
+ doReturn(Call.State.ACTIVE).when(mImsCall0).getState();
+ doReturn(Call.State.ACTIVE).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+ mVoiceCallSessionStats0.setTimeMillis(9500L);
+ mVoiceCallSessionStats0.onPreciseDataConnectionStateChanged(
+ makePreciseDataConnectionState(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ TelephonyManager.DATA_HANDOVER_IN_PROGRESS, ApnSetting.TYPE_IMS));
+ mVoiceCallSessionStats0.setTimeMillis(12000L);
+ mVoiceCallSessionStats0.onImsCallTerminated(
+ mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED, 0));
+
+ ArgumentCaptor<VoiceCallSession> callCaptor =
+ ArgumentCaptor.forClass(VoiceCallSession.class);
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ assertProtoEquals(expectedCall, callCaptor.getValue());
+ assertThat(ratUsage.get()).hasLength(1);
+ assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+ }
+
+ @Test
+ @SmallTest
+ public void singleCall_handoverSuccess() {
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(true).when(mImsConnection0).isIncoming();
+ doReturn(2000L).when(mImsConnection0).getCreateTime();
+ doReturn(mImsCall0).when(mImsConnection0).getCall();
+ doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ VoiceCallSession expectedCall =
+ makeSlot0CallProto(
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+ VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ ImsReasonInfo.CODE_USER_TERMINATED);
+ expectedCall.setupDurationMillis = 80;
+ expectedCall.setupFailed = false;
+ expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.handoverInProgress = false;
+ VoiceCallRatUsage expectedRatUsage =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+ mVoiceCallSessionStats0.setTimeMillis(2000L);
+ doReturn(Call.State.INCOMING).when(mImsCall0).getState();
+ doReturn(Call.State.INCOMING).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onImsCallReceived(mImsConnection0);
+ mVoiceCallSessionStats0.setTimeMillis(2100L);
+ mVoiceCallSessionStats0.onAudioCodecChanged(
+ mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_AMR);
+ mVoiceCallSessionStats0.setTimeMillis(2200L);
+ mVoiceCallSessionStats0.onImsAcceptCall(List.of(mImsConnection0));
+ mVoiceCallSessionStats0.setTimeMillis(2280L);
+ doReturn(Call.State.ACTIVE).when(mImsCall0).getState();
+ doReturn(Call.State.ACTIVE).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+ mVoiceCallSessionStats0.setTimeMillis(9500L);
+ mVoiceCallSessionStats0.onPreciseDataConnectionStateChanged(
+ makePreciseDataConnectionState(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ TelephonyManager.DATA_HANDOVER_IN_PROGRESS, ApnSetting.TYPE_IMS));
+ mVoiceCallSessionStats0.setTimeMillis(11000L);
+ mVoiceCallSessionStats0.onPreciseDataConnectionStateChanged(
+ makePreciseDataConnectionState(AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
+ TelephonyManager.DATA_CONNECTED, ApnSetting.TYPE_IMS));
+ mVoiceCallSessionStats0.setTimeMillis(12000L);
+ mVoiceCallSessionStats0.onImsCallTerminated(
+ mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED, 0));
+
+ ArgumentCaptor<VoiceCallSession> callCaptor =
+ ArgumentCaptor.forClass(VoiceCallSession.class);
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ assertProtoEquals(expectedCall, callCaptor.getValue());
+ assertThat(ratUsage.get()).hasLength(1);
+ assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+ }
+
+ @Test
+ @SmallTest
+ public void singleEmergencyCall_callTerminatedDuringHandover() {
+ setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+ doReturn(true).when(mImsConnection0).isIncoming();
+ doReturn(2000L).when(mImsConnection0).getCreateTime();
+ doReturn(true).when(mImsConnection0).isEmergencyCall();
+ doReturn(mImsCall0).when(mImsConnection0).getCall();
+ doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ VoiceCallSession expectedCall =
+ makeSlot0CallProto(
+ VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+ VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ ImsReasonInfo.CODE_USER_TERMINATED);
+ expectedCall.setupDurationMillis = 80;
+ expectedCall.setupFailed = false;
+ expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.isEmergency = true;
+ expectedCall.handoverInProgress = true;
+ VoiceCallRatUsage expectedRatUsage =
+ makeRatUsageProto(
+ CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+ mVoiceCallSessionStats0.setTimeMillis(2000L);
+ doReturn(Call.State.INCOMING).when(mImsCall0).getState();
+ doReturn(Call.State.INCOMING).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onImsCallReceived(mImsConnection0);
+ mVoiceCallSessionStats0.setTimeMillis(2100L);
+ mVoiceCallSessionStats0.onAudioCodecChanged(
+ mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_AMR);
+ mVoiceCallSessionStats0.setTimeMillis(2200L);
+ mVoiceCallSessionStats0.onImsAcceptCall(List.of(mImsConnection0));
+ mVoiceCallSessionStats0.setTimeMillis(2280L);
+ doReturn(Call.State.ACTIVE).when(mImsCall0).getState();
+ doReturn(Call.State.ACTIVE).when(mImsConnection0).getState();
+ mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+ mVoiceCallSessionStats0.setTimeMillis(9500L);
+ mVoiceCallSessionStats0.onPreciseDataConnectionStateChanged(
+ makePreciseDataConnectionState(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ TelephonyManager.DATA_HANDOVER_IN_PROGRESS, ApnSetting.TYPE_EMERGENCY));
+ mVoiceCallSessionStats0.setTimeMillis(10000L);
+ // connection state changes for IMS APN shouldn't have impact on emergency call
+ mVoiceCallSessionStats0.onPreciseDataConnectionStateChanged(
+ makePreciseDataConnectionState(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ TelephonyManager.DATA_CONNECTED, ApnSetting.TYPE_IMS));
+ mVoiceCallSessionStats0.setTimeMillis(12000L);
+ mVoiceCallSessionStats0.onImsCallTerminated(
+ mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_USER_TERMINATED, 0));
+
+ ArgumentCaptor<VoiceCallSession> callCaptor =
+ ArgumentCaptor.forClass(VoiceCallSession.class);
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+ verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ assertProtoEquals(expectedCall, callCaptor.getValue());
+ assertThat(ratUsage.get()).hasLength(1);
+ assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+ }
+
+ @Test
+ @SmallTest
public void singleWifiCall_preferred() {
setServiceStateWithWifiCalling(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
doReturn(mImsPhone).when(mPhone).getImsPhone();
@@ -2329,6 +2675,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
call.lastKnownRat = rat;
call.bandAtEnd = 1;
call.ratSwitchCount = 0L;
+ call.ratSwitchCountAfterConnected = 0L;
call.codecBitmask = 0L;
call.mainCodecQuality = VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
call.simSlotIndex = 0;
@@ -2363,6 +2710,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
call.lastKnownRat = rat;
call.bandAtEnd = 1;
call.ratSwitchCount = 0L;
+ call.ratSwitchCountAfterConnected = 0L;
call.codecBitmask = 0L;
call.mainCodecQuality = VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
call.simSlotIndex = 1;
@@ -2390,6 +2738,22 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
return usage;
}
+ private static PreciseDataConnectionState makePreciseDataConnectionState(int transport,
+ int state, int apnType) {
+ return new PreciseDataConnectionState.Builder()
+ .setTransportType(transport)
+ .setId(1)
+ .setState(state)
+ .setApnSetting(new ApnSetting.Builder()
+ .setApnTypeBitmask(apnType)
+ .setApnName("ims,emergency")
+ .setEntryName("ims,emergency")
+ .build())
+ .setLinkProperties(new android.net.LinkProperties())
+ .setFailCause(0)
+ .build();
+ }
+
private static void assertProtoEquals(MessageNano expected, MessageNano actual) {
assertWithMessage(
" actual proto:\n"
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
new file mode 100644
index 0000000000..c202f0cf00
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/NtnCapabilityResolverTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 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 static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
+import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_DATA;
+import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY;
+import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_SMS;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.NonNull;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityGsm;
+import android.telephony.NetworkRegistrationInfo;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArraySet;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NtnCapabilityResolverTest extends TelephonyTest {
+ private static final String TAG = "NtnCapabilityResolverTest";
+ private static final int SUB_ID = 0;
+ private static final String VISITING_PLMN = "00102";
+ private static final String SATELLITE_PLMN = "00103";
+ private static final String[] SATELLITE_PLMN_ARRAY = {SATELLITE_PLMN};
+
+ private final int[] mSatelliteSupportedServices = {SERVICE_TYPE_SMS, SERVICE_TYPE_EMERGENCY};
+ private final List<Integer> mSatelliteSupportedServiceList =
+ Arrays.stream(mSatelliteSupportedServices).boxed().collect(Collectors.toList());
+ @Mock private SatelliteController mMockSatelliteController;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ logd(TAG + " Setup!");
+
+ replaceInstance(SatelliteController.class, "sInstance", null,
+ mMockSatelliteController);
+ doReturn(Arrays.asList(SATELLITE_PLMN_ARRAY))
+ .when(mMockSatelliteController).getSatellitePlmnList();
+ doReturn(mSatelliteSupportedServiceList).when(mMockSatelliteController)
+ .getSupportedSatelliteServices(SUB_ID, SATELLITE_PLMN);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logd(TAG + " tearDown");
+ super.tearDown();
+ }
+
+ @Test
+ public void testResolveNTNCapability() {
+ // Test resolving a satellite NetworkRegistrationInfo.
+ NetworkRegistrationInfo satelliteNri = createNetworkRegistrationInfo(SATELLITE_PLMN);
+ NetworkRegistrationInfo originalNri = new NetworkRegistrationInfo(satelliteNri);
+
+ assertEquals(satelliteNri, originalNri);
+ assertFalse(satelliteNri.isNonTerrestrialNetwork());
+ assertFalse(Arrays.equals(mSatelliteSupportedServices,
+ satelliteNri.getAvailableServices().stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+ NtnCapabilityResolver.resolveNtnCapability(satelliteNri, SUB_ID);
+ assertNotEquals(satelliteNri, originalNri);
+ assertTrue(satelliteNri.isNonTerrestrialNetwork());
+ assertTrue(Arrays.equals(mSatelliteSupportedServices,
+ satelliteNri.getAvailableServices().stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ // Test resolving a non-satellite NetworkRegistrationInfo.
+ NetworkRegistrationInfo cellularNri = createNetworkRegistrationInfo(VISITING_PLMN);
+ originalNri = new NetworkRegistrationInfo(cellularNri);
+
+ assertEquals(cellularNri, originalNri);
+ assertFalse(cellularNri.isNonTerrestrialNetwork());
+ assertFalse(Arrays.equals(mSatelliteSupportedServices,
+ cellularNri.getAvailableServices().stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+ NtnCapabilityResolver.resolveNtnCapability(cellularNri, SUB_ID);
+ assertEquals(cellularNri, originalNri);
+ assertFalse(cellularNri.isNonTerrestrialNetwork());
+ assertFalse(Arrays.equals(mSatelliteSupportedServices,
+ cellularNri.getAvailableServices().stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+ }
+
+ private NetworkRegistrationInfo createNetworkRegistrationInfo(@NonNull String registeredPlmn) {
+ List<Integer> availableServices = new ArrayList<>();
+ availableServices.add(SERVICE_TYPE_DATA);
+ CellIdentity cellIdentity = new CellIdentityGsm(0, 0, 0,
+ 0, "mcc", "mnc", "", "", new ArraySet<>());
+ return new NetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN,
+ REGISTRATION_STATE_ROAMING, NETWORK_TYPE_LTE, 0, false, availableServices,
+ cellIdentity, registeredPlmn, false, 0, 0, 0);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
new file mode 100644
index 0000000000..4587b7cfa9
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2023 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.os.Bundle;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
+import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.SatelliteManager.SatelliteException;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PointingAppControllerTest extends TelephonyTest {
+ private static final String TAG = "PointingAppControllerTest";
+ private static final int SUB_ID = 0;
+ private static final long TIMEOUT = 500;
+
+ //Events For SatelliteControllerHandler
+ private static final int EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE = 2;
+ private static final int EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE = 4;
+ private static final String KEY_POINTING_UI_PACKAGE_NAME = "default_pointing_ui_package";
+ private static final String KEY_POINTING_UI_CLASS_NAME = "default_pointing_ui_class";
+ private static final String KEY_NEED_FULL_SCREEN = "needFullScreen";
+
+ private PointingAppController mPointingAppController;
+ InOrder mInOrder;
+
+ @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
+ @Mock private SatelliteController mMockSatelliteController;
+
+ private TestSatelliteTransmissionUpdateCallback mSatelliteTransmissionUpdateCallback;
+ private TestSatelliteControllerHandler mTestSatelliteControllerHandler;
+ /** Variables required to receive datagrams in the unit tests. */
+ LinkedBlockingQueue<Integer> mResultListener;
+ int mResultCode = -1;
+ private Semaphore mSendDatagramStateSemaphore = new Semaphore(0);
+ private Semaphore mReceiveDatagramStateSemaphore = new Semaphore(0);
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ logd(TAG + " Setup!");
+
+ replaceInstance(SatelliteModemInterface.class, "sInstance", null,
+ mMockSatelliteModemInterface);
+ replaceInstance(SatelliteController.class, "sInstance", null,
+ mMockSatelliteController);
+ mPointingAppController = new PointingAppController(mContext);
+ mContextFixture.putResource(R.string.config_pointing_ui_package,
+ KEY_POINTING_UI_PACKAGE_NAME);
+ mContextFixture.putResource(R.string.config_pointing_ui_class,
+ KEY_POINTING_UI_CLASS_NAME);
+ mResultListener = new LinkedBlockingQueue<>(1);
+ mSatelliteTransmissionUpdateCallback = new TestSatelliteTransmissionUpdateCallback();
+ mTestSatelliteControllerHandler = new TestSatelliteControllerHandler();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logd(TAG + " tearDown");
+ mResultListener = null;
+
+ mSatelliteTransmissionUpdateCallback = null;
+ super.tearDown();
+ }
+
+ private class TestSatelliteControllerHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch(msg.what) {
+ case EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE: {
+ ar = (AsyncResult) msg.obj;
+ mResultCode = SatelliteServiceUtils.getSatelliteError(ar,
+ "startSatelliteTransmissionUpdates");
+ logd("mResultCode = " + mResultCode);
+ break;
+ }
+ case EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE: {
+ ar = (AsyncResult) msg.obj;
+ mResultCode = SatelliteServiceUtils.getSatelliteError(ar,
+ "stopSatelliteTransmissionUpdates");
+ logd("mResultCode = " + mResultCode);
+ break;
+ }
+ default:
+ logd("TestSatelliteController: " + msg.what);
+ break;
+ }
+ }
+ }
+ private class TestSatelliteTransmissionUpdateCallback
+ extends ISatelliteTransmissionUpdateCallback.Stub {
+ int mState;
+ int mSendPendingCount;
+ int mReceivePendingCount;
+ int mErrorCode;
+ public boolean inSendDatagramStateCallback = false;
+ public boolean inReceiveDatagramStateCallback = false;
+
+ @Override
+ public void onSatellitePositionChanged(PointingInfo pointingInfo) {
+ //Not Used
+ }
+
+ @Override
+ public void onSendDatagramStateChanged(int state, int sendPendingCount,
+ int errorCode) {
+ mState = state;
+ mSendPendingCount = sendPendingCount;
+ mErrorCode = errorCode;
+ inSendDatagramStateCallback = true;
+ try {
+ mSendDatagramStateSemaphore.release();
+ } catch (Exception ex) {
+ loge("mSendDatagramStateSemaphore: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+
+ @Override
+ public void onReceiveDatagramStateChanged(int state,
+ int receivePendingCount, int errorCode) {
+ mState = state;
+ mReceivePendingCount = receivePendingCount;
+ mErrorCode = errorCode;
+ inReceiveDatagramStateCallback = true;
+ try {
+ mReceiveDatagramStateSemaphore.release();
+ } catch (Exception ex) {
+ loge("mReceiveDatagramStateSemaphore: Got exception in releasing semaphore, ex="
+ + ex);
+ }
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public int getSendPendingCount() {
+ return mSendPendingCount;
+ }
+
+ public int getReceivePendingCount() {
+ return mReceivePendingCount;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+ }
+
+ private boolean waitForReceiveDatagramStateChangedRessult(
+ int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mReceiveDatagramStateSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive "
+ + "ReceiveDatagramStateChanged event");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForReceiveDatagramStateChangedRessult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean waitForSendDatagramStateChangedRessult(
+ int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!mSendDatagramStateSemaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ loge("Timeout to receive "
+ + "SendDatagramStateChanged event");
+ return false;
+ }
+ } catch (Exception ex) {
+ loge("waitForSendDatagramStateChangedRessult: Got exception=" + ex);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void setUpResponseForStartTransmissionUpdates(
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SatelliteManager.SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mPhone).startSatellitePositionUpdates(any(Message.class));
+
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).startSendingSatellitePointingInfo(any(Message.class));
+ }
+
+ private void setUpResponseForStopTransmissionUpdates(
+ @SatelliteManager.SatelliteError int error) {
+ SatelliteException exception = (error == SatelliteManager.SATELLITE_ERROR_NONE)
+ ? null : new SatelliteException(error);
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mPhone).stopSatellitePositionUpdates(any(Message.class));
+
+ doAnswer(invocation -> {
+ Message message = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(message, null, exception);
+ message.sendToTarget();
+ return null;
+ }).when(mMockSatelliteModemInterface).stopSendingSatellitePointingInfo(any(Message.class));
+ }
+
+ @Test
+ public void testStartSatelliteTransmissionUpdates_CommandInterface()
+ throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ Message testMessage = mTestSatelliteControllerHandler
+ .obtainMessage(EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
+ setUpResponseForStartTransmissionUpdates(SatelliteManager.SATELLITE_ERROR_NONE);
+ mPointingAppController.startSatelliteTransmissionUpdates(testMessage, mPhone);
+
+ processAllMessages();
+
+ verify(mMockSatelliteModemInterface, never())
+ .startSendingSatellitePointingInfo(eq(testMessage));
+
+ verify(mPhone)
+ .startSatellitePositionUpdates(eq(testMessage));
+
+ assertEquals(SatelliteManager.SATELLITE_ERROR_NONE, mResultCode);
+
+ assertTrue(mPointingAppController.getStartedSatelliteTransmissionUpdates());
+ }
+
+ @Test
+ public void testStartSatelliteTransmissionUpdates_success()
+ throws Exception {
+ doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ mPointingAppController.setStartedSatelliteTransmissionUpdates(false);
+ Message testMessage = mTestSatelliteControllerHandler
+ .obtainMessage(EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
+ setUpResponseForStartTransmissionUpdates(SatelliteManager.SATELLITE_ERROR_NONE);
+ mPointingAppController.startSatelliteTransmissionUpdates(testMessage, mPhone);
+
+ verify(mMockSatelliteModemInterface)
+ .startSendingSatellitePointingInfo(eq(testMessage));
+
+ verify(mPhone, never())
+ .startSatellitePositionUpdates(eq(testMessage));
+
+ processAllMessages();
+
+
+ assertTrue(mPointingAppController.getStartedSatelliteTransmissionUpdates());
+ assertEquals(SatelliteManager.SATELLITE_ERROR_NONE, mResultCode);
+ }
+
+ @Test
+ public void testStartSatelliteTransmissionUpdates_phoneNull()
+ throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ mPointingAppController.setStartedSatelliteTransmissionUpdates(false);
+ Message testMessage = mTestSatelliteControllerHandler
+ .obtainMessage(EVENT_START_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
+
+ mPointingAppController.startSatelliteTransmissionUpdates(testMessage, null);
+ processAllMessages();
+ verify(mMockSatelliteModemInterface, never())
+ .startSendingSatellitePointingInfo(eq(testMessage));
+
+ verify(mPhone, never())
+ .startSatellitePositionUpdates(eq(testMessage));
+
+ assertFalse(mPointingAppController.getStartedSatelliteTransmissionUpdates());
+
+ assertEquals(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, mResultCode);
+ }
+
+ @Test
+ public void testStopSatelliteTransmissionUpdates_CommandInterface()
+ throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ setUpResponseForStopTransmissionUpdates(SatelliteManager.SATELLITE_ERROR_NONE);
+ Message testMessage = mTestSatelliteControllerHandler
+ .obtainMessage(EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
+ mPointingAppController.stopSatelliteTransmissionUpdates(testMessage, mPhone);
+
+ processAllMessages();
+
+ verify(mMockSatelliteModemInterface, never())
+ .stopSendingSatellitePointingInfo(eq(testMessage));
+
+ verify(mPhone)
+ .stopSatellitePositionUpdates(eq(testMessage));
+
+ assertFalse(mPointingAppController.getStartedSatelliteTransmissionUpdates());
+
+ assertEquals(SatelliteManager.SATELLITE_ERROR_NONE, mResultCode);
+ }
+
+ @Test
+ public void testStopSatelliteTransmissionUpdates_success()
+ throws Exception {
+ doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ setUpResponseForStopTransmissionUpdates(SatelliteManager.SATELLITE_ERROR_NONE);
+ Message testMessage = mTestSatelliteControllerHandler
+ .obtainMessage(EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
+ mPointingAppController.stopSatelliteTransmissionUpdates(testMessage, mPhone);
+
+ processAllMessages();
+
+ verify(mMockSatelliteModemInterface)
+ .stopSendingSatellitePointingInfo(eq(testMessage));
+
+ verify(mPhone, never())
+ .stopSatellitePositionUpdates(eq(testMessage));
+
+ assertFalse(mPointingAppController.getStartedSatelliteTransmissionUpdates());
+ assertEquals(SatelliteManager.SATELLITE_ERROR_NONE, mResultCode);
+ }
+
+ @Test
+ public void testStopSatellitePointingInfo_phoneNull()
+ throws Exception {
+ doReturn(false).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ Message testMessage = mTestSatelliteControllerHandler
+ .obtainMessage(EVENT_STOP_SATELLITE_TRANSMISSION_UPDATES_DONE, null);
+ mPointingAppController.stopSatelliteTransmissionUpdates(testMessage, null);
+
+ processAllMessages();
+
+ verify(mMockSatelliteModemInterface, never())
+ .stopSendingSatellitePointingInfo(eq(testMessage));
+
+ verify(mPhone, never())
+ .stopSatellitePositionUpdates(eq(testMessage));
+
+ assertEquals(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, mResultCode);
+
+ }
+
+ @Test
+ public void testStartPointingUI() throws Exception {
+ ArgumentCaptor<Intent> startedIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+ mPointingAppController.startPointingUI(true);
+ verify(mContext).startActivity(startedIntentCaptor.capture());
+ Intent intent = startedIntentCaptor.getValue();
+ assertEquals(KEY_POINTING_UI_PACKAGE_NAME, intent.getComponent().getPackageName());
+ assertEquals(KEY_POINTING_UI_CLASS_NAME, intent.getComponent().getClassName());
+ Bundle b = intent.getExtras();
+ assertTrue(b.containsKey(KEY_NEED_FULL_SCREEN));
+ assertTrue(b.getBoolean(KEY_NEED_FULL_SCREEN));
+ }
+
+ @Test
+ public void testUpdateSendDatagramTransferState() throws Exception {
+ mPointingAppController.registerForSatelliteTransmissionUpdates(SUB_ID,
+ mSatelliteTransmissionUpdateCallback, mPhone);
+ mPointingAppController.updateSendDatagramTransferState(SUB_ID,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS, 1,
+ SatelliteManager.SATELLITE_ERROR_NONE);
+ assertTrue(waitForSendDatagramStateChangedRessult(1));
+ assertEquals(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
+ mSatelliteTransmissionUpdateCallback.getState());
+ assertEquals(1, mSatelliteTransmissionUpdateCallback.getSendPendingCount());
+ assertEquals(SatelliteManager.SATELLITE_ERROR_NONE,
+ mSatelliteTransmissionUpdateCallback.getErrorCode());
+ assertTrue(mSatelliteTransmissionUpdateCallback.inSendDatagramStateCallback);
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(SUB_ID,
+ mResultListener::offer, mSatelliteTransmissionUpdateCallback, mPhone);
+ mResultListener.clear();
+ }
+
+ @Test
+ public void testUpdateReceiveDatagramTransferState() throws Exception {
+ mPointingAppController.registerForSatelliteTransmissionUpdates(SUB_ID,
+ mSatelliteTransmissionUpdateCallback, mPhone);
+ mPointingAppController.updateReceiveDatagramTransferState(SUB_ID,
+ SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS, 2,
+ SatelliteManager.SATELLITE_ERROR_NONE);
+ assertTrue(waitForReceiveDatagramStateChangedRessult(1));
+ assertEquals(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS,
+ mSatelliteTransmissionUpdateCallback.getState());
+ assertEquals(2, mSatelliteTransmissionUpdateCallback.getReceivePendingCount());
+ assertEquals(SatelliteManager.SATELLITE_ERROR_NONE,
+ mSatelliteTransmissionUpdateCallback.getErrorCode());
+ assertTrue(mSatelliteTransmissionUpdateCallback.inReceiveDatagramStateCallback);
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(SUB_ID,
+ mResultListener::offer, mSatelliteTransmissionUpdateCallback, mPhone);
+ mResultListener.clear();
+ }
+
+ @Test
+ public void testRegisterForSatelliteTransmissionUpdates_CommandInterface() throws Exception {
+ mResultListener.clear();
+ mInOrder = inOrder(mPhone);
+ TestSatelliteTransmissionUpdateCallback callback1 = new
+ TestSatelliteTransmissionUpdateCallback();
+ TestSatelliteTransmissionUpdateCallback callback2 = new
+ TestSatelliteTransmissionUpdateCallback();
+ int subId1 = 1;
+ int subId2 = 2;
+ mPointingAppController.registerForSatelliteTransmissionUpdates(subId1,
+ callback1, mPhone);
+ mInOrder.verify(mPhone).registerForSatellitePositionInfoChanged(any(),
+ eq(1), eq(null));
+ mPointingAppController.registerForSatelliteTransmissionUpdates(subId1,
+ callback2, mPhone);
+ mInOrder.verify(mPhone, never()).registerForSatellitePositionInfoChanged(any(),
+ eq(1), eq(null));
+ mPointingAppController.registerForSatelliteTransmissionUpdates(subId2,
+ callback1, mPhone);
+ mInOrder.verify(mPhone).registerForSatellitePositionInfoChanged(any(),
+ eq(1), eq(null));
+ mPointingAppController.registerForSatelliteTransmissionUpdates(subId2,
+ callback2, mPhone);
+ mInOrder.verify(mPhone, never()).registerForSatellitePositionInfoChanged(any(),
+ eq(1), eq(null));
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId1,
+ mResultListener::offer, callback1, mPhone);
+ processAllMessages();
+ //since there are 2 callbacks registered for this sub_id, Handler is not unregistered
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ mResultListener.remove();
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId1,
+ mResultListener::offer, callback2, mPhone);
+ mInOrder.verify(mPhone).unregisterForSatellitePositionInfoChanged(any(Handler.class));
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
+ mResultListener::offer, callback1, mPhone);
+ processAllMessages();
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ mResultListener.remove();
+ mInOrder.verify(mPhone, never()).unregisterForSatellitePositionInfoChanged(
+ any(Handler.class));
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
+ mResultListener::offer, callback2, null);
+ processAllMessages();
+ assertThat(mResultListener.peek())
+ .isEqualTo(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+ mResultListener.remove();
+ mInOrder = null;
+ }
+
+ @Test
+ public void testRegisterForSatelliteTransmissionUpdates() throws Exception {
+ mResultListener.clear();
+ doReturn(true).when(mMockSatelliteModemInterface).isSatelliteServiceSupported();
+ mInOrder = inOrder(mMockSatelliteModemInterface);
+ TestSatelliteTransmissionUpdateCallback callback1 = new
+ TestSatelliteTransmissionUpdateCallback();
+ TestSatelliteTransmissionUpdateCallback callback2 = new
+ TestSatelliteTransmissionUpdateCallback();
+ int subId1 = 3;
+ int subId2 = 4;
+ mPointingAppController.registerForSatelliteTransmissionUpdates(subId1,
+ callback1, mPhone);
+ mInOrder.verify(mMockSatelliteModemInterface).registerForSatellitePositionInfoChanged(any(),
+ eq(1), eq(null));
+ mInOrder.verify(mMockSatelliteModemInterface).registerForDatagramTransferStateChanged(any(),
+ eq(4), eq(null));
+ mPointingAppController.registerForSatelliteTransmissionUpdates(subId1,
+ callback2, mPhone);
+ mInOrder.verify(mMockSatelliteModemInterface, never())
+ .registerForSatellitePositionInfoChanged(any(), eq(1), eq(null));
+ mInOrder.verify(mMockSatelliteModemInterface, never())
+ .registerForDatagramTransferStateChanged(any(), eq(4), eq(null));
+ mPointingAppController.registerForSatelliteTransmissionUpdates(subId2,
+ callback1, mPhone);
+ mInOrder.verify(mMockSatelliteModemInterface).registerForSatellitePositionInfoChanged(any(),
+ eq(1), eq(null));
+ mInOrder.verify(mMockSatelliteModemInterface).registerForDatagramTransferStateChanged(any(),
+ eq(4), eq(null));
+ mPointingAppController.registerForSatelliteTransmissionUpdates(subId2,
+ callback2, mPhone);
+ mInOrder.verify(mMockSatelliteModemInterface, never())
+ .registerForSatellitePositionInfoChanged(any(), eq(1), eq(null));
+ mInOrder.verify(mMockSatelliteModemInterface, never())
+ .registerForDatagramTransferStateChanged(any(), eq(4), eq(null));
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId1,
+ mResultListener::offer, callback1, mPhone);
+ processAllMessages();
+ //since there are 2 callbacks registered for this sub_id, Handler is not unregistered
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ mResultListener.remove();
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId1,
+ mResultListener::offer, callback2, mPhone);
+ mInOrder.verify(mMockSatelliteModemInterface).unregisterForSatellitePositionInfoChanged(
+ any(Handler.class));
+ mInOrder.verify(mMockSatelliteModemInterface).unregisterForDatagramTransferStateChanged(
+ any(Handler.class));
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
+ mResultListener::offer, callback1, mPhone);
+ processAllMessages();
+ assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_ERROR_NONE);
+ mResultListener.remove();
+ mInOrder.verify(mMockSatelliteModemInterface, never())
+ .unregisterForSatellitePositionInfoChanged(any(Handler.class));
+ mInOrder.verify(mMockSatelliteModemInterface, never())
+ .unregisterForDatagramTransferStateChanged(any(Handler.class));
+ mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
+ mResultListener::offer, callback2, null);
+ processAllMessages();
+ mInOrder.verify(mMockSatelliteModemInterface).unregisterForSatellitePositionInfoChanged(
+ any(Handler.class));
+ mInOrder.verify(mMockSatelliteModemInterface).unregisterForDatagramTransferStateChanged(
+ any(Handler.class));
+ mInOrder = null;
+ }
+
+ private static void loge(String message) {
+ Log.e(TAG, message);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
index f6ed2e24df..edfd6105ad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -53,7 +53,9 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.anyVararg;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -72,7 +74,9 @@ import android.os.Handler;
import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.satellite.ISatelliteDatagramCallback;
import android.telephony.satellite.ISatelliteProvisionStateCallback;
@@ -84,7 +88,9 @@ import android.telephony.satellite.SatelliteManager;
import android.telephony.satellite.SatelliteManager.SatelliteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Pair;
+import com.android.internal.R;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.IVoidConsumer;
import com.android.internal.telephony.Phone;
@@ -92,6 +98,7 @@ import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
import org.junit.After;
import org.junit.Before;
@@ -107,6 +114,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -114,11 +122,17 @@ import java.util.concurrent.TimeUnit;
@TestableLooper.RunWithLooper
public class SatelliteControllerTest extends TelephonyTest {
private static final String TAG = "SatelliteControllerTest";
+
+ private static final int EVENT_DEVICE_CONFIG_CHANGED = 29;
+
private static final long TIMEOUT = 500;
private static final int SUB_ID = 0;
+ private static final int SUB_ID1 = 1;
private static final int MAX_BYTES_PER_OUT_GOING_DATAGRAM = 339;
private static final String TEST_SATELLITE_TOKEN = "TEST_SATELLITE_TOKEN";
private static final String TEST_NEXT_SATELLITE_TOKEN = "TEST_NEXT_SATELLITE_TOKEN";
+ private static final String[] EMPTY_SATELLITE_SERVICES_SUPPORTED_BY_PROVIDERS_STRING_ARRAY = {};
+ private static final int[] ACTIVE_SUB_IDS = {SUB_ID};
private TestSatelliteController mSatelliteControllerUT;
private TestSharedPreferences mSharedPreferences;
@@ -130,6 +144,7 @@ public class SatelliteControllerTest extends TelephonyTest {
@Mock private ControllerMetricsStats mMockControllerMetricsStats;
@Mock private ProvisionMetricsStats mMockProvisionMetricsStats;
@Mock private SessionMetricsStats mMockSessionMetricsStats;
+ @Mock private SubscriptionManagerService mMockSubscriptionManagerService;
private List<Integer> mIIntegerConsumerResults = new ArrayList<>();
@Mock private ISatelliteTransmissionUpdateCallback mStartTransmissionUpdateCallback;
@Mock private ISatelliteTransmissionUpdateCallback mStopTransmissionUpdateCallback;
@@ -215,6 +230,7 @@ public class SatelliteControllerTest extends TelephonyTest {
private ResultReceiver mIsSatelliteEnabledReceiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
+ logd("mIsSatelliteEnabledReceiver: resultCode=" + resultCode);
mQueriedIsSatelliteEnabledResultCode = resultCode;
if (resultCode == SATELLITE_ERROR_NONE) {
if (resultData.containsKey(KEY_SATELLITE_ENABLED)) {
@@ -224,7 +240,6 @@ public class SatelliteControllerTest extends TelephonyTest {
mQueriedIsSatelliteEnabled = false;
}
} else {
- logd("mIsSatelliteEnableReceiver: resultCode=" + resultCode);
mQueriedIsSatelliteEnabled = false;
}
try {
@@ -343,6 +358,9 @@ public class SatelliteControllerTest extends TelephonyTest {
}
};
+ private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
+ mCarrierConfigChangedListenerList = new ArrayList<>();
+ private PersistableBundle mCarrierConfigBundle;
@Before
public void setUp() throws Exception {
@@ -364,6 +382,25 @@ public class SatelliteControllerTest extends TelephonyTest {
mMockProvisionMetricsStats);
replaceInstance(SessionMetricsStats.class, "sInstance", null,
mMockSessionMetricsStats);
+ replaceInstance(SubscriptionManagerService.class, "sInstance", null,
+ mMockSubscriptionManagerService);
+
+ mContextFixture.putStringArrayResource(
+ R.array.config_satellite_services_supported_by_providers,
+ EMPTY_SATELLITE_SERVICES_SUPPORTED_BY_PROVIDERS_STRING_ARRAY);
+ doReturn(ACTIVE_SUB_IDS).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
+
+ mCarrierConfigBundle = mContextFixture.getCarrierConfigBundle();
+ doReturn(mCarrierConfigBundle)
+ .when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyVararg());
+ doAnswer(invocation -> {
+ Executor executor = invocation.getArgument(0);
+ CarrierConfigManager.CarrierConfigChangeListener listener = invocation.getArgument(1);
+ mCarrierConfigChangedListenerList.add(new Pair<>(executor, listener));
+ return null;
+ }).when(mCarrierConfigManager).registerCarrierConfigChangeListener(
+ any(Executor.class),
+ any(CarrierConfigManager.CarrierConfigChangeListener.class));
mSharedPreferences = new TestSharedPreferences();
when(mContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
@@ -586,18 +623,39 @@ public class SatelliteControllerTest extends TelephonyTest {
processAllMessages();
verifySatelliteProvisioned(true, SATELLITE_ERROR_NONE);
+ // Successfully enable satellite
+ mIIntegerConsumerResults.clear();
+ mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
+ setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_ERROR_NONE);
+ mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
+ processAllMessages();
+ assertTrue(waitForIIntegerConsumerResult(1));
+ assertEquals(SATELLITE_ERROR_NONE, (long) mIIntegerConsumerResults.get(0));
+ verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
+ assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
+ assertEquals(
+ SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
+ verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(true));
+ verify(mMockSatelliteSessionController, times(2)).setDemoMode(eq(false));
+ verify(mMockDatagramController, times(2)).setDemoMode(eq(false));
+ verify(mMockPointingAppController).startPointingUI(eq(false));
+ verify(mMockControllerMetricsStats, times(1)).onSatelliteEnabled();
+ verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementSuccessCount();
+
// Successfully disable satellite when radio is turned off.
mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
setRadioPower(false);
processAllMessages();
+ sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_OFF, null);
+ processAllMessages();
verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
assertEquals(
SATELLITE_MODE_ENABLED_FALSE, mSatelliteControllerUT.satelliteModeSettingValue);
verify(mMockSatelliteSessionController, times(2)).onSatelliteEnabledStateChanged(eq(false));
- verify(mMockSatelliteSessionController, times(2)).setDemoMode(eq(false));
- verify(mMockDatagramController, times(2)).setDemoMode(eq(false));
+ verify(mMockSatelliteSessionController, times(3)).setDemoMode(eq(false));
+ verify(mMockDatagramController, times(3)).setDemoMode(eq(false));
verify(mMockControllerMetricsStats, times(1)).onSatelliteDisabled();
// Fail to enable satellite when radio is off.
@@ -615,6 +673,7 @@ public class SatelliteControllerTest extends TelephonyTest {
// Fail to enable satellite with an error response from modem when radio is on.
mIIntegerConsumerResults.clear();
+ clearInvocations(mMockPointingAppController);
mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
setUpResponseForRequestSatelliteEnabled(true, false, SATELLITE_INVALID_MODEM_STATE);
mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, true, false, mIIntegerConsumer);
@@ -638,14 +697,14 @@ public class SatelliteControllerTest extends TelephonyTest {
assertTrue(mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled);
assertEquals(SATELLITE_MODE_ENABLED_TRUE, mSatelliteControllerUT.satelliteModeSettingValue);
verify(mMockPointingAppController).startPointingUI(eq(false));
- verify(mMockSatelliteSessionController, times(1)).onSatelliteEnabledStateChanged(eq(true));
- verify(mMockSatelliteSessionController, times(3)).setDemoMode(eq(false));
- verify(mMockDatagramController, times(3)).setDemoMode(eq(false));
- verify(mMockControllerMetricsStats, times(1)).onSatelliteEnabled();
- verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementSuccessCount();
- verify(mMockSessionMetricsStats, times(2)).setInitializationResult(anyInt());
- verify(mMockSessionMetricsStats, times(2)).setRadioTechnology(anyInt());
- verify(mMockSessionMetricsStats, times(2)).reportSessionMetrics();
+ verify(mMockSatelliteSessionController, times(2)).onSatelliteEnabledStateChanged(eq(true));
+ verify(mMockSatelliteSessionController, times(4)).setDemoMode(eq(false));
+ verify(mMockDatagramController, times(4)).setDemoMode(eq(false));
+ verify(mMockControllerMetricsStats, times(2)).onSatelliteEnabled();
+ verify(mMockControllerMetricsStats, times(2)).reportServiceEnablementSuccessCount();
+ verify(mMockSessionMetricsStats, times(3)).setInitializationResult(anyInt());
+ verify(mMockSessionMetricsStats, times(3)).setRadioTechnology(anyInt());
+ verify(mMockSessionMetricsStats, times(3)).reportSessionMetrics();
// Successfully enable satellite when it is already enabled.
mIIntegerConsumerResults.clear();
@@ -663,7 +722,7 @@ public class SatelliteControllerTest extends TelephonyTest {
assertEquals(SATELLITE_INVALID_ARGUMENTS, (long) mIIntegerConsumerResults.get(0));
verifySatelliteEnabled(true, SATELLITE_ERROR_NONE);
- // Disable satellite.
+ // Successfully disable satellite.
mIIntegerConsumerResults.clear();
setUpResponseForRequestSatelliteEnabled(false, false, SATELLITE_ERROR_NONE);
mSatelliteControllerUT.requestSatelliteEnabled(SUB_ID, false, false, mIIntegerConsumer);
@@ -1412,13 +1471,128 @@ public class SatelliteControllerTest extends TelephonyTest {
processAllMessages();
assertTrue(waitForIIntegerConsumerResult(1));
assertEquals(SATELLITE_INVALID_MODEM_STATE, (long) mIIntegerConsumerResults.get(0));
+ }
+ @Test
+ public void testSupportedSatelliteServices() {
+ List<String> satellitePlmnList = mSatelliteControllerUT.getSatellitePlmnList();
+ assertEquals(EMPTY_SATELLITE_SERVICES_SUPPORTED_BY_PROVIDERS_STRING_ARRAY.length,
+ satellitePlmnList.size());
+ List<Integer> supportedSatelliteServices =
+ mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00101");
+ assertTrue(supportedSatelliteServices.isEmpty());
+
+ String[] satellitePlmnArray = {"00101", "00102"};
+ String[] satelliteServicesSupportedByProviderStrArray = {"00101:1,2", "00102:2,3"};
+ int[] expectedSupportedServices1 = {1, 2};
+ int[] expectedSupportedServices2 = {2, 3};
+
+ mContextFixture.putStringArrayResource(
+ R.array.config_satellite_services_supported_by_providers,
+ satelliteServicesSupportedByProviderStrArray);
+ TestSatelliteController testSatelliteController =
+ new TestSatelliteController(mContext, Looper.myLooper());
+
+ satellitePlmnList = testSatelliteController.getSatellitePlmnList();
+ assertTrue(Arrays.equals(satellitePlmnArray, satellitePlmnList.stream().toArray()));
+
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00101");
+ assertNotNull(supportedSatelliteServices);
+ assertTrue(Arrays.equals(expectedSupportedServices1,
+ supportedSatelliteServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102");
+ assertNotNull(supportedSatelliteServices);
+ assertTrue(Arrays.equals(expectedSupportedServices2,
+ supportedSatelliteServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ // Carrier config changed
+ int[] expectedSupportedServices3 = {2};
+ int[] supportedServices = {1, 3};
+ PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle();
+ carrierSupportedSatelliteServicesPerProvider.putIntArray(
+ "00102", expectedSupportedServices3);
+ carrierSupportedSatelliteServicesPerProvider.putIntArray("00103", supportedServices);
+ mCarrierConfigBundle.putPersistableBundle(CarrierConfigManager
+ .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+ carrierSupportedSatelliteServicesPerProvider);
+ for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+ : mCarrierConfigChangedListenerList) {
+ pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+ /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+ );
+ }
+ processAllMessages();
+
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00101");
+ assertNotNull(supportedSatelliteServices);
+ assertTrue(Arrays.equals(expectedSupportedServices1,
+ supportedSatelliteServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102");
+ assertNotNull(supportedSatelliteServices);
+ assertTrue(Arrays.equals(expectedSupportedServices3,
+ supportedSatelliteServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ supportedSatelliteServices =
+ mSatelliteControllerUT.getSupportedSatelliteServices(SUB_ID, "00103");
+ assertTrue(supportedSatelliteServices.isEmpty());
+
+ // Subscriptions changed
+ int[] newActiveSubIds = {SUB_ID1};
+ doReturn(newActiveSubIds).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
+ for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+ : mCarrierConfigChangedListenerList) {
+ pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+ /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+ );
+ }
+ processAllMessages();
+
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00101");
+ assertTrue(supportedSatelliteServices.isEmpty());
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID, "00102");
+ assertTrue(supportedSatelliteServices.isEmpty());
+
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00101");
+ assertNotNull(supportedSatelliteServices);
+ assertTrue(Arrays.equals(expectedSupportedServices1,
+ supportedSatelliteServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00102");
+ assertNotNull(supportedSatelliteServices);
+ assertTrue(Arrays.equals(expectedSupportedServices3,
+ supportedSatelliteServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ supportedSatelliteServices =
+ testSatelliteController.getSupportedSatelliteServices(SUB_ID1, "00103");
+ assertTrue(supportedSatelliteServices.isEmpty());
}
private void resetSatelliteControllerUTEnabledState() {
logd("resetSatelliteControllerUTEnabledState");
setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
- doReturn(true).when(mMockSatelliteModemInterface)
+ doNothing().when(mMockSatelliteModemInterface)
.setSatelliteServicePackageName(anyString());
mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService");
processAllMessages();
@@ -1438,7 +1612,7 @@ public class SatelliteControllerTest extends TelephonyTest {
// Reset all cached states
setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RADIO_NOT_AVAILABLE);
- doReturn(true).when(mMockSatelliteModemInterface)
+ doNothing().when(mMockSatelliteModemInterface)
.setSatelliteServicePackageName(anyString());
mSatelliteControllerUT.setSatelliteServicePackageName("TestSatelliteService");
processAllMessages();
@@ -1455,7 +1629,8 @@ public class SatelliteControllerTest extends TelephonyTest {
private void resetSatelliteControllerUTToOffAndProvisionedState() {
resetSatelliteControllerUTToSupportedAndProvisionedState();
- sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_OFF, null);
+ // Clean up pending resources and move satellite controller to OFF state.
+ sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_UNAVAILABLE, null);
processAllMessages();
verifySatelliteEnabled(false, SATELLITE_ERROR_NONE);
}
@@ -1568,6 +1743,9 @@ public class SatelliteControllerTest extends TelephonyTest {
SatelliteException exception = (error == SATELLITE_ERROR_NONE)
? null : new SatelliteException(error);
doAnswer(invocation -> {
+ if (exception == null && !enabled) {
+ sendSatelliteModemStateChangedEvent(SATELLITE_MODEM_STATE_OFF, null);
+ }
Message message = (Message) invocation.getArguments()[2];
AsyncResult.forMessage(message, null, exception);
message.sendToTarget();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
index 418d0aaede..5e1129747e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -36,6 +36,7 @@ import android.os.ResultReceiver;
import android.telecom.Call;
import android.telecom.Connection;
import android.telephony.BinderCacheManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.ims.RegistrationManager;
@@ -96,6 +97,14 @@ public class SatelliteSOSMessageRecommenderTest extends TelephonyTest {
when(mMockContext.getResources()).thenReturn(mResources);
when(mResources.getString(com.android.internal.R.string.config_satellite_service_package))
.thenReturn("");
+ when(mMockContext.getSystemServiceName(CarrierConfigManager.class))
+ .thenReturn("CarrierConfigManager");
+ when(mMockContext.getSystemService(CarrierConfigManager.class))
+ .thenReturn(mCarrierConfigManager);
+ when(mMockContext.getSystemServiceName(SubscriptionManager.class))
+ .thenReturn("SubscriptionManager");
+ when(mMockContext.getSystemService(SubscriptionManager.class))
+ .thenReturn(mSubscriptionManager);
mTestSatelliteController = new TestSatelliteController(mMockContext,
Looper.myLooper());
mTestImsManager = new TestImsManager(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java
new file mode 100644
index 0000000000..ba1fb9ea39
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteServiceUtilsTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2023 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.PersistableBundle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class SatelliteServiceUtilsTest extends TelephonyTest {
+ private static final String TAG = "SatelliteServiceUtilsTest";
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ logd(TAG + " Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ logd(TAG + " tearDown");
+ super.tearDown();
+ }
+
+ @Test
+ public void testParseSupportedSatelliteServicesFromStringArray() {
+ // Parse correct format input string
+ int[] expectedServices1 = {2, 3};
+ int[] expectedServices2 = {3};
+ String[] supportedServicesStrArr1 = {"10011:2,3", "10112:3"};
+ Map<String, Set<Integer>> supportedServiceMap =
+ SatelliteServiceUtils.parseSupportedSatelliteServices(supportedServicesStrArr1);
+
+ assertTrue(supportedServiceMap.containsKey("10011"));
+ Set<Integer> supportedServices = supportedServiceMap.get("10011");
+ assertTrue(Arrays.equals(expectedServices1,
+ supportedServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ assertTrue(supportedServiceMap.containsKey("10112"));
+ supportedServices = supportedServiceMap.get("10112");
+ assertTrue(Arrays.equals(expectedServices2,
+ supportedServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ // Parse correct mixed with incorrect format input string
+ String[] supportedServicesStrArr2 = {"10011:2,3,1xy", "10112:3,70", "10012:"};
+ supportedServiceMap = SatelliteServiceUtils.parseSupportedSatelliteServices(
+ supportedServicesStrArr2);
+
+ assertTrue(supportedServiceMap.containsKey("10011"));
+ supportedServices = supportedServiceMap.get("10011");
+ assertTrue(Arrays.equals(expectedServices1,
+ supportedServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ assertTrue(supportedServiceMap.containsKey("10112"));
+ supportedServices = supportedServiceMap.get("10112");
+ assertTrue(Arrays.equals(expectedServices2,
+ supportedServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ assertTrue(supportedServiceMap.containsKey("10012"));
+ assertTrue(supportedServiceMap.get("10012").isEmpty());
+
+ // Parse an empty input string
+ String[] supportedServicesStrArr3 = {};
+ supportedServiceMap = SatelliteServiceUtils.parseSupportedSatelliteServices(
+ supportedServicesStrArr3);
+ assertTrue(supportedServiceMap.isEmpty());
+ }
+
+ @Test
+ public void testParseSupportedSatelliteServicesFromPersistableBundle() {
+ PersistableBundle supportedServicesBundle = new PersistableBundle();
+ String plmn1 = "10101";
+ String plmn2 = "10102";
+ String plmn3 = "10103";
+ int[] supportedServicesForPlmn1 = {1, 2, 3};
+ int[] supportedServicesForPlmn2 = {3, 4, 100};
+ int[] expectedServicesForPlmn1 = {1, 2, 3};
+ int[] expectedServicesForPlmn2 = {3, 4};
+
+ // Parse an empty bundle
+ Map<String, Set<Integer>> supportedServiceMap =
+ SatelliteServiceUtils.parseSupportedSatelliteServices(supportedServicesBundle);
+ assertTrue(supportedServiceMap.isEmpty());
+
+ // Add some more fields
+ supportedServicesBundle.putIntArray(plmn1, supportedServicesForPlmn1);
+ supportedServicesBundle.putIntArray(plmn2, supportedServicesForPlmn2);
+ supportedServicesBundle.putIntArray(plmn3, new int[0]);
+
+ supportedServiceMap =
+ SatelliteServiceUtils.parseSupportedSatelliteServices(supportedServicesBundle);
+ assertEquals(3, supportedServiceMap.size());
+
+ assertTrue(supportedServiceMap.containsKey(plmn1));
+ Set<Integer> supportedServices = supportedServiceMap.get(plmn1);
+ assertTrue(Arrays.equals(expectedServicesForPlmn1,
+ supportedServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ assertTrue(supportedServiceMap.containsKey(plmn2));
+ supportedServices = supportedServiceMap.get(plmn2);
+ assertTrue(Arrays.equals(expectedServicesForPlmn2,
+ supportedServices.stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+
+ assertTrue(supportedServiceMap.containsKey(plmn3));
+ supportedServices = supportedServiceMap.get(plmn3);
+ assertTrue(supportedServices.isEmpty());
+ }
+
+ @Test
+ public void testMergeSupportedSatelliteServices() {
+ String plmn1 = "00101";
+ String plmn2 = "00102";
+ String plmn3 = "00103";
+
+ Integer[] providerSupportedServicesForPlmn1 = {1, 2, 3};
+ Integer[] providerSupportedServicesForPlmn2 = {3, 4};
+ Map<String, Set<Integer>> providerSupportedServicesMap = new HashMap<>();
+ providerSupportedServicesMap.put(
+ plmn1, new HashSet<>(Arrays.asList(providerSupportedServicesForPlmn1)));
+ providerSupportedServicesMap.put(
+ plmn2, new HashSet<>(Arrays.asList(providerSupportedServicesForPlmn2)));
+
+ Integer[] carrierSupportedServicesForPlmn2 = {3};
+ Integer[] carrierSupportedServicesForPlmn3 = {1, 3, 4};
+ Map<String, Set<Integer>> carrierSupportedServicesMap = new HashMap<>();
+ carrierSupportedServicesMap.put(
+ plmn2, new HashSet<>(Arrays.asList(carrierSupportedServicesForPlmn2)));
+ carrierSupportedServicesMap.put(
+ plmn3, new HashSet<>(Arrays.asList(carrierSupportedServicesForPlmn3)));
+
+ // {@code plmn1} is present in only provider support services.
+ int[] expectedSupportedServicesForPlmn1 = {1, 2, 3};
+ // Intersection of {3,4} and {3}.
+ int[] expectedSupportedServicesForPlmn2 = {3};
+ Map<String, Set<Integer>> supportedServicesMap =
+ SatelliteServiceUtils.mergeSupportedSatelliteServices(
+ providerSupportedServicesMap, carrierSupportedServicesMap);
+
+ assertEquals(2, supportedServicesMap.size());
+ assertTrue(supportedServicesMap.containsKey(plmn1));
+ assertTrue(supportedServicesMap.containsKey(plmn2));
+ assertFalse(supportedServicesMap.containsKey(plmn3));
+ assertTrue(Arrays.equals(expectedSupportedServicesForPlmn1,
+ supportedServicesMap.get(plmn1).stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+ assertTrue(Arrays.equals(expectedSupportedServicesForPlmn2,
+ supportedServicesMap.get(plmn2).stream()
+ .mapToInt(Integer::intValue)
+ .toArray()));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
index 9898353178..0358809584 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -269,6 +269,8 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest {
private final List<String> mAllColumns;
+ private boolean mDatabaseChanged;
+
SubscriptionProvider() {
mAllColumns = SimInfo.getAllColumns();
}
@@ -378,7 +380,17 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest {
@Override
public Bundle call(String method, @Nullable String args, @Nullable Bundle bundle) {
- return new Bundle();
+ Bundle result = new Bundle();
+ if (method.equals(SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME)) {
+ result.putBoolean(
+ SubscriptionManager.RESTORE_SIM_SPECIFIC_SETTINGS_DATABASE_UPDATED,
+ mDatabaseChanged);
+ }
+ return result;
+ }
+
+ public void setRestoreDatabaseChanged(boolean changed) {
+ mDatabaseChanged = changed;
}
}
@@ -422,7 +434,7 @@ public class SubscriptionDatabaseManagerTest extends TelephonyTest {
.that(mDatabaseManagerUT.getSubscriptionInfoInternal(subId)).isEqualTo(subInfo);
// Load subscription info from the database.
- mDatabaseManagerUT.reloadDatabase();
+ mDatabaseManagerUT.reloadDatabaseSync();
processAllMessages();
// Verify the database value is same as the inserted one.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
index 06dd17cc47..aea79656c1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -69,8 +69,10 @@ import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.PropertyInvalidatedCache;
import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -174,6 +176,8 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
mContextFixture.putBooleanResource(com.android.internal.R.bool
.config_subscription_database_async_update, true);
mContextFixture.putIntArrayResource(com.android.internal.R.array.sim_colors, new int[0]);
+ mContextFixture.putResource(com.android.internal.R.string.default_card_name,
+ FAKE_DEFAULT_CARD_NAME);
mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_EUICC);
setupMocksForTelephonyPermissions(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
@@ -829,6 +833,8 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(2));
doReturn(TelephonyManager.INVALID_PORT_INDEX).when(mUiccSlot)
.getPortIndexFromIccId(anyString());
+ doReturn(FAKE_ICCID1).when(mUiccController).convertToCardString(eq(1));
+ doReturn(FAKE_ICCID2).when(mUiccController).convertToCardString(eq(2));
mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1, 2), null);
processAllMessages();
@@ -849,6 +855,9 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
assertThat(subInfo.isEmbedded()).isTrue();
assertThat(subInfo.isRemovableEmbedded()).isFalse();
assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES1);
+ // Downloaded esim profile should contain proper cardId
+ assertThat(subInfo.getCardId()).isEqualTo(1);
+ assertThat(subInfo.getCardString()).isEqualTo(FAKE_ICCID1);
subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2);
assertThat(subInfo.getSubscriptionId()).isEqualTo(2);
@@ -865,6 +874,9 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
assertThat(subInfo.isEmbedded()).isTrue();
assertThat(subInfo.isRemovableEmbedded()).isFalse();
assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES2);
+ // Downloaded esim profile should contain proper cardId
+ assertThat(subInfo.getCardId()).isEqualTo(2);
+ assertThat(subInfo.getCardString()).isEqualTo(FAKE_ICCID2);
}
@Test
@@ -1084,6 +1096,19 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
}
@Test
+ public void testGetSubscriptionUserHandleUnknownSubscription() {
+ mContextFixture.addCallingOrSelfPermission(
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+
+ // getSubscriptionUserHandle() returns null when subscription is not available on the device
+ assertThat(mSubscriptionManagerServiceUT.getSubscriptionUserHandle(10))
+ .isEqualTo(null);
+
+ mContextFixture.removeCallingOrSelfPermission(
+ Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION);
+ }
+
+ @Test
public void testIsSubscriptionAssociatedWithUser() {
insertSubscription(FAKE_SUBSCRIPTION_INFO1);
@@ -1891,12 +1916,16 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
assertThat(mSubscriptionManagerServiceUT.getSlotIndex(1)).isEqualTo(0);
assertThat(mSubscriptionManagerServiceUT.getPhoneId(1)).isEqualTo(0);
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ assertThat(subInfo.getDisplayName()).isEqualTo("CARD 1");
+
mSubscriptionManagerServiceUT.setCarrierId(1, FAKE_CARRIER_ID1);
mSubscriptionManagerServiceUT.setDisplayNameUsingSrc(FAKE_CARRIER_NAME1, 1,
SubscriptionManager.NAME_SOURCE_SIM_SPN);
mSubscriptionManagerServiceUT.setCarrierName(1, FAKE_CARRIER_NAME1);
- SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ subInfo = mSubscriptionManagerServiceUT
.getSubscriptionInfoInternal(1);
assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
assertThat(subInfo.getSimSlotIndex()).isEqualTo(0);
@@ -2314,9 +2343,6 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
@Test
public void testInactiveSimInserted() {
- mContextFixture.putResource(com.android.internal.R.string.default_card_name,
- FAKE_DEFAULT_CARD_NAME);
-
doReturn(0).when(mUiccSlot).getPortIndexFromIccId(eq(FAKE_ICCID1));
mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
@@ -2333,16 +2359,40 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
}
@Test
- public void testRestoreAllSimSpecificSettingsFromBackup() {
+ public void testRestoreAllSimSpecificSettingsFromBackup() throws Exception {
assertThrows(SecurityException.class, ()
-> mSubscriptionManagerServiceUT.restoreAllSimSpecificSettingsFromBackup(
new byte[0]));
+ insertSubscription(FAKE_SUBSCRIPTION_INFO1);
mContextFixture.addCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
- // TODO: Briefly copy the logic from TelephonyProvider to
- // SubscriptionDatabaseManagerTest.SubscriptionProvider
+
+ // getSubscriptionDatabaseManager().setWifiCallingEnabled(1, 0);
+
+ // Simulate restoration altered the database directly.
+ ContentValues cvs = new ContentValues();
+ cvs.put(SimInfo.COLUMN_WFC_IMS_ENABLED, 0);
+ mSubscriptionProvider.update(Uri.withAppendedPath(SimInfo.CONTENT_URI, "1"), cvs, null,
+ null);
+
+ // Setting this to false to prevent database reload.
+ mSubscriptionProvider.setRestoreDatabaseChanged(false);
mSubscriptionManagerServiceUT.restoreAllSimSpecificSettingsFromBackup(
new byte[0]);
+
+ SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT
+ .getSubscriptionInfoInternal(1);
+ // Since reload didn't happen, WFC should remains enabled.
+ assertThat(subInfo.getWifiCallingEnabled()).isEqualTo(1);
+
+ // Now the database reload should happen
+ mSubscriptionProvider.setRestoreDatabaseChanged(true);
+ mSubscriptionManagerServiceUT.restoreAllSimSpecificSettingsFromBackup(
+ new byte[0]);
+
+ subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(1);
+ // Since reload didn't happen, WFC should remains enabled.
+ assertThat(subInfo.getWifiCallingEnabled()).isEqualTo(0);
}
@Test
@@ -2494,7 +2544,7 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
.getSubscriptionInfoInternal(1);
assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1);
- assertThat(subInfo.getDisplayName()).isEqualTo("");
+ assertThat(subInfo.getDisplayName()).isEqualTo("CARD 1");
assertThat(subInfo.getDisplayNameSource()).isEqualTo(
SubscriptionManager.NAME_SOURCE_UNKNOWN);
assertThat(subInfo.getMcc()).isEqualTo("");
@@ -2506,7 +2556,7 @@ public class SubscriptionManagerServiceTest extends TelephonyTest {
subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2);
assertThat(subInfo.getSubscriptionId()).isEqualTo(2);
assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID2);
- assertThat(subInfo.getDisplayName()).isEqualTo("");
+ assertThat(subInfo.getDisplayName()).isEqualTo("CARD 2");
assertThat(subInfo.getDisplayNameSource()).isEqualTo(
SubscriptionManager.NAME_SOURCE_UNKNOWN);
assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC2);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
index 143d7c9872..15fb72933b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -570,7 +570,7 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
public Void answer(InvocationOnMock invocation) throws Throwable {
currentFileId.set((String) invocation.getArguments()[6]);
Message message = (Message) invocation.getArguments()[8];
- AsyncResult ar = new AsyncResult(null, new int[]{2}, null);
+ AsyncResult ar = new AsyncResult(null, new IccIoResult(0x90, 0x00, ""), null);
message.obj = ar;
message.sendToTarget();
return null;
@@ -668,7 +668,7 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
public Void answer(InvocationOnMock invocation) throws Throwable {
currentFileId.set((String) invocation.getArguments()[6]);
Message message = (Message) invocation.getArguments()[8];
- AsyncResult ar = new AsyncResult(null, new int[]{2}, null);
+ AsyncResult ar = new AsyncResult(null, new IccIoResult(0x90, 0x00, ""), null);
message.obj = ar;
message.sendToTarget();
return null;