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