WallpaperService.java revision b047b8bd7e363081e91ba6cbc8d09cd355624584
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, int displayId) { 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