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