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