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

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

Fix ArrayIndexOOB crash with invalid sim slot

Added guards for multiple ArrayIndexOOB Exceptions.

CarrierTextController needs to be refactored to show proper names and
not need split

Test: atest
Bug: 123753387
Change-Id: I55fa40ec26d91da0379440b010c20b61a0941f66
parent 1ec11dbd
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -21,12 +21,15 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.settingslib.WirelessUtils;
@@ -206,6 +209,7 @@ public class CarrierTextController {
    protected void updateCarrierText() {
        boolean allSimsMissing = true;
        boolean anySimReadyAndInService = false;
        boolean missingSimsWithSubs = false;
        CharSequence displayText = null;

        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
@@ -252,6 +256,7 @@ 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
@@ -288,12 +293,14 @@ public class CarrierTextController {
            displayText = getAirplaneModeMessage();
        }

        if (mCarrierTextCallback != null) {
            mCarrierTextCallback.updateCarrierInfo(new CarrierTextCallbackInfo(
        Handler handler = Dependency.get(Dependency.MAIN_HANDLER);
        final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
                displayText,
                displayText.toString().split(mSeparator.toString()),
                    anySimReadyAndInService,
                    subsIds));
                anySimReadyAndInService && !missingSimsWithSubs,
                subsIds);
        if (mCarrierTextCallback != null) {
            handler.post(() -> mCarrierTextCallback.updateCarrierInfo(info));
        }

    }
@@ -487,7 +494,8 @@ public class CarrierTextController {
        public final boolean anySimReady;
        public final int[] subscriptionIds;

        CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
        @VisibleForTesting
        public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
                boolean anySimReady, int[] subscriptionIds) {
            this.carrierText = carrierText;
            this.listOfCarriers = listOfCarriers;
+60 −16
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.R.dimen;
import com.android.systemui.plugins.ActivityStarter;
@@ -134,6 +135,15 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
        mDeviceProvisionedController = deviceProvisionedController;
    }

    @VisibleForTesting
    public QSFooterImpl(Context context, AttributeSet attrs) {
        this(context, attrs,
                Dependency.get(ActivityStarter.class),
                Dependency.get(UserInfoController.class),
                Dependency.get(NetworkController.class),
                Dependency.get(DeviceProvisionedController.class));
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
@@ -476,12 +486,28 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
                mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
    }

    @VisibleForTesting
    protected int getSlotIndex(int subscriptionId) {
        return SubscriptionManager.getSlotIndex(subscriptionId);
    }

    @Override
    public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
        if (info.anySimReady) {
            boolean[] slotSeen = new boolean[SIM_SLOTS];
            if (info.listOfCarriers.length == info.subscriptionIds.length) {
                for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
                int slot = SubscriptionManager.getSlotIndex(info.subscriptionIds[i]);
                    int slot = getSlotIndex(info.subscriptionIds[i]);
                    if (slot >= SIM_SLOTS) {
                        Log.w(TAG, "updateInfoCarrier - slot: " + slot);
                        continue;
                    }
                    if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
                        Log.e(TAG,
                                "Invalid SIM slot index for subscription: "
                                        + info.subscriptionIds[i]);
                        continue;
                    }
                    mInfos[slot].visible = true;
                    slotSeen[slot] = true;
                    mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim());
@@ -493,16 +519,30 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
                        mCarrierGroups[i].setVisibility(View.GONE);
                    }
                }
            handleUpdateState();
            } 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);
                }
            }
        } else {
            mInfos[0].visible = false;
            mInfos[1].visible = false;
            mCarrierTexts[0].setText(info.carrierText);
            mCarrierGroups[0].setVisibility(View.VISIBLE);
            mCarrierGroups[1].setVisibility(View.GONE);
            handleUpdateState();
            for (int i = 1; i < SIM_SLOTS; i++) {
                mInfos[i].visible = false;
                mCarrierTexts[i].setText("");
                mCarrierGroups[i].setVisibility(View.GONE);
            }
        }
        handleUpdateState();
    }

    @Override
    public void setMobileDataIndicators(NetworkController.IconState statusIcon,
@@ -510,9 +550,14 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
            int qsType, boolean activityIn, boolean activityOut,
            String typeContentDescription,
            String description, boolean isWide, int subId, boolean roaming) {
        int slotIndex = SubscriptionManager.getSlotIndex(subId);
        int slotIndex = getSlotIndex(subId);
        if (slotIndex >= SIM_SLOTS) {
            Log.e(TAG, "setMobileDataIndicators - slot: " + slotIndex);
            Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
            return;
        }
        if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
            Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
            return;
        }
        mInfos[slotIndex].visible = statusIcon.visible;
        mInfos[slotIndex].mobileSignalIconId = statusIcon.icon;
@@ -539,7 +584,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
        boolean roaming;
    }


    /**
     * TextView that changes its ellipsize value with its visibility.
     */
+121 −0
Original line number Diff line number Diff line
@@ -16,27 +16,35 @@ package com.android.systemui.qs;

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.verify;
import static org.mockito.Mockito.when;

import android.support.test.filters.SmallTest;
import android.telephony.SubscriptionManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
import android.view.View;

import com.android.keyguard.CarrierTextController.CarrierTextCallbackInfo;
import com.android.systemui.R;
import com.android.systemui.R.id;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.utils.leaks.LeakCheckedTest;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -68,4 +76,117 @@ public class QSFooterImplTest extends LeakCheckedTest {
        // Verify Settings wasn't launched.
        verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
    }

    @Test // throws no Exception
    public void testUpdateCarrierText_sameLengts() {
        QSFooterImpl spiedFooter = Mockito.spy(mFooter);
        when(spiedFooter.getSlotIndex(anyInt())).thenAnswer(
                new Answer<Integer>() {
                    @Override
                    public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
                        return invocationOnMock.getArgument(0);
                    }
                });

        // listOfCarriers length 1, subscriptionIds length 1, anySims false
        CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{""},
                false,
                new int[]{0});
        spiedFooter.updateCarrierInfo(c1);

        // listOfCarriers length 1, subscriptionIds length 1, anySims true
        CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{""},
                true,
                new int[]{0});
        spiedFooter.updateCarrierInfo(c2);

        // listOfCarriers length 2, subscriptionIds length 2, anySims false
        CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{"", ""},
                false,
                new int[]{0, 1});
        spiedFooter.updateCarrierInfo(c3);

        // listOfCarriers length 2, subscriptionIds length 2, anySims true
        CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{"", ""},
                true,
                new int[]{0, 1});
        spiedFooter.updateCarrierInfo(c4);
    }

    @Test // throws no Exception
    public void testUpdateCarrierText_differentLength() {
        QSFooterImpl spiedFooter = Mockito.spy(mFooter);
        when(spiedFooter.getSlotIndex(anyInt())).thenAnswer(
                new Answer<Integer>() {
                    @Override
                    public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
                        return invocationOnMock.getArgument(0);
                    }
                });

        // listOfCarriers length 2, subscriptionIds length 1, anySims false
        CarrierTextCallbackInfo c1 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{"", ""},
                false,
                new int[]{0});
        spiedFooter.updateCarrierInfo(c1);

        // listOfCarriers length 2, subscriptionIds length 1, anySims true
        CarrierTextCallbackInfo c2 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{"", ""},
                true,
                new int[]{0});
        spiedFooter.updateCarrierInfo(c2);

        // listOfCarriers length 1, subscriptionIds length 2, anySims false
        CarrierTextCallbackInfo c3 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{""},
                false,
                new int[]{0, 1});
        spiedFooter.updateCarrierInfo(c3);

        // listOfCarriers length 1, subscriptionIds length 2, anySims true
        CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{""},
                true,
                new int[]{0, 1});
        spiedFooter.updateCarrierInfo(c4);
    }

    @Test // throws no Exception
    public void testUpdateCarrierText_invalidSim() {
        QSFooterImpl spiedFooter = Mockito.spy(mFooter);
        when(spiedFooter.getSlotIndex(anyInt())).thenReturn(
                SubscriptionManager.INVALID_SIM_SLOT_INDEX);
        CarrierTextCallbackInfo c4 = new CarrierTextCallbackInfo(
                "",
                new CharSequence[]{"", ""},
                true,
                new int[]{0, 1});
        spiedFooter.updateCarrierInfo(c4);
    }

    @Test // throws no Exception
    public void testSetMobileDataIndicators_invalidSim() {
        QSFooterImpl spiedFooter = Mockito.spy(mFooter);
        when(spiedFooter.getSlotIndex(anyInt())).thenReturn(
                SubscriptionManager.INVALID_SIM_SLOT_INDEX);
        spiedFooter.setMobileDataIndicators(
                mock(NetworkController.IconState.class),
                mock(NetworkController.IconState.class),
                0, 0, true, true, "", "", true, 0, true);
    }

}