WallpaperService.java revision 00fa7bdd69f0868fd17ea7c881c771d785b2fbbd
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.BaseInputHandler; 22import com.android.internal.view.BaseSurfaceHolder; 23 24import android.annotation.SdkConstant; 25import android.annotation.SdkConstant.SdkConstantType; 26import android.app.Service; 27import android.app.WallpaperManager; 28import android.content.BroadcastReceiver; 29import android.content.Context; 30import android.content.Intent; 31import android.content.IntentFilter; 32import android.content.res.Configuration; 33import android.graphics.Rect; 34import android.os.Bundle; 35import android.os.IBinder; 36import android.os.Looper; 37import android.os.Message; 38import android.os.RemoteException; 39import android.util.Log; 40import android.util.LogPrinter; 41import android.view.Gravity; 42import android.view.IWindowSession; 43import android.view.InputChannel; 44import android.view.InputHandler; 45import android.view.InputQueue; 46import android.view.KeyEvent; 47import android.view.MotionEvent; 48import android.view.SurfaceHolder; 49import android.view.View; 50import android.view.ViewGroup; 51import android.view.ViewRoot; 52import android.view.WindowManager; 53import android.view.WindowManagerImpl; 54import android.view.WindowManagerPolicy; 55 56import java.util.ArrayList; 57 58/** 59 * A wallpaper service is responsible for showing a live wallpaper behind 60 * applications that would like to sit on top of it. This service object 61 * itself does very little -- its only purpose is to generate instances of 62 * {@link Engine} as needed. Implementing a wallpaper thus 63 * involves subclassing from this, subclassing an Engine implementation, 64 * and implementing {@link #onCreateEngine()} to return a new instance of 65 * your engine. 66 */ 67public abstract class WallpaperService extends Service { 68 /** 69 * The {@link Intent} that must be declared as handled by the service. 70 * To be supported, the service must also require the 71 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so 72 * that other applications can not abuse it. 73 */ 74 @SdkConstant(SdkConstantType.SERVICE_ACTION) 75 public static final String SERVICE_INTERFACE = 76 "android.service.wallpaper.WallpaperService"; 77 78 /** 79 * Name under which a WallpaperService component publishes information 80 * about itself. This meta-data must reference an XML resource containing 81 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 82 * tag. 83 */ 84 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 85 86 static final String TAG = "WallpaperService"; 87 static final boolean DEBUG = false; 88 89 private static final int DO_ATTACH = 10; 90 private static final int DO_DETACH = 20; 91 private static final int DO_SET_DESIRED_SIZE = 30; 92 93 private static final int MSG_UPDATE_SURFACE = 10000; 94 private static final int MSG_VISIBILITY_CHANGED = 10010; 95 private static final int MSG_WALLPAPER_OFFSETS = 10020; 96 private static final int MSG_WALLPAPER_COMMAND = 10025; 97 private static final int MSG_WINDOW_RESIZED = 10030; 98 private static final int MSG_TOUCH_EVENT = 10040; 99 100 private Looper mCallbackLooper; 101 private final ArrayList<Engine> mActiveEngines 102 = new ArrayList<Engine>(); 103 104 static final class WallpaperCommand { 105 String action; 106 int x; 107 int y; 108 int z; 109 Bundle extras; 110 boolean sync; 111 } 112 113 /** 114 * The actual implementation of a wallpaper. A wallpaper service may 115 * have multiple instances running (for example as a real wallpaper 116 * and as a preview), each of which is represented by its own Engine 117 * instance. You must implement {@link WallpaperService#onCreateEngine()} 118 * to return your concrete Engine implementation. 119 */ 120 public class Engine { 121 IWallpaperEngineWrapper mIWallpaperEngine; 122 123 // Copies from mIWallpaperEngine. 124 HandlerCaller mCaller; 125 IWallpaperConnection mConnection; 126 IBinder mWindowToken; 127 128 boolean mInitializing = true; 129 boolean mVisible; 130 boolean mScreenOn = true; 131 boolean mReportedVisible; 132 boolean mDestroyed; 133 134 // Current window state. 135 boolean mCreated; 136 boolean mSurfaceCreated; 137 boolean mIsCreating; 138 boolean mDrawingAllowed; 139 int mWidth; 140 int mHeight; 141 int mFormat; 142 int mType; 143 int mCurWidth; 144 int mCurHeight; 145 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 146 int mCurWindowFlags = mWindowFlags; 147 final Rect mVisibleInsets = new Rect(); 148 final Rect mWinFrame = new Rect(); 149 final Rect mContentInsets = new Rect(); 150 final Configuration mConfiguration = new Configuration(); 151 152 final WindowManager.LayoutParams mLayout 153 = new WindowManager.LayoutParams(); 154 IWindowSession mSession; 155 InputChannel mInputChannel; 156 157 final Object mLock = new Object(); 158 boolean mOffsetMessageEnqueued; 159 float mPendingXOffset; 160 float mPendingYOffset; 161 float mPendingXOffsetStep; 162 float mPendingYOffsetStep; 163 boolean mPendingSync; 164 MotionEvent mPendingMove; 165 166 final BroadcastReceiver mReceiver = new BroadcastReceiver() { 167 @Override 168 public void onReceive(Context context, Intent intent) { 169 if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { 170 mScreenOn = true; 171 reportVisibility(); 172 } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 173 mScreenOn = false; 174 reportVisibility(); 175 } 176 } 177 }; 178 179 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 180 181 @Override 182 public boolean onAllowLockCanvas() { 183 return mDrawingAllowed; 184 } 185 186 @Override 187 public void onRelayoutContainer() { 188 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 189 mCaller.sendMessage(msg); 190 } 191 192 @Override 193 public void onUpdateSurface() { 194 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 195 mCaller.sendMessage(msg); 196 } 197 198 public boolean isCreating() { 199 return mIsCreating; 200 } 201 202 @Override 203 public void setFixedSize(int width, int height) { 204 throw new UnsupportedOperationException( 205 "Wallpapers currently only support sizing from layout"); 206 } 207 208 public void setKeepScreenOn(boolean screenOn) { 209 throw new UnsupportedOperationException( 210 "Wallpapers do not support keep screen on"); 211 } 212 213 }; 214 215 final InputHandler mInputHandler = new BaseInputHandler() { 216 @Override 217 public void handleTouch(MotionEvent event, Runnable finishedCallback) { 218 try { 219 dispatchPointer(event); 220 } finally { 221 finishedCallback.run(); 222 } 223 } 224 }; 225 226 final BaseIWindow mWindow = new BaseIWindow() { 227 @Override 228 public void resized(int w, int h, Rect coveredInsets, 229 Rect visibleInsets, boolean reportDraw, Configuration newConfig) { 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 private void dispatchPointer(MotionEvent event) { 438 synchronized (mLock) { 439 if (event.getAction() == MotionEvent.ACTION_MOVE) { 440 if (mPendingMove != null) { 441 mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove); 442 mPendingMove.recycle(); 443 } 444 mPendingMove = event; 445 } else { 446 mPendingMove = null; 447 } 448 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 449 mCaller.sendMessage(msg); 450 } 451 } 452 453 void updateSurface(boolean forceRelayout, boolean forceReport) { 454 if (mDestroyed) { 455 Log.w(TAG, "Ignoring updateSurface: destroyed"); 456 } 457 458 int myWidth = mSurfaceHolder.getRequestedWidth(); 459 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 460 int myHeight = mSurfaceHolder.getRequestedHeight(); 461 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 462 463 final boolean creating = !mCreated; 464 final boolean surfaceCreating = !mSurfaceCreated; 465 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 466 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 467 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 468 final boolean flagsChanged = mCurWindowFlags != mWindowFlags; 469 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 470 || typeChanged || flagsChanged) { 471 472 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 473 + " format=" + formatChanged + " size=" + sizeChanged); 474 475 try { 476 mWidth = myWidth; 477 mHeight = myHeight; 478 mFormat = mSurfaceHolder.getRequestedFormat(); 479 mType = mSurfaceHolder.getRequestedType(); 480 481 mLayout.x = 0; 482 mLayout.y = 0; 483 mLayout.width = myWidth; 484 mLayout.height = myHeight; 485 486 mLayout.format = mFormat; 487 488 mCurWindowFlags = mWindowFlags; 489 mLayout.flags = mWindowFlags 490 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 491 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 492 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 493 ; 494 495 mLayout.memoryType = mType; 496 mLayout.token = mWindowToken; 497 498 if (!mCreated) { 499 mLayout.type = mIWallpaperEngine.mWindowType; 500 mLayout.gravity = Gravity.LEFT|Gravity.TOP; 501 mLayout.setTitle(WallpaperService.this.getClass().getName()); 502 mLayout.windowAnimations = 503 com.android.internal.R.style.Animation_Wallpaper; 504 mInputChannel = new InputChannel(); 505 mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets, 506 mInputChannel); 507 mCreated = true; 508 509 InputQueue.registerInputChannel(mInputChannel, mInputHandler, 510 Looper.myQueue()); 511 } 512 513 mSurfaceHolder.mSurfaceLock.lock(); 514 mDrawingAllowed = true; 515 516 final int relayoutResult = mSession.relayout( 517 mWindow, mLayout, mWidth, mHeight, 518 View.VISIBLE, false, mWinFrame, mContentInsets, 519 mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface); 520 521 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 522 + ", frame=" + mWinFrame); 523 524 int w = mWinFrame.width(); 525 if (mCurWidth != w) { 526 sizeChanged = true; 527 mCurWidth = w; 528 } 529 int h = mWinFrame.height(); 530 if (mCurHeight != h) { 531 sizeChanged = true; 532 mCurHeight = h; 533 } 534 535 mSurfaceHolder.mSurfaceLock.unlock(); 536 537 if (!mSurfaceHolder.mSurface.isValid()) { 538 reportSurfaceDestroyed(); 539 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 540 return; 541 } 542 543 try { 544 mSurfaceHolder.ungetCallbacks(); 545 546 if (surfaceCreating) { 547 mIsCreating = true; 548 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 549 + mSurfaceHolder + "): " + this); 550 onSurfaceCreated(mSurfaceHolder); 551 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 552 if (callbacks != null) { 553 for (SurfaceHolder.Callback c : callbacks) { 554 c.surfaceCreated(mSurfaceHolder); 555 } 556 } 557 } 558 if (forceReport || creating || surfaceCreating 559 || formatChanged || sizeChanged) { 560 if (DEBUG) { 561 RuntimeException e = new RuntimeException(); 562 e.fillInStackTrace(); 563 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 564 + " formatChanged=" + formatChanged 565 + " sizeChanged=" + sizeChanged, e); 566 } 567 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 568 + mSurfaceHolder + ", " + mFormat 569 + ", " + mCurWidth + ", " + mCurHeight 570 + "): " + this); 571 onSurfaceChanged(mSurfaceHolder, mFormat, 572 mCurWidth, mCurHeight); 573 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 574 if (callbacks != null) { 575 for (SurfaceHolder.Callback c : callbacks) { 576 c.surfaceChanged(mSurfaceHolder, mFormat, 577 mCurWidth, mCurHeight); 578 } 579 } 580 } 581 } finally { 582 mIsCreating = false; 583 mSurfaceCreated = true; 584 if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 585 mSession.finishDrawing(mWindow); 586 } 587 } 588 } catch (RemoteException ex) { 589 } 590 if (DEBUG) Log.v( 591 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 592 " w=" + mLayout.width + " h=" + mLayout.height); 593 } 594 } 595 596 void attach(IWallpaperEngineWrapper wrapper) { 597 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 598 if (mDestroyed) { 599 return; 600 } 601 602 mIWallpaperEngine = wrapper; 603 mCaller = wrapper.mCaller; 604 mConnection = wrapper.mConnection; 605 mWindowToken = wrapper.mWindowToken; 606 mSurfaceHolder.setSizeFromLayout(); 607 mInitializing = true; 608 mSession = ViewRoot.getWindowSession(getMainLooper()); 609 610 mWindow.setSession(mSession); 611 612 IntentFilter filter = new IntentFilter(); 613 filter.addAction(Intent.ACTION_SCREEN_ON); 614 filter.addAction(Intent.ACTION_SCREEN_OFF); 615 registerReceiver(mReceiver, filter); 616 617 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 618 onCreate(mSurfaceHolder); 619 620 mInitializing = false; 621 updateSurface(false, false); 622 } 623 624 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 625 if (!mDestroyed) { 626 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 627 + desiredWidth + "," + desiredHeight + "): " + this); 628 onDesiredSizeChanged(desiredWidth, desiredHeight); 629 } 630 } 631 632 void doVisibilityChanged(boolean visible) { 633 if (!mDestroyed) { 634 mVisible = visible; 635 reportVisibility(); 636 } 637 } 638 639 void reportVisibility() { 640 if (!mDestroyed) { 641 boolean visible = mVisible && mScreenOn; 642 if (mReportedVisible != visible) { 643 mReportedVisible = visible; 644 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 645 + "): " + this); 646 if (visible) { 647 // If becoming visible, in preview mode the surface 648 // may have been destroyed so now we need to make 649 // sure it is re-created. 650 updateSurface(false, false); 651 } 652 onVisibilityChanged(visible); 653 } 654 } 655 } 656 657 void doOffsetsChanged() { 658 if (mDestroyed) { 659 return; 660 } 661 662 float xOffset; 663 float yOffset; 664 float xOffsetStep; 665 float yOffsetStep; 666 boolean sync; 667 synchronized (mLock) { 668 xOffset = mPendingXOffset; 669 yOffset = mPendingYOffset; 670 xOffsetStep = mPendingXOffsetStep; 671 yOffsetStep = mPendingYOffsetStep; 672 sync = mPendingSync; 673 mPendingSync = false; 674 mOffsetMessageEnqueued = false; 675 } 676 677 if (mSurfaceCreated) { 678 if (DEBUG) Log.v(TAG, "Offsets change in " + this 679 + ": " + xOffset + "," + yOffset); 680 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 681 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 682 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 683 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 684 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 685 } 686 687 if (sync) { 688 try { 689 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 690 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 691 } catch (RemoteException e) { 692 } 693 } 694 } 695 696 void doCommand(WallpaperCommand cmd) { 697 Bundle result; 698 if (!mDestroyed) { 699 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 700 cmd.extras, cmd.sync); 701 } else { 702 result = null; 703 } 704 if (cmd.sync) { 705 try { 706 if (DEBUG) Log.v(TAG, "Reporting command complete"); 707 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 708 } catch (RemoteException e) { 709 } 710 } 711 } 712 713 void reportSurfaceDestroyed() { 714 if (mSurfaceCreated) { 715 mSurfaceCreated = false; 716 mSurfaceHolder.ungetCallbacks(); 717 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 718 if (callbacks != null) { 719 for (SurfaceHolder.Callback c : callbacks) { 720 c.surfaceDestroyed(mSurfaceHolder); 721 } 722 } 723 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 724 + mSurfaceHolder + "): " + this); 725 onSurfaceDestroyed(mSurfaceHolder); 726 } 727 } 728 729 void detach() { 730 if (mDestroyed) { 731 return; 732 } 733 734 mDestroyed = true; 735 736 if (mVisible) { 737 mVisible = false; 738 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 739 onVisibilityChanged(false); 740 } 741 742 reportSurfaceDestroyed(); 743 744 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 745 onDestroy(); 746 747 unregisterReceiver(mReceiver); 748 749 if (mCreated) { 750 try { 751 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 752 + mSurfaceHolder.getSurface() + " of: " + this); 753 754 if (mInputChannel != null) { 755 InputQueue.unregisterInputChannel(mInputChannel); 756 } 757 758 mSession.remove(mWindow); 759 } catch (RemoteException e) { 760 } 761 mSurfaceHolder.mSurface.release(); 762 mCreated = false; 763 764 // Dispose the input channel after removing the window so the Window Manager 765 // doesn't interpret the input channel being closed as an abnormal termination. 766 if (mInputChannel != null) { 767 mInputChannel.dispose(); 768 mInputChannel = null; 769 } 770 } 771 } 772 } 773 774 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 775 implements HandlerCaller.Callback { 776 private final HandlerCaller mCaller; 777 778 final IWallpaperConnection mConnection; 779 final IBinder mWindowToken; 780 final int mWindowType; 781 final boolean mIsPreview; 782 int mReqWidth; 783 int mReqHeight; 784 785 Engine mEngine; 786 787 IWallpaperEngineWrapper(WallpaperService context, 788 IWallpaperConnection conn, IBinder windowToken, 789 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 790 if (DEBUG && mCallbackLooper != null) { 791 mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG)); 792 } 793 mCaller = new HandlerCaller(context, 794 mCallbackLooper != null 795 ? mCallbackLooper : context.getMainLooper(), 796 this); 797 mConnection = conn; 798 mWindowToken = windowToken; 799 mWindowType = windowType; 800 mIsPreview = isPreview; 801 mReqWidth = reqWidth; 802 mReqHeight = reqHeight; 803 804 Message msg = mCaller.obtainMessage(DO_ATTACH); 805 mCaller.sendMessage(msg); 806 } 807 808 public void setDesiredSize(int width, int height) { 809 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 810 mCaller.sendMessage(msg); 811 } 812 813 public void setVisibility(boolean visible) { 814 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 815 visible ? 1 : 0); 816 mCaller.sendMessage(msg); 817 } 818 819 public void dispatchPointer(MotionEvent event) { 820 if (mEngine != null) { 821 mEngine.dispatchPointer(event); 822 } 823 } 824 825 public void destroy() { 826 Message msg = mCaller.obtainMessage(DO_DETACH); 827 mCaller.sendMessage(msg); 828 } 829 830 public void executeMessage(Message message) { 831 switch (message.what) { 832 case DO_ATTACH: { 833 try { 834 mConnection.attachEngine(this); 835 } catch (RemoteException e) { 836 Log.w(TAG, "Wallpaper host disappeared", e); 837 return; 838 } 839 Engine engine = onCreateEngine(); 840 mEngine = engine; 841 mActiveEngines.add(engine); 842 engine.attach(this); 843 return; 844 } 845 case DO_DETACH: { 846 mActiveEngines.remove(mEngine); 847 mEngine.detach(); 848 return; 849 } 850 case DO_SET_DESIRED_SIZE: { 851 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 852 return; 853 } 854 case MSG_UPDATE_SURFACE: 855 mEngine.updateSurface(true, false); 856 break; 857 case MSG_VISIBILITY_CHANGED: 858 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 859 + ": " + message.arg1); 860 mEngine.doVisibilityChanged(message.arg1 != 0); 861 break; 862 case MSG_WALLPAPER_OFFSETS: { 863 mEngine.doOffsetsChanged(); 864 } break; 865 case MSG_WALLPAPER_COMMAND: { 866 WallpaperCommand cmd = (WallpaperCommand)message.obj; 867 mEngine.doCommand(cmd); 868 } break; 869 case MSG_WINDOW_RESIZED: { 870 final boolean reportDraw = message.arg1 != 0; 871 mEngine.updateSurface(true, false); 872 mEngine.doOffsetsChanged(); 873 if (reportDraw) { 874 try { 875 mEngine.mSession.finishDrawing(mEngine.mWindow); 876 } catch (RemoteException e) { 877 } 878 } 879 } break; 880 case MSG_TOUCH_EVENT: { 881 MotionEvent ev = (MotionEvent)message.obj; 882 synchronized (mEngine.mLock) { 883 if (mEngine.mPendingMove == ev) { 884 mEngine.mPendingMove = null; 885 } 886 } 887 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 888 mEngine.onTouchEvent(ev); 889 ev.recycle(); 890 } break; 891 default : 892 Log.w(TAG, "Unknown message type " + message.what); 893 } 894 } 895 } 896 897 /** 898 * Implements the internal {@link IWallpaperService} interface to convert 899 * incoming calls to it back to calls on an {@link WallpaperService}. 900 */ 901 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 902 private final WallpaperService mTarget; 903 904 public IWallpaperServiceWrapper(WallpaperService context) { 905 mTarget = context; 906 } 907 908 public void attach(IWallpaperConnection conn, IBinder windowToken, 909 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 910 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 911 windowType, isPreview, reqWidth, reqHeight); 912 } 913 } 914 915 @Override 916 public void onCreate() { 917 super.onCreate(); 918 } 919 920 @Override 921 public void onDestroy() { 922 super.onDestroy(); 923 for (int i=0; i<mActiveEngines.size(); i++) { 924 mActiveEngines.get(i).detach(); 925 } 926 mActiveEngines.clear(); 927 } 928 929 /** 930 * Implement to return the implementation of the internal accessibility 931 * service interface. Subclasses should not override. 932 */ 933 @Override 934 public final IBinder onBind(Intent intent) { 935 return new IWallpaperServiceWrapper(this); 936 } 937 938 /** 939 * This allows subclasses to change the thread that most callbacks 940 * occur on. Currently hidden because it is mostly needed for the 941 * image wallpaper (which runs in the system process and doesn't want 942 * to get stuck running on that seriously in use main thread). Not 943 * exposed right now because the semantics of this are not totally 944 * well defined and some callbacks can still happen on the main thread). 945 * @hide 946 */ 947 public void setCallbackLooper(Looper looper) { 948 mCallbackLooper = looper; 949 } 950 951 /** 952 * Must be implemented to return a new instance of the wallpaper's engine. 953 * Note that multiple instances may be active at the same time, such as 954 * when the wallpaper is currently set as the active wallpaper and the user 955 * is in the wallpaper picker viewing a preview of it as well. 956 */ 957 public abstract Engine onCreateEngine(); 958} 959