WallpaperService.java revision 46b9ac0ae2162309774a7478cd9d4e578747bfc2
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 synchronized (mLock) { 220 if (event.getAction() == MotionEvent.ACTION_MOVE) { 221 if (mPendingMove != null) { 222 mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove); 223 mPendingMove.recycle(); 224 } 225 mPendingMove = event; 226 } else { 227 mPendingMove = null; 228 } 229 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, 230 event); 231 mCaller.sendMessage(msg); 232 } 233 } finally { 234 finishedCallback.run(); 235 } 236 } 237 }; 238 239 final BaseIWindow mWindow = new BaseIWindow() { 240 @Override 241 public boolean onDispatchPointer(MotionEvent event, long eventTime, 242 boolean callWhenDone) { 243 synchronized (mLock) { 244 if (event.getAction() == MotionEvent.ACTION_MOVE) { 245 if (mPendingMove != null) { 246 mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove); 247 mPendingMove.recycle(); 248 } 249 mPendingMove = event; 250 } else { 251 mPendingMove = null; 252 } 253 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, 254 event); 255 mCaller.sendMessage(msg); 256 } 257 return false; 258 } 259 260 @Override 261 public void resized(int w, int h, Rect coveredInsets, 262 Rect visibleInsets, boolean reportDraw, Configuration newConfig) { 263 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED, 264 reportDraw ? 1 : 0); 265 mCaller.sendMessage(msg); 266 } 267 268 @Override 269 public void dispatchAppVisibility(boolean visible) { 270 // We don't do this in preview mode; we'll let the preview 271 // activity tell us when to run. 272 if (!mIWallpaperEngine.mIsPreview) { 273 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 274 visible ? 1 : 0); 275 mCaller.sendMessage(msg); 276 } 277 } 278 279 @Override 280 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 281 boolean sync) { 282 synchronized (mLock) { 283 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 284 mPendingXOffset = x; 285 mPendingYOffset = y; 286 mPendingXOffsetStep = xStep; 287 mPendingYOffsetStep = yStep; 288 if (sync) { 289 mPendingSync = true; 290 } 291 if (!mOffsetMessageEnqueued) { 292 mOffsetMessageEnqueued = true; 293 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 294 mCaller.sendMessage(msg); 295 } 296 } 297 } 298 299 public void dispatchWallpaperCommand(String action, int x, int y, 300 int z, Bundle extras, boolean sync) { 301 synchronized (mLock) { 302 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 303 WallpaperCommand cmd = new WallpaperCommand(); 304 cmd.action = action; 305 cmd.x = x; 306 cmd.y = y; 307 cmd.z = z; 308 cmd.extras = extras; 309 cmd.sync = sync; 310 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 311 msg.obj = cmd; 312 mCaller.sendMessage(msg); 313 } 314 } 315 }; 316 317 /** 318 * Provides access to the surface in which this wallpaper is drawn. 319 */ 320 public SurfaceHolder getSurfaceHolder() { 321 return mSurfaceHolder; 322 } 323 324 /** 325 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 326 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 327 * that the system would like this wallpaper to run in. 328 */ 329 public int getDesiredMinimumWidth() { 330 return mIWallpaperEngine.mReqWidth; 331 } 332 333 /** 334 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 335 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 336 * that the system would like this wallpaper to run in. 337 */ 338 public int getDesiredMinimumHeight() { 339 return mIWallpaperEngine.mReqHeight; 340 } 341 342 /** 343 * Return whether the wallpaper is currently visible to the user, 344 * this is the last value supplied to 345 * {@link #onVisibilityChanged(boolean)}. 346 */ 347 public boolean isVisible() { 348 return mReportedVisible; 349 } 350 351 /** 352 * Returns true if this engine is running in preview mode -- that is, 353 * it is being shown to the user before they select it as the actual 354 * wallpaper. 355 */ 356 public boolean isPreview() { 357 return mIWallpaperEngine.mIsPreview; 358 } 359 360 /** 361 * Control whether this wallpaper will receive raw touch events 362 * from the window manager as the user interacts with the window 363 * that is currently displaying the wallpaper. By default they 364 * are turned off. If enabled, the events will be received in 365 * {@link #onTouchEvent(MotionEvent)}. 366 */ 367 public void setTouchEventsEnabled(boolean enabled) { 368 mWindowFlags = enabled 369 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 370 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 371 if (mCreated) { 372 updateSurface(false, false); 373 } 374 } 375 376 /** 377 * Called once to initialize the engine. After returning, the 378 * engine's surface will be created by the framework. 379 */ 380 public void onCreate(SurfaceHolder surfaceHolder) { 381 } 382 383 /** 384 * Called right before the engine is going away. After this the 385 * surface will be destroyed and this Engine object is no longer 386 * valid. 387 */ 388 public void onDestroy() { 389 } 390 391 /** 392 * Called to inform you of the wallpaper becoming visible or 393 * hidden. <em>It is very important that a wallpaper only use 394 * CPU while it is visible.</em>. 395 */ 396 public void onVisibilityChanged(boolean visible) { 397 } 398 399 /** 400 * Called as the user performs touch-screen interaction with the 401 * window that is currently showing this wallpaper. Note that the 402 * events you receive here are driven by the actual application the 403 * user is interacting with, so if it is slow you will get fewer 404 * move events. 405 */ 406 public void onTouchEvent(MotionEvent event) { 407 } 408 409 /** 410 * Called to inform you of the wallpaper's offsets changing 411 * within its contain, corresponding to the container's 412 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 413 * WallpaperManager.setWallpaperOffsets()}. 414 */ 415 public void onOffsetsChanged(float xOffset, float yOffset, 416 float xOffsetStep, float yOffsetStep, 417 int xPixelOffset, int yPixelOffset) { 418 } 419 420 /** 421 * Process a command that was sent to the wallpaper with 422 * {@link WallpaperManager#sendWallpaperCommand}. 423 * The default implementation does nothing, and always returns null 424 * as the result. 425 * 426 * @param action The name of the command to perform. This tells you 427 * what to do and how to interpret the rest of the arguments. 428 * @param x Generic integer parameter. 429 * @param y Generic integer parameter. 430 * @param z Generic integer parameter. 431 * @param extras Any additional parameters. 432 * @param resultRequested If true, the caller is requesting that 433 * a result, appropriate for the command, be returned back. 434 * @return If returning a result, create a Bundle and place the 435 * result data in to it. Otherwise return null. 436 */ 437 public Bundle onCommand(String action, int x, int y, int z, 438 Bundle extras, boolean resultRequested) { 439 return null; 440 } 441 442 /** 443 * Called when an application has changed the desired virtual size of 444 * the wallpaper. 445 */ 446 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 447 } 448 449 /** 450 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 451 * SurfaceHolder.Callback.surfaceChanged()}. 452 */ 453 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 454 } 455 456 /** 457 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 458 * SurfaceHolder.Callback.surfaceCreated()}. 459 */ 460 public void onSurfaceCreated(SurfaceHolder holder) { 461 } 462 463 /** 464 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 465 * SurfaceHolder.Callback.surfaceDestroyed()}. 466 */ 467 public void onSurfaceDestroyed(SurfaceHolder holder) { 468 } 469 470 void updateSurface(boolean forceRelayout, boolean forceReport) { 471 if (mDestroyed) { 472 Log.w(TAG, "Ignoring updateSurface: destroyed"); 473 } 474 475 int myWidth = mSurfaceHolder.getRequestedWidth(); 476 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 477 int myHeight = mSurfaceHolder.getRequestedHeight(); 478 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 479 480 final boolean creating = !mCreated; 481 final boolean surfaceCreating = !mSurfaceCreated; 482 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 483 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 484 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 485 final boolean flagsChanged = mCurWindowFlags != mWindowFlags; 486 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 487 || typeChanged || flagsChanged) { 488 489 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 490 + " format=" + formatChanged + " size=" + sizeChanged); 491 492 try { 493 mWidth = myWidth; 494 mHeight = myHeight; 495 mFormat = mSurfaceHolder.getRequestedFormat(); 496 mType = mSurfaceHolder.getRequestedType(); 497 498 mLayout.x = 0; 499 mLayout.y = 0; 500 mLayout.width = myWidth; 501 mLayout.height = myHeight; 502 503 mLayout.format = mFormat; 504 505 mCurWindowFlags = mWindowFlags; 506 mLayout.flags = mWindowFlags 507 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 508 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 509 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 510 ; 511 512 mLayout.memoryType = mType; 513 mLayout.token = mWindowToken; 514 515 if (!mCreated) { 516 mLayout.type = mIWallpaperEngine.mWindowType; 517 mLayout.gravity = Gravity.LEFT|Gravity.TOP; 518 mLayout.setTitle(WallpaperService.this.getClass().getName()); 519 mLayout.windowAnimations = 520 com.android.internal.R.style.Animation_Wallpaper; 521 mInputChannel = new InputChannel(); 522 mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets, 523 mInputChannel); 524 mCreated = true; 525 526 if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { 527 InputQueue.registerInputChannel(mInputChannel, mInputHandler, 528 Looper.myQueue()); 529 } 530 } 531 532 mSurfaceHolder.mSurfaceLock.lock(); 533 mDrawingAllowed = true; 534 535 final int relayoutResult = mSession.relayout( 536 mWindow, mLayout, mWidth, mHeight, 537 View.VISIBLE, false, mWinFrame, mContentInsets, 538 mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface); 539 540 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 541 + ", frame=" + mWinFrame); 542 543 int w = mWinFrame.width(); 544 if (mCurWidth != w) { 545 sizeChanged = true; 546 mCurWidth = w; 547 } 548 int h = mWinFrame.height(); 549 if (mCurHeight != h) { 550 sizeChanged = true; 551 mCurHeight = h; 552 } 553 554 mSurfaceHolder.mSurfaceLock.unlock(); 555 556 if (!mSurfaceHolder.mSurface.isValid()) { 557 reportSurfaceDestroyed(); 558 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 559 return; 560 } 561 562 try { 563 mSurfaceHolder.ungetCallbacks(); 564 565 if (surfaceCreating) { 566 mIsCreating = true; 567 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 568 + mSurfaceHolder + "): " + this); 569 onSurfaceCreated(mSurfaceHolder); 570 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 571 if (callbacks != null) { 572 for (SurfaceHolder.Callback c : callbacks) { 573 c.surfaceCreated(mSurfaceHolder); 574 } 575 } 576 } 577 if (forceReport || creating || surfaceCreating 578 || formatChanged || sizeChanged) { 579 if (DEBUG) { 580 RuntimeException e = new RuntimeException(); 581 e.fillInStackTrace(); 582 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 583 + " formatChanged=" + formatChanged 584 + " sizeChanged=" + sizeChanged, e); 585 } 586 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 587 + mSurfaceHolder + ", " + mFormat 588 + ", " + mCurWidth + ", " + mCurHeight 589 + "): " + this); 590 onSurfaceChanged(mSurfaceHolder, mFormat, 591 mCurWidth, mCurHeight); 592 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 593 if (callbacks != null) { 594 for (SurfaceHolder.Callback c : callbacks) { 595 c.surfaceChanged(mSurfaceHolder, mFormat, 596 mCurWidth, mCurHeight); 597 } 598 } 599 } 600 } finally { 601 mIsCreating = false; 602 mSurfaceCreated = true; 603 if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 604 mSession.finishDrawing(mWindow); 605 } 606 } 607 } catch (RemoteException ex) { 608 } 609 if (DEBUG) Log.v( 610 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 611 " w=" + mLayout.width + " h=" + mLayout.height); 612 } 613 } 614 615 void attach(IWallpaperEngineWrapper wrapper) { 616 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 617 if (mDestroyed) { 618 return; 619 } 620 621 mIWallpaperEngine = wrapper; 622 mCaller = wrapper.mCaller; 623 mConnection = wrapper.mConnection; 624 mWindowToken = wrapper.mWindowToken; 625 mSurfaceHolder.setSizeFromLayout(); 626 mInitializing = true; 627 mSession = ViewRoot.getWindowSession(getMainLooper()); 628 629 mWindow.setSession(mSession); 630 631 IntentFilter filter = new IntentFilter(); 632 filter.addAction(Intent.ACTION_SCREEN_ON); 633 filter.addAction(Intent.ACTION_SCREEN_OFF); 634 registerReceiver(mReceiver, filter); 635 636 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 637 onCreate(mSurfaceHolder); 638 639 mInitializing = false; 640 updateSurface(false, false); 641 } 642 643 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 644 if (!mDestroyed) { 645 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 646 + desiredWidth + "," + desiredHeight + "): " + this); 647 onDesiredSizeChanged(desiredWidth, desiredHeight); 648 } 649 } 650 651 void doVisibilityChanged(boolean visible) { 652 if (!mDestroyed) { 653 mVisible = visible; 654 reportVisibility(); 655 } 656 } 657 658 void reportVisibility() { 659 if (!mDestroyed) { 660 boolean visible = mVisible && mScreenOn; 661 if (mReportedVisible != visible) { 662 mReportedVisible = visible; 663 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 664 + "): " + this); 665 if (visible) { 666 // If becoming visible, in preview mode the surface 667 // may have been destroyed so now we need to make 668 // sure it is re-created. 669 updateSurface(false, false); 670 } 671 onVisibilityChanged(visible); 672 } 673 } 674 } 675 676 void doOffsetsChanged() { 677 if (mDestroyed) { 678 return; 679 } 680 681 float xOffset; 682 float yOffset; 683 float xOffsetStep; 684 float yOffsetStep; 685 boolean sync; 686 synchronized (mLock) { 687 xOffset = mPendingXOffset; 688 yOffset = mPendingYOffset; 689 xOffsetStep = mPendingXOffsetStep; 690 yOffsetStep = mPendingYOffsetStep; 691 sync = mPendingSync; 692 mPendingSync = false; 693 mOffsetMessageEnqueued = false; 694 } 695 696 if (mSurfaceCreated) { 697 if (DEBUG) Log.v(TAG, "Offsets change in " + this 698 + ": " + xOffset + "," + yOffset); 699 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 700 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 701 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 702 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 703 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 704 } 705 706 if (sync) { 707 try { 708 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 709 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 710 } catch (RemoteException e) { 711 } 712 } 713 } 714 715 void doCommand(WallpaperCommand cmd) { 716 Bundle result; 717 if (!mDestroyed) { 718 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 719 cmd.extras, cmd.sync); 720 } else { 721 result = null; 722 } 723 if (cmd.sync) { 724 try { 725 if (DEBUG) Log.v(TAG, "Reporting command complete"); 726 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 727 } catch (RemoteException e) { 728 } 729 } 730 } 731 732 void reportSurfaceDestroyed() { 733 if (mSurfaceCreated) { 734 mSurfaceCreated = false; 735 mSurfaceHolder.ungetCallbacks(); 736 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 737 if (callbacks != null) { 738 for (SurfaceHolder.Callback c : callbacks) { 739 c.surfaceDestroyed(mSurfaceHolder); 740 } 741 } 742 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 743 + mSurfaceHolder + "): " + this); 744 onSurfaceDestroyed(mSurfaceHolder); 745 } 746 } 747 748 void detach() { 749 if (mDestroyed) { 750 return; 751 } 752 753 mDestroyed = true; 754 755 if (mVisible) { 756 mVisible = false; 757 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 758 onVisibilityChanged(false); 759 } 760 761 reportSurfaceDestroyed(); 762 763 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 764 onDestroy(); 765 766 unregisterReceiver(mReceiver); 767 768 if (mCreated) { 769 try { 770 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 771 + mSurfaceHolder.getSurface() + " of: " + this); 772 773 if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) { 774 if (mInputChannel != null) { 775 InputQueue.unregisterInputChannel(mInputChannel); 776 mInputChannel.dispose(); 777 mInputChannel = null; 778 } 779 } 780 781 mSession.remove(mWindow); 782 } catch (RemoteException e) { 783 } 784 mSurfaceHolder.mSurface.release(); 785 mCreated = false; 786 } 787 } 788 } 789 790 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 791 implements HandlerCaller.Callback { 792 private final HandlerCaller mCaller; 793 794 final IWallpaperConnection mConnection; 795 final IBinder mWindowToken; 796 final int mWindowType; 797 final boolean mIsPreview; 798 int mReqWidth; 799 int mReqHeight; 800 801 Engine mEngine; 802 803 IWallpaperEngineWrapper(WallpaperService context, 804 IWallpaperConnection conn, IBinder windowToken, 805 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 806 if (DEBUG && mCallbackLooper != null) { 807 mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG)); 808 } 809 mCaller = new HandlerCaller(context, 810 mCallbackLooper != null 811 ? mCallbackLooper : context.getMainLooper(), 812 this); 813 mConnection = conn; 814 mWindowToken = windowToken; 815 mWindowType = windowType; 816 mIsPreview = isPreview; 817 mReqWidth = reqWidth; 818 mReqHeight = reqHeight; 819 820 Message msg = mCaller.obtainMessage(DO_ATTACH); 821 mCaller.sendMessage(msg); 822 } 823 824 public void setDesiredSize(int width, int height) { 825 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 826 mCaller.sendMessage(msg); 827 } 828 829 public void setVisibility(boolean visible) { 830 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 831 visible ? 1 : 0); 832 mCaller.sendMessage(msg); 833 } 834 835 public void dispatchPointer(MotionEvent event) { 836 if (mEngine != null) { 837 mEngine.mWindow.onDispatchPointer(event, event.getEventTime(), false); 838 } 839 } 840 841 public void destroy() { 842 Message msg = mCaller.obtainMessage(DO_DETACH); 843 mCaller.sendMessage(msg); 844 } 845 846 public void executeMessage(Message message) { 847 switch (message.what) { 848 case DO_ATTACH: { 849 try { 850 mConnection.attachEngine(this); 851 } catch (RemoteException e) { 852 Log.w(TAG, "Wallpaper host disappeared", e); 853 return; 854 } 855 Engine engine = onCreateEngine(); 856 mEngine = engine; 857 mActiveEngines.add(engine); 858 engine.attach(this); 859 return; 860 } 861 case DO_DETACH: { 862 mActiveEngines.remove(mEngine); 863 mEngine.detach(); 864 return; 865 } 866 case DO_SET_DESIRED_SIZE: { 867 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 868 return; 869 } 870 case MSG_UPDATE_SURFACE: 871 mEngine.updateSurface(true, false); 872 break; 873 case MSG_VISIBILITY_CHANGED: 874 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 875 + ": " + message.arg1); 876 mEngine.doVisibilityChanged(message.arg1 != 0); 877 break; 878 case MSG_WALLPAPER_OFFSETS: { 879 mEngine.doOffsetsChanged(); 880 } break; 881 case MSG_WALLPAPER_COMMAND: { 882 WallpaperCommand cmd = (WallpaperCommand)message.obj; 883 mEngine.doCommand(cmd); 884 } break; 885 case MSG_WINDOW_RESIZED: { 886 final boolean reportDraw = message.arg1 != 0; 887 mEngine.updateSurface(true, false); 888 mEngine.doOffsetsChanged(); 889 if (reportDraw) { 890 try { 891 mEngine.mSession.finishDrawing(mEngine.mWindow); 892 } catch (RemoteException e) { 893 } 894 } 895 } break; 896 case MSG_TOUCH_EVENT: { 897 MotionEvent ev = (MotionEvent)message.obj; 898 synchronized (mEngine.mLock) { 899 if (mEngine.mPendingMove == ev) { 900 mEngine.mPendingMove = null; 901 } 902 } 903 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 904 mEngine.onTouchEvent(ev); 905 ev.recycle(); 906 } break; 907 default : 908 Log.w(TAG, "Unknown message type " + message.what); 909 } 910 } 911 } 912 913 /** 914 * Implements the internal {@link IWallpaperService} interface to convert 915 * incoming calls to it back to calls on an {@link WallpaperService}. 916 */ 917 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 918 private final WallpaperService mTarget; 919 920 public IWallpaperServiceWrapper(WallpaperService context) { 921 mTarget = context; 922 } 923 924 public void attach(IWallpaperConnection conn, IBinder windowToken, 925 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 926 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 927 windowType, isPreview, reqWidth, reqHeight); 928 } 929 } 930 931 @Override 932 public void onCreate() { 933 super.onCreate(); 934 } 935 936 @Override 937 public void onDestroy() { 938 super.onDestroy(); 939 for (int i=0; i<mActiveEngines.size(); i++) { 940 mActiveEngines.get(i).detach(); 941 } 942 mActiveEngines.clear(); 943 } 944 945 /** 946 * Implement to return the implementation of the internal accessibility 947 * service interface. Subclasses should not override. 948 */ 949 @Override 950 public final IBinder onBind(Intent intent) { 951 return new IWallpaperServiceWrapper(this); 952 } 953 954 /** 955 * This allows subclasses to change the thread that most callbacks 956 * occur on. Currently hidden because it is mostly needed for the 957 * image wallpaper (which runs in the system process and doesn't want 958 * to get stuck running on that seriously in use main thread). Not 959 * exposed right now because the semantics of this are not totally 960 * well defined and some callbacks can still happen on the main thread). 961 * @hide 962 */ 963 public void setCallbackLooper(Looper looper) { 964 mCallbackLooper = looper; 965 } 966 967 /** 968 * Must be implemented to return a new instance of the wallpaper's engine. 969 * Note that multiple instances may be active at the same time, such as 970 * when the wallpaper is currently set as the active wallpaper and the user 971 * is in the wallpaper picker viewing a preview of it as well. 972 */ 973 public abstract Engine onCreateEngine(); 974} 975