blob: 1534653bc1ddfafc82df2f882038953c643e7b4e [file] [log] [blame]
/*
* Copyright (C) 2008 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.
*/
package com.android.systemui.power;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Temperature;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Slog;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.CoreStartable;
import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.concurrent.Future;
import javax.inject.Inject;
@SysUISingleton
public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
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 static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
private static final int CHARGE_CYCLE_PERCENT_RESET = 30;
public static final int NO_ESTIMATE_AVAILABLE = -1;
private static final String BOOT_COUNT_KEY = "boot_count";
private static final String PREFS = "powerui_prefs";
private final Handler mHandler = new Handler();
@VisibleForTesting
final Receiver mReceiver = new Receiver();
private final PowerManager mPowerManager;
private final WarningsUI mWarnings;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final UserTracker mUserTracker;
private InattentiveSleepWarningView mOverlayView;
private final Configuration mLastConfiguration = new Configuration();
private int mPlugType = 0;
private int mInvalidCharger = 0;
private final EnhancedEstimates mEnhancedEstimates;
private Future mLastShowWarningTask;
private boolean mEnableSkinTemperatureWarning;
private boolean mEnableUsbTemperatureAlarm;
private int mLowBatteryAlertCloseLevel;
private final int[] mLowBatteryReminderLevels = new int[2];
private long mScreenOffTime = -1;
@VisibleForTesting boolean mLowWarningShownThisChargeCycle;
@VisibleForTesting boolean mSevereWarningShownThisChargeCycle;
@VisibleForTesting BatteryStateSnapshot mCurrentBatteryStateSnapshot;
@VisibleForTesting BatteryStateSnapshot mLastBatteryStateSnapshot;
@VisibleForTesting IThermalService mThermalService;
@VisibleForTesting int mBatteryLevel = 100;
@VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
private boolean mInVrMode;
private IThermalEventListener mSkinThermalEventListener;
private IThermalEventListener mUsbThermalEventListener;
private final Context mContext;
private final BroadcastDispatcher mBroadcastDispatcher;
private final CommandQueue mCommandQueue;
@Nullable
private final IVrManager mVrManager;
private final WakefulnessLifecycle.Observer mWakefulnessObserver =
new WakefulnessLifecycle.Observer() {
@Override
public void onStartedWakingUp() {
mScreenOffTime = -1;
}
@Override
public void onFinishedGoingToSleep() {
mScreenOffTime = SystemClock.elapsedRealtime();
}
};
private final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@Override
public void onUserChanged(int newUser, @NonNull Context userContext) {
mWarnings.userSwitched();
}
};
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
public void onVrStateChanged(boolean enabled) {
mInVrMode = enabled;
}
};
@Inject
public PowerUI(
Context context,
BroadcastDispatcher broadcastDispatcher,
CommandQueue commandQueue,
@Nullable IVrManager vrManager,
WarningsUI warningsUI,
EnhancedEstimates enhancedEstimates,
WakefulnessLifecycle wakefulnessLifecycle,
PowerManager powerManager,
UserTracker userTracker) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
mVrManager = vrManager;
mWarnings = warningsUI;
mEnhancedEstimates = enhancedEstimates;
mPowerManager = powerManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
mUserTracker = userTracker;
}
public void start() {
mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
mLastConfiguration.setTo(mContext.getResources().getConfiguration());
ContentObserver obs = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateBatteryWarningLevels();
}
};
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
mReceiver.init();
mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
// Check to see if we need to let the user know that the phone previously shut down due
// to the temperature being too high.
showWarnOnThermalShutdown();
// Register an observer to configure mEnableSkinTemperatureWarning and perform the
// registration of skin thermal event listener upon Settings change.
resolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.SHOW_TEMPERATURE_WARNING),
false /*notifyForDescendants*/,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
doSkinThermalEventListenerRegistration();
}
});
// Register an observer to configure mEnableUsbTemperatureAlarm and perform the
// registration of usb thermal event listener upon Settings change.
resolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.SHOW_USB_TEMPERATURE_ALARM),
false /*notifyForDescendants*/,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
doUsbThermalEventListenerRegistration();
}
});
initThermalEventListeners();
mCommandQueue.addCallback(this);
if (mVrManager != null) {
try {
mVrManager.registerListener(mVrStateCallbacks);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
final int mask = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
// Safe to modify mLastConfiguration here as it's only updated by the main thread (here).
if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) {
mHandler.post(this::initThermalEventListeners);
}
}
void updateBatteryWarningLevels() {
int critLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
int warnLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
if (warnLevel < critLevel) {
warnLevel = critLevel;
}
mLowBatteryReminderLevels[0] = warnLevel;
mLowBatteryReminderLevels[1] = critLevel;
mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
+ mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
}
/**
* Buckets the battery level.
*
* The code in this function is a little weird because I couldn't comprehend
* the bucket going up when the battery level was going down. --joeo
*
* 1 means that the battery is "ok"
* 0 means that the battery is between "ok" and what we should warn about.
* less than 0 means that the battery is low, -1 means the battery is reaching warning level,
* -2 means the battery is reaching severe level.
*/
private int findBatteryLevelBucket(int level) {
if (level >= mLowBatteryAlertCloseLevel) {
return 1;
}
if (level > mLowBatteryReminderLevels[0]) {
return 0;
}
final int N = mLowBatteryReminderLevels.length;
for (int i=N-1; i>=0; i--) {
if (level <= mLowBatteryReminderLevels[i]) {
return -1-i;
}
}
throw new RuntimeException("not possible!");
}
@VisibleForTesting
final class Receiver extends BroadcastReceiver {
private boolean mHasReceivedBattery = false;
public void init() {
// Register for Intent broadcasts for...
IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
// Force get initial values. Relying on Sticky behavior until API for getting info.
if (!mHasReceivedBattery) {
// Get initial state
Intent intent = mContext.registerReceiver(
null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
);
if (intent != null) {
onReceive(mContext, intent);
}
}
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
ThreadUtils.postOnBackgroundThread(() -> {
if (mPowerManager.isPowerSaveMode()) {
mWarnings.dismissLowBatteryWarning();
}
});
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
mHasReceivedBattery = true;
final int oldBatteryLevel = mBatteryLevel;
mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
final int oldBatteryStatus = mBatteryStatus;
mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
final int oldPlugType = mPlugType;
mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
final int oldInvalidCharger = mInvalidCharger;
mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;
final boolean plugged = mPlugType != 0;
final boolean oldPlugged = oldPlugType != 0;
int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
int bucket = findBatteryLevelBucket(mBatteryLevel);
if (DEBUG) {
Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
+ " .. " + mLowBatteryReminderLevels[0]
+ " .. " + mLowBatteryReminderLevels[1]);
Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
}
mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Slog.d(TAG, "showing invalid charger warning");
mWarnings.showInvalidChargerWarning();
return;
} else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
mWarnings.dismissInvalidChargerWarning();
} else if (mWarnings.isInvalidChargerWarningShowing()) {
// if invalid charger is showing, don't show low battery
if (DEBUG) {
Slog.d(TAG, "Bad Charger");
}
return;
}
// Show the correct version of low battery warning if needed
if (mLastShowWarningTask != null) {
mLastShowWarningTask.cancel(true);
if (DEBUG) {
Slog.d(TAG, "cancelled task");
}
}
mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
maybeShowBatteryWarningV2(
plugged, bucket);
});
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
}
}
protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();
// Stick current battery state into an immutable container to determine if we should show
// a warning.
if (DEBUG) {
Slog.d(TAG, "evaluating which notification to show");
}
if (hybridEnabled) {
if (DEBUG) {
Slog.d(TAG, "using hybrid");
}
Estimate estimate = refreshEstimateIfNeeded();
mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
estimate.getAverageDischargeTime(),
mEnhancedEstimates.getSevereWarningThreshold(),
mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage(),
mEnhancedEstimates.getLowWarningEnabled());
} else {
if (DEBUG) {
Slog.d(TAG, "using standard");
}
mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
mLowBatteryReminderLevels[0]);
}
mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
}
// updates the time estimate if we don't have one or battery level has changed.
@VisibleForTesting
Estimate refreshEstimateIfNeeded() {
if (mLastBatteryStateSnapshot == null
|| mLastBatteryStateSnapshot.getTimeRemainingMillis() == NO_ESTIMATE_AVAILABLE
|| mBatteryLevel != mLastBatteryStateSnapshot.getBatteryLevel()) {
final Estimate estimate = mEnhancedEstimates.getEstimate();
if (DEBUG) {
Slog.d(TAG, "updated estimate: " + estimate.getEstimateMillis());
}
return estimate;
}
return new Estimate(mLastBatteryStateSnapshot.getTimeRemainingMillis(),
mLastBatteryStateSnapshot.isBasedOnUsage(),
mLastBatteryStateSnapshot.getAverageTimeToDischargeMillis());
}
@VisibleForTesting
void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
BatteryStateSnapshot lastSnapshot) {
// if we are now over 30% battery, we can trigger hybrid notification again
if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET) {
mLowWarningShownThisChargeCycle = false;
mSevereWarningShownThisChargeCycle = false;
if (DEBUG) {
Slog.d(TAG, "Charge cycle reset! Can show warnings again");
}
}
final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
|| lastSnapshot.getPlugged();
if (shouldShowHybridWarning(currentSnapshot)) {
mWarnings.showLowBatteryWarning(playSound);
// mark if we've already shown a warning this cycle. This will prevent the notification
// trigger from spamming users by only showing low/critical warnings once per cycle
if (currentSnapshot.getBatteryLevel() <= currentSnapshot.getSevereLevelThreshold()) {
mSevereWarningShownThisChargeCycle = true;
mLowWarningShownThisChargeCycle = true;
if (DEBUG) {
Slog.d(TAG, "Severe warning marked as shown this cycle");
}
} else {
Slog.d(TAG, "Low warning marked as shown this cycle");
mLowWarningShownThisChargeCycle = true;
}
} else if (shouldDismissHybridWarning(currentSnapshot)) {
if (DEBUG) {
Slog.d(TAG, "Dismissing warning");
}
mWarnings.dismissLowBatteryWarning();
} else {
if (DEBUG) {
Slog.d(TAG, "Updating warning");
}
mWarnings.updateLowBatteryWarning();
}
}
@VisibleForTesting
boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) {
if (snapshot.getPlugged()
|| snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) {
Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged()
+ " status unknown: "
+ (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN));
return false;
}
// Only show the low warning if enabled once per charge cycle & no battery saver
final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
&& snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold();
// Only show the severe warning once per charge cycle
final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
&& snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold();
final boolean canShow = canShowWarning || canShowSevereWarning;
if (DEBUG) {
Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
+ " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
+ " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
+ "\n" + snapshot.toString());
}
return canShow;
}
@VisibleForTesting
boolean shouldDismissHybridWarning(BatteryStateSnapshot snapshot) {
return snapshot.getPlugged()
|| snapshot.getBatteryLevel()
> snapshot.getLowLevelThreshold();
}
protected void maybeShowBatteryWarning(
BatteryStateSnapshot currentSnapshot,
BatteryStateSnapshot lastSnapshot) {
final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
|| lastSnapshot.getPlugged();
if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
mWarnings.showLowBatteryWarning(playSound);
} else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
mWarnings.dismissLowBatteryWarning();
} else {
mWarnings.updateLowBatteryWarning();
}
}
@VisibleForTesting
boolean shouldShowLowBatteryWarning(
BatteryStateSnapshot currentSnapshot,
BatteryStateSnapshot lastSnapshot) {
return !currentSnapshot.getPlugged()
&& !currentSnapshot.isPowerSaver()
&& (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
|| lastSnapshot.getPlugged())
&& currentSnapshot.getBucket() < 0))
&& currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
}
@VisibleForTesting
boolean shouldDismissLowBatteryWarning(
BatteryStateSnapshot currentSnapshot,
BatteryStateSnapshot lastSnapshot) {
return currentSnapshot.isPowerSaver()
|| currentSnapshot.getPlugged()
|| (currentSnapshot.getBucket() > lastSnapshot.getBucket()
&& currentSnapshot.getBucket() > 0);
}
private void initThermalEventListeners() {
doSkinThermalEventListenerRegistration();
doUsbThermalEventListenerRegistration();
}
@VisibleForTesting
synchronized void doSkinThermalEventListenerRegistration() {
final boolean oldEnableSkinTemperatureWarning = mEnableSkinTemperatureWarning;
boolean ret = false;
mEnableSkinTemperatureWarning = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.SHOW_TEMPERATURE_WARNING,
mContext.getResources().getInteger(R.integer.config_showTemperatureWarning)) != 0;
if (mEnableSkinTemperatureWarning != oldEnableSkinTemperatureWarning) {
try {
if (mSkinThermalEventListener == null) {
mSkinThermalEventListener = new SkinThermalEventListener();
}
if (mThermalService == null) {
mThermalService = IThermalService.Stub.asInterface(
ServiceManager.getService(Context.THERMAL_SERVICE));
}
if (mEnableSkinTemperatureWarning) {
ret = mThermalService.registerThermalEventListenerWithType(
mSkinThermalEventListener, Temperature.TYPE_SKIN);
} else {
ret = mThermalService.unregisterThermalEventListener(mSkinThermalEventListener);
}
} catch (RemoteException e) {
Slog.e(TAG, "Exception while (un)registering skin thermal event listener.", e);
}
if (!ret) {
mEnableSkinTemperatureWarning = !mEnableSkinTemperatureWarning;
Slog.e(TAG, "Failed to register or unregister skin thermal event listener.");
}
}
}
@VisibleForTesting
synchronized void doUsbThermalEventListenerRegistration() {
final boolean oldEnableUsbTemperatureAlarm = mEnableUsbTemperatureAlarm;
boolean ret = false;
mEnableUsbTemperatureAlarm = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
mContext.getResources().getInteger(R.integer.config_showUsbPortAlarm)) != 0;
if (mEnableUsbTemperatureAlarm != oldEnableUsbTemperatureAlarm) {
try {
if (mUsbThermalEventListener == null) {
mUsbThermalEventListener = new UsbThermalEventListener();
}
if (mThermalService == null) {
mThermalService = IThermalService.Stub.asInterface(
ServiceManager.getService(Context.THERMAL_SERVICE));
}
if (mEnableUsbTemperatureAlarm) {
ret = mThermalService.registerThermalEventListenerWithType(
mUsbThermalEventListener, Temperature.TYPE_USB_PORT);
} else {
ret = mThermalService.unregisterThermalEventListener(mUsbThermalEventListener);
}
} catch (RemoteException e) {
Slog.e(TAG, "Exception while (un)registering usb thermal event listener.", e);
}
if (!ret) {
mEnableUsbTemperatureAlarm = !mEnableUsbTemperatureAlarm;
Slog.e(TAG, "Failed to register or unregister usb thermal event listener.");
}
}
}
private void showWarnOnThermalShutdown() {
int bootCount = -1;
int lastReboot = mContext.getSharedPreferences(PREFS, 0).getInt(BOOT_COUNT_KEY, -1);
try {
bootCount = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.BOOT_COUNT);
} catch (Settings.SettingNotFoundException e) {
Slog.e(TAG, "Failed to read system boot count from Settings.Global.BOOT_COUNT");
}
// Only show the thermal shutdown warning when there is a thermal reboot.
if (bootCount > lastReboot) {
mContext.getSharedPreferences(PREFS, 0).edit().putInt(BOOT_COUNT_KEY,
bootCount).apply();
if (mPowerManager.getLastShutdownReason()
== PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) {
mWarnings.showThermalShutdownWarning();
}
}
}
@Override
public void showInattentiveSleepWarning() {
if (mOverlayView == null) {
mOverlayView = new InattentiveSleepWarningView(mContext);
}
mOverlayView.show();
}
@Override
public void dismissInattentiveSleepWarning(boolean animated) {
if (mOverlayView != null) {
mOverlayView.dismiss(animated);
}
}
public void dump(PrintWriter pw, String[] args) {
pw.print("mLowBatteryAlertCloseLevel=");
pw.println(mLowBatteryAlertCloseLevel);
pw.print("mLowBatteryReminderLevels=");
pw.println(Arrays.toString(mLowBatteryReminderLevels));
pw.print("mBatteryLevel=");
pw.println(Integer.toString(mBatteryLevel));
pw.print("mBatteryStatus=");
pw.println(Integer.toString(mBatteryStatus));
pw.print("mPlugType=");
pw.println(Integer.toString(mPlugType));
pw.print("mInvalidCharger=");
pw.println(Integer.toString(mInvalidCharger));
pw.print("mScreenOffTime=");
pw.print(mScreenOffTime);
if (mScreenOffTime >= 0) {
pw.print(" (");
pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
pw.print(" ago)");
}
pw.println();
pw.print("soundTimeout=");
pw.println(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
pw.print("bucket: ");
pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
pw.print("mEnableSkinTemperatureWarning=");
pw.println(mEnableSkinTemperatureWarning);
pw.print("mEnableUsbTemperatureAlarm=");
pw.println(mEnableUsbTemperatureAlarm);
mWarnings.dump(pw);
}
/**
* The interface to allow PowerUI to communicate with whatever implementation of WarningsUI
* is being used by the system.
*/
public interface WarningsUI {
/**
* Updates battery and screen info for determining whether to trigger battery warnings or
* not.
* @param batteryLevel The current battery level
* @param bucket The current battery bucket
* @param screenOffTime How long the screen has been off in millis
*/
void update(int batteryLevel, int bucket, long screenOffTime);
void dismissLowBatteryWarning();
void showLowBatteryWarning(boolean playSound);
void dismissInvalidChargerWarning();
void showInvalidChargerWarning();
void updateLowBatteryWarning();
boolean isInvalidChargerWarningShowing();
void dismissHighTemperatureWarning();
void showHighTemperatureWarning();
/**
* Display USB port overheat alarm
*/
void showUsbHighTemperatureAlarm();
void showThermalShutdownWarning();
void dump(PrintWriter pw);
void userSwitched();
/**
* Updates the snapshot of battery state used for evaluating battery warnings
* @param snapshot object containing relevant values for making battery warning decisions.
*/
void updateSnapshot(BatteryStateSnapshot snapshot);
}
// Skin thermal event received from thermal service manager subsystem
@VisibleForTesting
final class SkinThermalEventListener extends IThermalEventListener.Stub {
@Override public void notifyThrottling(Temperature temp) {
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
if (!mInVrMode) {
mWarnings.showHighTemperatureWarning();
Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
+ ", current skin status = " + status
+ ", temperature = " + temp.getValue());
}
} else {
mWarnings.dismissHighTemperatureWarning();
}
}
}
// Usb thermal event received from thermal service manager subsystem
@VisibleForTesting
final class UsbThermalEventListener extends IThermalEventListener.Stub {
@Override public void notifyThrottling(Temperature temp) {
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
mWarnings.showUsbHighTemperatureAlarm();
Slog.d(TAG, "UsbThermalEventListener: notifyThrottling was called "
+ ", current usb port status = " + status
+ ", temperature = " + temp.getValue());
}
}
}
}