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