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