Fix BroadcastReceiver registration in DefaultDeviceEffectsApplier

Test: atest DefaultDeviceEffectsApplierTest
Bug: 308673343
Change-Id: I0dc733b4846876af4c27737ef6285d8e4ab4597e
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index 82faeef..8855666 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -32,6 +32,8 @@
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
 
+import com.android.internal.annotations.GuardedBy;
+
 /** Default implementation for {@link DeviceEffectsApplier}. */
 class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
 
@@ -50,6 +52,10 @@
     private final UiModeManager mUiModeManager;
     private final WallpaperManager mWallpaperManager;
 
+    private final Object mRegisterReceiverLock = new Object();
+    @GuardedBy("mRegisterReceiverLock")
+    private boolean mIsScreenOffReceiverRegistered;
+
     private ZenDeviceEffects mLastAppliedEffects = new ZenDeviceEffects.Builder().build();
     private boolean mPendingNightMode;
 
@@ -96,7 +102,6 @@
 
     private void updateOrScheduleNightMode(boolean useNightMode, @ConfigChangeOrigin int origin) {
         mPendingNightMode = useNightMode;
-        safeUnregisterReceiver(mNightModeWhenScreenOff);
 
         // Changing the theme can be disruptive for the user (Activities are likely recreated, may
         // lose some state). Therefore we only apply the change immediately if the rule was
@@ -106,17 +111,18 @@
                 || origin == ZenModeConfig.UPDATE_ORIGIN_USER
                 || origin == ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                 || !mPowerManager.isInteractive()) {
+            unregisterScreenOffReceiver();
             updateNightModeImmediately(useNightMode);
         } else {
-            mContext.registerReceiver(mNightModeWhenScreenOff, SCREEN_OFF_INTENT_FILTER,
-                    Context.RECEIVER_NOT_EXPORTED);
+            registerScreenOffReceiver();
         }
     }
 
+    @GuardedBy("mRegisterReceiverLock")
     private final BroadcastReceiver mNightModeWhenScreenOff = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            safeUnregisterReceiver(mNightModeWhenScreenOff);
+            unregisterScreenOffReceiver();
             updateNightModeImmediately(mPendingNightMode);
         }
     };
@@ -130,11 +136,22 @@
         });
     }
 
-    private void safeUnregisterReceiver(BroadcastReceiver br) {
-        try {
-            mContext.unregisterReceiver(br);
-        } catch (IllegalArgumentException e) {
-            // It's fine, we haven't registered it yet.
+    private void registerScreenOffReceiver() {
+        synchronized (mRegisterReceiverLock) {
+            if (!mIsScreenOffReceiverRegistered) {
+                mContext.registerReceiver(mNightModeWhenScreenOff, SCREEN_OFF_INTENT_FILTER,
+                        Context.RECEIVER_NOT_EXPORTED);
+                mIsScreenOffReceiverRegistered = true;
+            }
+        }
+    }
+
+    private void unregisterScreenOffReceiver() {
+        synchronized (mRegisterReceiverLock) {
+            if (mIsScreenOffReceiverRegistered) {
+                mIsScreenOffReceiverRegistered = false;
+                mContext.unregisterReceiver(mNightModeWhenScreenOff);
+            }
         }
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index c9e6d6a..8936dc6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -27,7 +27,6 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -200,7 +199,7 @@
         // So the effect is applied, and we stopped listening for this event.
         verify(mUiModeManager).setNightModeActivatedForCustomMode(
                 eq(MODE_NIGHT_CUSTOM_TYPE_BEDTIME), eq(true));
-        verify(mContext, atLeastOnce()).unregisterReceiver(eq(screenOffReceiver));
+        verify(mContext).unregisterReceiver(eq(screenOffReceiver));
     }
 
     @Test