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 17 package android.service.dreams; 18 19 import android.annotation.IdRes; 20 import android.annotation.LayoutRes; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SdkConstant; 24 import android.annotation.SdkConstant.SdkConstantType; 25 import android.annotation.TestApi; 26 import android.app.Activity; 27 import android.app.ActivityTaskManager; 28 import android.app.AlarmManager; 29 import android.app.Service; 30 import android.compat.annotation.UnsupportedAppUsage; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.pm.PackageManager; 35 import android.content.pm.ServiceInfo; 36 import android.content.res.Resources; 37 import android.content.res.TypedArray; 38 import android.content.res.XmlResourceParser; 39 import android.graphics.drawable.Drawable; 40 import android.os.Binder; 41 import android.os.Build; 42 import android.os.Handler; 43 import android.os.IBinder; 44 import android.os.IRemoteCallback; 45 import android.os.Looper; 46 import android.os.PowerManager; 47 import android.os.RemoteException; 48 import android.os.ServiceManager; 49 import android.util.AttributeSet; 50 import android.util.Log; 51 import android.util.MathUtils; 52 import android.util.Slog; 53 import android.util.Xml; 54 import android.view.ActionMode; 55 import android.view.Display; 56 import android.view.KeyEvent; 57 import android.view.Menu; 58 import android.view.MenuItem; 59 import android.view.MotionEvent; 60 import android.view.SearchEvent; 61 import android.view.View; 62 import android.view.ViewGroup; 63 import android.view.Window; 64 import android.view.WindowInsets; 65 import android.view.WindowManager; 66 import android.view.WindowManager.LayoutParams; 67 import android.view.accessibility.AccessibilityEvent; 68 69 import com.android.internal.R; 70 import com.android.internal.util.DumpUtils; 71 72 import org.xmlpull.v1.XmlPullParser; 73 import org.xmlpull.v1.XmlPullParserException; 74 75 import java.io.FileDescriptor; 76 import java.io.IOException; 77 import java.io.PrintWriter; 78 import java.util.function.Consumer; 79 80 /** 81 * Extend this class to implement a custom dream (available to the user as a "Daydream"). 82 * 83 * <p>Dreams are interactive screensavers launched when a charging device is idle, or docked in a 84 * desk dock. Dreams provide another modality for apps to express themselves, tailored for 85 * an exhibition/lean-back experience.</p> 86 * 87 * <p>The {@code DreamService} lifecycle is as follows:</p> 88 * <ol> 89 * <li>{@link #onAttachedToWindow} 90 * <p>Use this for initial setup, such as calling {@link #setContentView setContentView()}.</li> 91 * <li>{@link #onDreamingStarted} 92 * <p>Your dream has started, so you should begin animations or other behaviors here.</li> 93 * <li>{@link #onDreamingStopped} 94 * <p>Use this to stop the things you started in {@link #onDreamingStarted}.</li> 95 * <li>{@link #onDetachedFromWindow} 96 * <p>Use this to dismantle resources (for example, detach from handlers 97 * and listeners).</li> 98 * </ol> 99 * 100 * <p>In addition, onCreate and onDestroy (from the Service interface) will also be called, but 101 * initialization and teardown should be done by overriding the hooks above.</p> 102 * 103 * <p>To be available to the system, your {@code DreamService} should be declared in the 104 * manifest as follows:</p> 105 * <pre> 106 * <service 107 * android:name=".MyDream" 108 * android:exported="true" 109 * android:icon="@drawable/my_icon" 110 * android:label="@string/my_dream_label" > 111 * 112 * <intent-filter> 113 * <action android:name="android.service.dreams.DreamService" /> 114 * <category android:name="android.intent.category.DEFAULT" /> 115 * </intent-filter> 116 * 117 * <!-- Point to additional information for this dream (optional) --> 118 * <meta-data 119 * android:name="android.service.dream" 120 * android:resource="@xml/my_dream" /> 121 * </service> 122 * </pre> 123 * 124 * <p>If specified with the {@code <meta-data>} element, 125 * additional information for the dream is defined using the 126 * {@link android.R.styleable#Dream <dream>} element in a separate XML file. 127 * Currently, the only additional 128 * information you can provide is for a settings activity that allows the user to configure 129 * the dream behavior. For example:</p> 130 * <p class="code-caption">res/xml/my_dream.xml</p> 131 * <pre> 132 * <dream xmlns:android="http://schemas.android.com/apk/res/android" 133 * android:settingsActivity="com.example.app/.MyDreamSettingsActivity" /> 134 * </pre> 135 * <p>This makes a Settings button available alongside your dream's listing in the 136 * system settings, which when pressed opens the specified activity.</p> 137 * 138 * 139 * <p>To specify your dream layout, call {@link #setContentView}, typically during the 140 * {@link #onAttachedToWindow} callback. For example:</p> 141 * <pre> 142 * public class MyDream extends DreamService { 143 * 144 * @Override 145 * public void onAttachedToWindow() { 146 * super.onAttachedToWindow(); 147 * 148 * // Exit dream upon user touch 149 * setInteractive(false); 150 * // Hide system UI 151 * setFullscreen(true); 152 * // Set the dream layout 153 * setContentView(R.layout.dream); 154 * } 155 * } 156 * </pre> 157 * 158 * <p>When targeting api level 21 and above, you must declare the service in your manifest file 159 * with the {@link android.Manifest.permission#BIND_DREAM_SERVICE} permission. For example:</p> 160 * <pre> 161 * <service 162 * android:name=".MyDream" 163 * android:exported="true" 164 * android:icon="@drawable/my_icon" 165 * android:label="@string/my_dream_label" 166 * android:permission="android.permission.BIND_DREAM_SERVICE"> 167 * <intent-filter> 168 * <action android:name=”android.service.dreams.DreamService” /> 169 * <category android:name=”android.intent.category.DEFAULT” /> 170 * </intent-filter> 171 * </service> 172 * </pre> 173 */ 174 public class DreamService extends Service implements Window.Callback { 175 private static final String TAG = DreamService.class.getSimpleName(); 176 private final String mTag = TAG + "[" + getClass().getSimpleName() + "]"; 177 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 178 179 /** 180 * The name of the dream manager service. 181 * @hide 182 */ 183 public static final String DREAM_SERVICE = "dreams"; 184 185 /** 186 * The {@link Intent} that must be declared as handled by the service. 187 */ 188 @SdkConstant(SdkConstantType.SERVICE_ACTION) 189 public static final String SERVICE_INTERFACE = 190 "android.service.dreams.DreamService"; 191 192 /** 193 * The name of the extra where the dream overlay component is stored. 194 * @hide 195 */ 196 public static final String EXTRA_DREAM_OVERLAY_COMPONENT = 197 "android.service.dream.DreamService.dream_overlay_component"; 198 199 /** 200 * Name under which a Dream publishes information about itself. 201 * This meta-data must reference an XML resource containing 202 * a <code><{@link android.R.styleable#Dream dream}></code> 203 * tag. 204 */ 205 public static final String DREAM_META_DATA = "android.service.dream"; 206 207 /** 208 * Name of the root tag under which a Dream defines its metadata in an XML file. 209 */ 210 private static final String DREAM_META_DATA_ROOT_TAG = "dream"; 211 212 /** 213 * The default value for whether to show complications on the overlay. 214 * 215 * @hide 216 */ 217 public static final boolean DEFAULT_SHOW_COMPLICATIONS = false; 218 219 private final IDreamManager mDreamManager; 220 private final Handler mHandler = new Handler(Looper.getMainLooper()); 221 private IBinder mDreamToken; 222 private Window mWindow; 223 private Activity mActivity; 224 private boolean mInteractive; 225 private boolean mFullscreen; 226 private boolean mScreenBright = true; 227 private boolean mStarted; 228 private boolean mWaking; 229 private boolean mFinished; 230 private boolean mCanDoze; 231 private boolean mDozing; 232 private boolean mWindowless; 233 private int mDozeScreenState = Display.STATE_UNKNOWN; 234 private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT; 235 236 private boolean mDebug = false; 237 238 private ComponentName mDreamComponent; 239 private boolean mShouldShowComplications; 240 241 private DreamServiceWrapper mDreamServiceWrapper; 242 private Runnable mDispatchAfterOnAttachedToWindow; 243 244 private DreamOverlayConnectionHandler mOverlayConnection; 245 246 private final IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() { 247 @Override 248 public void onExitRequested() { 249 // Simply finish dream when exit is requested. 250 mHandler.post(() -> finish()); 251 } 252 253 @Override 254 public void onWakeUpComplete() { 255 // Finish the dream once overlay animations are complete. Execute on handler since 256 // this is coming in on the overlay binder. 257 mHandler.post(() -> finish()); 258 } 259 }; 260 261 DreamService()262 public DreamService() { 263 mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); 264 } 265 266 /** 267 * @hide 268 */ setDebug(boolean dbg)269 public void setDebug(boolean dbg) { 270 mDebug = dbg; 271 } 272 273 // begin Window.Callback methods 274 /** {@inheritDoc} */ 275 @Override dispatchKeyEvent(KeyEvent event)276 public boolean dispatchKeyEvent(KeyEvent event) { 277 // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK 278 if (!mInteractive) { 279 if (mDebug) Slog.v(mTag, "Waking up on keyEvent"); 280 wakeUp(); 281 return true; 282 } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 283 if (mDebug) Slog.v(mTag, "Waking up on back key"); 284 wakeUp(); 285 return true; 286 } 287 return mWindow.superDispatchKeyEvent(event); 288 } 289 290 /** {@inheritDoc} */ 291 @Override dispatchKeyShortcutEvent(KeyEvent event)292 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 293 if (!mInteractive) { 294 if (mDebug) Slog.v(mTag, "Waking up on keyShortcutEvent"); 295 wakeUp(); 296 return true; 297 } 298 return mWindow.superDispatchKeyShortcutEvent(event); 299 } 300 301 /** {@inheritDoc} */ 302 @Override dispatchTouchEvent(MotionEvent event)303 public boolean dispatchTouchEvent(MotionEvent event) { 304 // TODO: create more flexible version of mInteractive that allows clicks 305 // but finish()es on any other kind of activity 306 if (!mInteractive && event.getActionMasked() == MotionEvent.ACTION_UP) { 307 if (mDebug) Slog.v(mTag, "Waking up on touchEvent"); 308 wakeUp(); 309 return true; 310 } 311 return mWindow.superDispatchTouchEvent(event); 312 } 313 314 /** {@inheritDoc} */ 315 @Override dispatchTrackballEvent(MotionEvent event)316 public boolean dispatchTrackballEvent(MotionEvent event) { 317 if (!mInteractive) { 318 if (mDebug) Slog.v(mTag, "Waking up on trackballEvent"); 319 wakeUp(); 320 return true; 321 } 322 return mWindow.superDispatchTrackballEvent(event); 323 } 324 325 /** {@inheritDoc} */ 326 @Override dispatchGenericMotionEvent(MotionEvent event)327 public boolean dispatchGenericMotionEvent(MotionEvent event) { 328 if (!mInteractive) { 329 if (mDebug) Slog.v(mTag, "Waking up on genericMotionEvent"); 330 wakeUp(); 331 return true; 332 } 333 return mWindow.superDispatchGenericMotionEvent(event); 334 } 335 336 /** {@inheritDoc} */ 337 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)338 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 339 return false; 340 } 341 342 /** {@inheritDoc} */ 343 @Override onCreatePanelView(int featureId)344 public View onCreatePanelView(int featureId) { 345 return null; 346 } 347 348 /** {@inheritDoc} */ 349 @Override onCreatePanelMenu(int featureId, Menu menu)350 public boolean onCreatePanelMenu(int featureId, Menu menu) { 351 return false; 352 } 353 354 /** {@inheritDoc} */ 355 @Override onPreparePanel(int featureId, View view, Menu menu)356 public boolean onPreparePanel(int featureId, View view, Menu menu) { 357 return false; 358 } 359 360 /** {@inheritDoc} */ 361 @Override onMenuOpened(int featureId, Menu menu)362 public boolean onMenuOpened(int featureId, Menu menu) { 363 return false; 364 } 365 366 /** {@inheritDoc} */ 367 @Override onMenuItemSelected(int featureId, MenuItem item)368 public boolean onMenuItemSelected(int featureId, MenuItem item) { 369 return false; 370 } 371 372 /** {@inheritDoc} */ 373 @Override onWindowAttributesChanged(LayoutParams attrs)374 public void onWindowAttributesChanged(LayoutParams attrs) { 375 } 376 377 /** {@inheritDoc} */ 378 @Override onContentChanged()379 public void onContentChanged() { 380 } 381 382 /** {@inheritDoc} */ 383 @Override onWindowFocusChanged(boolean hasFocus)384 public void onWindowFocusChanged(boolean hasFocus) { 385 } 386 387 /** {@inheritDoc} */ 388 @Override onAttachedToWindow()389 public void onAttachedToWindow() { 390 } 391 392 /** {@inheritDoc} */ 393 @Override onDetachedFromWindow()394 public void onDetachedFromWindow() { 395 } 396 397 /** {@inheritDoc} */ 398 @Override onPanelClosed(int featureId, Menu menu)399 public void onPanelClosed(int featureId, Menu menu) { 400 } 401 402 /** {@inheritDoc} */ 403 @Override onSearchRequested(SearchEvent event)404 public boolean onSearchRequested(SearchEvent event) { 405 return onSearchRequested(); 406 } 407 408 /** {@inheritDoc} */ 409 @Override onSearchRequested()410 public boolean onSearchRequested() { 411 return false; 412 } 413 414 /** {@inheritDoc} */ 415 @Override onWindowStartingActionMode(android.view.ActionMode.Callback callback)416 public ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback callback) { 417 return null; 418 } 419 420 /** {@inheritDoc} */ 421 @Override onWindowStartingActionMode( android.view.ActionMode.Callback callback, int type)422 public ActionMode onWindowStartingActionMode( 423 android.view.ActionMode.Callback callback, int type) { 424 return null; 425 } 426 427 /** {@inheritDoc} */ 428 @Override onActionModeStarted(ActionMode mode)429 public void onActionModeStarted(ActionMode mode) { 430 } 431 432 /** {@inheritDoc} */ 433 @Override onActionModeFinished(ActionMode mode)434 public void onActionModeFinished(ActionMode mode) { 435 } 436 // end Window.Callback methods 437 438 // begin public api 439 /** 440 * Retrieves the current {@link android.view.WindowManager} for the dream. 441 * Behaves similarly to {@link android.app.Activity#getWindowManager()}. 442 * 443 * @return The current window manager, or null if the dream is not started. 444 */ getWindowManager()445 public WindowManager getWindowManager() { 446 return mWindow != null ? mWindow.getWindowManager() : null; 447 } 448 449 /** 450 * Retrieves the current {@link android.view.Window} for the dream. 451 * Behaves similarly to {@link android.app.Activity#getWindow()}. 452 * 453 * @return The current window, or null if the dream is not started. 454 */ getWindow()455 public Window getWindow() { 456 return mWindow; 457 } 458 459 /** 460 * Inflates a layout resource and set it to be the content view for this Dream. 461 * Behaves similarly to {@link android.app.Activity#setContentView(int)}. 462 * 463 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 464 * 465 * @param layoutResID Resource ID to be inflated. 466 * 467 * @see #setContentView(android.view.View) 468 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 469 */ setContentView(@ayoutRes int layoutResID)470 public void setContentView(@LayoutRes int layoutResID) { 471 getWindow().setContentView(layoutResID); 472 } 473 474 /** 475 * Sets a view to be the content view for this Dream. 476 * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)} in an activity, 477 * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view. 478 * 479 * <p>Note: This requires a window, so you should usually call it during 480 * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it 481 * during {@link #onCreate}).</p> 482 * 483 * @see #setContentView(int) 484 * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) 485 */ setContentView(View view)486 public void setContentView(View view) { 487 getWindow().setContentView(view); 488 } 489 490 /** 491 * Sets a view to be the content view for this Dream. 492 * Behaves similarly to 493 * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)} 494 * in an activity. 495 * 496 * <p>Note: This requires a window, so you should usually call it during 497 * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it 498 * during {@link #onCreate}).</p> 499 * 500 * @param view The desired content to display. 501 * @param params Layout parameters for the view. 502 * 503 * @see #setContentView(android.view.View) 504 * @see #setContentView(int) 505 */ setContentView(View view, ViewGroup.LayoutParams params)506 public void setContentView(View view, ViewGroup.LayoutParams params) { 507 getWindow().setContentView(view, params); 508 } 509 510 /** 511 * Adds a view to the Dream's window, leaving other content views in place. 512 * 513 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 514 * 515 * @param view The desired content to display. 516 * @param params Layout parameters for the view. 517 */ addContentView(View view, ViewGroup.LayoutParams params)518 public void addContentView(View view, ViewGroup.LayoutParams params) { 519 getWindow().addContentView(view, params); 520 } 521 522 /** 523 * Finds a view that was identified by the id attribute from the XML that 524 * was processed in {@link #onCreate}. 525 * 526 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 527 * <p> 528 * <strong>Note:</strong> In most cases -- depending on compiler support -- 529 * the resulting view is automatically cast to the target class type. If 530 * the target class type is unconstrained, an explicit cast may be 531 * necessary. 532 * 533 * @param id the ID to search for 534 * @return The view if found or null otherwise. 535 * @see View#findViewById(int) 536 * @see DreamService#requireViewById(int) 537 */ 538 @Nullable findViewById(@dRes int id)539 public <T extends View> T findViewById(@IdRes int id) { 540 return getWindow().findViewById(id); 541 } 542 543 /** 544 * Finds a view that was identified by the id attribute from the XML that was processed in 545 * {@link #onCreate}, or throws an IllegalArgumentException if the ID is invalid or there is no 546 * matching view in the hierarchy. 547 * 548 * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> 549 * <p> 550 * <strong>Note:</strong> In most cases -- depending on compiler support -- 551 * the resulting view is automatically cast to the target class type. If 552 * the target class type is unconstrained, an explicit cast may be 553 * necessary. 554 * 555 * @param id the ID to search for 556 * @return a view with given ID 557 * @see View#requireViewById(int) 558 * @see DreamService#findViewById(int) 559 */ 560 @NonNull requireViewById(@dRes int id)561 public final <T extends View> T requireViewById(@IdRes int id) { 562 T view = findViewById(id); 563 if (view == null) { 564 throw new IllegalArgumentException( 565 "ID does not reference a View inside this DreamService"); 566 } 567 return view; 568 } 569 570 /** 571 * Marks this dream as interactive to receive input events. 572 * 573 * <p>Non-interactive dreams (default) will dismiss on the first input event.</p> 574 * 575 * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p> 576 * 577 * @param interactive True if this dream will handle input events. 578 */ setInteractive(boolean interactive)579 public void setInteractive(boolean interactive) { 580 mInteractive = interactive; 581 } 582 583 /** 584 * Returns whether this dream is interactive. Defaults to false. 585 * 586 * @see #setInteractive(boolean) 587 */ isInteractive()588 public boolean isInteractive() { 589 return mInteractive; 590 } 591 592 /** 593 * Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} 594 * on the dream's window. 595 * 596 * @param fullscreen If true, the fullscreen flag will be set; else it 597 * will be cleared. 598 */ setFullscreen(boolean fullscreen)599 public void setFullscreen(boolean fullscreen) { 600 if (mFullscreen != fullscreen) { 601 mFullscreen = fullscreen; 602 int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; 603 applyWindowFlags(mFullscreen ? flag : 0, flag); 604 } 605 } 606 607 /** 608 * Returns whether this dream is in fullscreen mode. Defaults to false. 609 * 610 * @see #setFullscreen(boolean) 611 */ isFullscreen()612 public boolean isFullscreen() { 613 return mFullscreen; 614 } 615 616 /** 617 * Marks this dream as keeping the screen bright while dreaming. 618 * 619 * @param screenBright True to keep the screen bright while dreaming. 620 */ setScreenBright(boolean screenBright)621 public void setScreenBright(boolean screenBright) { 622 if (mScreenBright != screenBright) { 623 mScreenBright = screenBright; 624 int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; 625 applyWindowFlags(mScreenBright ? flag : 0, flag); 626 } 627 } 628 629 /** 630 * Returns whether this dream keeps the screen bright while dreaming. 631 * Defaults to false, allowing the screen to dim if necessary. 632 * 633 * @see #setScreenBright(boolean) 634 */ isScreenBright()635 public boolean isScreenBright() { 636 return getWindowFlagValue(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, mScreenBright); 637 } 638 639 /** 640 * Marks this dream as windowless. Only available to doze dreams. 641 * 642 * @hide 643 * 644 */ setWindowless(boolean windowless)645 public void setWindowless(boolean windowless) { 646 mWindowless = windowless; 647 } 648 649 /** 650 * Returns whether this dream is windowless. Only available to doze dreams. 651 * 652 * @hide 653 */ isWindowless()654 public boolean isWindowless() { 655 return mWindowless; 656 } 657 658 /** 659 * Returns true if this dream is allowed to doze. 660 * <p> 661 * The value returned by this method is only meaningful when the dream has started. 662 * </p> 663 * 664 * @return True if this dream can doze. 665 * @see #startDozing 666 * @hide For use by system UI components only. 667 */ 668 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) canDoze()669 public boolean canDoze() { 670 return mCanDoze; 671 } 672 673 /** 674 * Starts dozing, entering a deep dreamy sleep. 675 * <p> 676 * Dozing enables the system to conserve power while the user is not actively interacting 677 * with the device. While dozing, the display will remain on in a low-power state 678 * and will continue to show its previous contents but the application processor and 679 * other system components will be allowed to suspend when possible. 680 * </p><p> 681 * While the application processor is suspended, the dream may stop executing code 682 * for long periods of time. Prior to being suspended, the dream may schedule periodic 683 * wake-ups to render new content by scheduling an alarm with the {@link AlarmManager}. 684 * The dream may also keep the CPU awake by acquiring a 685 * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} when necessary. 686 * Note that since the purpose of doze mode is to conserve power (especially when 687 * running on battery), the dream should not wake the CPU very often or keep it 688 * awake for very long. 689 * </p><p> 690 * It is a good idea to call this method some time after the dream's entry animation 691 * has completed and the dream is ready to doze. It is important to completely 692 * finish all of the work needed before dozing since the application processor may 693 * be suspended at any moment once this method is called unless other wake locks 694 * are being held. 695 * </p><p> 696 * Call {@link #stopDozing} or {@link #finish} to stop dozing. 697 * </p> 698 * 699 * @see #stopDozing 700 * @hide For use by system UI components only. 701 */ 702 @UnsupportedAppUsage startDozing()703 public void startDozing() { 704 if (mCanDoze && !mDozing) { 705 mDozing = true; 706 updateDoze(); 707 } 708 } 709 updateDoze()710 private void updateDoze() { 711 if (mDreamToken == null) { 712 Slog.w(mTag, "Updating doze without a dream token."); 713 return; 714 } 715 716 if (mDozing) { 717 try { 718 mDreamManager.startDozing(mDreamToken, mDozeScreenState, mDozeScreenBrightness); 719 } catch (RemoteException ex) { 720 // system server died 721 } 722 } 723 } 724 725 /** 726 * Stops dozing, returns to active dreaming. 727 * <p> 728 * This method reverses the effect of {@link #startDozing}. From this moment onward, 729 * the application processor will be kept awake as long as the dream is running 730 * or until the dream starts dozing again. 731 * </p> 732 * 733 * @see #startDozing 734 * @hide For use by system UI components only. 735 */ 736 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) stopDozing()737 public void stopDozing() { 738 if (mDozing) { 739 mDozing = false; 740 try { 741 mDreamManager.stopDozing(mDreamToken); 742 } catch (RemoteException ex) { 743 // system server died 744 } 745 } 746 } 747 748 /** 749 * Returns true if the dream will allow the system to enter a low-power state while 750 * it is running without actually turning off the screen. Defaults to false, 751 * keeping the application processor awake while the dream is running. 752 * 753 * @return True if the dream is dozing. 754 * 755 * @hide For use by system UI components only. 756 */ 757 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) isDozing()758 public boolean isDozing() { 759 return mDozing; 760 } 761 762 /** 763 * Gets the screen state to use while dozing. 764 * 765 * @return The screen state to use while dozing, such as {@link Display#STATE_ON}, 766 * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND}, 767 * {@link Display#STATE_ON_SUSPEND}, {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} 768 * for the default behavior. 769 * 770 * @see #setDozeScreenState 771 * @hide For use by system UI components only. 772 */ getDozeScreenState()773 public int getDozeScreenState() { 774 return mDozeScreenState; 775 } 776 777 /** 778 * Sets the screen state to use while dozing. 779 * <p> 780 * The value of this property determines the power state of the primary display 781 * once {@link #startDozing} has been called. The default value is 782 * {@link Display#STATE_UNKNOWN} which lets the system decide. 783 * The dream may set a different state before starting to doze and may 784 * perform transitions between states while dozing to conserve power and 785 * achieve various effects. 786 * </p><p> 787 * Some devices will have dedicated hardware ("Sidekick") to animate 788 * the display content while the CPU sleeps. If the dream and the hardware support 789 * this, {@link Display#STATE_ON_SUSPEND} or {@link Display#STATE_DOZE_SUSPEND} 790 * will switch control to the Sidekick. 791 * </p><p> 792 * If not using Sidekick, it is recommended that the state be set to 793 * {@link Display#STATE_DOZE_SUSPEND} once the dream has completely 794 * finished drawing and before it releases its wakelock 795 * to allow the display hardware to be fully suspended. While suspended, 796 * the display will preserve its on-screen contents. 797 * </p><p> 798 * If the doze suspend state is used, the dream must make sure to set the mode back 799 * to {@link Display#STATE_DOZE} or {@link Display#STATE_ON} before drawing again 800 * since the display updates may be ignored and not seen by the user otherwise. 801 * </p><p> 802 * The set of available display power states and their behavior while dozing is 803 * hardware dependent and may vary across devices. The dream may therefore 804 * need to be modified or configured to correctly support the hardware. 805 * </p> 806 * 807 * @param state The screen state to use while dozing, such as {@link Display#STATE_ON}, 808 * {@link Display#STATE_DOZE}, {@link Display#STATE_DOZE_SUSPEND}, 809 * {@link Display#STATE_ON_SUSPEND}, {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN} 810 * for the default behavior. 811 * 812 * @hide For use by system UI components only. 813 */ 814 @UnsupportedAppUsage setDozeScreenState(int state)815 public void setDozeScreenState(int state) { 816 if (mDozeScreenState != state) { 817 mDozeScreenState = state; 818 updateDoze(); 819 } 820 } 821 822 /** 823 * Gets the screen brightness to use while dozing. 824 * 825 * @return The screen brightness while dozing as a value between 826 * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255), 827 * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply 828 * its default policy based on the screen state. 829 * 830 * @see #setDozeScreenBrightness 831 * @hide For use by system UI components only. 832 */ 833 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getDozeScreenBrightness()834 public int getDozeScreenBrightness() { 835 return mDozeScreenBrightness; 836 } 837 838 /** 839 * Sets the screen brightness to use while dozing. 840 * <p> 841 * The value of this property determines the power state of the primary display 842 * once {@link #startDozing} has been called. The default value is 843 * {@link PowerManager#BRIGHTNESS_DEFAULT} which lets the system decide. 844 * The dream may set a different brightness before starting to doze and may adjust 845 * the brightness while dozing to conserve power and achieve various effects. 846 * </p><p> 847 * Note that dream may specify any brightness in the full 0-255 range, including 848 * values that are less than the minimum value for manual screen brightness 849 * adjustments by the user. In particular, the value may be set to 0 which may 850 * turn off the backlight entirely while still leaving the screen on although 851 * this behavior is device dependent and not guaranteed. 852 * </p><p> 853 * The available range of display brightness values and their behavior while dozing is 854 * hardware dependent and may vary across devices. The dream may therefore 855 * need to be modified or configured to correctly support the hardware. 856 * </p> 857 * 858 * @param brightness The screen brightness while dozing as a value between 859 * {@link PowerManager#BRIGHTNESS_OFF} (0) and {@link PowerManager#BRIGHTNESS_ON} (255), 860 * or {@link PowerManager#BRIGHTNESS_DEFAULT} (-1) to ask the system to apply 861 * its default policy based on the screen state. 862 * 863 * @hide For use by system UI components only. 864 */ 865 @UnsupportedAppUsage setDozeScreenBrightness(int brightness)866 public void setDozeScreenBrightness(int brightness) { 867 if (brightness != PowerManager.BRIGHTNESS_DEFAULT) { 868 brightness = clampAbsoluteBrightness(brightness); 869 } 870 if (mDozeScreenBrightness != brightness) { 871 mDozeScreenBrightness = brightness; 872 updateDoze(); 873 } 874 } 875 876 /** 877 * Called when this Dream is constructed. 878 */ 879 @Override onCreate()880 public void onCreate() { 881 if (mDebug) Slog.v(mTag, "onCreate()"); 882 883 mDreamComponent = new ComponentName(this, getClass()); 884 mShouldShowComplications = fetchShouldShowComplications(this /*context*/, 885 fetchServiceInfo(this /*context*/, mDreamComponent)); 886 887 super.onCreate(); 888 } 889 890 /** 891 * Called when the dream's window has been created and is visible and animation may now begin. 892 */ onDreamingStarted()893 public void onDreamingStarted() { 894 if (mDebug) Slog.v(mTag, "onDreamingStarted()"); 895 // hook for subclasses 896 } 897 898 /** 899 * Called when this Dream is stopped, either by external request or by calling finish(), 900 * before the window has been removed. 901 */ onDreamingStopped()902 public void onDreamingStopped() { 903 if (mDebug) Slog.v(mTag, "onDreamingStopped()"); 904 // hook for subclasses 905 } 906 907 /** 908 * Called when the dream is being asked to stop itself and wake. 909 * <p> 910 * The default implementation simply calls {@link #finish} which ends the dream 911 * immediately. Subclasses may override this function to perform a smooth exit 912 * transition then call {@link #finish} afterwards. 913 * </p><p> 914 * Note that the dream will only be given a short period of time (currently about 915 * five seconds) to wake up. If the dream does not finish itself in a timely manner 916 * then the system will forcibly finish it once the time allowance is up. 917 * </p> 918 */ onWakeUp()919 public void onWakeUp() { 920 if (mOverlayConnection != null) { 921 mOverlayConnection.addConsumer(overlay -> { 922 try { 923 overlay.wakeUp(); 924 } catch (RemoteException e) { 925 Slog.e(TAG, "Error waking the overlay service", e); 926 finish(); 927 } 928 }); 929 } else { 930 finish(); 931 } 932 } 933 934 /** {@inheritDoc} */ 935 @Override onBind(Intent intent)936 public final IBinder onBind(Intent intent) { 937 if (mDebug) Slog.v(mTag, "onBind() intent = " + intent); 938 mDreamServiceWrapper = new DreamServiceWrapper(); 939 final ComponentName overlayComponent = intent.getParcelableExtra( 940 EXTRA_DREAM_OVERLAY_COMPONENT, ComponentName.class); 941 942 // Connect to the overlay service if present. 943 if (!mWindowless && overlayComponent != null) { 944 final Resources resources = getResources(); 945 final Intent overlayIntent = new Intent().setComponent(overlayComponent); 946 947 mOverlayConnection = new DreamOverlayConnectionHandler( 948 /* context= */ this, 949 Looper.getMainLooper(), 950 overlayIntent, 951 resources.getInteger(R.integer.config_minDreamOverlayDurationMs), 952 resources.getInteger(R.integer.config_dreamOverlayMaxReconnectAttempts), 953 resources.getInteger(R.integer.config_dreamOverlayReconnectTimeoutMs)); 954 955 if (!mOverlayConnection.bind()) { 956 // Binding failed. 957 mOverlayConnection = null; 958 } 959 } 960 961 return mDreamServiceWrapper; 962 } 963 964 @Override onUnbind(Intent intent)965 public boolean onUnbind(Intent intent) { 966 // We must unbind from any overlay connection if we are unbound before finishing. 967 if (mOverlayConnection != null) { 968 mOverlayConnection.unbind(); 969 mOverlayConnection = null; 970 } 971 972 return super.onUnbind(intent); 973 } 974 975 /** 976 * Stops the dream and detaches from the window. 977 * <p> 978 * When the dream ends, the system will be allowed to go to sleep fully unless there 979 * is a reason for it to be awake such as recent user activity or wake locks being held. 980 * </p> 981 */ finish()982 public final void finish() { 983 // If there is an active overlay connection, signal that the dream is ending before 984 // continuing. Note that the overlay cannot rely on the unbound state, since another dream 985 // might have bound to it in the meantime. 986 if (mOverlayConnection != null) { 987 mOverlayConnection.addConsumer(overlay -> { 988 try { 989 overlay.endDream(); 990 mOverlayConnection.unbind(); 991 mOverlayConnection = null; 992 finish(); 993 } catch (RemoteException e) { 994 Log.e(mTag, "could not inform overlay of dream end:" + e); 995 } 996 }); 997 return; 998 } 999 1000 if (mDebug) Slog.v(mTag, "finish(): mFinished=" + mFinished); 1001 1002 Activity activity = mActivity; 1003 if (activity != null) { 1004 if (!activity.isFinishing()) { 1005 // In case the activity is not finished yet, do it now. 1006 activity.finishAndRemoveTask(); 1007 } 1008 return; 1009 } 1010 1011 if (mFinished) { 1012 return; 1013 } 1014 mFinished = true; 1015 1016 if (mDreamToken == null) { 1017 if (mDebug) Slog.v(mTag, "finish() called when not attached."); 1018 stopSelf(); 1019 return; 1020 } 1021 1022 try { 1023 // finishSelf will unbind the dream controller from the dream service. This will 1024 // trigger DreamService.this.onDestroy and DreamService.this will die. 1025 mDreamManager.finishSelf(mDreamToken, true /*immediate*/); 1026 } catch (RemoteException ex) { 1027 // system server died 1028 } 1029 } 1030 1031 /** 1032 * Wakes the dream up gently. 1033 * <p> 1034 * Calls {@link #onWakeUp} to give the dream a chance to perform an exit transition. 1035 * When the transition is over, the dream should call {@link #finish}. 1036 * </p> 1037 */ wakeUp()1038 public final void wakeUp() { 1039 wakeUp(false); 1040 } 1041 wakeUp(boolean fromSystem)1042 private void wakeUp(boolean fromSystem) { 1043 if (mDebug) { 1044 Slog.v(mTag, "wakeUp(): fromSystem=" + fromSystem + ", mWaking=" + mWaking 1045 + ", mFinished=" + mFinished); 1046 } 1047 1048 if (!mWaking && !mFinished) { 1049 mWaking = true; 1050 1051 if (mActivity != null) { 1052 // During wake up the activity should be translucent to allow the application 1053 // underneath to start drawing. Normally, the WM animation system takes care of 1054 // this, but here we give the dream application some time to perform a custom exit 1055 // animation. If it uses a view animation, the WM doesn't know about it and can't 1056 // make the activity translucent in the normal way. Therefore, here we ensure that 1057 // the activity is translucent during wake up regardless of what animation is used 1058 // in onWakeUp(). 1059 mActivity.convertToTranslucent(null, null); 1060 } 1061 1062 // As a minor optimization, invoke the callback first in case it simply 1063 // calls finish() immediately so there wouldn't be much point in telling 1064 // the system that we are finishing the dream gently. 1065 onWakeUp(); 1066 1067 // Now tell the system we are waking gently, unless we already told 1068 // it we were finishing immediately. 1069 if (!fromSystem && !mFinished) { 1070 if (mActivity == null) { 1071 Slog.w(mTag, "WakeUp was called before the dream was attached."); 1072 } else { 1073 try { 1074 mDreamManager.finishSelf(mDreamToken, false /*immediate*/); 1075 } catch (RemoteException ex) { 1076 // system server died 1077 } 1078 } 1079 } 1080 } 1081 } 1082 1083 /** {@inheritDoc} */ 1084 @Override onDestroy()1085 public void onDestroy() { 1086 if (mDebug) Slog.v(mTag, "onDestroy()"); 1087 // hook for subclasses 1088 1089 // Just in case destroy came in before detach, let's take care of that now 1090 detach(); 1091 1092 super.onDestroy(); 1093 } 1094 1095 // end public api 1096 1097 /** 1098 * Parses and returns metadata of the dream service indicated by the service info. Returns null 1099 * if metadata cannot be found. 1100 * 1101 * Note that {@link ServiceInfo} must be fetched with {@link PackageManager#GET_META_DATA} flag. 1102 * 1103 * @hide 1104 */ 1105 @Nullable 1106 @TestApi getDreamMetadata(@onNull Context context, @Nullable ServiceInfo serviceInfo)1107 public static DreamMetadata getDreamMetadata(@NonNull Context context, 1108 @Nullable ServiceInfo serviceInfo) { 1109 if (serviceInfo == null) return null; 1110 1111 final PackageManager pm = context.getPackageManager(); 1112 1113 try (TypedArray rawMetadata = readMetadata(pm, serviceInfo)) { 1114 if (rawMetadata == null) return null; 1115 return new DreamMetadata( 1116 convertToComponentName(rawMetadata.getString( 1117 com.android.internal.R.styleable.Dream_settingsActivity), serviceInfo), 1118 rawMetadata.getDrawable( 1119 com.android.internal.R.styleable.Dream_previewImage), 1120 rawMetadata.getBoolean(R.styleable.Dream_showClockAndComplications, 1121 DEFAULT_SHOW_COMPLICATIONS)); 1122 } 1123 } 1124 1125 /** 1126 * Returns the raw XML metadata fetched from the {@link ServiceInfo}. 1127 * 1128 * Returns <code>null</code> if the {@link ServiceInfo} doesn't contain valid dream metadata. 1129 */ 1130 @Nullable readMetadata(PackageManager pm, ServiceInfo serviceInfo)1131 private static TypedArray readMetadata(PackageManager pm, ServiceInfo serviceInfo) { 1132 if (serviceInfo == null || serviceInfo.metaData == null) { 1133 return null; 1134 } 1135 1136 try (XmlResourceParser parser = 1137 serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA)) { 1138 if (parser == null) { 1139 if (DEBUG) Log.w(TAG, "No " + DreamService.DREAM_META_DATA + " metadata"); 1140 return null; 1141 } 1142 1143 final AttributeSet attrs = Xml.asAttributeSet(parser); 1144 while (true) { 1145 final int type = parser.next(); 1146 if (type == XmlPullParser.END_DOCUMENT || type == XmlPullParser.START_TAG) { 1147 break; 1148 } 1149 } 1150 1151 if (!parser.getName().equals(DREAM_META_DATA_ROOT_TAG)) { 1152 if (DEBUG) { 1153 Log.w(TAG, "Metadata does not start with " + DREAM_META_DATA_ROOT_TAG + " tag"); 1154 } 1155 return null; 1156 } 1157 1158 return pm.getResourcesForApplication(serviceInfo.applicationInfo).obtainAttributes( 1159 attrs, com.android.internal.R.styleable.Dream); 1160 } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) { 1161 if (DEBUG) Log.e(TAG, "Error parsing: " + serviceInfo.packageName, e); 1162 return null; 1163 } 1164 } 1165 1166 @Nullable convertToComponentName(@ullable String flattenedString, ServiceInfo serviceInfo)1167 private static ComponentName convertToComponentName(@Nullable String flattenedString, 1168 ServiceInfo serviceInfo) { 1169 if (flattenedString == null) { 1170 return null; 1171 } 1172 1173 if (!flattenedString.contains("/")) { 1174 return new ComponentName(serviceInfo.packageName, flattenedString); 1175 } 1176 1177 // Ensure that the component is from the same package as the dream service. If not, 1178 // treat the component as invalid and return null instead. 1179 final ComponentName cn = ComponentName.unflattenFromString(flattenedString); 1180 if (cn == null) return null; 1181 if (!cn.getPackageName().equals(serviceInfo.packageName)) { 1182 Log.w(TAG, 1183 "Inconsistent package name in component: " + cn.getPackageName() 1184 + ", should be: " + serviceInfo.packageName); 1185 return null; 1186 } 1187 return cn; 1188 } 1189 1190 /** 1191 * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed. 1192 * 1193 * Must run on mHandler. 1194 */ detach()1195 private void detach() { 1196 if (mStarted) { 1197 if (mDebug) Slog.v(mTag, "detach(): Calling onDreamingStopped()"); 1198 mStarted = false; 1199 onDreamingStopped(); 1200 } 1201 1202 if (mActivity != null && !mActivity.isFinishing()) { 1203 mActivity.finishAndRemoveTask(); 1204 } else { 1205 finish(); 1206 } 1207 1208 mDreamToken = null; 1209 mCanDoze = false; 1210 } 1211 1212 /** 1213 * Called when the Dream is ready to be shown. 1214 * 1215 * Must run on mHandler. 1216 * 1217 * @param dreamToken Token for this dream service. 1218 * @param started A callback that will be invoked once onDreamingStarted has completed. 1219 */ attach(IBinder dreamToken, boolean canDoze, boolean isPreviewMode, IRemoteCallback started)1220 private void attach(IBinder dreamToken, boolean canDoze, boolean isPreviewMode, 1221 IRemoteCallback started) { 1222 if (mDreamToken != null) { 1223 Slog.e(mTag, "attach() called when dream with token=" + mDreamToken 1224 + " already attached"); 1225 return; 1226 } 1227 if (mFinished || mWaking) { 1228 Slog.w(mTag, "attach() called after dream already finished"); 1229 try { 1230 mDreamManager.finishSelf(dreamToken, true /*immediate*/); 1231 } catch (RemoteException ex) { 1232 // system server died 1233 } 1234 return; 1235 } 1236 1237 mDreamToken = dreamToken; 1238 mCanDoze = canDoze; 1239 if (mWindowless && !mCanDoze) { 1240 throw new IllegalStateException("Only doze dreams can be windowless"); 1241 } 1242 1243 mDispatchAfterOnAttachedToWindow = () -> { 1244 if (mWindow != null || mWindowless) { 1245 mStarted = true; 1246 try { 1247 onDreamingStarted(); 1248 } finally { 1249 try { 1250 started.sendResult(null); 1251 } catch (RemoteException e) { 1252 throw e.rethrowFromSystemServer(); 1253 } 1254 } 1255 } 1256 }; 1257 1258 // We need to defer calling onDreamingStarted until after the activity is created. 1259 // If the dream is windowless, we can call it immediately. Otherwise, we wait 1260 // for the DreamActivity to report onActivityCreated via 1261 // DreamServiceWrapper.onActivityCreated. 1262 if (!mWindowless) { 1263 Intent i = new Intent(this, DreamActivity.class); 1264 i.setPackage(getApplicationContext().getPackageName()); 1265 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION); 1266 i.putExtra(DreamActivity.EXTRA_CALLBACK, new DreamActivityCallbacks(mDreamToken)); 1267 final ServiceInfo serviceInfo = fetchServiceInfo(this, 1268 new ComponentName(this, getClass())); 1269 i.putExtra(DreamActivity.EXTRA_DREAM_TITLE, 1270 fetchDreamLabel(this, serviceInfo, isPreviewMode)); 1271 1272 try { 1273 if (!ActivityTaskManager.getService().startDreamActivity(i)) { 1274 detach(); 1275 } 1276 } catch (SecurityException e) { 1277 Log.w(mTag, 1278 "Received SecurityException trying to start DreamActivity. " 1279 + "Aborting dream start."); 1280 detach(); 1281 } catch (RemoteException e) { 1282 Log.w(mTag, "Could not connect to activity task manager to start dream activity"); 1283 e.rethrowFromSystemServer(); 1284 } 1285 } else { 1286 mDispatchAfterOnAttachedToWindow.run(); 1287 } 1288 } 1289 onWindowCreated(Window w)1290 private void onWindowCreated(Window w) { 1291 mWindow = w; 1292 mWindow.setCallback(this); 1293 mWindow.requestFeature(Window.FEATURE_NO_TITLE); 1294 1295 WindowManager.LayoutParams lp = mWindow.getAttributes(); 1296 lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1297 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 1298 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 1299 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD 1300 | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 1301 | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED 1302 | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) 1303 | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) 1304 ); 1305 lp.layoutInDisplayCutoutMode = 1306 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 1307 mWindow.setAttributes(lp); 1308 // Workaround: Currently low-profile and in-window system bar backgrounds don't go 1309 // along well. Dreams usually don't need such bars anyways, so disable them by default. 1310 mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 1311 1312 // Hide all insets when the dream is showing 1313 mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars()); 1314 mWindow.setDecorFitsSystemWindows(false); 1315 1316 mWindow.getDecorView().addOnAttachStateChangeListener( 1317 new View.OnAttachStateChangeListener() { 1318 private Consumer<IDreamOverlayClient> mDreamStartOverlayConsumer; 1319 1320 @Override 1321 public void onViewAttachedToWindow(View v) { 1322 mDispatchAfterOnAttachedToWindow.run(); 1323 1324 if (mOverlayConnection != null) { 1325 // Request the DreamOverlay be told to dream with dream's window 1326 // parameters once the window has been attached. 1327 mDreamStartOverlayConsumer = overlay -> { 1328 if (mWindow == null) { 1329 Slog.d(TAG, "mWindow is null"); 1330 return; 1331 } 1332 try { 1333 overlay.startDream(mWindow.getAttributes(), mOverlayCallback, 1334 mDreamComponent.flattenToString(), 1335 mShouldShowComplications); 1336 } catch (RemoteException e) { 1337 Log.e(mTag, "could not send window attributes:" + e); 1338 } 1339 }; 1340 mOverlayConnection.addConsumer(mDreamStartOverlayConsumer); 1341 } 1342 } 1343 1344 @Override 1345 public void onViewDetachedFromWindow(View v) { 1346 if (mActivity == null || !mActivity.isChangingConfigurations()) { 1347 // Only stop the dream if the view is not detached by relaunching 1348 // activity for configuration changes. It is important to also clear 1349 // the window reference in order to fully release the DreamActivity. 1350 mWindow = null; 1351 mActivity = null; 1352 finish(); 1353 } 1354 1355 if (mOverlayConnection != null && mDreamStartOverlayConsumer != null) { 1356 mOverlayConnection.removeConsumer(mDreamStartOverlayConsumer); 1357 } 1358 } 1359 }); 1360 } 1361 getWindowFlagValue(int flag, boolean defaultValue)1362 private boolean getWindowFlagValue(int flag, boolean defaultValue) { 1363 return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0; 1364 } 1365 applyWindowFlags(int flags, int mask)1366 private void applyWindowFlags(int flags, int mask) { 1367 if (mWindow != null) { 1368 WindowManager.LayoutParams lp = mWindow.getAttributes(); 1369 lp.flags = applyFlags(lp.flags, flags, mask); 1370 mWindow.setAttributes(lp); 1371 mWindow.getWindowManager().updateViewLayout(mWindow.getDecorView(), lp); 1372 } 1373 } 1374 applyFlags(int oldFlags, int flags, int mask)1375 private int applyFlags(int oldFlags, int flags, int mask) { 1376 return (oldFlags&~mask) | (flags&mask); 1377 } 1378 1379 /** 1380 * Fetches metadata of the dream indicated by the {@link ComponentName}, and returns whether 1381 * the dream should show complications on the overlay. If not defined, returns 1382 * {@link DreamService#DEFAULT_SHOW_COMPLICATIONS}. 1383 */ fetchShouldShowComplications(Context context, @Nullable ServiceInfo serviceInfo)1384 private static boolean fetchShouldShowComplications(Context context, 1385 @Nullable ServiceInfo serviceInfo) { 1386 final DreamMetadata metadata = getDreamMetadata(context, serviceInfo); 1387 if (metadata != null) { 1388 return metadata.showComplications; 1389 } 1390 return DEFAULT_SHOW_COMPLICATIONS; 1391 } 1392 1393 @Nullable fetchDreamLabel(Context context, @Nullable ServiceInfo serviceInfo, boolean isPreviewMode)1394 private static CharSequence fetchDreamLabel(Context context, 1395 @Nullable ServiceInfo serviceInfo, 1396 boolean isPreviewMode) { 1397 if (serviceInfo == null) { 1398 return null; 1399 } 1400 final PackageManager pm = context.getPackageManager(); 1401 final CharSequence dreamLabel = serviceInfo.loadLabel(pm); 1402 if (!isPreviewMode || dreamLabel == null) { 1403 return dreamLabel; 1404 } 1405 // When in preview mode, return a special label indicating the dream is in preview. 1406 return context.getResources().getString(R.string.dream_preview_title, dreamLabel); 1407 } 1408 1409 @Nullable fetchServiceInfo(Context context, ComponentName componentName)1410 private static ServiceInfo fetchServiceInfo(Context context, ComponentName componentName) { 1411 final PackageManager pm = context.getPackageManager(); 1412 1413 try { 1414 return pm.getServiceInfo(componentName, 1415 PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA)); 1416 } catch (PackageManager.NameNotFoundException e) { 1417 if (DEBUG) Log.w(TAG, "cannot find component " + componentName.flattenToShortString()); 1418 } 1419 return null; 1420 } 1421 1422 @Override dump(final FileDescriptor fd, PrintWriter pw, final String[] args)1423 protected void dump(final FileDescriptor fd, PrintWriter pw, final String[] args) { 1424 DumpUtils.dumpAsync(mHandler, (pw1, prefix) -> dumpOnHandler(fd, pw1, args), pw, "", 1000); 1425 } 1426 1427 /** @hide */ dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args)1428 protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) { 1429 pw.print(mTag + ": "); 1430 if (mFinished) { 1431 pw.println("stopped"); 1432 } else { 1433 pw.println("running (dreamToken=" + mDreamToken + ")"); 1434 } 1435 pw.println(" window: " + mWindow); 1436 pw.print(" flags:"); 1437 if (isInteractive()) pw.print(" interactive"); 1438 if (isFullscreen()) pw.print(" fullscreen"); 1439 if (isScreenBright()) pw.print(" bright"); 1440 if (isWindowless()) pw.print(" windowless"); 1441 if (isDozing()) pw.print(" dozing"); 1442 else if (canDoze()) pw.print(" candoze"); 1443 pw.println(); 1444 if (canDoze()) { 1445 pw.println(" doze screen state: " + Display.stateToString(mDozeScreenState)); 1446 pw.println(" doze screen brightness: " + mDozeScreenBrightness); 1447 } 1448 } 1449 clampAbsoluteBrightness(int value)1450 private static int clampAbsoluteBrightness(int value) { 1451 return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); 1452 } 1453 1454 /** 1455 * The DreamServiceWrapper is used as a gateway to the system_server, where DreamController 1456 * uses it to control the DreamService. It is also used to receive callbacks from the 1457 * DreamActivity. 1458 */ 1459 final class DreamServiceWrapper extends IDreamService.Stub { 1460 @Override attach(final IBinder dreamToken, final boolean canDoze, final boolean isPreviewMode, IRemoteCallback started)1461 public void attach(final IBinder dreamToken, final boolean canDoze, 1462 final boolean isPreviewMode, IRemoteCallback started) { 1463 mHandler.post( 1464 () -> DreamService.this.attach(dreamToken, canDoze, isPreviewMode, started)); 1465 } 1466 1467 @Override detach()1468 public void detach() { 1469 mHandler.post(DreamService.this::detach); 1470 } 1471 1472 @Override wakeUp()1473 public void wakeUp() { 1474 mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/)); 1475 } 1476 } 1477 1478 /** @hide */ 1479 final class DreamActivityCallbacks extends Binder { 1480 private final IBinder mActivityDreamToken; 1481 DreamActivityCallbacks(IBinder token)1482 DreamActivityCallbacks(IBinder token) { 1483 mActivityDreamToken = token; 1484 } 1485 onActivityCreated(DreamActivity activity)1486 void onActivityCreated(DreamActivity activity) { 1487 if (mActivityDreamToken != mDreamToken || mFinished) { 1488 Slog.d(TAG, "DreamActivity was created after the dream was finished or " 1489 + "a new dream started, finishing DreamActivity"); 1490 if (!activity.isFinishing()) { 1491 activity.finishAndRemoveTask(); 1492 } 1493 return; 1494 } 1495 if (mActivity != null) { 1496 Slog.w(TAG, "A DreamActivity has already been started, " 1497 + "finishing latest DreamActivity"); 1498 if (!activity.isFinishing()) { 1499 activity.finishAndRemoveTask(); 1500 } 1501 return; 1502 } 1503 1504 mActivity = activity; 1505 onWindowCreated(activity.getWindow()); 1506 } 1507 1508 // If DreamActivity is destroyed, wake up from Dream. onActivityDestroyed()1509 void onActivityDestroyed() { 1510 mActivity = null; 1511 onDestroy(); 1512 } 1513 } 1514 1515 /** 1516 * Represents metadata defined in {@link android.R.styleable#Dream <dream>}. 1517 * 1518 * @hide 1519 */ 1520 @TestApi 1521 public static final class DreamMetadata { 1522 @Nullable 1523 public final ComponentName settingsActivity; 1524 1525 @Nullable 1526 public final Drawable previewImage; 1527 1528 @NonNull 1529 public final boolean showComplications; 1530 DreamMetadata(ComponentName settingsActivity, Drawable previewImage, boolean showComplications)1531 DreamMetadata(ComponentName settingsActivity, Drawable previewImage, 1532 boolean showComplications) { 1533 this.settingsActivity = settingsActivity; 1534 this.previewImage = previewImage; 1535 this.showComplications = showComplications; 1536 } 1537 } 1538 } 1539