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