Merge "Update handleImsUnregistered"
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index bfcc064..ede0015 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -1374,17 +1374,18 @@
}
for (String key : keys) {
Objects.requireNonNull(key, "Config key must be non-null");
- // TODO(b/261776046): validate provided key which may has no default value.
- // For now, return empty bundle if any required key is not supported
- if (!allConfigs.containsKey(key)) {
- return new PersistableBundle();
- }
}
PersistableBundle configSubset = new PersistableBundle(
keys.length + CONFIG_SUBSET_METADATA_KEYS.length);
for (String carrierConfigKey : keys) {
Object value = allConfigs.get(carrierConfigKey);
+ if (value == null) {
+ // Filter out keys without values.
+ // In history, many AOSP or OEMs/carriers private configs didn't provide default
+ // values. We have to continue supporting them for now. See b/261776046 for details.
+ continue;
+ }
// Config value itself could be PersistableBundle which requires different API to put
if (value instanceof PersistableBundle) {
configSubset.putPersistableBundle(carrierConfigKey, (PersistableBundle) value);
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 186e7b6..78a734a 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -70,6 +70,7 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.data.DataEvaluation.DataDisallowedReason;
import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
import com.android.internal.telephony.ims.ImsResolver;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
@@ -460,6 +461,10 @@
if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
mDomainSelectionService = new TelephonyDomainSelectionService(this);
DomainSelectionResolver.getInstance().initialize(mDomainSelectionService);
+ // Initialize EmergencyStateTracker if domain selection is supported
+ boolean isSuplDdsSwitchRequiredForEmergencyCall = getResources()
+ .getBoolean(R.bool.config_gnss_supl_requires_default_data_for_emergency);
+ EmergencyStateTracker.make(this, isSuplDdsSwitchRequiredForEmergencyCall);
}
// Only bring up ImsResolver if the device supports having an IMS stack.
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 0124ee1..ffc67e2 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -84,6 +84,7 @@
import android.telephony.CallForwardingInfo;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
+import android.telephony.CellBroadcastIdRange;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
@@ -11756,4 +11757,51 @@
}
return simState.ordinal();
}
-}
\ No newline at end of file
+
+ /**
+ * Get current cell broadcast ranges.
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+ public List<CellBroadcastIdRange> getCellBroadcastIdRanges(int subId) {
+ mApp.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
+ "getCellBroadcastIdRanges");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return getPhone(subId).getCellBroadcastIdRanges();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set reception of cell broadcast messages with the list of the given ranges
+ *
+ * @param ranges the list of {@link CellBroadcastIdRange} to be enabled
+ */
+ @Override
+ @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+ public void setCellBroadcastIdRanges(int subId, @NonNull List<CellBroadcastIdRange> ranges,
+ @Nullable IIntegerConsumer callback) {
+ mApp.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
+ "setCellBroadcastIdRanges");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Phone phone = getPhoneFromSubId(subId);
+ if (DBG) {
+ log("setCellBroadcastIdRanges for subId :" + subId + ", phone:" + phone);
+ }
+ phone.setCellBroadcastIdRanges(ranges, result -> {
+ if (callback != null) {
+ try {
+ callback.accept(result);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "setCellBroadcastIdRanges: callback not available.");
+ }
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/src/com/android/phone/slice/SlicePurchaseController.java b/src/com/android/phone/slice/SlicePurchaseController.java
index f258e2c..64261e1 100644
--- a/src/com/android/phone/slice/SlicePurchaseController.java
+++ b/src/com/android/phone/slice/SlicePurchaseController.java
@@ -956,7 +956,6 @@
}
int capabilityServiceType = getSliceServiceType(capability);
for (NetworkSliceInfo sliceInfo : mSlicingConfig.getSliceInfo()) {
- // TODO: check if TrafficDescriptor has realtime capability slice
if (sliceInfo.getSliceServiceType() == capabilityServiceType
&& sliceInfo.getStatus() == NetworkSliceInfo.SLICE_STATUS_ALLOWED) {
return true;
@@ -967,7 +966,9 @@
@NetworkSliceInfo.SliceServiceType private int getSliceServiceType(
@TelephonyManager.PremiumCapability int capability) {
- // TODO: Implement properly -- potentially need to add new slice service types?
+ if (capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY) {
+ return NetworkSliceInfo.SLICE_SERVICE_TYPE_URLLC;
+ }
return NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
}
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index d58c211..f78a9b9 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -393,9 +393,8 @@
*/
private PhoneAccountHandle findCorrectPhoneAccountHandle() {
TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry.getInstance(null);
- // Check to see if a SIM PhoneAccountHandle Exists for the Call.
- PhoneAccountHandle handle = telecomAccountRegistry.getPhoneAccountHandleForSubId(
- mPhone.getSubId());
+ // Check to see if a the SIM PhoneAccountHandle Exists for the Call.
+ PhoneAccountHandle handle = PhoneUtils.makePstnPhoneAccountHandle(mPhone);
if (telecomAccountRegistry.hasAccountEntryForPhoneAccount(handle)) {
return handle;
}
diff --git a/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java
new file mode 100644
index 0000000..cd588e1
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.BarringInfo;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.VopsSupportInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Implements an emergency SMS domain selector for sending an emergency SMS.
+ */
+public class EmergencySmsDomainSelector extends SmsDomainSelector implements
+ ImsStateTracker.BarringInfoListener, ImsStateTracker.ServiceStateListener {
+ /**
+ * Stores the configuration value of
+ * {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL}.
+ * This value is always updated whenever the domain selection is requested.
+ */
+ private Boolean mEmergencySmsOverImsSupportedByConfig;
+ private ServiceState mServiceState;
+ private boolean mServiceStateReceived;
+ private BarringInfo mBarringInfo;
+ private boolean mBarringInfoReceived;
+
+ public EmergencySmsDomainSelector(Context context, int slotId, int subId,
+ @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
+ @NonNull DestroyListener listener) {
+ super(context, slotId, subId, looper, imsStateTracker, listener,
+ "DomainSelector-EmergencySMS");
+
+ mImsStateTracker.addServiceStateListener(this);
+ mImsStateTracker.addBarringInfoListener(this);
+ }
+
+ @Override
+ public void destroy() {
+ if (mDestroyed) {
+ return;
+ }
+ mImsStateTracker.removeServiceStateListener(this);
+ mImsStateTracker.removeBarringInfoListener(this);
+ super.destroy();
+ }
+
+ @Override
+ public void finishSelection() {
+ super.finishSelection();
+ mServiceStateReceived = false;
+ mServiceState = null;
+ mBarringInfoReceived = false;
+ mBarringInfo = null;
+ mEmergencySmsOverImsSupportedByConfig = null;
+ }
+
+ @Override
+ public void onBarringInfoUpdated(BarringInfo barringInfo) {
+ mBarringInfoReceived = true;
+ mBarringInfo = barringInfo;
+ sendMessageForDomainSelection();
+ }
+
+ @Override
+ public void onServiceStateUpdated(ServiceState serviceState) {
+ mServiceStateReceived = true;
+ mServiceState = serviceState;
+ sendMessageForDomainSelection();
+ }
+
+ /**
+ * Checks whether the domain selector is ready to select the domain or not.
+ * The emergency SMS requires to be updated for the {@link ServiceState} and
+ * {@link BarringInfo} to confirm that the cellular network supports to send emergency SMS
+ * messages over IMS.
+ */
+ @VisibleForTesting
+ public boolean isDomainSelectionReady() {
+ return mServiceStateReceived && mBarringInfoReceived;
+ }
+
+ @Override
+ protected boolean isSmsOverImsAvailable() {
+ if (super.isSmsOverImsAvailable()) {
+ /**
+ * Even though IMS is successfully registered, the cellular domain should be
+ * available for the emergency SMS according to the carrier's requirement
+ * when {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL} is set
+ * to true.
+ */
+ if (isEmergencySmsOverImsSupportedByConfig()) {
+ /**
+ * Emergency SMS should be supported via emergency PDN.
+ * If this condition is false, then need to fallback to CS network
+ * because the current PS network does not allow the emergency service.
+ */
+ return isNetworkAvailableForImsEmergencySms();
+ }
+
+ // Emergency SMS is supported via IMS PDN.
+ return true;
+ }
+
+ return isImsEmergencySmsAvailable();
+ }
+
+ @Override
+ protected void selectDomain() {
+ if (!isDomainSelectionRequested()) {
+ logi("Domain selection is not requested!");
+ return;
+ }
+
+ if (!isDomainSelectionReady()) {
+ logd("Wait for the readiness of the domain selection!");
+ return;
+ }
+
+ logi("selectDomain: " + mImsStateTracker.imsStateToString());
+
+ if (isSmsOverImsAvailable()) {
+ if (mImsStateTracker.isImsRegisteredOverWlan()) {
+ if (!isEmergencySmsOverImsSupportedByConfig()) {
+ notifyWlanSelected();
+ return;
+ }
+
+ /**
+ * When {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL}
+ * is set to true, the emergency SMS supports on the LTE network using the
+ * emergency PDN. So, considering EUTRAN only at this point.
+ */
+ logi("DomainSelected: WLAN >> WWAN");
+ }
+ notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_PS);
+ } else {
+ notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_CS);
+ }
+ }
+
+ /**
+ * Checks if the emergency SMS messages over IMS is available according to the carrier
+ * configuration and the current network states.
+ */
+ private boolean isImsEmergencySmsAvailable() {
+ boolean emergencySmsOverImsSupportedByConfig = isEmergencySmsOverImsSupportedByConfig();
+ boolean networkAvailable = isNetworkAvailableForImsEmergencySms();
+
+ logi("isImsEmergencySmsAvailable: "
+ + "emergencySmsOverIms=" + emergencySmsOverImsSupportedByConfig
+ + ", mmTelFeatureAvailable=" + mImsStateTracker.isMmTelFeatureAvailable()
+ + ", networkAvailable=" + networkAvailable);
+
+ return emergencySmsOverImsSupportedByConfig
+ && mImsStateTracker.isMmTelFeatureAvailable()
+ && networkAvailable;
+ }
+
+ /**
+ * Checks if sending emergency SMS messages over IMS is supported when in LTE/limited LTE
+ * (Emergency only) service mode from the carrier configuration.
+ */
+ private boolean isEmergencySmsOverImsSupportedByConfig() {
+ if (mEmergencySmsOverImsSupportedByConfig == null) {
+ CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
+
+ if (ccm == null) {
+ loge("CarrierConfigManager is null");
+ return false;
+ }
+
+ PersistableBundle b = ccm.getConfigForSubId(getSubId());
+
+ if (b == null) {
+ loge("PersistableBundle is null");
+ return false;
+ }
+
+ mEmergencySmsOverImsSupportedByConfig = b.getBoolean(
+ CarrierConfigManager.KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL);
+ }
+
+ return mEmergencySmsOverImsSupportedByConfig;
+ }
+
+ /**
+ * Checks if the emergency service is available in the LTE service mode.
+ */
+ private boolean isLteEmergencyAvailableInService() {
+ if (mServiceState == null) {
+ return false;
+ }
+
+ final NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ if (regInfo != null
+ && regInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_LTE
+ && regInfo.isRegistered()) {
+ return isEmergencyServiceSupported(regInfo) && isEmergencyServiceAllowed();
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the emergency service is available in the limited LTE service(Emergency only) mode.
+ */
+ private boolean isLteEmergencyAvailableInLimitedService() {
+ if (mServiceState == null) {
+ return false;
+ }
+
+ final NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ if (regInfo != null
+ && regInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_LTE
+ && regInfo.isEmergencyEnabled()) {
+ return isEmergencyServiceSupported(regInfo) && isEmergencyServiceAllowed();
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the network is available for the IMS emergency SMS.
+ */
+ private boolean isNetworkAvailableForImsEmergencySms() {
+ return isLteEmergencyAvailableInService()
+ || isLteEmergencyAvailableInLimitedService();
+ }
+
+ /**
+ * Checks if the emergency service is supported by the network.
+ *
+ * This checks if "Emergency bearer services indicator (EMC-BS)" field (bits) set to
+ * the "Emergency bearer services in S1 mode supported".
+ *
+ * @return {@code true} if the emergency service is supported by the network,
+ * {@code false} otherwise.
+ */
+ private boolean isEmergencyServiceSupported(@NonNull NetworkRegistrationInfo regInfo) {
+ final DataSpecificRegistrationInfo dsRegInfo = regInfo.getDataSpecificInfo();
+ if (dsRegInfo != null) {
+ final VopsSupportInfo vopsSupportInfo = dsRegInfo.getVopsSupportInfo();
+ return vopsSupportInfo != null
+ && vopsSupportInfo.isEmergencyServiceSupported();
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the emergency service is allowed (not barred) by the network.
+ *
+ * This checks if SystemInformationBlockType2 includes the ac-BarringInfo and
+ * with the ac-BarringForEmergency set to FALSE or
+ * if the SystemInformationBlockType2 does not include the ac-BarringInfo.
+ *
+ * @return {@code true} if the emergency service is allowed by the network,
+ * {@code false} otherwise.
+ */
+ private boolean isEmergencyServiceAllowed() {
+ if (mBarringInfo == null) {
+ return true;
+ }
+ final BarringInfo.BarringServiceInfo bsi =
+ mBarringInfo.getBarringServiceInfo(BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY);
+ return !bsi.isBarred();
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/SmsDomainSelector.java b/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
new file mode 100644
index 0000000..e7c9ac5
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.DisconnectCause;
+import android.telephony.DomainSelectionService;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TransportSelectorCallback;
+
+/**
+ * Implements SMS domain selector for sending MO SMS.
+ */
+public class SmsDomainSelector extends DomainSelectorBase implements
+ ImsStateTracker.ImsStateListener {
+ protected static final int EVENT_SELECT_DOMAIN = 101;
+
+ protected boolean mDestroyed = false;
+ private boolean mDomainSelectionRequested = false;
+
+ public SmsDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper,
+ @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener listener) {
+ this(context, slotId, subId, looper, imsStateTracker, listener, "DomainSelector-SMS");
+ }
+
+ protected SmsDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper,
+ @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener listener,
+ String logTag) {
+ super(context, slotId, subId, looper, imsStateTracker, listener, logTag);
+ }
+
+ @Override
+ public void destroy() {
+ if (mDestroyed) {
+ return;
+ }
+ logd("destroy");
+ mDestroyed = true;
+ mImsStateTracker.removeImsStateListener(this);
+ super.destroy();
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case EVENT_SELECT_DOMAIN:
+ selectDomain();
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+ }
+ }
+
+ @Override
+ public void cancelSelection() {
+ logi("cancelSelection");
+ finishSelection();
+ }
+
+ @Override
+ public void reselectDomain(@NonNull SelectionAttributes attr) {
+ if (isDomainSelectionRequested()) {
+ // The domain selection is already requested,
+ // so we don't need to request it again before completing the previous task.
+ logi("Domain selection is already running.");
+ return;
+ }
+
+ logi("reselectDomain");
+ mSelectionAttributes = attr;
+ setDomainSelectionRequested(true);
+ obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
+ }
+
+ @Override
+ public void finishSelection() {
+ logi("finishSelection");
+ setDomainSelectionRequested(false);
+ mSelectionAttributes = null;
+ mTransportSelectorCallback = null;
+ mWwanSelectorCallback = null;
+ destroy();
+ }
+
+ @Override
+ public void selectDomain(SelectionAttributes attr, TransportSelectorCallback callback) {
+ if (isDomainSelectionRequested()) {
+ // The domain selection is already requested,
+ // so we don't need to request it again before completing the previous task.
+ logi("Domain selection is already running.");
+ return;
+ }
+ mSelectionAttributes = attr;
+ mTransportSelectorCallback = callback;
+ setDomainSelectionRequested(true);
+ mImsStateTracker.addImsStateListener(this);
+ obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
+ }
+
+ @Override
+ public void onImsMmTelFeatureAvailableChanged() {
+ sendMessageForDomainSelection();
+ }
+
+ @Override
+ public void onImsRegistrationStateChanged() {
+ sendMessageForDomainSelection();
+ }
+
+ @Override
+ public void onImsMmTelCapabilitiesChanged() {
+ sendMessageForDomainSelection();
+ }
+
+ protected boolean isSmsOverImsAvailable() {
+ return mImsStateTracker.isImsSmsCapable()
+ && mImsStateTracker.isImsRegistered()
+ && mImsStateTracker.isMmTelFeatureAvailable();
+ }
+
+ protected void selectDomain() {
+ if (!isDomainSelectionRequested()) {
+ logi("Domain selection is not requested!");
+ return;
+ }
+
+ logi("selectDomain: " + mImsStateTracker.imsStateToString());
+
+ if (isSmsOverImsAvailable()) {
+ if (mImsStateTracker.isImsRegisteredOverWlan()) {
+ notifyWlanSelected();
+ return;
+ }
+ notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_PS);
+ } else {
+ notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_CS);
+ }
+ }
+
+ protected void sendMessageForDomainSelection() {
+ // If the event is already queued to this handler,
+ // it will be removed first to avoid the duplicate operation.
+ removeMessages(EVENT_SELECT_DOMAIN);
+ // Since the IMS state may have already been posted,
+ // proceed with the domain selection after processing all pending messages.
+ obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
+ }
+
+ protected boolean isDomainSelectionRequested() {
+ return mDomainSelectionRequested;
+ }
+
+ protected void setDomainSelectionRequested(boolean requested) {
+ if (mDomainSelectionRequested != requested) {
+ logd("DomainSelectionRequested: " + mDomainSelectionRequested + " >> " + requested);
+ mDomainSelectionRequested = requested;
+ }
+ }
+
+ protected void notifyWlanSelected() {
+ logi("DomainSelected: WLAN");
+ mTransportSelectorCallback.onWlanSelected();
+ setDomainSelectionRequested(false);
+ }
+
+ protected void notifyWwanSelected(@NetworkRegistrationInfo.Domain int domain) {
+ if (mWwanSelectorCallback == null) {
+ mTransportSelectorCallback.onWwanSelected((callback) -> {
+ mWwanSelectorCallback = callback;
+ notifyWwanSelectedInternal(domain);
+ });
+ } else {
+ notifyWwanSelectedInternal(domain);
+ }
+
+ setDomainSelectionRequested(false);
+ }
+
+ protected void notifyWwanSelectedInternal(@NetworkRegistrationInfo.Domain int domain) {
+ logi("DomainSelected: WWAN/" + DomainSelectionService.getDomainName(domain));
+
+ if (mWwanSelectorCallback != null) {
+ mWwanSelectorCallback.onDomainSelected(domain);
+ } else {
+ mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.LOCAL);
+ }
+ }
+}
diff --git a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
index d537281..6f47ee6 100644
--- a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
+++ b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
@@ -97,14 +97,13 @@
}
break;
case SELECTOR_TYPE_SMS:
- // TODO(ag/20075167) uncomment when SMS domain selector is ready.
- /*if (isEmergency) {
+ if (isEmergency) {
selector = new EmergencySmsDomainSelector(context, slotId, subId, looper,
imsStateTracker, listener);
} else {
selector = new SmsDomainSelector(context, slotId, subId, looper,
imsStateTracker, listener);
- }*/
+ }
break;
default:
// Not reachable.
diff --git a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
index e9e23f3..8320807 100644
--- a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
+++ b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
@@ -505,7 +505,7 @@
public void testPurchasePremiumCapabilityResultAlreadyPurchased() {
testPurchasePremiumCapabilityResultSuccess();
- sendNetworkSlicingConfig(true);
+ sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, true);
mSlicePurchaseController.purchasePremiumCapability(
TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, TAG,
@@ -523,7 +523,7 @@
mResult);
// retry to verify purchase expired
- sendNetworkSlicingConfig(false);
+ sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, false);
testPurchasePremiumCapabilityResultSuccess();
}
@@ -705,9 +705,9 @@
assertEquals(TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, mResult);
// complete network setup
- sendNetworkSlicingConfig(true);
+ sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, true);
// purchase expired
- sendNetworkSlicingConfig(false);
+ sendNetworkSlicingConfig(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, false);
}
private void sendValidPurchaseRequest() {
@@ -755,13 +755,17 @@
verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
}
- private void sendNetworkSlicingConfig(boolean configExists) {
- // TODO: implement slicing config logic properly
+ private void sendNetworkSlicingConfig(int capability, boolean configActive) {
+ int sliceServiceType = capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY
+ ? NetworkSliceInfo.SLICE_SERVICE_TYPE_URLLC
+ : NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE;
+ NetworkSliceInfo sliceInfo = new NetworkSliceInfo.Builder()
+ .setStatus(configActive ? NetworkSliceInfo.SLICE_STATUS_ALLOWED
+ : NetworkSliceInfo.SLICE_STATUS_UNKNOWN)
+ .setSliceServiceType(sliceServiceType)
+ .build();
NetworkSlicingConfig slicingConfig = new NetworkSlicingConfig(Collections.emptyList(),
- configExists
- ? Collections.singletonList(new NetworkSliceInfo.Builder()
- .setStatus(NetworkSliceInfo.SLICE_STATUS_ALLOWED).build())
- : Collections.emptyList());
+ Collections.singletonList(sliceInfo));
mSlicePurchaseController.obtainMessage(2 /* EVENT_SLICING_CONFIG_CHANGED */,
new AsyncResult(null, slicingConfig, null)).sendToTarget();
mTestableLooper.processAllMessages();
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
new file mode 100644
index 0000000..8b63530
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
@@ -0,0 +1,799 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.telephony.BarringInfo;
+import android.telephony.CarrierConfigManager;
+import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.VopsSupportInfo;
+import android.telephony.WwanSelectorCallback;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+import android.util.SparseArray;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+/**
+ * Unit tests for EmergencySmsDomainSelector.
+ */
+@RunWith(AndroidJUnit4.class)
+public class EmergencySmsDomainSelectorTest {
+ private static final int SLOT_0 = 0;
+ private static final int SUB_1 = 1;
+
+ @Mock private ServiceState mServiceState;
+ @Mock private TransportSelectorCallback mTransportSelectorCallback;
+ @Mock private WwanSelectorCallback mWwanSelectorCallback;
+ @Mock private VopsSupportInfo mVopsSupportInfo;
+ @Mock private ImsStateTracker mImsStateTracker;
+ @Mock private DomainSelectorBase.DestroyListener mDomainSelectorDestroyListener;
+
+ private final SelectionAttributes mSelectionAttributes =
+ new SelectionAttributes.Builder(SLOT_0, SUB_1, SELECTOR_TYPE_SMS)
+ .setEmergency(true)
+ .build();
+ private Context mContext;
+ private Looper mLooper;
+ private TestableLooper mTestableLooper;
+ private CarrierConfigManager mCarrierConfigManager;
+ private NetworkRegistrationInfo mNetworkRegistrationInfo;
+ private boolean mCarrierConfigManagerNullTest = false;
+ private BarringInfo mBarringInfo = new BarringInfo();
+ private ImsStateTracker.ImsStateListener mImsStateListener;
+ private ImsStateTracker.BarringInfoListener mBarringInfoListener;
+ private ImsStateTracker.ServiceStateListener mServiceStateListener;
+ private EmergencySmsDomainSelector mDomainSelector;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext() {
+ @Override
+ public Object getSystemService(String name) {
+ if (name.equals(Context.CARRIER_CONFIG_SERVICE)) {
+ if (mCarrierConfigManagerNullTest) {
+ return null;
+ }
+ }
+
+ return super.getSystemService(name);
+ }
+ };
+
+ HandlerThread handlerThread = new HandlerThread(
+ EmergencySmsDomainSelectorTest.class.getSimpleName());
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ mTestableLooper = new TestableLooper(mLooper);
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+
+ mDomainSelector = new EmergencySmsDomainSelector(mContext, SLOT_0, SUB_1,
+ mLooper, mImsStateTracker, mDomainSelectorDestroyListener);
+
+ ArgumentCaptor<ImsStateTracker.ServiceStateListener> serviceStateListenerCaptor =
+ ArgumentCaptor.forClass(ImsStateTracker.ServiceStateListener.class);
+ verify(mImsStateTracker).addServiceStateListener(serviceStateListenerCaptor.capture());
+ mServiceStateListener = serviceStateListenerCaptor.getValue();
+ assertNotNull(mServiceStateListener);
+
+ ArgumentCaptor<ImsStateTracker.BarringInfoListener> barringInfoListenerCaptor =
+ ArgumentCaptor.forClass(ImsStateTracker.BarringInfoListener.class);
+ verify(mImsStateTracker).addBarringInfoListener(barringInfoListenerCaptor.capture());
+ mBarringInfoListener = barringInfoListenerCaptor.getValue();
+ assertNotNull(mBarringInfoListener);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTestableLooper != null) {
+ mTestableLooper.destroy();
+ mTestableLooper = null;
+ }
+
+ if (mDomainSelector != null) {
+ mDomainSelector.destroy();
+ verify(mImsStateTracker).removeImsStateListener(eq(mDomainSelector));
+ verify(mImsStateTracker).removeBarringInfoListener(eq(mDomainSelector));
+ verify(mImsStateTracker).removeServiceStateListener(eq(mDomainSelector));
+ }
+
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+
+ mDomainSelector = null;
+ mNetworkRegistrationInfo = null;
+ mVopsSupportInfo = null;
+ mDomainSelectorDestroyListener = null;
+ mWwanSelectorCallback = null;
+ mTransportSelectorCallback = null;
+ mServiceState = null;
+ mCarrierConfigManager = null;
+ mCarrierConfigManagerNullTest = false;
+ }
+
+ @Test
+ @SmallTest
+ public void testFinishSelection() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(mBarringInfo);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+ assertTrue(mDomainSelector.isDomainSelectionReady());
+
+ mDomainSelector.finishSelection();
+
+ assertFalse(mDomainSelector.isDomainSelectionReady());
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsDomainSelectionReady() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ assertFalse(mDomainSelector.isDomainSelectionReady());
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(mBarringInfo);
+
+ assertTrue(mDomainSelector.isDomainSelectionReady());
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsDomainSelectionReadyAndSelectDomain() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(mBarringInfo);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+ assertTrue(mDomainSelector.isDomainSelectionReady());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenImsRegistered() {
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenImsRegisteredAndConfigEnabledAndLteAvailable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenImsRegisteredAndConfigEnabledAndLteNotAvailable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, false, false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenCarrierConfigManagerIsNull() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ mCarrierConfigManagerNullTest = true;
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenCarrierConfigIsNull() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenCarrierConfigNotEnabled() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenMmTelFeatureUnavailable() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN, false, false);
+ setUpCarrierConfig(true);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenServiceStateIsNull() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenNoLte() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteNotRegisteredOrEmergencyNotEnabled() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndNoDataSpecificRegistrationInfo() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(true, true, true, true, false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndNoVopsSupportInfo() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, true, true, true, false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndEmcBsNotSupported() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, true, false);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndEmcBsSupportedAndNoBarringInfo() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, true, false);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndEmcBsSupportedAndBarred() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, true);
+
+ assertFalse(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInServiceAndEmcBsSupportedAndNotBarred() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testIsSmsOverImsAvailableWhenLteInLimitedService() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpCarrierConfig(true);
+ setUpLimitedLteService(false, false, true, false, false);
+
+ assertTrue(mDomainSelector.isSmsOverImsAvailable());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhilePreviousRequestInProgress() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(mBarringInfo);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ processAllMessages();
+
+ // onDomainSelected will be invoked only once
+ // even though the domain selection was requested twice.
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndConfigDisabled() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(false);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndUmtsNetwork() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpNonLteService(TelephonyManager.NETWORK_TYPE_UMTS);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndUnknownNetwork() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpNonLteService(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLteInService() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: PS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLteEmcBsNotSupported() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLteEmergencyBarred() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, true);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLimitedLteService() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLimitedLteService(false, false, true, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: PS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLimitedLteEmcBsNotSupported() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLimitedLteService(false, false, false, false, false);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegisteredAndLimitedLteEmergencyBarred() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLimitedLteService(false, false, true, false, true);
+ setUpImsStateListener(true, false, false);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnLte() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: PS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnLteAndEmcBsNotSupported() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnLteAndEmergencyBarred() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, true);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnIwlanAndConfigDisabled() {
+ setUpImsStateTracker(AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(false);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: WLAN
+ verify(mTransportSelectorCallback).onWlanSelected();
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnIwlanAndLteNotAvailable() {
+ setUpImsStateTracker(AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, false, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: CS network - even though IMS is successfully registered over Wi-Fi,
+ // if the emergency SMS messages over IMS is enabled in the carrier configuration and
+ // the PS network does not allow the emergency service, this MO SMS should be routed to
+ // CS domain.
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnIwlanAndLteAvailable() {
+ setUpImsStateTracker(AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+ setUpCarrierConfig(true);
+ setUpLteInService(false, false, true, false, false);
+ setUpImsStateListener(true, true, true);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+ processAllMessages();
+
+ // Expected: PS network
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ }
+
+ private void setUpCarrierConfig(boolean supported) {
+ PersistableBundle b = new PersistableBundle();
+ b.putBoolean(CarrierConfigManager.KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL, supported);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(b);
+ }
+
+ private void setUpNonLteService(int networkType) {
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(networkType)
+ .setRegistrationState(networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN
+ ? NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN
+ : NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(null);
+ }
+
+ private void setUpLteInService(boolean noDataSpecificRegistrationInfo,
+ boolean noVopsSupportInfo, boolean emcBsSupported,
+ boolean noBarringInfo, boolean barred) {
+ DataSpecificRegistrationInfo dsri = noDataSpecificRegistrationInfo
+ ? null : new DataSpecificRegistrationInfo(
+ 8, false, false, false, noVopsSupportInfo ? null : mVopsSupportInfo);
+
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setDataSpecificInfo(dsri)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+ when(mVopsSupportInfo.isEmergencyServiceSupported()).thenReturn(emcBsSupported);
+
+ BarringInfo barringInfo = null;
+
+ if (!noBarringInfo) {
+ SparseArray<BarringInfo.BarringServiceInfo> barringServiceInfos = new SparseArray<>();
+ barringServiceInfos.put(BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY,
+ new BarringInfo.BarringServiceInfo(
+ barred ? BarringInfo.BarringServiceInfo.BARRING_TYPE_UNCONDITIONAL :
+ BarringInfo.BarringServiceInfo.BARRING_TYPE_NONE, false, 0, 0));
+ barringInfo = new BarringInfo(null, barringServiceInfos);
+ }
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(barringInfo);
+ }
+
+ private void setUpLimitedLteService(boolean noDataSpecificRegistrationInfo,
+ boolean noVopsSupportInfo, boolean emcBsSupported,
+ boolean noBarringInfo, boolean barred) {
+ DataSpecificRegistrationInfo dsri = noDataSpecificRegistrationInfo
+ ? null : new DataSpecificRegistrationInfo(
+ 8, false, false, false, noVopsSupportInfo ? null : mVopsSupportInfo);
+
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setEmergencyOnly(true)
+ .setDataSpecificInfo(dsri)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(
+ anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+ .thenReturn(mNetworkRegistrationInfo);
+ when(mVopsSupportInfo.isEmergencyServiceSupported()).thenReturn(emcBsSupported);
+
+ BarringInfo barringInfo = null;
+
+ if (!noBarringInfo) {
+ SparseArray<BarringInfo.BarringServiceInfo> barringServiceInfos = new SparseArray<>();
+ barringServiceInfos.put(BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY,
+ new BarringInfo.BarringServiceInfo(
+ barred ? BarringInfo.BarringServiceInfo.BARRING_TYPE_UNCONDITIONAL :
+ BarringInfo.BarringServiceInfo.BARRING_TYPE_NONE, false, 0, 0));
+ barringInfo = new BarringInfo(null, barringServiceInfos);
+ }
+
+ mServiceStateListener.onServiceStateUpdated(mServiceState);
+ mBarringInfoListener.onBarringInfoUpdated(barringInfo);
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType) {
+ setUpImsStateTracker(accessNetworkType, true, true);
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType,
+ boolean mmTelFeatureAvailable, boolean smsCapable) {
+ when(mImsStateTracker.isMmTelFeatureAvailable()).thenReturn(mmTelFeatureAvailable);
+ when(mImsStateTracker.isImsRegistered())
+ .thenReturn(accessNetworkType != AccessNetworkType.UNKNOWN);
+ when(mImsStateTracker.isImsRegisteredOverWlan())
+ .thenReturn(accessNetworkType == AccessNetworkType.IWLAN);
+ when(mImsStateTracker.getImsAccessNetworkType()).thenReturn(accessNetworkType);
+ when(mImsStateTracker.isImsSmsCapable()).thenReturn(smsCapable);
+ }
+
+ private void setUpWwanSelectorCallback() {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final Consumer<WwanSelectorCallback> callback =
+ (Consumer<WwanSelectorCallback>) args[0];
+ callback.accept(mWwanSelectorCallback);
+ return null;
+ }).when(mTransportSelectorCallback).onWwanSelected(any(Consumer.class));
+ }
+
+ private void setUpImsStateListener(boolean notifyMmTelFeatureAvailable,
+ boolean notifyImsRegState, boolean notifyMmTelCapability) {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final ImsStateTracker.ImsStateListener listener =
+ (ImsStateTracker.ImsStateListener) args[0];
+ mDomainSelector.post(() -> {
+ if (notifyMmTelFeatureAvailable) {
+ listener.onImsMmTelFeatureAvailableChanged();
+ }
+ if (notifyImsRegState) {
+ listener.onImsRegistrationStateChanged();
+ }
+ if (notifyMmTelCapability) {
+ listener.onImsMmTelCapabilitiesChanged();
+ }
+ });
+ return null;
+ }).when(mImsStateTracker).addImsStateListener(any(ImsStateTracker.ImsStateListener.class));
+ }
+
+ private void processAllMessages() {
+ while (!mTestableLooper.getLooper().getQueue().isIdle()) {
+ mTestableLooper.processAllMessages();
+ }
+ }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
new file mode 100644
index 0000000..412d83d
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2022 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.services.telephony.domainselection;
+
+import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
+import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TransportSelectorCallback;
+import android.telephony.WwanSelectorCallback;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TestContext;
+
+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.function.Consumer;
+
+/**
+ * Unit tests for SmsDomainSelector.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmsDomainSelectorTest {
+ private static final String LOG_TAG = "DomainSelector-SMS";
+ private static final int SLOT_0 = 0;
+ private static final int SUB_1 = 1;
+ private static final int SUB_2 = 2;
+
+ @Mock private TransportSelectorCallback mTransportSelectorCallback;
+ @Mock private WwanSelectorCallback mWwanSelectorCallback;
+ @Mock private ImsStateTracker mImsStateTracker;
+ @Mock private DomainSelectorBase.DestroyListener mDomainSelectorDestroyListener;
+
+ private final SelectionAttributes mSelectionAttributes =
+ new SelectionAttributes.Builder(SLOT_0, SUB_1, SELECTOR_TYPE_SMS).build();
+ private Context mContext;
+ private Looper mLooper;
+ private TestableLooper mTestableLooper;
+ private SmsDomainSelector mDomainSelector;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext();
+ HandlerThread handlerThread = new HandlerThread(
+ SmsDomainSelectorTest.class.getSimpleName());
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ mTestableLooper = new TestableLooper(mLooper);
+ mDomainSelector = new SmsDomainSelector(mContext, SLOT_0, SUB_1,
+ mLooper, mImsStateTracker, mDomainSelectorDestroyListener);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mTestableLooper != null) {
+ mTestableLooper.destroy();
+ mTestableLooper = null;
+ }
+
+ if (mDomainSelector != null) {
+ mDomainSelector.destroy();
+ verify(mImsStateTracker).removeImsStateListener(eq(mDomainSelector));
+ }
+
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+
+ mDomainSelector = null;
+ mWwanSelectorCallback = null;
+ mTransportSelectorCallback = null;
+ mImsStateTracker = null;
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnEutran() {
+ selectDomain(AccessNetworkType.EUTRAN);
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnNgran() {
+ selectDomain(AccessNetworkType.NGRAN);
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsRegisteredOnIwlan() {
+ selectDomain(AccessNetworkType.IWLAN);
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenImsNotRegistered() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpImsStateListener(true, false, false);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhenWwanSelectorCallbackNull() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final Consumer<WwanSelectorCallback> callback =
+ (Consumer<WwanSelectorCallback>) args[0];
+ callback.accept(null);
+ return null;
+ }).when(mTransportSelectorCallback).onWwanSelected(any(Consumer.class));
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mTransportSelectorCallback).onSelectionTerminated(anyInt());
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testSelectDomainWhilePreviousRequestInProgress() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ processAllMessages();
+
+ // onDomainSelected will be invoked only once
+ // even though the domain selection was requested twice.
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testCancelSelection() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.cancelSelection();
+
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ verify(mDomainSelectorDestroyListener).onDomainSelectorDestroyed(eq(mDomainSelector));
+ }
+
+ @Test
+ @SmallTest
+ public void testFinishSelection() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN);
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.finishSelection();
+
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ verify(mDomainSelectorDestroyListener).onDomainSelectorDestroyed(eq(mDomainSelector));
+ }
+
+ @Test
+ @SmallTest
+ public void testReselectDomain() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.reselectDomain(mSelectionAttributes);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mTransportSelectorCallback).onWlanSelected();
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testReselectDomainWhilePreviousRequestInProgress() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, AccessNetworkType.IWLAN);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ mDomainSelector.reselectDomain(mSelectionAttributes);
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ verify(mTransportSelectorCallback, never()).onWlanSelected();
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsRegistrationStateChangedWhenNotRegistered() {
+ setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+ setUpImsStateListener(false, true, false);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsRegistrationStateChangedWhenRegisteredAndSmsCapable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, true);
+ setUpImsStateListener(false, true, false);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsRegistrationStateChangedWhenRegisteredAndSmsIncapable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, false);
+ setUpImsStateListener(false, true, false);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsMmTelCapabilitiesChangedWhenSmsCapable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, true);
+ setUpImsStateListener(false, false, true);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ @Test
+ @SmallTest
+ public void testOnImsMmTelCapabilitiesChangedWhenSmsIncapable() {
+ setUpImsStateTracker(AccessNetworkType.EUTRAN, false);
+ setUpImsStateListener(false, false, true);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS));
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ private void selectDomain(@RadioAccessNetworkType int accessNetworkType) {
+ setUpImsStateTracker(accessNetworkType);
+ setUpWwanSelectorCallback();
+
+ mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+
+ assertTrue(mDomainSelector.isDomainSelectionRequested());
+
+ processAllMessages();
+
+ if (accessNetworkType == AccessNetworkType.IWLAN) {
+ verify(mTransportSelectorCallback).onWlanSelected();
+ } else {
+ verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS));
+ }
+ assertFalse(mDomainSelector.isDomainSelectionRequested());
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType) {
+ setUpImsStateTracker(accessNetworkType, true);
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType,
+ boolean smsCapable) {
+ when(mImsStateTracker.isMmTelFeatureAvailable()).thenReturn(true);
+ when(mImsStateTracker.isImsRegistered())
+ .thenReturn(accessNetworkType != AccessNetworkType.UNKNOWN);
+ when(mImsStateTracker.isImsRegisteredOverWlan())
+ .thenReturn(accessNetworkType == AccessNetworkType.IWLAN);
+ when(mImsStateTracker.getImsAccessNetworkType()).thenReturn(accessNetworkType);
+ when(mImsStateTracker.isImsSmsCapable()).thenReturn(smsCapable);
+ }
+
+ private void setUpImsStateTracker(@RadioAccessNetworkType int firstAccessNetworkType,
+ @RadioAccessNetworkType int secondAccessNetworkType) {
+ when(mImsStateTracker.isMmTelFeatureAvailable()).thenReturn(true);
+ when(mImsStateTracker.isImsRegistered()).thenReturn(
+ firstAccessNetworkType != AccessNetworkType.UNKNOWN,
+ secondAccessNetworkType != AccessNetworkType.UNKNOWN);
+ when(mImsStateTracker.isImsRegisteredOverWlan()).thenReturn(
+ firstAccessNetworkType == AccessNetworkType.IWLAN,
+ secondAccessNetworkType == AccessNetworkType.IWLAN);
+ when(mImsStateTracker.getImsAccessNetworkType()).thenReturn(
+ firstAccessNetworkType,
+ secondAccessNetworkType);
+ when(mImsStateTracker.isImsSmsCapable()).thenReturn(true);
+ }
+
+ private void setUpWwanSelectorCallback() {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final Consumer<WwanSelectorCallback> callback =
+ (Consumer<WwanSelectorCallback>) args[0];
+ callback.accept(mWwanSelectorCallback);
+ return null;
+ }).when(mTransportSelectorCallback).onWwanSelected(any(Consumer.class));
+ }
+
+ private void setUpImsStateListener(boolean notifyMmTelFeatureAvailable,
+ boolean notifyImsRegState, boolean notifyMmTelCapability) {
+ doAnswer((invocation) -> {
+ Object[] args = invocation.getArguments();
+ final ImsStateTracker.ImsStateListener listener =
+ (ImsStateTracker.ImsStateListener) args[0];
+ mDomainSelector.post(() -> {
+ if (notifyMmTelFeatureAvailable) {
+ listener.onImsMmTelFeatureAvailableChanged();
+ }
+ if (notifyImsRegState) {
+ listener.onImsRegistrationStateChanged();
+ }
+ if (notifyMmTelCapability) {
+ listener.onImsMmTelCapabilitiesChanged();
+ }
+ });
+ return null;
+ }).when(mImsStateTracker).addImsStateListener(any(ImsStateTracker.ImsStateListener.class));
+ }
+
+ private void processAllMessages() {
+ while (!mTestableLooper.getLooper().getQueue().isIdle()) {
+ mTestableLooper.processAllMessages();
+ }
+ }
+}