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