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