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