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