Update the summary of AoD when Bedtime mode is on

Since Bedtime mode can suppress AoD, after reviewed by UX, we decide
update the summary to "Unavailable because Bedtime mode is on" when AoD
is suppressed by Bedtime mode.

Bug: 168790245
Test: manual & robotest
Change-Id: Id2511cb0ad93b44f6bf701a707b7ddef9438653d
Merged-In: Id2511cb0ad93b44f6bf701a707b7ddef9438653d
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index de4ae39..87859ab 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -104,7 +104,9 @@
     <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
     <uses-permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE" />
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-    
+    <uses-permission android:name="android.permission.READ_DREAM_STATE" />
+    <uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION" />
+
     <application android:label="@string/settings_label"
             android:icon="@drawable/ic_launcher_settings"
             android:theme="@style/Theme.Settings"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d141823..4e43aee 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -12187,4 +12187,7 @@
 
     <!-- Summary for see all preference when bluetooth is disable [CHAR LIMIT=none]-->
     <string name="connected_device_see_all_summary">Bluetooth will turn on</string>
+
+    <!-- Summary for preference when Bedtime mode is on [CHAR LIMIT=NONE] -->
+    <string name="aware_summary_when_bedtime_on">Unavailable because bedtime mode is on</string>
 </resources>
diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
index 6dee5d2..7749c8f 100644
--- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
@@ -16,12 +16,17 @@
 package com.android.settings.display;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.PowerManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 
+import androidx.preference.Preference;
+
+import com.android.settings.R;
 import com.android.settings.core.TogglePreferenceController;
 
 public class AmbientDisplayAlwaysOnPreferenceController extends TogglePreferenceController {
@@ -31,13 +36,9 @@
 
     private static final int MY_USER = UserHandle.myUserId();
     private static final String PROP_AWARE_AVAILABLE = "ro.vendor.aware_available";
+    private static final String AOD_SUPPRESSED_TOKEN = "winddown";
 
     private AmbientDisplayConfiguration mConfig;
-    private OnPreferenceChangedCallback mCallback;
-
-    public interface OnPreferenceChangedCallback {
-        void onPreferenceChanged();
-    }
 
     public AmbientDisplayAlwaysOnPreferenceController(Context context, String key) {
         super(context, key);
@@ -51,6 +52,12 @@
     }
 
     @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        refreshSummary(preference);
+    }
+
+    @Override
     public boolean isSliceable() {
         return TextUtils.equals(getPreferenceKey(), "ambient_display_always_on");
     }
@@ -70,24 +77,22 @@
         int enabled = isChecked ? ON : OFF;
         Settings.Secure.putInt(
                 mContext.getContentResolver(), Settings.Secure.DOZE_ALWAYS_ON, enabled);
-        if (mCallback != null) {
-            mCallback.onPreferenceChanged();
-        }
         return true;
     }
 
+    @Override
+    public CharSequence getSummary() {
+        return mContext.getText(
+                isAodSuppressedByBedtime(mContext) ? R.string.aware_summary_when_bedtime_on
+                        : R.string.doze_always_on_summary);
+    }
+
     public AmbientDisplayAlwaysOnPreferenceController setConfig(
             AmbientDisplayConfiguration config) {
         mConfig = config;
         return this;
     }
 
-    public AmbientDisplayAlwaysOnPreferenceController setCallback(
-            OnPreferenceChangedCallback callback) {
-        mCallback = callback;
-        return this;
-    }
-
     public static boolean isAvailable(AmbientDisplayConfiguration config) {
         return config.alwaysOnAvailableForUser(MY_USER);
     }
@@ -98,4 +103,25 @@
         }
         return mConfig;
     }
+
+    /**
+     * Returns whether AOD is suppressed by Bedtime mode, a feature of Digital Wellbeing.
+     *
+     * We know that Bedtime mode suppresses AOD using {@link AOD_SUPPRESSED_TOKEN}. If the Digital
+     * Wellbeing app is suppressing AOD with {@link AOD_SUPPRESSED_TOKEN}, then we can infer that
+     * AOD is being suppressed by Bedtime mode.
+     */
+    public static boolean isAodSuppressedByBedtime(Context context) {
+        int uid;
+        final PowerManager powerManager = context.getSystemService(PowerManager.class);
+        final PackageManager packageManager = context.getPackageManager();
+        final String packageName = context.getString(
+                com.android.internal.R.string.config_defaultWellbeingPackage);
+        try {
+            uid = packageManager.getApplicationInfo(packageName, /* flags= */ 0).uid;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+        return powerManager.isAmbientDisplaySuppressedForTokenByApp(AOD_SUPPRESSED_TOKEN, uid);
+    }
 }
diff --git a/src/com/android/settings/security/LockscreenDashboardFragment.java b/src/com/android/settings/security/LockscreenDashboardFragment.java
index 3352991..39355f3 100644
--- a/src/com/android/settings/security/LockscreenDashboardFragment.java
+++ b/src/com/android/settings/security/LockscreenDashboardFragment.java
@@ -88,9 +88,7 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        use(AmbientDisplayAlwaysOnPreferenceController.class)
-                .setConfig(getConfig(context))
-                .setCallback(this::updatePreferenceStates);
+        use(AmbientDisplayAlwaysOnPreferenceController.class).setConfig(getConfig(context));
         use(AmbientDisplayNotificationsPreferenceController.class).setConfig(getConfig(context));
         use(DoubleTapScreenPreferenceController.class).setConfig(getConfig(context));
         use(PickupGesturePreferenceController.class).setConfig(getConfig(context));
diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
index 1548b42..405ea12 100644
--- a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
@@ -19,13 +19,20 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.PowerManager;
 import android.provider.Settings;
 
+import com.android.internal.R;
 import com.android.settings.testutils.shadow.ShadowSecureSettings;
 
 import org.junit.Before;
@@ -41,24 +48,41 @@
 @Config(shadows = ShadowSecureSettings.class)
 public class AmbientDisplayAlwaysOnPreferenceControllerTest {
 
+    private static final String TEST_PACKAGE = "com.android.test";
+
     @Mock
     private AmbientDisplayConfiguration mConfig;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private PowerManager mPowerManager;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
 
     private Context mContext;
 
     private ContentResolver mContentResolver;
 
     private AmbientDisplayAlwaysOnPreferenceController mController;
-    private boolean mCallbackInvoked;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = spy(RuntimeEnvironment.application);
         mContentResolver = mContext.getContentResolver();
         mController = new AmbientDisplayAlwaysOnPreferenceController(mContext, "key");
         mController.setConfig(mConfig);
-        mController.setCallback(() -> mCallbackInvoked = true);
+
+        mApplicationInfo.uid = 1;
+        when(mContext.getString(R.string.config_defaultWellbeingPackage)).thenReturn(TEST_PACKAGE);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(
+                TEST_PACKAGE, /* flag= */0);
+
+        doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
+        when(mPowerManager.isAmbientDisplaySuppressedForTokenByApp(anyString(), anyInt()))
+                .thenReturn(false);
     }
 
     @Test
@@ -108,13 +132,6 @@
     }
 
     @Test
-    public void onPreferenceChange_callback() {
-        assertThat(mCallbackInvoked).isFalse();
-        mController.setChecked(true);
-        assertThat(mCallbackInvoked).isTrue();
-    }
-
-    @Test
     public void isSliceableCorrectKey_returnsTrue() {
         final AmbientDisplayAlwaysOnPreferenceController controller =
                 new AmbientDisplayAlwaysOnPreferenceController(mContext,
@@ -133,4 +150,39 @@
     public void isPublicSlice_returnTrue() {
         assertThat(mController.isPublicSlice()).isTrue();
     }
+
+    @Test
+    public void isAodSuppressedByBedtime_bedTimeModeOn_returnTrue() {
+        when(mPowerManager.isAmbientDisplaySuppressedForTokenByApp(anyString(), anyInt()))
+                .thenReturn(true);
+
+        assertThat(AmbientDisplayAlwaysOnPreferenceController
+                .isAodSuppressedByBedtime(mContext)).isTrue();
+    }
+
+    @Test
+    public void isAodSuppressedByBedtime_bedTimeModeOff_returnFalse() {
+        assertThat(AmbientDisplayAlwaysOnPreferenceController
+                .isAodSuppressedByBedtime(mContext)).isFalse();
+    }
+
+    @Test
+    public void isAodSuppressedByBedtime_notFoundWellbeingPackage_returnFalse()
+            throws PackageManager.NameNotFoundException {
+        when(mPackageManager.getApplicationInfo(TEST_PACKAGE, /* flag= */0)).thenThrow(
+                new PackageManager.NameNotFoundException());
+
+        assertThat(AmbientDisplayAlwaysOnPreferenceController
+                .isAodSuppressedByBedtime(mContext)).isFalse();
+    }
+
+    @Test
+    public void getSummary_bedTimeModeOn_shouldReturnUnavailableSummary() {
+        when(mPowerManager.isAmbientDisplaySuppressedForTokenByApp(anyString(), anyInt()))
+                .thenReturn(true);
+
+        final CharSequence summary = mController.getSummary();
+        assertThat(summary).isEqualTo(mContext.getString(
+                com.android.settings.R.string.aware_summary_when_bedtime_on));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/security/LockscreenDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/security/LockscreenDashboardFragmentTest.java
index 8b5c5fe..4146a4b 100644
--- a/tests/robotests/src/com/android/settings/security/LockscreenDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/security/LockscreenDashboardFragmentTest.java
@@ -88,7 +88,6 @@
 
         mTestFragment.onAttach(mContext);
         verify(controller).setConfig(any());
-        verify(controller).setCallback(any());
     }
 
     @Test