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