Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b176f420 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Refactor CarrierTextController

The list of carriers is now created on the go, so it will always have
the same number of items as there are subscriptions.

Fixes code that reused same slotID into carrierName order.

Test: atest
Test: manual using DSDS

Change-Id: I2533738429b8f0f94c84110641e8c162d19f094a
parent a9424139
Loading
Loading
Loading
Loading
+59 −28
Original line number Original line Diff line number Diff line
@@ -52,9 +52,11 @@ public class CarrierTextController {
    private boolean mTelephonyCapable;
    private boolean mTelephonyCapable;
    private boolean mShowMissingSim;
    private boolean mShowMissingSim;
    private boolean mShowAirplaneMode;
    private boolean mShowAirplaneMode;
    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    @VisibleForTesting
    protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    private WifiManager mWifiManager;
    private WifiManager mWifiManager;
    private boolean[] mSimErrorState = new boolean[TelephonyManager.getDefault().getPhoneCount()];
    private boolean[] mSimErrorState;
    private final int mSimSlotsNumber;
    private CarrierTextCallback mCarrierTextCallback;
    private CarrierTextCallback mCarrierTextCallback;
    private Context mContext;
    private Context mContext;
    private CharSequence mSeparator;
    private CharSequence mSeparator;
@@ -72,7 +74,8 @@ public class CarrierTextController {
                }
                }
            };
            };


    private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
    @VisibleForTesting
    protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
        @Override
        @Override
        public void onRefreshCarrierInfo() {
        public void onRefreshCarrierInfo() {
            if (DEBUG) {
            if (DEBUG) {
@@ -93,7 +96,7 @@ public class CarrierTextController {
        }
        }


        public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) {
        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
                Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId
                        + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
                        + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
                return;
                return;
@@ -144,21 +147,30 @@ public class CarrierTextController {
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        mSeparator = separator;
        mSeparator = separator;
        mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
        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
     * 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 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
     * @param noSims: whether a valid sim card is inserted
     * @return text
     * @return text
     */
     */
    private CharSequence updateCarrierTextWithSimIoError(CharSequence text, boolean noSims) {
    private CharSequence updateCarrierTextWithSimIoError(CharSequence text,
            CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) {
        final CharSequence carrier = "";
        final CharSequence carrier = "";
        CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
        CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
                IccCardConstants.State.CARD_IO_ERROR, carrier);
                IccCardConstants.State.CARD_IO_ERROR, carrier);
        // mSimErrorState has the state of each sim indexed by slotID.
        for (int index = 0; index < mSimErrorState.length; index++) {
        for (int index = 0; index < mSimErrorState.length; index++) {
            if (mSimErrorState[index]) {
            if (!mSimErrorState[index]) {
                continue;
            }
            // In the case when no sim cards are detected but a faulty card is inserted
            // In the case when no sim cards are detected but a faulty card is inserted
            // overwrite the text and only show "Invalid card"
            // overwrite the text and only show "Invalid card"
            if (noSims) {
            if (noSims) {
@@ -166,14 +178,17 @@ public class CarrierTextController {
                        getContext().getText(
                        getContext().getText(
                                com.android.internal.R.string.emergency_calls_only),
                                com.android.internal.R.string.emergency_calls_only),
                        mSeparator);
                        mSeparator);
                } else if (index == 0) {
            } else if (subOrderBySlot[index] != -1) {
                    // prepend "Invalid card" when faulty card is inserted in slot 0
                int subIndex = subOrderBySlot[index];
                    text = concatenate(carrierTextForSimIOError, text, mSeparator);
                // prepend "Invalid card" when faulty card is inserted in slot 0 or 1
                carrierNames[subIndex] = concatenate(carrierTextForSimIOError,
                        carrierNames[subIndex],
                        mSeparator);
            } else {
            } else {
                    // concatenate "Invalid card" when faulty card is inserted in slot 1
                // concatenate "Invalid card" when faulty card is inserted in other slot
                text = concatenate(text, carrierTextForSimIOError, mSeparator);
                text = concatenate(text, carrierTextForSimIOError, mSeparator);
            }
            }
            }

        }
        }
        return text;
        return text;
    }
    }
@@ -209,16 +224,25 @@ public class CarrierTextController {
    protected void updateCarrierText() {
    protected void updateCarrierText() {
        boolean allSimsMissing = true;
        boolean allSimsMissing = true;
        boolean anySimReadyAndInService = false;
        boolean anySimReadyAndInService = false;
        boolean missingSimsWithSubs = false;
        CharSequence displayText = null;
        CharSequence displayText = null;


        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
        final int numSubs = subs.size();
        final int numSubs = subs.size();
        final int[] subsIds = new int[numSubs];
        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);
        if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);

        for (int i = 0; i < numSubs; i++) {
        for (int i = 0; i < numSubs; i++) {
            int subId = subs.get(i).getSubscriptionId();
            int subId = subs.get(i).getSubscriptionId();
            carrierNames[i] = "";
            subsIds[i] = subId;
            subsIds[i] = subId;
            subOrderBySlot[subs.get(i).getSimSlotIndex()] = i;
            IccCardConstants.State simState = mKeyguardUpdateMonitor.getSimState(subId);
            IccCardConstants.State simState = mKeyguardUpdateMonitor.getSimState(subId);
            CharSequence carrierName = subs.get(i).getCarrierName();
            CharSequence carrierName = subs.get(i).getCarrierName();
            CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
            CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
@@ -227,7 +251,7 @@ public class CarrierTextController {
            }
            }
            if (carrierTextForSimState != null) {
            if (carrierTextForSimState != null) {
                allSimsMissing = false;
                allSimsMissing = false;
                displayText = concatenate(displayText, carrierTextForSimState, mSeparator);
                carrierNames[i] = carrierTextForSimState;
            }
            }
            if (simState == IccCardConstants.State.READY) {
            if (simState == IccCardConstants.State.READY) {
                ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
                ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
@@ -256,7 +280,6 @@ public class CarrierTextController {
                // described above.
                // described above.
                displayText = makeCarrierStringOnEmergencyCapable(
                displayText = makeCarrierStringOnEmergencyCapable(
                        getMissingSimMessage(), subs.get(0).getCarrierName());
                        getMissingSimMessage(), subs.get(0).getCarrierName());
                missingSimsWithSubs = true;
            } else {
            } else {
                // We don't have a SubscriptionInfo to get the emergency calls only from.
                // 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
                // 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
        // APM (airplane mode) != no carrier state. There are carrier services
        // (e.g. WFC = Wi-Fi calling) which may operate in APM.
        // (e.g. WFC = Wi-Fi calling) which may operate in APM.
        if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
        if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
            displayText = getAirplaneModeMessage();
            displayText = getAirplaneModeMessage();
        }
        }


        Handler handler = Dependency.get(Dependency.MAIN_HANDLER);
        if (TextUtils.isEmpty(displayText)) {
            displayText = TextUtils.join(mSeparator, carrierNames);
        }
        final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
        final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
                displayText,
                displayText,
                displayText.toString().split(mSeparator.toString()),
                carrierNames,
                anySimReadyAndInService && !missingSimsWithSubs,
                !allSimsMissing,
                subsIds);
                subsIds);
        postToCallback(info);
    }

    @VisibleForTesting
    protected void postToCallback(CarrierTextCallbackInfo info) {
        Handler handler = Dependency.get(Dependency.MAIN_HANDLER);
        final CarrierTextCallback callback = mCarrierTextCallback;
        final CarrierTextCallback callback = mCarrierTextCallback;
        if (callback != null) {
        if (callback != null) {
            handler.post(() -> callback.updateCarrierInfo(info));
            handler.post(() -> callback.updateCarrierInfo(info));
        }
        }

    }
    }


    private Context getContext() {
    private Context getContext() {
+3 −10
Original line number Original line Diff line number Diff line
@@ -521,16 +521,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
                    }
                    }
                }
                }
            } else {
            } else {
                // If there are sims ready but there are not the same number of carrier names as
                Log.e(TAG, "Carrier information arrays not of same length");
                // 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);
                }
            }
            }
        } else {
        } else {
            mInfos[0].visible = false;
            mInfos[0].visible = false;
@@ -612,8 +603,10 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
            // Only show marquee when visible
            // Only show marquee when visible
            if (visibility == VISIBLE) {
            if (visibility == VISIBLE) {
                setEllipsize(TextUtils.TruncateAt.MARQUEE);
                setEllipsize(TextUtils.TruncateAt.MARQUEE);
                setSelected(true);
            } else {
            } else {
                setEllipsize(TextUtils.TruncateAt.END);
                setEllipsize(TextUtils.TruncateAt.END);
                setSelected(false);
            }
            }
        }
        }
    }
    }
+245 −0
Original line number Original line Diff line number Diff line
/*
 * 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;
        }
    }
}