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