DreamManagerService.java revision 567f7ca424280ae957b04d4f3df690055f2d41df
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 com.android.internal.util.DumpUtils;
20import com.android.server.FgThread;
21import com.android.server.SystemService;
22
23import android.Manifest;
24import android.app.ActivityManager;
25import android.content.BroadcastReceiver;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.pm.PackageManager;
31import android.content.pm.PackageManager.NameNotFoundException;
32import android.os.Binder;
33import android.os.Handler;
34import android.os.IBinder;
35import android.os.Looper;
36import android.os.PowerManager;
37import android.os.SystemClock;
38import android.os.UserHandle;
39import android.provider.Settings;
40import android.service.dreams.DreamManagerInternal;
41import android.service.dreams.DreamService;
42import android.service.dreams.IDreamManager;
43import android.util.Slog;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47import java.util.ArrayList;
48import java.util.List;
49
50import libcore.util.Objects;
51
52/**
53 * Service api for managing dreams.
54 *
55 * @hide
56 */
57public final class DreamManagerService extends SystemService {
58    private static final boolean DEBUG = false;
59    private static final String TAG = "DreamManagerService";
60
61    private final Object mLock = new Object();
62
63    private final Context mContext;
64    private final DreamHandler mHandler;
65    private final DreamController mController;
66    private final PowerManager mPowerManager;
67
68    private Binder mCurrentDreamToken;
69    private ComponentName mCurrentDreamName;
70    private int mCurrentDreamUserId;
71    private boolean mCurrentDreamIsTest;
72
73    public DreamManagerService(Context context) {
74        super(context);
75        mContext = context;
76        mHandler = new DreamHandler(FgThread.get().getLooper());
77        mController = new DreamController(context, mHandler, mControllerListener);
78
79        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
80    }
81
82    @Override
83    public void onStart() {
84        publishBinderService(DreamService.DREAM_SERVICE, new BinderService());
85        publishLocalService(DreamManagerInternal.class, new LocalService());
86    }
87
88    @Override
89    public void onBootPhase(int phase) {
90        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
91            mContext.registerReceiver(new BroadcastReceiver() {
92                @Override
93                public void onReceive(Context context, Intent intent) {
94                    synchronized (mLock) {
95                        stopDreamLocked();
96                    }
97                }
98            }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
99        }
100    }
101
102    private void dumpInternal(PrintWriter pw) {
103        pw.println("DREAM MANAGER (dumpsys dreams)");
104        pw.println();
105
106        pw.println("mCurrentDreamToken=" + mCurrentDreamToken);
107        pw.println("mCurrentDreamName=" + mCurrentDreamName);
108        pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId);
109        pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest);
110        pw.println();
111
112        DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
113            @Override
114            public void dump(PrintWriter pw) {
115                mController.dump(pw);
116            }
117        }, pw, 200);
118    }
119
120    private boolean isDreamingInternal() {
121        synchronized (mLock) {
122            return mCurrentDreamToken != null && !mCurrentDreamIsTest;
123        }
124    }
125
126    private void requestDreamInternal() {
127        // Ask the power manager to nap.  It will eventually call back into
128        // startDream() if/when it is appropriate to start dreaming.
129        // Because napping could cause the screen to turn off immediately if the dream
130        // cannot be started, we keep one eye open and gently poke user activity.
131        long time = SystemClock.uptimeMillis();
132        mPowerManager.userActivity(time, true /*noChangeLights*/);
133        mPowerManager.nap(time);
134    }
135
136    private void requestAwakenInternal() {
137        // Treat an explicit request to awaken as user activity so that the
138        // device doesn't immediately go to sleep if the timeout expired,
139        // for example when being undocked.
140        long time = SystemClock.uptimeMillis();
141        mPowerManager.userActivity(time, false /*noChangeLights*/);
142        stopDreamInternal();
143    }
144
145    private void finishSelfInternal(IBinder token) {
146        if (DEBUG) {
147            Slog.d(TAG, "Dream finished: " + token);
148        }
149
150        // Note that a dream finishing and self-terminating is not
151        // itself considered user activity.  If the dream is ending because
152        // the user interacted with the device then user activity will already
153        // have been poked so the device will stay awake a bit longer.
154        // If the dream is ending on its own for other reasons and no wake
155        // locks are held and the user activity timeout has expired then the
156        // device may simply go to sleep.
157        synchronized (mLock) {
158            if (mCurrentDreamToken == token) {
159                stopDreamLocked();
160            }
161        }
162    }
163
164    private void testDreamInternal(ComponentName dream, int userId) {
165        synchronized (mLock) {
166            startDreamLocked(dream, true /*isTest*/, userId);
167        }
168    }
169
170    private void startDreamInternal() {
171        int userId = ActivityManager.getCurrentUser();
172        ComponentName dream = chooseDreamForUser(userId);
173        if (dream != null) {
174            synchronized (mLock) {
175                startDreamLocked(dream, false /*isTest*/, userId);
176            }
177        }
178    }
179
180    private void stopDreamInternal() {
181        synchronized (mLock) {
182            stopDreamLocked();
183        }
184    }
185
186    private ComponentName chooseDreamForUser(int userId) {
187        ComponentName[] dreams = getDreamComponentsForUser(userId);
188        return dreams != null && dreams.length != 0 ? dreams[0] : null;
189    }
190
191    private ComponentName[] getDreamComponentsForUser(int userId) {
192        String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
193                Settings.Secure.SCREENSAVER_COMPONENTS,
194                userId);
195        ComponentName[] components = componentsFromString(names);
196
197        // first, ensure components point to valid services
198        List<ComponentName> validComponents = new ArrayList<ComponentName>();
199        if (components != null) {
200            for (ComponentName component : components) {
201                if (serviceExists(component)) {
202                    validComponents.add(component);
203                } else {
204                    Slog.w(TAG, "Dream " + component + " does not exist");
205                }
206            }
207        }
208
209        // fallback to the default dream component if necessary
210        if (validComponents.isEmpty()) {
211            ComponentName defaultDream = getDefaultDreamComponentForUser(userId);
212            if (defaultDream != null) {
213                Slog.w(TAG, "Falling back to default dream " + defaultDream);
214                validComponents.add(defaultDream);
215            }
216        }
217        return validComponents.toArray(new ComponentName[validComponents.size()]);
218    }
219
220    private void setDreamComponentsForUser(int userId, ComponentName[] componentNames) {
221        Settings.Secure.putStringForUser(mContext.getContentResolver(),
222                Settings.Secure.SCREENSAVER_COMPONENTS,
223                componentsToString(componentNames),
224                userId);
225    }
226
227    private ComponentName getDefaultDreamComponentForUser(int userId) {
228        String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
229                Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
230                userId);
231        return name == null ? null : ComponentName.unflattenFromString(name);
232    }
233
234    private boolean serviceExists(ComponentName name) {
235        try {
236            return name != null && mContext.getPackageManager().getServiceInfo(name, 0) != null;
237        } catch (NameNotFoundException e) {
238            return false;
239        }
240    }
241
242    private void startDreamLocked(final ComponentName name,
243            final boolean isTest, final int userId) {
244        if (Objects.equal(mCurrentDreamName, name)
245                && mCurrentDreamIsTest == isTest
246                && mCurrentDreamUserId == userId) {
247            return;
248        }
249
250        stopDreamLocked();
251
252        if (DEBUG) Slog.i(TAG, "Entering dreamland.");
253
254        final Binder newToken = new Binder();
255        mCurrentDreamToken = newToken;
256        mCurrentDreamName = name;
257        mCurrentDreamIsTest = isTest;
258        mCurrentDreamUserId = userId;
259
260        mHandler.post(new Runnable() {
261            @Override
262            public void run() {
263                mController.startDream(newToken, name, isTest, userId);
264            }
265        });
266    }
267
268    private void stopDreamLocked() {
269        if (mCurrentDreamToken != null) {
270            if (DEBUG) Slog.i(TAG, "Leaving dreamland.");
271
272            cleanupDreamLocked();
273
274            mHandler.post(new Runnable() {
275                @Override
276                public void run() {
277                    mController.stopDream();
278                }
279            });
280        }
281    }
282
283    private void cleanupDreamLocked() {
284        mCurrentDreamToken = null;
285        mCurrentDreamName = null;
286        mCurrentDreamIsTest = false;
287        mCurrentDreamUserId = 0;
288    }
289
290    private void checkPermission(String permission) {
291        if (mContext.checkCallingOrSelfPermission(permission)
292                != PackageManager.PERMISSION_GRANTED) {
293            throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
294                    + ", must have permission " + permission);
295        }
296    }
297
298    private static String componentsToString(ComponentName[] componentNames) {
299        StringBuilder names = new StringBuilder();
300        if (componentNames != null) {
301            for (ComponentName componentName : componentNames) {
302                if (names.length() > 0) {
303                    names.append(',');
304                }
305                names.append(componentName.flattenToString());
306            }
307        }
308        return names.toString();
309    }
310
311    private static ComponentName[] componentsFromString(String names) {
312        if (names == null) {
313            return null;
314        }
315        String[] namesArray = names.split(",");
316        ComponentName[] componentNames = new ComponentName[namesArray.length];
317        for (int i = 0; i < namesArray.length; i++) {
318            componentNames[i] = ComponentName.unflattenFromString(namesArray[i]);
319        }
320        return componentNames;
321    }
322
323    private final DreamController.Listener mControllerListener = new DreamController.Listener() {
324        @Override
325        public void onDreamStopped(Binder token) {
326            synchronized (mLock) {
327                if (mCurrentDreamToken == token) {
328                    cleanupDreamLocked();
329                }
330            }
331        }
332    };
333
334    /**
335     * Handler for asynchronous operations performed by the dream manager.
336     * Ensures operations to {@link DreamController} are single-threaded.
337     */
338    private final class DreamHandler extends Handler {
339        public DreamHandler(Looper looper) {
340            super(looper, null, true /*async*/);
341        }
342    }
343
344    private final class BinderService extends IDreamManager.Stub {
345        @Override // Binder call
346        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
347            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
348                    != PackageManager.PERMISSION_GRANTED) {
349                pw.println("Permission Denial: can't dump DreamManager from from pid="
350                        + Binder.getCallingPid()
351                        + ", uid=" + Binder.getCallingUid());
352                return;
353            }
354
355            final long ident = Binder.clearCallingIdentity();
356            try {
357                dumpInternal(pw);
358            } finally {
359                Binder.restoreCallingIdentity(ident);
360            }
361        }
362
363        @Override // Binder call
364        public ComponentName[] getDreamComponents() {
365            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
366
367            final int userId = UserHandle.getCallingUserId();
368            final long ident = Binder.clearCallingIdentity();
369            try {
370                return getDreamComponentsForUser(userId);
371            } finally {
372                Binder.restoreCallingIdentity(ident);
373            }
374        }
375
376        @Override // Binder call
377        public void setDreamComponents(ComponentName[] componentNames) {
378            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
379
380            final int userId = UserHandle.getCallingUserId();
381            final long ident = Binder.clearCallingIdentity();
382            try {
383                setDreamComponentsForUser(userId, componentNames);
384            } finally {
385                Binder.restoreCallingIdentity(ident);
386            }
387        }
388
389        @Override // Binder call
390        public ComponentName getDefaultDreamComponent() {
391            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
392
393            final int userId = UserHandle.getCallingUserId();
394            final long ident = Binder.clearCallingIdentity();
395            try {
396                return getDefaultDreamComponentForUser(userId);
397            } finally {
398                Binder.restoreCallingIdentity(ident);
399            }
400        }
401
402        @Override // Binder call
403        public boolean isDreaming() {
404            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
405
406            final long ident = Binder.clearCallingIdentity();
407            try {
408                return isDreamingInternal();
409            } finally {
410                Binder.restoreCallingIdentity(ident);
411            }
412        }
413
414        @Override // Binder call
415        public void dream() {
416            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
417
418            final long ident = Binder.clearCallingIdentity();
419            try {
420                requestDreamInternal();
421            } finally {
422                Binder.restoreCallingIdentity(ident);
423            }
424        }
425
426        @Override // Binder call
427        public void testDream(ComponentName dream) {
428            if (dream == null) {
429                throw new IllegalArgumentException("dream must not be null");
430            }
431            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
432
433            final int callingUserId = UserHandle.getCallingUserId();
434            final int currentUserId = ActivityManager.getCurrentUser();
435            if (callingUserId != currentUserId) {
436                // This check is inherently prone to races but at least it's something.
437                Slog.w(TAG, "Aborted attempt to start a test dream while a different "
438                        + " user is active: callingUserId=" + callingUserId
439                        + ", currentUserId=" + currentUserId);
440                return;
441            }
442            final long ident = Binder.clearCallingIdentity();
443            try {
444                testDreamInternal(dream, callingUserId);
445            } finally {
446                Binder.restoreCallingIdentity(ident);
447            }
448        }
449
450        @Override // Binder call
451        public void awaken() {
452            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
453
454            final long ident = Binder.clearCallingIdentity();
455            try {
456                requestAwakenInternal();
457            } finally {
458                Binder.restoreCallingIdentity(ident);
459            }
460        }
461
462        @Override // Binder call
463        public void finishSelf(IBinder token) {
464            // Requires no permission, called by Dream from an arbitrary process.
465            if (token == null) {
466                throw new IllegalArgumentException("token must not be null");
467            }
468
469            final long ident = Binder.clearCallingIdentity();
470            try {
471                finishSelfInternal(token);
472            } finally {
473                Binder.restoreCallingIdentity(ident);
474            }
475        }
476    }
477
478    private final class LocalService extends DreamManagerInternal {
479        @Override
480        public void startDream() {
481            startDreamInternal();
482        }
483
484        @Override
485        public void stopDream() {
486            stopDreamInternal();
487        }
488
489        @Override
490        public boolean isDreaming() {
491            return isDreamingInternal();
492        }
493    }
494}
495