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