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