diff options
| author | 2019-02-04 09:21:38 -0500 | |
|---|---|---|
| committer | 2019-02-04 14:20:27 -0500 | |
| commit | bf6fef3fe6cf95d1d3fcfab4b40b9f293602ae09 (patch) | |
| tree | be146595797ba2dc384f0dad6f9c902e6643c12f | |
| parent | 1ec11dbda282e02b3f1db03a454d0e9db91a6b0e (diff) | |
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
3 files changed, 195 insertions, 22 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index 2ce69650b65c..d5bd2b202803 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -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; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index c0ed4b97eaff..b865ce8d261a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -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.       */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java index 850396299ae5..374a73c1d451 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java @@ -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); +    } +  }  |