WallpaperService.java revision 23ef7b4836d66d31430fedee2aac36275bb30d4b
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 com.android.internal.os.HandlerCaller; 20import com.android.internal.view.BaseIWindow; 21import com.android.internal.view.BaseSurfaceHolder; 22 23import android.app.Service; 24import android.app.WallpaperManager; 25import android.content.BroadcastReceiver; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.graphics.Rect; 30import android.os.Bundle; 31import android.os.IBinder; 32import android.os.Looper; 33import android.os.Message; 34import android.os.RemoteException; 35import android.util.Log; 36import android.util.LogPrinter; 37import android.view.Gravity; 38import android.view.IWindowSession; 39import android.view.MotionEvent; 40import android.view.SurfaceHolder; 41import android.view.View; 42import android.view.ViewGroup; 43import android.view.ViewRoot; 44import android.view.WindowManager; 45import android.view.WindowManagerImpl; 46 47/** 48 * A wallpaper service is responsible for showing a live wallpaper behind 49 * applications that would like to sit on top of it. This service object 50 * itself does very little -- its only purpose is to generate instances of 51 * {@link WallpaperSerice.Engine} as needed. Implementing a wallpaper thus 52 * involves subclassing from this, subclassing an Engine implementation, 53 * and implementing {@link #onCreateEngine()} to return a new instance of 54 * your engine. 55 */ 56public abstract class WallpaperService extends Service { 57 /** 58 * The {@link Intent} that must be declared as handled by the service. 59 */ 60 public static final String SERVICE_INTERFACE = 61 "android.service.wallpaper.WallpaperService"; 62 63 /** 64 * Name under which a WallpaperService component publishes information 65 * about itself. This meta-data must reference an XML resource containing 66 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 67 * tag. 68 */ 69 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 70 71 static final String TAG = "WallpaperService"; 72 static final boolean DEBUG = false; 73 74 private static final int DO_ATTACH = 10; 75 private static final int DO_DETACH = 20; 76 private static final int DO_SET_DESIRED_SIZE = 30; 77 78 private static final int MSG_UPDATE_SURFACE = 10000; 79 private static final int MSG_VISIBILITY_CHANGED = 10010; 80 private static final int MSG_WALLPAPER_OFFSETS = 10020; 81 private static final int MSG_WALLPAPER_COMMAND = 10025; 82 private static final int MSG_WINDOW_RESIZED = 10030; 83 private static final int MSG_TOUCH_EVENT = 10040; 84 85 private Looper mCallbackLooper; 86 87 static final class WallpaperCommand { 88 String action; 89 int x; 90 int y; 91 int z; 92 Bundle extras; 93 boolean sync; 94 } 95 96 /** 97 * The actual implementation of a wallpaper. A wallpaper service may 98 * have multiple instances running (for example as a real wallpaper 99 * and as a preview), each of which is represented by its own Engine 100 * instance. You must implement {@link WallpaperService#onCreateEngine()} 101 * to return your concrete Engine implementation. 102 */ 103 public class Engine { 104 IWallpaperEngineWrapper mIWallpaperEngine; 105 106 // Copies from mIWallpaperEngine. 107 HandlerCaller mCaller; 108 IWallpaperConnection mConnection; 109 IBinder mWindowToken; 110 111 boolean mInitializing = true; 112 boolean mVisible; 113 boolean mScreenOn = true; 114 boolean mReportedVisible; 115 boolean mDestroyed; 116 117 // Current window state. 118 boolean mCreated; 119 boolean mIsCreating; 120 boolean mDrawingAllowed; 121 int mWidth; 122 int mHeight; 123 int mFormat; 124 int mType; 125 int mCurWidth; 126 int mCurHeight; 127 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 128 int mCurWindowFlags = mWindowFlags; 129 boolean mDestroyReportNeeded; 130 final Rect mVisibleInsets = new Rect(); 131 final Rect mWinFrame = new Rect(); 132 final Rect mContentInsets = new Rect(); 133 134 final WindowManager.LayoutParams mLayout 135 = new WindowManager.LayoutParams(); 136 IWindowSession mSession; 137 138 final Object mLock = new Object(); 139 boolean mOffsetMessageEnqueued; 140 float mPendingXOffset; 141 float mPendingYOffset; 142 float mPendingXOffsetStep; 143 float mPendingYOffsetStep; 144 boolean mPendingSync; 145 MotionEvent mPendingMove; 146 147 final BroadcastReceiver mReceiver = new BroadcastReceiver() { 148 @Override 149 public void onReceive(Context context, Intent intent) { 150 if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { 151 mScreenOn = true; 152 reportVisibility(); 153 } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 154 mScreenOn = false; 155 reportVisibility(); 156 } 157 } 158 }; 159 160 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 161 162 @Override 163 public boolean onAllowLockCanvas() { 164 return mDrawingAllowed; 165 } 166 167 @Override 168 public void onRelayoutContainer() { 169 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 170 mCaller.sendMessage(msg); 171 } 172 173 @Override 174 public void onUpdateSurface() { 175 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 176 mCaller.sendMessage(msg); 177 } 178 179 public boolean isCreating() { 180 return mIsCreating; 181 } 182 183 @Override 184 public void setFixedSize(int width, int height) { 185 throw new UnsupportedOperationException( 186 "Wallpapers currently only support sizing from layout"); 187 } 188 189 public void setKeepScreenOn(boolean screenOn) { 190 throw new UnsupportedOperationException( 191 "Wallpapers do not support keep screen on"); 192 } 193 194 }; 195 196 final BaseIWindow mWindow = new BaseIWindow() { 197 @Override 198 public boolean onDispatchPointer(MotionEvent event, long eventTime, 199 boolean callWhenDone) { 200 synchronized (mLock) { 201 if (event.getAction() == MotionEvent.ACTION_MOVE) { 202 if (mPendingMove != null) { 203 mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove); 204 mPendingMove.recycle(); 205 } 206 mPendingMove = event; 207 } else { 208 mPendingMove = null; 209 } 210 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, 211 event); 212 mCaller.sendMessage(msg); 213 } 214 return false; 215 } 216 217 @Override 218 public void resized(int w, int h, Rect coveredInsets, 219 Rect visibleInsets, boolean reportDraw) { 220 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED, 221 reportDraw ? 1 : 0); 222 mCaller.sendMessage(msg); 223 } 224 225 @Override 226 public void dispatchAppVisibility(boolean visible) { 227 // We don't do this in preview mode; we'll let the preview 228 // activity tell us when to run. 229 if (!mIWallpaperEngine.mIsPreview) { 230 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 231 visible ? 1 : 0); 232 mCaller.sendMessage(msg); 233 } 234 } 235 236 @Override 237 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 238 boolean sync) { 239 synchronized (mLock) { 240 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 241 mPendingXOffset = x; 242 mPendingYOffset = y; 243 mPendingXOffsetStep = xStep; 244 mPendingYOffsetStep = yStep; 245 if (sync) { 246 mPendingSync = true; 247 } 248 if (!mOffsetMessageEnqueued) { 249 mOffsetMessageEnqueued = true; 250 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 251 mCaller.sendMessage(msg); 252 } 253 } 254 } 255 256 public void dispatchWallpaperCommand(String action, int x, int y, 257 int z, Bundle extras, boolean sync) { 258 synchronized (mLock) { 259 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 260 WallpaperCommand cmd = new WallpaperCommand(); 261 cmd.action = action; 262 cmd.x = x; 263 cmd.y = y; 264 cmd.z = z; 265 cmd.extras = extras; 266 cmd.sync = sync; 267 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 268 msg.obj = cmd; 269 mCaller.sendMessage(msg); 270 } 271 } 272 }; 273 274 /** 275 * Provides access to the surface in which this wallpaper is drawn. 276 */ 277 public SurfaceHolder getSurfaceHolder() { 278 return mSurfaceHolder; 279 } 280 281 /** 282 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 283 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 284 * that the system would like this wallpaper to run in. 285 */ 286 public int getDesiredMinimumWidth() { 287 return mIWallpaperEngine.mReqWidth; 288 } 289 290 /** 291 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 292 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 293 * that the system would like this wallpaper to run in. 294 */ 295 public int getDesiredMinimumHeight() { 296 return mIWallpaperEngine.mReqHeight; 297 } 298 299 /** 300 * Return whether the wallpaper is currently visible to the user, 301 * this is the last value supplied to 302 * {@link #onVisibilityChanged(boolean)}. 303 */ 304 public boolean isVisible() { 305 return mReportedVisible; 306 } 307 308 /** 309 * Returns true if this engine is running in preview mode -- that is, 310 * it is being shown to the user before they select it as the actual 311 * wallpaper. 312 */ 313 public boolean isPreview() { 314 return mIWallpaperEngine.mIsPreview; 315 } 316 317 /** 318 * Control whether this wallpaper will receive raw touch events 319 * from the window manager as the user interacts with the window 320 * that is currently displaying the wallpaper. By default they 321 * are turned off. If enabled, the events will be received in 322 * {@link #onTouchEvent(MotionEvent)}. 323 */ 324 public void setTouchEventsEnabled(boolean enabled) { 325 mWindowFlags = enabled 326 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 327 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 328 if (mCreated) { 329 updateSurface(false, false); 330 } 331 } 332 333 /** 334 * Called once to initialize the engine. After returning, the 335 * engine's surface will be created by the framework. 336 */ 337 public void onCreate(SurfaceHolder surfaceHolder) { 338 } 339 340 /** 341 * Called right before the engine is going away. After this the 342 * surface will be destroyed and this Engine object is no longer 343 * valid. 344 */ 345 public void onDestroy() { 346 } 347 348 /** 349 * Called to inform you of the wallpaper becoming visible or 350 * hidden. <em>It is very important that a wallpaper only use 351 * CPU while it is visible.</em>. 352 */ 353 public void onVisibilityChanged(boolean visible) { 354 } 355 356 /** 357 * Called as the user performs touch-screen interaction with the 358 * window that is currently showing this wallpaper. Note that the 359 * events you receive here are driven by the actual application the 360 * user is interacting with, so if it is slow you will get fewer 361 * move events. 362 */ 363 public void onTouchEvent(MotionEvent event) { 364 } 365 366 /** 367 * Called to inform you of the wallpaper's offsets changing 368 * within its contain, corresponding to the container's 369 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 370 * WallpaperManager.setWallpaperOffsets()}. 371 */ 372 public void onOffsetsChanged(float xOffset, float yOffset, 373 float xOffsetStep, float yOffsetStep, 374 int xPixelOffset, int yPixelOffset) { 375 } 376 377 /** 378 * Process a command that was sent to the wallpaper with 379 * {@link WallpaperManager#sendWallpaperCommand}. 380 * The default implementation does nothing, and always returns null 381 * as the result. 382 * 383 * @param action The name of the command to perform. This tells you 384 * what to do and how to interpret the rest of the arguments. 385 * @param x Generic integer parameter. 386 * @param y Generic integer parameter. 387 * @param z Generic integer parameter. 388 * @param extras Any additional parameters. 389 * @param resultRequested If true, the caller is requesting that 390 * a result, appropriate for the command, be returned back. 391 * @return If returning a result, create a Bundle and place the 392 * result data in to it. Otherwise return null. 393 */ 394 public Bundle onCommand(String action, int x, int y, int z, 395 Bundle extras, boolean resultRequested) { 396 return null; 397 } 398 399 /** 400 * Called when an application has changed the desired virtual size of 401 * the wallpaper. 402 */ 403 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 404 } 405 406 /** 407 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 408 * SurfaceHolder.Callback.surfaceChanged()}. 409 */ 410 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 411 } 412 413 /** 414 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 415 * SurfaceHolder.Callback.surfaceCreated()}. 416 */ 417 public void onSurfaceCreated(SurfaceHolder holder) { 418 } 419 420 /** 421 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 422 * SurfaceHolder.Callback.surfaceDestroyed()}. 423 */ 424 public void onSurfaceDestroyed(SurfaceHolder holder) { 425 } 426 427 void updateSurface(boolean forceRelayout, boolean forceReport) { 428 if (mDestroyed) { 429 Log.w(TAG, "Ignoring updateSurface: destroyed"); 430 } 431 432 int myWidth = mSurfaceHolder.getRequestedWidth(); 433 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT; 434 int myHeight = mSurfaceHolder.getRequestedHeight(); 435 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT; 436 437 final boolean creating = !mCreated; 438 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 439 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 440 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 441 final boolean flagsChanged = mCurWindowFlags != mWindowFlags; 442 if (forceRelayout || creating || formatChanged || sizeChanged 443 || typeChanged || flagsChanged) { 444 445 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 446 + " format=" + formatChanged + " size=" + sizeChanged); 447 448 try { 449 mWidth = myWidth; 450 mHeight = myHeight; 451 mFormat = mSurfaceHolder.getRequestedFormat(); 452 mType = mSurfaceHolder.getRequestedType(); 453 454 mLayout.x = 0; 455 mLayout.y = 0; 456 mLayout.width = myWidth; 457 mLayout.height = myHeight; 458 459 mLayout.format = mFormat; 460 461 mCurWindowFlags = mWindowFlags; 462 mLayout.flags = mWindowFlags 463 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 464 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 465 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 466 ; 467 468 mLayout.memoryType = mType; 469 mLayout.token = mWindowToken; 470 471 if (!mCreated) { 472 mLayout.type = mIWallpaperEngine.mWindowType; 473 mLayout.gravity = Gravity.LEFT|Gravity.TOP; 474 mLayout.setTitle(WallpaperService.this.getClass().getName()); 475 mLayout.windowAnimations = 476 com.android.internal.R.style.Animation_Wallpaper; 477 mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets); 478 } 479 480 mSurfaceHolder.mSurfaceLock.lock(); 481 mDrawingAllowed = true; 482 483 final int relayoutResult = mSession.relayout( 484 mWindow, mLayout, mWidth, mHeight, 485 View.VISIBLE, false, mWinFrame, mContentInsets, 486 mVisibleInsets, mSurfaceHolder.mSurface); 487 488 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 489 + ", frame=" + mWinFrame); 490 491 int w = mWinFrame.width(); 492 if (mCurWidth != w) { 493 sizeChanged = true; 494 mCurWidth = w; 495 } 496 int h = mWinFrame.height(); 497 if (mCurHeight != h) { 498 sizeChanged = true; 499 mCurHeight = h; 500 } 501 502 mSurfaceHolder.mSurfaceLock.unlock(); 503 504 try { 505 mDestroyReportNeeded = true; 506 507 SurfaceHolder.Callback callbacks[] = null; 508 synchronized (mSurfaceHolder.mCallbacks) { 509 final int N = mSurfaceHolder.mCallbacks.size(); 510 if (N > 0) { 511 callbacks = new SurfaceHolder.Callback[N]; 512 mSurfaceHolder.mCallbacks.toArray(callbacks); 513 } 514 } 515 516 if (!mCreated) { 517 mIsCreating = true; 518 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 519 + mSurfaceHolder + "): " + this); 520 onSurfaceCreated(mSurfaceHolder); 521 if (callbacks != null) { 522 for (SurfaceHolder.Callback c : callbacks) { 523 c.surfaceCreated(mSurfaceHolder); 524 } 525 } 526 } 527 if (forceReport || creating || formatChanged || sizeChanged) { 528 if (DEBUG) { 529 RuntimeException e = new RuntimeException(); 530 e.fillInStackTrace(); 531 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 532 + " formatChanged=" + formatChanged 533 + " sizeChanged=" + sizeChanged, e); 534 } 535 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 536 + mSurfaceHolder + ", " + mFormat 537 + ", " + mCurWidth + ", " + mCurHeight 538 + "): " + this); 539 onSurfaceChanged(mSurfaceHolder, mFormat, 540 mCurWidth, mCurHeight); 541 if (callbacks != null) { 542 for (SurfaceHolder.Callback c : callbacks) { 543 c.surfaceChanged(mSurfaceHolder, mFormat, 544 mCurWidth, mCurHeight); 545 } 546 } 547 } 548 } finally { 549 mIsCreating = false; 550 mCreated = true; 551 if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 552 mSession.finishDrawing(mWindow); 553 } 554 } 555 } catch (RemoteException ex) { 556 } 557 if (DEBUG) Log.v( 558 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 559 " w=" + mLayout.width + " h=" + mLayout.height); 560 } 561 } 562 563 void attach(IWallpaperEngineWrapper wrapper) { 564 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 565 if (mDestroyed) { 566 return; 567 } 568 569 mIWallpaperEngine = wrapper; 570 mCaller = wrapper.mCaller; 571 mConnection = wrapper.mConnection; 572 mWindowToken = wrapper.mWindowToken; 573 mSurfaceHolder.setSizeFromLayout(); 574 mInitializing = true; 575 mSession = ViewRoot.getWindowSession(getMainLooper()); 576 mWindow.setSession(mSession); 577 578 IntentFilter filter = new IntentFilter(); 579 filter.addAction(Intent.ACTION_SCREEN_ON); 580 filter.addAction(Intent.ACTION_SCREEN_OFF); 581 registerReceiver(mReceiver, filter); 582 583 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 584 onCreate(mSurfaceHolder); 585 586 mInitializing = false; 587 updateSurface(false, false); 588 } 589 590 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 591 if (!mDestroyed) { 592 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 593 + desiredWidth + "," + desiredHeight + "): " + this); 594 onDesiredSizeChanged(desiredWidth, desiredHeight); 595 } 596 } 597 598 void doVisibilityChanged(boolean visible) { 599 mVisible = visible; 600 reportVisibility(); 601 } 602 603 void reportVisibility() { 604 if (!mDestroyed) { 605 boolean visible = mVisible && mScreenOn; 606 if (mReportedVisible != visible) { 607 mReportedVisible = visible; 608 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 609 + "): " + this); 610 onVisibilityChanged(visible); 611 } 612 } 613 } 614 615 void doOffsetsChanged() { 616 if (mDestroyed) { 617 return; 618 } 619 620 float xOffset; 621 float yOffset; 622 float xOffsetStep; 623 float yOffsetStep; 624 boolean sync; 625 synchronized (mLock) { 626 xOffset = mPendingXOffset; 627 yOffset = mPendingYOffset; 628 xOffsetStep = mPendingXOffsetStep; 629 yOffsetStep = mPendingYOffsetStep; 630 sync = mPendingSync; 631 mPendingSync = false; 632 mOffsetMessageEnqueued = false; 633 } 634 if (DEBUG) Log.v(TAG, "Offsets change in " + this 635 + ": " + xOffset + "," + yOffset); 636 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 637 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 638 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 639 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 640 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 641 642 if (sync) { 643 try { 644 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 645 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 646 } catch (RemoteException e) { 647 } 648 } 649 } 650 651 void doCommand(WallpaperCommand cmd) { 652 Bundle result; 653 if (!mDestroyed) { 654 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 655 cmd.extras, cmd.sync); 656 } else { 657 result = null; 658 } 659 if (cmd.sync) { 660 try { 661 if (DEBUG) Log.v(TAG, "Reporting command complete"); 662 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 663 } catch (RemoteException e) { 664 } 665 } 666 } 667 668 void detach() { 669 mDestroyed = true; 670 671 if (mVisible) { 672 mVisible = false; 673 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 674 onVisibilityChanged(false); 675 } 676 677 if (mDestroyReportNeeded) { 678 mDestroyReportNeeded = false; 679 SurfaceHolder.Callback callbacks[]; 680 synchronized (mSurfaceHolder.mCallbacks) { 681 callbacks = new SurfaceHolder.Callback[ 682 mSurfaceHolder.mCallbacks.size()]; 683 mSurfaceHolder.mCallbacks.toArray(callbacks); 684 } 685 for (SurfaceHolder.Callback c : callbacks) { 686 c.surfaceDestroyed(mSurfaceHolder); 687 } 688 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 689 + mSurfaceHolder + "): " + this); 690 onSurfaceDestroyed(mSurfaceHolder); 691 } 692 693 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 694 onDestroy(); 695 696 unregisterReceiver(mReceiver); 697 698 if (mCreated) { 699 try { 700 mSession.remove(mWindow); 701 } catch (RemoteException e) { 702 } 703 mSurfaceHolder.mSurface.release(); 704 mCreated = false; 705 } 706 } 707 } 708 709 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 710 implements HandlerCaller.Callback { 711 private final HandlerCaller mCaller; 712 713 final IWallpaperConnection mConnection; 714 final IBinder mWindowToken; 715 final int mWindowType; 716 final boolean mIsPreview; 717 int mReqWidth; 718 int mReqHeight; 719 720 Engine mEngine; 721 722 IWallpaperEngineWrapper(WallpaperService context, 723 IWallpaperConnection conn, IBinder windowToken, 724 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 725 if (DEBUG && mCallbackLooper != null) { 726 mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG)); 727 } 728 mCaller = new HandlerCaller(context, 729 mCallbackLooper != null 730 ? mCallbackLooper : context.getMainLooper(), 731 this); 732 mConnection = conn; 733 mWindowToken = windowToken; 734 mWindowType = windowType; 735 mIsPreview = isPreview; 736 mReqWidth = reqWidth; 737 mReqHeight = reqHeight; 738 739 Message msg = mCaller.obtainMessage(DO_ATTACH); 740 mCaller.sendMessage(msg); 741 } 742 743 public void setDesiredSize(int width, int height) { 744 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 745 mCaller.sendMessage(msg); 746 } 747 748 public void setVisibility(boolean visible) { 749 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 750 visible ? 1 : 0); 751 mCaller.sendMessage(msg); 752 } 753 754 public void dispatchPointer(MotionEvent event) { 755 if (mEngine != null) { 756 mEngine.mWindow.onDispatchPointer(event, event.getEventTime(), false); 757 } 758 } 759 760 public void destroy() { 761 Message msg = mCaller.obtainMessage(DO_DETACH); 762 mCaller.sendMessage(msg); 763 } 764 765 public void executeMessage(Message message) { 766 switch (message.what) { 767 case DO_ATTACH: { 768 try { 769 mConnection.attachEngine(this); 770 } catch (RemoteException e) { 771 Log.w(TAG, "Wallpaper host disappeared", e); 772 return; 773 } 774 Engine engine = onCreateEngine(); 775 mEngine = engine; 776 engine.attach(this); 777 return; 778 } 779 case DO_DETACH: { 780 mEngine.detach(); 781 return; 782 } 783 case DO_SET_DESIRED_SIZE: { 784 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 785 return; 786 } 787 case MSG_UPDATE_SURFACE: 788 mEngine.updateSurface(true, false); 789 break; 790 case MSG_VISIBILITY_CHANGED: 791 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 792 + ": " + message.arg1); 793 mEngine.doVisibilityChanged(message.arg1 != 0); 794 break; 795 case MSG_WALLPAPER_OFFSETS: { 796 mEngine.doOffsetsChanged(); 797 } break; 798 case MSG_WALLPAPER_COMMAND: { 799 WallpaperCommand cmd = (WallpaperCommand)message.obj; 800 mEngine.doCommand(cmd); 801 } break; 802 case MSG_WINDOW_RESIZED: { 803 final boolean reportDraw = message.arg1 != 0; 804 mEngine.updateSurface(true, false); 805 if (reportDraw) { 806 try { 807 mEngine.mSession.finishDrawing(mEngine.mWindow); 808 } catch (RemoteException e) { 809 } 810 } 811 } break; 812 case MSG_TOUCH_EVENT: { 813 MotionEvent ev = (MotionEvent)message.obj; 814 synchronized (mEngine.mLock) { 815 if (mEngine.mPendingMove == ev) { 816 mEngine.mPendingMove = null; 817 } 818 } 819 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 820 mEngine.onTouchEvent(ev); 821 ev.recycle(); 822 } break; 823 default : 824 Log.w(TAG, "Unknown message type " + message.what); 825 } 826 } 827 } 828 829 /** 830 * Implements the internal {@link IWallpaperService} interface to convert 831 * incoming calls to it back to calls on an {@link WallpaperService}. 832 */ 833 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 834 private final WallpaperService mTarget; 835 836 public IWallpaperServiceWrapper(WallpaperService context) { 837 mTarget = context; 838 } 839 840 public void attach(IWallpaperConnection conn, IBinder windowToken, 841 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 842 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 843 windowType, isPreview, reqWidth, reqHeight); 844 } 845 } 846 847 /** 848 * Implement to return the implementation of the internal accessibility 849 * service interface. Subclasses should not override. 850 */ 851 @Override 852 public final IBinder onBind(Intent intent) { 853 return new IWallpaperServiceWrapper(this); 854 } 855 856 /** 857 * This allows subclasses to change the thread that most callbacks 858 * occur on. Currently hidden because it is mostly needed for the 859 * image wallpaper (which runs in the system process and doesn't want 860 * to get stuck running on that seriously in use main thread). Not 861 * exposed right now because the semantics of this are not totally 862 * well defined and some callbacks can still happen on the main thread). 863 * @hide 864 */ 865 public void setCallbackLooper(Looper looper) { 866 mCallbackLooper = looper; 867 } 868 869 /** 870 * Must be implemented to return a new instance of the wallpaper's engine. 871 * Note that multiple instances may be active at the same time, such as 872 * when the wallpaper is currently set as the active wallpaper and the user 873 * is in the wallpaper picker viewing a preview of it as well. 874 */ 875 public abstract Engine onCreateEngine(); 876} 877