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