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