WallpaperService.java revision 6bc701421047bf881ee16c49b242ea19ae4cd9b9
1/* 2 * Copyright (C) 2009 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 17package android.service.wallpaper; 18 19import android.content.res.TypedArray; 20import android.graphics.Canvas; 21import android.view.WindowInsets; 22 23import com.android.internal.R; 24import com.android.internal.os.HandlerCaller; 25import com.android.internal.util.ScreenShapeHelper; 26import com.android.internal.view.BaseIWindow; 27import com.android.internal.view.BaseSurfaceHolder; 28 29import android.annotation.SdkConstant; 30import android.annotation.SdkConstant.SdkConstantType; 31import android.app.Service; 32import android.app.WallpaperManager; 33import android.content.Context; 34import android.content.Intent; 35import android.content.res.Configuration; 36import android.graphics.PixelFormat; 37import android.graphics.Rect; 38import android.hardware.display.DisplayManager; 39import android.hardware.display.DisplayManager.DisplayListener; 40import android.os.Bundle; 41import android.os.IBinder; 42import android.os.Looper; 43import android.os.Message; 44import android.os.RemoteException; 45import android.util.Log; 46import android.view.Display; 47import android.view.Gravity; 48import android.view.IWindowSession; 49import android.view.InputChannel; 50import android.view.InputDevice; 51import android.view.InputEvent; 52import android.view.InputEventReceiver; 53import android.view.MotionEvent; 54import android.view.SurfaceHolder; 55import android.view.View; 56import android.view.ViewGroup; 57import android.view.WindowManager; 58import android.view.WindowManager.LayoutParams; 59import android.view.WindowManagerGlobal; 60 61import java.io.FileDescriptor; 62import java.io.PrintWriter; 63import java.util.ArrayList; 64 65/** 66 * A wallpaper service is responsible for showing a live wallpaper behind 67 * applications that would like to sit on top of it. This service object 68 * itself does very little -- its only purpose is to generate instances of 69 * {@link Engine} as needed. Implementing a wallpaper thus 70 * involves subclassing from this, subclassing an Engine implementation, 71 * and implementing {@link #onCreateEngine()} to return a new instance of 72 * your engine. 73 */ 74public abstract class WallpaperService extends Service { 75 /** 76 * The {@link Intent} that must be declared as handled by the service. 77 * To be supported, the service must also require the 78 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so 79 * that other applications can not abuse it. 80 */ 81 @SdkConstant(SdkConstantType.SERVICE_ACTION) 82 public static final String SERVICE_INTERFACE = 83 "android.service.wallpaper.WallpaperService"; 84 85 /** 86 * Name under which a WallpaperService component publishes information 87 * about itself. This meta-data must reference an XML resource containing 88 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 89 * tag. 90 */ 91 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 92 93 static final String TAG = "WallpaperService"; 94 static final boolean DEBUG = false; 95 96 private static final int DO_ATTACH = 10; 97 private static final int DO_DETACH = 20; 98 private static final int DO_SET_DESIRED_SIZE = 30; 99 private static final int DO_SET_DISPLAY_PADDING = 40; 100 101 private static final int MSG_UPDATE_SURFACE = 10000; 102 private static final int MSG_VISIBILITY_CHANGED = 10010; 103 private static final int MSG_WALLPAPER_OFFSETS = 10020; 104 private static final int MSG_WALLPAPER_COMMAND = 10025; 105 private static final int MSG_WINDOW_RESIZED = 10030; 106 private static final int MSG_WINDOW_MOVED = 10035; 107 private static final int MSG_TOUCH_EVENT = 10040; 108 109 private final ArrayList<Engine> mActiveEngines 110 = new ArrayList<Engine>(); 111 112 static final class WallpaperCommand { 113 String action; 114 int x; 115 int y; 116 int z; 117 Bundle extras; 118 boolean sync; 119 } 120 121 /** 122 * The actual implementation of a wallpaper. A wallpaper service may 123 * have multiple instances running (for example as a real wallpaper 124 * and as a preview), each of which is represented by its own Engine 125 * instance. You must implement {@link WallpaperService#onCreateEngine()} 126 * to return your concrete Engine implementation. 127 */ 128 public class Engine { 129 IWallpaperEngineWrapper mIWallpaperEngine; 130 131 // Copies from mIWallpaperEngine. 132 HandlerCaller mCaller; 133 IWallpaperConnection mConnection; 134 IBinder mWindowToken; 135 136 boolean mInitializing = true; 137 boolean mVisible; 138 boolean mReportedVisible; 139 boolean mDestroyed; 140 141 // Current window state. 142 boolean mCreated; 143 boolean mSurfaceCreated; 144 boolean mIsCreating; 145 boolean mDrawingAllowed; 146 boolean mOffsetsChanged; 147 boolean mFixedSizeAllowed; 148 int mWidth; 149 int mHeight; 150 int mFormat; 151 int mType; 152 int mCurWidth; 153 int mCurHeight; 154 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 155 int mWindowPrivateFlags = 156 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; 157 int mCurWindowFlags = mWindowFlags; 158 int mCurWindowPrivateFlags = mWindowPrivateFlags; 159 final Rect mVisibleInsets = new Rect(); 160 final Rect mWinFrame = new Rect(); 161 final Rect mOverscanInsets = new Rect(); 162 final Rect mContentInsets = new Rect(); 163 final Rect mStableInsets = new Rect(); 164 final Rect mOutsets = new Rect(); 165 final Rect mDispatchedOverscanInsets = new Rect(); 166 final Rect mDispatchedContentInsets = new Rect(); 167 final Rect mDispatchedStableInsets = new Rect(); 168 final Rect mDispatchedOutsets = new Rect(); 169 final Rect mFinalSystemInsets = new Rect(); 170 final Rect mFinalStableInsets = new Rect(); 171 final Rect mBackdropFrame = new Rect(); 172 final Configuration mConfiguration = new Configuration(); 173 174 final WindowManager.LayoutParams mLayout 175 = new WindowManager.LayoutParams(); 176 IWindowSession mSession; 177 InputChannel mInputChannel; 178 179 final Object mLock = new Object(); 180 boolean mOffsetMessageEnqueued; 181 float mPendingXOffset; 182 float mPendingYOffset; 183 float mPendingXOffsetStep; 184 float mPendingYOffsetStep; 185 boolean mPendingSync; 186 MotionEvent mPendingMove; 187 188 DisplayManager mDisplayManager; 189 Display mDisplay; 190 private int mDisplayState; 191 192 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 193 { 194 mRequestedFormat = PixelFormat.RGBX_8888; 195 } 196 197 @Override 198 public boolean onAllowLockCanvas() { 199 return mDrawingAllowed; 200 } 201 202 @Override 203 public void onRelayoutContainer() { 204 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 205 mCaller.sendMessage(msg); 206 } 207 208 @Override 209 public void onUpdateSurface() { 210 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 211 mCaller.sendMessage(msg); 212 } 213 214 public boolean isCreating() { 215 return mIsCreating; 216 } 217 218 @Override 219 public void setFixedSize(int width, int height) { 220 if (!mFixedSizeAllowed) { 221 // Regular apps can't do this. It can only work for 222 // certain designs of window animations, so you can't 223 // rely on it. 224 throw new UnsupportedOperationException( 225 "Wallpapers currently only support sizing from layout"); 226 } 227 super.setFixedSize(width, height); 228 } 229 230 public void setKeepScreenOn(boolean screenOn) { 231 throw new UnsupportedOperationException( 232 "Wallpapers do not support keep screen on"); 233 } 234 235 private void prepareToDraw() { 236 if (mDisplayState == Display.STATE_DOZE 237 || mDisplayState == Display.STATE_DOZE_SUSPEND) { 238 try { 239 mSession.pokeDrawLock(mWindow); 240 } catch (RemoteException e) { 241 // System server died, can be ignored. 242 } 243 } 244 } 245 246 @Override 247 public Canvas lockCanvas() { 248 prepareToDraw(); 249 return super.lockCanvas(); 250 } 251 252 @Override 253 public Canvas lockCanvas(Rect dirty) { 254 prepareToDraw(); 255 return super.lockCanvas(dirty); 256 } 257 258 @Override 259 public Canvas lockHardwareCanvas() { 260 prepareToDraw(); 261 return super.lockHardwareCanvas(); 262 } 263 }; 264 265 final class WallpaperInputEventReceiver extends InputEventReceiver { 266 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) { 267 super(inputChannel, looper); 268 } 269 270 @Override 271 public void onInputEvent(InputEvent event) { 272 boolean handled = false; 273 try { 274 if (event instanceof MotionEvent 275 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 276 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event); 277 dispatchPointer(dup); 278 handled = true; 279 } 280 } finally { 281 finishInputEvent(event, handled); 282 } 283 } 284 } 285 WallpaperInputEventReceiver mInputEventReceiver; 286 287 final BaseIWindow mWindow = new BaseIWindow() { 288 @Override 289 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, 290 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, 291 Configuration newConfig, Rect backDropRect, boolean forceLayout, 292 boolean alwaysConsumeNavBar) { 293 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED, 294 reportDraw ? 1 : 0, outsets); 295 mCaller.sendMessage(msg); 296 } 297 298 @Override 299 public void moved(int newX, int newY) { 300 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY); 301 mCaller.sendMessage(msg); 302 } 303 304 @Override 305 public void dispatchAppVisibility(boolean visible) { 306 // We don't do this in preview mode; we'll let the preview 307 // activity tell us when to run. 308 if (!mIWallpaperEngine.mIsPreview) { 309 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 310 visible ? 1 : 0); 311 mCaller.sendMessage(msg); 312 } 313 } 314 315 @Override 316 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 317 boolean sync) { 318 synchronized (mLock) { 319 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 320 mPendingXOffset = x; 321 mPendingYOffset = y; 322 mPendingXOffsetStep = xStep; 323 mPendingYOffsetStep = yStep; 324 if (sync) { 325 mPendingSync = true; 326 } 327 if (!mOffsetMessageEnqueued) { 328 mOffsetMessageEnqueued = true; 329 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 330 mCaller.sendMessage(msg); 331 } 332 } 333 } 334 335 @Override 336 public void dispatchWallpaperCommand(String action, int x, int y, 337 int z, Bundle extras, boolean sync) { 338 synchronized (mLock) { 339 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 340 WallpaperCommand cmd = new WallpaperCommand(); 341 cmd.action = action; 342 cmd.x = x; 343 cmd.y = y; 344 cmd.z = z; 345 cmd.extras = extras; 346 cmd.sync = sync; 347 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 348 msg.obj = cmd; 349 mCaller.sendMessage(msg); 350 } 351 } 352 }; 353 354 /** 355 * Provides access to the surface in which this wallpaper is drawn. 356 */ 357 public SurfaceHolder getSurfaceHolder() { 358 return mSurfaceHolder; 359 } 360 361 /** 362 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 363 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 364 * that the system would like this wallpaper to run in. 365 */ 366 public int getDesiredMinimumWidth() { 367 return mIWallpaperEngine.mReqWidth; 368 } 369 370 /** 371 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 372 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 373 * that the system would like this wallpaper to run in. 374 */ 375 public int getDesiredMinimumHeight() { 376 return mIWallpaperEngine.mReqHeight; 377 } 378 379 /** 380 * Return whether the wallpaper is currently visible to the user, 381 * this is the last value supplied to 382 * {@link #onVisibilityChanged(boolean)}. 383 */ 384 public boolean isVisible() { 385 return mReportedVisible; 386 } 387 388 /** 389 * Returns true if this engine is running in preview mode -- that is, 390 * it is being shown to the user before they select it as the actual 391 * wallpaper. 392 */ 393 public boolean isPreview() { 394 return mIWallpaperEngine.mIsPreview; 395 } 396 397 /** 398 * Control whether this wallpaper will receive raw touch events 399 * from the window manager as the user interacts with the window 400 * that is currently displaying the wallpaper. By default they 401 * are turned off. If enabled, the events will be received in 402 * {@link #onTouchEvent(MotionEvent)}. 403 */ 404 public void setTouchEventsEnabled(boolean enabled) { 405 mWindowFlags = enabled 406 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 407 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 408 if (mCreated) { 409 updateSurface(false, false, false); 410 } 411 } 412 413 /** 414 * Control whether this wallpaper will receive notifications when the wallpaper 415 * has been scrolled. By default, wallpapers will receive notifications, although 416 * the default static image wallpapers do not. It is a performance optimization to 417 * set this to false. 418 * 419 * @param enabled whether the wallpaper wants to receive offset notifications 420 */ 421 public void setOffsetNotificationsEnabled(boolean enabled) { 422 mWindowPrivateFlags = enabled 423 ? (mWindowPrivateFlags | 424 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) 425 : (mWindowPrivateFlags & 426 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS); 427 if (mCreated) { 428 updateSurface(false, false, false); 429 } 430 } 431 432 /** {@hide} */ 433 public void setFixedSizeAllowed(boolean allowed) { 434 mFixedSizeAllowed = allowed; 435 } 436 437 /** 438 * Called once to initialize the engine. After returning, the 439 * engine's surface will be created by the framework. 440 */ 441 public void onCreate(SurfaceHolder surfaceHolder) { 442 } 443 444 /** 445 * Called right before the engine is going away. After this the 446 * surface will be destroyed and this Engine object is no longer 447 * valid. 448 */ 449 public void onDestroy() { 450 } 451 452 /** 453 * Called to inform you of the wallpaper becoming visible or 454 * hidden. <em>It is very important that a wallpaper only use 455 * CPU while it is visible.</em>. 456 */ 457 public void onVisibilityChanged(boolean visible) { 458 } 459 460 /** 461 * Called with the current insets that are in effect for the wallpaper. 462 * This gives you the part of the overall wallpaper surface that will 463 * generally be visible to the user (ignoring position offsets applied to it). 464 * 465 * @param insets Insets to apply. 466 */ 467 public void onApplyWindowInsets(WindowInsets insets) { 468 } 469 470 /** 471 * Called as the user performs touch-screen interaction with the 472 * window that is currently showing this wallpaper. Note that the 473 * events you receive here are driven by the actual application the 474 * user is interacting with, so if it is slow you will get fewer 475 * move events. 476 */ 477 public void onTouchEvent(MotionEvent event) { 478 } 479 480 /** 481 * Called to inform you of the wallpaper's offsets changing 482 * within its contain, corresponding to the container's 483 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 484 * WallpaperManager.setWallpaperOffsets()}. 485 */ 486 public void onOffsetsChanged(float xOffset, float yOffset, 487 float xOffsetStep, float yOffsetStep, 488 int xPixelOffset, int yPixelOffset) { 489 } 490 491 /** 492 * Process a command that was sent to the wallpaper with 493 * {@link WallpaperManager#sendWallpaperCommand}. 494 * The default implementation does nothing, and always returns null 495 * as the result. 496 * 497 * @param action The name of the command to perform. This tells you 498 * what to do and how to interpret the rest of the arguments. 499 * @param x Generic integer parameter. 500 * @param y Generic integer parameter. 501 * @param z Generic integer parameter. 502 * @param extras Any additional parameters. 503 * @param resultRequested If true, the caller is requesting that 504 * a result, appropriate for the command, be returned back. 505 * @return If returning a result, create a Bundle and place the 506 * result data in to it. Otherwise return null. 507 */ 508 public Bundle onCommand(String action, int x, int y, int z, 509 Bundle extras, boolean resultRequested) { 510 return null; 511 } 512 513 /** 514 * Called when an application has changed the desired virtual size of 515 * the wallpaper. 516 */ 517 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 518 } 519 520 /** 521 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 522 * SurfaceHolder.Callback.surfaceChanged()}. 523 */ 524 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 525 } 526 527 /** 528 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded 529 * SurfaceHolder.Callback.surfaceRedrawNeeded()}. 530 */ 531 public void onSurfaceRedrawNeeded(SurfaceHolder holder) { 532 } 533 534 /** 535 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 536 * SurfaceHolder.Callback.surfaceCreated()}. 537 */ 538 public void onSurfaceCreated(SurfaceHolder holder) { 539 } 540 541 /** 542 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 543 * SurfaceHolder.Callback.surfaceDestroyed()}. 544 */ 545 public void onSurfaceDestroyed(SurfaceHolder holder) { 546 } 547 548 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 549 out.print(prefix); out.print("mInitializing="); out.print(mInitializing); 550 out.print(" mDestroyed="); out.println(mDestroyed); 551 out.print(prefix); out.print("mVisible="); out.print(mVisible); 552 out.print(" mReportedVisible="); out.println(mReportedVisible); 553 out.print(prefix); out.print("mDisplay="); out.println(mDisplay); 554 out.print(prefix); out.print("mCreated="); out.print(mCreated); 555 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); 556 out.print(" mIsCreating="); out.print(mIsCreating); 557 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); 558 out.print(prefix); out.print("mWidth="); out.print(mWidth); 559 out.print(" mCurWidth="); out.print(mCurWidth); 560 out.print(" mHeight="); out.print(mHeight); 561 out.print(" mCurHeight="); out.println(mCurHeight); 562 out.print(prefix); out.print("mType="); out.print(mType); 563 out.print(" mWindowFlags="); out.print(mWindowFlags); 564 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); 565 out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags); 566 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags); 567 out.print(prefix); out.print("mVisibleInsets="); 568 out.print(mVisibleInsets.toShortString()); 569 out.print(" mWinFrame="); out.print(mWinFrame.toShortString()); 570 out.print(" mContentInsets="); out.println(mContentInsets.toShortString()); 571 out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration); 572 out.print(prefix); out.print("mLayout="); out.println(mLayout); 573 synchronized (mLock) { 574 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); 575 out.print(" mPendingXOffset="); out.println(mPendingXOffset); 576 out.print(prefix); out.print("mPendingXOffsetStep="); 577 out.print(mPendingXOffsetStep); 578 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); 579 out.print(prefix); out.print("mOffsetMessageEnqueued="); 580 out.print(mOffsetMessageEnqueued); 581 out.print(" mPendingSync="); out.println(mPendingSync); 582 if (mPendingMove != null) { 583 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); 584 } 585 } 586 } 587 588 private void dispatchPointer(MotionEvent event) { 589 if (event.isTouchEvent()) { 590 synchronized (mLock) { 591 if (event.getAction() == MotionEvent.ACTION_MOVE) { 592 mPendingMove = event; 593 } else { 594 mPendingMove = null; 595 } 596 } 597 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 598 mCaller.sendMessage(msg); 599 } else { 600 event.recycle(); 601 } 602 } 603 604 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { 605 if (mDestroyed) { 606 Log.w(TAG, "Ignoring updateSurface: destroyed"); 607 } 608 609 boolean fixedSize = false; 610 int myWidth = mSurfaceHolder.getRequestedWidth(); 611 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 612 else fixedSize = true; 613 int myHeight = mSurfaceHolder.getRequestedHeight(); 614 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 615 else fixedSize = true; 616 617 final boolean creating = !mCreated; 618 final boolean surfaceCreating = !mSurfaceCreated; 619 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 620 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 621 boolean insetsChanged = !mCreated; 622 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 623 final boolean flagsChanged = mCurWindowFlags != mWindowFlags || 624 mCurWindowPrivateFlags != mWindowPrivateFlags; 625 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 626 || typeChanged || flagsChanged || redrawNeeded 627 || !mIWallpaperEngine.mShownReported) { 628 629 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 630 + " format=" + formatChanged + " size=" + sizeChanged); 631 632 try { 633 mWidth = myWidth; 634 mHeight = myHeight; 635 mFormat = mSurfaceHolder.getRequestedFormat(); 636 mType = mSurfaceHolder.getRequestedType(); 637 638 mLayout.x = 0; 639 mLayout.y = 0; 640 mLayout.width = myWidth; 641 mLayout.height = myHeight; 642 643 mLayout.format = mFormat; 644 645 mCurWindowFlags = mWindowFlags; 646 mLayout.flags = mWindowFlags 647 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 648 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 649 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 650 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 651 mCurWindowPrivateFlags = mWindowPrivateFlags; 652 mLayout.privateFlags = mWindowPrivateFlags; 653 654 mLayout.memoryType = mType; 655 mLayout.token = mWindowToken; 656 657 if (!mCreated) { 658 // Retrieve watch round info 659 TypedArray windowStyle = obtainStyledAttributes( 660 com.android.internal.R.styleable.Window); 661 windowStyle.recycle(); 662 663 // Add window 664 mLayout.type = mIWallpaperEngine.mWindowType; 665 mLayout.gravity = Gravity.START|Gravity.TOP; 666 mLayout.setTitle(WallpaperService.this.getClass().getName()); 667 mLayout.windowAnimations = 668 com.android.internal.R.style.Animation_Wallpaper; 669 mInputChannel = new InputChannel(); 670 if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, 671 Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets, 672 mInputChannel) < 0) { 673 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 674 return; 675 } 676 mCreated = true; 677 678 mInputEventReceiver = new WallpaperInputEventReceiver( 679 mInputChannel, Looper.myLooper()); 680 } 681 682 mSurfaceHolder.mSurfaceLock.lock(); 683 mDrawingAllowed = true; 684 685 if (!fixedSize) { 686 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 687 mLayout.surfaceInsets.left += mOutsets.left; 688 mLayout.surfaceInsets.top += mOutsets.top; 689 mLayout.surfaceInsets.right += mOutsets.right; 690 mLayout.surfaceInsets.bottom += mOutsets.bottom; 691 } else { 692 mLayout.surfaceInsets.set(0, 0, 0, 0); 693 } 694 final int relayoutResult = mSession.relayout( 695 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 696 View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, 697 mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, 698 mConfiguration, mSurfaceHolder.mSurface); 699 700 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 701 + ", frame=" + mWinFrame); 702 703 int w = mWinFrame.width(); 704 int h = mWinFrame.height(); 705 706 if (!fixedSize) { 707 final Rect padding = mIWallpaperEngine.mDisplayPadding; 708 w += padding.left + padding.right + mOutsets.left + mOutsets.right; 709 h += padding.top + padding.bottom + mOutsets.top + mOutsets.bottom; 710 mOverscanInsets.left += padding.left; 711 mOverscanInsets.top += padding.top; 712 mOverscanInsets.right += padding.right; 713 mOverscanInsets.bottom += padding.bottom; 714 mContentInsets.left += padding.left; 715 mContentInsets.top += padding.top; 716 mContentInsets.right += padding.right; 717 mContentInsets.bottom += padding.bottom; 718 mStableInsets.left += padding.left; 719 mStableInsets.top += padding.top; 720 mStableInsets.right += padding.right; 721 mStableInsets.bottom += padding.bottom; 722 } 723 724 if (mCurWidth != w) { 725 sizeChanged = true; 726 mCurWidth = w; 727 } 728 if (mCurHeight != h) { 729 sizeChanged = true; 730 mCurHeight = h; 731 } 732 733 if (DEBUG) { 734 Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight); 735 } 736 737 insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets); 738 insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets); 739 insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets); 740 insetsChanged |= !mDispatchedOutsets.equals(mOutsets); 741 742 mSurfaceHolder.setSurfaceFrameSize(w, h); 743 mSurfaceHolder.mSurfaceLock.unlock(); 744 745 if (!mSurfaceHolder.mSurface.isValid()) { 746 reportSurfaceDestroyed(); 747 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 748 return; 749 } 750 751 boolean didSurface = false; 752 753 try { 754 mSurfaceHolder.ungetCallbacks(); 755 756 if (surfaceCreating) { 757 mIsCreating = true; 758 didSurface = true; 759 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 760 + mSurfaceHolder + "): " + this); 761 onSurfaceCreated(mSurfaceHolder); 762 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 763 if (callbacks != null) { 764 for (SurfaceHolder.Callback c : callbacks) { 765 c.surfaceCreated(mSurfaceHolder); 766 } 767 } 768 } 769 770 redrawNeeded |= creating || (relayoutResult 771 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 772 773 if (forceReport || creating || surfaceCreating 774 || formatChanged || sizeChanged) { 775 if (DEBUG) { 776 RuntimeException e = new RuntimeException(); 777 e.fillInStackTrace(); 778 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 779 + " formatChanged=" + formatChanged 780 + " sizeChanged=" + sizeChanged, e); 781 } 782 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 783 + mSurfaceHolder + ", " + mFormat 784 + ", " + mCurWidth + ", " + mCurHeight 785 + "): " + this); 786 didSurface = true; 787 onSurfaceChanged(mSurfaceHolder, mFormat, 788 mCurWidth, mCurHeight); 789 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 790 if (callbacks != null) { 791 for (SurfaceHolder.Callback c : callbacks) { 792 c.surfaceChanged(mSurfaceHolder, mFormat, 793 mCurWidth, mCurHeight); 794 } 795 } 796 } 797 798 if (insetsChanged) { 799 mDispatchedOverscanInsets.set(mOverscanInsets); 800 mDispatchedOverscanInsets.left += mOutsets.left; 801 mDispatchedOverscanInsets.top += mOutsets.top; 802 mDispatchedOverscanInsets.right += mOutsets.right; 803 mDispatchedOverscanInsets.bottom += mOutsets.bottom; 804 mDispatchedContentInsets.set(mContentInsets); 805 mDispatchedStableInsets.set(mStableInsets); 806 mDispatchedOutsets.set(mOutsets); 807 mFinalSystemInsets.set(mDispatchedOverscanInsets); 808 mFinalStableInsets.set(mDispatchedStableInsets); 809 WindowInsets insets = new WindowInsets(mFinalSystemInsets, 810 null, mFinalStableInsets, 811 getResources().getConfiguration().isScreenRound(), false); 812 if (DEBUG) { 813 Log.v(TAG, "dispatching insets=" + insets); 814 } 815 onApplyWindowInsets(insets); 816 } 817 818 if (redrawNeeded) { 819 onSurfaceRedrawNeeded(mSurfaceHolder); 820 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 821 if (callbacks != null) { 822 for (SurfaceHolder.Callback c : callbacks) { 823 if (c instanceof SurfaceHolder.Callback2) { 824 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 825 mSurfaceHolder); 826 } 827 } 828 } 829 } 830 831 if (didSurface && !mReportedVisible) { 832 // This wallpaper is currently invisible, but its 833 // surface has changed. At this point let's tell it 834 // again that it is invisible in case the report about 835 // the surface caused it to start running. We really 836 // don't want wallpapers running when not visible. 837 if (mIsCreating) { 838 // Some wallpapers will ignore this call if they 839 // had previously been told they were invisble, 840 // so if we are creating a new surface then toggle 841 // the state to get them to notice. 842 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 843 + this); 844 onVisibilityChanged(true); 845 } 846 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 847 + this); 848 onVisibilityChanged(false); 849 } 850 851 } finally { 852 mIsCreating = false; 853 mSurfaceCreated = true; 854 if (redrawNeeded) { 855 mSession.finishDrawing(mWindow); 856 } 857 mIWallpaperEngine.reportShown(); 858 } 859 } catch (RemoteException ex) { 860 } 861 if (DEBUG) Log.v( 862 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 863 " w=" + mLayout.width + " h=" + mLayout.height); 864 } 865 } 866 867 void attach(IWallpaperEngineWrapper wrapper) { 868 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 869 if (mDestroyed) { 870 return; 871 } 872 873 mIWallpaperEngine = wrapper; 874 mCaller = wrapper.mCaller; 875 mConnection = wrapper.mConnection; 876 mWindowToken = wrapper.mWindowToken; 877 mSurfaceHolder.setSizeFromLayout(); 878 mInitializing = true; 879 mSession = WindowManagerGlobal.getWindowSession(); 880 881 mWindow.setSession(mSession); 882 883 mLayout.packageName = getPackageName(); 884 885 mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); 886 mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); 887 mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 888 mDisplayState = mDisplay.getState(); 889 890 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 891 onCreate(mSurfaceHolder); 892 893 mInitializing = false; 894 mReportedVisible = false; 895 updateSurface(false, false, false); 896 } 897 898 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 899 if (!mDestroyed) { 900 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 901 + desiredWidth + "," + desiredHeight + "): " + this); 902 mIWallpaperEngine.mReqWidth = desiredWidth; 903 mIWallpaperEngine.mReqHeight = desiredHeight; 904 onDesiredSizeChanged(desiredWidth, desiredHeight); 905 doOffsetsChanged(true); 906 } 907 } 908 909 void doDisplayPaddingChanged(Rect padding) { 910 if (!mDestroyed) { 911 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 912 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 913 mIWallpaperEngine.mDisplayPadding.set(padding); 914 updateSurface(true, false, false); 915 } 916 } 917 } 918 919 void doVisibilityChanged(boolean visible) { 920 if (!mDestroyed) { 921 mVisible = visible; 922 reportVisibility(); 923 } 924 } 925 926 void reportVisibility() { 927 if (!mDestroyed) { 928 mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState(); 929 boolean visible = mVisible && mDisplayState != Display.STATE_OFF; 930 if (mReportedVisible != visible) { 931 mReportedVisible = visible; 932 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 933 + "): " + this); 934 if (visible) { 935 // If becoming visible, in preview mode the surface 936 // may have been destroyed so now we need to make 937 // sure it is re-created. 938 doOffsetsChanged(false); 939 updateSurface(false, false, false); 940 } 941 onVisibilityChanged(visible); 942 } 943 } 944 } 945 946 void doOffsetsChanged(boolean always) { 947 if (mDestroyed) { 948 return; 949 } 950 951 if (!always && !mOffsetsChanged) { 952 return; 953 } 954 955 float xOffset; 956 float yOffset; 957 float xOffsetStep; 958 float yOffsetStep; 959 boolean sync; 960 synchronized (mLock) { 961 xOffset = mPendingXOffset; 962 yOffset = mPendingYOffset; 963 xOffsetStep = mPendingXOffsetStep; 964 yOffsetStep = mPendingYOffsetStep; 965 sync = mPendingSync; 966 mPendingSync = false; 967 mOffsetMessageEnqueued = false; 968 } 969 970 if (mSurfaceCreated) { 971 if (mReportedVisible) { 972 if (DEBUG) Log.v(TAG, "Offsets change in " + this 973 + ": " + xOffset + "," + yOffset); 974 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 975 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 976 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 977 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 978 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 979 } else { 980 mOffsetsChanged = true; 981 } 982 } 983 984 if (sync) { 985 try { 986 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 987 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 988 } catch (RemoteException e) { 989 } 990 } 991 } 992 993 void doCommand(WallpaperCommand cmd) { 994 Bundle result; 995 if (!mDestroyed) { 996 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 997 cmd.extras, cmd.sync); 998 } else { 999 result = null; 1000 } 1001 if (cmd.sync) { 1002 try { 1003 if (DEBUG) Log.v(TAG, "Reporting command complete"); 1004 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 1005 } catch (RemoteException e) { 1006 } 1007 } 1008 } 1009 1010 void reportSurfaceDestroyed() { 1011 if (mSurfaceCreated) { 1012 mSurfaceCreated = false; 1013 mSurfaceHolder.ungetCallbacks(); 1014 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1015 if (callbacks != null) { 1016 for (SurfaceHolder.Callback c : callbacks) { 1017 c.surfaceDestroyed(mSurfaceHolder); 1018 } 1019 } 1020 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 1021 + mSurfaceHolder + "): " + this); 1022 onSurfaceDestroyed(mSurfaceHolder); 1023 } 1024 } 1025 1026 void detach() { 1027 if (mDestroyed) { 1028 return; 1029 } 1030 1031 mDestroyed = true; 1032 1033 if (mDisplayManager != null) { 1034 mDisplayManager.unregisterDisplayListener(mDisplayListener); 1035 } 1036 1037 if (mVisible) { 1038 mVisible = false; 1039 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 1040 onVisibilityChanged(false); 1041 } 1042 1043 reportSurfaceDestroyed(); 1044 1045 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 1046 onDestroy(); 1047 1048 if (mCreated) { 1049 try { 1050 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 1051 + mSurfaceHolder.getSurface() + " of: " + this); 1052 1053 if (mInputEventReceiver != null) { 1054 mInputEventReceiver.dispose(); 1055 mInputEventReceiver = null; 1056 } 1057 1058 mSession.remove(mWindow); 1059 } catch (RemoteException e) { 1060 } 1061 mSurfaceHolder.mSurface.release(); 1062 mCreated = false; 1063 1064 // Dispose the input channel after removing the window so the Window Manager 1065 // doesn't interpret the input channel being closed as an abnormal termination. 1066 if (mInputChannel != null) { 1067 mInputChannel.dispose(); 1068 mInputChannel = null; 1069 } 1070 } 1071 } 1072 1073 private final DisplayListener mDisplayListener = new DisplayListener() { 1074 @Override 1075 public void onDisplayChanged(int displayId) { 1076 if (mDisplay.getDisplayId() == displayId) { 1077 reportVisibility(); 1078 } 1079 } 1080 1081 @Override 1082 public void onDisplayRemoved(int displayId) { 1083 } 1084 1085 @Override 1086 public void onDisplayAdded(int displayId) { 1087 } 1088 }; 1089 } 1090 1091 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 1092 implements HandlerCaller.Callback { 1093 private final HandlerCaller mCaller; 1094 1095 final IWallpaperConnection mConnection; 1096 final IBinder mWindowToken; 1097 final int mWindowType; 1098 final boolean mIsPreview; 1099 boolean mShownReported; 1100 int mReqWidth; 1101 int mReqHeight; 1102 final Rect mDisplayPadding = new Rect(); 1103 1104 Engine mEngine; 1105 1106 IWallpaperEngineWrapper(WallpaperService context, 1107 IWallpaperConnection conn, IBinder windowToken, 1108 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1109 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); 1110 mConnection = conn; 1111 mWindowToken = windowToken; 1112 mWindowType = windowType; 1113 mIsPreview = isPreview; 1114 mReqWidth = reqWidth; 1115 mReqHeight = reqHeight; 1116 mDisplayPadding.set(padding); 1117 1118 Message msg = mCaller.obtainMessage(DO_ATTACH); 1119 mCaller.sendMessage(msg); 1120 } 1121 1122 public void setDesiredSize(int width, int height) { 1123 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 1124 mCaller.sendMessage(msg); 1125 } 1126 1127 public void setDisplayPadding(Rect padding) { 1128 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 1129 mCaller.sendMessage(msg); 1130 } 1131 1132 public void setVisibility(boolean visible) { 1133 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 1134 visible ? 1 : 0); 1135 mCaller.sendMessage(msg); 1136 } 1137 1138 public void dispatchPointer(MotionEvent event) { 1139 if (mEngine != null) { 1140 mEngine.dispatchPointer(event); 1141 } else { 1142 event.recycle(); 1143 } 1144 } 1145 1146 public void dispatchWallpaperCommand(String action, int x, int y, 1147 int z, Bundle extras) { 1148 if (mEngine != null) { 1149 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 1150 } 1151 } 1152 1153 public void reportShown() { 1154 if (!mShownReported) { 1155 mShownReported = true; 1156 try { 1157 mConnection.engineShown(this); 1158 } catch (RemoteException e) { 1159 Log.w(TAG, "Wallpaper host disappeared", e); 1160 return; 1161 } 1162 } 1163 } 1164 1165 public void destroy() { 1166 Message msg = mCaller.obtainMessage(DO_DETACH); 1167 mCaller.sendMessage(msg); 1168 } 1169 1170 public void executeMessage(Message message) { 1171 switch (message.what) { 1172 case DO_ATTACH: { 1173 try { 1174 mConnection.attachEngine(this); 1175 } catch (RemoteException e) { 1176 Log.w(TAG, "Wallpaper host disappeared", e); 1177 return; 1178 } 1179 Engine engine = onCreateEngine(); 1180 mEngine = engine; 1181 mActiveEngines.add(engine); 1182 engine.attach(this); 1183 return; 1184 } 1185 case DO_DETACH: { 1186 mActiveEngines.remove(mEngine); 1187 mEngine.detach(); 1188 return; 1189 } 1190 case DO_SET_DESIRED_SIZE: { 1191 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 1192 return; 1193 } 1194 case DO_SET_DISPLAY_PADDING: { 1195 mEngine.doDisplayPaddingChanged((Rect) message.obj); 1196 } 1197 case MSG_UPDATE_SURFACE: 1198 mEngine.updateSurface(true, false, false); 1199 break; 1200 case MSG_VISIBILITY_CHANGED: 1201 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 1202 + ": " + message.arg1); 1203 mEngine.doVisibilityChanged(message.arg1 != 0); 1204 break; 1205 case MSG_WALLPAPER_OFFSETS: { 1206 mEngine.doOffsetsChanged(true); 1207 } break; 1208 case MSG_WALLPAPER_COMMAND: { 1209 WallpaperCommand cmd = (WallpaperCommand)message.obj; 1210 mEngine.doCommand(cmd); 1211 } break; 1212 case MSG_WINDOW_RESIZED: { 1213 final boolean reportDraw = message.arg1 != 0; 1214 mEngine.mOutsets.set((Rect) message.obj); 1215 mEngine.updateSurface(true, false, reportDraw); 1216 mEngine.doOffsetsChanged(true); 1217 } break; 1218 case MSG_WINDOW_MOVED: { 1219 // Do nothing. What does it mean for a Wallpaper to move? 1220 } break; 1221 case MSG_TOUCH_EVENT: { 1222 boolean skip = false; 1223 MotionEvent ev = (MotionEvent)message.obj; 1224 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 1225 synchronized (mEngine.mLock) { 1226 if (mEngine.mPendingMove == ev) { 1227 mEngine.mPendingMove = null; 1228 } else { 1229 // this is not the motion event we are looking for.... 1230 skip = true; 1231 } 1232 } 1233 } 1234 if (!skip) { 1235 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 1236 mEngine.onTouchEvent(ev); 1237 } 1238 ev.recycle(); 1239 } break; 1240 default : 1241 Log.w(TAG, "Unknown message type " + message.what); 1242 } 1243 } 1244 } 1245 1246 /** 1247 * Implements the internal {@link IWallpaperService} interface to convert 1248 * incoming calls to it back to calls on an {@link WallpaperService}. 1249 */ 1250 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 1251 private final WallpaperService mTarget; 1252 1253 public IWallpaperServiceWrapper(WallpaperService context) { 1254 mTarget = context; 1255 } 1256 1257 @Override 1258 public void attach(IWallpaperConnection conn, IBinder windowToken, 1259 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1260 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 1261 windowType, isPreview, reqWidth, reqHeight, padding); 1262 } 1263 } 1264 1265 @Override 1266 public void onCreate() { 1267 super.onCreate(); 1268 } 1269 1270 @Override 1271 public void onDestroy() { 1272 super.onDestroy(); 1273 for (int i=0; i<mActiveEngines.size(); i++) { 1274 mActiveEngines.get(i).detach(); 1275 } 1276 mActiveEngines.clear(); 1277 } 1278 1279 /** 1280 * Implement to return the implementation of the internal accessibility 1281 * service interface. Subclasses should not override. 1282 */ 1283 @Override 1284 public final IBinder onBind(Intent intent) { 1285 return new IWallpaperServiceWrapper(this); 1286 } 1287 1288 /** 1289 * Must be implemented to return a new instance of the wallpaper's engine. 1290 * Note that multiple instances may be active at the same time, such as 1291 * when the wallpaper is currently set as the active wallpaper and the user 1292 * is in the wallpaper picker viewing a preview of it as well. 1293 */ 1294 public abstract Engine onCreateEngine(); 1295 1296 @Override 1297 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 1298 out.print("State of wallpaper "); out.print(this); out.println(":"); 1299 for (int i=0; i<mActiveEngines.size(); i++) { 1300 Engine engine = mActiveEngines.get(i); 1301 out.print(" Engine "); out.print(engine); out.println(":"); 1302 engine.dump(" ", fd, out, args); 1303 } 1304 } 1305} 1306