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