All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.power.Notifier Maven / Gradle / Ivy

/*
 * 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.
 * 

* 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. *

* The notifier is designed to collapse unnecessary broadcasts when it is not * possible for the system to have observed an intermediate state. *

* 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. *

*/ @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; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy