DreamManagerService.java revision 69515b2261335be4bd8510ed5a40a6bed3d7b48f
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.dreams;
18
19import static android.Manifest.permission.BIND_DREAM_SERVICE;
20
21import com.android.internal.hardware.AmbientDisplayConfiguration;
22import com.android.internal.util.DumpUtils;
23import com.android.server.FgThread;
24import com.android.server.LocalServices;
25import com.android.server.SystemService;
26
27import android.Manifest;
28import android.app.ActivityManager;
29import android.content.BroadcastReceiver;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.pm.PackageManager;
35import android.content.pm.PackageManager.NameNotFoundException;
36import android.content.pm.ServiceInfo;
37import android.database.ContentObserver;
38import android.hardware.input.InputManagerInternal;
39import android.os.Binder;
40import android.os.Build;
41import android.os.Handler;
42import android.os.IBinder;
43import android.os.Looper;
44import android.os.PowerManager;
45import android.os.PowerManagerInternal;
46import android.os.SystemClock;
47import android.os.SystemProperties;
48import android.os.UserHandle;
49import android.provider.Settings;
50import android.service.dreams.DreamManagerInternal;
51import android.service.dreams.DreamService;
52import android.service.dreams.IDreamManager;
53import android.text.TextUtils;
54import android.util.Slog;
55import android.view.Display;
56
57import java.io.FileDescriptor;
58import java.io.PrintWriter;
59import java.util.ArrayList;
60import java.util.List;
61
62import libcore.util.Objects;
63
64/**
65 * Service api for managing dreams.
66 *
67 * @hide
68 */
69public final class DreamManagerService extends SystemService {
70    private static final boolean DEBUG = false;
71    private static final String TAG = "DreamManagerService";
72
73    private final Object mLock = new Object();
74
75    private final Context mContext;
76    private final DreamHandler mHandler;
77    private final DreamController mController;
78    private final PowerManager mPowerManager;
79    private final PowerManagerInternal mPowerManagerInternal;
80    private final PowerManager.WakeLock mDozeWakeLock;
81
82    private Binder mCurrentDreamToken;
83    private ComponentName mCurrentDreamName;
84    private int mCurrentDreamUserId;
85    private boolean mCurrentDreamIsTest;
86    private boolean mCurrentDreamCanDoze;
87    private boolean mCurrentDreamIsDozing;
88    private boolean mCurrentDreamIsWaking;
89    private Runnable mStopDreamRunnable;
90    private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
91    private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
92
93    private AmbientDisplayConfiguration mDozeConfig;
94
95    public DreamManagerService(Context context) {
96        super(context);
97        mContext = context;
98        mHandler = new DreamHandler(FgThread.get().getLooper());
99        mController = new DreamController(context, mHandler, mControllerListener);
100
101        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
102        mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
103        mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
104        mDozeConfig = new AmbientDisplayConfiguration(mContext);
105    }
106
107    @Override
108    public void onStart() {
109        publishBinderService(DreamService.DREAM_SERVICE, new BinderService());
110        publishLocalService(DreamManagerInternal.class, new LocalService());
111    }
112
113    @Override
114    public void onBootPhase(int phase) {
115        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
116            if (Build.IS_DEBUGGABLE) {
117                SystemProperties.addChangeCallback(mSystemPropertiesChanged);
118            }
119            mContext.registerReceiver(new BroadcastReceiver() {
120                @Override
121                public void onReceive(Context context, Intent intent) {
122                    writePulseGestureEnabled();
123                    synchronized (mLock) {
124                        stopDreamLocked(false /*immediate*/);
125                    }
126                }
127            }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
128            mContext.getContentResolver().registerContentObserver(
129                    Settings.Secure.getUriFor(Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP), false,
130                    mDozeEnabledObserver, UserHandle.USER_ALL);
131            writePulseGestureEnabled();
132        }
133    }
134
135    private void dumpInternal(PrintWriter pw) {
136        pw.println("DREAM MANAGER (dumpsys dreams)");
137        pw.println();
138        pw.println("mCurrentDreamToken=" + mCurrentDreamToken);
139        pw.println("mCurrentDreamName=" + mCurrentDreamName);
140        pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId);
141        pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest);
142        pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
143        pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
144        pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
145        pw.println("mCurrentDreamDozeScreenState="
146                + Display.stateToString(mCurrentDreamDozeScreenState));
147        pw.println("mCurrentDreamDozeScreenBrightness=" + mCurrentDreamDozeScreenBrightness);
148        pw.println("getDozeComponent()=" + getDozeComponent());
149        pw.println();
150
151        DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
152            @Override
153            public void dump(PrintWriter pw, String prefix) {
154                mController.dump(pw);
155            }
156        }, pw, "", 200);
157    }
158
159    private boolean isDreamingInternal() {
160        synchronized (mLock) {
161            return mCurrentDreamToken != null && !mCurrentDreamIsTest
162                    && !mCurrentDreamIsWaking;
163        }
164    }
165
166    private void requestDreamInternal() {
167        // Ask the power manager to nap.  It will eventually call back into
168        // startDream() if/when it is appropriate to start dreaming.
169        // Because napping could cause the screen to turn off immediately if the dream
170        // cannot be started, we keep one eye open and gently poke user activity.
171        long time = SystemClock.uptimeMillis();
172        mPowerManager.userActivity(time, true /*noChangeLights*/);
173        mPowerManager.nap(time);
174    }
175
176    private void requestAwakenInternal() {
177        // Treat an explicit request to awaken as user activity so that the
178        // device doesn't immediately go to sleep if the timeout expired,
179        // for example when being undocked.
180        long time = SystemClock.uptimeMillis();
181        mPowerManager.userActivity(time, false /*noChangeLights*/);
182        stopDreamInternal(false /*immediate*/);
183    }
184
185    private void finishSelfInternal(IBinder token, boolean immediate) {
186        if (DEBUG) {
187            Slog.d(TAG, "Dream finished: " + token + ", immediate=" + immediate);
188        }
189
190        // Note that a dream finishing and self-terminating is not
191        // itself considered user activity.  If the dream is ending because
192        // the user interacted with the device then user activity will already
193        // have been poked so the device will stay awake a bit longer.
194        // If the dream is ending on its own for other reasons and no wake
195        // locks are held and the user activity timeout has expired then the
196        // device may simply go to sleep.
197        synchronized (mLock) {
198            if (mCurrentDreamToken == token) {
199                stopDreamLocked(immediate);
200            }
201        }
202    }
203
204    private void testDreamInternal(ComponentName dream, int userId) {
205        synchronized (mLock) {
206            startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId);
207        }
208    }
209
210    private void startDreamInternal(boolean doze) {
211        final int userId = ActivityManager.getCurrentUser();
212        final ComponentName dream = chooseDreamForUser(doze, userId);
213        if (dream != null) {
214            synchronized (mLock) {
215                startDreamLocked(dream, false /*isTest*/, doze, userId);
216            }
217        }
218    }
219
220    private void stopDreamInternal(boolean immediate) {
221        synchronized (mLock) {
222            stopDreamLocked(immediate);
223        }
224    }
225
226    private void startDozingInternal(IBinder token, int screenState,
227            int screenBrightness) {
228        if (DEBUG) {
229            Slog.d(TAG, "Dream requested to start dozing: " + token
230                    + ", screenState=" + screenState
231                    + ", screenBrightness=" + screenBrightness);
232        }
233
234        synchronized (mLock) {
235            if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
236                mCurrentDreamDozeScreenState = screenState;
237                mCurrentDreamDozeScreenBrightness = screenBrightness;
238                mPowerManagerInternal.setDozeOverrideFromDreamManager(
239                        screenState, screenBrightness);
240                if (!mCurrentDreamIsDozing) {
241                    mCurrentDreamIsDozing = true;
242                    mDozeWakeLock.acquire();
243                }
244            }
245        }
246    }
247
248    private void stopDozingInternal(IBinder token) {
249        if (DEBUG) {
250            Slog.d(TAG, "Dream requested to stop dozing: " + token);
251        }
252
253        synchronized (mLock) {
254            if (mCurrentDreamToken == token && mCurrentDreamIsDozing) {
255                mCurrentDreamIsDozing = false;
256                mDozeWakeLock.release();
257                mPowerManagerInternal.setDozeOverrideFromDreamManager(
258                        Display.STATE_UNKNOWN, PowerManager.BRIGHTNESS_DEFAULT);
259            }
260        }
261    }
262
263    private ComponentName chooseDreamForUser(boolean doze, int userId) {
264        if (doze) {
265            ComponentName dozeComponent = getDozeComponent(userId);
266            return validateDream(dozeComponent) ? dozeComponent : null;
267        }
268        ComponentName[] dreams = getDreamComponentsForUser(userId);
269        return dreams != null && dreams.length != 0 ? dreams[0] : null;
270    }
271
272    private boolean validateDream(ComponentName component) {
273        if (component == null) return false;
274        final ServiceInfo serviceInfo = getServiceInfo(component);
275        if (serviceInfo == null) {
276            Slog.w(TAG, "Dream " + component + " does not exist");
277            return false;
278        } else if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP
279                && !BIND_DREAM_SERVICE.equals(serviceInfo.permission)) {
280            Slog.w(TAG, "Dream " + component
281                    + " is not available because its manifest is missing the " + BIND_DREAM_SERVICE
282                    + " permission on the dream service declaration.");
283            return false;
284        }
285        return true;
286    }
287
288    private ComponentName[] getDreamComponentsForUser(int userId) {
289        String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
290                Settings.Secure.SCREENSAVER_COMPONENTS,
291                userId);
292        ComponentName[] components = componentsFromString(names);
293
294        // first, ensure components point to valid services
295        List<ComponentName> validComponents = new ArrayList<ComponentName>();
296        if (components != null) {
297            for (ComponentName component : components) {
298                if (validateDream(component)) {
299                    validComponents.add(component);
300                }
301            }
302        }
303
304        // fallback to the default dream component if necessary
305        if (validComponents.isEmpty()) {
306            ComponentName defaultDream = getDefaultDreamComponentForUser(userId);
307            if (defaultDream != null) {
308                Slog.w(TAG, "Falling back to default dream " + defaultDream);
309                validComponents.add(defaultDream);
310            }
311        }
312        return validComponents.toArray(new ComponentName[validComponents.size()]);
313    }
314
315    private void setDreamComponentsForUser(int userId, ComponentName[] componentNames) {
316        Settings.Secure.putStringForUser(mContext.getContentResolver(),
317                Settings.Secure.SCREENSAVER_COMPONENTS,
318                componentsToString(componentNames),
319                userId);
320    }
321
322    private ComponentName getDefaultDreamComponentForUser(int userId) {
323        String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
324                Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
325                userId);
326        return name == null ? null : ComponentName.unflattenFromString(name);
327    }
328
329    private ComponentName getDozeComponent() {
330        return getDozeComponent(ActivityManager.getCurrentUser());
331    }
332
333    private ComponentName getDozeComponent(int userId) {
334        if (mDozeConfig.enabled(userId)) {
335            return ComponentName.unflattenFromString(mDozeConfig.ambientDisplayComponent());
336        } else {
337            return null;
338        }
339
340    }
341
342    private ServiceInfo getServiceInfo(ComponentName name) {
343        try {
344            return name != null ? mContext.getPackageManager().getServiceInfo(name,
345                    PackageManager.MATCH_DEBUG_TRIAGED_MISSING) : null;
346        } catch (NameNotFoundException e) {
347            return null;
348        }
349    }
350
351    private void startDreamLocked(final ComponentName name,
352            final boolean isTest, final boolean canDoze, final int userId) {
353        if (mStopDreamRunnable != null) {
354            mHandler.removeCallbacks(mStopDreamRunnable);
355            mStopDreamRunnable = null;
356        }
357
358        if (Objects.equal(mCurrentDreamName, name)
359                && mCurrentDreamIsTest == isTest
360                && mCurrentDreamCanDoze == canDoze
361                && mCurrentDreamUserId == userId) {
362            Slog.i(TAG, "Already in target dream.");
363            return;
364        }
365
366        stopDreamLocked(true /*immediate*/);
367
368        Slog.i(TAG, "Entering dreamland.");
369
370        final Binder newToken = new Binder();
371        mCurrentDreamToken = newToken;
372        mCurrentDreamName = name;
373        mCurrentDreamIsTest = isTest;
374        mCurrentDreamCanDoze = canDoze;
375        mCurrentDreamUserId = userId;
376
377        PowerManager.WakeLock wakeLock = mPowerManager
378                .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "startDream");
379        mHandler.post(wakeLock.wrap(
380                () -> mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock)));
381    }
382
383    private void stopDreamLocked(final boolean immediate) {
384        if (mCurrentDreamToken != null) {
385            if (immediate) {
386                Slog.i(TAG, "Leaving dreamland.");
387                cleanupDreamLocked();
388            } else if (mCurrentDreamIsWaking) {
389                return; // already waking
390            } else {
391                Slog.i(TAG, "Gently waking up from dream.");
392                mCurrentDreamIsWaking = true;
393            }
394
395            mStopDreamRunnable = new Runnable() {
396                @Override
397                public void run() {
398                    Slog.i(TAG, "Performing gentle wake from dream.");
399                    mController.stopDream(immediate);
400                    mStopDreamRunnable = null;
401                }
402            };
403            mHandler.post(mStopDreamRunnable);
404        }
405    }
406
407    private void cleanupDreamLocked() {
408        mCurrentDreamToken = null;
409        mCurrentDreamName = null;
410        mCurrentDreamIsTest = false;
411        mCurrentDreamCanDoze = false;
412        mCurrentDreamUserId = 0;
413        mCurrentDreamIsWaking = false;
414        if (mCurrentDreamIsDozing) {
415            mCurrentDreamIsDozing = false;
416            mDozeWakeLock.release();
417        }
418        mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
419        mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
420    }
421
422    private void checkPermission(String permission) {
423        if (mContext.checkCallingOrSelfPermission(permission)
424                != PackageManager.PERMISSION_GRANTED) {
425            throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
426                    + ", must have permission " + permission);
427        }
428    }
429
430    private void writePulseGestureEnabled() {
431        ComponentName name = getDozeComponent();
432        boolean dozeEnabled = validateDream(name);
433        LocalServices.getService(InputManagerInternal.class).setPulseGestureEnabled(dozeEnabled);
434    }
435
436    private static String componentsToString(ComponentName[] componentNames) {
437        StringBuilder names = new StringBuilder();
438        if (componentNames != null) {
439            for (ComponentName componentName : componentNames) {
440                if (names.length() > 0) {
441                    names.append(',');
442                }
443                names.append(componentName.flattenToString());
444            }
445        }
446        return names.toString();
447    }
448
449    private static ComponentName[] componentsFromString(String names) {
450        if (names == null) {
451            return null;
452        }
453        String[] namesArray = names.split(",");
454        ComponentName[] componentNames = new ComponentName[namesArray.length];
455        for (int i = 0; i < namesArray.length; i++) {
456            componentNames[i] = ComponentName.unflattenFromString(namesArray[i]);
457        }
458        return componentNames;
459    }
460
461    private final DreamController.Listener mControllerListener = new DreamController.Listener() {
462        @Override
463        public void onDreamStopped(Binder token) {
464            synchronized (mLock) {
465                if (mCurrentDreamToken == token) {
466                    cleanupDreamLocked();
467                }
468            }
469        }
470    };
471
472    private final ContentObserver mDozeEnabledObserver = new ContentObserver(null) {
473        @Override
474        public void onChange(boolean selfChange) {
475            writePulseGestureEnabled();
476        }
477    };
478
479    /**
480     * Handler for asynchronous operations performed by the dream manager.
481     * Ensures operations to {@link DreamController} are single-threaded.
482     */
483    private final class DreamHandler extends Handler {
484        public DreamHandler(Looper looper) {
485            super(looper, null, true /*async*/);
486        }
487    }
488
489    private final class BinderService extends IDreamManager.Stub {
490        @Override // Binder call
491        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
492            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
493                    != PackageManager.PERMISSION_GRANTED) {
494                pw.println("Permission Denial: can't dump DreamManager from from pid="
495                        + Binder.getCallingPid()
496                        + ", uid=" + Binder.getCallingUid());
497                return;
498            }
499
500            final long ident = Binder.clearCallingIdentity();
501            try {
502                dumpInternal(pw);
503            } finally {
504                Binder.restoreCallingIdentity(ident);
505            }
506        }
507
508        @Override // Binder call
509        public ComponentName[] getDreamComponents() {
510            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
511
512            final int userId = UserHandle.getCallingUserId();
513            final long ident = Binder.clearCallingIdentity();
514            try {
515                return getDreamComponentsForUser(userId);
516            } finally {
517                Binder.restoreCallingIdentity(ident);
518            }
519        }
520
521        @Override // Binder call
522        public void setDreamComponents(ComponentName[] componentNames) {
523            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
524
525            final int userId = UserHandle.getCallingUserId();
526            final long ident = Binder.clearCallingIdentity();
527            try {
528                setDreamComponentsForUser(userId, componentNames);
529            } finally {
530                Binder.restoreCallingIdentity(ident);
531            }
532        }
533
534        @Override // Binder call
535        public ComponentName getDefaultDreamComponent() {
536            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
537
538            final int userId = UserHandle.getCallingUserId();
539            final long ident = Binder.clearCallingIdentity();
540            try {
541                return getDefaultDreamComponentForUser(userId);
542            } finally {
543                Binder.restoreCallingIdentity(ident);
544            }
545        }
546
547        @Override // Binder call
548        public boolean isDreaming() {
549            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
550
551            final long ident = Binder.clearCallingIdentity();
552            try {
553                return isDreamingInternal();
554            } finally {
555                Binder.restoreCallingIdentity(ident);
556            }
557        }
558
559        @Override // Binder call
560        public void dream() {
561            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
562
563            final long ident = Binder.clearCallingIdentity();
564            try {
565                requestDreamInternal();
566            } finally {
567                Binder.restoreCallingIdentity(ident);
568            }
569        }
570
571        @Override // Binder call
572        public void testDream(ComponentName dream) {
573            if (dream == null) {
574                throw new IllegalArgumentException("dream must not be null");
575            }
576            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
577
578            final int callingUserId = UserHandle.getCallingUserId();
579            final int currentUserId = ActivityManager.getCurrentUser();
580            if (callingUserId != currentUserId) {
581                // This check is inherently prone to races but at least it's something.
582                Slog.w(TAG, "Aborted attempt to start a test dream while a different "
583                        + " user is active: callingUserId=" + callingUserId
584                        + ", currentUserId=" + currentUserId);
585                return;
586            }
587            final long ident = Binder.clearCallingIdentity();
588            try {
589                testDreamInternal(dream, callingUserId);
590            } finally {
591                Binder.restoreCallingIdentity(ident);
592            }
593        }
594
595        @Override // Binder call
596        public void awaken() {
597            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
598
599            final long ident = Binder.clearCallingIdentity();
600            try {
601                requestAwakenInternal();
602            } finally {
603                Binder.restoreCallingIdentity(ident);
604            }
605        }
606
607        @Override // Binder call
608        public void finishSelf(IBinder token, boolean immediate) {
609            // Requires no permission, called by Dream from an arbitrary process.
610            if (token == null) {
611                throw new IllegalArgumentException("token must not be null");
612            }
613
614            final long ident = Binder.clearCallingIdentity();
615            try {
616                finishSelfInternal(token, immediate);
617            } finally {
618                Binder.restoreCallingIdentity(ident);
619            }
620        }
621
622        @Override // Binder call
623        public void startDozing(IBinder token, int screenState, int screenBrightness) {
624            // Requires no permission, called by Dream from an arbitrary process.
625            if (token == null) {
626                throw new IllegalArgumentException("token must not be null");
627            }
628
629            final long ident = Binder.clearCallingIdentity();
630            try {
631                startDozingInternal(token, screenState, screenBrightness);
632            } finally {
633                Binder.restoreCallingIdentity(ident);
634            }
635        }
636
637        @Override // Binder call
638        public void stopDozing(IBinder token) {
639            // Requires no permission, called by Dream from an arbitrary process.
640            if (token == null) {
641                throw new IllegalArgumentException("token must not be null");
642            }
643
644            final long ident = Binder.clearCallingIdentity();
645            try {
646                stopDozingInternal(token);
647            } finally {
648                Binder.restoreCallingIdentity(ident);
649            }
650        }
651    }
652
653    private final class LocalService extends DreamManagerInternal {
654        @Override
655        public void startDream(boolean doze) {
656            startDreamInternal(doze);
657        }
658
659        @Override
660        public void stopDream(boolean immediate) {
661            stopDreamInternal(immediate);
662        }
663
664        @Override
665        public boolean isDreaming() {
666            return isDreamingInternal();
667        }
668    }
669
670    private final Runnable mSystemPropertiesChanged = new Runnable() {
671        @Override
672        public void run() {
673            if (DEBUG) Slog.d(TAG, "System properties changed");
674            synchronized (mLock) {
675                if (mCurrentDreamName != null && mCurrentDreamCanDoze
676                        && !mCurrentDreamName.equals(getDozeComponent())) {
677                    // May have updated the doze component, wake up
678                    mPowerManager.wakeUp(SystemClock.uptimeMillis(),
679                            "android.server.dreams:SYSPROP");
680                }
681            }
682        }
683    };
684}
685