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