WallpaperService.java revision 5c58de3a523a384c47b0b1e0f5dd9728a74cd9f7
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.service.wallpaper; 18 19import com.android.internal.os.HandlerCaller; 20import com.android.internal.view.BaseIWindow; 21import com.android.internal.view.BaseSurfaceHolder; 22 23import android.annotation.SdkConstant; 24import android.annotation.SdkConstant.SdkConstantType; 25import android.app.Service; 26import android.app.WallpaperManager; 27import android.content.BroadcastReceiver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentFilter; 31import android.content.res.Configuration; 32import android.graphics.PixelFormat; 33import android.graphics.Rect; 34import android.os.Bundle; 35import android.os.IBinder; 36import android.os.Looper; 37import android.os.Message; 38import android.os.PowerManager; 39import android.os.Process; 40import android.os.RemoteException; 41import android.util.Log; 42import android.util.LogPrinter; 43import android.view.Gravity; 44import android.view.IWindowSession; 45import android.view.InputChannel; 46import android.view.InputDevice; 47import android.view.InputEvent; 48import android.view.InputEventReceiver; 49import android.view.MotionEvent; 50import android.view.SurfaceHolder; 51import android.view.View; 52import android.view.ViewGroup; 53import android.view.ViewRootImpl; 54import android.view.WindowManager; 55import android.view.WindowManagerImpl; 56 57import java.io.FileDescriptor; 58import java.io.PrintWriter; 59import java.util.ArrayList; 60 61/** 62 * A wallpaper service is responsible for showing a live wallpaper behind 63 * applications that would like to sit on top of it. This service object 64 * itself does very little -- its only purpose is to generate instances of 65 * {@link Engine} as needed. Implementing a wallpaper thus 66 * involves subclassing from this, subclassing an Engine implementation, 67 * and implementing {@link #onCreateEngine()} to return a new instance of 68 * your engine. 69 */ 70public abstract class WallpaperService extends Service { 71 /** 72 * The {@link Intent} that must be declared as handled by the service. 73 * To be supported, the service must also require the 74 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so 75 * that other applications can not abuse it. 76 */ 77 @SdkConstant(SdkConstantType.SERVICE_ACTION) 78 public static final String SERVICE_INTERFACE = 79 "android.service.wallpaper.WallpaperService"; 80 81 /** 82 * Name under which a WallpaperService component publishes information 83 * about itself. This meta-data must reference an XML resource containing 84 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 85 * tag. 86 */ 87 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 88 89 static final String TAG = "WallpaperService"; 90 static final boolean DEBUG = false; 91 92 private static final int DO_ATTACH = 10; 93 private static final int DO_DETACH = 20; 94 private static final int DO_SET_DESIRED_SIZE = 30; 95 96 private static final int MSG_UPDATE_SURFACE = 10000; 97 private static final int MSG_VISIBILITY_CHANGED = 10010; 98 private static final int MSG_WALLPAPER_OFFSETS = 10020; 99 private static final int MSG_WALLPAPER_COMMAND = 10025; 100 private static final int MSG_WINDOW_RESIZED = 10030; 101 private static final int MSG_TOUCH_EVENT = 10040; 102 103 private Looper mCallbackLooper; 104 private final ArrayList<Engine> mActiveEngines 105 = new ArrayList<Engine>(); 106 107 static final class WallpaperCommand { 108 String action; 109 int x; 110 int y; 111 int z; 112 Bundle extras; 113 boolean sync; 114 } 115 116 /** 117 * The actual implementation of a wallpaper. A wallpaper service may 118 * have multiple instances running (for example as a real wallpaper 119 * and as a preview), each of which is represented by its own Engine 120 * instance. You must implement {@link WallpaperService#onCreateEngine()} 121 * to return your concrete Engine implementation. 122 */ 123 public class Engine { 124 IWallpaperEngineWrapper mIWallpaperEngine; 125 126 // Copies from mIWallpaperEngine. 127 HandlerCaller mCaller; 128 IWallpaperConnection mConnection; 129 IBinder mWindowToken; 130 131 boolean mInitializing = true; 132 boolean mVisible; 133 boolean mScreenOn = true; 134 boolean mReportedVisible; 135 boolean mDestroyed; 136 137 // Current window state. 138 boolean mCreated; 139 boolean mSurfaceCreated; 140 boolean mIsCreating; 141 boolean mDrawingAllowed; 142 boolean mOffsetsChanged; 143 boolean mFixedSizeAllowed; 144 int mWidth; 145 int mHeight; 146 int mFormat; 147 int mType; 148 int mCurWidth; 149 int mCurHeight; 150 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 151 int mWindowPrivateFlags = 152 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; 153 int mCurWindowFlags = mWindowFlags; 154 int mCurWindowPrivateFlags = mWindowPrivateFlags; 155 final Rect mVisibleInsets = new Rect(); 156 final Rect mWinFrame = new Rect(); 157 final Rect mSystemInsets = new Rect(); 158 final Rect mContentInsets = new Rect(); 159 final Configuration mConfiguration = new Configuration(); 160 161 final WindowManager.LayoutParams mLayout 162 = new WindowManager.LayoutParams(); 163 IWindowSession mSession; 164 InputChannel mInputChannel; 165 166 final Object mLock = new Object(); 167 boolean mOffsetMessageEnqueued; 168 float mPendingXOffset; 169 float mPendingYOffset; 170 float mPendingXOffsetStep; 171 float mPendingYOffsetStep; 172 boolean mPendingSync; 173 MotionEvent mPendingMove; 174 175 final BroadcastReceiver mReceiver = new BroadcastReceiver() { 176 @Override 177 public void onReceive(Context context, Intent intent) { 178 if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { 179 mScreenOn = true; 180 reportVisibility(); 181 } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { 182 mScreenOn = false; 183 reportVisibility(); 184 } 185 } 186 }; 187 188 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 189 { 190 mRequestedFormat = PixelFormat.RGBX_8888; 191 } 192 193 @Override 194 public boolean onAllowLockCanvas() { 195 return mDrawingAllowed; 196 } 197 198 @Override 199 public void onRelayoutContainer() { 200 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 201 mCaller.sendMessage(msg); 202 } 203 204 @Override 205 public void onUpdateSurface() { 206 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 207 mCaller.sendMessage(msg); 208 } 209 210 public boolean isCreating() { 211 return mIsCreating; 212 } 213 214 @Override 215 public void setFixedSize(int width, int height) { 216 if (!mFixedSizeAllowed) { 217 // Regular apps can't do this. It can only work for 218 // certain designs of window animations, so you can't 219 // rely on it. 220 throw new UnsupportedOperationException( 221 "Wallpapers currently only support sizing from layout"); 222 } 223 super.setFixedSize(width, height); 224 } 225 226 public void setKeepScreenOn(boolean screenOn) { 227 throw new UnsupportedOperationException( 228 "Wallpapers do not support keep screen on"); 229 } 230 231 }; 232 233 final class WallpaperInputEventReceiver extends InputEventReceiver { 234 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) { 235 super(inputChannel, looper); 236 } 237 238 @Override 239 public void onInputEvent(InputEvent event) { 240 boolean handled = false; 241 try { 242 if (event instanceof MotionEvent 243 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 244 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event); 245 dispatchPointer(dup); 246 handled = true; 247 } 248 } finally { 249 finishInputEvent(event, handled); 250 } 251 } 252 } 253 WallpaperInputEventReceiver mInputEventReceiver; 254 255 final BaseIWindow mWindow = new BaseIWindow() { 256 @Override 257 public void resized(int w, int h, Rect systemInsets, Rect contentInsets, 258 Rect visibleInsets, boolean reportDraw, Configuration newConfig) { 259 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED, 260 reportDraw ? 1 : 0); 261 mCaller.sendMessage(msg); 262 } 263 264 @Override 265 public void dispatchAppVisibility(boolean visible) { 266 // We don't do this in preview mode; we'll let the preview 267 // activity tell us when to run. 268 if (!mIWallpaperEngine.mIsPreview) { 269 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 270 visible ? 1 : 0); 271 mCaller.sendMessage(msg); 272 } 273 } 274 275 @Override 276 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 277 boolean sync) { 278 synchronized (mLock) { 279 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 280 mPendingXOffset = x; 281 mPendingYOffset = y; 282 mPendingXOffsetStep = xStep; 283 mPendingYOffsetStep = yStep; 284 if (sync) { 285 mPendingSync = true; 286 } 287 if (!mOffsetMessageEnqueued) { 288 mOffsetMessageEnqueued = true; 289 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 290 mCaller.sendMessage(msg); 291 } 292 } 293 } 294 295 public void dispatchWallpaperCommand(String action, int x, int y, 296 int z, Bundle extras, boolean sync) { 297 synchronized (mLock) { 298 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 299 WallpaperCommand cmd = new WallpaperCommand(); 300 cmd.action = action; 301 cmd.x = x; 302 cmd.y = y; 303 cmd.z = z; 304 cmd.extras = extras; 305 cmd.sync = sync; 306 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 307 msg.obj = cmd; 308 mCaller.sendMessage(msg); 309 } 310 } 311 }; 312 313 /** 314 * Provides access to the surface in which this wallpaper is drawn. 315 */ 316 public SurfaceHolder getSurfaceHolder() { 317 return mSurfaceHolder; 318 } 319 320 /** 321 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 322 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 323 * that the system would like this wallpaper to run in. 324 */ 325 public int getDesiredMinimumWidth() { 326 return mIWallpaperEngine.mReqWidth; 327 } 328 329 /** 330 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 331 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 332 * that the system would like this wallpaper to run in. 333 */ 334 public int getDesiredMinimumHeight() { 335 return mIWallpaperEngine.mReqHeight; 336 } 337 338 /** 339 * Return whether the wallpaper is currently visible to the user, 340 * this is the last value supplied to 341 * {@link #onVisibilityChanged(boolean)}. 342 */ 343 public boolean isVisible() { 344 return mReportedVisible; 345 } 346 347 /** 348 * Returns true if this engine is running in preview mode -- that is, 349 * it is being shown to the user before they select it as the actual 350 * wallpaper. 351 */ 352 public boolean isPreview() { 353 return mIWallpaperEngine.mIsPreview; 354 } 355 356 /** 357 * Control whether this wallpaper will receive raw touch events 358 * from the window manager as the user interacts with the window 359 * that is currently displaying the wallpaper. By default they 360 * are turned off. If enabled, the events will be received in 361 * {@link #onTouchEvent(MotionEvent)}. 362 */ 363 public void setTouchEventsEnabled(boolean enabled) { 364 mWindowFlags = enabled 365 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 366 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 367 if (mCreated) { 368 updateSurface(false, false, false); 369 } 370 } 371 372 /** 373 * Control whether this wallpaper will receive notifications when the wallpaper 374 * has been scrolled. By default, wallpapers will receive notifications, although 375 * the default static image wallpapers do not. It is a performance optimization to 376 * set this to false. 377 * 378 * @param enabled whether the wallpaper wants to receive offset notifications 379 */ 380 public void setOffsetNotificationsEnabled(boolean enabled) { 381 mWindowPrivateFlags = enabled 382 ? (mWindowPrivateFlags | 383 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) 384 : (mWindowPrivateFlags & 385 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS); 386 if (mCreated) { 387 updateSurface(false, false, false); 388 } 389 } 390 391 /** {@hide} */ 392 public void setFixedSizeAllowed(boolean allowed) { 393 mFixedSizeAllowed = allowed; 394 } 395 396 /** 397 * Called once to initialize the engine. After returning, the 398 * engine's surface will be created by the framework. 399 */ 400 public void onCreate(SurfaceHolder surfaceHolder) { 401 } 402 403 /** 404 * Called right before the engine is going away. After this the 405 * surface will be destroyed and this Engine object is no longer 406 * valid. 407 */ 408 public void onDestroy() { 409 } 410 411 /** 412 * Called to inform you of the wallpaper becoming visible or 413 * hidden. <em>It is very important that a wallpaper only use 414 * CPU while it is visible.</em>. 415 */ 416 public void onVisibilityChanged(boolean visible) { 417 } 418 419 /** 420 * Called as the user performs touch-screen interaction with the 421 * window that is currently showing this wallpaper. Note that the 422 * events you receive here are driven by the actual application the 423 * user is interacting with, so if it is slow you will get fewer 424 * move events. 425 */ 426 public void onTouchEvent(MotionEvent event) { 427 } 428 429 /** 430 * Called to inform you of the wallpaper's offsets changing 431 * within its contain, corresponding to the container's 432 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 433 * WallpaperManager.setWallpaperOffsets()}. 434 */ 435 public void onOffsetsChanged(float xOffset, float yOffset, 436 float xOffsetStep, float yOffsetStep, 437 int xPixelOffset, int yPixelOffset) { 438 } 439 440 /** 441 * Process a command that was sent to the wallpaper with 442 * {@link WallpaperManager#sendWallpaperCommand}. 443 * The default implementation does nothing, and always returns null 444 * as the result. 445 * 446 * @param action The name of the command to perform. This tells you 447 * what to do and how to interpret the rest of the arguments. 448 * @param x Generic integer parameter. 449 * @param y Generic integer parameter. 450 * @param z Generic integer parameter. 451 * @param extras Any additional parameters. 452 * @param resultRequested If true, the caller is requesting that 453 * a result, appropriate for the command, be returned back. 454 * @return If returning a result, create a Bundle and place the 455 * result data in to it. Otherwise return null. 456 */ 457 public Bundle onCommand(String action, int x, int y, int z, 458 Bundle extras, boolean resultRequested) { 459 return null; 460 } 461 462 /** 463 * Called when an application has changed the desired virtual size of 464 * the wallpaper. 465 */ 466 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 467 } 468 469 /** 470 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 471 * SurfaceHolder.Callback.surfaceChanged()}. 472 */ 473 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 474 } 475 476 /** 477 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded 478 * SurfaceHolder.Callback.surfaceRedrawNeeded()}. 479 */ 480 public void onSurfaceRedrawNeeded(SurfaceHolder holder) { 481 } 482 483 /** 484 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 485 * SurfaceHolder.Callback.surfaceCreated()}. 486 */ 487 public void onSurfaceCreated(SurfaceHolder holder) { 488 } 489 490 /** 491 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 492 * SurfaceHolder.Callback.surfaceDestroyed()}. 493 */ 494 public void onSurfaceDestroyed(SurfaceHolder holder) { 495 } 496 497 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 498 out.print(prefix); out.print("mInitializing="); out.print(mInitializing); 499 out.print(" mDestroyed="); out.println(mDestroyed); 500 out.print(prefix); out.print("mVisible="); out.print(mVisible); 501 out.print(" mScreenOn="); out.print(mScreenOn); 502 out.print(" mReportedVisible="); out.println(mReportedVisible); 503 out.print(prefix); out.print("mCreated="); out.print(mCreated); 504 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); 505 out.print(" mIsCreating="); out.print(mIsCreating); 506 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); 507 out.print(prefix); out.print("mWidth="); out.print(mWidth); 508 out.print(" mCurWidth="); out.print(mCurWidth); 509 out.print(" mHeight="); out.print(mHeight); 510 out.print(" mCurHeight="); out.println(mCurHeight); 511 out.print(prefix); out.print("mType="); out.print(mType); 512 out.print(" mWindowFlags="); out.print(mWindowFlags); 513 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); 514 out.print(" mWindowPrivateFlags="); out.print(mWindowPrivateFlags); 515 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags); 516 out.print(prefix); out.print("mVisibleInsets="); 517 out.print(mVisibleInsets.toShortString()); 518 out.print(" mWinFrame="); out.print(mWinFrame.toShortString()); 519 out.print(" mContentInsets="); out.println(mContentInsets.toShortString()); 520 out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration); 521 out.print(prefix); out.print("mLayout="); out.println(mLayout); 522 synchronized (mLock) { 523 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); 524 out.print(" mPendingXOffset="); out.println(mPendingXOffset); 525 out.print(prefix); out.print("mPendingXOffsetStep="); 526 out.print(mPendingXOffsetStep); 527 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); 528 out.print(prefix); out.print("mOffsetMessageEnqueued="); 529 out.print(mOffsetMessageEnqueued); 530 out.print(" mPendingSync="); out.println(mPendingSync); 531 if (mPendingMove != null) { 532 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); 533 } 534 } 535 } 536 537 private void dispatchPointer(MotionEvent event) { 538 if (event.isTouchEvent()) { 539 synchronized (mLock) { 540 if (event.getAction() == MotionEvent.ACTION_MOVE) { 541 mPendingMove = event; 542 } else { 543 mPendingMove = null; 544 } 545 } 546 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 547 mCaller.sendMessage(msg); 548 } else { 549 event.recycle(); 550 } 551 } 552 553 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { 554 if (mDestroyed) { 555 Log.w(TAG, "Ignoring updateSurface: destroyed"); 556 } 557 558 int myWidth = mSurfaceHolder.getRequestedWidth(); 559 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 560 int myHeight = mSurfaceHolder.getRequestedHeight(); 561 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 562 563 final boolean creating = !mCreated; 564 final boolean surfaceCreating = !mSurfaceCreated; 565 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 566 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 567 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 568 final boolean flagsChanged = mCurWindowFlags != mWindowFlags || 569 mCurWindowPrivateFlags != mWindowPrivateFlags; 570 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 571 || typeChanged || flagsChanged || redrawNeeded) { 572 573 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 574 + " format=" + formatChanged + " size=" + sizeChanged); 575 576 try { 577 mWidth = myWidth; 578 mHeight = myHeight; 579 mFormat = mSurfaceHolder.getRequestedFormat(); 580 mType = mSurfaceHolder.getRequestedType(); 581 582 mLayout.x = 0; 583 mLayout.y = 0; 584 mLayout.width = myWidth; 585 mLayout.height = myHeight; 586 587 mLayout.format = mFormat; 588 589 mCurWindowFlags = mWindowFlags; 590 mLayout.flags = mWindowFlags 591 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 592 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 593 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 594 ; 595 mCurWindowPrivateFlags = mWindowPrivateFlags; 596 mLayout.privateFlags = mWindowPrivateFlags; 597 598 mLayout.memoryType = mType; 599 mLayout.token = mWindowToken; 600 601 if (!mCreated) { 602 mLayout.type = mIWallpaperEngine.mWindowType; 603 mLayout.gravity = Gravity.LEFT|Gravity.TOP; 604 mLayout.setTitle(WallpaperService.this.getClass().getName()); 605 mLayout.windowAnimations = 606 com.android.internal.R.style.Animation_Wallpaper; 607 mInputChannel = new InputChannel(); 608 if (mSession.add(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, mContentInsets, 609 mInputChannel) < 0) { 610 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 611 return; 612 } 613 mCreated = true; 614 615 mInputEventReceiver = new WallpaperInputEventReceiver( 616 mInputChannel, Looper.myLooper()); 617 } 618 619 mSurfaceHolder.mSurfaceLock.lock(); 620 mDrawingAllowed = true; 621 622 final int relayoutResult = mSession.relayout( 623 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 624 View.VISIBLE, 0, mWinFrame, mSystemInsets, mContentInsets, 625 mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface); 626 627 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 628 + ", frame=" + mWinFrame); 629 630 int w = mWinFrame.width(); 631 if (mCurWidth != w) { 632 sizeChanged = true; 633 mCurWidth = w; 634 } 635 int h = mWinFrame.height(); 636 if (mCurHeight != h) { 637 sizeChanged = true; 638 mCurHeight = h; 639 } 640 641 mSurfaceHolder.setSurfaceFrameSize(w, h); 642 mSurfaceHolder.mSurfaceLock.unlock(); 643 644 if (!mSurfaceHolder.mSurface.isValid()) { 645 reportSurfaceDestroyed(); 646 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 647 return; 648 } 649 650 boolean didSurface = false; 651 652 try { 653 mSurfaceHolder.ungetCallbacks(); 654 655 if (surfaceCreating) { 656 mIsCreating = true; 657 didSurface = true; 658 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 659 + mSurfaceHolder + "): " + this); 660 onSurfaceCreated(mSurfaceHolder); 661 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 662 if (callbacks != null) { 663 for (SurfaceHolder.Callback c : callbacks) { 664 c.surfaceCreated(mSurfaceHolder); 665 } 666 } 667 } 668 669 redrawNeeded |= creating 670 || (relayoutResult&WindowManagerImpl.RELAYOUT_RES_FIRST_TIME) != 0; 671 672 if (forceReport || creating || surfaceCreating 673 || formatChanged || sizeChanged) { 674 if (DEBUG) { 675 RuntimeException e = new RuntimeException(); 676 e.fillInStackTrace(); 677 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 678 + " formatChanged=" + formatChanged 679 + " sizeChanged=" + sizeChanged, e); 680 } 681 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 682 + mSurfaceHolder + ", " + mFormat 683 + ", " + mCurWidth + ", " + mCurHeight 684 + "): " + this); 685 didSurface = true; 686 onSurfaceChanged(mSurfaceHolder, mFormat, 687 mCurWidth, mCurHeight); 688 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 689 if (callbacks != null) { 690 for (SurfaceHolder.Callback c : callbacks) { 691 c.surfaceChanged(mSurfaceHolder, mFormat, 692 mCurWidth, mCurHeight); 693 } 694 } 695 } 696 697 if (redrawNeeded) { 698 onSurfaceRedrawNeeded(mSurfaceHolder); 699 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 700 if (callbacks != null) { 701 for (SurfaceHolder.Callback c : callbacks) { 702 if (c instanceof SurfaceHolder.Callback2) { 703 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 704 mSurfaceHolder); 705 } 706 } 707 } 708 } 709 710 if (didSurface && !mReportedVisible) { 711 // This wallpaper is currently invisible, but its 712 // surface has changed. At this point let's tell it 713 // again that it is invisible in case the report about 714 // the surface caused it to start running. We really 715 // don't want wallpapers running when not visible. 716 if (mIsCreating) { 717 // Some wallpapers will ignore this call if they 718 // had previously been told they were invisble, 719 // so if we are creating a new surface then toggle 720 // the state to get them to notice. 721 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 722 + this); 723 onVisibilityChanged(true); 724 } 725 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 726 + this); 727 onVisibilityChanged(false); 728 } 729 730 } finally { 731 mIsCreating = false; 732 mSurfaceCreated = true; 733 if (redrawNeeded) { 734 mSession.finishDrawing(mWindow); 735 } 736 } 737 } catch (RemoteException ex) { 738 } 739 if (DEBUG) Log.v( 740 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 741 " w=" + mLayout.width + " h=" + mLayout.height); 742 } 743 } 744 745 void attach(IWallpaperEngineWrapper wrapper) { 746 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 747 if (mDestroyed) { 748 return; 749 } 750 751 mIWallpaperEngine = wrapper; 752 mCaller = wrapper.mCaller; 753 mConnection = wrapper.mConnection; 754 mWindowToken = wrapper.mWindowToken; 755 mSurfaceHolder.setSizeFromLayout(); 756 mInitializing = true; 757 mSession = ViewRootImpl.getWindowSession(getMainLooper()); 758 759 mWindow.setSession(mSession); 760 761 mScreenOn = ((PowerManager)getSystemService(Context.POWER_SERVICE)).isScreenOn(); 762 763 IntentFilter filter = new IntentFilter(); 764 filter.addAction(Intent.ACTION_SCREEN_ON); 765 filter.addAction(Intent.ACTION_SCREEN_OFF); 766 registerReceiver(mReceiver, filter); 767 768 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 769 onCreate(mSurfaceHolder); 770 771 mInitializing = false; 772 mReportedVisible = false; 773 updateSurface(false, false, false); 774 } 775 776 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 777 if (!mDestroyed) { 778 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 779 + desiredWidth + "," + desiredHeight + "): " + this); 780 mIWallpaperEngine.mReqWidth = desiredWidth; 781 mIWallpaperEngine.mReqHeight = desiredHeight; 782 onDesiredSizeChanged(desiredWidth, desiredHeight); 783 doOffsetsChanged(true); 784 } 785 } 786 787 void doVisibilityChanged(boolean visible) { 788 if (!mDestroyed) { 789 mVisible = visible; 790 reportVisibility(); 791 } 792 } 793 794 void reportVisibility() { 795 if (!mDestroyed) { 796 boolean visible = mVisible && mScreenOn; 797 if (mReportedVisible != visible) { 798 mReportedVisible = visible; 799 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 800 + "): " + this); 801 if (visible) { 802 // If becoming visible, in preview mode the surface 803 // may have been destroyed so now we need to make 804 // sure it is re-created. 805 doOffsetsChanged(false); 806 updateSurface(false, false, false); 807 } 808 onVisibilityChanged(visible); 809 } 810 } 811 } 812 813 void doOffsetsChanged(boolean always) { 814 if (mDestroyed) { 815 return; 816 } 817 818 if (!always && !mOffsetsChanged) { 819 return; 820 } 821 822 float xOffset; 823 float yOffset; 824 float xOffsetStep; 825 float yOffsetStep; 826 boolean sync; 827 synchronized (mLock) { 828 xOffset = mPendingXOffset; 829 yOffset = mPendingYOffset; 830 xOffsetStep = mPendingXOffsetStep; 831 yOffsetStep = mPendingYOffsetStep; 832 sync = mPendingSync; 833 mPendingSync = false; 834 mOffsetMessageEnqueued = false; 835 } 836 837 if (mSurfaceCreated) { 838 if (mReportedVisible) { 839 if (DEBUG) Log.v(TAG, "Offsets change in " + this 840 + ": " + xOffset + "," + yOffset); 841 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 842 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 843 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 844 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 845 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 846 } else { 847 mOffsetsChanged = true; 848 } 849 } 850 851 if (sync) { 852 try { 853 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 854 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 855 } catch (RemoteException e) { 856 } 857 } 858 } 859 860 void doCommand(WallpaperCommand cmd) { 861 Bundle result; 862 if (!mDestroyed) { 863 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 864 cmd.extras, cmd.sync); 865 } else { 866 result = null; 867 } 868 if (cmd.sync) { 869 try { 870 if (DEBUG) Log.v(TAG, "Reporting command complete"); 871 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 872 } catch (RemoteException e) { 873 } 874 } 875 } 876 877 void reportSurfaceDestroyed() { 878 if (mSurfaceCreated) { 879 mSurfaceCreated = false; 880 mSurfaceHolder.ungetCallbacks(); 881 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 882 if (callbacks != null) { 883 for (SurfaceHolder.Callback c : callbacks) { 884 c.surfaceDestroyed(mSurfaceHolder); 885 } 886 } 887 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 888 + mSurfaceHolder + "): " + this); 889 onSurfaceDestroyed(mSurfaceHolder); 890 } 891 } 892 893 void detach() { 894 if (mDestroyed) { 895 return; 896 } 897 898 mDestroyed = true; 899 900 if (mVisible) { 901 mVisible = false; 902 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 903 onVisibilityChanged(false); 904 } 905 906 reportSurfaceDestroyed(); 907 908 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 909 onDestroy(); 910 911 unregisterReceiver(mReceiver); 912 913 if (mCreated) { 914 try { 915 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 916 + mSurfaceHolder.getSurface() + " of: " + this); 917 918 if (mInputEventReceiver != null) { 919 mInputEventReceiver.dispose(); 920 mInputEventReceiver = null; 921 } 922 923 mSession.remove(mWindow); 924 } catch (RemoteException e) { 925 } 926 mSurfaceHolder.mSurface.release(); 927 mCreated = false; 928 929 // Dispose the input channel after removing the window so the Window Manager 930 // doesn't interpret the input channel being closed as an abnormal termination. 931 if (mInputChannel != null) { 932 mInputChannel.dispose(); 933 mInputChannel = null; 934 } 935 } 936 } 937 } 938 939 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 940 implements HandlerCaller.Callback { 941 private final HandlerCaller mCaller; 942 943 final IWallpaperConnection mConnection; 944 final IBinder mWindowToken; 945 final int mWindowType; 946 final boolean mIsPreview; 947 int mReqWidth; 948 int mReqHeight; 949 950 Engine mEngine; 951 952 IWallpaperEngineWrapper(WallpaperService context, 953 IWallpaperConnection conn, IBinder windowToken, 954 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 955 if (DEBUG && mCallbackLooper != null) { 956 mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG)); 957 } 958 mCaller = new HandlerCaller(context, 959 mCallbackLooper != null 960 ? mCallbackLooper : context.getMainLooper(), 961 this); 962 mConnection = conn; 963 mWindowToken = windowToken; 964 mWindowType = windowType; 965 mIsPreview = isPreview; 966 mReqWidth = reqWidth; 967 mReqHeight = reqHeight; 968 969 Message msg = mCaller.obtainMessage(DO_ATTACH); 970 mCaller.sendMessage(msg); 971 } 972 973 public void setDesiredSize(int width, int height) { 974 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 975 mCaller.sendMessage(msg); 976 } 977 978 public void setVisibility(boolean visible) { 979 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 980 visible ? 1 : 0); 981 mCaller.sendMessage(msg); 982 } 983 984 public void dispatchPointer(MotionEvent event) { 985 if (mEngine != null) { 986 mEngine.dispatchPointer(event); 987 } else { 988 event.recycle(); 989 } 990 } 991 992 public void dispatchWallpaperCommand(String action, int x, int y, 993 int z, Bundle extras) { 994 if (mEngine != null) { 995 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 996 } 997 } 998 999 public void destroy() { 1000 Message msg = mCaller.obtainMessage(DO_DETACH); 1001 mCaller.sendMessage(msg); 1002 } 1003 1004 public void executeMessage(Message message) { 1005 switch (message.what) { 1006 case DO_ATTACH: { 1007 try { 1008 mConnection.attachEngine(this); 1009 } catch (RemoteException e) { 1010 Log.w(TAG, "Wallpaper host disappeared", e); 1011 return; 1012 } 1013 Engine engine = onCreateEngine(); 1014 mEngine = engine; 1015 mActiveEngines.add(engine); 1016 engine.attach(this); 1017 return; 1018 } 1019 case DO_DETACH: { 1020 mActiveEngines.remove(mEngine); 1021 mEngine.detach(); 1022 return; 1023 } 1024 case DO_SET_DESIRED_SIZE: { 1025 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 1026 return; 1027 } 1028 case MSG_UPDATE_SURFACE: 1029 mEngine.updateSurface(true, false, false); 1030 break; 1031 case MSG_VISIBILITY_CHANGED: 1032 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 1033 + ": " + message.arg1); 1034 mEngine.doVisibilityChanged(message.arg1 != 0); 1035 break; 1036 case MSG_WALLPAPER_OFFSETS: { 1037 mEngine.doOffsetsChanged(true); 1038 } break; 1039 case MSG_WALLPAPER_COMMAND: { 1040 WallpaperCommand cmd = (WallpaperCommand)message.obj; 1041 mEngine.doCommand(cmd); 1042 } break; 1043 case MSG_WINDOW_RESIZED: { 1044 final boolean reportDraw = message.arg1 != 0; 1045 mEngine.updateSurface(true, false, reportDraw); 1046 mEngine.doOffsetsChanged(true); 1047 } break; 1048 case MSG_TOUCH_EVENT: { 1049 boolean skip = false; 1050 MotionEvent ev = (MotionEvent)message.obj; 1051 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 1052 synchronized (mEngine.mLock) { 1053 if (mEngine.mPendingMove == ev) { 1054 mEngine.mPendingMove = null; 1055 } else { 1056 // this is not the motion event we are looking for.... 1057 skip = true; 1058 } 1059 } 1060 } 1061 if (!skip) { 1062 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 1063 mEngine.onTouchEvent(ev); 1064 } 1065 ev.recycle(); 1066 } break; 1067 default : 1068 Log.w(TAG, "Unknown message type " + message.what); 1069 } 1070 } 1071 } 1072 1073 /** 1074 * Implements the internal {@link IWallpaperService} interface to convert 1075 * incoming calls to it back to calls on an {@link WallpaperService}. 1076 */ 1077 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 1078 private final WallpaperService mTarget; 1079 1080 public IWallpaperServiceWrapper(WallpaperService context) { 1081 mTarget = context; 1082 } 1083 1084 public void attach(IWallpaperConnection conn, IBinder windowToken, 1085 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 1086 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 1087 windowType, isPreview, reqWidth, reqHeight); 1088 } 1089 } 1090 1091 @Override 1092 public void onCreate() { 1093 super.onCreate(); 1094 } 1095 1096 @Override 1097 public void onDestroy() { 1098 super.onDestroy(); 1099 for (int i=0; i<mActiveEngines.size(); i++) { 1100 mActiveEngines.get(i).detach(); 1101 } 1102 mActiveEngines.clear(); 1103 } 1104 1105 /** 1106 * Implement to return the implementation of the internal accessibility 1107 * service interface. Subclasses should not override. 1108 */ 1109 @Override 1110 public final IBinder onBind(Intent intent) { 1111 return new IWallpaperServiceWrapper(this); 1112 } 1113 1114 /** 1115 * This allows subclasses to change the thread that most callbacks 1116 * occur on. Currently hidden because it is mostly needed for the 1117 * image wallpaper (which runs in the system process and doesn't want 1118 * to get stuck running on that seriously in use main thread). Not 1119 * exposed right now because the semantics of this are not totally 1120 * well defined and some callbacks can still happen on the main thread). 1121 * @hide 1122 */ 1123 public void setCallbackLooper(Looper looper) { 1124 mCallbackLooper = looper; 1125 } 1126 1127 /** 1128 * Must be implemented to return a new instance of the wallpaper's engine. 1129 * Note that multiple instances may be active at the same time, such as 1130 * when the wallpaper is currently set as the active wallpaper and the user 1131 * is in the wallpaper picker viewing a preview of it as well. 1132 */ 1133 public abstract Engine onCreateEngine(); 1134 1135 @Override 1136 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 1137 out.print("State of wallpaper "); out.print(this); out.println(":"); 1138 for (int i=0; i<mActiveEngines.size(); i++) { 1139 Engine engine = mActiveEngines.get(i); 1140 out.print(" Engine "); out.print(engine); out.println(":"); 1141 engine.dump(" ", fd, out, args); 1142 } 1143 } 1144} 1145