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