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