diff options
3 files changed, 307 insertions, 38 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index 1539582ef18b..64517bae6d6b 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -52,9 +52,11 @@ public class CarrierTextController { private boolean mTelephonyCapable; private boolean mShowMissingSim; private boolean mShowAirplaneMode; - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @VisibleForTesting + protected KeyguardUpdateMonitor mKeyguardUpdateMonitor; private WifiManager mWifiManager; - private boolean[] mSimErrorState = new boolean[TelephonyManager.getDefault().getPhoneCount()]; + private boolean[] mSimErrorState; + private final int mSimSlotsNumber; private CarrierTextCallback mCarrierTextCallback; private Context mContext; private CharSequence mSeparator; @@ -72,7 +74,8 @@ public class CarrierTextController { } }; - private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { + @VisibleForTesting + protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { @Override public void onRefreshCarrierInfo() { if (DEBUG) { @@ -93,7 +96,7 @@ public class CarrierTextController { } public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) { - if (slotId < 0) { + if (slotId < 0 || slotId >= mSimSlotsNumber) { Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable)); return; @@ -144,36 +147,48 @@ public class CarrierTextController { mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mSeparator = separator; mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); + mSimSlotsNumber = ((TelephonyManager) context.getSystemService( + Context.TELEPHONY_SERVICE)).getPhoneCount(); + mSimErrorState = new boolean[mSimSlotsNumber]; } /** * Checks if there are faulty cards. Adds the text depending on the slot of the card * * @param text: current carrier text based on the sim state + * @param carrierNames names order by subscription order + * @param subOrderBySlot array containing the sub index for each slot ID * @param noSims: whether a valid sim card is inserted * @return text */ - private CharSequence updateCarrierTextWithSimIoError(CharSequence text, boolean noSims) { + private CharSequence updateCarrierTextWithSimIoError(CharSequence text, + CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) { final CharSequence carrier = ""; CharSequence carrierTextForSimIOError = getCarrierTextForSimState( IccCardConstants.State.CARD_IO_ERROR, carrier); + // mSimErrorState has the state of each sim indexed by slotID. for (int index = 0; index < mSimErrorState.length; index++) { - if (mSimErrorState[index]) { - // In the case when no sim cards are detected but a faulty card is inserted - // overwrite the text and only show "Invalid card" - if (noSims) { - return concatenate(carrierTextForSimIOError, - getContext().getText( - com.android.internal.R.string.emergency_calls_only), - mSeparator); - } else if (index == 0) { - // prepend "Invalid card" when faulty card is inserted in slot 0 - text = concatenate(carrierTextForSimIOError, text, mSeparator); - } else { - // concatenate "Invalid card" when faulty card is inserted in slot 1 - text = concatenate(text, carrierTextForSimIOError, mSeparator); - } + if (!mSimErrorState[index]) { + continue; + } + // In the case when no sim cards are detected but a faulty card is inserted + // overwrite the text and only show "Invalid card" + if (noSims) { + return concatenate(carrierTextForSimIOError, + getContext().getText( + com.android.internal.R.string.emergency_calls_only), + mSeparator); + } else if (subOrderBySlot[index] != -1) { + int subIndex = subOrderBySlot[index]; + // prepend "Invalid card" when faulty card is inserted in slot 0 or 1 + carrierNames[subIndex] = concatenate(carrierTextForSimIOError, + carrierNames[subIndex], + mSeparator); + } else { + // concatenate "Invalid card" when faulty card is inserted in other slot + text = concatenate(text, carrierTextForSimIOError, mSeparator); } + } return text; } @@ -209,16 +224,25 @@ public class CarrierTextController { protected void updateCarrierText() { boolean allSimsMissing = true; boolean anySimReadyAndInService = false; - boolean missingSimsWithSubs = false; CharSequence displayText = null; List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); final int numSubs = subs.size(); final int[] subsIds = new int[numSubs]; + // This array will contain in position i, the index of subscription in slot ID i. + // -1 if no subscription in that slot + final int[] subOrderBySlot = new int[mSimSlotsNumber]; + for (int i = 0; i < mSimSlotsNumber; i++) { + subOrderBySlot[i] = -1; + } + final CharSequence[] carrierNames = new CharSequence[numSubs]; if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs); + 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); CharSequence carrierName = subs.get(i).getCarrierName(); CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName); @@ -227,7 +251,7 @@ public class CarrierTextController { } if (carrierTextForSimState != null) { allSimsMissing = false; - displayText = concatenate(displayText, carrierTextForSimState, mSeparator); + carrierNames[i] = carrierTextForSimState; } if (simState == IccCardConstants.State.READY) { ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId); @@ -256,7 +280,6 @@ public class CarrierTextController { // described above. displayText = makeCarrierStringOnEmergencyCapable( getMissingSimMessage(), subs.get(0).getCarrierName()); - missingSimsWithSubs = true; } else { // We don't have a SubscriptionInfo to get the emergency calls only from. // Grab it from the old sticky broadcast if possible instead. We can use it @@ -286,24 +309,32 @@ public class CarrierTextController { } } - displayText = updateCarrierTextWithSimIoError(displayText, allSimsMissing); + displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot, + allSimsMissing); // APM (airplane mode) != no carrier state. There are carrier services // (e.g. WFC = Wi-Fi calling) which may operate in APM. if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) { displayText = getAirplaneModeMessage(); } - Handler handler = Dependency.get(Dependency.MAIN_HANDLER); + if (TextUtils.isEmpty(displayText)) { + displayText = TextUtils.join(mSeparator, carrierNames); + } final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( displayText, - displayText.toString().split(mSeparator.toString()), - anySimReadyAndInService && !missingSimsWithSubs, + carrierNames, + !allSimsMissing, subsIds); + postToCallback(info); + } + + @VisibleForTesting + protected void postToCallback(CarrierTextCallbackInfo info) { + Handler handler = Dependency.get(Dependency.MAIN_HANDLER); final CarrierTextCallback callback = mCarrierTextCallback; if (callback != null) { handler.post(() -> callback.updateCarrierInfo(info)); } - } private Context getContext() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index f91c9d944439..bbd8de333d79 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -521,16 +521,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, } } } else { - // If there are sims ready but there are not the same number of carrier names as - // subscription ids, just show the full text in the first slot - mInfos[0].visible = true; - mCarrierTexts[0].setText(info.carrierText); - mCarrierGroups[0].setVisibility(View.VISIBLE); - for (int i = 1; i < SIM_SLOTS; i++) { - mInfos[i].visible = false; - mCarrierTexts[i].setText(""); - mCarrierGroups[i].setVisibility(View.GONE); - } + Log.e(TAG, "Carrier information arrays not of same length"); } } else { mInfos[0].visible = false; @@ -612,8 +603,10 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, // Only show marquee when visible if (visibility == VISIBLE) { setEllipsize(TextUtils.TruncateAt.MARQUEE); + setSelected(true); } else { setEllipsize(TextUtils.TruncateAt.END); + setSelected(false); } } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java new file mode 100644 index 000000000000..8e061cc84396 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2019 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.keyguard; + + +import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; +import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE; +import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE; + +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.telephony.SubscriptionInfo; +import android.telephony.TelephonyManager; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.internal.telephony.IccCardConstants; +import com.android.systemui.Dependency; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.WakefulnessLifecycle; + +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.ArrayList; +import java.util.HashMap; +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class CarrierTextControllerTest extends SysuiTestCase { + + private static final CharSequence SEPARATOR = " \u2014 "; + private static final String TEST_CARRIER = "TEST_CARRIER"; + private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0, + TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", + DATA_ROAMING_DISABLE, null, null, null, null, false, null, ""); + private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0, + TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", + DATA_ROAMING_ENABLE, null, null, null, null, false, null, ""); + @Mock + private WifiManager mWifiManager; + @Mock + private CarrierTextController.CarrierTextCallback mCarrierTextCallback; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private ConnectivityManager mConnectivityManager; + @Mock + private TelephonyManager mTelephonyManager; + private CarrierTextController.CarrierTextCallbackInfo mCarrierTextCallbackInfo; + + private CarrierTextController mCarrierTextController; + private TestableLooper mTestableLooper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + + mContext.addMockSystemService(WifiManager.class, mWifiManager); + mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager); + mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager); + mDependency.injectMockDependency(WakefulnessLifecycle.class); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + new Handler(mTestableLooper.getLooper())); + + mCarrierTextCallbackInfo = new CarrierTextController.CarrierTextCallbackInfo("", + new CharSequence[]{}, false, new int[]{}); + when(mTelephonyManager.getPhoneCount()).thenReturn(2); + mCarrierTextController = new TestCarrierTextController(mContext, SEPARATOR, true, true, + mKeyguardUpdateMonitor); + // This should not start listening on any of the real dependencies + mCarrierTextController.setListening(mCarrierTextCallback); + } + + @Test + public void testWrongSlots() { + reset(mCarrierTextCallback); + when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn( + new ArrayList<>()); + when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( + IccCardConstants.State.CARD_IO_ERROR); + // This should not produce an out of bounds error, even though there are no subscriptions + mCarrierTextController.mCallback.onSimStateChanged(0, -3, + IccCardConstants.State.CARD_IO_ERROR); + mCarrierTextController.mCallback.onSimStateChanged(0, 3, IccCardConstants.State.READY); + verify(mCarrierTextCallback, never()).updateCarrierInfo(any()); + } + + @Test + public void testMoreSlotsThanSubs() { + reset(mCarrierTextCallback); + when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn( + new ArrayList<>()); + when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( + IccCardConstants.State.CARD_IO_ERROR); + // This should not produce an out of bounds error, even though there are no subscriptions + mCarrierTextController.mCallback.onSimStateChanged(0, 1, + IccCardConstants.State.CARD_IO_ERROR); + + mTestableLooper.processAllMessages(); + verify(mCarrierTextCallback).updateCarrierInfo( + any(CarrierTextController.CarrierTextCallbackInfo.class)); + } + + @Test + public void testCallback() { + reset(mCarrierTextCallback); + mCarrierTextController.postToCallback(mCarrierTextCallbackInfo); + mTestableLooper.processAllMessages(); + + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextController.CarrierTextCallbackInfo.class); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertEquals(mCarrierTextCallbackInfo, captor.getValue()); + } + + @Test + public void testNullingCallback() { + reset(mCarrierTextCallback); + + mCarrierTextController.postToCallback(mCarrierTextCallbackInfo); + mCarrierTextController.setListening(null); + + // This shouldn't produce NPE + mTestableLooper.processAllMessages(); + verify(mCarrierTextCallback).updateCarrierInfo(any()); + } + + @Test + public void testCreateInfo_OneValidSubscription() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + 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); + + mCarrierTextController.updateCarrierText(); + mTestableLooper.processAllMessages(); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); + assertEquals(1, info.listOfCarriers.length); + assertEquals(TEST_CARRIER, info.listOfCarriers[0]); + assertEquals(1, info.subscriptionIds.length); + } + + @Test + public void testCreateInfo_OneValidSubscriptionWithRoaming() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION_ROAMING); + 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); + + mCarrierTextController.updateCarrierText(); + mTestableLooper.processAllMessages(); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); + assertEquals(1, info.listOfCarriers.length); + assertTrue(info.listOfCarriers[0].toString().contains(TEST_CARRIER)); + assertEquals(1, info.subscriptionIds.length); + } + + @Test + public void testCreateInfo_noSubscriptions() { + reset(mCarrierTextCallback); + when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn( + new ArrayList<>()); + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextController.CarrierTextCallbackInfo.class); + + mCarrierTextController.updateCarrierText(); + mTestableLooper.processAllMessages(); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); + assertEquals(0, info.listOfCarriers.length); + assertEquals(0, info.subscriptionIds.length); + + } + + public static class TestCarrierTextController extends CarrierTextController { + private KeyguardUpdateMonitor mKUM; + + public TestCarrierTextController(Context context, CharSequence separator, + boolean showAirplaneMode, boolean showMissingSim, KeyguardUpdateMonitor kum) { + super(context, separator, showAirplaneMode, showMissingSim); + mKUM = kum; + } + + @Override + public void setListening(CarrierTextCallback callback) { + super.setListening(callback); + mKeyguardUpdateMonitor = mKUM; + } + } +} |