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 static android.Manifest.permission.BIND_DREAM_SERVICE; 20 21 import com.android.internal.util.DumpUtils; 22 import com.android.server.FgThread; 23 import com.android.server.SystemService; 24 25 import android.Manifest; 26 import android.app.ActivityManager; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.PackageManager; 33 import android.content.pm.PackageManager.NameNotFoundException; 34 import android.content.pm.ServiceInfo; 35 import android.os.Binder; 36 import android.os.Build; 37 import android.os.Handler; 38 import android.os.IBinder; 39 import android.os.Looper; 40 import android.os.PowerManager; 41 import android.os.PowerManagerInternal; 42 import android.os.SystemClock; 43 import android.os.SystemProperties; 44 import android.os.UserHandle; 45 import android.provider.Settings; 46 import android.service.dreams.DreamManagerInternal; 47 import android.service.dreams.DreamService; 48 import android.service.dreams.IDreamManager; 49 import android.text.TextUtils; 50 import android.util.Slog; 51 import android.view.Display; 52 53 import java.io.FileDescriptor; 54 import java.io.PrintWriter; 55 import java.util.ArrayList; 56 import java.util.List; 57 58 import libcore.util.Objects; 59 60 /** 61 * Service api for managing dreams. 62 * 63 * @hide 64 */ 65 public final class DreamManagerService extends SystemService { 66 private static final boolean DEBUG = false; 67 private static final String TAG = "DreamManagerService"; 68 69 private final Object mLock = new Object(); 70 71 private final Context mContext; 72 private final DreamHandler mHandler; 73 private final DreamController mController; 74 private final PowerManager mPowerManager; 75 private final PowerManagerInternal mPowerManagerInternal; 76 private final PowerManager.WakeLock mDozeWakeLock; 77 78 private Binder mCurrentDreamToken; 79 private ComponentName mCurrentDreamName; 80 private int mCurrentDreamUserId; 81 private boolean mCurrentDreamIsTest; 82 private boolean mCurrentDreamCanDoze; 83 private boolean mCurrentDreamIsDozing; 84 private boolean mCurrentDreamIsWaking; 85 private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN; 86 private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; 87 88 public DreamManagerService(Context context) { 89 super(context); 90 mContext = context; 91 mHandler = new DreamHandler(FgThread.get().getLooper()); 92 mController = new DreamController(context, mHandler, mControllerListener); 93 94 mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 95 mPowerManagerInternal = getLocalService(PowerManagerInternal.class); 96 mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG); 97 } 98 99 @Override 100 public void onStart() { 101 publishBinderService(DreamService.DREAM_SERVICE, new BinderService()); 102 publishLocalService(DreamManagerInternal.class, new LocalService()); 103 } 104 105 @Override 106 public void onBootPhase(int phase) { 107 if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { 108 if (Build.IS_DEBUGGABLE) { 109 SystemProperties.addChangeCallback(mSystemPropertiesChanged); 110 } 111 mContext.registerReceiver(new BroadcastReceiver() { 112 @Override 113 public void onReceive(Context context, Intent intent) { 114 synchronized (mLock) { 115 stopDreamLocked(false /*immediate*/); 116 } 117 } 118 }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); 119 } 120 } 121 122 private void dumpInternal(PrintWriter pw) { 123 pw.println("DREAM MANAGER (dumpsys dreams)"); 124 pw.println(); 125 pw.println("mCurrentDreamToken=" + mCurrentDreamToken); 126 pw.println("mCurrentDreamName=" + mCurrentDreamName); 127 pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId); 128 pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest); 129 pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze); 130 pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing); 131 pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking); 132 pw.println("mCurrentDreamDozeScreenState=" 133 + Display.stateToString(mCurrentDreamDozeScreenState)); 134 pw.println("mCurrentDreamDozeScreenBrightness=" + mCurrentDreamDozeScreenBrightness); 135 pw.println("getDozeComponent()=" + getDozeComponent()); 136 pw.println(); 137 138 DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() { 139 @Override 140 public void dump(PrintWriter pw) { 141 mController.dump(pw); 142 } 143 }, pw, 200); 144 } 145 146 private boolean isDreamingInternal() { 147 synchronized (mLock) { 148 return mCurrentDreamToken != null && !mCurrentDreamIsTest 149 && !mCurrentDreamIsWaking; 150 } 151 } 152 153 private void requestDreamInternal() { 154 // Ask the power manager to nap. It will eventually call back into 155 // startDream() if/when it is appropriate to start dreaming. 156 // Because napping could cause the screen to turn off immediately if the dream 157 // cannot be started, we keep one eye open and gently poke user activity. 158 long time = SystemClock.uptimeMillis(); 159 mPowerManager.userActivity(time, true /*noChangeLights*/); 160 mPowerManager.nap(time); 161 } 162 163 private void requestAwakenInternal() { 164 // Treat an explicit request to awaken as user activity so that the 165 // device doesn't immediately go to sleep if the timeout expired, 166 // for example when being undocked. 167 long time = SystemClock.uptimeMillis(); 168 mPowerManager.userActivity(time, false /*noChangeLights*/); 169 stopDreamInternal(false /*immediate*/); 170 } 171 172 private void finishSelfInternal(IBinder token, boolean immediate) { 173 if (DEBUG) { 174 Slog.d(TAG, "Dream finished: " + token + ", immediate=" + immediate); 175 } 176 177 // Note that a dream finishing and self-terminating is not 178 // itself considered user activity. If the dream is ending because 179 // the user interacted with the device then user activity will already 180 // have been poked so the device will stay awake a bit longer. 181 // If the dream is ending on its own for other reasons and no wake 182 // locks are held and the user activity timeout has expired then the 183 // device may simply go to sleep. 184 synchronized (mLock) { 185 if (mCurrentDreamToken == token) { 186 stopDreamLocked(immediate); 187 } 188 } 189 } 190 191 private void testDreamInternal(ComponentName dream, int userId) { 192 synchronized (mLock) { 193 startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId); 194 } 195 } 196 197 private void startDreamInternal(boolean doze) { 198 final int userId = ActivityManager.getCurrentUser(); 199 final ComponentName dream = chooseDreamForUser(doze, userId); 200 if (dream != null) { 201 synchronized (mLock) { 202 startDreamLocked(dream, false /*isTest*/, doze, userId); 203 } 204 } 205 } 206 207 private void stopDreamInternal(boolean immediate) { 208 synchronized (mLock) { 209 stopDreamLocked(immediate); 210 } 211 } 212 213 private void startDozingInternal(IBinder token, int screenState, 214 int screenBrightness) { 215 if (DEBUG) { 216 Slog.d(TAG, "Dream requested to start dozing: " + token 217 + ", screenState=" + screenState 218 + ", screenBrightness=" + screenBrightness); 219 } 220 221 synchronized (mLock) { 222 if (mCurrentDreamToken == token && mCurrentDreamCanDoze) { 223 mCurrentDreamDozeScreenState = screenState; 224 mCurrentDreamDozeScreenBrightness = screenBrightness; 225 mPowerManagerInternal.setDozeOverrideFromDreamManager( 226 screenState, screenBrightness); 227 if (!mCurrentDreamIsDozing) { 228 mCurrentDreamIsDozing = true; 229 mDozeWakeLock.acquire(); 230 } 231 } 232 } 233 } 234 235 private void stopDozingInternal(IBinder token) { 236 if (DEBUG) { 237 Slog.d(TAG, "Dream requested to stop dozing: " + token); 238 } 239 240 synchronized (mLock) { 241 if (mCurrentDreamToken == token && mCurrentDreamIsDozing) { 242 mCurrentDreamIsDozing = false; 243 mDozeWakeLock.release(); 244 mPowerManagerInternal.setDozeOverrideFromDreamManager( 245 Display.STATE_UNKNOWN, PowerManager.BRIGHTNESS_DEFAULT); 246 } 247 } 248 } 249 250 private ComponentName chooseDreamForUser(boolean doze, int userId) { 251 if (doze) { 252 ComponentName dozeComponent = getDozeComponent(userId); 253 return validateDream(dozeComponent) ? dozeComponent : null; 254 } 255 ComponentName[] dreams = getDreamComponentsForUser(userId); 256 return dreams != null && dreams.length != 0 ? dreams[0] : null; 257 } 258 259 private boolean validateDream(ComponentName component) { 260 if (component == null) return false; 261 final ServiceInfo serviceInfo = getServiceInfo(component); 262 if (serviceInfo == null) { 263 Slog.w(TAG, "Dream " + component + " does not exist"); 264 return false; 265 } else if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP 266 && !BIND_DREAM_SERVICE.equals(serviceInfo.permission)) { 267 Slog.w(TAG, "Dream " + component 268 + " is not available because its manifest is missing the " + BIND_DREAM_SERVICE 269 + " permission on the dream service declaration."); 270 return false; 271 } 272 return true; 273 } 274 275 private ComponentName[] getDreamComponentsForUser(int userId) { 276 String names = Settings.Secure.getStringForUser(mContext.getContentResolver(), 277 Settings.Secure.SCREENSAVER_COMPONENTS, 278 userId); 279 ComponentName[] components = componentsFromString(names); 280 281 // first, ensure components point to valid services 282 List<ComponentName> validComponents = new ArrayList<ComponentName>(); 283 if (components != null) { 284 for (ComponentName component : components) { 285 if (validateDream(component)) { 286 validComponents.add(component); 287 } 288 } 289 } 290 291 // fallback to the default dream component if necessary 292 if (validComponents.isEmpty()) { 293 ComponentName defaultDream = getDefaultDreamComponentForUser(userId); 294 if (defaultDream != null) { 295 Slog.w(TAG, "Falling back to default dream " + defaultDream); 296 validComponents.add(defaultDream); 297 } 298 } 299 return validComponents.toArray(new ComponentName[validComponents.size()]); 300 } 301 302 private void setDreamComponentsForUser(int userId, ComponentName[] componentNames) { 303 Settings.Secure.putStringForUser(mContext.getContentResolver(), 304 Settings.Secure.SCREENSAVER_COMPONENTS, 305 componentsToString(componentNames), 306 userId); 307 } 308 309 private ComponentName getDefaultDreamComponentForUser(int userId) { 310 String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), 311 Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT, 312 userId); 313 return name == null ? null : ComponentName.unflattenFromString(name); 314 } 315 316 private ComponentName getDozeComponent() { 317 return getDozeComponent(ActivityManager.getCurrentUser()); 318 } 319 320 private ComponentName getDozeComponent(int userId) { 321 // Read the component from a system property to facilitate debugging. 322 // Note that for production devices, the dream should actually be declared in 323 // a config.xml resource. 324 String name = Build.IS_DEBUGGABLE ? SystemProperties.get("debug.doze.component") : null; 325 if (TextUtils.isEmpty(name)) { 326 // Read the component from a config.xml resource. 327 // The value should be specified in a resource overlay for the product. 328 name = mContext.getResources().getString( 329 com.android.internal.R.string.config_dozeComponent); 330 } 331 boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), 332 Settings.Secure.DOZE_ENABLED, 1, userId) != 0; 333 return TextUtils.isEmpty(name) || !enabled ? null : ComponentName.unflattenFromString(name); 334 } 335 336 private ServiceInfo getServiceInfo(ComponentName name) { 337 try { 338 return name != null ? mContext.getPackageManager().getServiceInfo(name, 0) : null; 339 } catch (NameNotFoundException e) { 340 return null; 341 } 342 } 343 344 private void startDreamLocked(final ComponentName name, 345 final boolean isTest, final boolean canDoze, final int userId) { 346 if (Objects.equal(mCurrentDreamName, name) 347 && mCurrentDreamIsTest == isTest 348 && mCurrentDreamCanDoze == canDoze 349 && mCurrentDreamUserId == userId) { 350 return; 351 } 352 353 stopDreamLocked(true /*immediate*/); 354 355 Slog.i(TAG, "Entering dreamland."); 356 357 final Binder newToken = new Binder(); 358 mCurrentDreamToken = newToken; 359 mCurrentDreamName = name; 360 mCurrentDreamIsTest = isTest; 361 mCurrentDreamCanDoze = canDoze; 362 mCurrentDreamUserId = userId; 363 364 mHandler.post(new Runnable() { 365 @Override 366 public void run() { 367 mController.startDream(newToken, name, isTest, canDoze, userId); 368 } 369 }); 370 } 371 372 private void stopDreamLocked(final boolean immediate) { 373 if (mCurrentDreamToken != null) { 374 if (immediate) { 375 Slog.i(TAG, "Leaving dreamland."); 376 cleanupDreamLocked(); 377 } else if (mCurrentDreamIsWaking) { 378 return; // already waking 379 } else { 380 Slog.i(TAG, "Gently waking up from dream."); 381 mCurrentDreamIsWaking = true; 382 } 383 384 mHandler.post(new Runnable() { 385 @Override 386 public void run() { 387 mController.stopDream(immediate); 388 } 389 }); 390 } 391 } 392 393 private void cleanupDreamLocked() { 394 mCurrentDreamToken = null; 395 mCurrentDreamName = null; 396 mCurrentDreamIsTest = false; 397 mCurrentDreamCanDoze = false; 398 mCurrentDreamUserId = 0; 399 mCurrentDreamIsWaking = false; 400 if (mCurrentDreamIsDozing) { 401 mCurrentDreamIsDozing = false; 402 mDozeWakeLock.release(); 403 } 404 mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN; 405 mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; 406 } 407 408 private void checkPermission(String permission) { 409 if (mContext.checkCallingOrSelfPermission(permission) 410 != PackageManager.PERMISSION_GRANTED) { 411 throw new SecurityException("Access denied to process: " + Binder.getCallingPid() 412 + ", must have permission " + permission); 413 } 414 } 415 416 private static String componentsToString(ComponentName[] componentNames) { 417 StringBuilder names = new StringBuilder(); 418 if (componentNames != null) { 419 for (ComponentName componentName : componentNames) { 420 if (names.length() > 0) { 421 names.append(','); 422 } 423 names.append(componentName.flattenToString()); 424 } 425 } 426 return names.toString(); 427 } 428 429 private static ComponentName[] componentsFromString(String names) { 430 if (names == null) { 431 return null; 432 } 433 String[] namesArray = names.split(","); 434 ComponentName[] componentNames = new ComponentName[namesArray.length]; 435 for (int i = 0; i < namesArray.length; i++) { 436 componentNames[i] = ComponentName.unflattenFromString(namesArray[i]); 437 } 438 return componentNames; 439 } 440 441 private final DreamController.Listener mControllerListener = new DreamController.Listener() { 442 @Override 443 public void onDreamStopped(Binder token) { 444 synchronized (mLock) { 445 if (mCurrentDreamToken == token) { 446 cleanupDreamLocked(); 447 } 448 } 449 } 450 }; 451 452 /** 453 * Handler for asynchronous operations performed by the dream manager. 454 * Ensures operations to {@link DreamController} are single-threaded. 455 */ 456 private final class DreamHandler extends Handler { 457 public DreamHandler(Looper looper) { 458 super(looper, null, true /*async*/); 459 } 460 } 461 462 private final class BinderService extends IDreamManager.Stub { 463 @Override // Binder call 464 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 465 if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) 466 != PackageManager.PERMISSION_GRANTED) { 467 pw.println("Permission Denial: can't dump DreamManager from from pid=" 468 + Binder.getCallingPid() 469 + ", uid=" + Binder.getCallingUid()); 470 return; 471 } 472 473 final long ident = Binder.clearCallingIdentity(); 474 try { 475 dumpInternal(pw); 476 } finally { 477 Binder.restoreCallingIdentity(ident); 478 } 479 } 480 481 @Override // Binder call 482 public ComponentName[] getDreamComponents() { 483 checkPermission(android.Manifest.permission.READ_DREAM_STATE); 484 485 final int userId = UserHandle.getCallingUserId(); 486 final long ident = Binder.clearCallingIdentity(); 487 try { 488 return getDreamComponentsForUser(userId); 489 } finally { 490 Binder.restoreCallingIdentity(ident); 491 } 492 } 493 494 @Override // Binder call 495 public void setDreamComponents(ComponentName[] componentNames) { 496 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); 497 498 final int userId = UserHandle.getCallingUserId(); 499 final long ident = Binder.clearCallingIdentity(); 500 try { 501 setDreamComponentsForUser(userId, componentNames); 502 } finally { 503 Binder.restoreCallingIdentity(ident); 504 } 505 } 506 507 @Override // Binder call 508 public ComponentName getDefaultDreamComponent() { 509 checkPermission(android.Manifest.permission.READ_DREAM_STATE); 510 511 final int userId = UserHandle.getCallingUserId(); 512 final long ident = Binder.clearCallingIdentity(); 513 try { 514 return getDefaultDreamComponentForUser(userId); 515 } finally { 516 Binder.restoreCallingIdentity(ident); 517 } 518 } 519 520 @Override // Binder call 521 public boolean isDreaming() { 522 checkPermission(android.Manifest.permission.READ_DREAM_STATE); 523 524 final long ident = Binder.clearCallingIdentity(); 525 try { 526 return isDreamingInternal(); 527 } finally { 528 Binder.restoreCallingIdentity(ident); 529 } 530 } 531 532 @Override // Binder call 533 public void dream() { 534 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); 535 536 final long ident = Binder.clearCallingIdentity(); 537 try { 538 requestDreamInternal(); 539 } finally { 540 Binder.restoreCallingIdentity(ident); 541 } 542 } 543 544 @Override // Binder call 545 public void testDream(ComponentName dream) { 546 if (dream == null) { 547 throw new IllegalArgumentException("dream must not be null"); 548 } 549 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); 550 551 final int callingUserId = UserHandle.getCallingUserId(); 552 final int currentUserId = ActivityManager.getCurrentUser(); 553 if (callingUserId != currentUserId) { 554 // This check is inherently prone to races but at least it's something. 555 Slog.w(TAG, "Aborted attempt to start a test dream while a different " 556 + " user is active: callingUserId=" + callingUserId 557 + ", currentUserId=" + currentUserId); 558 return; 559 } 560 final long ident = Binder.clearCallingIdentity(); 561 try { 562 testDreamInternal(dream, callingUserId); 563 } finally { 564 Binder.restoreCallingIdentity(ident); 565 } 566 } 567 568 @Override // Binder call 569 public void awaken() { 570 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); 571 572 final long ident = Binder.clearCallingIdentity(); 573 try { 574 requestAwakenInternal(); 575 } finally { 576 Binder.restoreCallingIdentity(ident); 577 } 578 } 579 580 @Override // Binder call 581 public void finishSelf(IBinder token, boolean immediate) { 582 // Requires no permission, called by Dream from an arbitrary process. 583 if (token == null) { 584 throw new IllegalArgumentException("token must not be null"); 585 } 586 587 final long ident = Binder.clearCallingIdentity(); 588 try { 589 finishSelfInternal(token, immediate); 590 } finally { 591 Binder.restoreCallingIdentity(ident); 592 } 593 } 594 595 @Override // Binder call 596 public void startDozing(IBinder token, int screenState, int screenBrightness) { 597 // Requires no permission, called by Dream from an arbitrary process. 598 if (token == null) { 599 throw new IllegalArgumentException("token must not be null"); 600 } 601 602 final long ident = Binder.clearCallingIdentity(); 603 try { 604 startDozingInternal(token, screenState, screenBrightness); 605 } finally { 606 Binder.restoreCallingIdentity(ident); 607 } 608 } 609 610 @Override // Binder call 611 public void stopDozing(IBinder token) { 612 // Requires no permission, called by Dream from an arbitrary process. 613 if (token == null) { 614 throw new IllegalArgumentException("token must not be null"); 615 } 616 617 final long ident = Binder.clearCallingIdentity(); 618 try { 619 stopDozingInternal(token); 620 } finally { 621 Binder.restoreCallingIdentity(ident); 622 } 623 } 624 } 625 626 private final class LocalService extends DreamManagerInternal { 627 @Override 628 public void startDream(boolean doze) { 629 startDreamInternal(doze); 630 } 631 632 @Override 633 public void stopDream(boolean immediate) { 634 stopDreamInternal(immediate); 635 } 636 637 @Override 638 public boolean isDreaming() { 639 return isDreamingInternal(); 640 } 641 } 642 643 private final Runnable mSystemPropertiesChanged = new Runnable() { 644 @Override 645 public void run() { 646 if (DEBUG) Slog.d(TAG, "System properties changed"); 647 synchronized (mLock) { 648 if (mCurrentDreamName != null && mCurrentDreamCanDoze 649 && !mCurrentDreamName.equals(getDozeComponent())) { 650 // May have updated the doze component, wake up 651 mPowerManager.wakeUp(SystemClock.uptimeMillis()); 652 } 653 } 654 } 655 }; 656 } 657