Merge "Refactor CarrierTextController"

This commit is contained in:
Fabian Kozynski
2019-03-01 17:53:43 +00:00
committed by Android (Google) Code Review
3 changed files with 307 additions and 38 deletions

View File

@@ -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() {

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}