blob: c9e0da5b8f54c001da293a4756bd8ba8762c008a [file] [log] [blame]
John Spurlockf4f6b4c2012-08-25 12:08:03 -04001/*
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
Jeff Browncef440f2012-09-25 18:58:48 -070017package com.android.server.dreams;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040018
Jeff Brown62c82e42012-09-26 01:30:41 -070019import com.android.internal.util.DumpUtils;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040020
Jeff Brown62c82e42012-09-26 01:30:41 -070021import android.app.ActivityManager;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040022import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040027import android.content.pm.PackageManager;
John Spurlockbbdb0622012-12-10 18:15:07 -050028import android.content.pm.PackageManager.NameNotFoundException;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040029import android.os.Binder;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040030import android.os.Handler;
31import android.os.IBinder;
Jeff Brown62c82e42012-09-26 01:30:41 -070032import android.os.Looper;
33import android.os.PowerManager;
34import android.os.SystemClock;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040035import android.os.UserHandle;
36import android.provider.Settings;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040037import android.service.dreams.IDreamManager;
38import android.util.Slog;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040039
40import java.io.FileDescriptor;
41import java.io.PrintWriter;
John Spurlockbbdb0622012-12-10 18:15:07 -050042import java.util.ArrayList;
43import java.util.List;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040044
Jeff Brown62c82e42012-09-26 01:30:41 -070045import libcore.util.Objects;
46
John Spurlockf4f6b4c2012-08-25 12:08:03 -040047/**
48 * Service api for managing dreams.
49 *
50 * @hide
51 */
Jeff Brown62c82e42012-09-26 01:30:41 -070052public final class DreamManagerService extends IDreamManager.Stub {
Dianne Hackborn40e9f292012-11-27 19:12:23 -080053 private static final boolean DEBUG = false;
Jeff Brown62c82e42012-09-26 01:30:41 -070054 private static final String TAG = "DreamManagerService";
John Spurlockf4f6b4c2012-08-25 12:08:03 -040055
56 private final Object mLock = new Object();
Jeff Brown62c82e42012-09-26 01:30:41 -070057
John Spurlockf4f6b4c2012-08-25 12:08:03 -040058 private final Context mContext;
Jeff Brown62c82e42012-09-26 01:30:41 -070059 private final DreamHandler mHandler;
60 private final DreamController mController;
61 private final PowerManager mPowerManager;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040062
Jeff Brown62c82e42012-09-26 01:30:41 -070063 private Binder mCurrentDreamToken;
64 private ComponentName mCurrentDreamName;
65 private int mCurrentDreamUserId;
66 private boolean mCurrentDreamIsTest;
John Spurlockf4f6b4c2012-08-25 12:08:03 -040067
Jeff Brown62c82e42012-09-26 01:30:41 -070068 public DreamManagerService(Context context, Handler mainHandler) {
John Spurlockf4f6b4c2012-08-25 12:08:03 -040069 mContext = context;
Jeff Brown62c82e42012-09-26 01:30:41 -070070 mHandler = new DreamHandler(mainHandler.getLooper());
71 mController = new DreamController(context, mHandler, mControllerListener);
72
73 mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
John Spurlockf4f6b4c2012-08-25 12:08:03 -040074 }
75
76 public void systemReady() {
Jeff Brown62c82e42012-09-26 01:30:41 -070077 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);
John Spurlockf4f6b4c2012-08-25 12:08:03 -040085 }
86
87 @Override
88 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
89 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
90
Jeff Brown62c82e42012-09-26 01:30:41 -070091 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);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400106 }
107
Jeff Brown62c82e42012-09-26 01:30:41 -0700108 @Override // Binder call
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400109 public ComponentName[] getDreamComponents() {
110 checkPermission(android.Manifest.permission.READ_DREAM_STATE);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400111
Jeff Brown62c82e42012-09-26 01:30:41 -0700112 final int userId = UserHandle.getCallingUserId();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400113 final long ident = Binder.clearCallingIdentity();
114 try {
115 return getDreamComponentsForUser(userId);
116 } finally {
117 Binder.restoreCallingIdentity(ident);
118 }
119 }
120
Jeff Brown62c82e42012-09-26 01:30:41 -0700121 @Override // Binder call
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400122 public void setDreamComponents(ComponentName[] componentNames) {
123 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400124
Jeff Brown62c82e42012-09-26 01:30:41 -0700125 final int userId = UserHandle.getCallingUserId();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400126 final long ident = Binder.clearCallingIdentity();
127 try {
128 Settings.Secure.putStringForUser(mContext.getContentResolver(),
Jeff Brown62c82e42012-09-26 01:30:41 -0700129 Settings.Secure.SCREENSAVER_COMPONENTS,
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400130 componentsToString(componentNames),
131 userId);
132 } finally {
133 Binder.restoreCallingIdentity(ident);
134 }
135 }
136
Jeff Brown62c82e42012-09-26 01:30:41 -0700137 @Override // Binder call
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400138 public ComponentName getDefaultDreamComponent() {
139 checkPermission(android.Manifest.permission.READ_DREAM_STATE);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400140
Jeff Brown62c82e42012-09-26 01:30:41 -0700141 final int userId = UserHandle.getCallingUserId();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400142 final long ident = Binder.clearCallingIdentity();
143 try {
144 String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Jeff Brown62c82e42012-09-26 01:30:41 -0700145 Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400146 userId);
147 return name == null ? null : ComponentName.unflattenFromString(name);
148 } finally {
149 Binder.restoreCallingIdentity(ident);
150 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400151 }
152
Jeff Brown62c82e42012-09-26 01:30:41 -0700153 @Override // Binder call
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400154 public boolean isDreaming() {
155 checkPermission(android.Manifest.permission.READ_DREAM_STATE);
156
Jeff Brown62c82e42012-09-26 01:30:41 -0700157 synchronized (mLock) {
158 return mCurrentDreamToken != null && !mCurrentDreamIsTest;
159 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400160 }
161
Jeff Brown62c82e42012-09-26 01:30:41 -0700162 @Override // Binder call
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400163 public void dream() {
164 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
165
166 final long ident = Binder.clearCallingIdentity();
167 try {
Jeff Brown62c82e42012-09-26 01:30:41 -0700168 // 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);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400175 } finally {
176 Binder.restoreCallingIdentity(ident);
177 }
178 }
179
Jeff Brown62c82e42012-09-26 01:30:41 -0700180 @Override // Binder call
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400181 public void testDream(ComponentName dream) {
182 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
183
Jeff Brown62c82e42012-09-26 01:30:41 -0700184 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 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400197 final long ident = Binder.clearCallingIdentity();
198 try {
Jeff Brown62c82e42012-09-26 01:30:41 -0700199 synchronized (mLock) {
200 startDreamLocked(dream, true /*isTest*/, callingUserId);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400201 }
202 } finally {
203 Binder.restoreCallingIdentity(ident);
204 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400205 }
206
Jeff Brown62c82e42012-09-26 01:30:41 -0700207 @Override // Binder call
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400208 public void awaken() {
209 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
210
211 final long ident = Binder.clearCallingIdentity();
212 try {
Jeff Brown62c82e42012-09-26 01:30:41 -0700213 // 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();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400219 } finally {
220 Binder.restoreCallingIdentity(ident);
221 }
222 }
223
Jeff Brown62c82e42012-09-26 01:30:41 -0700224 @Override // Binder call
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 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400230
231 final long ident = Binder.clearCallingIdentity();
232 try {
Jeff Brown62c82e42012-09-26 01:30:41 -0700233 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 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400248 }
249 } finally {
250 Binder.restoreCallingIdentity(ident);
251 }
252 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400253
Jeff Brown62c82e42012-09-26 01:30:41 -0700254 /**
255 * Called by the power manager to start a dream.
256 */
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);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400263 }
264 }
Jeff Brown62c82e42012-09-26 01:30:41 -0700265 }
266
267 /**
268 * Called by the power manager to stop a dream.
269 */
270 public void stopDream() {
271 synchronized (mLock) {
272 stopDreamLocked();
273 }
274 }
275
276 private ComponentName chooseDreamForUser(int userId) {
277 ComponentName[] dreams = getDreamComponentsForUser(userId);
278 return dreams != null && dreams.length != 0 ? dreams[0] : null;
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400279 }
280
281 private ComponentName[] getDreamComponentsForUser(int userId) {
282 String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Jeff Brown62c82e42012-09-26 01:30:41 -0700283 Settings.Secure.SCREENSAVER_COMPONENTS,
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400284 userId);
John Spurlockbbdb0622012-12-10 18:15:07 -0500285 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
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 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400316 }
317
Jeff Brown62c82e42012-09-26 01:30:41 -0700318 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
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800328 if (DEBUG) Slog.i(TAG, "Entering dreamland.");
Jeff Brown62c82e42012-09-26 01:30:41 -0700329
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
344 private void stopDreamLocked() {
345 if (mCurrentDreamToken != null) {
Dianne Hackborn40e9f292012-11-27 19:12:23 -0800346 if (DEBUG) Slog.i(TAG, "Leaving dreamland.");
Jeff Brown62c82e42012-09-26 01:30:41 -0700347
348 cleanupDreamLocked();
349
350 mHandler.post(new Runnable() {
351 @Override
352 public void run() {
353 mController.stopDream();
354 }
355 });
356 }
357 }
358
359 private void cleanupDreamLocked() {
360 mCurrentDreamToken = null;
361 mCurrentDreamName = null;
362 mCurrentDreamIsTest = false;
363 mCurrentDreamUserId = 0;
364 }
365
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
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400374 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
387 private static ComponentName[] componentsFromString(String names) {
John Spurlockf5df6892012-12-14 13:12:43 -0500388 if (names == null) {
389 return null;
390 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400391 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
Jeff Brown62c82e42012-09-26 01:30:41 -0700399 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();
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400405 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400406 }
407 }
Jeff Brown62c82e42012-09-26 01:30:41 -0700408 };
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400409
410 /**
411 * Handler for asynchronous operations performed by the dream manager.
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400412 * Ensures operations to {@link DreamController} are single-threaded.
413 */
Jeff Brown62c82e42012-09-26 01:30:41 -0700414 private final class DreamHandler extends Handler {
415 public DreamHandler(Looper looper) {
416 super(looper, null, true /*async*/);
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400417 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400418 }
John Spurlockf4f6b4c2012-08-25 12:08:03 -0400419}