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