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