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