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