1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.power;
18
19import android.app.ActivityManagerInternal;
20import android.app.AppOpsManager;
21
22import com.android.internal.app.IAppOpsService;
23import com.android.internal.app.IBatteryStats;
24import com.android.server.EventLogTags;
25import com.android.server.LocalServices;
26
27import android.app.ActivityManagerNative;
28import android.content.BroadcastReceiver;
29import android.content.Context;
30import android.content.Intent;
31import android.hardware.input.InputManagerInternal;
32import android.media.AudioManager;
33import android.media.Ringtone;
34import android.media.RingtoneManager;
35import android.net.Uri;
36import android.os.BatteryStats;
37import android.os.Handler;
38import android.os.Looper;
39import android.os.Message;
40import android.os.PowerManager;
41import android.os.PowerManagerInternal;
42import android.os.Process;
43import android.os.RemoteException;
44import android.os.SystemClock;
45import android.os.UserHandle;
46import android.os.WorkSource;
47import android.provider.Settings;
48import android.util.EventLog;
49import android.util.Slog;
50import android.view.WindowManagerPolicy;
51import android.view.inputmethod.InputMethodManagerInternal;
52
53/**
54 * Sends broadcasts about important power state changes.
55 * <p>
56 * This methods of this class may be called by the power manager service while
57 * its lock is being held.  Internally it takes care of sending broadcasts to
58 * notify other components of the system or applications asynchronously.
59 * </p><p>
60 * The notifier is designed to collapse unnecessary broadcasts when it is not
61 * possible for the system to have observed an intermediate state.
62 * </p><p>
63 * For example, if the device wakes up, goes to sleep, wakes up again and goes to
64 * sleep again before the wake up notification is sent, then the system will
65 * be told about only one wake up and sleep.  However, we always notify the
66 * fact that at least one transition occurred.  It is especially important to
67 * tell the system when we go to sleep so that it can lock the keyguard if needed.
68 * </p>
69 */
70final class Notifier {
71    private static final String TAG = "PowerManagerNotifier";
72
73    private static final boolean DEBUG = false;
74
75    private static final int INTERACTIVE_STATE_UNKNOWN = 0;
76    private static final int INTERACTIVE_STATE_AWAKE = 1;
77    private static final int INTERACTIVE_STATE_ASLEEP = 2;
78
79    private static final int MSG_USER_ACTIVITY = 1;
80    private static final int MSG_BROADCAST = 2;
81    private static final int MSG_WIRELESS_CHARGING_STARTED = 3;
82    private static final int MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED = 4;
83
84    private final Object mLock = new Object();
85
86    private final Context mContext;
87    private final IBatteryStats mBatteryStats;
88    private final IAppOpsService mAppOps;
89    private final SuspendBlocker mSuspendBlocker;
90    private final WindowManagerPolicy mPolicy;
91    private final ActivityManagerInternal mActivityManagerInternal;
92    private final InputManagerInternal mInputManagerInternal;
93    private final InputMethodManagerInternal mInputMethodManagerInternal;
94
95    private final NotifierHandler mHandler;
96    private final Intent mScreenOnIntent;
97    private final Intent mScreenOffIntent;
98    private final Intent mScreenBrightnessBoostIntent;
99
100    // True if the device should suspend when the screen is off due to proximity.
101    private final boolean mSuspendWhenScreenOffDueToProximityConfig;
102
103    // The current interactive state.  This is set as soon as an interactive state
104    // transition begins so as to capture the reason that it happened.  At some point
105    // this state will propagate to the pending state then eventually to the
106    // broadcasted state over the course of reporting the transition asynchronously.
107    private boolean mInteractive = true;
108    private int mInteractiveChangeReason;
109    private boolean mInteractiveChanging;
110
111    // The pending interactive state that we will eventually want to broadcast.
112    // This is designed so that we can collapse redundant sequences of awake/sleep
113    // transition pairs while still guaranteeing that at least one transition is observed
114    // whenever this happens.
115    private int mPendingInteractiveState;
116    private boolean mPendingWakeUpBroadcast;
117    private boolean mPendingGoToSleepBroadcast;
118
119    // The currently broadcasted interactive state.  This reflects what other parts of the
120    // system have observed.
121    private int mBroadcastedInteractiveState;
122    private boolean mBroadcastInProgress;
123    private long mBroadcastStartTime;
124
125    // True if a user activity message should be sent.
126    private boolean mUserActivityPending;
127
128    public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
129            IAppOpsService appOps, SuspendBlocker suspendBlocker,
130            WindowManagerPolicy policy) {
131        mContext = context;
132        mBatteryStats = batteryStats;
133        mAppOps = appOps;
134        mSuspendBlocker = suspendBlocker;
135        mPolicy = policy;
136        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
137        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
138        mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
139
140        mHandler = new NotifierHandler(looper);
141        mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
142        mScreenOnIntent.addFlags(
143                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
144        mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
145        mScreenOffIntent.addFlags(
146                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
147        mScreenBrightnessBoostIntent =
148                new Intent(PowerManager.ACTION_SCREEN_BRIGHTNESS_BOOST_CHANGED);
149        mScreenBrightnessBoostIntent.addFlags(
150                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
151
152        mSuspendWhenScreenOffDueToProximityConfig = context.getResources().getBoolean(
153                com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity);
154
155        // Initialize interactive state for battery stats.
156        try {
157            mBatteryStats.noteInteractive(true);
158        } catch (RemoteException ex) { }
159    }
160
161    /**
162     * Called when a wake lock is acquired.
163     */
164    public void onWakeLockAcquired(int flags, String tag, String packageName,
165            int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
166        if (DEBUG) {
167            Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
168                    + "\", packageName=" + packageName
169                    + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
170                    + ", workSource=" + workSource);
171        }
172
173        final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
174        if (monitorType >= 0) {
175            try {
176                final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID
177                        && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
178                if (workSource != null) {
179                    mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag,
180                            historyTag, monitorType, unimportantForLogging);
181                } else {
182                    mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
183                            monitorType, unimportantForLogging);
184                    // XXX need to deal with disabled operations.
185                    mAppOps.startOperation(AppOpsManager.getToken(mAppOps),
186                            AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
187                }
188            } catch (RemoteException ex) {
189                // Ignore
190            }
191        }
192    }
193
194    /**
195     * Called when a wake lock is changing.
196     */
197    public void onWakeLockChanging(int flags, String tag, String packageName,
198            int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
199            int newFlags, String newTag, String newPackageName, int newOwnerUid,
200            int newOwnerPid, WorkSource newWorkSource, String newHistoryTag) {
201
202        final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
203        final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags);
204        if (workSource != null && newWorkSource != null
205                && monitorType >= 0 && newMonitorType >= 0) {
206            if (DEBUG) {
207                Slog.d(TAG, "onWakeLockChanging: flags=" + newFlags + ", tag=\"" + newTag
208                        + "\", packageName=" + newPackageName
209                        + ", ownerUid=" + newOwnerUid + ", ownerPid=" + newOwnerPid
210                        + ", workSource=" + newWorkSource);
211            }
212
213            final boolean unimportantForLogging = newOwnerUid == Process.SYSTEM_UID
214                    && (newFlags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0;
215            try {
216                mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, historyTag,
217                        monitorType, newWorkSource, newOwnerPid, newTag, newHistoryTag,
218                        newMonitorType, unimportantForLogging);
219            } catch (RemoteException ex) {
220                // Ignore
221            }
222        } else {
223            onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag);
224            onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid,
225                    newWorkSource, newHistoryTag);
226        }
227    }
228
229    /**
230     * Called when a wake lock is released.
231     */
232    public void onWakeLockReleased(int flags, String tag, String packageName,
233            int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
234        if (DEBUG) {
235            Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag
236                    + "\", packageName=" + packageName
237                    + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
238                    + ", workSource=" + workSource);
239        }
240
241        final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
242        if (monitorType >= 0) {
243            try {
244                if (workSource != null) {
245                    mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag,
246                            historyTag, monitorType);
247                } else {
248                    mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
249                            historyTag, monitorType);
250                    mAppOps.finishOperation(AppOpsManager.getToken(mAppOps),
251                            AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
252                }
253            } catch (RemoteException ex) {
254                // Ignore
255            }
256        }
257    }
258
259    private int getBatteryStatsWakeLockMonitorType(int flags) {
260        switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
261            case PowerManager.PARTIAL_WAKE_LOCK:
262                return BatteryStats.WAKE_TYPE_PARTIAL;
263
264            case PowerManager.SCREEN_DIM_WAKE_LOCK:
265            case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
266                return BatteryStats.WAKE_TYPE_FULL;
267
268            case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
269                if (mSuspendWhenScreenOffDueToProximityConfig) {
270                    return -1;
271                }
272                return BatteryStats.WAKE_TYPE_PARTIAL;
273
274            case PowerManager.DRAW_WAKE_LOCK:
275                return BatteryStats.WAKE_TYPE_DRAW;
276
277            case PowerManager.DOZE_WAKE_LOCK:
278                // Doze wake locks are an internal implementation detail of the
279                // communication between dream manager service and power manager
280                // service.  They have no additive battery impact.
281                return -1;
282
283            default:
284                return -1;
285        }
286    }
287
288    /**
289     * Notifies that the device is changing wakefulness.
290     * This function may be called even if the previous change hasn't finished in
291     * which case it will assume that the state did not fully converge before the
292     * next transition began and will recover accordingly.
293     */
294    public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
295        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
296        if (DEBUG) {
297            Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness
298                    + ", reason=" + reason + ", interactive=" + interactive);
299        }
300
301        // Tell the activity manager about changes in wakefulness, not just interactivity.
302        // It needs more granularity than other components.
303        mHandler.post(new Runnable() {
304            @Override
305            public void run() {
306                mActivityManagerInternal.onWakefulnessChanged(wakefulness);
307            }
308        });
309
310        // Handle any early interactive state changes.
311        // Finish pending incomplete ones from a previous cycle.
312        if (mInteractive != interactive) {
313            // Finish up late behaviors if needed.
314            if (mInteractiveChanging) {
315                handleLateInteractiveChange();
316            }
317
318            // Start input as soon as we start waking up or going to sleep.
319            mInputManagerInternal.setInteractive(interactive);
320            mInputMethodManagerInternal.setInteractive(interactive);
321
322            // Notify battery stats.
323            try {
324                mBatteryStats.noteInteractive(interactive);
325            } catch (RemoteException ex) { }
326
327            // Handle early behaviors.
328            mInteractive = interactive;
329            mInteractiveChangeReason = reason;
330            mInteractiveChanging = true;
331            handleEarlyInteractiveChange();
332        }
333    }
334
335    /**
336     * Notifies that the device has finished changing wakefulness.
337     */
338    public void onWakefulnessChangeFinished() {
339        if (DEBUG) {
340            Slog.d(TAG, "onWakefulnessChangeFinished");
341        }
342
343        if (mInteractiveChanging) {
344            mInteractiveChanging = false;
345            handleLateInteractiveChange();
346        }
347    }
348
349    /**
350     * Handle early interactive state changes such as getting applications or the lock
351     * screen running and ready for the user to see (such as when turning on the screen).
352     */
353    private void handleEarlyInteractiveChange() {
354        synchronized (mLock) {
355            if (mInteractive) {
356                // Waking up...
357                mHandler.post(new Runnable() {
358                    @Override
359                    public void run() {
360                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
361                        mPolicy.startedWakingUp();
362                    }
363                });
364
365                // Send interactive broadcast.
366                mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
367                mPendingWakeUpBroadcast = true;
368                updatePendingBroadcastLocked();
369            } else {
370                // Going to sleep...
371                // Tell the policy that we started going to sleep.
372                final int why = translateOffReason(mInteractiveChangeReason);
373                mHandler.post(new Runnable() {
374                    @Override
375                    public void run() {
376                        mPolicy.startedGoingToSleep(why);
377                    }
378                });
379            }
380        }
381    }
382
383    /**
384     * Handle late interactive state changes once they are finished so that the system can
385     * finish pending transitions (such as turning the screen off) before causing
386     * applications to change state visibly.
387     */
388    private void handleLateInteractiveChange() {
389        synchronized (mLock) {
390            if (mInteractive) {
391                // Finished waking up...
392                mHandler.post(new Runnable() {
393                    @Override
394                    public void run() {
395                        mPolicy.finishedWakingUp();
396                    }
397                });
398            } else {
399                // Finished going to sleep...
400                // This is a good time to make transitions that we don't want the user to see,
401                // such as bringing the key guard to focus.  There's no guarantee for this
402                // however because the user could turn the device on again at any time.
403                // Some things may need to be protected by other mechanisms that defer screen on.
404
405                // Cancel pending user activity.
406                if (mUserActivityPending) {
407                    mUserActivityPending = false;
408                    mHandler.removeMessages(MSG_USER_ACTIVITY);
409                }
410
411                // Tell the policy we finished going to sleep.
412                final int why = translateOffReason(mInteractiveChangeReason);
413                mHandler.post(new Runnable() {
414                    @Override
415                    public void run() {
416                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
417                        mPolicy.finishedGoingToSleep(why);
418                    }
419                });
420
421                // Send non-interactive broadcast.
422                mPendingInteractiveState = INTERACTIVE_STATE_ASLEEP;
423                mPendingGoToSleepBroadcast = true;
424                updatePendingBroadcastLocked();
425            }
426        }
427    }
428
429    private static int translateOffReason(int reason) {
430        switch (reason) {
431            case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
432                return WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
433            case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
434                return WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
435            default:
436                return WindowManagerPolicy.OFF_BECAUSE_OF_USER;
437        }
438    }
439
440    /**
441     * Called when screen brightness boost begins or ends.
442     */
443    public void onScreenBrightnessBoostChanged() {
444        if (DEBUG) {
445            Slog.d(TAG, "onScreenBrightnessBoostChanged");
446        }
447
448        mSuspendBlocker.acquire();
449        Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED);
450        msg.setAsynchronous(true);
451        mHandler.sendMessage(msg);
452    }
453
454    /**
455     * Called when there has been user activity.
456     */
457    public void onUserActivity(int event, int uid) {
458        if (DEBUG) {
459            Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid);
460        }
461
462        try {
463            mBatteryStats.noteUserActivity(uid, event);
464        } catch (RemoteException ex) {
465            // Ignore
466        }
467
468        synchronized (mLock) {
469            if (!mUserActivityPending) {
470                mUserActivityPending = true;
471                Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
472                msg.setAsynchronous(true);
473                mHandler.sendMessage(msg);
474            }
475        }
476    }
477
478    /**
479     * Called when the screen has turned on.
480     */
481    public void onWakeUp(String reason, int reasonUid, String opPackageName, int opUid) {
482        if (DEBUG) {
483            Slog.d(TAG, "onWakeUp: event=" + reason + ", reasonUid=" + reasonUid
484                    + " opPackageName=" + opPackageName + " opUid=" + opUid);
485        }
486
487        try {
488            mBatteryStats.noteWakeUp(reason, reasonUid);
489            if (opPackageName != null) {
490                mAppOps.noteOperation(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
491            }
492        } catch (RemoteException ex) {
493            // Ignore
494        }
495
496    }
497
498    /**
499     * Called when wireless charging has started so as to provide user feedback.
500     */
501    public void onWirelessChargingStarted() {
502        if (DEBUG) {
503            Slog.d(TAG, "onWirelessChargingStarted");
504        }
505
506        mSuspendBlocker.acquire();
507        Message msg = mHandler.obtainMessage(MSG_WIRELESS_CHARGING_STARTED);
508        msg.setAsynchronous(true);
509        mHandler.sendMessage(msg);
510    }
511
512    private void updatePendingBroadcastLocked() {
513        if (!mBroadcastInProgress
514                && mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
515                && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
516                        || mPendingInteractiveState != mBroadcastedInteractiveState)) {
517            mBroadcastInProgress = true;
518            mSuspendBlocker.acquire();
519            Message msg = mHandler.obtainMessage(MSG_BROADCAST);
520            msg.setAsynchronous(true);
521            mHandler.sendMessage(msg);
522        }
523    }
524
525    private void finishPendingBroadcastLocked() {
526        mBroadcastInProgress = false;
527        mSuspendBlocker.release();
528    }
529
530    private void sendUserActivity() {
531        synchronized (mLock) {
532            if (!mUserActivityPending) {
533                return;
534            }
535            mUserActivityPending = false;
536        }
537
538        mPolicy.userActivity();
539    }
540
541    private void sendNextBroadcast() {
542        final int powerState;
543        synchronized (mLock) {
544            if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
545                // Broadcasted power state is unknown.  Send wake up.
546                mPendingWakeUpBroadcast = false;
547                mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
548            } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
549                // Broadcasted power state is awake.  Send asleep if needed.
550                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
551                        || mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {
552                    mPendingGoToSleepBroadcast = false;
553                    mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
554                } else {
555                    finishPendingBroadcastLocked();
556                    return;
557                }
558            } else {
559                // Broadcasted power state is asleep.  Send awake if needed.
560                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
561                        || mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {
562                    mPendingWakeUpBroadcast = false;
563                    mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
564                } else {
565                    finishPendingBroadcastLocked();
566                    return;
567                }
568            }
569
570            mBroadcastStartTime = SystemClock.uptimeMillis();
571            powerState = mBroadcastedInteractiveState;
572        }
573
574        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
575
576        if (powerState == INTERACTIVE_STATE_AWAKE) {
577            sendWakeUpBroadcast();
578        } else {
579            sendGoToSleepBroadcast();
580        }
581    }
582
583    private void sendBrightnessBoostChangedBroadcast() {
584        if (DEBUG) {
585            Slog.d(TAG, "Sending brightness boost changed broadcast.");
586        }
587
588        mContext.sendOrderedBroadcastAsUser(mScreenBrightnessBoostIntent, UserHandle.ALL, null,
589                mScreeBrightnessBoostChangedDone, mHandler, 0, null, null);
590    }
591
592    private final BroadcastReceiver mScreeBrightnessBoostChangedDone = new BroadcastReceiver() {
593        @Override
594        public void onReceive(Context context, Intent intent) {
595            mSuspendBlocker.release();
596        }
597    };
598
599    private void sendWakeUpBroadcast() {
600        if (DEBUG) {
601            Slog.d(TAG, "Sending wake up broadcast.");
602        }
603
604        if (ActivityManagerNative.isSystemReady()) {
605            mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
606                    mWakeUpBroadcastDone, mHandler, 0, null, null);
607        } else {
608            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
609            sendNextBroadcast();
610        }
611    }
612
613    private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
614        @Override
615        public void onReceive(Context context, Intent intent) {
616            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
617                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
618            sendNextBroadcast();
619        }
620    };
621
622    private void sendGoToSleepBroadcast() {
623        if (DEBUG) {
624            Slog.d(TAG, "Sending go to sleep broadcast.");
625        }
626
627        if (ActivityManagerNative.isSystemReady()) {
628            mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
629                    mGoToSleepBroadcastDone, mHandler, 0, null, null);
630        } else {
631            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
632            sendNextBroadcast();
633        }
634    }
635
636    private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() {
637        @Override
638        public void onReceive(Context context, Intent intent) {
639            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
640                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
641            sendNextBroadcast();
642        }
643    };
644
645    private void playWirelessChargingStartedSound() {
646        final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
647                Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
648        final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
649                Settings.Global.WIRELESS_CHARGING_STARTED_SOUND);
650        if (enabled && soundPath != null) {
651            final Uri soundUri = Uri.parse("file://" + soundPath);
652            if (soundUri != null) {
653                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
654                if (sfx != null) {
655                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);
656                    sfx.play();
657                }
658            }
659        }
660
661        mSuspendBlocker.release();
662    }
663
664    private final class NotifierHandler extends Handler {
665        public NotifierHandler(Looper looper) {
666            super(looper, null, true /*async*/);
667        }
668
669        @Override
670        public void handleMessage(Message msg) {
671            switch (msg.what) {
672                case MSG_USER_ACTIVITY:
673                    sendUserActivity();
674                    break;
675
676                case MSG_BROADCAST:
677                    sendNextBroadcast();
678                    break;
679
680                case MSG_WIRELESS_CHARGING_STARTED:
681                    playWirelessChargingStartedSound();
682                    break;
683                case MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED:
684                    sendBrightnessBoostChangedBroadcast();
685                    break;
686            }
687        }
688    }
689}
690