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