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