diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml
new file mode 100644
index 0000000000000..1972f6e4427c9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9eea3750a8ecc..cf20c273fc1e0 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -284,4 +284,6 @@
false
+ false
+
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 94d79f27e67f9..cbac1dc4cf0f6 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -44,6 +44,7 @@
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3ceb331ff6963..7f68be04882cc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1694,4 +1694,12 @@
Notifications can\'t be silenced or blocked
+
+
+ Phone is getting warm
+
+ Some features limited while phone cools down
+
+ Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.
+
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index b831235d1179e..67072180cbfbc 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -49,7 +49,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG = PowerUI.TAG + ".Notification";
private static final boolean DEBUG = PowerUI.DEBUG;
- private static final String TAG_NOTIFICATION = "low_battery";
+ private static final String TAG_NOTIFICATION_BATTERY = "low_battery";
+ private static final String TAG_NOTIFICATION_TEMPERATURE = "high_temp";
private static final int SHOWING_NOTHING = 0;
private static final int SHOWING_WARNING = 1;
@@ -64,6 +65,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
private static final String ACTION_START_SAVER = "PNW.startSaver";
private static final String ACTION_DISMISSED_WARNING = "PNW.dismissedWarning";
+ private static final String ACTION_CLICKED_TEMP_WARNING = "PNW.clickedTempWarning";
+ private static final String ACTION_DISMISSED_TEMP_WARNING = "PNW.dismissedTempWarning";
private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -88,6 +91,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private boolean mPlaySound;
private boolean mInvalidCharger;
private SystemUIDialog mSaverConfirmation;
+ private boolean mTempWarning;
+ private SystemUIDialog mHighTempDialog;
public PowerNotificationWarnings(Context context, PhoneStatusBar phoneStatusBar) {
mContext = context;
@@ -103,6 +108,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]);
pw.print("mSaverConfirmation="); pw.println(mSaverConfirmation != null ? "not null" : null);
+ pw.print("mTempWarning="); pw.println(mTempWarning);
+ pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null);
}
@Override
@@ -127,7 +134,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
showWarningNotification();
mShowing = SHOWING_WARNING;
} else {
- mNoMan.cancelAsUser(TAG_NOTIFICATION, R.id.notification_power, UserHandle.ALL);
+ mNoMan.cancelAsUser(TAG_NOTIFICATION_BATTERY, R.id.notification_power, UserHandle.ALL);
mShowing = SHOWING_NOTHING;
}
}
@@ -146,7 +153,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
com.android.internal.R.color.system_notification_accent_color));
SystemUI.overrideNotificationAppName(mContext, nb);
final Notification n = nb.build();
- mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, n, UserHandle.ALL);
+ mNoMan.notifyAsUser(TAG_NOTIFICATION_BATTERY, R.id.notification_power, n, UserHandle.ALL);
}
private void showWarningNotification() {
@@ -176,12 +183,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
mPlaySound = false;
}
SystemUI.overrideNotificationAppName(mContext, nb);
- mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, nb.build(), UserHandle.ALL);
- }
-
- private PendingIntent pendingActivity(Intent intent) {
- return PendingIntent.getActivityAsUser(mContext,
- 0, intent, 0, null, UserHandle.CURRENT);
+ mNoMan.notifyAsUser(
+ TAG_NOTIFICATION_BATTERY, R.id.notification_power, nb.build(), UserHandle.ALL);
}
private PendingIntent pendingBroadcast(String action) {
@@ -202,6 +205,53 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
return mInvalidCharger;
}
+ @Override
+ public void dismissTemperatureWarning() {
+ if (!mTempWarning) {
+ return;
+ }
+ mTempWarning = false;
+ mNoMan.cancelAsUser(
+ TAG_NOTIFICATION_TEMPERATURE, R.id.notification_temperature, UserHandle.ALL);
+ }
+
+ @Override
+ public void showTemperatureWarning() {
+ if (mTempWarning) {
+ return;
+ }
+ mTempWarning = true;
+ final Notification.Builder nb = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_device_thermostat_24)
+ .setWhen(0)
+ .setShowWhen(false)
+ .setContentTitle(mContext.getString(R.string.high_temp_title))
+ .setContentText(mContext.getString(R.string.high_temp_notif_message))
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING))
+ .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.battery_saver_mode_color));
+ SystemUI.overrideNotificationAppName(mContext, nb);
+ final Notification n = nb.build();
+ mNoMan.notifyAsUser(
+ TAG_NOTIFICATION_TEMPERATURE, R.id.notification_temperature, n, UserHandle.ALL);
+
+ }
+
+ private void showTemperatureDialog() {
+ if (mHighTempDialog != null) return;
+ final SystemUIDialog d = new SystemUIDialog(mContext);
+ d.setTitle(R.string.high_temp_title);
+ d.setMessage(R.string.high_temp_dialog_message);
+ d.setPositiveButton(com.android.internal.R.string.ok, null);
+ d.setShowForAllUsers(true);
+ d.setOnDismissListener(dialog -> mHighTempDialog = null);
+ d.show();
+ mHighTempDialog = d;
+ }
+
@Override
public void updateLowBatteryWarning() {
updateNotification();
@@ -315,6 +365,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
filter.addAction(ACTION_START_SAVER);
filter.addAction(ACTION_DISMISSED_WARNING);
+ filter.addAction(ACTION_CLICKED_TEMP_WARNING);
+ filter.addAction(ACTION_DISMISSED_TEMP_WARNING);
mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
android.Manifest.permission.STATUS_BAR_SERVICE, mHandler);
}
@@ -331,6 +383,11 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
showStartSaverConfirmation();
} else if (action.equals(ACTION_DISMISSED_WARNING)) {
dismissLowBatteryWarning();
+ } else if (ACTION_CLICKED_TEMP_WARNING.equals(action)) {
+ dismissTemperatureWarning();
+ showTemperatureDialog();
+ } else if (ACTION_DISMISSED_TEMP_WARNING.equals(action)) {
+ dismissTemperatureWarning();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 109a45660754a..a7598685c6282 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -24,13 +24,15 @@ import android.content.IntentFilter;
import android.database.ContentObserver;
import android.os.BatteryManager;
import android.os.Handler;
+import android.os.HardwarePropertiesManager;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.format.DateUtils;
import android.util.Log;
import android.util.Slog;
-
+import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -41,11 +43,13 @@ import java.util.Arrays;
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
private final Handler mHandler = new Handler();
private final Receiver mReceiver = new Receiver();
private PowerManager mPowerManager;
+ private HardwarePropertiesManager mHardwarePropertiesManager;
private WarningsUI mWarnings;
private int mBatteryLevel = 100;
private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -57,8 +61,12 @@ public class PowerUI extends SystemUI {
private long mScreenOffTime = -1;
+ private float mThrottlingTemp;
+
public void start() {
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mHardwarePropertiesManager = (HardwarePropertiesManager)
+ mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
mWarnings = new PowerNotificationWarnings(mContext, getComponent(PhoneStatusBar.class));
@@ -74,6 +82,8 @@ public class PowerUI extends SystemUI {
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
mReceiver.init();
+
+ initTemperatureWarning();
}
void updateBatteryWarningLevels() {
@@ -209,6 +219,47 @@ public class PowerUI extends SystemUI {
}
};
+ private void initTemperatureWarning() {
+ if (!mContext.getResources().getBoolean(R.bool.config_showTemperatureWarning)) {
+ return;
+ }
+
+ // Get the throttling temperature. No need to check if we're not throttling.
+ float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures(
+ HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
+ HardwarePropertiesManager.TEMPERATURE_THROTTLING);
+ if (throttlingTemps == null
+ || throttlingTemps.length == 0
+ || throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) {
+ return;
+ }
+ mThrottlingTemp = throttlingTemps[0];
+
+ // We have passed all of the checks, start checking the temp
+ updateTemperatureWarning();
+ }
+
+ private void updateTemperatureWarning() {
+ // TODO: Add VR mode check
+ float[] temps = mHardwarePropertiesManager.getDeviceTemperatures(
+ HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
+ HardwarePropertiesManager.TEMPERATURE_CURRENT);
+ boolean shouldShowTempWarning = false;
+ for (float temp : temps) {
+ if (temp >= mThrottlingTemp) {
+ shouldShowTempWarning = true;
+ break;
+ }
+ }
+ if (shouldShowTempWarning) {
+ mWarnings.showTemperatureWarning();
+ } else {
+ mWarnings.dismissTemperatureWarning();
+ }
+
+ mHandler.postDelayed(this::updateTemperatureWarning, TEMPERATURE_INTERVAL);
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print("mLowBatteryAlertCloseLevel=");
pw.println(mLowBatteryAlertCloseLevel);
@@ -235,6 +286,8 @@ public class PowerUI extends SystemUI {
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
pw.print("bucket: ");
pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
+ pw.print("mThrottlingTemp=");
+ pw.println(Float.toString(mThrottlingTemp));
mWarnings.dump(pw);
}
@@ -246,6 +299,8 @@ public class PowerUI extends SystemUI {
void showInvalidChargerWarning();
void updateLowBatteryWarning();
boolean isInvalidChargerWarningShowing();
+ void dismissTemperatureWarning();
+ void showTemperatureWarning();
void dump(PrintWriter pw);
void userSwitched();
}
diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
index 23cf64a031af3..36a16cd0a18b6 100644
--- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java
+++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import android.Manifest;
+import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -80,8 +82,9 @@ public class HardwarePropertiesManagerService extends IHardwarePropertiesManager
*
* @param callingPackage The calling package name.
*
- * @throws SecurityException if something other than the profile or device owner, or the
- * current VR service tries to retrieve information provided by this service.
+ * @throws SecurityException if something other than the profile or device owner, the
+ * current VR service, or a caller holding the {@link Manifest.permission#DEVICE_POWER}
+ * permission tries to retrieve information provided by this service.
*/
private void enforceHardwarePropertiesRetrievalAllowed(String callingPackage)
throws SecurityException {
@@ -100,9 +103,11 @@ public class HardwarePropertiesManagerService extends IHardwarePropertiesManager
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (!dpm.isDeviceOwnerApp(callingPackage) && !dpm.isProfileOwnerApp(callingPackage)
- && !vrService.isCurrentVrListener(callingPackage, userId)) {
- throw new SecurityException("The caller is not a device or profile owner or bound "
- + "VrListenerService.");
+ && !vrService.isCurrentVrListener(callingPackage, userId)
+ && mContext.checkCallingOrSelfPermission(Manifest.permission.DEVICE_POWER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("The caller is not a device or profile owner, bound "
+ + "VrListenerService, or holding the DEVICE_POWER permission.");
}
}
}