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