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