Notifier.java revision 36c4db8bd3bd7dad4b6cb8abd9cdc1a627fe3bbc
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                    mHandler.post(new Runnable() {
260                        @Override
261                        public void run() {
262                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
263                            mPolicy.wakingUp();
264                            mActivityManagerInternal.wakingUp();
265                        }
266                    });
267                    updatePendingBroadcastLocked();
268                }
269            } else {
270                // Going to sleep...
271                mLastGoToSleepReason = reason;
272            }
273        }
274
275        mInputManagerInternal.setInteractive(interactive);
276
277        if (interactive) {
278            try {
279                mBatteryStats.noteInteractive(true);
280            } catch (RemoteException ex) { }
281        }
282    }
283
284    /**
285     * Notifies that the device has finished changing interactive state.
286     */
287    public void onInteractiveStateChangeFinished(boolean interactive) {
288        if (DEBUG) {
289            Slog.d(TAG, "onInteractiveChangeFinished");
290        }
291
292        synchronized (mLock) {
293            if (!interactive) {
294                // Finished going to sleep...
295                // This is a good time to make transitions that we don't want the user to see,
296                // such as bringing the key guard to focus.  There's no guarantee for this,
297                // however because the user could turn the device on again at any time.
298                // Some things may need to be protected by other mechanisms that defer screen on.
299                if (mActualPowerState != POWER_STATE_ASLEEP) {
300                    mActualPowerState = POWER_STATE_ASLEEP;
301                    mPendingGoToSleepBroadcast = true;
302                    if (mUserActivityPending) {
303                        mUserActivityPending = false;
304                        mHandler.removeMessages(MSG_USER_ACTIVITY);
305                    }
306                    mHandler.post(new Runnable() {
307                        @Override
308                        public void run() {
309                            int why = WindowManagerPolicy.OFF_BECAUSE_OF_USER;
310                            switch (mLastGoToSleepReason) {
311                                case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:
312                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_ADMIN;
313                                    break;
314                                case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:
315                                    why = WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT;
316                                    break;
317                            }
318                            EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
319                            mPolicy.goingToSleep(why);
320                            mActivityManagerInternal.goingToSleep();
321                        }
322                    });
323                    updatePendingBroadcastLocked();
324                }
325            }
326        }
327
328        if (!interactive) {
329            try {
330                mBatteryStats.noteInteractive(false);
331            } catch (RemoteException ex) { }
332        }
333    }
334
335    /**
336     * Notifies that screen is about to be turned on (any state other than off).
337     */
338    public void onScreenTurningOn() {
339        synchronized (mLock) {
340            final ScreenOnUnblocker unblocker = blockScreenOnLocked();
341            mHandler.post(new Runnable() {
342                @Override
343                public void run() {
344                    mPolicy.screenTurningOn(unblocker);
345                }
346            });
347        }
348    }
349
350    /**
351     * Called when there has been user activity.
352     */
353    public void onUserActivity(int event, int uid) {
354        if (DEBUG) {
355            Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid);
356        }
357
358        try {
359            mBatteryStats.noteUserActivity(uid, event);
360        } catch (RemoteException ex) {
361            // Ignore
362        }
363
364        synchronized (mLock) {
365            if (!mUserActivityPending) {
366                mUserActivityPending = true;
367                Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY);
368                msg.setAsynchronous(true);
369                mHandler.sendMessage(msg);
370            }
371        }
372    }
373
374    /**
375     * Called when wireless charging has started so as to provide user feedback.
376     */
377    public void onWirelessChargingStarted() {
378        if (DEBUG) {
379            Slog.d(TAG, "onWirelessChargingStarted");
380        }
381
382        mSuspendBlocker.acquire();
383        Message msg = mHandler.obtainMessage(MSG_WIRELESS_CHARGING_STARTED);
384        msg.setAsynchronous(true);
385        mHandler.sendMessage(msg);
386    }
387
388    private void updatePendingBroadcastLocked() {
389        if (!mBroadcastInProgress
390                && mActualPowerState != POWER_STATE_UNKNOWN
391                && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
392                        || mActualPowerState != mBroadcastedPowerState)) {
393            mBroadcastInProgress = true;
394            mSuspendBlocker.acquire();
395            Message msg = mHandler.obtainMessage(MSG_BROADCAST);
396            msg.setAsynchronous(true);
397            mHandler.sendMessage(msg);
398        }
399    }
400
401    private void finishPendingBroadcastLocked() {
402        mBroadcastInProgress = false;
403        mSuspendBlocker.release();
404    }
405
406    private void sendUserActivity() {
407        synchronized (mLock) {
408            if (!mUserActivityPending) {
409                return;
410            }
411            mUserActivityPending = false;
412        }
413
414        mPolicy.userActivity();
415    }
416
417    private void sendNextBroadcast() {
418        final int powerState;
419        synchronized (mLock) {
420            if (mBroadcastedPowerState == POWER_STATE_UNKNOWN) {
421                // Broadcasted power state is unknown.  Send wake up.
422                mPendingWakeUpBroadcast = false;
423                mBroadcastedPowerState = POWER_STATE_AWAKE;
424            } else if (mBroadcastedPowerState == POWER_STATE_AWAKE) {
425                // Broadcasted power state is awake.  Send asleep if needed.
426                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
427                        || mActualPowerState == POWER_STATE_ASLEEP) {
428                    mPendingGoToSleepBroadcast = false;
429                    mBroadcastedPowerState = POWER_STATE_ASLEEP;
430                } else {
431                    finishPendingBroadcastLocked();
432                    return;
433                }
434            } else {
435                // Broadcasted power state is asleep.  Send awake if needed.
436                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
437                        || mActualPowerState == POWER_STATE_AWAKE) {
438                    mPendingWakeUpBroadcast = false;
439                    mBroadcastedPowerState = POWER_STATE_AWAKE;
440                } else {
441                    finishPendingBroadcastLocked();
442                    return;
443                }
444            }
445
446            mBroadcastStartTime = SystemClock.uptimeMillis();
447            powerState = mBroadcastedPowerState;
448        }
449
450        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
451
452        if (powerState == POWER_STATE_AWAKE) {
453            sendWakeUpBroadcast();
454        } else {
455            sendGoToSleepBroadcast();
456        }
457    }
458
459    private void sendWakeUpBroadcast() {
460        if (DEBUG) {
461            Slog.d(TAG, "Sending wake up broadcast.");
462        }
463
464        if (ActivityManagerNative.isSystemReady()) {
465            mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
466                    mWakeUpBroadcastDone, mHandler, 0, null, null);
467        } else {
468            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
469            sendNextBroadcast();
470        }
471    }
472
473    private ScreenOnUnblocker blockScreenOnLocked() {
474        if (mPendingScreenOnUnblocker == null) {
475            mScreenOnBlocker.acquire();
476        }
477        mPendingScreenOnUnblocker = new ScreenOnUnblocker();
478        return mPendingScreenOnUnblocker;
479    }
480
481    private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
482        @Override
483        public void onScreenOn() {
484            synchronized (mLock) {
485                if (mPendingScreenOnUnblocker == this) {
486                    mPendingScreenOnUnblocker = null;
487                    mScreenOnBlocker.release();
488                }
489            }
490        }
491    }
492
493    private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
494        @Override
495        public void onReceive(Context context, Intent intent) {
496            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
497                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
498            sendNextBroadcast();
499        }
500    };
501
502    private void sendGoToSleepBroadcast() {
503        if (DEBUG) {
504            Slog.d(TAG, "Sending go to sleep broadcast.");
505        }
506
507        if (ActivityManagerNative.isSystemReady()) {
508            mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
509                    mGoToSleepBroadcastDone, mHandler, 0, null, null);
510        } else {
511            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
512            sendNextBroadcast();
513        }
514    }
515
516    private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() {
517        @Override
518        public void onReceive(Context context, Intent intent) {
519            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
520                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
521            sendNextBroadcast();
522        }
523    };
524
525    private void playWirelessChargingStartedSound() {
526        final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
527                Settings.Global.WIRELESS_CHARGING_STARTED_SOUND);
528        if (soundPath != null) {
529            final Uri soundUri = Uri.parse("file://" + soundPath);
530            if (soundUri != null) {
531                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
532                if (sfx != null) {
533                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);
534                    sfx.play();
535                }
536            }
537        }
538
539        mSuspendBlocker.release();
540    }
541
542    private final class NotifierHandler extends Handler {
543        public NotifierHandler(Looper looper) {
544            super(looper, null, true /*async*/);
545        }
546
547        @Override
548        public void handleMessage(Message msg) {
549            switch (msg.what) {
550                case MSG_USER_ACTIVITY:
551                    sendUserActivity();
552                    break;
553
554                case MSG_BROADCAST:
555                    sendNextBroadcast();
556                    break;
557
558                case MSG_WIRELESS_CHARGING_STARTED:
559                    playWirelessChargingStartedSound();
560                    break;
561            }
562        }
563    }
564}
565