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