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