Notifier.java revision 3edf5272fb2185403dfe64b9722b9fc9b9de80f8
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        synchronized (mLock) {
410            if (mBroadcastedPowerState == POWER_STATE_UNKNOWN) {
411                // Broadcasted power state is unknown.  Send wake up.
412                mPendingWakeUpBroadcast = false;
413                mBroadcastedPowerState = POWER_STATE_AWAKE;
414            } else if (mBroadcastedPowerState == POWER_STATE_AWAKE) {
415                // Broadcasted power state is awake.  Send asleep if needed.
416                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
417                        || mActualPowerState == POWER_STATE_ASLEEP) {
418                    mPendingGoToSleepBroadcast = false;
419                    mBroadcastedPowerState = POWER_STATE_ASLEEP;
420                } else {
421                    finishPendingBroadcastLocked();
422                    return;
423                }
424            } else {
425                // Broadcasted power state is asleep.  Send awake if needed.
426                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
427                        || mActualPowerState == POWER_STATE_AWAKE) {
428                    mPendingWakeUpBroadcast = false;
429                    mBroadcastedPowerState = POWER_STATE_AWAKE;
430                } else {
431                    finishPendingBroadcastLocked();
432                    return;
433                }
434            }
435
436            mBroadcastStartTime = SystemClock.uptimeMillis();
437            powerState = mBroadcastedPowerState;
438        }
439
440        EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);
441
442        if (powerState == POWER_STATE_AWAKE) {
443            sendWakeUpBroadcast();
444        } else {
445            sendGoToSleepBroadcast();
446        }
447    }
448
449    private void sendWakeUpBroadcast() {
450        if (DEBUG) {
451            Slog.d(TAG, "Sending wake up broadcast.");
452        }
453
454        if (ActivityManagerNative.isSystemReady()) {
455            mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
456                    mWakeUpBroadcastDone, mHandler, 0, null, null);
457        } else {
458            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
459            sendNextBroadcast();
460        }
461    }
462
463    private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
464        @Override
465        public void onScreenOn() {
466            synchronized (mLock) {
467                if (mPendingScreenOnUnblocker == this) {
468                    mPendingScreenOnUnblocker = null;
469                    mScreenOnBlocker.release();
470                }
471            }
472        }
473    }
474
475    private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
476        @Override
477        public void onReceive(Context context, Intent intent) {
478            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
479                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
480            sendNextBroadcast();
481        }
482    };
483
484    private void sendGoToSleepBroadcast() {
485        if (DEBUG) {
486            Slog.d(TAG, "Sending go to sleep broadcast.");
487        }
488
489        if (ActivityManagerNative.isSystemReady()) {
490            mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
491                    mGoToSleepBroadcastDone, mHandler, 0, null, null);
492        } else {
493            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
494            sendNextBroadcast();
495        }
496    }
497
498    private final BroadcastReceiver mGoToSleepBroadcastDone = new BroadcastReceiver() {
499        @Override
500        public void onReceive(Context context, Intent intent) {
501            EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
502                    SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
503            sendNextBroadcast();
504        }
505    };
506
507    private void playWirelessChargingStartedSound() {
508        final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
509                Settings.Global.WIRELESS_CHARGING_STARTED_SOUND);
510        if (soundPath != null) {
511            final Uri soundUri = Uri.parse("file://" + soundPath);
512            if (soundUri != null) {
513                final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
514                if (sfx != null) {
515                    sfx.setStreamType(AudioManager.STREAM_SYSTEM);
516                    sfx.play();
517                }
518            }
519        }
520
521        mSuspendBlocker.release();
522    }
523
524    private final class NotifierHandler extends Handler {
525        public NotifierHandler(Looper looper) {
526            super(looper, null, true /*async*/);
527        }
528
529        @Override
530        public void handleMessage(Message msg) {
531            switch (msg.what) {
532                case MSG_USER_ACTIVITY:
533                    sendUserActivity();
534                    break;
535
536                case MSG_BROADCAST:
537                    sendNextBroadcast();
538                    break;
539
540                case MSG_WIRELESS_CHARGING_STARTED:
541                    playWirelessChargingStartedSound();
542                    break;
543            }
544        }
545    }
546}
547