DreamService.java revision 6612727c18fe6e7b6edf811f3a1a871a710c0ebe
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 mFinished; 175 private boolean mCanDoze; 176 private boolean mDozing; 177 private boolean mWindowless; 178 private DozeHardware mDozeHardware; 179 180 private boolean mDebug = false; 181 182 public DreamService() { 183 mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); 184 } 185 186 /** 187 * @hide 188 */ 189 public void setDebug(boolean dbg) { 190 mDebug = dbg; 191 } 192 193 // begin Window.Callback methods 194 /** {@inheritDoc} */ 195 @Override 196 public boolean dispatchKeyEvent(KeyEvent event) { 197 // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK 198 if (!mInteractive) { 199 if (mDebug) Slog.v(TAG, "Finishing on keyEvent"); 200 safelyFinish(); 201 return true; 202 } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 203 if (mDebug) Slog.v(TAG, "Finishing on back key"); 204 safelyFinish(); 205 return true; 206 } 207 return mWindow.superDispatchKeyEvent(event); 208 } 209 210 /** {@inheritDoc} */ 211 @Override 212 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 213 if (!mInteractive) { 214 if (mDebug) Slog.v(TAG, "Finishing on keyShortcutEvent"); 215 safelyFinish(); 216 return true; 217 } 218 return mWindow.superDispatchKeyShortcutEvent(event); 219 } 220 221 /** {@inheritDoc} */ 222 @Override 223 public boolean dispatchTouchEvent(MotionEvent event) { 224 // TODO: create more flexible version of mInteractive that allows clicks 225 // but finish()es on any other kind of activity 226 if (!mInteractive) { 227 if (mDebug) Slog.v(TAG, "Finishing on touchEvent"); 228 safelyFinish(); 229 return true; 230 } 231 return mWindow.superDispatchTouchEvent(event); 232 } 233 234 /** {@inheritDoc} */ 235 @Override 236 public boolean dispatchTrackballEvent(MotionEvent event) { 237 if (!mInteractive) { 238 if (mDebug) Slog.v(TAG, "Finishing on trackballEvent"); 239 safelyFinish(); 240 return true; 241 } 242 return mWindow.superDispatchTrackballEvent(event); 243 } 244 245 /** {@inheritDoc} */ 246 @Override 247 public boolean dispatchGenericMotionEvent(MotionEvent event) { 248 if (!mInteractive) { 249 if (mDebug) Slog.v(TAG, "Finishing on genericMotionEvent"); 250 safelyFinish(); 251 return true; 252 } 253 return mWindow.superDispatchGenericMotionEvent(event); 254 } 255 256 /** {@inheritDoc} */ 257 @Override 258 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 259 return false; 260 } 261 262 /** {@inheritDoc} */ 263 @Override 264 public View onCreatePanelView(int featureId) { 265 return null; 266 } 267 268 /** {@inheritDoc} */ 269 @Override 270 public boolean onCreatePanelMenu(int featureId, Menu menu) { 271 return false; 272 } 273 274 /** {@inheritDoc} */ 275 @Override 276 public boolean onPreparePanel(int featureId, View view, Menu menu) { 277 return false; 278 } 279 280 /** {@inheritDoc} */ 281 @Override 282 public boolean onMenuOpened(int featureId, Menu menu) { 283 return false; 284 } 285 286 /** {@inheritDoc} */ 287 @Override 288 public boolean onMenuItemSelected(int featureId, MenuItem item) { 289 return false; 290 } 291 292 /** {@inheritDoc} */ 293 @Override 294 public void onWindowAttributesChanged(LayoutParams attrs) { 295 } 296 297 /** {@inheritDoc} */ 298 @Override 299 public void onContentChanged() { 300 } 301 302 /** {@inheritDoc} */ 303 @Override 304 public void onWindowFocusChanged(boolean hasFocus) { 305 } 306 307 /** {@inheritDoc} */ 308 @Override 309 public void onAttachedToWindow() { 310 } 311 312 /** {@inheritDoc} */ 313 @Override 314 public void onDetachedFromWindow() { 315 } 316 317 /** {@inheritDoc} */ 318 @Override 319 public void onPanelClosed(int featureId, Menu menu) { 320 } 321 322 /** {@inheritDoc} */ 323 @Override 324 public boolean onSearchRequested() { 325 return false; 326 } 327 328 /** {@inheritDoc} */ 329 @Override 330 public ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback callback) { 331 return null; 332 } 333 334 /** {@inheritDoc} */ 335 @Override 336 public void onActionModeStarted(ActionMode mode) { 337 } 338 339 /** {@inheritDoc} */ 340 @Override 341 public void onActionModeFinished(ActionMode mode) { 342 } 343 // end Window.Callback methods 344 345 // begin public api 346 /** 347 * Retrieves the current {@link android.view.WindowManager} for the dream. 348 * Behaves similarly to {@link android.app.Activity#getWindowManager()}. 349 * 350 * @return The current window manager, or null if the dream is not started. 351 */ 352 public WindowManager getWindowManager() { 353 return mWindow != null ? mWindow.getWindowManager() : null; 354 } 355 356 /** 357 * Retrieves the current {@link android.view.Window} for the dream. 358 * Behaves similarly to {@link android.app.Activity#getWindow()}. 359 * 360 * @return The current window, or null if the dream is not started. 361 */ 362 public Window getWindow() { 363 return mWindow; 364 } 365 366 /** 367 * Inflates a layout resource and set it to be the content view for this Dream. 368 * Behaves similarly to {@link android.app.Activity#setContentView(int)}. 369 * 370 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 371 * 372 * @param layoutResID Resource ID to be inflated. 373 * 374 * @see #setContentView(android.view.View) 375 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 376 */ 377 public void setContentView(int layoutResID) { 378 getWindow().setContentView(layoutResID); 379 } 380 381 /** 382 * Sets a view to be the content view for this Dream. 383 * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)} in an activity, 384 * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view. 385 * 386 * <p>Note: This requires a window, so you should usually call it during 387 * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it 388 * during {@link #onCreate}).</p> 389 * 390 * @see #setContentView(int) 391 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 392 */ 393 public void setContentView(View view) { 394 getWindow().setContentView(view); 395 } 396 397 /** 398 * Sets a view to be the content view for this Dream. 399 * Behaves similarly to 400 * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)} 401 * in an activity. 402 * 403 * <p>Note: This requires a window, so you should usually call it during 404 * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it 405 * during {@link #onCreate}).</p> 406 * 407 * @param view The desired content to display. 408 * @param params Layout parameters for the view. 409 * 410 * @see #setContentView(android.view.View) 411 * @see #setContentView(int) 412 */ 413 public void setContentView(View view, ViewGroup.LayoutParams params) { 414 getWindow().setContentView(view, params); 415 } 416 417 /** 418 * Adds a view to the Dream's window, leaving other content views in place. 419 * 420 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 421 * 422 * @param view The desired content to display. 423 * @param params Layout parameters for the view. 424 */ 425 public void addContentView(View view, ViewGroup.LayoutParams params) { 426 getWindow().addContentView(view, params); 427 } 428 429 /** 430 * Finds a view that was identified by the id attribute from the XML that 431 * was processed in {@link #onCreate}. 432 * 433 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 434 * 435 * @return The view if found or null otherwise. 436 */ 437 public View findViewById(int id) { 438 return getWindow().findViewById(id); 439 } 440 441 /** 442 * Marks this dream as interactive to receive input events. 443 * 444 * <p>Non-interactive dreams (default) will dismiss on the first input event.</p> 445 * 446 * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p> 447 * 448 * @param interactive True if this dream will handle input events. 449 */ 450 public void setInteractive(boolean interactive) { 451 mInteractive = interactive; 452 } 453 454 /** 455 * Returns whether or not this dream is interactive. Defaults to false. 456 * 457 * @see #setInteractive(boolean) 458 */ 459 public boolean isInteractive() { 460 return mInteractive; 461 } 462 463 /** 464 * Sets View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. 465 * 466 * @param lowProfile True to set View.SYSTEM_UI_FLAG_LOW_PROFILE 467 * @hide There is no reason to have this -- dreams can set this flag 468 * on their own content view, and from there can actually do the 469 * correct interactions with it (seeing when it is cleared etc). 470 */ 471 public void setLowProfile(boolean lowProfile) { 472 if (mLowProfile != lowProfile) { 473 mLowProfile = lowProfile; 474 int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; 475 applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); 476 } 477 } 478 479 /** 480 * Returns whether or not this dream is in low profile mode. Defaults to true. 481 * 482 * @see #setLowProfile(boolean) 483 * @hide 484 */ 485 public boolean isLowProfile() { 486 return getSystemUiVisibilityFlagValue(View.SYSTEM_UI_FLAG_LOW_PROFILE, mLowProfile); 487 } 488 489 /** 490 * Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} 491 * on the dream's window. 492 * 493 * @param fullscreen If true, the fullscreen flag will be set; else it 494 * will be cleared. 495 */ 496 public void setFullscreen(boolean fullscreen) { 497 if (mFullscreen != fullscreen) { 498 mFullscreen = fullscreen; 499 int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; 500 applyWindowFlags(mFullscreen ? flag : 0, flag); 501 } 502 } 503 504 /** 505 * Returns whether or not this dream is in fullscreen mode. Defaults to false. 506 * 507 * @see #setFullscreen(boolean) 508 */ 509 public boolean isFullscreen() { 510 return mFullscreen; 511 } 512 513 /** 514 * Marks this dream as keeping the screen bright while dreaming. 515 * 516 * @param screenBright True to keep the screen bright while dreaming. 517 */ 518 public void setScreenBright(boolean screenBright) { 519 if (mScreenBright != screenBright) { 520 mScreenBright = screenBright; 521 int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 522 applyWindowFlags(mScreenBright ? flag : 0, flag); 523 } 524 } 525 526 /** 527 * Returns whether or not this dream keeps the screen bright while dreaming. 528 * Defaults to false, allowing the screen to dim if necessary. 529 * 530 * @see #setScreenBright(boolean) 531 */ 532 public boolean isScreenBright() { 533 return getWindowFlagValue(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, mScreenBright); 534 } 535 536 /** 537 * Marks this dream as windowless. Only available to doze dreams. 538 * 539 * @hide 540 */ 541 public void setWindowless(boolean windowless) { 542 mWindowless = windowless; 543 } 544 545 /** 546 * Returns whether or not this dream is windowless. Only available to doze dreams. 547 * 548 * @hide 549 */ 550 public boolean isWindowless() { 551 return mWindowless; 552 } 553 554 /** 555 * Returns true if this dream is allowed to doze. 556 * <p> 557 * The value returned by this method is only meaningful when the dream has started. 558 * </p> 559 * 560 * @return True if this dream can doze. 561 * @see #startDozing 562 * @hide experimental 563 */ 564 public boolean canDoze() { 565 return mCanDoze; 566 } 567 568 /** 569 * Starts dozing, entering a deep dreamy sleep. 570 * <p> 571 * Dozing enables the system to conserve power while the user is not actively interacting 572 * with the device. While dozing, the display will remain on in a low-power state 573 * and will continue to show its previous contents but the application processor and 574 * other system components will be allowed to suspend when possible. 575 * </p><p> 576 * While the application processor is suspended, the dream may stop executing code 577 * for long periods of time. Prior to being suspended, the dream may schedule periodic 578 * wake-ups to render new content by scheduling an alarm with the {@link AlarmManager}. 579 * The dream may also keep the CPU awake by acquiring a 580 * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} when necessary. 581 * Note that since the purpose of doze mode is to conserve power (especially when 582 * running on battery), the dream should not wake the CPU very often or keep it 583 * awake for very long. 584 * </p><p> 585 * It is a good idea to call this method some time after the dream's entry animation 586 * has completed and the dream is ready to doze. It is important to completely 587 * finish all of the work needed before dozing since the application processor may 588 * be suspended at any moment once this method is called unless other wake locks 589 * are being held. 590 * </p><p> 591 * Call {@link #stopDozing} or {@link #finish} to stop dozing. 592 * </p> 593 * 594 * @see #stopDozing 595 * @hide experimental 596 */ 597 public void startDozing() { 598 if (mCanDoze && !mDozing) { 599 mDozing = true; 600 try { 601 mSandman.startDozing(mWindowToken); 602 } catch (RemoteException ex) { 603 // system server died 604 } 605 } 606 } 607 608 /** 609 * Stops dozing, returns to active dreaming. 610 * <p> 611 * This method reverses the effect of {@link #startDozing}. From this moment onward, 612 * the application processor will be kept awake as long as the dream is running 613 * or until the dream starts dozing again. 614 * </p> 615 * 616 * @see #startDozing 617 * @hide experimental 618 */ 619 public void stopDozing() { 620 if (mDozing) { 621 mDozing = false; 622 try { 623 mSandman.stopDozing(mWindowToken); 624 } catch (RemoteException ex) { 625 // system server died 626 } 627 } 628 } 629 630 /** 631 * Returns true if the dream will allow the system to enter a low-power state while 632 * it is running without actually turning off the screen. Defaults to false, 633 * keeping the application processor awake while the dream is running. 634 * 635 * @return True if the dream is dozing. 636 * 637 * @see #setDozing(boolean) 638 * @hide experimental 639 */ 640 public boolean isDozing() { 641 return mDozing; 642 } 643 644 /** 645 * Gets an object that may be used to access low-level hardware features that a 646 * dream may use to provide a richer user experience while dozing. 647 * 648 * @return An instance of {@link DozeHardware} or null if this device does not offer 649 * hardware support for dozing. 650 * 651 * @hide experimental 652 */ 653 public DozeHardware getDozeHardware() { 654 if (mCanDoze && mDozeHardware == null && mWindowToken != null) { 655 try { 656 IDozeHardware hardware = mSandman.getDozeHardware(mWindowToken); 657 if (hardware != null) { 658 mDozeHardware = new DozeHardware(hardware); 659 } 660 } catch (RemoteException ex) { 661 // system server died 662 } 663 } 664 return mDozeHardware; 665 } 666 667 /** 668 * Called when this Dream is constructed. 669 */ 670 @Override 671 public void onCreate() { 672 if (mDebug) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId()); 673 super.onCreate(); 674 } 675 676 /** 677 * Called when the dream's window has been created and is visible and animation may now begin. 678 */ 679 public void onDreamingStarted() { 680 if (mDebug) Slog.v(TAG, "onDreamingStarted()"); 681 // hook for subclasses 682 } 683 684 /** 685 * Called when this Dream is stopped, either by external request or by calling finish(), 686 * before the window has been removed. 687 */ 688 public void onDreamingStopped() { 689 if (mDebug) Slog.v(TAG, "onDreamingStopped()"); 690 // hook for subclasses 691 } 692 693 /** {@inheritDoc} */ 694 @Override 695 public final IBinder onBind(Intent intent) { 696 if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); 697 return new DreamServiceWrapper(); 698 } 699 700 /** 701 * Stops the dream and detaches from the window. 702 * <p> 703 * When the dream ends, the system will be allowed to go to sleep fully unless there 704 * is a reason for it to be awake such as recent user activity or wake locks being held. 705 * </p> 706 */ 707 public final void finish() { 708 if (mDebug) Slog.v(TAG, "finish()"); 709 finishInternal(); 710 } 711 712 /** {@inheritDoc} */ 713 @Override 714 public void onDestroy() { 715 if (mDebug) Slog.v(TAG, "onDestroy()"); 716 // hook for subclasses 717 718 // Just in case destroy came in before detach, let's take care of that now 719 detach(); 720 721 super.onDestroy(); 722 } 723 724 // end public api 725 726 /** 727 * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed. 728 * 729 * Must run on mHandler. 730 */ 731 private final void detach() { 732 if (mStarted) { 733 if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()"); 734 mStarted = false; 735 onDreamingStopped(); 736 } 737 738 if (mWindow != null) { 739 // force our window to be removed synchronously 740 if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); 741 mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView()); 742 mWindow = null; 743 } 744 745 if (mWindowToken != null) { 746 // the following will print a log message if it finds any other leaked windows 747 WindowManagerGlobal.getInstance().closeAll(mWindowToken, 748 this.getClass().getName(), "Dream"); 749 mWindowToken = null; 750 mCanDoze = false; 751 } 752 } 753 754 /** 755 * Called when the Dream is ready to be shown. 756 * 757 * Must run on mHandler. 758 * 759 * @param windowToken A window token that will allow a window to be created in the correct layer. 760 */ 761 private final void attach(IBinder windowToken, boolean canDoze) { 762 if (mWindowToken != null) { 763 Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); 764 return; 765 } 766 if (mFinished) { 767 Slog.w(TAG, "attach() called after dream already finished"); 768 try { 769 mSandman.finishSelf(windowToken); 770 } catch (RemoteException ex) { 771 // system server died 772 } 773 return; 774 } 775 776 if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); 777 778 mWindowToken = windowToken; 779 mCanDoze = canDoze; 780 if (mWindowless && !mCanDoze) { 781 throw new IllegalStateException("Only doze dreams can be windowless"); 782 } 783 if (!mWindowless) { 784 mWindow = PolicyManager.makeNewWindow(this); 785 mWindow.setCallback(this); 786 mWindow.requestFeature(Window.FEATURE_NO_TITLE); 787 mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); 788 mWindow.setFormat(PixelFormat.OPAQUE); 789 790 if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", 791 windowToken, WindowManager.LayoutParams.TYPE_DREAM)); 792 793 WindowManager.LayoutParams lp = mWindow.getAttributes(); 794 lp.type = WindowManager.LayoutParams.TYPE_DREAM; 795 lp.token = windowToken; 796 lp.windowAnimations = com.android.internal.R.style.Animation_Dream; 797 lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 798 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 799 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 800 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD 801 | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 802 | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) 803 | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) 804 ); 805 mWindow.setAttributes(lp); 806 mWindow.setWindowManager(null, windowToken, "dream", true); 807 808 applySystemUiVisibilityFlags( 809 (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), 810 View.SYSTEM_UI_FLAG_LOW_PROFILE); 811 812 try { 813 getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); 814 } catch (WindowManager.BadTokenException ex) { 815 // This can happen because the dream manager service will remove the token 816 // immediately without necessarily waiting for the dream to start. 817 // We should receive a finish message soon. 818 Slog.i(TAG, "attach() called after window token already removed, dream will " 819 + "finish soon"); 820 mWindow = null; 821 return; 822 } 823 } 824 // We need to defer calling onDreamingStarted until after onWindowAttached, 825 // which is posted to the handler by addView, so we post onDreamingStarted 826 // to the handler also. Need to watch out here in case detach occurs before 827 // this callback is invoked. 828 mHandler.post(new Runnable() { 829 @Override 830 public void run() { 831 if (mWindow != null || mWindowless) { 832 if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); 833 mStarted = true; 834 onDreamingStarted(); 835 } 836 } 837 }); 838 } 839 840 private void safelyFinish() { 841 if (mDebug) Slog.v(TAG, "safelyFinish()"); 842 843 finish(); 844 845 if (!mFinished) { 846 Slog.w(TAG, "Bad dream, did not call super.finish()"); 847 finishInternal(); 848 } 849 } 850 851 private void finishInternal() { 852 if (mDebug) Slog.v(TAG, "finishInternal() mFinished = " + mFinished); 853 854 if (!mFinished) { 855 mFinished = true; 856 857 if (mWindowToken == null) { 858 Slog.w(TAG, "Finish was called before the dream was attached."); 859 } else { 860 try { 861 mSandman.finishSelf(mWindowToken); 862 } catch (RemoteException ex) { 863 // system server died 864 } 865 } 866 867 stopSelf(); // if launched via any other means 868 } 869 } 870 871 private boolean getWindowFlagValue(int flag, boolean defaultValue) { 872 return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0; 873 } 874 875 private void applyWindowFlags(int flags, int mask) { 876 if (mWindow != null) { 877 WindowManager.LayoutParams lp = mWindow.getAttributes(); 878 lp.flags = applyFlags(lp.flags, flags, mask); 879 mWindow.setAttributes(lp); 880 mWindow.getWindowManager().updateViewLayout(mWindow.getDecorView(), lp); 881 } 882 } 883 884 private boolean getSystemUiVisibilityFlagValue(int flag, boolean defaultValue) { 885 View v = mWindow == null ? null : mWindow.getDecorView(); 886 return v == null ? defaultValue : (v.getSystemUiVisibility() & flag) != 0; 887 } 888 889 private void applySystemUiVisibilityFlags(int flags, int mask) { 890 View v = mWindow == null ? null : mWindow.getDecorView(); 891 if (v != null) { 892 v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask)); 893 } 894 } 895 896 private int applyFlags(int oldFlags, int flags, int mask) { 897 return (oldFlags&~mask) | (flags&mask); 898 } 899 900 @Override 901 protected void dump(final FileDescriptor fd, PrintWriter pw, final String[] args) { 902 DumpUtils.dumpAsync(mHandler, new Dump() { 903 @Override 904 public void dump(PrintWriter pw) { 905 dumpOnHandler(fd, pw, args); 906 } 907 }, pw, 1000); 908 } 909 910 /** @hide */ 911 protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) { 912 pw.print(TAG + ": "); 913 if (mWindowToken == null) { 914 pw.println("stopped"); 915 } else { 916 pw.println("running (token=" + mWindowToken + ")"); 917 } 918 pw.println(" window: " + mWindow); 919 pw.print(" flags:"); 920 if (isInteractive()) pw.print(" interactive"); 921 if (isLowProfile()) pw.print(" lowprofile"); 922 if (isFullscreen()) pw.print(" fullscreen"); 923 if (isScreenBright()) pw.print(" bright"); 924 if (isWindowless()) pw.print(" windowless"); 925 if (isDozing()) pw.print(" dozing"); 926 pw.println(); 927 } 928 929 private final class DreamServiceWrapper extends IDreamService.Stub { 930 @Override 931 public void attach(final IBinder windowToken, final boolean canDoze) { 932 mHandler.post(new Runnable() { 933 @Override 934 public void run() { 935 DreamService.this.attach(windowToken, canDoze); 936 } 937 }); 938 } 939 940 @Override 941 public void detach() { 942 mHandler.post(new Runnable() { 943 @Override 944 public void run() { 945 DreamService.this.detach(); 946 } 947 }); 948 } 949 } 950 951} 952