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