From 67e97053c5efea9396be420e5b5015cd808ab417 Mon Sep 17 00:00:00 2001 From: Andrew Sapperstein Date: Wed, 14 Dec 2016 15:39:20 -0800 Subject: [PATCH] Show notif when device reaches throttling temp Adds logic to SystemUI that shows a notification alerting the user that their phone has reached a certain temperature and has throttled itself in order to cool down. The logic is controlled by a configuration resource: R.bool.config_showTemperatureWarning. If false, no action is taken. When true, PowerUI checks every 30 seconds if the current temp of HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN is greater than the throttling temp of HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN. If any one of the temperatures returned is greater, a notification is shown. Clicking on the notification will show a dialog explaining more details about what the device is doing in response to the high temperature. The notification will disappear once the temperature drops below the throttling temperature. In order to check the temperature in SystemUI, HardwarePropertiesManager has been updated to also allow any calls made by callers holding the signature-level DEVICE_POWER permission. Test: runtest systemui Bug: 30995038 Change-Id: I1b3f122341911c68e90c8a49ad35267ac382b356 (cherry picked from commit b7caf1d06d5e4ef21cadcc6a68d4b4c4552fb7bc) --- .../res/drawable/ic_device_thermostat_24.xml | 25 +++++++ packages/SystemUI/res/values/config.xml | 2 + packages/SystemUI/res/values/ids.xml | 1 + packages/SystemUI/res/values/strings.xml | 8 ++ .../power/PowerNotificationWarnings.java | 75 ++++++++++++++++--- .../com/android/systemui/power/PowerUI.java | 57 +++++++++++++- .../HardwarePropertiesManagerService.java | 15 ++-- 7 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 packages/SystemUI/res/drawable/ic_device_thermostat_24.xml 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."); } } }