diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index b35481d3c31ba..97e753e2bdeb8 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -227,6 +227,12 @@
- @string/concurrent_display_notification_thermal_content
+
+ - @string/concurrent_display_notification_power_save_title
+
+
+ - @string/concurrent_display_notification_power_save_content
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d8e69d748324d..6afdae5086232 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6267,6 +6267,12 @@ ul.
Device is too warm
Dual Screen is unavailable because your phone is getting too warm
+
+ Dual Screen is unavailable
+
+ Dual Screen is unavailable because Battery Saver is on. You can turn this off in Settings.
+
+ Go to Settings
Turn off
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c10612e16acf5..1cb56e0c203b8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4931,12 +4931,17 @@
+
+
+
+
+
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index a589313a1e921..00af22468fd3d 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -76,7 +76,13 @@ public final class DeviceState {
* This flag indicates that the corresponding state should be disabled when the device is
* overheating and reaching the critical status.
*/
- public static final int FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
+ public static final int FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
+
+ /**
+ * This flag indicates that the corresponding state should be disabled when power save mode
+ * is enabled.
+ */
+ public static final int FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 1 << 5;
/** @hide */
@IntDef(prefix = {"FLAG_"}, flag = true, value = {
@@ -84,7 +90,8 @@ public final class DeviceState {
FLAG_APP_INACCESSIBLE,
FLAG_EMULATED_ONLY,
FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
- FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL
+ FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
+ FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE
})
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceStateFlags {}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 43ee5e268fe05..964569008accc 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -25,6 +25,7 @@ import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STA
import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
+import static com.android.server.devicestate.OverrideRequestController.FLAG_POWER_SAVE_ENABLED;
import static com.android.server.devicestate.OverrideRequestController.FLAG_THERMAL_CRITICAL;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
@@ -609,7 +610,8 @@ public final class DeviceStateManagerService extends SystemService {
@GuardedBy("mLock")
private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
- @OverrideRequestController.RequestStatus int status, int flags) {
+ @OverrideRequestController.RequestStatus int status,
+ @OverrideRequestController.StatusChangedFlag int flags) {
if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_BASE_STATE) {
switch (status) {
case STATUS_ACTIVE:
@@ -641,6 +643,10 @@ public final class DeviceStateManagerService extends SystemService {
mDeviceStateNotificationController
.showThermalCriticalNotificationIfNeeded(
request.getRequestedState());
+ } else if ((flags & FLAG_POWER_SAVE_ENABLED) == FLAG_POWER_SAVE_ENABLED) {
+ mDeviceStateNotificationController
+ .showPowerSaveNotificationIfNeeded(
+ request.getRequestedState());
}
}
break;
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
index 900874044881c..ab261ac240910 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
@@ -16,6 +16,8 @@
package com.android.server.devicestate;
+import static android.provider.Settings.ACTION_BATTERY_SAVER_SETTINGS;
+
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -101,10 +103,16 @@ class DeviceStateNotificationController extends BroadcastReceiver {
}
String requesterApplicationLabel = getApplicationLabel(requestingAppUid);
if (requesterApplicationLabel != null) {
+ final Intent intent = new Intent(INTENT_ACTION_CANCEL_STATE)
+ .setPackage(mContext.getPackageName());
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
showNotification(
info.name, info.activeNotificationTitle,
String.format(info.activeNotificationContent, requesterApplicationLabel),
- true /* ongoing */, R.drawable.ic_dual_screen
+ true /* ongoing */, R.drawable.ic_dual_screen,
+ pendingIntent,
+ mContext.getString(R.string.device_state_notification_turn_off_button)
);
} else {
Slog.e(TAG, "Cannot determine the requesting app name when showing state active "
@@ -126,7 +134,33 @@ class DeviceStateNotificationController extends BroadcastReceiver {
showNotification(
info.name, info.thermalCriticalNotificationTitle,
info.thermalCriticalNotificationContent, false /* ongoing */,
- R.drawable.ic_thermostat
+ R.drawable.ic_thermostat,
+ null /* pendingIntent */,
+ null /* actionText */
+ );
+ }
+
+ /**
+ * Displays the notification indicating that the device state is canceled due to power
+ * save mode being enabled. Does nothing if the state does not have a power save mode
+ * notification.
+ *
+ * @param state the identifier of the device state being canceled.
+ */
+ void showPowerSaveNotificationIfNeeded(int state) {
+ NotificationInfo info = mNotificationInfos.get(state);
+ if (info == null || !info.hasPowerSaveModeNotification()) {
+ return;
+ }
+ final Intent intent = new Intent(ACTION_BATTERY_SAVER_SETTINGS);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(
+ mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+ showNotification(
+ info.name, info.powerSaveModeNotificationTitle,
+ info.powerSaveModeNotificationContent, false /* ongoing */,
+ R.drawable.ic_thermostat,
+ pendingIntent,
+ mContext.getString(R.string.device_state_notification_settings_button)
);
}
@@ -161,7 +195,8 @@ class DeviceStateNotificationController extends BroadcastReceiver {
*/
private void showNotification(
@NonNull String name, @NonNull String title, @NonNull String content, boolean ongoing,
- @DrawableRes int iconRes) {
+ @DrawableRes int iconRes,
+ @Nullable PendingIntent pendingIntent, @Nullable String actionText) {
final NotificationChannel channel = new NotificationChannel(
CHANNEL_ID, name, NotificationManager.IMPORTANCE_HIGH);
final Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID)
@@ -173,14 +208,10 @@ class DeviceStateNotificationController extends BroadcastReceiver {
.setOngoing(ongoing)
.setCategory(Notification.CATEGORY_SYSTEM);
- if (ongoing) {
- final Intent intent = new Intent(INTENT_ACTION_CANCEL_STATE)
- .setPackage(mContext.getPackageName());
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(
- mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+ if (pendingIntent != null && actionText != null) {
final Notification.Action action = new Notification.Action.Builder(
null /* icon */,
- mContext.getString(R.string.device_state_notification_turn_off_button),
+ actionText,
pendingIntent)
.build();
builder.addAction(action);
@@ -215,12 +246,21 @@ class DeviceStateNotificationController extends BroadcastReceiver {
final String[] thermalCriticalNotificationContents =
context.getResources().getStringArray(
R.array.device_state_notification_thermal_contents);
+ final String[] powerSaveModeNotificationTitles =
+ context.getResources().getStringArray(
+ R.array.device_state_notification_power_save_titles);
+ final String[] powerSaveModeNotificationContents =
+ context.getResources().getStringArray(
+ R.array.device_state_notification_power_save_contents);
+
if (stateIdentifiers.length != names.length
|| stateIdentifiers.length != activeNotificationTitles.length
|| stateIdentifiers.length != activeNotificationContents.length
|| stateIdentifiers.length != thermalCriticalNotificationTitles.length
|| stateIdentifiers.length != thermalCriticalNotificationContents.length
+ || stateIdentifiers.length != powerSaveModeNotificationTitles.length
+ || stateIdentifiers.length != powerSaveModeNotificationContents.length
) {
throw new IllegalStateException(
"The length of state identifiers and notification texts must match!");
@@ -237,7 +277,9 @@ class DeviceStateNotificationController extends BroadcastReceiver {
new NotificationInfo(
names[i], activeNotificationTitles[i], activeNotificationContents[i],
thermalCriticalNotificationTitles[i],
- thermalCriticalNotificationContents[i])
+ thermalCriticalNotificationContents[i],
+ powerSaveModeNotificationTitles[i],
+ powerSaveModeNotificationContents[i])
);
}
@@ -272,16 +314,21 @@ class DeviceStateNotificationController extends BroadcastReceiver {
public final String activeNotificationContent;
public final String thermalCriticalNotificationTitle;
public final String thermalCriticalNotificationContent;
+ public final String powerSaveModeNotificationTitle;
+ public final String powerSaveModeNotificationContent;
NotificationInfo(String name, String activeNotificationTitle,
String activeNotificationContent, String thermalCriticalNotificationTitle,
- String thermalCriticalNotificationContent) {
+ String thermalCriticalNotificationContent, String powerSaveModeNotificationTitle,
+ String powerSaveModeNotificationContent) {
this.name = name;
this.activeNotificationTitle = activeNotificationTitle;
this.activeNotificationContent = activeNotificationContent;
this.thermalCriticalNotificationTitle = thermalCriticalNotificationTitle;
this.thermalCriticalNotificationContent = thermalCriticalNotificationContent;
+ this.powerSaveModeNotificationTitle = powerSaveModeNotificationTitle;
+ this.powerSaveModeNotificationContent = powerSaveModeNotificationContent;
}
boolean hasActiveNotification() {
@@ -292,5 +339,10 @@ class DeviceStateNotificationController extends BroadcastReceiver {
return thermalCriticalNotificationTitle != null
&& thermalCriticalNotificationTitle.length() > 0;
}
+
+ boolean hasPowerSaveModeNotification() {
+ return powerSaveModeNotificationTitle != null
+ && powerSaveModeNotificationTitle.length() > 0;
+ }
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index fecc13fd0d034..af33de0426b1c 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -52,11 +52,24 @@ public interface DeviceStateProvider {
*/
int SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL = 3;
+ /**
+ * Indicating that the supported device states have changed because power save mode was enabled.
+ */
+ int SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED = 4;
+
+ /**
+ * Indicating that the supported device states have changed because power save mode was
+ * disabled.
+ */
+ int SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED = 5;
+
@IntDef(prefix = { "SUPPORTED_DEVICE_STATES_CHANGED_" }, value = {
SUPPORTED_DEVICE_STATES_CHANGED_DEFAULT,
SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED,
SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL,
- SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL
+ SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL,
+ SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED,
+ SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED
})
@Retention(RetentionPolicy.SOURCE)
@interface SupportedStatesUpdatedReason {}
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 2ed4765874f79..46f0bc0d98056 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -64,6 +64,18 @@ final class OverrideRequestController {
*/
static final int FLAG_THERMAL_CRITICAL = 1 << 0;
+ /**
+ * A flag indicating that the status change was triggered by power save mode.
+ */
+ static final int FLAG_POWER_SAVE_ENABLED = 1 << 1;
+
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {
+ FLAG_THERMAL_CRITICAL,
+ FLAG_POWER_SAVE_ENABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface StatusChangedFlag {}
+
static String statusToString(@RequestStatus int status) {
switch (status) {
case STATUS_ACTIVE:
@@ -228,13 +240,18 @@ final class OverrideRequestController {
@DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
boolean isThermalCritical =
reason == DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
+ boolean isPowerSaveEnabled =
+ reason == DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
+ @StatusChangedFlag int flags = 0;
+ flags |= isThermalCritical ? FLAG_THERMAL_CRITICAL : 0;
+ flags |= isPowerSaveEnabled ? FLAG_POWER_SAVE_ENABLED : 0;
if (mBaseStateRequest != null && !contains(newSupportedStates,
mBaseStateRequest.getRequestedState())) {
- cancelCurrentBaseStateRequestLocked(isThermalCritical ? FLAG_THERMAL_CRITICAL : 0);
+ cancelCurrentBaseStateRequestLocked(flags);
}
if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
- cancelCurrentRequestLocked(isThermalCritical ? FLAG_THERMAL_CRITICAL : 0);
+ cancelCurrentRequestLocked(flags);
}
}
@@ -255,7 +272,8 @@ final class OverrideRequestController {
cancelRequestLocked(requestToCancel, 0 /* flags */);
}
- private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel, int flags) {
+ private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel,
+ @StatusChangedFlag int flags) {
mListener.onStatusChanged(requestToCancel, STATUS_CANCELED, flags);
}
@@ -267,7 +285,7 @@ final class OverrideRequestController {
cancelCurrentRequestLocked(0 /* flags */);
}
- private void cancelCurrentRequestLocked(int flags) {
+ private void cancelCurrentRequestLocked(@StatusChangedFlag int flags) {
if (mRequest == null) {
Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
return;
@@ -285,7 +303,7 @@ final class OverrideRequestController {
cancelCurrentBaseStateRequestLocked(0 /* flags */);
}
- private void cancelCurrentBaseStateRequestLocked(int flags) {
+ private void cancelCurrentBaseStateRequestLocked(@StatusChangedFlag int flags) {
if (mBaseStateRequest == null) {
Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
return;
@@ -312,6 +330,6 @@ final class OverrideRequestController {
* cancelled request.
*/
void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus,
- int flags);
+ @StatusChangedFlag int flags);
}
}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 8d7f78209a4e7..3644054e3b780 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -21,7 +21,10 @@ import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STA
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -101,8 +104,10 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
private static final String FLAG_EMULATED_ONLY = "FLAG_EMULATED_ONLY";
private static final String FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
"FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP";
- private static final String FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL =
- "FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL";
+ private static final String FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL =
+ "FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL";
+ private static final String FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE =
+ "FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE";
/** Interface that allows reading the device state configuration. */
interface ReadableConfig {
@@ -162,9 +167,12 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
case FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP:
flags |= DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP;
break;
- case FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL:
- flags |= DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL;
+ case FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL:
+ flags |= DeviceState
+ .FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL;
break;
+ case FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE:
+ flags |= DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE;
default:
Slog.w(TAG, "Parsed unknown flag with name: "
+ configFlagString);
@@ -210,6 +218,9 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
@GuardedBy("mLock")
private @PowerManager.ThermalStatus int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
+ @GuardedBy("mLock")
+ private boolean mPowerSaveModeEnabled;
+
private DeviceStateProviderImpl(@NonNull Context context,
@NonNull List deviceStates,
@NonNull List stateConditions) {
@@ -224,14 +235,32 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
setStateConditions(deviceStates, stateConditions);
- // If any of the device states are thermal sensitive, i.e. it should be disabled when the
- // device is overheating, then we will update the list of supported states when thermal
- // status changes.
- if (hasThermalSensitiveState(deviceStates)) {
- PowerManager powerManager = context.getSystemService(PowerManager.class);
- if (powerManager != null) {
+ PowerManager powerManager = context.getSystemService(PowerManager.class);
+ if (powerManager != null) {
+ // If any of the device states are thermal sensitive, i.e. it should be disabled when
+ // the device is overheating, then we will update the list of supported states when
+ // thermal status changes.
+ if (hasThermalSensitiveState(deviceStates)) {
powerManager.addThermalStatusListener(this);
}
+
+ // If any of the device states are power sensitive, i.e. it should be disabled when
+ // power save mode is enabled, then we will update the list of supported states when
+ // power save mode is toggled.
+ if (hasPowerSaveSensitiveState(deviceStates)) {
+ IntentFilter filter = new IntentFilter(
+ PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL.equals(
+ intent.getAction())) {
+ onPowerSaveModeChanged(powerManager.isPowerSaveMode());
+ }
+ }
+ };
+ mContext.registerReceiver(receiver, filter);
+ }
}
}
@@ -382,7 +411,11 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
for (DeviceState deviceState : mOrderedStates) {
if (isThermalStatusCriticalOrAbove(mThermalStatus)
&& deviceState.hasFlag(
- DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL)) {
+ DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ continue;
+ }
+ if (mPowerSaveModeEnabled && deviceState.hasFlag(
+ DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
continue;
}
supportedStates.add(deviceState);
@@ -674,6 +707,18 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
}
}
+ @VisibleForTesting
+ void onPowerSaveModeChanged(boolean isPowerSaveModeEnabled) {
+ synchronized (mLock) {
+ if (mPowerSaveModeEnabled != isPowerSaveModeEnabled) {
+ mPowerSaveModeEnabled = isPowerSaveModeEnabled;
+ notifySupportedStatesChanged(
+ isPowerSaveModeEnabled ? SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED
+ : SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED);
+ }
+ }
+ }
+
@Override
public void onThermalStatusChanged(@PowerManager.ThermalStatus int thermalStatus) {
int previousThermalStatus;
@@ -709,7 +754,16 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
private static boolean hasThermalSensitiveState(List deviceStates) {
for (DeviceState state : deviceStates) {
- if (state.hasFlag(DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL)) {
+ if (state.hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasPowerSaveSensitiveState(List deviceStates) {
+ for (int i = 0; i < deviceStates.size(); i++) {
+ if (deviceStates.get(i).hasFlag(DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
return true;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
index 8196d6a35cbd6..e396263b1679d 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
@@ -52,7 +52,7 @@ public class DeviceStateNotificationControllerTest {
private static final int STATE_WITHOUT_NOTIFICATION = 1;
private static final int STATE_WITH_ACTIVE_NOTIFICATION = 2;
- private static final int STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION = 3;
+ private static final int STATE_WITH_ALL_NOTIFICATION = 3;
private static final int VALID_APP_UID = 1000;
private static final int INVALID_APP_UID = 2000;
@@ -68,6 +68,8 @@ public class DeviceStateNotificationControllerTest {
private static final String CONTENT_2 = "content2:%1$s";
private static final String THERMAL_TITLE_2 = "thermal_title2";
private static final String THERMAL_CONTENT_2 = "thermal_content2";
+ private static final String POWER_SAVE_TITLE_2 = "power_save_title2";
+ private static final String POWER_SAVE_CONTENT_2 = "power_save_content2";
private DeviceStateNotificationController mController;
@@ -88,11 +90,12 @@ public class DeviceStateNotificationControllerTest {
notificationInfos.put(STATE_WITH_ACTIVE_NOTIFICATION,
new DeviceStateNotificationController.NotificationInfo(
NAME_1, TITLE_1, CONTENT_1,
- "", ""));
- notificationInfos.put(STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION,
+ "", "", "", ""));
+ notificationInfos.put(STATE_WITH_ALL_NOTIFICATION,
new DeviceStateNotificationController.NotificationInfo(
NAME_2, TITLE_2, CONTENT_2,
- THERMAL_TITLE_2, THERMAL_CONTENT_2));
+ THERMAL_TITLE_2, THERMAL_CONTENT_2,
+ POWER_SAVE_TITLE_2, POWER_SAVE_CONTENT_2));
when(packageManager.getNameForUid(VALID_APP_UID)).thenReturn(VALID_APP_NAME);
when(packageManager.getNameForUid(INVALID_APP_UID)).thenReturn(INVALID_APP_NAME);
@@ -138,11 +141,47 @@ public class DeviceStateNotificationControllerTest {
DeviceStateNotificationController.NOTIFICATION_ID);
}
+ @Test
+ public void test_powerSaveNotification() {
+ // Verify that the active notification is created.
+ mController.showStateActiveNotificationIfNeeded(
+ STATE_WITH_ALL_NOTIFICATION, VALID_APP_UID);
+ verify(mNotificationManager).notify(
+ eq(DeviceStateNotificationController.NOTIFICATION_TAG),
+ eq(DeviceStateNotificationController.NOTIFICATION_ID),
+ mNotificationCaptor.capture());
+ Notification notification = mNotificationCaptor.getValue();
+ assertEquals(TITLE_2, notification.extras.getString(Notification.EXTRA_TITLE));
+ assertEquals(String.format(CONTENT_2, VALID_APP_LABEL),
+ notification.extras.getString(Notification.EXTRA_TEXT));
+ assertEquals(Notification.FLAG_ONGOING_EVENT,
+ notification.flags & Notification.FLAG_ONGOING_EVENT);
+ Mockito.clearInvocations(mNotificationManager);
+
+ // Verify that the thermal critical notification is created.
+ mController.showPowerSaveNotificationIfNeeded(
+ STATE_WITH_ALL_NOTIFICATION);
+ verify(mNotificationManager).notify(
+ eq(DeviceStateNotificationController.NOTIFICATION_TAG),
+ eq(DeviceStateNotificationController.NOTIFICATION_ID),
+ mNotificationCaptor.capture());
+ notification = mNotificationCaptor.getValue();
+ assertEquals(POWER_SAVE_TITLE_2, notification.extras.getString(Notification.EXTRA_TITLE));
+ assertEquals(POWER_SAVE_CONTENT_2, notification.extras.getString(Notification.EXTRA_TEXT));
+ assertEquals(0, notification.flags & Notification.FLAG_ONGOING_EVENT);
+
+ // Verify that the notification is canceled.
+ mController.cancelNotification(STATE_WITH_ALL_NOTIFICATION);
+ verify(mNotificationManager).cancel(
+ DeviceStateNotificationController.NOTIFICATION_TAG,
+ DeviceStateNotificationController.NOTIFICATION_ID);
+ }
+
@Test
public void test_thermalNotification() {
// Verify that the active notification is created.
mController.showStateActiveNotificationIfNeeded(
- STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION, VALID_APP_UID);
+ STATE_WITH_ALL_NOTIFICATION, VALID_APP_UID);
verify(mNotificationManager).notify(
eq(DeviceStateNotificationController.NOTIFICATION_TAG),
eq(DeviceStateNotificationController.NOTIFICATION_ID),
@@ -157,7 +196,7 @@ public class DeviceStateNotificationControllerTest {
// Verify that the thermal critical notification is created.
mController.showThermalCriticalNotificationIfNeeded(
- STATE_WITH_ACTIVE_AND_THERMAL_NOTIFICATION);
+ STATE_WITH_ALL_NOTIFICATION);
verify(mNotificationManager).notify(
eq(DeviceStateNotificationController.NOTIFICATION_TAG),
eq(DeviceStateNotificationController.NOTIFICATION_ID),
@@ -168,7 +207,7 @@ public class DeviceStateNotificationControllerTest {
assertEquals(0, notification.flags & Notification.FLAG_ONGOING_EVENT);
// Verify that the notification is canceled.
- mController.cancelNotification(STATE_WITH_ACTIVE_NOTIFICATION);
+ mController.cancelNotification(STATE_WITH_ALL_NOTIFICATION);
verify(mNotificationManager).cancel(
DeviceStateNotificationController.NOTIFICATION_TAG,
DeviceStateNotificationController.NOTIFICATION_ID);
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 7125796e7b986..7e40f96154d2f 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -20,6 +20,8 @@ package com.android.server.policy;
import static android.content.Context.SENSOR_SERVICE;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED;
+import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
import static com.android.server.devicestate.DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL;
import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_STATE;
@@ -327,7 +329,8 @@ public final class DeviceStateProviderImplTest {
+ " THERMAL_TEST\n"
+ " \n"
+ " FLAG_EMULATED_ONLY\n"
- + " FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL\n"
+ + " FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL\n"
+ + " FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE\n"
+ " \n"
+ " \n"
+ "\n";
@@ -354,7 +357,8 @@ public final class DeviceStateProviderImplTest {
new DeviceState(3, "OPENED", 0 /* flags */),
new DeviceState(4, "THERMAL_TEST",
DeviceState.FLAG_EMULATED_ONLY
- | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
mDeviceStateArrayCaptor.getValue());
// onStateChanged() should not be called because the provider has not yet been notified of
// the initial sensor state.
@@ -419,7 +423,8 @@ public final class DeviceStateProviderImplTest {
new DeviceState(3, "OPENED", 0 /* flags */),
new DeviceState(4, "THERMAL_TEST",
DeviceState.FLAG_EMULATED_ONLY
- | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
mDeviceStateArrayCaptor.getValue());
Mockito.clearInvocations(listener);
@@ -451,7 +456,65 @@ public final class DeviceStateProviderImplTest {
new DeviceState(3, "OPENED", 0 /* flags */),
new DeviceState(4, "THERMAL_TEST",
DeviceState.FLAG_EMULATED_ONLY
- | DeviceState.FLAG_DISABLE_WHEN_THERMAL_STATUS_CRITICAL) },
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
+ mDeviceStateArrayCaptor.getValue());
+ }
+
+ @Test
+ public void test_flagDisableWhenPowerSaveEnabled() throws Exception {
+ Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
+ when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
+ DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);
+
+ provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+ provider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
+ mDeviceStateArrayCaptor.getValue());
+ Mockito.clearInvocations(listener);
+
+ provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED));
+ Mockito.clearInvocations(listener);
+
+ // The THERMAL_TEST state should be disabled due to power save being enabled.
+ provider.onPowerSaveModeChanged(true /* isPowerSaveModeEnabled */);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */) },
+ mDeviceStateArrayCaptor.getValue());
+ Mockito.clearInvocations(listener);
+
+ // The THERMAL_TEST state should be re-enabled.
+ provider.onPowerSaveModeChanged(false /* isPowerSaveModeEnabled */);
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture(),
+ eq(SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED));
+ assertArrayEquals(
+ new DeviceState[]{
+ new DeviceState(1, "CLOSED", 0 /* flags */),
+ new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+ new DeviceState(3, "OPENED", 0 /* flags */),
+ new DeviceState(4, "THERMAL_TEST",
+ DeviceState.FLAG_EMULATED_ONLY
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
+ | DeviceState.FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE) },
mDeviceStateArrayCaptor.getValue());
}