Merge "Added dialog to notify user of thermal shutdown" into oc-dev

This commit is contained in:
Salvador Martinez
2017-05-01 16:45:25 +00:00
committed by Android (Google) Code Review
8 changed files with 259 additions and 27 deletions

View File

@@ -55,6 +55,7 @@ interface IPowerManager
void rebootSafeMode(boolean confirm, boolean wait);
void shutdown(boolean confirm, String reason, boolean wait);
void crash(String message);
int getLastShutdownReason();
void setStayOnSetting(int val);
void boostScreenBrightness(long time);

View File

@@ -16,10 +16,13 @@
package android.os;
import android.annotation.IntDef;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.content.Context;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This class gives you control of the power state of the device.
@@ -432,6 +435,49 @@ public final class PowerManager {
*/
public static final String SHUTDOWN_USER_REQUESTED = "userrequested";
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SHUTDOWN_REASON_UNKNOWN,
SHUTDOWN_REASON_SHUTDOWN,
SHUTDOWN_REASON_REBOOT,
SHUTDOWN_REASON_USER_REQUESTED,
SHUTDOWN_REASON_THERMAL_SHUTDOWN
})
public @interface ShutdownReason {}
/**
* constant for shutdown reason being unknown.
* @hide
*/
public static final int SHUTDOWN_REASON_UNKNOWN = 0;
/**
* constant for shutdown reason being normal shutdown.
* @hide
*/
public static final int SHUTDOWN_REASON_SHUTDOWN = 1;
/**
* constant for shutdown reason being reboot.
* @hide
*/
public static final int SHUTDOWN_REASON_REBOOT = 2;
/**
* constant for shutdown reason being user requested.
* @hide
*/
public static final int SHUTDOWN_REASON_USER_REQUESTED = 3;
/**
* constant for shutdown reason being overheating.
* @hide
*/
public static final int SHUTDOWN_REASON_THERMAL_SHUTDOWN = 4;
final Context mContext;
final IPowerManager mService;
final Handler mHandler;
@@ -1084,6 +1130,22 @@ public final class PowerManager {
com.android.internal.R.bool.config_sustainedPerformanceModeSupported);
}
/**
* Returns the reason the phone was last shutdown. Calling app must have the
* {@link android.Manifest.permission#DEVICE_POWER} permission to request this information.
* @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could
* not be accessed.
* @hide
*/
@ShutdownReason
public int getLastShutdownReason() {
try {
return mService.getLastShutdownReason();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
* This broadcast is only sent to registered receivers.

View File

@@ -36,6 +36,7 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.util.Slog;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -71,6 +72,10 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
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 String ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING =
"PNW.clickedThermalShutdownWarning";
private static final String ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING =
"PNW.dismissedThermalShutdownWarning";
private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -95,8 +100,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private boolean mPlaySound;
private boolean mInvalidCharger;
private SystemUIDialog mSaverConfirmation;
private boolean mTempWarning;
private boolean mHighTempWarning;
private SystemUIDialog mHighTempDialog;
private SystemUIDialog mThermalShutdownDialog;
public PowerNotificationWarnings(Context context, NotificationManager notificationManager,
StatusBar statusBar) {
@@ -113,8 +119,10 @@ 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("mHighTempWarning="); pw.println(mHighTempWarning);
pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null);
pw.print("mThermalShutdownDialog=");
pw.println(mThermalShutdownDialog != null ? "not null" : null);
}
@Override
@@ -212,29 +220,29 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
}
@Override
public void dismissTemperatureWarning() {
if (!mTempWarning) {
public void dismissHighTemperatureWarning() {
if (!mHighTempWarning) {
return;
}
mTempWarning = false;
dismissTemperatureWarningInternal();
mHighTempWarning = false;
dismissHighTemperatureWarningInternal();
}
/**
* Internal only version of {@link #dismissTemperatureWarning()} that simply dismisses
* Internal only version of {@link #dismissHighTemperatureWarning()} that simply dismisses
* the notification. As such, the notification will not show again until
* {@link #dismissTemperatureWarning()} is called.
* {@link #dismissHighTemperatureWarning()} is called.
*/
private void dismissTemperatureWarningInternal() {
private void dismissHighTemperatureWarningInternal() {
mNoMan.cancelAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, UserHandle.ALL);
}
@Override
public void showTemperatureWarning() {
if (mTempWarning) {
public void showHighTemperatureWarning() {
if (mHighTempWarning) {
return;
}
mTempWarning = true;
mHighTempWarning = true;
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.ALERTS)
.setSmallIcon(R.drawable.ic_device_thermostat_24)
@@ -249,10 +257,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
SystemUI.overrideNotificationAppName(mContext, nb);
final Notification n = nb.build();
mNoMan.notifyAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, n, UserHandle.ALL);
}
private void showTemperatureDialog() {
private void showHighTemperatureDialog() {
if (mHighTempDialog != null) return;
final SystemUIDialog d = new SystemUIDialog(mContext);
d.setIconAttribute(android.R.attr.alertDialogIcon);
@@ -265,6 +272,44 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
mHighTempDialog = d;
}
@VisibleForTesting
void dismissThermalShutdownWarning() {
mNoMan.cancelAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_THERMAL_SHUTDOWN, UserHandle.ALL);
}
private void showThermalShutdownDialog() {
if (mThermalShutdownDialog != null) return;
final SystemUIDialog d = new SystemUIDialog(mContext);
d.setIconAttribute(android.R.attr.alertDialogIcon);
d.setTitle(R.string.thermal_shutdown_title);
d.setMessage(R.string.thermal_shutdown_dialog_message);
d.setPositiveButton(com.android.internal.R.string.ok, null);
d.setShowForAllUsers(true);
d.setOnDismissListener(dialog -> mThermalShutdownDialog = null);
d.show();
mThermalShutdownDialog = d;
}
@Override
public void showThermalShutdownWarning() {
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.ALERTS)
.setSmallIcon(R.drawable.ic_device_thermostat_24)
.setWhen(0)
.setShowWhen(false)
.setContentTitle(mContext.getString(R.string.thermal_shutdown_title))
.setContentText(mContext.getString(R.string.thermal_shutdown_message))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(pendingBroadcast(ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING))
.setDeleteIntent(
pendingBroadcast(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING))
.setColor(Utils.getColorAttr(mContext, android.R.attr.colorError));
SystemUI.overrideNotificationAppName(mContext, nb);
final Notification n = nb.build();
mNoMan.notifyAsUser(
TAG_TEMPERATURE, SystemMessage.NOTE_THERMAL_SHUTDOWN, n, UserHandle.ALL);
}
@Override
public void updateLowBatteryWarning() {
updateNotification();
@@ -380,6 +425,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
filter.addAction(ACTION_DISMISSED_WARNING);
filter.addAction(ACTION_CLICKED_TEMP_WARNING);
filter.addAction(ACTION_DISMISSED_TEMP_WARNING);
filter.addAction(ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING);
filter.addAction(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING);
mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
android.Manifest.permission.STATUS_BAR_SERVICE, mHandler);
}
@@ -397,10 +444,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
} else if (action.equals(ACTION_DISMISSED_WARNING)) {
dismissLowBatteryWarning();
} else if (ACTION_CLICKED_TEMP_WARNING.equals(action)) {
dismissTemperatureWarningInternal();
showTemperatureDialog();
dismissHighTemperatureWarningInternal();
showHighTemperatureDialog();
} else if (ACTION_DISMISSED_TEMP_WARNING.equals(action)) {
dismissTemperatureWarningInternal();
dismissHighTemperatureWarningInternal();
} else if (ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING.equals(action)) {
dismissThermalShutdownWarning();
showThermalShutdownDialog();
} else if (ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING.equals(action)) {
dismissThermalShutdownWarning();
}
}
}

View File

@@ -31,6 +31,7 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Slog;
@@ -93,6 +94,10 @@ public class PowerUI extends SystemUI {
updateBatteryWarningLevels();
mReceiver.init();
// Check to see if we need to let the user know that the phone previously shut down due
// to the temperature being too high.
showThermalShutdownDialog();
initTemperatureWarning();
}
@@ -256,6 +261,13 @@ public class PowerUI extends SystemUI {
updateTemperatureWarning();
}
private void showThermalShutdownDialog() {
if (mPowerManager.getLastShutdownReason()
== PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
mWarnings.showThermalShutdownWarning();
}
}
private void updateTemperatureWarning() {
float[] temps = mHardwarePropertiesManager.getDeviceTemperatures(
HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN,
@@ -268,9 +280,9 @@ public class PowerUI extends SystemUI {
if (statusBar != null && !statusBar.isDeviceInVrMode()
&& temp >= mThresholdTemp) {
logAtTemperatureThreshold(temp);
mWarnings.showTemperatureWarning();
mWarnings.showHighTemperatureWarning();
} else {
mWarnings.dismissTemperatureWarning();
mWarnings.dismissHighTemperatureWarning();
}
}
@@ -369,8 +381,9 @@ public class PowerUI extends SystemUI {
void showInvalidChargerWarning();
void updateLowBatteryWarning();
boolean isInvalidChargerWarningShowing();
void dismissTemperatureWarning();
void showTemperatureWarning();
void dismissHighTemperatureWarning();
void showHighTemperatureWarning();
void showThermalShutdownWarning();
void dump(PrintWriter pw);
void userSwitched();
}

View File

@@ -24,7 +24,6 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -129,17 +128,32 @@ public class PowerNotificationWarningsTest extends SysuiTestCase {
}
@Test
public void testShowTemperatureWarning_NotifyAsUser() {
mPowerNotificationWarnings.showTemperatureWarning();
public void testShowHighTemperatureWarning_NotifyAsUser() {
mPowerNotificationWarnings.showHighTemperatureWarning();
verify(mMockNotificationManager, times(1))
.notifyAsUser(anyString(), eq(SystemMessage.NOTE_HIGH_TEMP), any(), any());
}
@Test
public void testDismissTemperatureWarning_CancelAsUser() {
mPowerNotificationWarnings.showTemperatureWarning();
mPowerNotificationWarnings.dismissTemperatureWarning();
public void testDismissHighTemperatureWarning_CancelAsUser() {
mPowerNotificationWarnings.showHighTemperatureWarning();
mPowerNotificationWarnings.dismissHighTemperatureWarning();
verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
eq(SystemMessage.NOTE_HIGH_TEMP), any());
}
@Test
public void testShowThermalShutdownWarning_NotifyAsUser() {
mPowerNotificationWarnings.showThermalShutdownWarning();
verify(mMockNotificationManager, times(1))
.notifyAsUser(anyString(), eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any(), any());
}
@Test
public void testDismissThermalShutdownWarning_CancelAsUser() {
mPowerNotificationWarnings.showThermalShutdownWarning();
mPowerNotificationWarnings.dismissThermalShutdownWarning();
verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
}
}

View File

@@ -65,6 +65,7 @@ import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.EventLog;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -91,6 +92,10 @@ import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.power.BatterySaverPolicy.ServiceType;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import libcore.util.Objects;
import java.io.FileDescriptor;
@@ -191,6 +196,12 @@ public final class PowerManagerService extends SystemService
// System property indicating that the screen should remain off until an explicit user action
private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
// Possible reasons for shutting down for use in data/misc/reboot/last_shutdown_reason
private static final String REASON_SHUTDOWN = "shutdown";
private static final String REASON_REBOOT = "reboot";
private static final String REASON_USERREQUESTED = "userrequested";
private static final String REASON_THERMAL_SHUTDOWN = "thermal-shutdown";
private static final String TRACE_SCREEN_ON = "Screen turning on";
/** If turning screen on takes more than this long, we show a warning on logcat. */
@@ -204,6 +215,9 @@ public final class PowerManagerService extends SystemService
private static final int HALT_MODE_REBOOT = 1;
private static final int HALT_MODE_REBOOT_SAFE_MODE = 2;
// File location for last reboot reason
private static final String LAST_REBOOT_LOCATION = "/data/misc/reboot/last_reboot_reason";
private final Context mContext;
private final ServiceThread mHandlerThread;
private final PowerManagerHandler mHandler;
@@ -4339,6 +4353,25 @@ public final class PowerManagerService extends SystemService
}
}
/**
* Gets the reason for the last time the phone had to reboot.
*
* @return The reason the phone last shut down as an int or
* {@link PowerManager.SHUTDOWN_REASON_UNKNOWN} if the file could not be opened.
*/
@Override // Binder call
public int getLastShutdownReason() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.DEVICE_POWER, null);
final long ident = Binder.clearCallingIdentity();
try {
return getLastShutdownReasonInternal(new File(LAST_REBOOT_LOCATION));
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Reboots the device.
*
@@ -4566,6 +4599,28 @@ public final class PowerManagerService extends SystemService
}
}
@VisibleForTesting
int getLastShutdownReasonInternal(File lastRebootReason) {
String line = "";
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(lastRebootReason))){
line = bufferedReader.readLine();
} catch (IOException e) {
Slog.e(TAG, "Failed to read last_reboot_reason file", e);
}
switch (line) {
case REASON_SHUTDOWN:
return PowerManager.SHUTDOWN_REASON_SHUTDOWN;
case REASON_REBOOT:
return PowerManager.SHUTDOWN_REASON_REBOOT;
case REASON_USERREQUESTED:
return PowerManager.SHUTDOWN_REASON_USER_REQUESTED;
case REASON_THERMAL_SHUTDOWN:
return PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN;
default:
return PowerManager.SHUTDOWN_REASON_UNKNOWN;
}
}
private final class LocalService extends PowerManagerInternal {
@Override
public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) {

View File

@@ -16,10 +16,19 @@
package com.android.server.power;
import android.content.Context;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.PowerManager;
import android.os.PowerSaveState;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -35,11 +44,16 @@ public class PowerManagerServiceTest extends AndroidTestCase {
private static final float PRECISION = 0.001f;
private static final float BRIGHTNESS_FACTOR = 0.7f;
private static final boolean BATTERY_SAVER_ENABLED = true;
private static final String LAST_REBOOT_REASON = "last_reboot_reason";
private @Mock BatterySaverPolicy mBatterySaverPolicy;
private PowerManagerService mService;
private PowerSaveState mPowerSaveState;
private DisplayPowerRequest mDisplayPowerRequest;
private File mTempReason;
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
public void setUp() throws Exception {
super.setUp();
@@ -54,6 +68,8 @@ public class PowerManagerServiceTest extends AndroidTestCase {
.thenReturn(mPowerSaveState);
mDisplayPowerRequest = new DisplayPowerRequest();
mService = new PowerManagerService(getContext(), mBatterySaverPolicy);
temporaryFolder.create();
mTempReason = temporaryFolder.newFile(LAST_REBOOT_REASON);
}
@SmallTest
@@ -63,4 +79,17 @@ public class PowerManagerServiceTest extends AndroidTestCase {
assertThat(mDisplayPowerRequest.screenLowPowerBrightnessFactor)
.isWithin(PRECISION).of(BRIGHTNESS_FACTOR);
}
@SmallTest
public void testGetLastShutdownReasonInternal() {
try {
FileWriter writer = new FileWriter(mTempReason);
writer.append("thermal-shutdown\n");
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
int reason = mService.getLastShutdownReasonInternal(mTempReason);
assertThat(reason).isEqualTo(PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN);
}
}

View File

@@ -18,6 +18,7 @@ package com.android.layoutlib.bridge.android;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.PowerManager;
import android.os.PowerSaveState;
import android.os.RemoteException;
import android.os.WorkSource;
@@ -170,4 +171,9 @@ public class BridgePowerManager implements IPowerManager {
public boolean isScreenBrightnessBoosted() throws RemoteException {
return false;
}
@Override
public int getLastShutdownReason() {
return PowerManager.SHUTDOWN_REASON_UNKNOWN;
}
}