• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Service;
24 import android.content.Intent;
25 import android.graphics.PixelFormat;
26 import android.graphics.drawable.ColorDrawable;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.ServiceManager;
30 import android.util.Slog;
31 import android.view.ActionMode;
32 import android.view.KeyEvent;
33 import android.view.Menu;
34 import android.view.MenuItem;
35 import android.view.MotionEvent;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.view.Window;
39 import android.view.WindowManager;
40 import android.view.WindowManagerGlobal;
41 import android.view.WindowManager.LayoutParams;
42 import android.view.accessibility.AccessibilityEvent;
43 
44 import com.android.internal.policy.PolicyManager;
45 
46 /**
47  * Extend this class to implement a custom dream (available to the user as a "Daydream").
48  *
49  * <p>Dreams are interactive screensavers launched when a charging device is idle, or docked in a
50  * desk dock. Dreams provide another modality for apps to express themselves, tailored for
51  * an exhibition/lean-back experience.</p>
52  *
53  * <p>The {@code DreamService} lifecycle is as follows:</p>
54  * <ol>
55  *   <li>{@link #onAttachedToWindow}
56  *     <p>Use this for initial setup, such as calling {@link #setContentView setContentView()}.</li>
57  *   <li>{@link #onDreamingStarted}
58  *     <p>Your dream has started, so you should begin animations or other behaviors here.</li>
59  *   <li>{@link #onDreamingStopped}
60  *     <p>Use this to stop the things you started in {@link #onDreamingStarted}.</li>
61  *   <li>{@link #onDetachedFromWindow}
62  *     <p>Use this to dismantle resources (for example, detach from handlers
63  *        and listeners).</li>
64  * </ol>
65  *
66  * <p>In addition, onCreate and onDestroy (from the Service interface) will also be called, but
67  * initialization and teardown should be done by overriding the hooks above.</p>
68  *
69  * <p>To be available to the system, your {@code DreamService} should be declared in the
70  * manifest as follows:</p>
71  * <pre>
72  * &lt;service
73  *     android:name=".MyDream"
74  *     android:exported="true"
75  *     android:icon="@drawable/my_icon"
76  *     android:label="@string/my_dream_label" >
77  *
78  *     &lt;intent-filter>
79  *         &lt;action android:name="android.service.dreams.DreamService" />
80  *         &lt;category android:name="android.intent.category.DEFAULT" />
81  *     &lt;/intent-filter>
82  *
83  *     &lt;!-- Point to additional information for this dream (optional) -->
84  *     &lt;meta-data
85  *         android:name="android.service.dream"
86  *         android:resource="@xml/my_dream" />
87  * &lt;/service>
88  * </pre>
89  *
90  * <p>If specified with the {@code &lt;meta-data&gt;} element,
91  * additional information for the dream is defined using the
92  * {@link android.R.styleable#Dream &lt;dream&gt;} element in a separate XML file.
93  * Currently, the only addtional
94  * information you can provide is for a settings activity that allows the user to configure
95  * the dream behavior. For example:</p>
96  * <p class="code-caption">res/xml/my_dream.xml</p>
97  * <pre>
98  * &lt;dream xmlns:android="http://schemas.android.com/apk/res/android"
99  *     android:settingsActivity="com.example.app/.MyDreamSettingsActivity" />
100  * </pre>
101  * <p>This makes a Settings button available alongside your dream's listing in the
102  * system settings, which when pressed opens the specified activity.</p>
103  *
104  *
105  * <p>To specify your dream layout, call {@link #setContentView}, typically during the
106  * {@link #onAttachedToWindow} callback. For example:</p>
107  * <pre>
108  * public class MyDream extends DreamService {
109  *
110  *     &#64;Override
111  *     public void onAttachedToWindow() {
112  *         super.onAttachedToWindow();
113  *
114  *         // Exit dream upon user touch
115  *         setInteractive(false);
116  *         // Hide system UI
117  *         setFullscreen(true);
118  *         // Set the dream layout
119  *         setContentView(R.layout.dream);
120  *     }
121  * }
122  * </pre>
123  */
124 public class DreamService extends Service implements Window.Callback {
125     private final String TAG = DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]";
126 
127     /**
128      * The name of the dream manager service.
129      * @hide
130      */
131     public static final String DREAM_SERVICE = "dreams";
132 
133     /**
134      * The {@link Intent} that must be declared as handled by the service.
135      */
136     @SdkConstant(SdkConstantType.SERVICE_ACTION)
137     public static final String SERVICE_INTERFACE =
138             "android.service.dreams.DreamService";
139 
140     /**
141      * Name under which a Dream publishes information about itself.
142      * This meta-data must reference an XML resource containing
143      * a <code>&lt;{@link android.R.styleable#Dream dream}&gt;</code>
144      * tag.
145      */
146     public static final String DREAM_META_DATA = "android.service.dream";
147 
148     private final Handler mHandler = new Handler();
149     private IBinder mWindowToken;
150     private Window mWindow;
151     private WindowManager mWindowManager;
152     private IDreamManager mSandman;
153     private boolean mInteractive = false;
154     private boolean mLowProfile = true;
155     private boolean mFullscreen = false;
156     private boolean mScreenBright = true;
157     private boolean mFinished;
158 
159     private boolean mDebug = false;
160 
161     /**
162      * @hide
163      */
setDebug(boolean dbg)164     public void setDebug(boolean dbg) {
165         mDebug = dbg;
166     }
167 
168     // begin Window.Callback methods
169     /** {@inheritDoc} */
170     @Override
dispatchKeyEvent(KeyEvent event)171     public boolean dispatchKeyEvent(KeyEvent event) {
172         // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK
173         if (!mInteractive) {
174             if (mDebug) Slog.v(TAG, "Finishing on keyEvent");
175             safelyFinish();
176             return true;
177         } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
178             if (mDebug) Slog.v(TAG, "Finishing on back key");
179             safelyFinish();
180             return true;
181         }
182         return mWindow.superDispatchKeyEvent(event);
183     }
184 
185     /** {@inheritDoc} */
186     @Override
dispatchKeyShortcutEvent(KeyEvent event)187     public boolean dispatchKeyShortcutEvent(KeyEvent event) {
188         if (!mInteractive) {
189             if (mDebug) Slog.v(TAG, "Finishing on keyShortcutEvent");
190             safelyFinish();
191             return true;
192         }
193         return mWindow.superDispatchKeyShortcutEvent(event);
194     }
195 
196     /** {@inheritDoc} */
197     @Override
dispatchTouchEvent(MotionEvent event)198     public boolean dispatchTouchEvent(MotionEvent event) {
199         // TODO: create more flexible version of mInteractive that allows clicks
200         // but finish()es on any other kind of activity
201         if (!mInteractive) {
202             if (mDebug) Slog.v(TAG, "Finishing on touchEvent");
203             safelyFinish();
204             return true;
205         }
206         return mWindow.superDispatchTouchEvent(event);
207     }
208 
209     /** {@inheritDoc} */
210     @Override
dispatchTrackballEvent(MotionEvent event)211     public boolean dispatchTrackballEvent(MotionEvent event) {
212         if (!mInteractive) {
213             if (mDebug) Slog.v(TAG, "Finishing on trackballEvent");
214             safelyFinish();
215             return true;
216         }
217         return mWindow.superDispatchTrackballEvent(event);
218     }
219 
220     /** {@inheritDoc} */
221     @Override
dispatchGenericMotionEvent(MotionEvent event)222     public boolean dispatchGenericMotionEvent(MotionEvent event) {
223         if (!mInteractive) {
224             if (mDebug) Slog.v(TAG, "Finishing on genericMotionEvent");
225             safelyFinish();
226             return true;
227         }
228         return mWindow.superDispatchGenericMotionEvent(event);
229     }
230 
231     /** {@inheritDoc} */
232     @Override
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)233     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
234         return false;
235     }
236 
237     /** {@inheritDoc} */
238     @Override
onCreatePanelView(int featureId)239     public View onCreatePanelView(int featureId) {
240         return null;
241     }
242 
243     /** {@inheritDoc} */
244     @Override
onCreatePanelMenu(int featureId, Menu menu)245     public boolean onCreatePanelMenu(int featureId, Menu menu) {
246         return false;
247     }
248 
249     /** {@inheritDoc} */
250     @Override
onPreparePanel(int featureId, View view, Menu menu)251     public boolean onPreparePanel(int featureId, View view, Menu menu) {
252         return false;
253     }
254 
255     /** {@inheritDoc} */
256     @Override
onMenuOpened(int featureId, Menu menu)257     public boolean onMenuOpened(int featureId, Menu menu) {
258         return false;
259     }
260 
261     /** {@inheritDoc} */
262     @Override
onMenuItemSelected(int featureId, MenuItem item)263     public boolean onMenuItemSelected(int featureId, MenuItem item) {
264         return false;
265     }
266 
267     /** {@inheritDoc} */
268     @Override
onWindowAttributesChanged(LayoutParams attrs)269     public void onWindowAttributesChanged(LayoutParams attrs) {
270     }
271 
272     /** {@inheritDoc} */
273     @Override
onContentChanged()274     public void onContentChanged() {
275     }
276 
277     /** {@inheritDoc} */
278     @Override
onWindowFocusChanged(boolean hasFocus)279     public void onWindowFocusChanged(boolean hasFocus) {
280     }
281 
282     /** {@inheritDoc} */
283     @Override
onAttachedToWindow()284     public void onAttachedToWindow() {
285     }
286 
287     /** {@inheritDoc} */
288     @Override
onDetachedFromWindow()289     public void onDetachedFromWindow() {
290     }
291 
292     /** {@inheritDoc} */
293     @Override
onPanelClosed(int featureId, Menu menu)294     public void onPanelClosed(int featureId, Menu menu) {
295     }
296 
297     /** {@inheritDoc} */
298     @Override
onSearchRequested()299     public boolean onSearchRequested() {
300         return false;
301     }
302 
303     /** {@inheritDoc} */
304     @Override
onWindowStartingActionMode(android.view.ActionMode.Callback callback)305     public ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback callback) {
306         return null;
307     }
308 
309     /** {@inheritDoc} */
310     @Override
onActionModeStarted(ActionMode mode)311     public void onActionModeStarted(ActionMode mode) {
312     }
313 
314     /** {@inheritDoc} */
315     @Override
onActionModeFinished(ActionMode mode)316     public void onActionModeFinished(ActionMode mode) {
317     }
318     // end Window.Callback methods
319 
320     // begin public api
321     /**
322      * Retrieves the current {@link android.view.WindowManager} for the dream.
323      * Behaves similarly to {@link android.app.Activity#getWindowManager()}.
324      *
325      * @return The current window manager, or null if the dream is not started.
326      */
getWindowManager()327     public WindowManager getWindowManager() {
328         return mWindowManager;
329     }
330 
331     /**
332      * Retrieves the current {@link android.view.Window} for the dream.
333      * Behaves similarly to {@link android.app.Activity#getWindow()}.
334      *
335      * @return The current window, or null if the dream is not started.
336      */
getWindow()337     public Window getWindow() {
338         return mWindow;
339     }
340 
341    /**
342      * Inflates a layout resource and set it to be the content view for this Dream.
343      * Behaves similarly to {@link android.app.Activity#setContentView(int)}.
344      *
345      * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p>
346      *
347      * @param layoutResID Resource ID to be inflated.
348      *
349      * @see #setContentView(android.view.View)
350      * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
351      */
setContentView(int layoutResID)352     public void setContentView(int layoutResID) {
353         getWindow().setContentView(layoutResID);
354     }
355 
356     /**
357      * Sets a view to be the content view for this Dream.
358      * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)} in an activity,
359      * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view.
360      *
361      * <p>Note: This requires a window, so you should usually call it during
362      * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it
363      * during {@link #onCreate}).</p>
364      *
365      * @see #setContentView(int)
366      * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
367      */
setContentView(View view)368     public void setContentView(View view) {
369         getWindow().setContentView(view);
370     }
371 
372     /**
373      * Sets a view to be the content view for this Dream.
374      * Behaves similarly to
375      * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
376      * in an activity.
377      *
378      * <p>Note: This requires a window, so you should usually call it during
379      * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it
380      * during {@link #onCreate}).</p>
381      *
382      * @param view The desired content to display.
383      * @param params Layout parameters for the view.
384      *
385      * @see #setContentView(android.view.View)
386      * @see #setContentView(int)
387      */
setContentView(View view, ViewGroup.LayoutParams params)388     public void setContentView(View view, ViewGroup.LayoutParams params) {
389         getWindow().setContentView(view, params);
390     }
391 
392     /**
393      * Adds a view to the Dream's window, leaving other content views in place.
394      *
395      * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p>
396      *
397      * @param view The desired content to display.
398      * @param params Layout parameters for the view.
399      */
addContentView(View view, ViewGroup.LayoutParams params)400     public void addContentView(View view, ViewGroup.LayoutParams params) {
401         getWindow().addContentView(view, params);
402     }
403 
404     /**
405      * Finds a view that was identified by the id attribute from the XML that
406      * was processed in {@link #onCreate}.
407      *
408      * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p>
409      *
410      * @return The view if found or null otherwise.
411      */
findViewById(int id)412     public View findViewById(int id) {
413         return getWindow().findViewById(id);
414     }
415 
416     /**
417      * Marks this dream as interactive to receive input events.
418      *
419      * <p>Non-interactive dreams (default) will dismiss on the first input event.</p>
420      *
421      * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p>
422      *
423      * @param interactive True if this dream will handle input events.
424      */
setInteractive(boolean interactive)425     public void setInteractive(boolean interactive) {
426         mInteractive = interactive;
427     }
428 
429     /**
430      * Returns whether or not this dream is interactive.  Defaults to false.
431      *
432      * @see #setInteractive(boolean)
433      */
isInteractive()434     public boolean isInteractive() {
435         return mInteractive;
436     }
437 
438     /**
439      * Sets View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view.
440      *
441      * @param lowProfile True to set View.SYSTEM_UI_FLAG_LOW_PROFILE
442      * @hide There is no reason to have this -- dreams can set this flag
443      * on their own content view, and from there can actually do the
444      * correct interactions with it (seeing when it is cleared etc).
445      */
setLowProfile(boolean lowProfile)446     public void setLowProfile(boolean lowProfile) {
447         mLowProfile = lowProfile;
448         int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE;
449         applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag);
450     }
451 
452     /**
453      * Returns whether or not this dream is in low profile mode. Defaults to true.
454      *
455      * @see #setLowProfile(boolean)
456      * @hide
457      */
isLowProfile()458     public boolean isLowProfile() {
459         return getSystemUiVisibilityFlagValue(View.SYSTEM_UI_FLAG_LOW_PROFILE, mLowProfile);
460     }
461 
462     /**
463      * Controls {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN}
464      * on the dream's window.
465      *
466      * @param fullscreen If true, the fullscreen flag will be set; else it
467      * will be cleared.
468      */
setFullscreen(boolean fullscreen)469     public void setFullscreen(boolean fullscreen) {
470         mFullscreen = fullscreen;
471         int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
472         applyWindowFlags(mFullscreen ? flag : 0, flag);
473     }
474 
475     /**
476      * Returns whether or not this dream is in fullscreen mode. Defaults to false.
477      *
478      * @see #setFullscreen(boolean)
479      */
isFullscreen()480     public boolean isFullscreen() {
481         return mFullscreen;
482     }
483 
484     /**
485      * Marks this dream as keeping the screen bright while dreaming.
486      *
487      * @param screenBright True to keep the screen bright while dreaming.
488      */
setScreenBright(boolean screenBright)489     public void setScreenBright(boolean screenBright) {
490         mScreenBright = screenBright;
491         int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
492         applyWindowFlags(mScreenBright ? flag : 0, flag);
493     }
494 
495     /**
496      * Returns whether or not this dream keeps the screen bright while dreaming. Defaults to false,
497      * allowing the screen to dim if necessary.
498      *
499      * @see #setScreenBright(boolean)
500      */
isScreenBright()501     public boolean isScreenBright() {
502         return getWindowFlagValue(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, mScreenBright);
503     }
504 
505     /**
506      * Called when this Dream is constructed.
507      */
508     @Override
onCreate()509     public void onCreate() {
510         if (mDebug) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId());
511         super.onCreate();
512     }
513 
514     /**
515      * Called when the dream's window has been created and is visible and animation may now begin.
516      */
onDreamingStarted()517     public void onDreamingStarted() {
518         if (mDebug) Slog.v(TAG, "onDreamingStarted()");
519         // hook for subclasses
520     }
521 
522     /**
523      * Called when this Dream is stopped, either by external request or by calling finish(),
524      * before the window has been removed.
525      */
onDreamingStopped()526     public void onDreamingStopped() {
527         if (mDebug) Slog.v(TAG, "onDreamingStopped()");
528         // hook for subclasses
529     }
530 
531     /** {@inheritDoc} */
532     @Override
onBind(Intent intent)533     public final IBinder onBind(Intent intent) {
534         if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
535         return new DreamServiceWrapper();
536     }
537 
538     /**
539      * Stops the dream, detaches from the window, and wakes up.
540      */
finish()541     public final void finish() {
542         if (mDebug) Slog.v(TAG, "finish()");
543         finishInternal();
544     }
545 
546     /** {@inheritDoc} */
547     @Override
onDestroy()548     public void onDestroy() {
549         if (mDebug) Slog.v(TAG, "onDestroy()");
550         // hook for subclasses
551 
552         // Just in case destroy came in before detach, let's take care of that now
553         detach();
554 
555         super.onDestroy();
556     }
557 
558     // end public api
559 
loadSandman()560     private void loadSandman() {
561         mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
562     }
563 
564     /**
565      * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed.
566      *
567      * Must run on mHandler.
568      */
detach()569     private final void detach() {
570         if (mWindow == null) {
571             // already detached!
572             return;
573         }
574 
575         try {
576             onDreamingStopped();
577         } catch (Throwable t) {
578             Slog.w(TAG, "Crashed in onDreamingStopped()", t);
579             // we were going to stop anyway
580         }
581 
582         if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager");
583         try {
584             // force our window to be removed synchronously
585             mWindowManager.removeViewImmediate(mWindow.getDecorView());
586             // the following will print a log message if it finds any other leaked windows
587             WindowManagerGlobal.getInstance().closeAll(mWindowToken,
588                     this.getClass().getName(), "Dream");
589         } catch (Throwable t) {
590             Slog.w(TAG, "Crashed removing window view", t);
591         }
592 
593         mWindow = null;
594         mWindowToken = null;
595     }
596 
597     /**
598      * Called when the Dream is ready to be shown.
599      *
600      * Must run on mHandler.
601      *
602      * @param windowToken A window token that will allow a window to be created in the correct layer.
603      */
attach(IBinder windowToken)604     private final void attach(IBinder windowToken) {
605         if (mWindowToken != null) {
606             Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken);
607             return;
608         }
609 
610         if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId());
611 
612         if (mSandman == null) {
613             loadSandman();
614         }
615         mWindowToken = windowToken;
616         mWindow = PolicyManager.makeNewWindow(this);
617         mWindow.setCallback(this);
618         mWindow.requestFeature(Window.FEATURE_NO_TITLE);
619         mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
620         mWindow.setFormat(PixelFormat.OPAQUE);
621 
622         if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
623                 windowToken, WindowManager.LayoutParams.TYPE_DREAM));
624 
625         WindowManager.LayoutParams lp = mWindow.getAttributes();
626         lp.type = WindowManager.LayoutParams.TYPE_DREAM;
627         lp.token = windowToken;
628         lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
629         lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
630                     | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
631                     | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
632                     | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
633                     | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
634                     | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
635                     | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
636                     );
637         mWindow.setAttributes(lp);
638 
639         if (mDebug) Slog.v(TAG, "Created and attached window: " + mWindow);
640 
641         mWindow.setWindowManager(null, windowToken, "dream", true);
642         mWindowManager = mWindow.getWindowManager();
643 
644         if (mDebug) Slog.v(TAG, "Window added on thread " + Thread.currentThread().getId());
645         try {
646             applySystemUiVisibilityFlags(
647                     (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
648                     View.SYSTEM_UI_FLAG_LOW_PROFILE);
649             getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
650         } catch (Throwable t) {
651             Slog.w(TAG, "Crashed adding window view", t);
652             safelyFinish();
653             return;
654         }
655 
656         // start it up
657         mHandler.post(new Runnable() {
658             @Override
659             public void run() {
660                 try {
661                     onDreamingStarted();
662                 } catch (Throwable t) {
663                     Slog.w(TAG, "Crashed in onDreamingStarted()", t);
664                     safelyFinish();
665                 }
666             }
667         });
668     }
669 
safelyFinish()670     private void safelyFinish() {
671         if (mDebug) Slog.v(TAG, "safelyFinish()");
672         try {
673             finish();
674         } catch (Throwable t) {
675             Slog.w(TAG, "Crashed in safelyFinish()", t);
676             finishInternal();
677             return;
678         }
679 
680         if (!mFinished) {
681             Slog.w(TAG, "Bad dream, did not call super.finish()");
682             finishInternal();
683         }
684     }
685 
finishInternal()686     private void finishInternal() {
687         if (mDebug) Slog.v(TAG, "finishInternal() mFinished = " + mFinished);
688         if (mFinished) return;
689         try {
690             mFinished = true;
691 
692             if (mSandman != null) {
693                 mSandman.finishSelf(mWindowToken);
694             } else {
695                 Slog.w(TAG, "No dream manager found");
696             }
697             stopSelf(); // if launched via any other means
698 
699         } catch (Throwable t) {
700             Slog.w(TAG, "Crashed in finishInternal()", t);
701         }
702     }
703 
getWindowFlagValue(int flag, boolean defaultValue)704     private boolean getWindowFlagValue(int flag, boolean defaultValue) {
705         return mWindow == null ? defaultValue : (mWindow.getAttributes().flags & flag) != 0;
706     }
707 
applyWindowFlags(int flags, int mask)708     private void applyWindowFlags(int flags, int mask) {
709         if (mWindow != null) {
710             WindowManager.LayoutParams lp = mWindow.getAttributes();
711             lp.flags = applyFlags(lp.flags, flags, mask);
712             mWindow.setAttributes(lp);
713             mWindowManager.updateViewLayout(mWindow.getDecorView(), lp);
714         }
715     }
716 
getSystemUiVisibilityFlagValue(int flag, boolean defaultValue)717     private boolean getSystemUiVisibilityFlagValue(int flag, boolean defaultValue) {
718         View v = mWindow == null ? null : mWindow.getDecorView();
719         return v == null ? defaultValue : (v.getSystemUiVisibility() & flag) != 0;
720     }
721 
applySystemUiVisibilityFlags(int flags, int mask)722     private void applySystemUiVisibilityFlags(int flags, int mask) {
723         View v = mWindow == null ? null : mWindow.getDecorView();
724         if (v != null) {
725             v.setSystemUiVisibility(applyFlags(v.getSystemUiVisibility(), flags, mask));
726         }
727     }
728 
applyFlags(int oldFlags, int flags, int mask)729     private int applyFlags(int oldFlags, int flags, int mask) {
730         return (oldFlags&~mask) | (flags&mask);
731     }
732 
733     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)734     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
735         super.dump(fd, pw, args);
736 
737         pw.print(TAG + ": ");
738         if (mWindowToken == null) {
739             pw.println("stopped");
740         } else {
741             pw.println("running (token=" + mWindowToken + ")");
742         }
743         pw.println("  window: " + mWindow);
744         pw.print("  flags:");
745         if (isInteractive()) pw.print(" interactive");
746         if (isLowProfile()) pw.print(" lowprofile");
747         if (isFullscreen()) pw.print(" fullscreen");
748         if (isScreenBright()) pw.print(" bright");
749         pw.println();
750     }
751 
752     private class DreamServiceWrapper extends IDreamService.Stub {
attach(final IBinder windowToken)753         public void attach(final IBinder windowToken) {
754             mHandler.post(new Runnable() {
755                 @Override
756                 public void run() {
757                     DreamService.this.attach(windowToken);
758                 }
759             });
760         }
detach()761         public void detach() {
762             mHandler.post(new Runnable() {
763                 @Override
764                 public void run() {
765                     DreamService.this.detach();
766                 }
767             });
768         }
769     }
770 
771 }
772