Enforce updates in main thread in QSCarrierGroup

Move all updates to a Handler in the Main thread

Test: atest QSCarrierGroup
Test: change display size and font in Secondary user and observe that QS
does not crash
Fixes: 141187305

Change-Id: I27c7e8076dad94878ed5a3556f2983d631840b56
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
index 55ae61d..b9f3a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
@@ -18,11 +18,16 @@
 
 import static com.android.systemui.Dependency.BG_HANDLER;
 import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+import static com.android.systemui.Dependency.MAIN_LOOPER;
+import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
+import android.annotation.MainThread;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 import android.util.AttributeSet;
@@ -39,6 +44,8 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.policy.NetworkController;
 
+import java.util.function.Consumer;
+
 import javax.inject.Inject;
 import javax.inject.Named;
 
@@ -46,7 +53,6 @@
  * Displays Carrier name and network status in QS
  */
 public class QSCarrierGroup extends LinearLayout implements
-        CarrierTextController.CarrierTextCallback,
         NetworkController.SignalCallback, View.OnClickListener {
 
     private static final String TAG = "QSCarrierGroup";
@@ -56,12 +62,14 @@
     private static final int SIM_SLOTS = 3;
     private final NetworkController mNetworkController;
     private final Handler mBgHandler;
+    private final H mMainHandler;
 
     private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
     private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
     private TextView mNoSimTextView;
     private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS];
     private CarrierTextController mCarrierTextController;
+    private CarrierTextController.CarrierTextCallback mCallback;
     private ActivityStarter mActivityStarter;
 
     private boolean mListening;
@@ -69,11 +77,19 @@
     @Inject
     public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
             NetworkController networkController, ActivityStarter activityStarter,
-            @Named(BG_HANDLER_NAME) Handler handler) {
+            @Named(BG_HANDLER_NAME) Handler handler,
+            @Named(MAIN_LOOPER_NAME) Looper looper) {
         super(context, attrs);
         mNetworkController = networkController;
         mActivityStarter = activityStarter;
         mBgHandler = handler;
+        mMainHandler = new H(looper, this::handleUpdateCarrierInfo, this::handleUpdateState);
+        mCallback = new Callback(mMainHandler);
+    }
+
+    @VisibleForTesting
+    protected CarrierTextController.CarrierTextCallback getCallback() {
+        return mCallback;
     }
 
     @VisibleForTesting
@@ -81,7 +97,8 @@
         this(context, attrs,
                 Dependency.get(NetworkController.class),
                 Dependency.get(ActivityStarter.class),
-                Dependency.get(BG_HANDLER));
+                Dependency.get(BG_HANDLER),
+                Dependency.get(MAIN_LOOPER));
     }
 
     @Override
@@ -136,14 +153,20 @@
             if (mNetworkController.hasVoiceCallingFeature()) {
                 mNetworkController.addCallback(this);
             }
-            mCarrierTextController.setListening(this);
+            mCarrierTextController.setListening(mCallback);
         } else {
             mNetworkController.removeCallback(this);
             mCarrierTextController.setListening(null);
         }
     }
 
+    @MainThread
     private void handleUpdateState() {
+        if (!mMainHandler.getLooper().isCurrentThread()) {
+            mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+            return;
+        }
+
         for (int i = 0; i < SIM_SLOTS; i++) {
             mCarrierGroups[i].updateState(mInfos[i]);
         }
@@ -163,8 +186,13 @@
         return SubscriptionManager.getSlotIndex(subscriptionId);
     }
 
-    @Override
-    public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
+    @MainThread
+    private void handleUpdateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
+        if (!mMainHandler.getLooper().isCurrentThread()) {
+            mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget();
+            return;
+        }
+
         mNoSimTextView.setVisibility(View.GONE);
         if (!info.airplaneMode && info.anySimReady) {
             boolean[] slotSeen = new boolean[SIM_SLOTS];
@@ -207,7 +235,7 @@
             mNoSimTextView.setText(info.carrierText);
             mNoSimTextView.setVisibility(View.VISIBLE);
         }
-        handleUpdateState();
+        handleUpdateState(); // handleUpdateCarrierInfo is always called from main thread.
     }
 
     @Override
@@ -230,7 +258,7 @@
         mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
         mInfos[slotIndex].typeContentDescription = typeContentDescription;
         mInfos[slotIndex].roaming = roaming;
-        handleUpdateState();
+        mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
     }
 
     @Override
@@ -240,7 +268,7 @@
                 mInfos[i].visible = false;
             }
         }
-        handleUpdateState();
+        mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
     }
 
     static final class CellSignalState {
@@ -250,4 +278,47 @@
         String typeContentDescription;
         boolean roaming;
     }
+
+    private static class H extends Handler {
+        private Consumer<CarrierTextController.CarrierTextCallbackInfo> mUpdateCarrierInfo;
+        private Runnable mUpdateState;
+        static final int MSG_UPDATE_CARRIER_INFO = 0;
+        static final int MSG_UPDATE_STATE = 1;
+
+        H(Looper looper,
+                Consumer<CarrierTextController.CarrierTextCallbackInfo> updateCarrierInfo,
+                Runnable updateState) {
+            super(looper);
+            mUpdateCarrierInfo = updateCarrierInfo;
+            mUpdateState = updateState;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_CARRIER_INFO:
+                    mUpdateCarrierInfo.accept(
+                            (CarrierTextController.CarrierTextCallbackInfo) msg.obj);
+                    break;
+                case MSG_UPDATE_STATE:
+                    mUpdateState.run();
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    private static class Callback implements CarrierTextController.CarrierTextCallback {
+        private H mMainHandler;
+
+        Callback(H handler) {
+            mMainHandler = handler;
+        }
+
+        @Override
+        public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
+            mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget();
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java
index f29392b..a2a20a95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.os.Handler;
 import android.telephony.SubscriptionManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -28,6 +29,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.CarrierTextController;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -45,13 +47,20 @@
 public class QSCarrierGroupTest extends LeakCheckedTest {
 
     private QSCarrierGroup mCarrierGroup;
+    private CarrierTextController.CarrierTextCallback mCallback;
+    private TestableLooper mTestableLooper;
 
     @Before
     public void setup() throws Exception {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
-        TestableLooper.get(this).runWithLooper(
+        mTestableLooper = TestableLooper.get(this);
+        mDependency.injectTestDependency(
+                Dependency.BG_HANDLER, new Handler(mTestableLooper.getLooper()));
+        mDependency.injectTestDependency(Dependency.MAIN_LOOPER, mTestableLooper.getLooper());
+        mTestableLooper.runWithLooper(
                 () -> mCarrierGroup = (QSCarrierGroup) LayoutInflater.from(mContext).inflate(
                         R.layout.qs_carrier_group, null));
+        mCallback = mCarrierGroup.getCallback();
     }
 
     @Test // throws no Exception
@@ -72,7 +81,7 @@
                 new CharSequence[]{""},
                 false,
                 new int[]{0});
-        spiedCarrierGroup.updateCarrierInfo(c1);
+        mCallback.updateCarrierInfo(c1);
 
         // listOfCarriers length 1, subscriptionIds length 1, anySims true
         CarrierTextController.CarrierTextCallbackInfo
@@ -81,7 +90,7 @@
                 new CharSequence[]{""},
                 true,
                 new int[]{0});
-        spiedCarrierGroup.updateCarrierInfo(c2);
+        mCallback.updateCarrierInfo(c2);
 
         // listOfCarriers length 2, subscriptionIds length 2, anySims false
         CarrierTextController.CarrierTextCallbackInfo
@@ -90,7 +99,7 @@
                 new CharSequence[]{"", ""},
                 false,
                 new int[]{0, 1});
-        spiedCarrierGroup.updateCarrierInfo(c3);
+        mCallback.updateCarrierInfo(c3);
 
         // listOfCarriers length 2, subscriptionIds length 2, anySims true
         CarrierTextController.CarrierTextCallbackInfo
@@ -99,7 +108,9 @@
                 new CharSequence[]{"", ""},
                 true,
                 new int[]{0, 1});
-        spiedCarrierGroup.updateCarrierInfo(c4);
+        mCallback.updateCarrierInfo(c4);
+
+        mTestableLooper.processAllMessages();
     }
 
     @Test // throws no Exception
@@ -120,7 +131,7 @@
                 new CharSequence[]{"", ""},
                 false,
                 new int[]{0});
-        spiedCarrierGroup.updateCarrierInfo(c1);
+        mCallback.updateCarrierInfo(c1);
 
         // listOfCarriers length 2, subscriptionIds length 1, anySims true
         CarrierTextController.CarrierTextCallbackInfo
@@ -129,7 +140,7 @@
                 new CharSequence[]{"", ""},
                 true,
                 new int[]{0});
-        spiedCarrierGroup.updateCarrierInfo(c2);
+        mCallback.updateCarrierInfo(c2);
 
         // listOfCarriers length 1, subscriptionIds length 2, anySims false
         CarrierTextController.CarrierTextCallbackInfo
@@ -138,7 +149,7 @@
                 new CharSequence[]{""},
                 false,
                 new int[]{0, 1});
-        spiedCarrierGroup.updateCarrierInfo(c3);
+        mCallback.updateCarrierInfo(c3);
 
         // listOfCarriers length 1, subscriptionIds length 2, anySims true
         CarrierTextController.CarrierTextCallbackInfo
@@ -147,7 +158,8 @@
                 new CharSequence[]{""},
                 true,
                 new int[]{0, 1});
-        spiedCarrierGroup.updateCarrierInfo(c4);
+        mCallback.updateCarrierInfo(c4);
+        mTestableLooper.processAllMessages();
     }
 
     @Test // throws no Exception
@@ -161,7 +173,8 @@
                 new CharSequence[]{"", ""},
                 true,
                 new int[]{0, 1});
-        spiedCarrierGroup.updateCarrierInfo(c4);
+        mCallback.updateCarrierInfo(c4);
+        mTestableLooper.processAllMessages();
     }
 
     @Test // throws no Exception