diff options
8 files changed, 188 insertions, 14 deletions
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 000000000000..1972f6e4427c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml @@ -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> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 0f5d37ea3691..c025f932adf8 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -290,4 +290,6 @@ <bool name="quick_settings_show_full_alarm">false</bool> + <bool name="config_showTemperatureWarning">false</bool> + </resources> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 2a895f98eefd..7ef2abdf9b2a 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -53,6 +53,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"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 194653ba15ce..d7033b26f563 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1766,4 +1766,12 @@ <!-- Tuner string --> <string name="default_theme" translatable="false">Default</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> diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 8a500c3219f0..7a45dd299151 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -50,7 +50,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; @@ -65,6 +66,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) @@ -89,6 +92,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, NotificationManager notificationManager, PhoneStatusBar phoneStatusBar) { @@ -105,6 +110,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 @@ -129,7 +136,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; } } @@ -148,7 +155,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() { @@ -178,12 +185,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) { @@ -205,6 +208,53 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { } @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(); } @@ -317,6 +367,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); } @@ -333,6 +385,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 b651f2d99523..a7cbb3d94557 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -25,12 +25,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; import java.io.FileDescriptor; @@ -40,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; @@ -56,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, @@ -76,6 +85,8 @@ public class PowerUI extends SystemUI { false, obs, UserHandle.USER_ALL); updateBatteryWarningLevels(); mReceiver.init(); + + initTemperatureWarning(); } void updateBatteryWarningLevels() { @@ -211,6 +222,53 @@ 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() { + PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class); + if (phoneStatusBar != null && phoneStatusBar.isDeviceInVrMode()) { + // ensure the warning isn't showing, since VR shows its own warning + mWarnings.dismissTemperatureWarning(); + } else { + 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(); + } + } + + // TODO: skip this when in VR mode since we already get a callback + mHandler.postDelayed(this::updateTemperatureWarning, TEMPERATURE_INTERVAL); + } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print("mLowBatteryAlertCloseLevel="); pw.println(mLowBatteryAlertCloseLevel); @@ -237,6 +295,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); } @@ -248,6 +308,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/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index 5c87fb01e23c..7070961e39b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java @@ -117,4 +117,18 @@ public class PowerNotificationWarningsTest extends SysuiTestCase { .notifyAsUser(anyString(), anyInt(), captor.capture(), any()); assertNotEqual(null, captor.getValue().sound); } + + @Test + public void testShowTemperatureWarning_NotifyAsUser() { + mPowerNotificationWarnings.showTemperatureWarning(); + verify(mMockNotificationManager, times(1)) + .notifyAsUser(anyString(), anyInt(), any(), any()); + } + + @Test + public void testDismissTemperatureWarning_CancelAsUser() { + mPowerNotificationWarnings.showTemperatureWarning(); + mPowerNotificationWarnings.dismissTemperatureWarning(); + verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(), anyInt(), any()); + } } diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java index 23cf64a031af..36a16cd0a18b 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."); } } } |