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 b7caf1d06d)
This commit is contained in:
25
packages/SystemUI/res/drawable/ic_device_thermostat_24.xml
Normal file
25
packages/SystemUI/res/drawable/ic_device_thermostat_24.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
Copyright (C) 2016 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M15,13L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v8c-1.21,0.91 -2,2.37 -2,4 0,2.76 2.24,5 5,5s5,-2.24 5,-5c0,-1.63 -0.79,-3.09 -2,-4zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1h-1v1h1v2h-1v1h1v2h-2L11,5z"/>
|
||||
</vector>
|
||||
@@ -284,4 +284,6 @@
|
||||
|
||||
<bool name="quick_settings_show_full_alarm">false</bool>
|
||||
|
||||
<bool name="config_showTemperatureWarning">false</bool>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
<item type="id" name="notification_screenshot"/>
|
||||
<item type="id" name="notification_hidden"/>
|
||||
<item type="id" name="notification_volumeui"/>
|
||||
<item type="id" name="notification_temperature"/>
|
||||
<item type="id" name="transformation_start_x_tag"/>
|
||||
<item type="id" name="transformation_start_y_tag"/>
|
||||
<item type="id" name="transformation_start_scale_x_tag"/>
|
||||
|
||||
@@ -1694,4 +1694,12 @@
|
||||
<!-- Label that replaces other notification controls when the notification is from the system
|
||||
and cannot be silenced (see @string/show_silently) or blocked (see @string/block) -->
|
||||
<string name="cant_silence_or_block">Notifications can\'t be silenced or blocked</string>
|
||||
|
||||
<!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] -->
|
||||
<string name="high_temp_title">Phone is getting warm</string>
|
||||
<!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=70] -->
|
||||
<string name="high_temp_notif_message">Some features limited while phone cools down</string>
|
||||
<!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] -->
|
||||
<string name="high_temp_dialog_message">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.</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user