Fix synchronization in SystemStateHelper

Properly acquire the big telecom lock before doing anything after
receiving a broadcast.

Bug: 181057509
Test: atest CarModeInCallServiceTest
Change-Id: Ie4c2311278c7c27560c16c87d1eeed282470f9e8
diff --git a/src/com/android/server/telecom/SystemStateHelper.java b/src/com/android/server/telecom/SystemStateHelper.java
index 8fb6bc5..0c14d5e 100644
--- a/src/com/android/server/telecom/SystemStateHelper.java
+++ b/src/com/android/server/telecom/SystemStateHelper.java
@@ -77,35 +77,39 @@
         public void onReceive(Context context, Intent intent) {
             Log.startSession("SSP.oR");
             try {
-                String action = intent.getAction();
-                if (UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED.equals(action)) {
-                    int priority = intent.getIntExtra(UiModeManager.EXTRA_PRIORITY,
-                            UiModeManager.DEFAULT_PRIORITY);
-                    String callingPackage = intent.getStringExtra(
-                            UiModeManager.EXTRA_CALLING_PACKAGE);
-                    Log.i(SystemStateHelper.this, "ENTER_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
-                            priority, callingPackage);
-                    onEnterCarMode(priority, callingPackage);
-                } else if (UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED.equals(action)) {
-                    int priority = intent.getIntExtra(UiModeManager.EXTRA_PRIORITY,
-                            UiModeManager.DEFAULT_PRIORITY);
-                    String callingPackage = intent.getStringExtra(
-                            UiModeManager.EXTRA_CALLING_PACKAGE);
-                    Log.i(SystemStateHelper.this, "EXIT_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
-                            priority, callingPackage);
-                    onExitCarMode(priority, callingPackage);
-                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-                    Uri data = intent.getData();
-                    if (data == null) {
+                synchronized (mLock) {
+                    String action = intent.getAction();
+                    if (UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED.equals(action)) {
+                        int priority = intent.getIntExtra(UiModeManager.EXTRA_PRIORITY,
+                                UiModeManager.DEFAULT_PRIORITY);
+                        String callingPackage = intent.getStringExtra(
+                                UiModeManager.EXTRA_CALLING_PACKAGE);
+                        Log.i(SystemStateHelper.this,
+                                "ENTER_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
+                                priority, callingPackage);
+                        onEnterCarMode(priority, callingPackage);
+                    } else if (UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED.equals(action)) {
+                        int priority = intent.getIntExtra(UiModeManager.EXTRA_PRIORITY,
+                                UiModeManager.DEFAULT_PRIORITY);
+                        String callingPackage = intent.getStringExtra(
+                                UiModeManager.EXTRA_CALLING_PACKAGE);
+                        Log.i(SystemStateHelper.this,
+                                "EXIT_CAR_MODE_PRIORITIZED; priority=%d, pkg=%s",
+                                priority, callingPackage);
+                        onExitCarMode(priority, callingPackage);
+                    } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                        Uri data = intent.getData();
+                        if (data == null) {
+                            Log.w(SystemStateHelper.this,
+                                    "Got null data for package removed, ignoring");
+                            return;
+                        }
+                        mListeners.forEach(
+                                l -> l.onPackageUninstalled(data.getEncodedSchemeSpecificPart()));
+                    } else {
                         Log.w(SystemStateHelper.this,
-                                "Got null data for package removed, ignoring");
-                        return;
+                                "Unexpected intent received: %s", intent.getAction());
                     }
-                    mListeners.forEach(
-                            l -> l.onPackageUninstalled(data.getEncodedSchemeSpecificPart()));
-                } else {
-                    Log.w(SystemStateHelper.this,
-                            "Unexpected intent received: %s", intent.getAction());
                 }
             } finally {
                 Log.endSession();
@@ -125,9 +129,11 @@
 
     private Set<SystemStateListener> mListeners = new CopyOnWriteArraySet<>();
     private boolean mIsCarModeOrProjectionActive;
+    private final TelecomSystem.SyncRoot mLock;
 
-    public SystemStateHelper(Context context) {
+    public SystemStateHelper(Context context, TelecomSystem.SyncRoot lock) {
         mContext = context;
+        mLock = lock;
 
         IntentFilter intentFilter1 = new IntentFilter(
                 UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 8928e76..112390e 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -233,7 +233,7 @@
         mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
 
         WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
-        SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
+        SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
 
         mMissedCallNotifier = missedCallNotifierImplFactory
                 .makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar, defaultDialerCache,
diff --git a/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java b/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java
index ad52625..94b8463 100644
--- a/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java
+++ b/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java
@@ -48,6 +48,7 @@
 
 import com.android.server.telecom.SystemStateHelper;
 import com.android.server.telecom.SystemStateHelper.SystemStateListener;
+import com.android.server.telecom.TelecomSystem;
 
 import org.junit.After;
 import org.junit.Before;
@@ -78,6 +79,7 @@
     @Mock SensorManager mSensorManager;
     @Mock Intent mIntentEnter;
     @Mock Intent mIntentExit;
+    TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
 
     @Override
     @Before
@@ -109,7 +111,7 @@
     @SmallTest
     @Test
     public void testListeners() throws Exception {
-        SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
+        SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
 
         assertFalse(systemStateHelper.removeListener(mSystemStateListener));
         systemStateHelper.addListener(mSystemStateListener);
@@ -121,14 +123,14 @@
     @Test
     public void testQuerySystemForCarMode_True() {
         when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
-        assertTrue(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+        assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
     }
 
     @SmallTest
     @Test
     public void testQuerySystemForCarMode_False() {
         when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_NORMAL);
-        assertFalse(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+        assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
     }
 
     @SmallTest
@@ -136,11 +138,11 @@
     public void testQuerySystemForAutomotiveProjection_True() {
         when(mUiModeManager.getActiveProjectionTypes())
                 .thenReturn(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE);
-        assertTrue(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+        assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
 
         when(mUiModeManager.getActiveProjectionTypes())
                 .thenReturn(UiModeManager.PROJECTION_TYPE_ALL);
-        assertTrue(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+        assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
     }
 
     @SmallTest
@@ -148,7 +150,7 @@
     public void testQuerySystemForAutomotiveProjection_False() {
         when(mUiModeManager.getActiveProjectionTypes())
                 .thenReturn(UiModeManager.PROJECTION_TYPE_NONE);
-        assertFalse(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+        assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
     }
 
     @SmallTest
@@ -157,7 +159,7 @@
         when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
         when(mUiModeManager.getActiveProjectionTypes())
                 .thenReturn(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE);
-        assertTrue(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+        assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
     }
 
     @SmallTest
@@ -166,7 +168,7 @@
         when(mContext.getSystemService(UiModeManager.class))
                 .thenReturn(mUiModeManager)  // Without this, class construction will throw NPE.
                 .thenReturn(null);
-        assertFalse(new SystemStateHelper(mContext).isCarModeOrProjectionActive());
+        assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive());
     }
 
     @SmallTest
@@ -174,7 +176,7 @@
     public void testPackageRemoved() {
         ArgumentCaptor<BroadcastReceiver> receiver =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
-        new SystemStateHelper(mContext).addListener(mSystemStateListener);
+        new SystemStateHelper(mContext, mLock).addListener(mSystemStateListener);
         verify(mContext, atLeastOnce())
                 .registerReceiver(receiver.capture(), any(IntentFilter.class));
         Intent packageRemovedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
@@ -188,7 +190,7 @@
     public void testReceiverAndIntentFilter() {
         ArgumentCaptor<IntentFilter> intentFilterCaptor =
                 ArgumentCaptor.forClass(IntentFilter.class);
-        new SystemStateHelper(mContext);
+        new SystemStateHelper(mContext, mLock);
         verify(mContext, times(2)).registerReceiver(
                 any(BroadcastReceiver.class), intentFilterCaptor.capture());
 
@@ -225,7 +227,7 @@
     public void testOnEnterExitCarMode() {
         ArgumentCaptor<BroadcastReceiver> receiver =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
-        new SystemStateHelper(mContext).addListener(mSystemStateListener);
+        new SystemStateHelper(mContext, mLock).addListener(mSystemStateListener);
 
         verify(mContext, atLeastOnce())
                 .registerReceiver(receiver.capture(), any(IntentFilter.class));
@@ -244,7 +246,7 @@
     @SmallTest
     @Test
     public void testOnSetReleaseAutomotiveProjection() {
-        SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
+        SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
         // We don't care what listener is registered, that's an implementation detail, but we need
         // to call methods on whatever it is.
         ArgumentCaptor<UiModeManager.OnProjectionStateChangeListener> listenerCaptor =