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