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