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