/*
|
* Copyright (C) 2012 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.server.power;
|
|
import android.annotation.Nullable;
|
import android.annotation.UserIdInt;
|
import android.app.ActivityManagerInternal;
|
import android.app.AppOpsManager;
|
import android.app.trust.TrustManager;
|
import android.content.BroadcastReceiver;
|
import android.content.Context;
|
import android.content.Intent;
|
import android.hardware.input.InputManagerInternal;
|
import android.media.AudioAttributes;
|
import android.media.AudioManager;
|
import android.media.Ringtone;
|
import android.media.RingtoneManager;
|
import android.metrics.LogMaker;
|
import android.net.Uri;
|
import android.os.BatteryStats;
|
import android.os.Handler;
|
import android.os.Looper;
|
import android.os.Message;
|
import android.os.PowerManager;
|
import android.os.PowerManager.WakeReason;
|
import android.os.PowerManagerInternal;
|
import android.os.Process;
|
import android.os.RemoteException;
|
import android.os.SystemClock;
|
import android.os.UserHandle;
|
import android.os.VibrationEffect;
|
import android.os.Vibrator;
|
import android.os.WorkSource;
|
import android.provider.Settings;
|
import android.util.EventLog;
|
import android.util.Slog;
|
import android.util.StatsLog;
|
import android.view.WindowManagerPolicyConstants.OnReason;
|
|
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.app.IBatteryStats;
|
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.server.EventLogTags;
|
import com.android.server.LocalServices;
|
import com.android.server.inputmethod.InputMethodManagerInternal;
|
import com.android.server.policy.WindowManagerPolicy;
|
import com.android.server.statusbar.StatusBarManagerInternal;
|
|
/**
|
* Sends broadcasts about important power state changes.
|
* <p>
|
* This methods of this class may be called by the power manager service while
|
* its lock is being held. Internally it takes care of sending broadcasts to
|
* notify other components of the system or applications asynchronously.
|
* </p><p>
|
* The notifier is designed to collapse unnecessary broadcasts when it is not
|
* possible for the system to have observed an intermediate state.
|
* </p><p>
|
* For example, if the device wakes up, goes to sleep, wakes up again and goes to
|
* sleep again before the wake up notification is sent, then the system will
|
* be told about only one wake up and sleep. However, we always notify the
|
* fact that at least one transition occurred. It is especially important to
|
* tell the system when we go to sleep so that it can lock the keyguard if needed.
|
* </p>
|
*/
|
@VisibleForTesting
|
public class Notifier {
|
private static final String TAG = "PowerManagerNotifier";
|
|
private static final boolean DEBUG = false;
|
|
private static final int INTERACTIVE_STATE_UNKNOWN = 0;
|
private static final int INTERACTIVE_STATE_AWAKE = 1;
|
private static final int INTERACTIVE_STATE_ASLEEP = 2;
|
|
private static final int MSG_USER_ACTIVITY = 1;
|
private static final int MSG_BROADCAST = 2;
|
private static final int MSG_WIRELESS_CHARGING_STARTED = 3;
|
private static final int MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED = 4;
|
private static final int MSG_PROFILE_TIMED_OUT = 5;
|
private static final int MSG_WIRED_CHARGING_STARTED = 6;
|
|
private static final long[] WIRELESS_VIBRATION_TIME = {
|
40, 40, 40, 40, 40, 40, 40, 40, 40, // ramp-up sampling rate = 40ms
|
40, 40, 40, 40, 40, 40, 40 // ramp-down sampling rate = 40ms
|
};
|
private static final int[] WIRELESS_VIBRATION_AMPLITUDE = {
|
1, 4, 11, 25, 44, 67, 91, 114, 123, // ramp-up amplitude (from 0 to 50%)
|
103, 79, 55, 34, 17, 7, 2 // ramp-up amplitude
|
};
|
private static final VibrationEffect WIRELESS_CHARGING_VIBRATION_EFFECT =
|
VibrationEffect.createWaveform(WIRELESS_VIBRATION_TIME, WIRELESS_VIBRATION_AMPLITUDE,
|
-1);
|
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
|
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
.build();
|
|
private final Object mLock = new Object();
|
|
private final Context mContext;
|
private final IBatteryStats mBatteryStats;
|
private final AppOpsManager mAppOps;
|
private final SuspendBlocker mSuspendBlocker;
|
private final WindowManagerPolicy mPolicy;
|
private final ActivityManagerInternal mActivityManagerInternal;
|
private final InputManagerInternal mInputManagerInternal;
|
private final InputMethodManagerInternal mInputMethodManagerInternal;
|
@Nullable private final StatusBarManagerInternal mStatusBarManagerInternal;
|
private final TrustManager mTrustManager;
|
private final Vibrator mVibrator;
|
|
private final NotifierHandler mHandler;
|
private final Intent mScreenOnIntent;
|
private final Intent mScreenOffIntent;
|
private final Intent mScreenBrightnessBoostIntent;
|
|
// True if the device should suspend when the screen is off due to proximity.
|
private final boolean mSuspendWhenScreenOffDueToProximityConfig;
|
|
// The current interactive state. This is set as soon as an interactive state
|
// transition begins so as to capture the reason that it happened. At some point
|
// this state will propagate to the pending state then eventually to the
|
// broadcasted state over the course of reporting the transition asynchronously.
|
private boolean mInteractive = true;
|
private int mInteractiveChangeReason;
|
private long mInteractiveChangeStartTime; // In SystemClock.uptimeMillis()
|
private boolean mInteractiveChanging;
|
|
// The pending interactive state that we will eventually want to broadcast.
|
// This is designed so that we can collapse redundant sequences of awake/sleep
|
// transition pairs while still guaranteeing that at least one transition is observed
|
// whenever this happens.
|
private int mPendingInteractiveState;
|
private boolean mPendingWakeUpBroadcast;
|
private boolean mPendingGoToSleepBroadcast;
|
|
// The currently broadcasted interactive state. This reflects what other parts of the
|
// system have observed.
|
private int mBroadcastedInteractiveState;
|
private boolean mBroadcastInProgress;
|
private long mBroadcastStartTime;
|
|
// True if a user activity message should be sent.
|
private boolean mUserActivityPending;
|
|
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
|
SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
|
mContext = context;
|
mBatteryStats = batteryStats;
|
mAppOps = mContext.getSystemService(AppOpsManager.class);
|
mSuspendBlocker = suspendBlocker;
|
mPolicy = policy;
|
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
|
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
|
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
|
mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
|
mTrustManager = mContext.getSystemService(TrustManager.class);
|
mVibrator = mContext.getSystemService(Vibrator.class);
|
|
mHandler = new NotifierHandler(looper);
|
mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
|
mScreenOnIntent.addFlags(
|
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
|
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
|
mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
|
mScreenOffIntent.addFlags(
|
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
|
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
|
mScreenBrightnessBoostIntent =
|
new Intent(PowerManager.ACTION_SCREEN_BRIGHTNESS_BOOST_CHANGED);
|
mScreenBrightnessBoostIntent.addFlags(
|
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
|
|
mSuspendWhenScreenOffDueToProximityConfig = context.getResources().getBoolean(
|
com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity);
|
|
// Initialize interactive state for battery stats.
|
try {
|
mBatteryStats.noteInteractive(true);
|
} catch (RemoteException ex) { }
|
StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
|
StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
|
}
|
|
/**
|
* Called when a wake lock is acquired.
|
*/
|
public void onWakeLockAcquired(int flags, String tag, String packageName,
|
int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
|
if (DEBUG) {
|
Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
|
+ "\", packageName=" + packageName
|
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
|
+ ", workSource=" + workSource);
|
}
|
|
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
|
if (monitorType >= 0) {
|
try {
|
final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID
|
&& (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
|
if (workSource != null) {
|
mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag,
|
historyTag, monitorType, unimportantForLogging);
|
} else {
|
mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
|
monitorType, unimportantForLogging);
|
// XXX need to deal with disabled operations.
|
mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
|
}
|
} catch (RemoteException ex) {
|
// Ignore
|
}
|
}
|
}
|
|
public void onLongPartialWakeLockStart(String tag, int ownerUid, WorkSource workSource,
|
String historyTag) {
|
if (DEBUG) {
|
Slog.d(TAG, "onLongPartialWakeLockStart: ownerUid=" + ownerUid
|
+ ", workSource=" + workSource);
|
}
|
|
try {
|
if (workSource != null) {
|
mBatteryStats.noteLongPartialWakelockStartFromSource(tag, historyTag, workSource);
|
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, workSource,
|
tag, historyTag, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
|
} else {
|
mBatteryStats.noteLongPartialWakelockStart(tag, historyTag, ownerUid);
|
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
|
ownerUid, null, tag, historyTag,
|
StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
|
}
|
} catch (RemoteException ex) {
|
// Ignore
|
}
|
}
|
|
public void onLongPartialWakeLockFinish(String tag, int ownerUid, WorkSource workSource,
|
String historyTag) {
|
if (DEBUG) {
|
Slog.d(TAG, "onLongPartialWakeLockFinish: ownerUid=" + ownerUid
|
+ ", workSource=" + workSource);
|
}
|
|
try {
|
if (workSource != null) {
|
mBatteryStats.noteLongPartialWakelockFinishFromSource(tag, historyTag, workSource);
|
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, workSource,
|
tag, historyTag, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
|
} else {
|
mBatteryStats.noteLongPartialWakelockFinish(tag, historyTag, ownerUid);
|
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
|
ownerUid, null, tag, historyTag,
|
StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
|
}
|
} catch (RemoteException ex) {
|
// Ignore
|
}
|
}
|
|
/**
|
* Called when a wake lock is changing.
|
*/
|
public void onWakeLockChanging(int flags, String tag, String packageName,
|
int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
|
int newFlags, String newTag, String newPackageName, int newOwnerUid,
|
int newOwnerPid, WorkSource newWorkSource, String newHistoryTag) {
|
|
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
|
final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags);
|
if (workSource != null && newWorkSource != null
|
&& monitorType >= 0 && newMonitorType >= 0) {
|
if (DEBUG) {
|
Slog.d(TAG, "onWakeLockChanging: flags=" + newFlags + ", tag=\"" + newTag
|
+ "\", packageName=" + newPackageName
|
+ ", ownerUid=" + newOwnerUid + ", ownerPid=" + newOwnerPid
|
+ ", workSource=" + newWorkSource);
|
}
|
|
final boolean unimportantForLogging = newOwnerUid == Process.SYSTEM_UID
|
&& (newFlags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
|
try {
|
mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, historyTag,
|
monitorType, newWorkSource, newOwnerPid, newTag, newHistoryTag,
|
newMonitorType, unimportantForLogging);
|
} catch (RemoteException ex) {
|
// Ignore
|
}
|
} else {
|
onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag);
|
onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid,
|
newWorkSource, newHistoryTag);
|
}
|
}
|
|
/**
|
* Called when a wake lock is released.
|
*/
|
public void onWakeLockReleased(int flags, String tag, String packageName,
|
int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
|
if (DEBUG) {
|
Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag
|
+ "\", packageName=" + packageName
|
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
|
+ ", workSource=" + workSource);
|
}
|
|
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
|
if (monitorType >= 0) {
|
try {
|
if (workSource != null) {
|
mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag,
|
historyTag, monitorType);
|
} else {
|
mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
|
historyTag, monitorType);
|
mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
|
}
|
} catch (RemoteException ex) {
|
// Ignore
|
}
|
}
|
}
|
|
private int getBatteryStatsWakeLockMonitorType(int flags) {
|
switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
|
case PowerManager.PARTIAL_WAKE_LOCK:
|
return BatteryStats.WAKE_TYPE_PARTIAL;
|
|
case PowerManager.SCREEN_DIM_WAKE_LOCK:
|
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
|
return BatteryStats.WAKE_TYPE_FULL;
|
|
case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
|
if (mSuspendWhenScreenOffDueToProximityConfig) {
|
return -1;
|
}
|
return BatteryStats.WAKE_TYPE_PARTIAL;
|
|
case PowerManager.DRAW_WAKE_LOCK:
|
return BatteryStats.WAKE_TYPE_DRAW;
|
|
case PowerManager.DOZE_WAKE_LOCK:
|
// Doze wake locks are an internal implementation detail of the
|
// communication between dream manager service and power manager
|
// service. They have no additive battery impact.
|
return -1;
|
|
default:
|
return -1;
|
}
|
}
|
|
/**
|
* Notifies that the device is changing wakefulness.
|
* This function may be called even if the previous change hasn't finished in
|
* which case it will assume that the state did not fully converge before the
|
* next transition began and will recover accordingly.
|
*/
|
public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {
|
final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
|
if (DEBUG) {
|
Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
|
+ ", reason=" + reason + ", interactive=" + interactive);
|
}
|
|
// Tell the activity manager about changes in wakefulness, not just interactivity.
|
// It needs more granularity than other components.
|
mHandler.post(new Runnable() {
|
@Override
|
public void run() {
|
mActivityManagerInternal.onWakefulnessChanged(wakefulness);
|
}
|
});
|
|
// Handle any early interactive state changes.
|
// Finish pending incomplete ones from a previous cycle.
|
if (mInteractive != interactive) {
|
// Finish up late behaviors if needed.
|
if (mInteractiveChanging) {
|
handleLateInteractiveChange();
|
}
|
|
// Start input as soon as we start waking up or going to sleep.
|
mInputManagerInternal.setInteractive(interactive);
|
mInputMethodManagerInternal.setInteractive(interactive);
|
|
// Notify battery stats.
|
try {
|
mBatteryStats.noteInteractive(interactive);
|
} catch (RemoteException ex) { }
|
StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
|
interactive ? StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
|
StatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);
|
|
// Handle early behaviors.
|
mInteractive = interactive;
|
mInteractiveChangeReason = reason;
|
mInteractiveChangeStartTime = eventTime;
|
mInteractiveChanging = true;
|
handleEarlyInteractiveChange();
|
}
|
}
|
|
/**
|
* Notifies that the device has finished changing wakefulness.
|
*/
|
public void onWakefulnessChangeFinished() {
|
if (DEBUG) {
|
Slog.d(TAG, "onWakefulnessChangeFinished");
|
}
|
|
if (mInteractiveChanging) {
|
mInteractiveChanging = false;
|
handleLateInteractiveChange();
|
}
|
}
|
|
/**
|
* Handle early interactive state changes such as getting applications or the lock
|
* screen running and ready for the user to see (such as when turning on the screen).
|
*/
|
private void handleEarlyInteractiveChange() {
|
synchronized (mLock) {
|
if (mInteractive) {
|
// Waking up...
|
mHandler.post(new Runnable() {
|
@Override
|
public void run() {
|
final int why = translateOnReason(mInteractiveChangeReason);
|
mPolicy.startedWakingUp(why);
|
}
|
});
|
|
// Send interactive broadcast.
|
mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
|
mPendingWakeUpBroadcast = true;
|
updatePendingBroadcastLocked();
|
} else {
|
// Going to sleep...
|
// Tell the policy that we started going to sleep.
|
final int why = translateOffReason(mInteractiveChangeReason);
|
mHandler.post(new Runnable() {
|
@Override
|
public void run() {
|
mPolicy.startedGoingToSleep(why);
|
}
|
});
|
}
|
}
|
}
|
|
/**
|
* Handle late interactive state changes once they are finished so that the system can
|
* finish pending transitions (such as turning the screen off) before causing
|
* applications to change state visibly.
|
*/
|
private void handleLateInteractiveChange() {
|
synchronized (mLock) {
|
final int interactiveChangeLatency =
|
(int) (SystemClock.uptimeMillis() - mInteractiveChangeStartTime);
|
if (mInteractive) {
|
// Finished waking up...
|
final int why = translateOnReason(mInteractiveChangeReason);
|
mHandler.post(new Runnable() {
|
@Override
|
public void run() {
|
LogMaker log = new LogMaker(MetricsEvent.SCREEN);
|
log.setType(MetricsEvent.TYPE_OPEN);
|
log.setSubtype(why);
|
log.setLatency(interactiveChangeLatency);
|
log.addTaggedData(
|
MetricsEvent.FIELD_SCREEN_WAKE_REASON, mInteractiveChangeReason);
|
MetricsLogger.action(log);
|
EventLogTags.writePowerScreenState(1, 0, 0, 0, interactiveChangeLatency);
|
mPolicy.finishedWakingUp(why);
|
}
|
});
|
} else {
|
// Finished going to sleep...
|
// This is a good time to make transitions that we don't want the user to see,
|
// such as bringing the key guard to focus. There's no guarantee for this
|
// however because the user could turn the device on again at any time.
|
// Some things may need to be protected by other mechanisms that defer screen on.
|
|
// Cancel pending user activity.
|
if (mUserActivityPending) {
|
mUserActivityPending = false;
|
mHandler.removeMessages(MSG_USER_ACTIVITY);
|
}
|
|
// Tell the policy we finished going to sleep.
|
final int why = translateOffReason(mInteractiveChangeReason);
|
mHandler.post(new Runnable() {
|
@Override
|
public void run() {
|
LogMaker log = new LogMaker(MetricsEvent.SCREEN);
|
log.setType(MetricsEvent.TYPE_CLOSE);
|
log.setSubtype(why);
|
log.setLatency(interactiveChangeLatency);
|
log.addTaggedData(
|
MetricsEvent.FIELD_SCREEN_SLEEP_REASON, mInteractiveChangeReason);
|
MetricsLogger.action(log);
|
EventLogTags.writePowerScreenState(0, why, 0, 0, interactiveChangeLatency);
|
mPolicy.finishedGoingToSleep(why);
|
}
|
});
|
|
// Send non-interactive broadcast.
|
mPendingInteractiveState = INTERACTIVE_STATE_ASLEEP;
|
mPendingGoToSleepBroadcast = true;
|
updatePendingBroadcastLocked();
|
}
|
}
|
}
|
|
private static int translateOffReason(int reason) {
|
switch (reason) {
|
case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
|
return WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
|
case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
|
return WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
|
default:
|
return WindowManagerPolicy.OFF_BECAUSE_OF_USER;
|
}
|
}
|
|
private static @OnReason int translateOnReason(@WakeReason int reason) {
|
switch (reason) {
|
case PowerManager.WAKE_REASON_POWER_BUTTON:
|
case PowerManager.WAKE_REASON_PLUGGED_IN:
|
case PowerManager.WAKE_REASON_GESTURE:
|
case PowerManager.WAKE_REASON_CAMERA_LAUNCH:
|
case PowerManager.WAKE_REASON_WAKE_KEY:
|
case PowerManager.WAKE_REASON_WAKE_MOTION:
|
case PowerManager.WAKE_REASON_LID:
|
return WindowManagerPolicy.ON_BECAUSE_OF_USER;
|
case PowerManager.WAKE_REASON_APPLICATION:
|
return WindowManagerPolicy.ON_BECAUSE_OF_APPLICATION;
|
default:
|
return WindowManagerPolicy.ON_BECAUSE_OF_UNKNOWN;
|
}
|
}
|
|
/**
|
* Called when screen brightness boost begins or ends.
|
*/
|
public void onScreenBrightnessBoostChanged() {
|
if (DEBUG) {
|
Slog.d(TAG, "onScreenBrightnessBoostChanged");
|
}
|
|
mSuspendBlocker.acquire();
|
Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED);
|
msg.setAsynchronous(true);
|
mHandler.sendMessage(msg);
|
}
|
|
/**
|
* Called when there has been user activity.
|
*/
|
public void onUserActivity(int event, int uid) {
|
if (DEBUG) {
|
Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid);
|
}
|
|
try {
|
mBatteryStats.noteUserActivity(uid, event);
|
} catch (RemoteException ex) {
|
// Ignore
|
}
|
|
synchronized (mLock) {
|
if (!mUserActivityPending) {
|
mUserActivityPending = true;
|
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
|
msg.setAsynchronous(true);
|
mHandler.sendMessage(msg);
|
}
|
}
|
}
|
|
/**
|
* Called when the screen has turned on.
|
*/
|
public void onWakeUp(int reason, String details, int reasonUid, String opPackageName,
|
int opUid) {
|
if (DEBUG) {
|
Slog.d(TAG, "onWakeUp: reason=" + PowerManager.wakeReasonToString(reason)
|
+ ", details=" + details + ", reasonUid=" + reasonUid
|
+ " opPackageName=" + opPackageName + " opUid=" + opUid);
|
}
|
|
try {
|
mBatteryStats.noteWakeUp(details, reasonUid);
|
if (opPackageName != null) {
|
mAppOps.noteOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
|
}
|
} catch (RemoteException ex) {
|
// Ignore
|
}
|
}
|
|
/**
|
* Called when profile screen lock timeout has expired.
|
*/
|
public void onProfileTimeout(@UserIdInt int userId) {
|
final Message msg = mHandler.obtainMessage(MSG_PROFILE_TIMED_OUT);
|
msg.setAsynchronous(true);
|
msg.arg1 = userId;
|
mHandler.sendMessage(msg);
|
}
|
|
/**
|
* Called when wireless charging has started - to provide user feedback (sound and visual).
|
*/
|
public void onWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
|
if (DEBUG) {
|
Slog.d(TAG, "onWirelessChargingStarted");
|
}
|
|
mSuspendBlocker.acquire();
|
Message msg = mHandler.obtainMessage(MSG_WIRELESS_CHARGING_STARTED);
|
msg.setAsynchronous(true);
|
msg.arg1 = batteryLevel;
|
msg.arg2 = userId;
|
mHandler.sendMessage(msg);
|
}
|
|
/**
|
* Called when wired charging has started - to provide user feedback
|
*/
|
public void onWiredChargingStarted(@UserIdInt int userId) {
|
if (DEBUG) {
|
Slog.d(TAG, "onWiredChargingStarted");
|
}
|
|
mSuspendBlocker.acquire();
|
Message msg = mHandler.obtainMessage(MSG_WIRED_CHARGING_STARTED);
|
msg.setAsynchronous(true);
|
msg.arg1 = userId;
|
mHandler.sendMessage(msg);
|
}
|
|
private void updatePendingBroadcastLocked() {
|
if (!mBroadcastInProgress
|
&& mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
|
&& (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
|
|| mPendingInteractiveState != mBroadcastedInteractiveState)) {
|
mBroadcastInProgress = true;
|
mSuspendBlocker.acquire();
|
Message msg = mHandler.obtainMessage(MSG_BROADCAST);
|
msg.setAsynchronous(true);
|
mHandler.sendMessage(msg);
|
}
|
}
|
|
private void finishPendingBroadcastLocked() {
|
mBroadcastInProgress = false;
|
mSuspendBlocker.release();
|
}
|
|
private void sendUserActivity() {
|
synchronized (mLock) {
|
if (!mUserActivityPending) {
|
return;
|
}
|
mUserActivityPending = false;
|
}
|
mPolicy.userActivity();
|
}
|
|
private void sendNextBroadcast() {
|
final int powerState;
|
synchronized (mLock) {
|
if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
|
// Broadcasted power state is unknown. Send wake up.
|
mPendingWakeUpBroadcast = false;
|
mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
|
} else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
|
// Broadcasted power state is awake. Send asleep if needed.
|
if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
|
|| mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {
|
mPendingGoToSleepBroadcast = false;
|
mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
|
} else {
|
finishPendingBroadcastLocked();
|
return;
|
}
|
} else {
|
// Broadcasted power state is asleep. Send awake if needed.
|
if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
|
|| mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {
|
mPendingWakeUpBroadcast = false;
|
mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
|
} else {
|
finishPendingBroadcastLocked();
|
return;
|
}
|
}
|
|
mBroadcastStartTime = SystemClock.uptimeMillis();
|
powerState = mBroadcastedInteractiveState;
|
}
|
|
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
|
|
if (powerState == INTERACTIVE_STATE_AWAKE) {
|
sendWakeUpBroadcast();
|
} else {
|
sendGoToSleepBroadcast();
|
}
|
}
|
|
private void sendBrightnessBoostChangedBroadcast() {
|
if (DEBUG) {
|
Slog.d(TAG, "Sending brightness boost changed broadcast.");
|
}
|
|
mContext.sendOrderedBroadcastAsUser(mScreenBrightnessBoostIntent, UserHandle.ALL, null,
|
mScreeBrightnessBoostChangedDone, mHandler, 0, null, null);
|
}
|
|
private final BroadcastReceiver mScreeBrightnessBoostChangedDone = new BroadcastReceiver() {
|
@Override
|
public void onReceive(Context context, Intent intent) {
|
mSuspendBlocker.release();
|
}
|
};
|
|
private void sendWakeUpBroadcast() {
|
if (DEBUG) {
|
Slog.d(TAG, "Sending wake up broadcast.");
|
}
|
|
if (mActivityManagerInternal.isSystemReady()) {
|
mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
|
mWakeUpBroadcastDone, mHandler, 0, null, null);
|
} else {
|
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
|
sendNextBroadcast();
|
}
|
}
|
|
private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
|
@Override
|
public void onReceive(Context context, Intent intent) {
|
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
|
SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
|
sendNextBroadcast();
|
}
|
};
|
|
private void sendGoToSleepBroadcast() {
|
if (DEBUG) {
|
Slog.d(TAG, "Sending go to sleep broadcast.");
|
}
|
|
if (mActivityManagerInternal.isSystemReady()) {
|
mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
|
mGoToSleepBroadcastDone, mHandler, 0, null, null);
|
} else {
|
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
|
sendNextBroadcast();
|
}
|
}
|
|
private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() {
|
@Override
|
public void onReceive(Context context, Intent intent) {
|
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
|
SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
|
sendNextBroadcast();
|
}
|
};
|
|
/**
|
* If enabled, plays a sound and/or vibration when wireless or non-wireless charging has started
|
*/
|
private void playChargingStartedFeedback(@UserIdInt int userId) {
|
playChargingStartedVibration(userId);
|
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
|
Settings.Global.CHARGING_STARTED_SOUND);
|
if (isChargingFeedbackEnabled(userId) && soundPath != null) {
|
final Uri soundUri = Uri.parse("file://" + soundPath);
|
if (soundUri != null) {
|
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
|
if (sfx != null) {
|
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
|
sfx.play();
|
}
|
}
|
}
|
}
|
|
private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
|
playChargingStartedFeedback(userId);
|
if (mStatusBarManagerInternal != null) {
|
mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
|
}
|
mSuspendBlocker.release();
|
}
|
|
private void showWiredChargingStarted(@UserIdInt int userId) {
|
playChargingStartedFeedback(userId);
|
mSuspendBlocker.release();
|
}
|
|
private void lockProfile(@UserIdInt int userId) {
|
mTrustManager.setDeviceLockedForUser(userId, true /*locked*/);
|
}
|
|
private void playChargingStartedVibration(@UserIdInt int userId) {
|
final boolean vibrateEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
|
if (vibrateEnabled && isChargingFeedbackEnabled(userId)) {
|
mVibrator.vibrate(WIRELESS_CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES);
|
}
|
}
|
|
private boolean isChargingFeedbackEnabled(@UserIdInt int userId) {
|
final boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
Settings.Secure.CHARGING_SOUNDS_ENABLED, 1, userId) != 0;
|
final boolean dndOff = Settings.Global.getInt(mContext.getContentResolver(),
|
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
|
== Settings.Global.ZEN_MODE_OFF;
|
return enabled && dndOff;
|
}
|
|
private final class NotifierHandler extends Handler {
|
|
public NotifierHandler(Looper looper) {
|
super(looper, null, true /*async*/);
|
}
|
@Override
|
public void handleMessage(Message msg) {
|
switch (msg.what) {
|
case MSG_USER_ACTIVITY:
|
sendUserActivity();
|
break;
|
case MSG_BROADCAST:
|
sendNextBroadcast();
|
break;
|
case MSG_WIRELESS_CHARGING_STARTED:
|
showWirelessChargingStarted(msg.arg1, msg.arg2);
|
break;
|
case MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED:
|
sendBrightnessBoostChangedBroadcast();
|
break;
|
case MSG_PROFILE_TIMED_OUT:
|
lockProfile(msg.arg1);
|
break;
|
case MSG_WIRED_CHARGING_STARTED:
|
showWiredChargingStarted(msg.arg1);
|
break;
|
}
|
}
|
}
|
}
|