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