OSDN Git Service

Fix ArrayIndexOOB crash with invalid sim slot
authorFabian Kozynski <kozynski@google.com>
Mon, 4 Feb 2019 14:21:38 +0000 (09:21 -0500)
committerFabian Kozynski <kozynski@google.com>
Mon, 4 Feb 2019 19:20:27 +0000 (14:20 -0500)
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

packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java

index 2ce6965..d5bd2b2 100644 (file)
@@ -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();
         }
 
+        Handler handler = Dependency.get(Dependency.MAIN_HANDLER);
+        final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
+                displayText,
+                displayText.toString().split(mSeparator.toString()),
+                anySimReadyAndInService && !missingSimsWithSubs,
+                subsIds);
         if (mCarrierTextCallback != null) {
-            mCarrierTextCallback.updateCarrierInfo(new CarrierTextCallbackInfo(
-                    displayText,
-                    displayText.toString().split(mSeparator.toString()),
-                    anySimReadyAndInService,
-                    subsIds));
+            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;
index c0ed4b9..b865ce8 100644 (file)
@@ -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,32 +486,62 @@ 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];
-            for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
-                int slot = SubscriptionManager.getSlotIndex(info.subscriptionIds[i]);
-                mInfos[slot].visible = true;
-                slotSeen[slot] = true;
-                mCarrierTexts[slot].setText(info.listOfCarriers[i].toString().trim());
-                mCarrierGroups[slot].setVisibility(View.VISIBLE);
-            }
-            for (int i = 0; i < SIM_SLOTS; i++) {
-                if (!slotSeen[i]) {
+            if (info.listOfCarriers.length == info.subscriptionIds.length) {
+                for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; 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());
+                    mCarrierGroups[slot].setVisibility(View.VISIBLE);
+                }
+                for (int i = 0; i < SIM_SLOTS; i++) {
+                    if (!slotSeen[i]) {
+                        mInfos[i].visible = false;
+                        mCarrierGroups[i].setVisibility(View.GONE);
+                    }
+                }
+            } 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);
                 }
             }
-            handleUpdateState();
         } 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
@@ -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.
      */
index 8503962..374a73c 100644 (file)
@@ -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);
+    }
+
 }