WallpaperService.java revision c40608c041b1eb8b8cb4b96347c7ca29f4f79169
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.annotation.Nullable; 20import android.app.WallpaperColors; 21import android.content.res.TypedArray; 22import android.graphics.Canvas; 23import android.util.MergedConfiguration; 24import android.view.WindowInsets; 25 26import com.android.internal.R; 27import com.android.internal.os.HandlerCaller; 28import com.android.internal.view.BaseIWindow; 29import com.android.internal.view.BaseSurfaceHolder; 30 31import android.annotation.SdkConstant; 32import android.annotation.SdkConstant.SdkConstantType; 33import android.app.Service; 34import android.app.WallpaperManager; 35import android.content.Context; 36import android.content.Intent; 37import android.graphics.PixelFormat; 38import android.graphics.Rect; 39import android.hardware.display.DisplayManager; 40import android.hardware.display.DisplayManager.DisplayListener; 41import android.os.Bundle; 42import android.os.IBinder; 43import android.os.Looper; 44import android.os.Message; 45import android.os.RemoteException; 46import android.util.Log; 47import android.view.Display; 48import android.view.Gravity; 49import android.view.IWindowSession; 50import android.view.InputChannel; 51import android.view.InputDevice; 52import android.view.InputEvent; 53import android.view.InputEventReceiver; 54import android.view.MotionEvent; 55import android.view.SurfaceHolder; 56import android.view.View; 57import android.view.ViewGroup; 58import android.view.WindowManager; 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 MergedConfiguration mMergedConfiguration = new MergedConfiguration(); 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 MergedConfiguration mergedConfiguration, 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 /** 549 * Notifies the engine that wallpaper colors changed significantly. 550 * This will trigger a {@link #onComputeWallpaperColors()} call. 551 */ 552 public void invalidateColors() { 553 } 554 555 /** 556 * Notifies the system about what colors the wallpaper is using. 557 * You might return null if no color information is available at the moment. In that case 558 * you might want to call {@link #invalidateColors()} in a near future. 559 * 560 * @return List of wallpaper colors and their weights. 561 */ 562 public @Nullable WallpaperColors onComputeWallpaperColors() { 563 return null; 564 } 565 566 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 567 out.print(prefix); out.print("mInitializing="); out.print(mInitializing); 568 out.print(" mDestroyed="); out.println(mDestroyed); 569 out.print(prefix); out.print("mVisible="); out.print(mVisible); 570 out.print(" mReportedVisible="); out.println(mReportedVisible); 571 out.print(prefix); out.print("mDisplay="); out.println(mDisplay); 572 out.print(prefix); out.print("mCreated="); out.print(mCreated); 573 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); 574 out.print(" mIsCreating="); out.print(mIsCreating); 575 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); 576 out.print(prefix); out.print("mWidth="); out.print(mWidth); 577 out.print(" mCurWidth="); out.print(mCurWidth); 578 out.print(" mHeight="); out.print(mHeight); 579 out.print(" mCurHeight="); out.println(mCurHeight); 580 out.print(prefix); out.print("mType="); out.print(mType); 581 out.print(" mWindowFlags="); out.print(mWindowFlags); 582 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); 583 out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags); 584 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags); 585 out.print(prefix); out.print("mVisibleInsets="); 586 out.print(mVisibleInsets.toShortString()); 587 out.print(" mWinFrame="); out.print(mWinFrame.toShortString()); 588 out.print(" mContentInsets="); out.println(mContentInsets.toShortString()); 589 out.print(prefix); out.print("mConfiguration="); 590 out.println(mMergedConfiguration.getMergedConfiguration()); 591 out.print(prefix); out.print("mLayout="); out.println(mLayout); 592 synchronized (mLock) { 593 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); 594 out.print(" mPendingXOffset="); out.println(mPendingXOffset); 595 out.print(prefix); out.print("mPendingXOffsetStep="); 596 out.print(mPendingXOffsetStep); 597 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); 598 out.print(prefix); out.print("mOffsetMessageEnqueued="); 599 out.print(mOffsetMessageEnqueued); 600 out.print(" mPendingSync="); out.println(mPendingSync); 601 if (mPendingMove != null) { 602 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); 603 } 604 } 605 } 606 607 private void dispatchPointer(MotionEvent event) { 608 if (event.isTouchEvent()) { 609 synchronized (mLock) { 610 if (event.getAction() == MotionEvent.ACTION_MOVE) { 611 mPendingMove = event; 612 } else { 613 mPendingMove = null; 614 } 615 } 616 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 617 mCaller.sendMessage(msg); 618 } else { 619 event.recycle(); 620 } 621 } 622 623 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { 624 if (mDestroyed) { 625 Log.w(TAG, "Ignoring updateSurface: destroyed"); 626 } 627 628 boolean fixedSize = false; 629 int myWidth = mSurfaceHolder.getRequestedWidth(); 630 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 631 else fixedSize = true; 632 int myHeight = mSurfaceHolder.getRequestedHeight(); 633 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 634 else fixedSize = true; 635 636 final boolean creating = !mCreated; 637 final boolean surfaceCreating = !mSurfaceCreated; 638 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 639 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 640 boolean insetsChanged = !mCreated; 641 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 642 final boolean flagsChanged = mCurWindowFlags != mWindowFlags || 643 mCurWindowPrivateFlags != mWindowPrivateFlags; 644 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 645 || typeChanged || flagsChanged || redrawNeeded 646 || !mIWallpaperEngine.mShownReported) { 647 648 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 649 + " format=" + formatChanged + " size=" + sizeChanged); 650 651 try { 652 mWidth = myWidth; 653 mHeight = myHeight; 654 mFormat = mSurfaceHolder.getRequestedFormat(); 655 mType = mSurfaceHolder.getRequestedType(); 656 657 mLayout.x = 0; 658 mLayout.y = 0; 659 mLayout.width = myWidth; 660 mLayout.height = myHeight; 661 662 mLayout.format = mFormat; 663 664 mCurWindowFlags = mWindowFlags; 665 mLayout.flags = mWindowFlags 666 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 667 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 668 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 669 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 670 mCurWindowPrivateFlags = mWindowPrivateFlags; 671 mLayout.privateFlags = mWindowPrivateFlags; 672 673 mLayout.memoryType = mType; 674 mLayout.token = mWindowToken; 675 676 if (!mCreated) { 677 // Retrieve watch round info 678 TypedArray windowStyle = obtainStyledAttributes( 679 com.android.internal.R.styleable.Window); 680 windowStyle.recycle(); 681 682 // Add window 683 mLayout.type = mIWallpaperEngine.mWindowType; 684 mLayout.gravity = Gravity.START|Gravity.TOP; 685 mLayout.setTitle(WallpaperService.this.getClass().getName()); 686 mLayout.windowAnimations = 687 com.android.internal.R.style.Animation_Wallpaper; 688 mInputChannel = new InputChannel(); 689 if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, 690 Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets, 691 mInputChannel) < 0) { 692 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 693 return; 694 } 695 mCreated = true; 696 697 mInputEventReceiver = new WallpaperInputEventReceiver( 698 mInputChannel, Looper.myLooper()); 699 } 700 701 mSurfaceHolder.mSurfaceLock.lock(); 702 mDrawingAllowed = true; 703 704 if (!fixedSize) { 705 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 706 mLayout.surfaceInsets.left += mOutsets.left; 707 mLayout.surfaceInsets.top += mOutsets.top; 708 mLayout.surfaceInsets.right += mOutsets.right; 709 mLayout.surfaceInsets.bottom += mOutsets.bottom; 710 } else { 711 mLayout.surfaceInsets.set(0, 0, 0, 0); 712 } 713 final int relayoutResult = mSession.relayout( 714 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 715 View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, 716 mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, 717 mMergedConfiguration, mSurfaceHolder.mSurface); 718 719 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 720 + ", frame=" + mWinFrame); 721 722 int w = mWinFrame.width(); 723 int h = mWinFrame.height(); 724 725 if (!fixedSize) { 726 final Rect padding = mIWallpaperEngine.mDisplayPadding; 727 w += padding.left + padding.right + mOutsets.left + mOutsets.right; 728 h += padding.top + padding.bottom + mOutsets.top + mOutsets.bottom; 729 mOverscanInsets.left += padding.left; 730 mOverscanInsets.top += padding.top; 731 mOverscanInsets.right += padding.right; 732 mOverscanInsets.bottom += padding.bottom; 733 mContentInsets.left += padding.left; 734 mContentInsets.top += padding.top; 735 mContentInsets.right += padding.right; 736 mContentInsets.bottom += padding.bottom; 737 mStableInsets.left += padding.left; 738 mStableInsets.top += padding.top; 739 mStableInsets.right += padding.right; 740 mStableInsets.bottom += padding.bottom; 741 } 742 743 if (mCurWidth != w) { 744 sizeChanged = true; 745 mCurWidth = w; 746 } 747 if (mCurHeight != h) { 748 sizeChanged = true; 749 mCurHeight = h; 750 } 751 752 if (DEBUG) { 753 Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight); 754 } 755 756 insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets); 757 insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets); 758 insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets); 759 insetsChanged |= !mDispatchedOutsets.equals(mOutsets); 760 761 mSurfaceHolder.setSurfaceFrameSize(w, h); 762 mSurfaceHolder.mSurfaceLock.unlock(); 763 764 if (!mSurfaceHolder.mSurface.isValid()) { 765 reportSurfaceDestroyed(); 766 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 767 return; 768 } 769 770 boolean didSurface = false; 771 772 try { 773 mSurfaceHolder.ungetCallbacks(); 774 775 if (surfaceCreating) { 776 mIsCreating = true; 777 didSurface = true; 778 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 779 + mSurfaceHolder + "): " + this); 780 onSurfaceCreated(mSurfaceHolder); 781 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 782 if (callbacks != null) { 783 for (SurfaceHolder.Callback c : callbacks) { 784 c.surfaceCreated(mSurfaceHolder); 785 } 786 } 787 } 788 789 redrawNeeded |= creating || (relayoutResult 790 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 791 792 if (forceReport || creating || surfaceCreating 793 || formatChanged || sizeChanged) { 794 if (DEBUG) { 795 RuntimeException e = new RuntimeException(); 796 e.fillInStackTrace(); 797 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 798 + " formatChanged=" + formatChanged 799 + " sizeChanged=" + sizeChanged, e); 800 } 801 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 802 + mSurfaceHolder + ", " + mFormat 803 + ", " + mCurWidth + ", " + mCurHeight 804 + "): " + this); 805 didSurface = true; 806 onSurfaceChanged(mSurfaceHolder, mFormat, 807 mCurWidth, mCurHeight); 808 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 809 if (callbacks != null) { 810 for (SurfaceHolder.Callback c : callbacks) { 811 c.surfaceChanged(mSurfaceHolder, mFormat, 812 mCurWidth, mCurHeight); 813 } 814 } 815 } 816 817 if (insetsChanged) { 818 mDispatchedOverscanInsets.set(mOverscanInsets); 819 mDispatchedOverscanInsets.left += mOutsets.left; 820 mDispatchedOverscanInsets.top += mOutsets.top; 821 mDispatchedOverscanInsets.right += mOutsets.right; 822 mDispatchedOverscanInsets.bottom += mOutsets.bottom; 823 mDispatchedContentInsets.set(mContentInsets); 824 mDispatchedStableInsets.set(mStableInsets); 825 mDispatchedOutsets.set(mOutsets); 826 mFinalSystemInsets.set(mDispatchedOverscanInsets); 827 mFinalStableInsets.set(mDispatchedStableInsets); 828 WindowInsets insets = new WindowInsets(mFinalSystemInsets, 829 null, mFinalStableInsets, 830 getResources().getConfiguration().isScreenRound(), false); 831 if (DEBUG) { 832 Log.v(TAG, "dispatching insets=" + insets); 833 } 834 onApplyWindowInsets(insets); 835 } 836 837 if (redrawNeeded) { 838 onSurfaceRedrawNeeded(mSurfaceHolder); 839 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 840 if (callbacks != null) { 841 for (SurfaceHolder.Callback c : callbacks) { 842 if (c instanceof SurfaceHolder.Callback2) { 843 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 844 mSurfaceHolder); 845 } 846 } 847 } 848 } 849 850 if (didSurface && !mReportedVisible) { 851 // This wallpaper is currently invisible, but its 852 // surface has changed. At this point let's tell it 853 // again that it is invisible in case the report about 854 // the surface caused it to start running. We really 855 // don't want wallpapers running when not visible. 856 if (mIsCreating) { 857 // Some wallpapers will ignore this call if they 858 // had previously been told they were invisble, 859 // so if we are creating a new surface then toggle 860 // the state to get them to notice. 861 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 862 + this); 863 onVisibilityChanged(true); 864 } 865 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 866 + this); 867 onVisibilityChanged(false); 868 } 869 870 } finally { 871 mIsCreating = false; 872 mSurfaceCreated = true; 873 if (redrawNeeded) { 874 mSession.finishDrawing(mWindow); 875 } 876 mIWallpaperEngine.reportShown(); 877 } 878 } catch (RemoteException ex) { 879 } 880 if (DEBUG) Log.v( 881 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 882 " w=" + mLayout.width + " h=" + mLayout.height); 883 } 884 } 885 886 void attach(IWallpaperEngineWrapper wrapper) { 887 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 888 if (mDestroyed) { 889 return; 890 } 891 892 mIWallpaperEngine = wrapper; 893 mCaller = wrapper.mCaller; 894 mConnection = wrapper.mConnection; 895 mWindowToken = wrapper.mWindowToken; 896 mSurfaceHolder.setSizeFromLayout(); 897 mInitializing = true; 898 mSession = WindowManagerGlobal.getWindowSession(); 899 900 mWindow.setSession(mSession); 901 902 mLayout.packageName = getPackageName(); 903 904 mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); 905 mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); 906 mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 907 mDisplayState = mDisplay.getState(); 908 909 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 910 onCreate(mSurfaceHolder); 911 912 mInitializing = false; 913 mReportedVisible = false; 914 updateSurface(false, false, false); 915 } 916 917 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 918 if (!mDestroyed) { 919 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 920 + desiredWidth + "," + desiredHeight + "): " + this); 921 mIWallpaperEngine.mReqWidth = desiredWidth; 922 mIWallpaperEngine.mReqHeight = desiredHeight; 923 onDesiredSizeChanged(desiredWidth, desiredHeight); 924 doOffsetsChanged(true); 925 } 926 } 927 928 void doDisplayPaddingChanged(Rect padding) { 929 if (!mDestroyed) { 930 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 931 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 932 mIWallpaperEngine.mDisplayPadding.set(padding); 933 updateSurface(true, false, false); 934 } 935 } 936 } 937 938 void doVisibilityChanged(boolean visible) { 939 if (!mDestroyed) { 940 mVisible = visible; 941 reportVisibility(); 942 } 943 } 944 945 void reportVisibility() { 946 if (!mDestroyed) { 947 mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState(); 948 boolean visible = mVisible && mDisplayState != Display.STATE_OFF; 949 if (mReportedVisible != visible) { 950 mReportedVisible = visible; 951 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 952 + "): " + this); 953 if (visible) { 954 // If becoming visible, in preview mode the surface 955 // may have been destroyed so now we need to make 956 // sure it is re-created. 957 doOffsetsChanged(false); 958 updateSurface(false, false, false); 959 } 960 onVisibilityChanged(visible); 961 } 962 } 963 } 964 965 void doOffsetsChanged(boolean always) { 966 if (mDestroyed) { 967 return; 968 } 969 970 if (!always && !mOffsetsChanged) { 971 return; 972 } 973 974 float xOffset; 975 float yOffset; 976 float xOffsetStep; 977 float yOffsetStep; 978 boolean sync; 979 synchronized (mLock) { 980 xOffset = mPendingXOffset; 981 yOffset = mPendingYOffset; 982 xOffsetStep = mPendingXOffsetStep; 983 yOffsetStep = mPendingYOffsetStep; 984 sync = mPendingSync; 985 mPendingSync = false; 986 mOffsetMessageEnqueued = false; 987 } 988 989 if (mSurfaceCreated) { 990 if (mReportedVisible) { 991 if (DEBUG) Log.v(TAG, "Offsets change in " + this 992 + ": " + xOffset + "," + yOffset); 993 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 994 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 995 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 996 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 997 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 998 } else { 999 mOffsetsChanged = true; 1000 } 1001 } 1002 1003 if (sync) { 1004 try { 1005 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 1006 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 1007 } catch (RemoteException e) { 1008 } 1009 } 1010 } 1011 1012 void doCommand(WallpaperCommand cmd) { 1013 Bundle result; 1014 if (!mDestroyed) { 1015 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 1016 cmd.extras, cmd.sync); 1017 } else { 1018 result = null; 1019 } 1020 if (cmd.sync) { 1021 try { 1022 if (DEBUG) Log.v(TAG, "Reporting command complete"); 1023 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 1024 } catch (RemoteException e) { 1025 } 1026 } 1027 } 1028 1029 void reportSurfaceDestroyed() { 1030 if (mSurfaceCreated) { 1031 mSurfaceCreated = false; 1032 mSurfaceHolder.ungetCallbacks(); 1033 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1034 if (callbacks != null) { 1035 for (SurfaceHolder.Callback c : callbacks) { 1036 c.surfaceDestroyed(mSurfaceHolder); 1037 } 1038 } 1039 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 1040 + mSurfaceHolder + "): " + this); 1041 onSurfaceDestroyed(mSurfaceHolder); 1042 } 1043 } 1044 1045 void detach() { 1046 if (mDestroyed) { 1047 return; 1048 } 1049 1050 mDestroyed = true; 1051 1052 if (mDisplayManager != null) { 1053 mDisplayManager.unregisterDisplayListener(mDisplayListener); 1054 } 1055 1056 if (mVisible) { 1057 mVisible = false; 1058 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 1059 onVisibilityChanged(false); 1060 } 1061 1062 reportSurfaceDestroyed(); 1063 1064 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 1065 onDestroy(); 1066 1067 if (mCreated) { 1068 try { 1069 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 1070 + mSurfaceHolder.getSurface() + " of: " + this); 1071 1072 if (mInputEventReceiver != null) { 1073 mInputEventReceiver.dispose(); 1074 mInputEventReceiver = null; 1075 } 1076 1077 mSession.remove(mWindow); 1078 } catch (RemoteException e) { 1079 } 1080 mSurfaceHolder.mSurface.release(); 1081 mCreated = false; 1082 1083 // Dispose the input channel after removing the window so the Window Manager 1084 // doesn't interpret the input channel being closed as an abnormal termination. 1085 if (mInputChannel != null) { 1086 mInputChannel.dispose(); 1087 mInputChannel = null; 1088 } 1089 } 1090 } 1091 1092 private final DisplayListener mDisplayListener = new DisplayListener() { 1093 @Override 1094 public void onDisplayChanged(int displayId) { 1095 if (mDisplay.getDisplayId() == displayId) { 1096 reportVisibility(); 1097 } 1098 } 1099 1100 @Override 1101 public void onDisplayRemoved(int displayId) { 1102 } 1103 1104 @Override 1105 public void onDisplayAdded(int displayId) { 1106 } 1107 }; 1108 } 1109 1110 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 1111 implements HandlerCaller.Callback { 1112 private final HandlerCaller mCaller; 1113 1114 final IWallpaperConnection mConnection; 1115 final IBinder mWindowToken; 1116 final int mWindowType; 1117 final boolean mIsPreview; 1118 boolean mShownReported; 1119 int mReqWidth; 1120 int mReqHeight; 1121 final Rect mDisplayPadding = new Rect(); 1122 1123 Engine mEngine; 1124 1125 IWallpaperEngineWrapper(WallpaperService context, 1126 IWallpaperConnection conn, IBinder windowToken, 1127 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1128 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); 1129 mConnection = conn; 1130 mWindowToken = windowToken; 1131 mWindowType = windowType; 1132 mIsPreview = isPreview; 1133 mReqWidth = reqWidth; 1134 mReqHeight = reqHeight; 1135 mDisplayPadding.set(padding); 1136 1137 Message msg = mCaller.obtainMessage(DO_ATTACH); 1138 mCaller.sendMessage(msg); 1139 } 1140 1141 public void setDesiredSize(int width, int height) { 1142 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 1143 mCaller.sendMessage(msg); 1144 } 1145 1146 public void setDisplayPadding(Rect padding) { 1147 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 1148 mCaller.sendMessage(msg); 1149 } 1150 1151 public void setVisibility(boolean visible) { 1152 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 1153 visible ? 1 : 0); 1154 mCaller.sendMessage(msg); 1155 } 1156 1157 public void dispatchPointer(MotionEvent event) { 1158 if (mEngine != null) { 1159 mEngine.dispatchPointer(event); 1160 } else { 1161 event.recycle(); 1162 } 1163 } 1164 1165 public void dispatchWallpaperCommand(String action, int x, int y, 1166 int z, Bundle extras) { 1167 if (mEngine != null) { 1168 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 1169 } 1170 } 1171 1172 public void reportShown() { 1173 if (!mShownReported) { 1174 mShownReported = true; 1175 try { 1176 mConnection.engineShown(this); 1177 } catch (RemoteException e) { 1178 Log.w(TAG, "Wallpaper host disappeared", e); 1179 return; 1180 } 1181 } 1182 } 1183 1184 public void destroy() { 1185 Message msg = mCaller.obtainMessage(DO_DETACH); 1186 mCaller.sendMessage(msg); 1187 } 1188 1189 public void executeMessage(Message message) { 1190 switch (message.what) { 1191 case DO_ATTACH: { 1192 try { 1193 mConnection.attachEngine(this); 1194 } catch (RemoteException e) { 1195 Log.w(TAG, "Wallpaper host disappeared", e); 1196 return; 1197 } 1198 Engine engine = onCreateEngine(); 1199 mEngine = engine; 1200 mActiveEngines.add(engine); 1201 engine.attach(this); 1202 return; 1203 } 1204 case DO_DETACH: { 1205 mActiveEngines.remove(mEngine); 1206 mEngine.detach(); 1207 return; 1208 } 1209 case DO_SET_DESIRED_SIZE: { 1210 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 1211 return; 1212 } 1213 case DO_SET_DISPLAY_PADDING: { 1214 mEngine.doDisplayPaddingChanged((Rect) message.obj); 1215 } 1216 case MSG_UPDATE_SURFACE: 1217 mEngine.updateSurface(true, false, false); 1218 break; 1219 case MSG_VISIBILITY_CHANGED: 1220 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 1221 + ": " + message.arg1); 1222 mEngine.doVisibilityChanged(message.arg1 != 0); 1223 break; 1224 case MSG_WALLPAPER_OFFSETS: { 1225 mEngine.doOffsetsChanged(true); 1226 } break; 1227 case MSG_WALLPAPER_COMMAND: { 1228 WallpaperCommand cmd = (WallpaperCommand)message.obj; 1229 mEngine.doCommand(cmd); 1230 } break; 1231 case MSG_WINDOW_RESIZED: { 1232 final boolean reportDraw = message.arg1 != 0; 1233 mEngine.mOutsets.set((Rect) message.obj); 1234 mEngine.updateSurface(true, false, reportDraw); 1235 mEngine.doOffsetsChanged(true); 1236 } break; 1237 case MSG_WINDOW_MOVED: { 1238 // Do nothing. What does it mean for a Wallpaper to move? 1239 } break; 1240 case MSG_TOUCH_EVENT: { 1241 boolean skip = false; 1242 MotionEvent ev = (MotionEvent)message.obj; 1243 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 1244 synchronized (mEngine.mLock) { 1245 if (mEngine.mPendingMove == ev) { 1246 mEngine.mPendingMove = null; 1247 } else { 1248 // this is not the motion event we are looking for.... 1249 skip = true; 1250 } 1251 } 1252 } 1253 if (!skip) { 1254 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 1255 mEngine.onTouchEvent(ev); 1256 } 1257 ev.recycle(); 1258 } break; 1259 default : 1260 Log.w(TAG, "Unknown message type " + message.what); 1261 } 1262 } 1263 } 1264 1265 /** 1266 * Implements the internal {@link IWallpaperService} interface to convert 1267 * incoming calls to it back to calls on an {@link WallpaperService}. 1268 */ 1269 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 1270 private final WallpaperService mTarget; 1271 1272 public IWallpaperServiceWrapper(WallpaperService context) { 1273 mTarget = context; 1274 } 1275 1276 @Override 1277 public void attach(IWallpaperConnection conn, IBinder windowToken, 1278 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1279 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 1280 windowType, isPreview, reqWidth, reqHeight, padding); 1281 } 1282 } 1283 1284 @Override 1285 public void onCreate() { 1286 super.onCreate(); 1287 } 1288 1289 @Override 1290 public void onDestroy() { 1291 super.onDestroy(); 1292 for (int i=0; i<mActiveEngines.size(); i++) { 1293 mActiveEngines.get(i).detach(); 1294 } 1295 mActiveEngines.clear(); 1296 } 1297 1298 /** 1299 * Implement to return the implementation of the internal accessibility 1300 * service interface. Subclasses should not override. 1301 */ 1302 @Override 1303 public final IBinder onBind(Intent intent) { 1304 return new IWallpaperServiceWrapper(this); 1305 } 1306 1307 /** 1308 * Must be implemented to return a new instance of the wallpaper's engine. 1309 * Note that multiple instances may be active at the same time, such as 1310 * when the wallpaper is currently set as the active wallpaper and the user 1311 * is in the wallpaper picker viewing a preview of it as well. 1312 */ 1313 public abstract Engine onCreateEngine(); 1314 1315 @Override 1316 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 1317 out.print("State of wallpaper "); out.print(this); out.println(":"); 1318 for (int i=0; i<mActiveEngines.size(); i++) { 1319 Engine engine = mActiveEngines.get(i); 1320 out.print(" Engine "); out.print(engine); out.println(":"); 1321 engine.dump(" ", fd, out, args); 1322 } 1323 } 1324} 1325