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