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