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.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SdkConstant; 23 import android.annotation.SdkConstant.SdkConstantType; 24 import android.app.AlarmManager; 25 import android.app.Service; 26 import android.content.Intent; 27 import android.graphics.PixelFormat; 28 import android.graphics.drawable.ColorDrawable; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.IRemoteCallback; 32 import android.os.PowerManager; 33 import android.os.RemoteException; 34 import android.os.ServiceManager; 35 import android.util.MathUtils; 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 android.view.SearchEvent; 44 import android.view.View; 45 import android.view.ViewGroup; 46 import android.view.Window; 47 import android.view.WindowManager; 48 import android.view.WindowManager.LayoutParams; 49 import android.view.WindowManagerGlobal; 50 import android.view.accessibility.AccessibilityEvent; 51 52 import com.android.internal.policy.PhoneWindow; 53 import com.android.internal.util.DumpUtils; 54 import com.android.internal.util.DumpUtils.Dump; 55 56 import java.io.FileDescriptor; 57 import java.io.PrintWriter; 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 * <p> 462 * <strong>Note:</strong> In most cases -- depending on compiler support -- 463 * the resulting view is automatically cast to the target class type. If 464 * the target class type is unconstrained, an explicit cast may be 465 * necessary. 466 * 467 * @param id the ID to search for 468 * @return The view if found or null otherwise. 469 * @see View#findViewById(int) 470 * @see DreamService#requireViewById(int) 471 */ 472 @Nullable findViewById(@dRes int id)473 public <T extends View> T findViewById(@IdRes int id) { 474 return getWindow().findViewById(id); 475 } 476 477 /** 478 * Finds a view that was identified by the id attribute from the XML that was processed in 479 * {@link #onCreate}, or throws an IllegalArgumentException if the ID is invalid or there is no 480 * matching view in the hierarchy. 481 * 482 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 483 * <p> 484 * <strong>Note:</strong> In most cases -- depending on compiler support -- 485 * the resulting view is automatically cast to the target class type. If 486 * the target class type is unconstrained, an explicit cast may be 487 * necessary. 488 * 489 * @param id the ID to search for 490 * @return a view with given ID 491 * @see View#requireViewById(int) 492 * @see DreamService#findViewById(int) 493 */ 494 @NonNull requireViewById(@dRes int id)495 public final <T extends View> T requireViewById(@IdRes int id) { 496 T view = findViewById(id); 497 if (view == null) { 498 throw new IllegalArgumentException( 499 "ID does not reference a View inside this DreamService"); 500 } 501 return view; 502 } 503 504 /** 505 * Marks this dream as interactive to receive input events. 506 * 507 * <p>Non-interactive dreams (default) will dismiss on the first input event.</p> 508 * 509 * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p> 510 * 511 * @param interactive True if this dream will handle input events. 512 */ setInteractive(boolean interactive)513 public void setInteractive(boolean interactive) { 514 mInteractive = interactive; 515 } 516 517 /** 518 * Returns whether or not this dream is interactive. Defaults to false. 519 * 520 * @see #setInteractive(boolean) 521 */ isInteractive()522 public boolean isInteractive() { 523 return mInteractive; 524 } 525 526 /** 527 * Sets View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. 528 * 529 * @param lowProfile True to set View.SYSTEM_UI_FLAG_LOW_PROFILE 530 * @hide There is no reason to have this -- dreams can set this flag 531 * on their own content view, and from there can actually do the 532 * correct interactions with it (seeing when it is cleared etc). 533 */ setLowProfile(boolean lowProfile)534 public void setLowProfile(boolean lowProfile) { 535 if (mLowProfile != lowProfile) { 536 mLowProfile = lowProfile; 537 int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; 538 applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); 539 } 540 } 541 542 /** 543 * Returns whether or not this dream is in low profile mode. Defaults to true. 544 * 545 * @see #setLowProfile(boolean) 546 * @hide 547 */ isLowProfile()548 public boolean isLowProfile() { 549 return getSystemUiVisibilityFlagValue(View.SYSTEM_UI_FLAG_LOW_PROFILE, mLowProfile); 550 } 551 552 /** 553 * Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} 554 * on the dream's window. 555 * 556 * @param fullscreen If true, the fullscreen flag will be set; else it 557 * will be cleared. 558 */ setFullscreen(boolean fullscreen)559 public void setFullscreen(boolean fullscreen) { 560 if (mFullscreen != fullscreen) { 561 mFullscreen = fullscreen; 562 int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; 563 applyWindowFlags(mFullscreen ? flag : 0, flag); 564 } 565 } 566 567 /** 568 * Returns whether or not this dream is in fullscreen mode. Defaults to false. 569 * 570 * @see #setFullscreen(boolean) 571 */ isFullscreen()572 public boolean isFullscreen() { 573 return mFullscreen; 574 } 575 576 /** 577 * Marks this dream as keeping the screen bright while dreaming. 578 * 579 * @param screenBright True to keep the screen bright while dreaming. 580 */ setScreenBright(boolean screenBright)581 public void setScreenBright(boolean screenBright) { 582 if (mScreenBright != screenBright) { 583 mScreenBright = screenBright; 584 int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 585 applyWindowFlags(mScreenBright ? flag : 0, flag); 586 } 587 } 588 589 /** 590 * Returns whether or not this dream keeps the screen bright while dreaming. 591 * Defaults to false, allowing the screen to dim if necessary. 592 * 593 * @see #setScreenBright(boolean) 594 */ isScreenBright()595 public boolean isScreenBright() { 596 return getWindowFlagValue(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, mScreenBright); 597 } 598 599 /** 600 * Marks this dream as windowless. Only available to doze dreams. 601 * 602 * @hide 603 */ setWindowless(boolean windowless)604 public void setWindowless(boolean windowless) { 605 mWindowless = windowless; 606 } 607 608 /** 609 * Returns whether or not this dream is windowless. Only available to doze dreams. 610 * 611 * @hide 612 */ isWindowless()613 public boolean isWindowless() { 614 return mWindowless; 615 } 616 617 /** 618 * Returns true if this dream is allowed to doze. 619 * <p> 620 * The value returned by this method is only meaningful when the dream has started. 621 * </p> 622 * 623 * @return True if this dream can doze. 624 * @see #startDozing 625 * @hide For use by system UI components only. 626 */ canDoze()627 public boolean canDoze() { 628 return mCanDoze; 629 } 630 631 /** 632 * Starts dozing, entering a deep dreamy sleep. 633 * <p> 634 * Dozing enables the system to conserve power while the user is not actively interacting 635 * with the device. While dozing, the display will remain on in a low-power state 636 * and will continue to show its previous contents but the application processor and 637 * other system components will be allowed to suspend when possible. 638 * </p><p> 639 * While the application processor is suspended, the dream may stop executing code 640 * for long periods of time. Prior to being suspended, the dream may schedule periodic 641 * wake-ups to render new content by scheduling an alarm with the {@link AlarmManager}. 642 * The dream may also keep the CPU awake by acquiring a 643 * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} when necessary. 644 * Note that since the purpose of doze mode is to conserve power (especially when 645 * running on battery), the dream should not wake the CPU very often or keep it 646 * awake for very long. 647 * </p><p> 648 * It is a good idea to call this method some time after the dream's entry animation 649 * has completed and the dream is ready to doze. It is important to completely 650 * finish all of the work needed before dozing since the application processor may 651 * be suspended at any moment once this method is called unless other wake locks 652 * are being held. 653 * </p><p> 654 * Call {@link #stopDozing} or {@link #finish} to stop dozing. 655 * </p> 656 * 657 * @see #stopDozing 658 * @hide For use by system UI components only. 659 */ startDozing()660 public void startDozing() { 661 if (mCanDoze && !mDozing) { 662 mDozing = true; 663 updateDoze(); 664 } 665 } 666 updateDoze()667 private void updateDoze() { 668 if (mWindowToken == null) { 669 Slog.w(TAG, "Updating doze without a window token."); 670 return; 671 } 672 673 if (mDozing) { 674 try { 675 mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness); 676 } catch (RemoteException ex) { 677 // system server died 678 } 679 } 680 } 681 682 /** 683 * Stops dozing, returns to active dreaming. 684 * <p> 685 * This method reverses the effect of {@link #startDozing}. From this moment onward, 686 * the application processor will be kept awake as long as the dream is running 687 * or until the dream starts dozing again. 688 * </p> 689 * 690 * @see #startDozing 691 * @hide For use by system UI components only. 692 */ stopDozing()693 public void stopDozing() { 694 if (mDozing) { 695 mDozing = false; 696 try { 697 mSandman.stopDozing(mWindowToken); 698 } catch (RemoteException ex) { 699 // system server died 700 } 701 } 702 } 703 704 /** 705 * Returns true if the dream will allow the system to enter a low-power state while 706 * it is running without actually turning off the screen. Defaults to false, 707 * keeping the application processor awake while the dream is running. 708 * 709 * @return True if the dream is dozing. 710 * 711 * @see #setDozing(boolean) 712 * @hide For use by system UI components only. 713 */ isDozing()714 public boolean isDozing() { 715 return mDozing; 716 } 717 718 /** 719 * Gets the screen state to use while dozing. 720 * 721 * @return The screen state to use while dozing, such as {@link Display#STATE_ON}, 722 * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND}, 723 * {@link Display#STATE_ON_SUSPEND}, {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} 724 * for the default behavior. 725 * 726 * @see #setDozeScreenState 727 * @hide For use by system UI components only. 728 */ getDozeScreenState()729 public int getDozeScreenState() { 730 return mDozeScreenState; 731 } 732 733 /** 734 * Sets the screen state to use while dozing. 735 * <p> 736 * The value of this property determines the power state of the primary display 737 * once {@link #startDozing} has been called. The default value is 738 * {@link Display#STATE_UNKNOWN} which lets the system decide. 739 * The dream may set a different state before starting to doze and may 740 * perform transitions between states while dozing to conserve power and 741 * achieve various effects. 742 * </p><p> 743 * Some devices will have dedicated hardware ("Sidekick") to animate 744 * the display content while the CPU sleeps. If the dream and the hardware support 745 * this, {@link Display#STATE_ON_SUSPEND} or {@link Display#STATE_DOZE_SUSPEND} 746 * will switch control to the Sidekick. 747 * </p><p> 748 * If not using Sidekick, it is recommended that the state be set to 749 * {@link Display#STATE_DOZE_SUSPEND} once the dream has completely 750 * finished drawing and before it releases its wakelock 751 * to allow the display hardware to be fully suspended. While suspended, 752 * the display will preserve its on-screen contents. 753 * </p><p> 754 * If the doze suspend state is used, the dream must make sure to set the mode back 755 * to {@link Display#STATE_DOZE} or {@link Display#STATE_ON} before drawing again 756 * since the display updates may be ignored and not seen by the user otherwise. 757 * </p><p> 758 * The set of available display power states and their behavior while dozing is 759 * hardware dependent and may vary across devices. The dream may therefore 760 * need to be modified or configured to correctly support the hardware. 761 * </p> 762 * 763 * @param state The screen state to use while dozing, such as {@link Display#STATE_ON}, 764 * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND}, 765 * {@link Display#STATE_ON_SUSPEND}, {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} 766 * for the default behavior. 767 * 768 * @hide For use by system UI components only. 769 */ setDozeScreenState(int state)770 public void setDozeScreenState(int state) { 771 if (mDozeScreenState != state) { 772 mDozeScreenState = state; 773 updateDoze(); 774 } 775 } 776 777 /** 778 * Gets the screen brightness to use while dozing. 779 * 780 * @return The screen brightness while dozing as a value between 781 * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255), 782 * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply 783 * its default policy based on the screen state. 784 * 785 * @see #setDozeScreenBrightness 786 * @hide For use by system UI components only. 787 */ getDozeScreenBrightness()788 public int getDozeScreenBrightness() { 789 return mDozeScreenBrightness; 790 } 791 792 /** 793 * Sets the screen brightness to use while dozing. 794 * <p> 795 * The value of this property determines the power state of the primary display 796 * once {@link #startDozing} has been called. The default value is 797 * {@link PowerManager#BRIGHTNESS_DEFAULT} which lets the system decide. 798 * The dream may set a different brightness before starting to doze and may adjust 799 * the brightness while dozing to conserve power and achieve various effects. 800 * </p><p> 801 * Note that dream may specify any brightness in the full 0-255 range, including 802 * values that are less than the minimum value for manual screen brightness 803 * adjustments by the user. In particular, the value may be set to 0 which may 804 * turn off the backlight entirely while still leaving the screen on although 805 * this behavior is device dependent and not guaranteed. 806 * </p><p> 807 * The available range of display brightness values and their behavior while dozing is 808 * hardware dependent and may vary across devices. The dream may therefore 809 * need to be modified or configured to correctly support the hardware. 810 * </p> 811 * 812 * @param brightness The screen brightness while dozing as a value between 813 * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255), 814 * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply 815 * its default policy based on the screen state. 816 * 817 * @hide For use by system UI components only. 818 */ setDozeScreenBrightness(int brightness)819 public void setDozeScreenBrightness(int brightness) { 820 if (brightness != PowerManager.BRIGHTNESS_DEFAULT) { 821 brightness = clampAbsoluteBrightness(brightness); 822 } 823 if (mDozeScreenBrightness != brightness) { 824 mDozeScreenBrightness = brightness; 825 updateDoze(); 826 } 827 } 828 829 /** 830 * Called when this Dream is constructed. 831 */ 832 @Override onCreate()833 public void onCreate() { 834 if (mDebug) Slog.v(TAG, "onCreate()"); 835 super.onCreate(); 836 } 837 838 /** 839 * Called when the dream's window has been created and is visible and animation may now begin. 840 */ onDreamingStarted()841 public void onDreamingStarted() { 842 if (mDebug) Slog.v(TAG, "onDreamingStarted()"); 843 // hook for subclasses 844 } 845 846 /** 847 * Called when this Dream is stopped, either by external request or by calling finish(), 848 * before the window has been removed. 849 */ onDreamingStopped()850 public void onDreamingStopped() { 851 if (mDebug) Slog.v(TAG, "onDreamingStopped()"); 852 // hook for subclasses 853 } 854 855 /** 856 * Called when the dream is being asked to stop itself and wake. 857 * <p> 858 * The default implementation simply calls {@link #finish} which ends the dream 859 * immediately. Subclasses may override this function to perform a smooth exit 860 * transition then call {@link #finish} afterwards. 861 * </p><p> 862 * Note that the dream will only be given a short period of time (currently about 863 * five seconds) to wake up. If the dream does not finish itself in a timely manner 864 * then the system will forcibly finish it once the time allowance is up. 865 * </p> 866 */ onWakeUp()867 public void onWakeUp() { 868 finish(); 869 } 870 871 /** {@inheritDoc} */ 872 @Override onBind(Intent intent)873 public final IBinder onBind(Intent intent) { 874 if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); 875 return new DreamServiceWrapper(); 876 } 877 878 /** 879 * Stops the dream and detaches from the window. 880 * <p> 881 * When the dream ends, the system will be allowed to go to sleep fully unless there 882 * is a reason for it to be awake such as recent user activity or wake locks being held. 883 * </p> 884 */ finish()885 public final void finish() { 886 if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished); 887 888 if (!mFinished) { 889 mFinished = true; 890 891 if (mWindowToken == null) { 892 Slog.w(TAG, "Finish was called before the dream was attached."); 893 } else { 894 try { 895 mSandman.finishSelf(mWindowToken, true /*immediate*/); 896 } catch (RemoteException ex) { 897 // system server died 898 } 899 } 900 901 stopSelf(); // if launched via any other means 902 } 903 } 904 905 /** 906 * Wakes the dream up gently. 907 * <p> 908 * Calls {@link #onWakeUp} to give the dream a chance to perform an exit transition. 909 * When the transition is over, the dream should call {@link #finish}. 910 * </p> 911 */ wakeUp()912 public final void wakeUp() { 913 wakeUp(false); 914 } 915 wakeUp(boolean fromSystem)916 private void wakeUp(boolean fromSystem) { 917 if (mDebug) Slog.v(TAG, "wakeUp(): fromSystem=" + fromSystem 918 + ", mWaking=" + mWaking + ", mFinished=" + mFinished); 919 920 if (!mWaking && !mFinished) { 921 mWaking = true; 922 923 // As a minor optimization, invoke the callback first in case it simply 924 // calls finish() immediately so there wouldn't be much point in telling 925 // the system that we are finishing the dream gently. 926 onWakeUp(); 927 928 // Now tell the system we are waking gently, unless we already told 929 // it we were finishing immediately. 930 if (!fromSystem && !mFinished) { 931 if (mWindowToken == null) { 932 Slog.w(TAG, "WakeUp was called before the dream was attached."); 933 } else { 934 try { 935 mSandman.finishSelf(mWindowToken, false /*immediate*/); 936 } catch (RemoteException ex) { 937 // system server died 938 } 939 } 940 } 941 } 942 } 943 944 /** {@inheritDoc} */ 945 @Override onDestroy()946 public void onDestroy() { 947 if (mDebug) Slog.v(TAG, "onDestroy()"); 948 // hook for subclasses 949 950 // Just in case destroy came in before detach, let's take care of that now 951 detach(); 952 953 super.onDestroy(); 954 } 955 956 // end public api 957 958 /** 959 * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed. 960 * 961 * Must run on mHandler. 962 */ detach()963 private final void detach() { 964 if (mStarted) { 965 if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()"); 966 mStarted = false; 967 onDreamingStopped(); 968 } 969 970 if (mWindow != null) { 971 // force our window to be removed synchronously 972 if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); 973 mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView()); 974 mWindow = null; 975 } 976 977 if (mWindowToken != null) { 978 // the following will print a log message if it finds any other leaked windows 979 WindowManagerGlobal.getInstance().closeAll(mWindowToken, 980 this.getClass().getName(), "Dream"); 981 mWindowToken = null; 982 mCanDoze = false; 983 } 984 } 985 986 /** 987 * Called when the Dream is ready to be shown. 988 * 989 * Must run on mHandler. 990 * 991 * @param windowToken A window token that will allow a window to be created in the correct layer. 992 * @param started A callback that will be invoked once onDreamingStarted has completed. 993 */ attach(IBinder windowToken, boolean canDoze, IRemoteCallback started)994 private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) { 995 if (mWindowToken != null) { 996 Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); 997 return; 998 } 999 if (mFinished || mWaking) { 1000 Slog.w(TAG, "attach() called after dream already finished"); 1001 try { 1002 mSandman.finishSelf(windowToken, true /*immediate*/); 1003 } catch (RemoteException ex) { 1004 // system server died 1005 } 1006 return; 1007 } 1008 1009 mWindowToken = windowToken; 1010 mCanDoze = canDoze; 1011 if (mWindowless && !mCanDoze) { 1012 throw new IllegalStateException("Only doze dreams can be windowless"); 1013 } 1014 if (!mWindowless) { 1015 mWindow = new PhoneWindow(this); 1016 mWindow.setCallback(this); 1017 mWindow.requestFeature(Window.FEATURE_NO_TITLE); 1018 mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); 1019 mWindow.setFormat(PixelFormat.OPAQUE); 1020 1021 if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", 1022 windowToken, WindowManager.LayoutParams.TYPE_DREAM)); 1023 1024 WindowManager.LayoutParams lp = mWindow.getAttributes(); 1025 lp.type = WindowManager.LayoutParams.TYPE_DREAM; 1026 lp.token = windowToken; 1027 lp.windowAnimations = com.android.internal.R.style.Animation_Dream; 1028 lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1029 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 1030 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 1031 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD 1032 | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 1033 | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) 1034 | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) 1035 ); 1036 mWindow.setAttributes(lp); 1037 // Workaround: Currently low-profile and in-window system bar backgrounds don't go 1038 // along well. Dreams usually don't need such bars anyways, so disable them by default. 1039 mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 1040 mWindow.setWindowManager(null, windowToken, "dream", true); 1041 1042 applySystemUiVisibilityFlags( 1043 (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), 1044 View.SYSTEM_UI_FLAG_LOW_PROFILE); 1045 1046 try { 1047 getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); 1048 } catch (WindowManager.BadTokenException ex) { 1049 // This can happen because the dream manager service will remove the token 1050 // immediately without necessarily waiting for the dream to start. 1051 // We should receive a finish message soon. 1052 Slog.i(TAG, "attach() called after window token already removed, dream will " 1053 + "finish soon"); 1054 mWindow = null; 1055 return; 1056 } 1057 } 1058 // We need to defer calling onDreamingStarted until after onWindowAttached, 1059 // which is posted to the handler by addView, so we post onDreamingStarted 1060 // to the handler also. Need to watch out here in case detach occurs before 1061 // this callback is invoked. 1062 mHandler.post(new Runnable() { 1063 @Override 1064 public void run() { 1065 if (mWindow != null || mWindowless) { 1066 if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); 1067 mStarted = true; 1068 try { 1069 onDreamingStarted(); 1070 } finally { 1071 try { 1072 started.sendResult(null); 1073 } catch (RemoteException e) { 1074 throw e.rethrowFromSystemServer(); 1075 } 1076 } 1077 } 1078 } 1079 }); 1080 } 1081 getWindowFlagValue(int flag, boolean defaultValue)1082 private boolean getWindowFlagValue(int flag, boolean defaultValue) { 1083 return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0; 1084 } 1085 applyWindowFlags(int flags, int mask)1086 private void applyWindowFlags(int flags, int mask) { 1087 if (mWindow != null) { 1088 WindowManager.LayoutParams lp = mWindow.getAttributes(); 1089 lp.flags = applyFlags(lp.flags, flags, mask); 1090 mWindow.setAttributes(lp); 1091 mWindow.getWindowManager().updateViewLayout(mWindow.getDecorView(), lp); 1092 } 1093 } 1094 getSystemUiVisibilityFlagValue(int flag, boolean defaultValue)1095 private boolean getSystemUiVisibilityFlagValue(int flag, boolean defaultValue) { 1096 View v = mWindow == null ? null : mWindow.getDecorView(); 1097 return v == null ? defaultValue : (v.getSystemUiVisibility() & flag) != 0; 1098 } 1099 applySystemUiVisibilityFlags(int flags, int mask)1100 private void applySystemUiVisibilityFlags(int flags, int mask) { 1101 View v = mWindow == null ? null : mWindow.getDecorView(); 1102 if (v != null) { 1103 v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask)); 1104 } 1105 } 1106 applyFlags(int oldFlags, int flags, int mask)1107 private int applyFlags(int oldFlags, int flags, int mask) { 1108 return (oldFlags&~mask) | (flags&mask); 1109 } 1110 1111 @Override dump(final FileDescriptor fd, PrintWriter pw, final String[] args)1112 protected void dump(final FileDescriptor fd, PrintWriter pw, final String[] args) { 1113 DumpUtils.dumpAsync(mHandler, new Dump() { 1114 @Override 1115 public void dump(PrintWriter pw, String prefix) { 1116 dumpOnHandler(fd, pw, args); 1117 } 1118 }, pw, "", 1000); 1119 } 1120 1121 /** @hide */ dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args)1122 protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) { 1123 pw.print(TAG + ": "); 1124 if (mWindowToken == null) { 1125 pw.println("stopped"); 1126 } else { 1127 pw.println("running (token=" + mWindowToken + ")"); 1128 } 1129 pw.println(" window: " + mWindow); 1130 pw.print(" flags:"); 1131 if (isInteractive()) pw.print(" interactive"); 1132 if (isLowProfile()) pw.print(" lowprofile"); 1133 if (isFullscreen()) pw.print(" fullscreen"); 1134 if (isScreenBright()) pw.print(" bright"); 1135 if (isWindowless()) pw.print(" windowless"); 1136 if (isDozing()) pw.print(" dozing"); 1137 else if (canDoze()) pw.print(" candoze"); 1138 pw.println(); 1139 if (canDoze()) { 1140 pw.println(" doze screen state: " + Display.stateToString(mDozeScreenState)); 1141 pw.println(" doze screen brightness: " + mDozeScreenBrightness); 1142 } 1143 } 1144 clampAbsoluteBrightness(int value)1145 private static int clampAbsoluteBrightness(int value) { 1146 return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); 1147 } 1148 1149 private final class DreamServiceWrapper extends IDreamService.Stub { 1150 @Override attach(final IBinder windowToken, final boolean canDoze, IRemoteCallback started)1151 public void attach(final IBinder windowToken, final boolean canDoze, 1152 IRemoteCallback started) { 1153 mHandler.post(new Runnable() { 1154 @Override 1155 public void run() { 1156 DreamService.this.attach(windowToken, canDoze, started); 1157 } 1158 }); 1159 } 1160 1161 @Override detach()1162 public void detach() { 1163 mHandler.post(new Runnable() { 1164 @Override 1165 public void run() { 1166 DreamService.this.detach(); 1167 } 1168 }); 1169 } 1170 1171 @Override wakeUp()1172 public void wakeUp() { 1173 mHandler.post(new Runnable() { 1174 @Override 1175 public void run() { 1176 DreamService.this.wakeUp(true /*fromSystem*/); 1177 } 1178 }); 1179 } 1180 } 1181 } 1182