DreamService.java revision f6d466895b74d620d646abbec1c8911f3a0ce0bb
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 */ 16package android.service.dreams; 17 18import java.io.FileDescriptor; 19import java.io.PrintWriter; 20 21import android.annotation.SdkConstant; 22import android.annotation.SdkConstant.SdkConstantType; 23import android.app.AlarmManager; 24import android.app.Service; 25import android.content.Intent; 26import android.graphics.PixelFormat; 27import android.graphics.drawable.ColorDrawable; 28import android.os.Handler; 29import android.os.IBinder; 30import android.os.RemoteException; 31import android.os.ServiceManager; 32import android.util.Slog; 33import android.view.ActionMode; 34import android.view.KeyEvent; 35import android.view.Menu; 36import android.view.MenuItem; 37import android.view.MotionEvent; 38import android.view.View; 39import android.view.ViewGroup; 40import android.view.Window; 41import android.view.WindowManager; 42import android.view.WindowManagerGlobal; 43import android.view.WindowManager.LayoutParams; 44import android.view.accessibility.AccessibilityEvent; 45 46import com.android.internal.policy.PolicyManager; 47import com.android.internal.util.DumpUtils; 48import com.android.internal.util.DumpUtils.Dump; 49 50/** 51 * Extend this class to implement a custom dream (available to the user as a "Daydream"). 52 * 53 * <p>Dreams are interactive screensavers launched when a charging device is idle, or docked in a 54 * desk dock. Dreams provide another modality for apps to express themselves, tailored for 55 * an exhibition/lean-back experience.</p> 56 * 57 * <p>The {@code DreamService} lifecycle is as follows:</p> 58 * <ol> 59 * <li>{@link #onAttachedToWindow} 60 * <p>Use this for initial setup, such as calling {@link #setContentView setContentView()}.</li> 61 * <li>{@link #onDreamingStarted} 62 * <p>Your dream has started, so you should begin animations or other behaviors here.</li> 63 * <li>{@link #onDreamingStopped} 64 * <p>Use this to stop the things you started in {@link #onDreamingStarted}.</li> 65 * <li>{@link #onDetachedFromWindow} 66 * <p>Use this to dismantle resources (for example, detach from handlers 67 * and listeners).</li> 68 * </ol> 69 * 70 * <p>In addition, onCreate and onDestroy (from the Service interface) will also be called, but 71 * initialization and teardown should be done by overriding the hooks above.</p> 72 * 73 * <p>To be available to the system, your {@code DreamService} should be declared in the 74 * manifest as follows:</p> 75 * <pre> 76 * <service 77 * android:name=".MyDream" 78 * android:exported="true" 79 * android:icon="@drawable/my_icon" 80 * android:label="@string/my_dream_label" > 81 * 82 * <intent-filter> 83 * <action android:name="android.service.dreams.DreamService" /> 84 * <category android:name="android.intent.category.DEFAULT" /> 85 * </intent-filter> 86 * 87 * <!-- Point to additional information for this dream (optional) --> 88 * <meta-data 89 * android:name="android.service.dream" 90 * android:resource="@xml/my_dream" /> 91 * </service> 92 * </pre> 93 * 94 * <p>If specified with the {@code <meta-data>} element, 95 * additional information for the dream is defined using the 96 * {@link android.R.styleable#Dream <dream>} element in a separate XML file. 97 * Currently, the only addtional 98 * information you can provide is for a settings activity that allows the user to configure 99 * the dream behavior. For example:</p> 100 * <p class="code-caption">res/xml/my_dream.xml</p> 101 * <pre> 102 * <dream xmlns:android="http://schemas.android.com/apk/res/android" 103 * android:settingsActivity="com.example.app/.MyDreamSettingsActivity" /> 104 * </pre> 105 * <p>This makes a Settings button available alongside your dream's listing in the 106 * system settings, which when pressed opens the specified activity.</p> 107 * 108 * 109 * <p>To specify your dream layout, call {@link #setContentView}, typically during the 110 * {@link #onAttachedToWindow} callback. For example:</p> 111 * <pre> 112 * public class MyDream extends DreamService { 113 * 114 * @Override 115 * public void onAttachedToWindow() { 116 * super.onAttachedToWindow(); 117 * 118 * // Exit dream upon user touch 119 * setInteractive(false); 120 * // Hide system UI 121 * setFullscreen(true); 122 * // Set the dream layout 123 * setContentView(R.layout.dream); 124 * } 125 * } 126 * </pre> 127 * 128 * <p>When targeting api level 21 and above, you must declare the service in your manifest file 129 * with the {@link android.Manifest.permission#BIND_DREAM_SERVICE} permission. For example:</p> 130 * <pre> 131 * <service 132 * android:name=".MyDream" 133 * android:exported="true" 134 * android:icon="@drawable/my_icon" 135 * android:label="@string/my_dream_label" 136 * android:permission="android.permission.BIND_DREAM_SERVICE" > 137 * ... 138 * </service> 139 * </pre> 140 */ 141public class DreamService extends Service implements Window.Callback { 142 private final String TAG = DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; 143 144 /** 145 * The name of the dream manager service. 146 * @hide 147 */ 148 public static final String DREAM_SERVICE = "dreams"; 149 150 /** 151 * The {@link Intent} that must be declared as handled by the service. 152 */ 153 @SdkConstant(SdkConstantType.SERVICE_ACTION) 154 public static final String SERVICE_INTERFACE = 155 "android.service.dreams.DreamService"; 156 157 /** 158 * Name under which a Dream publishes information about itself. 159 * This meta-data must reference an XML resource containing 160 * a <code><{@link android.R.styleable#Dream dream}></code> 161 * tag. 162 */ 163 public static final String DREAM_META_DATA = "android.service.dream"; 164 165 private final IDreamManager mSandman; 166 private final Handler mHandler = new Handler(); 167 private IBinder mWindowToken; 168 private Window mWindow; 169 private boolean mInteractive; 170 private boolean mLowProfile = true; 171 private boolean mFullscreen; 172 private boolean mScreenBright = true; 173 private boolean mStarted; 174 private boolean mWaking; 175 private boolean mFinished; 176 private boolean mCanDoze; 177 private boolean mDozing; 178 private boolean mWindowless; 179 private DozeHardware mDozeHardware; 180 181 private boolean mDebug = false; 182 183 public DreamService() { 184 mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); 185 } 186 187 /** 188 * @hide 189 */ 190 public void setDebug(boolean dbg) { 191 mDebug = dbg; 192 } 193 194 // begin Window.Callback methods 195 /** {@inheritDoc} */ 196 @Override 197 public boolean dispatchKeyEvent(KeyEvent event) { 198 // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK 199 if (!mInteractive) { 200 if (mDebug) Slog.v(TAG, "Waking up on keyEvent"); 201 wakeUp(); 202 return true; 203 } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 204 if (mDebug) Slog.v(TAG, "Waking up on back key"); 205 wakeUp(); 206 return true; 207 } 208 return mWindow.superDispatchKeyEvent(event); 209 } 210 211 /** {@inheritDoc} */ 212 @Override 213 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 214 if (!mInteractive) { 215 if (mDebug) Slog.v(TAG, "Waking up on keyShortcutEvent"); 216 wakeUp(); 217 return true; 218 } 219 return mWindow.superDispatchKeyShortcutEvent(event); 220 } 221 222 /** {@inheritDoc} */ 223 @Override 224 public boolean dispatchTouchEvent(MotionEvent event) { 225 // TODO: create more flexible version of mInteractive that allows clicks 226 // but finish()es on any other kind of activity 227 if (!mInteractive) { 228 if (mDebug) Slog.v(TAG, "Waking up on touchEvent"); 229 wakeUp(); 230 return true; 231 } 232 return mWindow.superDispatchTouchEvent(event); 233 } 234 235 /** {@inheritDoc} */ 236 @Override 237 public boolean dispatchTrackballEvent(MotionEvent event) { 238 if (!mInteractive) { 239 if (mDebug) Slog.v(TAG, "Waking up on trackballEvent"); 240 wakeUp(); 241 return true; 242 } 243 return mWindow.superDispatchTrackballEvent(event); 244 } 245 246 /** {@inheritDoc} */ 247 @Override 248 public boolean dispatchGenericMotionEvent(MotionEvent event) { 249 if (!mInteractive) { 250 if (mDebug) Slog.v(TAG, "Waking up on genericMotionEvent"); 251 wakeUp(); 252 return true; 253 } 254 return mWindow.superDispatchGenericMotionEvent(event); 255 } 256 257 /** {@inheritDoc} */ 258 @Override 259 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 260 return false; 261 } 262 263 /** {@inheritDoc} */ 264 @Override 265 public View onCreatePanelView(int featureId) { 266 return null; 267 } 268 269 /** {@inheritDoc} */ 270 @Override 271 public boolean onCreatePanelMenu(int featureId, Menu menu) { 272 return false; 273 } 274 275 /** {@inheritDoc} */ 276 @Override 277 public boolean onPreparePanel(int featureId, View view, Menu menu) { 278 return false; 279 } 280 281 /** {@inheritDoc} */ 282 @Override 283 public boolean onMenuOpened(int featureId, Menu menu) { 284 return false; 285 } 286 287 /** {@inheritDoc} */ 288 @Override 289 public boolean onMenuItemSelected(int featureId, MenuItem item) { 290 return false; 291 } 292 293 /** {@inheritDoc} */ 294 @Override 295 public void onWindowAttributesChanged(LayoutParams attrs) { 296 } 297 298 /** {@inheritDoc} */ 299 @Override 300 public void onContentChanged() { 301 } 302 303 /** {@inheritDoc} */ 304 @Override 305 public void onWindowFocusChanged(boolean hasFocus) { 306 } 307 308 /** {@inheritDoc} */ 309 @Override 310 public void onAttachedToWindow() { 311 } 312 313 /** {@inheritDoc} */ 314 @Override 315 public void onDetachedFromWindow() { 316 } 317 318 /** {@inheritDoc} */ 319 @Override 320 public void onPanelClosed(int featureId, Menu menu) { 321 } 322 323 /** {@inheritDoc} */ 324 @Override 325 public boolean onSearchRequested() { 326 return false; 327 } 328 329 /** {@inheritDoc} */ 330 @Override 331 public ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback callback) { 332 return null; 333 } 334 335 /** {@inheritDoc} */ 336 @Override 337 public void onActionModeStarted(ActionMode mode) { 338 } 339 340 /** {@inheritDoc} */ 341 @Override 342 public void onActionModeFinished(ActionMode mode) { 343 } 344 // end Window.Callback methods 345 346 // begin public api 347 /** 348 * Retrieves the current {@link android.view.WindowManager} for the dream. 349 * Behaves similarly to {@link android.app.Activity#getWindowManager()}. 350 * 351 * @return The current window manager, or null if the dream is not started. 352 */ 353 public WindowManager getWindowManager() { 354 return mWindow != null ? mWindow.getWindowManager() : null; 355 } 356 357 /** 358 * Retrieves the current {@link android.view.Window} for the dream. 359 * Behaves similarly to {@link android.app.Activity#getWindow()}. 360 * 361 * @return The current window, or null if the dream is not started. 362 */ 363 public Window getWindow() { 364 return mWindow; 365 } 366 367 /** 368 * Inflates a layout resource and set it to be the content view for this Dream. 369 * Behaves similarly to {@link android.app.Activity#setContentView(int)}. 370 * 371 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 372 * 373 * @param layoutResID Resource ID to be inflated. 374 * 375 * @see #setContentView(android.view.View) 376 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 377 */ 378 public void setContentView(int layoutResID) { 379 getWindow().setContentView(layoutResID); 380 } 381 382 /** 383 * Sets a view to be the content view for this Dream. 384 * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)} in an activity, 385 * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view. 386 * 387 * <p>Note: This requires a window, so you should usually call it during 388 * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it 389 * during {@link #onCreate}).</p> 390 * 391 * @see #setContentView(int) 392 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 393 */ 394 public void setContentView(View view) { 395 getWindow().setContentView(view); 396 } 397 398 /** 399 * Sets a view to be the content view for this Dream. 400 * Behaves similarly to 401 * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)} 402 * in an activity. 403 * 404 * <p>Note: This requires a window, so you should usually call it during 405 * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it 406 * during {@link #onCreate}).</p> 407 * 408 * @param view The desired content to display. 409 * @param params Layout parameters for the view. 410 * 411 * @see #setContentView(android.view.View) 412 * @see #setContentView(int) 413 */ 414 public void setContentView(View view, ViewGroup.LayoutParams params) { 415 getWindow().setContentView(view, params); 416 } 417 418 /** 419 * Adds a view to the Dream's window, leaving other content views in place. 420 * 421 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 422 * 423 * @param view The desired content to display. 424 * @param params Layout parameters for the view. 425 */ 426 public void addContentView(View view, ViewGroup.LayoutParams params) { 427 getWindow().addContentView(view, params); 428 } 429 430 /** 431 * Finds a view that was identified by the id attribute from the XML that 432 * was processed in {@link #onCreate}. 433 * 434 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 435 * 436 * @return The view if found or null otherwise. 437 */ 438 public View findViewById(int id) { 439 return getWindow().findViewById(id); 440 } 441 442 /** 443 * Marks this dream as interactive to receive input events. 444 * 445 * <p>Non-interactive dreams (default) will dismiss on the first input event.</p> 446 * 447 * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p> 448 * 449 * @param interactive True if this dream will handle input events. 450 */ 451 public void setInteractive(boolean interactive) { 452 mInteractive = interactive; 453 } 454 455 /** 456 * Returns whether or not this dream is interactive. Defaults to false. 457 * 458 * @see #setInteractive(boolean) 459 */ 460 public boolean isInteractive() { 461 return mInteractive; 462 } 463 464 /** 465 * Sets View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. 466 * 467 * @param lowProfile True to set View.SYSTEM_UI_FLAG_LOW_PROFILE 468 * @hide There is no reason to have this -- dreams can set this flag 469 * on their own content view, and from there can actually do the 470 * correct interactions with it (seeing when it is cleared etc). 471 */ 472 public void setLowProfile(boolean lowProfile) { 473 if (mLowProfile != lowProfile) { 474 mLowProfile = lowProfile; 475 int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; 476 applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); 477 } 478 } 479 480 /** 481 * Returns whether or not this dream is in low profile mode. Defaults to true. 482 * 483 * @see #setLowProfile(boolean) 484 * @hide 485 */ 486 public boolean isLowProfile() { 487 return getSystemUiVisibilityFlagValue(View.SYSTEM_UI_FLAG_LOW_PROFILE, mLowProfile); 488 } 489 490 /** 491 * Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} 492 * on the dream's window. 493 * 494 * @param fullscreen If true, the fullscreen flag will be set; else it 495 * will be cleared. 496 */ 497 public void setFullscreen(boolean fullscreen) { 498 if (mFullscreen != fullscreen) { 499 mFullscreen = fullscreen; 500 int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; 501 applyWindowFlags(mFullscreen ? flag : 0, flag); 502 } 503 } 504 505 /** 506 * Returns whether or not this dream is in fullscreen mode. Defaults to false. 507 * 508 * @see #setFullscreen(boolean) 509 */ 510 public boolean isFullscreen() { 511 return mFullscreen; 512 } 513 514 /** 515 * Marks this dream as keeping the screen bright while dreaming. 516 * 517 * @param screenBright True to keep the screen bright while dreaming. 518 */ 519 public void setScreenBright(boolean screenBright) { 520 if (mScreenBright != screenBright) { 521 mScreenBright = screenBright; 522 int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 523 applyWindowFlags(mScreenBright ? flag : 0, flag); 524 } 525 } 526 527 /** 528 * Returns whether or not this dream keeps the screen bright while dreaming. 529 * Defaults to false, allowing the screen to dim if necessary. 530 * 531 * @see #setScreenBright(boolean) 532 */ 533 public boolean isScreenBright() { 534 return getWindowFlagValue(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, mScreenBright); 535 } 536 537 /** 538 * Marks this dream as windowless. Only available to doze dreams. 539 * 540 * @hide 541 */ 542 public void setWindowless(boolean windowless) { 543 mWindowless = windowless; 544 } 545 546 /** 547 * Returns whether or not this dream is windowless. Only available to doze dreams. 548 * 549 * @hide 550 */ 551 public boolean isWindowless() { 552 return mWindowless; 553 } 554 555 /** 556 * Returns true if this dream is allowed to doze. 557 * <p> 558 * The value returned by this method is only meaningful when the dream has started. 559 * </p> 560 * 561 * @return True if this dream can doze. 562 * @see #startDozing 563 * @hide experimental 564 */ 565 public boolean canDoze() { 566 return mCanDoze; 567 } 568 569 /** 570 * Starts dozing, entering a deep dreamy sleep. 571 * <p> 572 * Dozing enables the system to conserve power while the user is not actively interacting 573 * with the device. While dozing, the display will remain on in a low-power state 574 * and will continue to show its previous contents but the application processor and 575 * other system components will be allowed to suspend when possible. 576 * </p><p> 577 * While the application processor is suspended, the dream may stop executing code 578 * for long periods of time. Prior to being suspended, the dream may schedule periodic 579 * wake-ups to render new content by scheduling an alarm with the {@link AlarmManager}. 580 * The dream may also keep the CPU awake by acquiring a 581 * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} when necessary. 582 * Note that since the purpose of doze mode is to conserve power (especially when 583 * running on battery), the dream should not wake the CPU very often or keep it 584 * awake for very long. 585 * </p><p> 586 * It is a good idea to call this method some time after the dream's entry animation 587 * has completed and the dream is ready to doze. It is important to completely 588 * finish all of the work needed before dozing since the application processor may 589 * be suspended at any moment once this method is called unless other wake locks 590 * are being held. 591 * </p><p> 592 * Call {@link #stopDozing} or {@link #finish} to stop dozing. 593 * </p> 594 * 595 * @see #stopDozing 596 * @hide experimental 597 */ 598 public void startDozing() { 599 if (mCanDoze && !mDozing) { 600 mDozing = true; 601 try { 602 mSandman.startDozing(mWindowToken); 603 } catch (RemoteException ex) { 604 // system server died 605 } 606 } 607 } 608 609 /** 610 * Stops dozing, returns to active dreaming. 611 * <p> 612 * This method reverses the effect of {@link #startDozing}. From this moment onward, 613 * the application processor will be kept awake as long as the dream is running 614 * or until the dream starts dozing again. 615 * </p> 616 * 617 * @see #startDozing 618 * @hide experimental 619 */ 620 public void stopDozing() { 621 if (mDozing) { 622 mDozing = false; 623 try { 624 mSandman.stopDozing(mWindowToken); 625 } catch (RemoteException ex) { 626 // system server died 627 } 628 } 629 } 630 631 /** 632 * Returns true if the dream will allow the system to enter a low-power state while 633 * it is running without actually turning off the screen. Defaults to false, 634 * keeping the application processor awake while the dream is running. 635 * 636 * @return True if the dream is dozing. 637 * 638 * @see #setDozing(boolean) 639 * @hide experimental 640 */ 641 public boolean isDozing() { 642 return mDozing; 643 } 644 645 /** 646 * Gets an object that may be used to access low-level hardware features that a 647 * dream may use to provide a richer user experience while dozing. 648 * 649 * @return An instance of {@link DozeHardware} or null if this device does not offer 650 * hardware support for dozing. 651 * 652 * @hide experimental 653 */ 654 public DozeHardware getDozeHardware() { 655 if (mCanDoze && mDozeHardware == null && mWindowToken != null) { 656 try { 657 IDozeHardware hardware = mSandman.getDozeHardware(mWindowToken); 658 if (hardware != null) { 659 mDozeHardware = new DozeHardware(hardware); 660 } 661 } catch (RemoteException ex) { 662 // system server died 663 } 664 } 665 return mDozeHardware; 666 } 667 668 /** 669 * Called when this Dream is constructed. 670 */ 671 @Override 672 public void onCreate() { 673 if (mDebug) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId()); 674 super.onCreate(); 675 } 676 677 /** 678 * Called when the dream's window has been created and is visible and animation may now begin. 679 */ 680 public void onDreamingStarted() { 681 if (mDebug) Slog.v(TAG, "onDreamingStarted()"); 682 // hook for subclasses 683 } 684 685 /** 686 * Called when this Dream is stopped, either by external request or by calling finish(), 687 * before the window has been removed. 688 */ 689 public void onDreamingStopped() { 690 if (mDebug) Slog.v(TAG, "onDreamingStopped()"); 691 // hook for subclasses 692 } 693 694 /** 695 * Called when the dream is being asked to stop itself and wake. 696 * <p> 697 * The default implementation simply calls {@link #finish} which ends the dream 698 * immediately. Subclasses may override this function to perform a smooth exit 699 * transition then call {@link #finish} afterwards. 700 * </p><p> 701 * Note that the dream will only be given a short period of time (currently about 702 * five seconds) to wake up. If the dream does not finish itself in a timely manner 703 * then the system will forcibly finish it once the time allowance is up. 704 * </p> 705 */ 706 public void onWakeUp() { 707 finish(); 708 } 709 710 /** {@inheritDoc} */ 711 @Override 712 public final IBinder onBind(Intent intent) { 713 if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); 714 return new DreamServiceWrapper(); 715 } 716 717 /** 718 * Stops the dream and detaches from the window. 719 * <p> 720 * When the dream ends, the system will be allowed to go to sleep fully unless there 721 * is a reason for it to be awake such as recent user activity or wake locks being held. 722 * </p> 723 */ 724 public final void finish() { 725 if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished); 726 727 if (!mFinished) { 728 mFinished = true; 729 730 if (mWindowToken == null) { 731 Slog.w(TAG, "Finish was called before the dream was attached."); 732 } else { 733 try { 734 mSandman.finishSelf(mWindowToken, true /*immediate*/); 735 } catch (RemoteException ex) { 736 // system server died 737 } 738 } 739 740 stopSelf(); // if launched via any other means 741 } 742 } 743 744 /** 745 * Wakes the dream up gently. 746 * <p> 747 * Calls {@link #onWakeUp} to give the dream a chance to perform an exit transition. 748 * When the transition is over, the dream should call {@link #finish}. 749 * </p> 750 */ 751 public final void wakeUp() { 752 wakeUp(false); 753 } 754 755 private void wakeUp(boolean fromSystem) { 756 if (mDebug) Slog.v(TAG, "wakeUp(): fromSystem=" + fromSystem 757 + ", mWaking=" + mWaking + ", mFinished=" + mFinished); 758 759 if (!mWaking && !mFinished) { 760 mWaking = true; 761 762 // As a minor optimization, invoke the callback first in case it simply 763 // calls finish() immediately so there wouldn't be much point in telling 764 // the system that we are finishing the dream gently. 765 onWakeUp(); 766 767 // Now tell the system we are waking gently, unless we already told 768 // it we were finishing immediately. 769 if (!fromSystem && !mFinished) { 770 if (mWindowToken == null) { 771 Slog.w(TAG, "WakeUp was called before the dream was attached."); 772 } else { 773 try { 774 mSandman.finishSelf(mWindowToken, false /*immediate*/); 775 } catch (RemoteException ex) { 776 // system server died 777 } 778 } 779 } 780 } 781 } 782 783 /** {@inheritDoc} */ 784 @Override 785 public void onDestroy() { 786 if (mDebug) Slog.v(TAG, "onDestroy()"); 787 // hook for subclasses 788 789 // Just in case destroy came in before detach, let's take care of that now 790 detach(); 791 792 super.onDestroy(); 793 } 794 795 // end public api 796 797 /** 798 * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed. 799 * 800 * Must run on mHandler. 801 */ 802 private final void detach() { 803 if (mStarted) { 804 if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()"); 805 mStarted = false; 806 onDreamingStopped(); 807 } 808 809 if (mWindow != null) { 810 // force our window to be removed synchronously 811 if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); 812 mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView()); 813 mWindow = null; 814 } 815 816 if (mWindowToken != null) { 817 // the following will print a log message if it finds any other leaked windows 818 WindowManagerGlobal.getInstance().closeAll(mWindowToken, 819 this.getClass().getName(), "Dream"); 820 mWindowToken = null; 821 mCanDoze = false; 822 } 823 } 824 825 /** 826 * Called when the Dream is ready to be shown. 827 * 828 * Must run on mHandler. 829 * 830 * @param windowToken A window token that will allow a window to be created in the correct layer. 831 */ 832 private final void attach(IBinder windowToken, boolean canDoze) { 833 if (mWindowToken != null) { 834 Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); 835 return; 836 } 837 if (mFinished || mWaking) { 838 Slog.w(TAG, "attach() called after dream already finished"); 839 try { 840 mSandman.finishSelf(windowToken, true /*immediate*/); 841 } catch (RemoteException ex) { 842 // system server died 843 } 844 return; 845 } 846 847 if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); 848 849 mWindowToken = windowToken; 850 mCanDoze = canDoze; 851 if (mWindowless && !mCanDoze) { 852 throw new IllegalStateException("Only doze dreams can be windowless"); 853 } 854 if (!mWindowless) { 855 mWindow = PolicyManager.makeNewWindow(this); 856 mWindow.setCallback(this); 857 mWindow.requestFeature(Window.FEATURE_NO_TITLE); 858 mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); 859 mWindow.setFormat(PixelFormat.OPAQUE); 860 861 if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", 862 windowToken, WindowManager.LayoutParams.TYPE_DREAM)); 863 864 WindowManager.LayoutParams lp = mWindow.getAttributes(); 865 lp.type = WindowManager.LayoutParams.TYPE_DREAM; 866 lp.token = windowToken; 867 lp.windowAnimations = com.android.internal.R.style.Animation_Dream; 868 lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 869 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 870 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 871 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD 872 | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 873 | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) 874 | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) 875 ); 876 mWindow.setAttributes(lp); 877 mWindow.setWindowManager(null, windowToken, "dream", true); 878 879 applySystemUiVisibilityFlags( 880 (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), 881 View.SYSTEM_UI_FLAG_LOW_PROFILE); 882 883 try { 884 getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); 885 } catch (WindowManager.BadTokenException ex) { 886 // This can happen because the dream manager service will remove the token 887 // immediately without necessarily waiting for the dream to start. 888 // We should receive a finish message soon. 889 Slog.i(TAG, "attach() called after window token already removed, dream will " 890 + "finish soon"); 891 mWindow = null; 892 return; 893 } 894 } 895 // We need to defer calling onDreamingStarted until after onWindowAttached, 896 // which is posted to the handler by addView, so we post onDreamingStarted 897 // to the handler also. Need to watch out here in case detach occurs before 898 // this callback is invoked. 899 mHandler.post(new Runnable() { 900 @Override 901 public void run() { 902 if (mWindow != null || mWindowless) { 903 if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); 904 mStarted = true; 905 onDreamingStarted(); 906 } 907 } 908 }); 909 } 910 911 private boolean getWindowFlagValue(int flag, boolean defaultValue) { 912 return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0; 913 } 914 915 private void applyWindowFlags(int flags, int mask) { 916 if (mWindow != null) { 917 WindowManager.LayoutParams lp = mWindow.getAttributes(); 918 lp.flags = applyFlags(lp.flags, flags, mask); 919 mWindow.setAttributes(lp); 920 mWindow.getWindowManager().updateViewLayout(mWindow.getDecorView(), lp); 921 } 922 } 923 924 private boolean getSystemUiVisibilityFlagValue(int flag, boolean defaultValue) { 925 View v = mWindow == null ? null : mWindow.getDecorView(); 926 return v == null ? defaultValue : (v.getSystemUiVisibility() & flag) != 0; 927 } 928 929 private void applySystemUiVisibilityFlags(int flags, int mask) { 930 View v = mWindow == null ? null : mWindow.getDecorView(); 931 if (v != null) { 932 v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask)); 933 } 934 } 935 936 private int applyFlags(int oldFlags, int flags, int mask) { 937 return (oldFlags&~mask) | (flags&mask); 938 } 939 940 @Override 941 protected void dump(final FileDescriptor fd, PrintWriter pw, final String[] args) { 942 DumpUtils.dumpAsync(mHandler, new Dump() { 943 @Override 944 public void dump(PrintWriter pw) { 945 dumpOnHandler(fd, pw, args); 946 } 947 }, pw, 1000); 948 } 949 950 /** @hide */ 951 protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) { 952 pw.print(TAG + ": "); 953 if (mWindowToken == null) { 954 pw.println("stopped"); 955 } else { 956 pw.println("running (token=" + mWindowToken + ")"); 957 } 958 pw.println(" window: " + mWindow); 959 pw.print(" flags:"); 960 if (isInteractive()) pw.print(" interactive"); 961 if (isLowProfile()) pw.print(" lowprofile"); 962 if (isFullscreen()) pw.print(" fullscreen"); 963 if (isScreenBright()) pw.print(" bright"); 964 if (isWindowless()) pw.print(" windowless"); 965 if (isDozing()) pw.print(" dozing"); 966 pw.println(); 967 } 968 969 private final class DreamServiceWrapper extends IDreamService.Stub { 970 @Override 971 public void attach(final IBinder windowToken, final boolean canDoze) { 972 mHandler.post(new Runnable() { 973 @Override 974 public void run() { 975 DreamService.this.attach(windowToken, canDoze); 976 } 977 }); 978 } 979 980 @Override 981 public void detach() { 982 mHandler.post(new Runnable() { 983 @Override 984 public void run() { 985 DreamService.this.detach(); 986 } 987 }); 988 } 989 990 @Override 991 public void wakeUp() { 992 mHandler.post(new Runnable() { 993 @Override 994 public void run() { 995 DreamService.this.wakeUp(true /*fromSystem*/); 996 } 997 }); 998 } 999 } 1000} 1001