Notifier.java revision 6714e86a545a38d8726e75fcd271947b4259c759
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.Process;
42import android.os.RemoteException;
43import android.os.SystemClock;
44import android.os.UserHandle;
45import android.os.WorkSource;
46import android.provider.Settings;
47import android.util.EventLog;
48import android.util.Slog;
49import android.view.WindowManagerPolicy;
50
51/**
52 * Sends broadcasts about important power state changes.
53 * <p>
54 * This methods of this class may be called by the power manager service while
55 * its lock is being held.  Internally it takes care of sending broadcasts to
56 * notify other components of the system or applications asynchronously.
57 * </p><p>
58 * The notifier is designed to collapse unnecessary broadcasts when it is not
59 * possible for the system to have observed an intermediate state.
60 * </p><p>
61 * For example, if the device wakes up, goes to sleep, wakes up again and goes to
62 * sleep again before the wake up notification is sent, then the system will
63 * be told about only one wake up and sleep.  However, we always notify the
64 * fact that at least one transition occurred.  It is especially important to
65 * tell the system when we go to sleep so that it can lock the keyguard if needed.
66 * </p>
67 */
68final class Notifier {
69    private static final String TAG = "PowerManagerNotifier";
70
71    private static final boolean DEBUG = false;
72
73    private static final int POWER_STATE_UNKNOWN = 0;
74    private static final int POWER_STATE_AWAKE = 1;
75    private static final int POWER_STATE_ASLEEP = 2;
76
77    private static final int MSG_USER_ACTIVITY = 1;
78    private static final int MSG_BROADCAST = 2;
79    private static final int MSG_WIRELESS_CHARGING_STARTED = 3;
80
81    private final Object mLock = new Object();
82
83    private final Context mContext;
84    private final IBatteryStats mBatteryStats;
85    private final IAppOpsService mAppOps;
86    private final SuspendBlocker mSuspendBlocker;
87    private final ScreenOnBlocker mScreenOnBlocker;
88    private final WindowManagerPolicy mPolicy;
89    private final ActivityManagerInternal mActivityManagerInternal;
90    private final InputManagerInternal mInputManagerInternal;
91
92    private final NotifierHandler mHandler;
93    private final Intent mScreenOnIntent;
94    private final Intent mScreenOffIntent;
95
96    // The current power state.
97    private int mActualPowerState;
98    private int mLastGoToSleepReason;
99
100    // True if there is a pending transition that needs to be reported.
101    private boolean mPendingWakeUpBroadcast;
102    private boolean mPendingGoToSleepBroadcast;
103
104    // The currently broadcasted power state.  This reflects what other parts of the
105    // system have observed.
106    private int mBroadcastedPowerState;
107    private boolean mBroadcastInProgress;
108    private long mBroadcastStartTime;
109
110    // True if a user activity message should be sent.
111    private boolean mUserActivityPending;
112
113    // The currently active screen on listener. This field is non-null whenever the
114    // ScreenOnBlocker has been acquired and we are awaiting a callback to release it.
115    private ScreenOnUnblocker mPendingScreenOnUnblocker;
116
117    public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
118            IAppOpsService appOps, SuspendBlocker suspendBlocker, ScreenOnBlocker screenOnBlocker,
119            WindowManagerPolicy policy) {
120        mContext = context;
121        mBatteryStats = batteryStats;
122        mAppOps = appOps;
123        mSuspendBlocker = suspendBlocker;
124        mScreenOnBlocker = screenOnBlocker;
125        mPolicy = policy;
126        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
127        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
128
129        mHandler = new NotifierHandler(looper);
130        mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
131        mScreenOnIntent.addFlags(
132                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
133        mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
134        mScreenOffIntent.addFlags(
135                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
136
137        // Initialize interactive state for battery stats.
138        try {
139            mBatteryStats.noteInteractive(true);
140        } catch (RemoteException ex) { }
141    }
142
143    /**
144     * Called when a wake lock is acquired.
145     */
146    public void onWakeLockAcquired(int flags, String tag, String packageName,
147            int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
148        if (DEBUG) {
149            Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
150                    + "\", packageName=" + packageName
151                    + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
152                    + ", workSource=" + workSource);
153        }
154
155        try {
156            final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
157            boolean unimportantForLogging = (flags&PowerManager.UNIMPORTANT_FOR_LOGGING) != 0
158                    && ownerUid == Process.SYSTEM_UID;
159            if (workSource != null) {
160                mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, historyTag,
161                        monitorType, unimportantForLogging);
162            } else {
163                mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
164                        monitorType, unimportantForLogging);
165                // XXX need to deal with disabled operations.
166                mAppOps.startOperation(AppOpsManager.getToken(mAppOps),
167                        AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
168            }
169        } catch (RemoteException ex) {
170            // Ignore
171        }
172    }
173
174    /**
175     * Called when a wake lock is changing.
176     */
177    public void onWakeLockChanging(int flags, String tag, String packageName,
178            int ownerUid, int ownerPid, WorkSource workSource, String historyTag,
179            int newFlags, String newTag, String newPackageName, int newOwnerUid,
180            int newOwnerPid, WorkSource newWorkSource, String newHistoryTag) {
181
182        if (workSource != null && newWorkSource != null) {
183            final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
184            final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags);
185            boolean unimportantForLogging = (newFlags&PowerManager.UNIMPORTANT_FOR_LOGGING) != 0
186                    && newOwnerUid == Process.SYSTEM_UID;
187            if (DEBUG) {
188                Slog.d(TAG, "onWakeLockChanging: flags=" + newFlags + ", tag=\"" + newTag
189                        + "\", packageName=" + newPackageName
190                        + ", ownerUid=" + newOwnerUid + ", ownerPid=" + newOwnerPid
191                        + ", workSource=" + newWorkSource);
192            }
193            try {
194                mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, historyTag,
195                        monitorType, newWorkSource, newOwnerPid, newTag, newHistoryTag,
196                        newMonitorType, unimportantForLogging);
197            } catch (RemoteException ex) {
198                // Ignore
199            }
200        } else {
201            onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag);
202            onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid,
203                    newWorkSource, newHistoryTag);
204        }
205    }
206
207    /**
208     * Called when a wake lock is released.
209     */
210    public void onWakeLockReleased(int flags, String tag, String packageName,
211            int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
212        if (DEBUG) {
213            Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag
214                    + "\", packageName=" + packageName
215                    + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
216                    + ", workSource=" + workSource);
217        }
218
219        try {
220            final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
221            if (workSource != null) {
222                mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, historyTag,
223                        monitorType);
224            } else {
225                mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, historyTag, monitorType);
226                mAppOps.finishOperation(AppOpsManager.getToken(mAppOps),
227                        AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
228            }
229        } catch (RemoteException ex) {
230            // Ignore
231        }
232    }
233
234    private static int getBatteryStatsWakeLockMonitorType(int flags) {
235        switch (flags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
236            case PowerManager.PARTIAL_WAKE_LOCK:
237            case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
238                return BatteryStats.WAKE_TYPE_PARTIAL;
239            default:
240                return BatteryStats.WAKE_TYPE_FULL;
241        }
242    }
243
244    /**
245     * Notifies that the device is changing interactive state.
246     */
247    public void onInteractiveStateChangeStarted(boolean interactive, final int reason) {
248        if (DEBUG) {
249            Slog.d(TAG, "onInteractiveChangeStarted: interactive=" + interactive
250                    + ", reason=" + reason);
251        }
252
253        synchronized (mLock) {
254            if (interactive) {
255                // Waking up...
256                if (mActualPowerState != POWER_STATE_AWAKE) {
257                    mActualPowerState = POWER_STATE_AWAKE;
258                    mPendingWakeUpBroadcast = true;
259                    if (mPendingScreenOnUnblocker == null) {
260                        mScreenOnBlocker.acquire();
261                    }
262                    final ScreenOnUnblocker unblocker = new ScreenOnUnblocker();
263                    mPendingScreenOnUnblocker = unblocker;
264                    mHandler.post(new Runnable() {
265                        @Override
266                        public void run() {
267                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
268                            mPolicy.wakingUp(unblocker);
269                            mActivityManagerInternal.wakingUp();
270                        }
271                    });
272                    updatePendingBroadcastLocked();
273                }
274            } else {
275                // Going to sleep...
276                mLastGoToSleepReason = reason;
277            }
278        }
279
280        mInputManagerInternal.setInteractive(interactive);
281
282        if (interactive) {
283            try {
284                mBatteryStats.noteInteractive(true);
285            } catch (RemoteException ex) { }
286        }
287    }
288
289    /**
290     * Notifies that the device has finished changing interactive state.
291     */
292    public void onInteractiveStateChangeFinished(boolean interactive) {
293        if (DEBUG) {
294            Slog.d(TAG, "onInteractiveChangeFinished");
295        }
296
297        synchronized (mLock) {
298            if (!interactive) {
299                // Finished going to sleep...
300                // This is a good time to make transitions that we don't want the user to see,
301                // such as bringing the key guard to focus.  There's no guarantee for this,
302                // however because the user could turn the device on again at any time.
303                // Some things may need to be protected by other mechanisms that defer screen on.
304                if (mActualPowerState != POWER_STATE_ASLEEP) {
305                    mActualPowerState = POWER_STATE_ASLEEP;
306                    mPendingGoToSleepBroadcast = true;
307                    if (mUserActivityPending) {
308                        mUserActivityPending = false;
309                        mHandler.removeMessages(MSG_USER_ACTIVITY);
310                    }
311                    mHandler.post(new Runnable() {
312                        @Override
313                        public void run() {
314                            int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
315                            switch (mLastGoToSleepReason) {
316                                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
317                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
318                                    break;
319                                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
320                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
321                                    break;
322                            }
323                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
324                            mPolicy.goingToSleep(why);
325                            mActivityManagerInternal.goingToSleep();
326                        }
327                    });
328                    updatePendingBroadcastLocked();
329                }
330            }
331        }
332
333        if (!interactive) {
334            try {
335                mBatteryStats.noteInteractive(false);
336            } catch (RemoteException ex) { }
337        }
338    }
339
340    /**
341     * Called when there has been user activity.
342     */
343    public void onUserActivity(int event, int uid) {
344        if (DEBUG) {
345            Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid);
346        }
347
348        try {
349            mBatteryStats.noteUserActivity(uid, event);
350        } catch (RemoteException ex) {
351            // Ignore
352        }
353
354        synchronized (mLock) {
355            if (!mUserActivityPending) {
356                mUserActivityPending = true;
357                Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
358                msg.setAsynchronous(true);
359                mHandler.sendMessage(msg);
360            }
361        }
362    }
363
364    /**
365     * Called when wireless charging has started so as to provide user feedback.
366     */
367    public void onWirelessChargingStarted() {
368        if (DEBUG) {
369            Slog.d(TAG, "onWirelessChargingStarted");
370        }
371
372        mSuspendBlocker.acquire();
373        Message msg = mHandler.obtainMessage(MSG_WIRELESS_CHARGING_STARTED);
374        msg.setAsynchronous(true);
375        mHandler.sendMessage(msg);
376    }
377
378    private void updatePendingBroadcastLocked() {
379        if (!mBroadcastInProgress
380                && mActualPowerState != POWER_STATE_UNKNOWN
381                && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
382                        || mActualPowerState != mBroadcastedPowerState)) {
383            mBroadcastInProgress = true;
384            mSuspendBlocker.acquire();
385            Message msg = mHandler.obtainMessage(MSG_BROADCAST);
386            msg.setAsynchronous(true);
387            mHandler.sendMessage(msg);
388        }
389    }
390
391    private void finishPendingBroadcastLocked() {
392        mBroadcastInProgress = false;
393        mSuspendBlocker.release();
394    }
395
396    private void sendUserActivity() {
397        synchronized (mLock) {
398            if (!mUserActivityPending) {
399                return;
400            }
401            mUserActivityPending = false;
402        }
403
404        mPolicy.userActivity();
405    }
406
407    private void sendNextBroadcast() {
408        final int powerState;
409        final int goToSleepReason;
410        synchronized (mLock) {
411            if (mBroadcastedPowerState == POWER_STATE_UNKNOWN) {
412                // Broadcasted power state is unknown.  Send wake up.
413                mPendingWakeUpBroadcast = false;
414                mBroadcastedPowerState = POWER_STATE_AWAKE;
415            } else if (mBroadcastedPowerState == POWER_STATE_AWAKE) {
416                // Broadcasted power state is awake.  Send asleep if needed.
417                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
418                        || mActualPowerState == POWER_STATE_ASLEEP) {
419                    mPendingGoToSleepBroadcast = false;
420                    mBroadcastedPowerState = POWER_STATE_ASLEEP;
421                } else {
422                    finishPendingBroadcastLocked();
423                    return;
424                }
425            } else {
426                // Broadcasted power state is asleep.  Send awake if needed.
427                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
428                        || mActualPowerState == POWER_STATE_AWAKE) {
429                    mPendingWakeUpBroadcast = false;
430                    mBroadcastedPowerState = POWER_STATE_AWAKE;
431                } else {
432                    finishPendingBroadcastLocked();
433                    return;
434                }
435            }
436
437            mBroadcastStartTime = SystemClock.uptimeMillis();
438            powerState = mBroadcastedPowerState;
439        }
440
441        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
442
443        if (powerState == POWER_STATE_AWAKE) {
444            sendWakeUpBroadcast();
445        } else {
446            sendGoToSleepBroadcast();
447        }
448    }
449
450    private void sendWakeUpBroadcast() {
451        if (DEBUG) {
452            Slog.d(TAG, "Sending wake up broadcast.");
453        }
454
455        if (ActivityManagerNative.isSystemReady()) {
456            mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
457                    mWakeUpBroadcastDone, mHandler, 0, null, null);
458        } else {
459            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
460            sendNextBroadcast();
461        }
462    }
463
464    private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
465        @Override
466        public void onScreenOn() {
467            synchronized (mLock) {
468                if (mPendingScreenOnUnblocker == this) {
469                    mPendingScreenOnUnblocker = null;
470                    mScreenOnBlocker.release();
471                }
472            }
473        }
474    }
475
476    private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
477        @Override
478        public void onReceive(Context context, Intent intent) {
479            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
480                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
481            sendNextBroadcast();
482        }
483    };
484
485    private void sendGoToSleepBroadcast() {
486        if (DEBUG) {
487            Slog.d(TAG, "Sending go to sleep broadcast.");
488        }
489
490        if (ActivityManagerNative.isSystemReady()) {
491            mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
492                    mGoToSleepBroadcastDone, mHandler, 0, null, null);
493        } else {
494            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
495            sendNextBroadcast();
496        }
497    }
498
499    private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() {
500        @Override
501        public void onReceive(Context context, Intent intent) {
502            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
503                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
504            sendNextBroadcast();
505        }
506    };
507
508    private void playWirelessChargingStartedSound() {
509        final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
510                Settings.Global.WIRELESS_CHARGING_STARTED_SOUND);
511        if (soundPath != null) {
512            final Uri soundUri = Uri.parse("file://" + soundPath);
513            if (soundUri != null) {
514                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
515                if (sfx != null) {
516                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);
517                    sfx.play();
518                }
519            }
520        }
521
522        mSuspendBlocker.release();
523    }
524
525    private final class NotifierHandler extends Handler {
526        public NotifierHandler(Looper looper) {
527            super(looper, null, true /*async*/);
528        }
529
530        @Override
531        public void handleMessage(Message msg) {
532            switch (msg.what) {
533                case MSG_USER_ACTIVITY:
534                    sendUserActivity();
535                    break;
536
537                case MSG_BROADCAST:
538                    sendNextBroadcast();
539                    break;
540
541                case MSG_WIRELESS_CHARGING_STARTED:
542                    playWirelessChargingStartedSound();
543                    break;
544            }
545        }
546    }
547}
548