final CharSequence[] carrierNames = new CharSequence[numSubs];
if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
+ boolean anySimEmergency = mKeyguardUpdateMonitor.isAnySimEmergencyAble();
for (int i = 0; i < numSubs; i++) {
int subId = subs.get(i).getSubscriptionId();
carrierNames[i] = "";
subsIds[i] = subId;
subOrderBySlot[subs.get(i).getSimSlotIndex()] = i;
IccCardConstants.State simState = mKeyguardUpdateMonitor.getSimState(subId);
+ ServiceState s = mKeyguardUpdateMonitor.getServiceState(subId);
CharSequence carrierName = subs.get(i).getCarrierName();
+ // If this sub is showing No service but at least one slot currently supports emergency
+ // calls, it should replace it by Emergency calls only
+ if (s != null && s.getState() != ServiceState.STATE_IN_SERVICE && !s.isEmergencyOnly()
+ && anySimEmergency) {
+ carrierName = getContext().getText(
+ com.android.internal.R.string.emergency_calls_only);
+ if (DEBUG) {
+ Log.d(TAG, "Subscription " + subId + "switched to ECO");
+ }
+ }
CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
if (DEBUG) {
Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName);
carrierNames[i] = carrierTextForSimState;
}
if (simState == IccCardConstants.State.READY) {
- ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
- if (ss != null && ss.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
+ if (s != null && s.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
// hack for WFC (IWLAN) not turning off immediately once
// Wi-Fi is disassociated or disabled
- if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+ if (s.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
|| (mWifiManager.isWifiEnabled()
&& mWifiManager.getConnectionInfo() != null
&& mWifiManager.getConnectionInfo().getBSSID() != null)) {
if (DEBUG) {
- Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss);
+ Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + s);
}
anySimReadyAndInService = true;
}
HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
HashMap<Integer, ServiceState> mServiceStates = new HashMap<Integer, ServiceState>();
+ /**
+ * Support up to 3 slots which is what's supported by {@link TelephonyManager#getPhoneCount}
+ */
+ private static final int SIM_SLOTS = 3;
+ private final ServiceState[] mServiceStatesBySlot = new ServiceState[SIM_SLOTS];
+
private int mRingMode;
private int mPhoneState;
private boolean mKeyguardIsVisible;
handleAirplaneModeChanged();
break;
case MSG_SERVICE_STATE_CHANGE:
- handleServiceStateChange(msg.arg1, (ServiceState) msg.obj);
+ handleServiceStateChange(msg.arg1, msg.arg2, (ServiceState) msg.obj);
break;
case MSG_SCREEN_TURNED_ON:
handleScreenTurnedOn();
ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ int slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY, -1);
if (DEBUG) {
Log.v(TAG, "action " + action + " serviceState=" + serviceState + " subId="
+ subId);
}
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
+ mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, slotId, serviceState)
+ .sendToTarget();
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
action)) {
mHandler.sendEmptyMessage(MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED);
*/
@VisibleForTesting
void handleServiceStateChange(int subId, ServiceState serviceState) {
+ handleServiceStateChange(subId, -1, serviceState);
+ }
+
+ /**
+ * Handle {@link #MSG_SERVICE_STATE_CHANGE}
+ */
+ @VisibleForTesting
+ void handleServiceStateChange(int subId, int slotId, ServiceState serviceState) {
if (DEBUG) {
Log.d(TAG,
"handleServiceStateChange(subId=" + subId + ", serviceState=" + serviceState);
}
mServiceStates.put(subId, serviceState);
+ if (slotId >= 0 && slotId < SIM_SLOTS) mServiceStatesBySlot[slotId] = serviceState;
for (int j = 0; j < mCallbacks.size(); j++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
return mServiceStates.get(subId);
}
+ /**
+ * @return true iff at least one slot currently supports emergency calls
+ */
+ public boolean isAnySimEmergencyAble() {
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ ServiceState s = mServiceStatesBySlot[i];
+ if (s != null) {
+ if (s.getState() == ServiceState.STATE_IN_SERVICE || s.isEmergencyOnly()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public void clearBiometricRecognized() {
mUserFingerprintAuthenticated.clear();
mUserFaceAuthenticated.clear();
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.R;
import com.android.internal.telephony.IccCardConstants;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
@SmallTest
private static final String TEST_CARRIER = "TEST_CARRIER";
private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
+ private static final String EMERGENCY = "Emergency";
private static final int TEST_CARRIER_ID = 1;
private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0,
TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager);
mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
mContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
+ mContext.getOrCreateTestableResources().addOverride(
+ R.string.emergency_calls_only, EMERGENCY);
mDependency.injectMockDependency(WakefulnessLifecycle.class);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
new Handler(mTestableLooper.getLooper()));
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
- mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
-
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
- mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
-
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
- mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
-
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
.thenReturn(IccCardConstants.State.NOT_READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
- mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
-
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
.thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
- mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
-
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
CarrierTextController.CarrierTextCallbackInfo.class);
.thenReturn(IccCardConstants.State.NOT_READY)
.thenReturn(IccCardConstants.State.READY);
when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
- mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
ArgumentCaptor.forClass(
when(mKeyguardUpdateMonitor.getSimState(anyInt()))
.thenReturn(IccCardConstants.State.READY);
- mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(true);
when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
assertEquals(TEST_CARRIER_2, captor.getValue().carrierText);
}
+ @Test
+ public void testCarrierText_replaceOutOfServiceWithEmergency() {
+ reset(mCarrierTextCallback);
+
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ when(mKeyguardUpdateMonitor.getSimState(anyInt()))
+ .thenReturn(IccCardConstants.State.READY);
+ ServiceState s = mock(ServiceState.class);
+ when(mKeyguardUpdateMonitor.getServiceState(anyInt())).thenReturn(s);
+ when(s.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+
+ when(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).thenReturn(true);
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertEquals(1, captor.getValue().listOfCarriers.length);
+ assertEquals(EMERGENCY, captor.getValue().listOfCarriers[0]);
+ }
+
+ @Test
+ public void testCarrierText_replaceOutOfServiceWithEmergencyOnlyInNoService() {
+ reset(mCarrierTextCallback);
+
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ list.add(TEST_SUBSCRIPTION_2);
+ when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ when(mKeyguardUpdateMonitor.getSimState(anyInt()))
+ .thenReturn(IccCardConstants.State.READY);
+ ServiceState sInService = mock(ServiceState.class);
+ ServiceState sOutOfService = mock(ServiceState.class);
+ when(mKeyguardUpdateMonitor.getServiceState(anyInt()))
+ .thenReturn(sInService)
+ .thenReturn(sOutOfService);
+ when(sInService.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ when(sOutOfService.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+
+ when(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).thenReturn(true);
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertEquals(2, captor.getValue().listOfCarriers.length);
+ assertEquals(TEST_CARRIER, captor.getValue().listOfCarriers[0]);
+ assertEquals(EMERGENCY, captor.getValue().listOfCarriers[1]);
+ }
+
+ @Test
+ public void testCarrierText_dontReplaceWithEmergencyIfNotAble() {
+ reset(mCarrierTextCallback);
+
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ list.add(TEST_SUBSCRIPTION_2);
+ when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ when(mKeyguardUpdateMonitor.getSimState(anyInt()))
+ .thenReturn(IccCardConstants.State.READY);
+ ServiceState sOutOfService = mock(ServiceState.class);
+ when(mKeyguardUpdateMonitor.getServiceState(anyInt())).thenReturn(sOutOfService);
+ when(sOutOfService.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+
+ when(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).thenReturn(false);
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertEquals(2, captor.getValue().listOfCarriers.length);
+ assertEquals(TEST_CARRIER, captor.getValue().listOfCarriers[0]);
+ assertEquals(TEST_CARRIER_2, captor.getValue().listOfCarriers[1]);
+ }
+
+ @Test
+ public void testCarrierText_dontReplaceWithEmergencyIfAlreadyEmergency() {
+ reset(mCarrierTextCallback);
+
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION);
+ when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+
+ when(mKeyguardUpdateMonitor.getSimState(anyInt()))
+ .thenReturn(IccCardConstants.State.READY);
+ ServiceState sOutOfService = mock(ServiceState.class);
+ when(mKeyguardUpdateMonitor.getServiceState(anyInt())).thenReturn(sOutOfService);
+ when(sOutOfService.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(sOutOfService.isEmergencyOnly()).thenReturn(true);
+
+ when(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).thenReturn(false);
+
+ ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
+ ArgumentCaptor.forClass(
+ CarrierTextController.CarrierTextCallbackInfo.class);
+
+ mCarrierTextController.updateCarrierText();
+ mTestableLooper.processAllMessages();
+ verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+
+ assertEquals(1, captor.getValue().listOfCarriers.length);
+ assertEquals(TEST_CARRIER, captor.getValue().listOfCarriers[0]);
+ }
+
public static class TestCarrierTextController extends CarrierTextController {
private KeyguardUpdateMonitor mKUM;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
+ @Test
+ public void testAnySimEmergency_allSimsInService() {
+ ServiceState s0 = mock(ServiceState.class);
+ when(s0.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+
+ mKeyguardUpdateMonitor.handleServiceStateChange(0, 0, s0);
+ assertThat(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).isTrue();
+ }
+
+ @Test
+ public void testAnySimEmergency_someSimsInServiceOthersNotECC() {
+ ServiceState s0 = mock(ServiceState.class);
+ when(s0.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ ServiceState s1 = mock(ServiceState.class);
+ when(s1.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+
+ mKeyguardUpdateMonitor.handleServiceStateChange(0, 0, s0);
+ mKeyguardUpdateMonitor.handleServiceStateChange(0, 1, s1);
+ assertThat(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).isTrue();
+ }
+
+ @Test
+ public void testAnySimEmergency_someSimsEmergencyCapable() {
+ ServiceState s0 = mock(ServiceState.class);
+ when(s0.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
+ ServiceState s1 = mock(ServiceState.class);
+ when(s1.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(s1.isEmergencyOnly()).thenReturn(true);
+
+ mKeyguardUpdateMonitor.handleServiceStateChange(0, 0, s0);
+ mKeyguardUpdateMonitor.handleServiceStateChange(0, 1, s1);
+ assertThat(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).isTrue();
+ }
+
+ @Test
+ public void testAnySimEmergency_noEmergencyCapable() {
+ ServiceState s0 = mock(ServiceState.class);
+ when(s0.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
+ ServiceState s1 = mock(ServiceState.class);
+ when(s1.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+
+ mKeyguardUpdateMonitor.handleServiceStateChange(0, 0, s0);
+ mKeyguardUpdateMonitor.handleServiceStateChange(0, 1, s1);
+ assertThat(mKeyguardUpdateMonitor.isAnySimEmergencyAble()).isFalse();
+ }
+
private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
int subscription = simInited
? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;