1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.wallpaper; 18 19 import android.annotation.Nullable; 20 import android.annotation.SdkConstant; 21 import android.annotation.SdkConstant.SdkConstantType; 22 import android.app.Service; 23 import android.app.WallpaperColors; 24 import android.app.WallpaperManager; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.res.TypedArray; 28 import android.graphics.Bitmap; 29 import android.graphics.Canvas; 30 import android.graphics.PixelFormat; 31 import android.graphics.Rect; 32 import android.graphics.drawable.Drawable; 33 import android.hardware.display.DisplayManager; 34 import android.hardware.display.DisplayManager.DisplayListener; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.IBinder; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.RemoteException; 41 import android.os.SystemClock; 42 import android.util.Log; 43 import android.util.MergedConfiguration; 44 import android.view.Display; 45 import android.view.DisplayCutout; 46 import android.view.Gravity; 47 import android.view.IWindowSession; 48 import android.view.InputChannel; 49 import android.view.InputDevice; 50 import android.view.InputEvent; 51 import android.view.InputEventReceiver; 52 import android.view.MotionEvent; 53 import android.view.SurfaceHolder; 54 import android.view.View; 55 import android.view.ViewGroup; 56 import android.view.WindowInsets; 57 import android.view.WindowManager; 58 import android.view.WindowManagerGlobal; 59 60 import com.android.internal.annotations.VisibleForTesting; 61 import com.android.internal.os.HandlerCaller; 62 import com.android.internal.view.BaseIWindow; 63 import com.android.internal.view.BaseSurfaceHolder; 64 65 import java.io.FileDescriptor; 66 import java.io.PrintWriter; 67 import java.util.ArrayList; 68 import java.util.function.Supplier; 69 70 /** 71 * A wallpaper service is responsible for showing a live wallpaper behind 72 * applications that would like to sit on top of it. This service object 73 * itself does very little -- its only purpose is to generate instances of 74 * {@link Engine} as needed. Implementing a wallpaper thus 75 * involves subclassing from this, subclassing an Engine implementation, 76 * and implementing {@link #onCreateEngine()} to return a new instance of 77 * your engine. 78 */ 79 public abstract class WallpaperService extends Service { 80 /** 81 * The {@link Intent} that must be declared as handled by the service. 82 * To be supported, the service must also require the 83 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so 84 * that other applications can not abuse it. 85 */ 86 @SdkConstant(SdkConstantType.SERVICE_ACTION) 87 public static final String SERVICE_INTERFACE = 88 "android.service.wallpaper.WallpaperService"; 89 90 /** 91 * Name under which a WallpaperService component publishes information 92 * about itself. This meta-data must reference an XML resource containing 93 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 94 * tag. 95 */ 96 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 97 98 static final String TAG = "WallpaperService"; 99 static final boolean DEBUG = false; 100 101 private static final int DO_ATTACH = 10; 102 private static final int DO_DETACH = 20; 103 private static final int DO_SET_DESIRED_SIZE = 30; 104 private static final int DO_SET_DISPLAY_PADDING = 40; 105 private static final int DO_IN_AMBIENT_MODE = 50; 106 107 private static final int MSG_UPDATE_SURFACE = 10000; 108 private static final int MSG_VISIBILITY_CHANGED = 10010; 109 private static final int MSG_WALLPAPER_OFFSETS = 10020; 110 private static final int MSG_WALLPAPER_COMMAND = 10025; 111 private static final int MSG_WINDOW_RESIZED = 10030; 112 private static final int MSG_WINDOW_MOVED = 10035; 113 private static final int MSG_TOUCH_EVENT = 10040; 114 private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; 115 116 private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; 117 118 private final ArrayList<Engine> mActiveEngines 119 = new ArrayList<Engine>(); 120 121 static final class WallpaperCommand { 122 String action; 123 int x; 124 int y; 125 int z; 126 Bundle extras; 127 boolean sync; 128 } 129 130 /** 131 * The actual implementation of a wallpaper. A wallpaper service may 132 * have multiple instances running (for example as a real wallpaper 133 * and as a preview), each of which is represented by its own Engine 134 * instance. You must implement {@link WallpaperService#onCreateEngine()} 135 * to return your concrete Engine implementation. 136 */ 137 public class Engine { 138 IWallpaperEngineWrapper mIWallpaperEngine; 139 140 // Copies from mIWallpaperEngine. 141 HandlerCaller mCaller; 142 IWallpaperConnection mConnection; 143 IBinder mWindowToken; 144 145 boolean mInitializing = true; 146 boolean mVisible; 147 boolean mReportedVisible; 148 boolean mDestroyed; 149 150 // Current window state. 151 boolean mCreated; 152 boolean mSurfaceCreated; 153 boolean mIsCreating; 154 boolean mDrawingAllowed; 155 boolean mOffsetsChanged; 156 boolean mFixedSizeAllowed; 157 int mWidth; 158 int mHeight; 159 int mFormat; 160 int mType; 161 int mCurWidth; 162 int mCurHeight; 163 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 164 int mWindowPrivateFlags = 165 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; 166 int mCurWindowFlags = mWindowFlags; 167 int mCurWindowPrivateFlags = mWindowPrivateFlags; 168 final Rect mVisibleInsets = new Rect(); 169 final Rect mWinFrame = new Rect(); 170 final Rect mOverscanInsets = new Rect(); 171 final Rect mContentInsets = new Rect(); 172 final Rect mStableInsets = new Rect(); 173 final Rect mOutsets = new Rect(); 174 final Rect mDispatchedOverscanInsets = new Rect(); 175 final Rect mDispatchedContentInsets = new Rect(); 176 final Rect mDispatchedStableInsets = new Rect(); 177 final Rect mDispatchedOutsets = new Rect(); 178 final Rect mFinalSystemInsets = new Rect(); 179 final Rect mFinalStableInsets = new Rect(); 180 final Rect mBackdropFrame = new Rect(); 181 final DisplayCutout.ParcelableWrapper mDisplayCutout = 182 new DisplayCutout.ParcelableWrapper(); 183 DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT; 184 final MergedConfiguration mMergedConfiguration = new MergedConfiguration(); 185 186 final WindowManager.LayoutParams mLayout 187 = new WindowManager.LayoutParams(); 188 IWindowSession mSession; 189 InputChannel mInputChannel; 190 191 final Object mLock = new Object(); 192 boolean mOffsetMessageEnqueued; 193 float mPendingXOffset; 194 float mPendingYOffset; 195 float mPendingXOffsetStep; 196 float mPendingYOffsetStep; 197 boolean mPendingSync; 198 MotionEvent mPendingMove; 199 boolean mIsInAmbientMode; 200 201 // Needed for throttling onComputeColors. 202 private long mLastColorInvalidation; 203 private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; 204 private final Supplier<Long> mClockFunction; 205 private final Handler mHandler; 206 207 DisplayManager mDisplayManager; 208 Display mDisplay; 209 private int mDisplayState; 210 211 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 212 { 213 mRequestedFormat = PixelFormat.RGBX_8888; 214 } 215 216 @Override 217 public boolean onAllowLockCanvas() { 218 return mDrawingAllowed; 219 } 220 221 @Override 222 public void onRelayoutContainer() { 223 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 224 mCaller.sendMessage(msg); 225 } 226 227 @Override 228 public void onUpdateSurface() { 229 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 230 mCaller.sendMessage(msg); 231 } 232 233 public boolean isCreating() { 234 return mIsCreating; 235 } 236 237 @Override 238 public void setFixedSize(int width, int height) { 239 if (!mFixedSizeAllowed) { 240 // Regular apps can't do this. It can only work for 241 // certain designs of window animations, so you can't 242 // rely on it. 243 throw new UnsupportedOperationException( 244 "Wallpapers currently only support sizing from layout"); 245 } 246 super.setFixedSize(width, height); 247 } 248 249 public void setKeepScreenOn(boolean screenOn) { 250 throw new UnsupportedOperationException( 251 "Wallpapers do not support keep screen on"); 252 } 253 254 private void prepareToDraw() { 255 if (mDisplayState == Display.STATE_DOZE 256 || mDisplayState == Display.STATE_DOZE_SUSPEND) { 257 try { 258 mSession.pokeDrawLock(mWindow); 259 } catch (RemoteException e) { 260 // System server died, can be ignored. 261 } 262 } 263 } 264 265 @Override 266 public Canvas lockCanvas() { 267 prepareToDraw(); 268 return super.lockCanvas(); 269 } 270 271 @Override 272 public Canvas lockCanvas(Rect dirty) { 273 prepareToDraw(); 274 return super.lockCanvas(dirty); 275 } 276 277 @Override 278 public Canvas lockHardwareCanvas() { 279 prepareToDraw(); 280 return super.lockHardwareCanvas(); 281 } 282 }; 283 284 final class WallpaperInputEventReceiver extends InputEventReceiver { 285 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) { 286 super(inputChannel, looper); 287 } 288 289 @Override 290 public void onInputEvent(InputEvent event, int displayId) { 291 boolean handled = false; 292 try { 293 if (event instanceof MotionEvent 294 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 295 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event); 296 dispatchPointer(dup); 297 handled = true; 298 } 299 } finally { 300 finishInputEvent(event, handled); 301 } 302 } 303 } 304 WallpaperInputEventReceiver mInputEventReceiver; 305 306 final BaseIWindow mWindow = new BaseIWindow() { 307 @Override 308 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, 309 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, 310 MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout, 311 boolean alwaysConsumeNavBar, int displayId, 312 DisplayCutout.ParcelableWrapper displayCutout) { 313 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED, 314 reportDraw ? 1 : 0, outsets); 315 mCaller.sendMessage(msg); 316 } 317 318 @Override 319 public void moved(int newX, int newY) { 320 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY); 321 mCaller.sendMessage(msg); 322 } 323 324 @Override 325 public void dispatchAppVisibility(boolean visible) { 326 // We don't do this in preview mode; we'll let the preview 327 // activity tell us when to run. 328 if (!mIWallpaperEngine.mIsPreview) { 329 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 330 visible ? 1 : 0); 331 mCaller.sendMessage(msg); 332 } 333 } 334 335 @Override 336 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 337 boolean sync) { 338 synchronized (mLock) { 339 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 340 mPendingXOffset = x; 341 mPendingYOffset = y; 342 mPendingXOffsetStep = xStep; 343 mPendingYOffsetStep = yStep; 344 if (sync) { 345 mPendingSync = true; 346 } 347 if (!mOffsetMessageEnqueued) { 348 mOffsetMessageEnqueued = true; 349 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 350 mCaller.sendMessage(msg); 351 } 352 } 353 } 354 355 @Override 356 public void dispatchWallpaperCommand(String action, int x, int y, 357 int z, Bundle extras, boolean sync) { 358 synchronized (mLock) { 359 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 360 WallpaperCommand cmd = new WallpaperCommand(); 361 cmd.action = action; 362 cmd.x = x; 363 cmd.y = y; 364 cmd.z = z; 365 cmd.extras = extras; 366 cmd.sync = sync; 367 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 368 msg.obj = cmd; 369 mCaller.sendMessage(msg); 370 } 371 } 372 }; 373 374 /** 375 * Default constructor 376 */ 377 public Engine() { 378 this(SystemClock::elapsedRealtime, Handler.getMain()); 379 } 380 381 /** 382 * Constructor used for test purposes. 383 * 384 * @param clockFunction Supplies current times in millis. 385 * @param handler Used for posting/deferring asynchronous calls. 386 * @hide 387 */ 388 @VisibleForTesting 389 public Engine(Supplier<Long> clockFunction, Handler handler) { 390 mClockFunction = clockFunction; 391 mHandler = handler; 392 } 393 394 /** 395 * Provides access to the surface in which this wallpaper is drawn. 396 */ 397 public SurfaceHolder getSurfaceHolder() { 398 return mSurfaceHolder; 399 } 400 401 /** 402 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 403 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 404 * that the system would like this wallpaper to run in. 405 */ 406 public int getDesiredMinimumWidth() { 407 return mIWallpaperEngine.mReqWidth; 408 } 409 410 /** 411 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 412 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 413 * that the system would like this wallpaper to run in. 414 */ 415 public int getDesiredMinimumHeight() { 416 return mIWallpaperEngine.mReqHeight; 417 } 418 419 /** 420 * Return whether the wallpaper is currently visible to the user, 421 * this is the last value supplied to 422 * {@link #onVisibilityChanged(boolean)}. 423 */ 424 public boolean isVisible() { 425 return mReportedVisible; 426 } 427 428 /** 429 * Returns true if this engine is running in preview mode -- that is, 430 * it is being shown to the user before they select it as the actual 431 * wallpaper. 432 */ 433 public boolean isPreview() { 434 return mIWallpaperEngine.mIsPreview; 435 } 436 437 /** 438 * Returns true if this engine is running in ambient mode -- that is, 439 * it is being shown in low power mode, in always on display. 440 * @hide 441 */ 442 public boolean isInAmbientMode() { 443 return mIsInAmbientMode; 444 } 445 446 /** 447 * Control whether this wallpaper will receive raw touch events 448 * from the window manager as the user interacts with the window 449 * that is currently displaying the wallpaper. By default they 450 * are turned off. If enabled, the events will be received in 451 * {@link #onTouchEvent(MotionEvent)}. 452 */ 453 public void setTouchEventsEnabled(boolean enabled) { 454 mWindowFlags = enabled 455 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 456 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 457 if (mCreated) { 458 updateSurface(false, false, false); 459 } 460 } 461 462 /** 463 * Control whether this wallpaper will receive notifications when the wallpaper 464 * has been scrolled. By default, wallpapers will receive notifications, although 465 * the default static image wallpapers do not. It is a performance optimization to 466 * set this to false. 467 * 468 * @param enabled whether the wallpaper wants to receive offset notifications 469 */ 470 public void setOffsetNotificationsEnabled(boolean enabled) { 471 mWindowPrivateFlags = enabled 472 ? (mWindowPrivateFlags | 473 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) 474 : (mWindowPrivateFlags & 475 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS); 476 if (mCreated) { 477 updateSurface(false, false, false); 478 } 479 } 480 481 /** {@hide} */ 482 public void setFixedSizeAllowed(boolean allowed) { 483 mFixedSizeAllowed = allowed; 484 } 485 486 /** 487 * Called once to initialize the engine. After returning, the 488 * engine's surface will be created by the framework. 489 */ 490 public void onCreate(SurfaceHolder surfaceHolder) { 491 } 492 493 /** 494 * Called right before the engine is going away. After this the 495 * surface will be destroyed and this Engine object is no longer 496 * valid. 497 */ 498 public void onDestroy() { 499 } 500 501 /** 502 * Called to inform you of the wallpaper becoming visible or 503 * hidden. <em>It is very important that a wallpaper only use 504 * CPU while it is visible.</em>. 505 */ 506 public void onVisibilityChanged(boolean visible) { 507 } 508 509 /** 510 * Called with the current insets that are in effect for the wallpaper. 511 * This gives you the part of the overall wallpaper surface that will 512 * generally be visible to the user (ignoring position offsets applied to it). 513 * 514 * @param insets Insets to apply. 515 */ 516 public void onApplyWindowInsets(WindowInsets insets) { 517 } 518 519 /** 520 * Called as the user performs touch-screen interaction with the 521 * window that is currently showing this wallpaper. Note that the 522 * events you receive here are driven by the actual application the 523 * user is interacting with, so if it is slow you will get fewer 524 * move events. 525 */ 526 public void onTouchEvent(MotionEvent event) { 527 } 528 529 /** 530 * Called to inform you of the wallpaper's offsets changing 531 * within its contain, corresponding to the container's 532 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 533 * WallpaperManager.setWallpaperOffsets()}. 534 */ 535 public void onOffsetsChanged(float xOffset, float yOffset, 536 float xOffsetStep, float yOffsetStep, 537 int xPixelOffset, int yPixelOffset) { 538 } 539 540 /** 541 * Process a command that was sent to the wallpaper with 542 * {@link WallpaperManager#sendWallpaperCommand}. 543 * The default implementation does nothing, and always returns null 544 * as the result. 545 * 546 * @param action The name of the command to perform. This tells you 547 * what to do and how to interpret the rest of the arguments. 548 * @param x Generic integer parameter. 549 * @param y Generic integer parameter. 550 * @param z Generic integer parameter. 551 * @param extras Any additional parameters. 552 * @param resultRequested If true, the caller is requesting that 553 * a result, appropriate for the command, be returned back. 554 * @return If returning a result, create a Bundle and place the 555 * result data in to it. Otherwise return null. 556 */ 557 public Bundle onCommand(String action, int x, int y, int z, 558 Bundle extras, boolean resultRequested) { 559 return null; 560 } 561 562 /** 563 * Called when the device enters or exits ambient mode. 564 * 565 * @param inAmbientMode {@code true} if in ambient mode. 566 * @param animated {@code true} if you'll have te opportunity of animating your transition 567 * {@code false} when the screen will blank and the wallpaper should be 568 * set to ambient mode immediately. 569 * @hide 570 */ 571 public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) { 572 } 573 574 /** 575 * Called when an application has changed the desired virtual size of 576 * the wallpaper. 577 */ 578 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 579 } 580 581 /** 582 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 583 * SurfaceHolder.Callback.surfaceChanged()}. 584 */ 585 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 586 } 587 588 /** 589 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded 590 * SurfaceHolder.Callback.surfaceRedrawNeeded()}. 591 */ 592 public void onSurfaceRedrawNeeded(SurfaceHolder holder) { 593 } 594 595 /** 596 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 597 * SurfaceHolder.Callback.surfaceCreated()}. 598 */ 599 public void onSurfaceCreated(SurfaceHolder holder) { 600 } 601 602 /** 603 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 604 * SurfaceHolder.Callback.surfaceDestroyed()}. 605 */ 606 public void onSurfaceDestroyed(SurfaceHolder holder) { 607 } 608 609 /** 610 * Notifies the engine that wallpaper colors changed significantly. 611 * This will trigger a {@link #onComputeColors()} call. 612 */ 613 public void notifyColorsChanged() { 614 final long now = mClockFunction.get(); 615 if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) { 616 Log.w(TAG, "This call has been deferred. You should only call " 617 + "notifyColorsChanged() once every " 618 + (NOTIFY_COLORS_RATE_LIMIT_MS / 1000f) + " seconds."); 619 if (!mHandler.hasCallbacks(mNotifyColorsChanged)) { 620 mHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS); 621 } 622 return; 623 } 624 mLastColorInvalidation = now; 625 mHandler.removeCallbacks(mNotifyColorsChanged); 626 627 try { 628 final WallpaperColors newColors = onComputeColors(); 629 if (mConnection != null) { 630 mConnection.onWallpaperColorsChanged(newColors); 631 } else { 632 Log.w(TAG, "Can't notify system because wallpaper connection " 633 + "was not established."); 634 } 635 } catch (RemoteException e) { 636 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); 637 } 638 } 639 640 /** 641 * Called by the system when it needs to know what colors the wallpaper is using. 642 * You might return null if no color information is available at the moment. 643 * In that case you might want to call {@link #notifyColorsChanged()} when 644 * color information becomes available. 645 * <p> 646 * The simplest way of creating a {@link android.app.WallpaperColors} object is by using 647 * {@link android.app.WallpaperColors#fromBitmap(Bitmap)} or 648 * {@link android.app.WallpaperColors#fromDrawable(Drawable)}, but you can also specify 649 * your main colors by constructing a {@link android.app.WallpaperColors} object manually. 650 * 651 * @return Wallpaper colors. 652 */ 653 public @Nullable WallpaperColors onComputeColors() { 654 return null; 655 } 656 657 /** 658 * Sets internal engine state. Only for testing. 659 * @param created {@code true} or {@code false}. 660 * @hide 661 */ 662 @VisibleForTesting 663 public void setCreated(boolean created) { 664 mCreated = created; 665 } 666 667 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 668 out.print(prefix); out.print("mInitializing="); out.print(mInitializing); 669 out.print(" mDestroyed="); out.println(mDestroyed); 670 out.print(prefix); out.print("mVisible="); out.print(mVisible); 671 out.print(" mReportedVisible="); out.println(mReportedVisible); 672 out.print(prefix); out.print("mDisplay="); out.println(mDisplay); 673 out.print(prefix); out.print("mCreated="); out.print(mCreated); 674 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); 675 out.print(" mIsCreating="); out.print(mIsCreating); 676 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); 677 out.print(prefix); out.print("mWidth="); out.print(mWidth); 678 out.print(" mCurWidth="); out.print(mCurWidth); 679 out.print(" mHeight="); out.print(mHeight); 680 out.print(" mCurHeight="); out.println(mCurHeight); 681 out.print(prefix); out.print("mType="); out.print(mType); 682 out.print(" mWindowFlags="); out.print(mWindowFlags); 683 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); 684 out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags); 685 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags); 686 out.print(prefix); out.print("mVisibleInsets="); 687 out.print(mVisibleInsets.toShortString()); 688 out.print(" mWinFrame="); out.print(mWinFrame.toShortString()); 689 out.print(" mContentInsets="); out.println(mContentInsets.toShortString()); 690 out.print(prefix); out.print("mConfiguration="); 691 out.println(mMergedConfiguration.getMergedConfiguration()); 692 out.print(prefix); out.print("mLayout="); out.println(mLayout); 693 synchronized (mLock) { 694 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); 695 out.print(" mPendingXOffset="); out.println(mPendingXOffset); 696 out.print(prefix); out.print("mPendingXOffsetStep="); 697 out.print(mPendingXOffsetStep); 698 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); 699 out.print(prefix); out.print("mOffsetMessageEnqueued="); 700 out.print(mOffsetMessageEnqueued); 701 out.print(" mPendingSync="); out.println(mPendingSync); 702 if (mPendingMove != null) { 703 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); 704 } 705 } 706 } 707 708 private void dispatchPointer(MotionEvent event) { 709 if (event.isTouchEvent()) { 710 synchronized (mLock) { 711 if (event.getAction() == MotionEvent.ACTION_MOVE) { 712 mPendingMove = event; 713 } else { 714 mPendingMove = null; 715 } 716 } 717 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 718 mCaller.sendMessage(msg); 719 } else { 720 event.recycle(); 721 } 722 } 723 724 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { 725 if (mDestroyed) { 726 Log.w(TAG, "Ignoring updateSurface: destroyed"); 727 } 728 729 boolean fixedSize = false; 730 int myWidth = mSurfaceHolder.getRequestedWidth(); 731 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 732 else fixedSize = true; 733 int myHeight = mSurfaceHolder.getRequestedHeight(); 734 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 735 else fixedSize = true; 736 737 final boolean creating = !mCreated; 738 final boolean surfaceCreating = !mSurfaceCreated; 739 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 740 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 741 boolean insetsChanged = !mCreated; 742 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 743 final boolean flagsChanged = mCurWindowFlags != mWindowFlags || 744 mCurWindowPrivateFlags != mWindowPrivateFlags; 745 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 746 || typeChanged || flagsChanged || redrawNeeded 747 || !mIWallpaperEngine.mShownReported) { 748 749 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 750 + " format=" + formatChanged + " size=" + sizeChanged); 751 752 try { 753 mWidth = myWidth; 754 mHeight = myHeight; 755 mFormat = mSurfaceHolder.getRequestedFormat(); 756 mType = mSurfaceHolder.getRequestedType(); 757 758 mLayout.x = 0; 759 mLayout.y = 0; 760 mLayout.width = myWidth; 761 mLayout.height = myHeight; 762 763 mLayout.format = mFormat; 764 765 mCurWindowFlags = mWindowFlags; 766 mLayout.flags = mWindowFlags 767 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 768 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 769 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 770 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 771 mCurWindowPrivateFlags = mWindowPrivateFlags; 772 mLayout.privateFlags = mWindowPrivateFlags; 773 774 mLayout.memoryType = mType; 775 mLayout.token = mWindowToken; 776 777 if (!mCreated) { 778 // Retrieve watch round info 779 TypedArray windowStyle = obtainStyledAttributes( 780 com.android.internal.R.styleable.Window); 781 windowStyle.recycle(); 782 783 // Add window 784 mLayout.type = mIWallpaperEngine.mWindowType; 785 mLayout.gravity = Gravity.START|Gravity.TOP; 786 mLayout.setTitle(WallpaperService.this.getClass().getName()); 787 mLayout.windowAnimations = 788 com.android.internal.R.style.Animation_Wallpaper; 789 mInputChannel = new InputChannel(); 790 if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, 791 Display.DEFAULT_DISPLAY, mWinFrame, mContentInsets, mStableInsets, 792 mOutsets, mDisplayCutout, mInputChannel) < 0) { 793 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 794 return; 795 } 796 mCreated = true; 797 798 mInputEventReceiver = new WallpaperInputEventReceiver( 799 mInputChannel, Looper.myLooper()); 800 } 801 802 mSurfaceHolder.mSurfaceLock.lock(); 803 mDrawingAllowed = true; 804 805 if (!fixedSize) { 806 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 807 mLayout.surfaceInsets.left += mOutsets.left; 808 mLayout.surfaceInsets.top += mOutsets.top; 809 mLayout.surfaceInsets.right += mOutsets.right; 810 mLayout.surfaceInsets.bottom += mOutsets.bottom; 811 } else { 812 mLayout.surfaceInsets.set(0, 0, 0, 0); 813 } 814 final int relayoutResult = mSession.relayout( 815 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 816 View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets, 817 mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, 818 mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface); 819 820 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 821 + ", frame=" + mWinFrame); 822 823 int w = mWinFrame.width(); 824 int h = mWinFrame.height(); 825 826 if (!fixedSize) { 827 final Rect padding = mIWallpaperEngine.mDisplayPadding; 828 w += padding.left + padding.right + mOutsets.left + mOutsets.right; 829 h += padding.top + padding.bottom + mOutsets.top + mOutsets.bottom; 830 mOverscanInsets.left += padding.left; 831 mOverscanInsets.top += padding.top; 832 mOverscanInsets.right += padding.right; 833 mOverscanInsets.bottom += padding.bottom; 834 mContentInsets.left += padding.left; 835 mContentInsets.top += padding.top; 836 mContentInsets.right += padding.right; 837 mContentInsets.bottom += padding.bottom; 838 mStableInsets.left += padding.left; 839 mStableInsets.top += padding.top; 840 mStableInsets.right += padding.right; 841 mStableInsets.bottom += padding.bottom; 842 mDisplayCutout.set(mDisplayCutout.get().inset(-padding.left, -padding.top, 843 -padding.right, -padding.bottom)); 844 } 845 846 if (mCurWidth != w) { 847 sizeChanged = true; 848 mCurWidth = w; 849 } 850 if (mCurHeight != h) { 851 sizeChanged = true; 852 mCurHeight = h; 853 } 854 855 if (DEBUG) { 856 Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight); 857 } 858 859 insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets); 860 insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets); 861 insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets); 862 insetsChanged |= !mDispatchedOutsets.equals(mOutsets); 863 insetsChanged |= !mDispatchedDisplayCutout.equals(mDisplayCutout.get()); 864 865 mSurfaceHolder.setSurfaceFrameSize(w, h); 866 mSurfaceHolder.mSurfaceLock.unlock(); 867 868 if (!mSurfaceHolder.mSurface.isValid()) { 869 reportSurfaceDestroyed(); 870 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 871 return; 872 } 873 874 boolean didSurface = false; 875 876 try { 877 mSurfaceHolder.ungetCallbacks(); 878 879 if (surfaceCreating) { 880 mIsCreating = true; 881 didSurface = true; 882 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 883 + mSurfaceHolder + "): " + this); 884 onSurfaceCreated(mSurfaceHolder); 885 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 886 if (callbacks != null) { 887 for (SurfaceHolder.Callback c : callbacks) { 888 c.surfaceCreated(mSurfaceHolder); 889 } 890 } 891 } 892 893 redrawNeeded |= creating || (relayoutResult 894 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 895 896 if (forceReport || creating || surfaceCreating 897 || formatChanged || sizeChanged) { 898 if (DEBUG) { 899 RuntimeException e = new RuntimeException(); 900 e.fillInStackTrace(); 901 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 902 + " formatChanged=" + formatChanged 903 + " sizeChanged=" + sizeChanged, e); 904 } 905 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 906 + mSurfaceHolder + ", " + mFormat 907 + ", " + mCurWidth + ", " + mCurHeight 908 + "): " + this); 909 didSurface = true; 910 onSurfaceChanged(mSurfaceHolder, mFormat, 911 mCurWidth, mCurHeight); 912 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 913 if (callbacks != null) { 914 for (SurfaceHolder.Callback c : callbacks) { 915 c.surfaceChanged(mSurfaceHolder, mFormat, 916 mCurWidth, mCurHeight); 917 } 918 } 919 } 920 921 if (insetsChanged) { 922 mDispatchedOverscanInsets.set(mOverscanInsets); 923 mDispatchedOverscanInsets.left += mOutsets.left; 924 mDispatchedOverscanInsets.top += mOutsets.top; 925 mDispatchedOverscanInsets.right += mOutsets.right; 926 mDispatchedOverscanInsets.bottom += mOutsets.bottom; 927 mDispatchedContentInsets.set(mContentInsets); 928 mDispatchedStableInsets.set(mStableInsets); 929 mDispatchedOutsets.set(mOutsets); 930 mDispatchedDisplayCutout = mDisplayCutout.get(); 931 mFinalSystemInsets.set(mDispatchedOverscanInsets); 932 mFinalStableInsets.set(mDispatchedStableInsets); 933 WindowInsets insets = new WindowInsets(mFinalSystemInsets, 934 null, mFinalStableInsets, 935 getResources().getConfiguration().isScreenRound(), false, 936 mDispatchedDisplayCutout); 937 if (DEBUG) { 938 Log.v(TAG, "dispatching insets=" + insets); 939 } 940 onApplyWindowInsets(insets); 941 } 942 943 if (redrawNeeded) { 944 onSurfaceRedrawNeeded(mSurfaceHolder); 945 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 946 if (callbacks != null) { 947 for (SurfaceHolder.Callback c : callbacks) { 948 if (c instanceof SurfaceHolder.Callback2) { 949 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 950 mSurfaceHolder); 951 } 952 } 953 } 954 } 955 956 if (didSurface && !mReportedVisible) { 957 // This wallpaper is currently invisible, but its 958 // surface has changed. At this point let's tell it 959 // again that it is invisible in case the report about 960 // the surface caused it to start running. We really 961 // don't want wallpapers running when not visible. 962 if (mIsCreating) { 963 // Some wallpapers will ignore this call if they 964 // had previously been told they were invisble, 965 // so if we are creating a new surface then toggle 966 // the state to get them to notice. 967 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 968 + this); 969 onVisibilityChanged(true); 970 } 971 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 972 + this); 973 onVisibilityChanged(false); 974 } 975 976 } finally { 977 mIsCreating = false; 978 mSurfaceCreated = true; 979 if (redrawNeeded) { 980 mSession.finishDrawing(mWindow); 981 } 982 mIWallpaperEngine.reportShown(); 983 } 984 } catch (RemoteException ex) { 985 } 986 if (DEBUG) Log.v( 987 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 988 " w=" + mLayout.width + " h=" + mLayout.height); 989 } 990 } 991 992 void attach(IWallpaperEngineWrapper wrapper) { 993 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 994 if (mDestroyed) { 995 return; 996 } 997 998 mIWallpaperEngine = wrapper; 999 mCaller = wrapper.mCaller; 1000 mConnection = wrapper.mConnection; 1001 mWindowToken = wrapper.mWindowToken; 1002 mSurfaceHolder.setSizeFromLayout(); 1003 mInitializing = true; 1004 mSession = WindowManagerGlobal.getWindowSession(); 1005 1006 mWindow.setSession(mSession); 1007 1008 mLayout.packageName = getPackageName(); 1009 1010 mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); 1011 mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); 1012 mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 1013 mDisplayState = mDisplay.getState(); 1014 1015 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 1016 onCreate(mSurfaceHolder); 1017 1018 mInitializing = false; 1019 mReportedVisible = false; 1020 updateSurface(false, false, false); 1021 } 1022 1023 /** 1024 * Executes life cycle event and updates internal ambient mode state based on 1025 * message sent from handler. 1026 * 1027 * @param inAmbientMode {@code true} if in ambient mode. 1028 * @param animated {@code true} if the transition will be animated. 1029 * @hide 1030 */ 1031 @VisibleForTesting 1032 public void doAmbientModeChanged(boolean inAmbientMode, boolean animated) { 1033 if (!mDestroyed) { 1034 if (DEBUG) { 1035 Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", " 1036 + animated + "): " + this); 1037 } 1038 mIsInAmbientMode = inAmbientMode; 1039 if (mCreated) { 1040 onAmbientModeChanged(inAmbientMode, animated); 1041 } 1042 } 1043 } 1044 1045 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 1046 if (!mDestroyed) { 1047 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 1048 + desiredWidth + "," + desiredHeight + "): " + this); 1049 mIWallpaperEngine.mReqWidth = desiredWidth; 1050 mIWallpaperEngine.mReqHeight = desiredHeight; 1051 onDesiredSizeChanged(desiredWidth, desiredHeight); 1052 doOffsetsChanged(true); 1053 } 1054 } 1055 1056 void doDisplayPaddingChanged(Rect padding) { 1057 if (!mDestroyed) { 1058 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 1059 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 1060 mIWallpaperEngine.mDisplayPadding.set(padding); 1061 updateSurface(true, false, false); 1062 } 1063 } 1064 } 1065 1066 void doVisibilityChanged(boolean visible) { 1067 if (!mDestroyed) { 1068 mVisible = visible; 1069 reportVisibility(); 1070 } 1071 } 1072 1073 void reportVisibility() { 1074 if (!mDestroyed) { 1075 mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState(); 1076 boolean visible = mVisible && mDisplayState != Display.STATE_OFF; 1077 if (mReportedVisible != visible) { 1078 mReportedVisible = visible; 1079 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 1080 + "): " + this); 1081 if (visible) { 1082 // If becoming visible, in preview mode the surface 1083 // may have been destroyed so now we need to make 1084 // sure it is re-created. 1085 doOffsetsChanged(false); 1086 updateSurface(false, false, false); 1087 } 1088 onVisibilityChanged(visible); 1089 } 1090 } 1091 } 1092 1093 void doOffsetsChanged(boolean always) { 1094 if (mDestroyed) { 1095 return; 1096 } 1097 1098 if (!always && !mOffsetsChanged) { 1099 return; 1100 } 1101 1102 float xOffset; 1103 float yOffset; 1104 float xOffsetStep; 1105 float yOffsetStep; 1106 boolean sync; 1107 synchronized (mLock) { 1108 xOffset = mPendingXOffset; 1109 yOffset = mPendingYOffset; 1110 xOffsetStep = mPendingXOffsetStep; 1111 yOffsetStep = mPendingYOffsetStep; 1112 sync = mPendingSync; 1113 mPendingSync = false; 1114 mOffsetMessageEnqueued = false; 1115 } 1116 1117 if (mSurfaceCreated) { 1118 if (mReportedVisible) { 1119 if (DEBUG) Log.v(TAG, "Offsets change in " + this 1120 + ": " + xOffset + "," + yOffset); 1121 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 1122 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 1123 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 1124 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 1125 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 1126 } else { 1127 mOffsetsChanged = true; 1128 } 1129 } 1130 1131 if (sync) { 1132 try { 1133 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 1134 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 1135 } catch (RemoteException e) { 1136 } 1137 } 1138 } 1139 1140 void doCommand(WallpaperCommand cmd) { 1141 Bundle result; 1142 if (!mDestroyed) { 1143 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 1144 cmd.extras, cmd.sync); 1145 } else { 1146 result = null; 1147 } 1148 if (cmd.sync) { 1149 try { 1150 if (DEBUG) Log.v(TAG, "Reporting command complete"); 1151 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 1152 } catch (RemoteException e) { 1153 } 1154 } 1155 } 1156 1157 void reportSurfaceDestroyed() { 1158 if (mSurfaceCreated) { 1159 mSurfaceCreated = false; 1160 mSurfaceHolder.ungetCallbacks(); 1161 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1162 if (callbacks != null) { 1163 for (SurfaceHolder.Callback c : callbacks) { 1164 c.surfaceDestroyed(mSurfaceHolder); 1165 } 1166 } 1167 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 1168 + mSurfaceHolder + "): " + this); 1169 onSurfaceDestroyed(mSurfaceHolder); 1170 } 1171 } 1172 1173 void detach() { 1174 if (mDestroyed) { 1175 return; 1176 } 1177 1178 mDestroyed = true; 1179 1180 if (mDisplayManager != null) { 1181 mDisplayManager.unregisterDisplayListener(mDisplayListener); 1182 } 1183 1184 if (mVisible) { 1185 mVisible = false; 1186 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 1187 onVisibilityChanged(false); 1188 } 1189 1190 reportSurfaceDestroyed(); 1191 1192 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 1193 onDestroy(); 1194 1195 if (mCreated) { 1196 try { 1197 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 1198 + mSurfaceHolder.getSurface() + " of: " + this); 1199 1200 if (mInputEventReceiver != null) { 1201 mInputEventReceiver.dispose(); 1202 mInputEventReceiver = null; 1203 } 1204 1205 mSession.remove(mWindow); 1206 } catch (RemoteException e) { 1207 } 1208 mSurfaceHolder.mSurface.release(); 1209 mCreated = false; 1210 1211 // Dispose the input channel after removing the window so the Window Manager 1212 // doesn't interpret the input channel being closed as an abnormal termination. 1213 if (mInputChannel != null) { 1214 mInputChannel.dispose(); 1215 mInputChannel = null; 1216 } 1217 } 1218 } 1219 1220 private final DisplayListener mDisplayListener = new DisplayListener() { 1221 @Override 1222 public void onDisplayChanged(int displayId) { 1223 if (mDisplay.getDisplayId() == displayId) { 1224 reportVisibility(); 1225 } 1226 } 1227 1228 @Override 1229 public void onDisplayRemoved(int displayId) { 1230 } 1231 1232 @Override 1233 public void onDisplayAdded(int displayId) { 1234 } 1235 }; 1236 } 1237 1238 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 1239 implements HandlerCaller.Callback { 1240 private final HandlerCaller mCaller; 1241 1242 final IWallpaperConnection mConnection; 1243 final IBinder mWindowToken; 1244 final int mWindowType; 1245 final boolean mIsPreview; 1246 boolean mShownReported; 1247 int mReqWidth; 1248 int mReqHeight; 1249 final Rect mDisplayPadding = new Rect(); 1250 1251 Engine mEngine; 1252 1253 IWallpaperEngineWrapper(WallpaperService context, 1254 IWallpaperConnection conn, IBinder windowToken, 1255 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1256 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); 1257 mConnection = conn; 1258 mWindowToken = windowToken; 1259 mWindowType = windowType; 1260 mIsPreview = isPreview; 1261 mReqWidth = reqWidth; 1262 mReqHeight = reqHeight; 1263 mDisplayPadding.set(padding); 1264 1265 Message msg = mCaller.obtainMessage(DO_ATTACH); 1266 mCaller.sendMessage(msg); 1267 } 1268 1269 public void setDesiredSize(int width, int height) { 1270 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 1271 mCaller.sendMessage(msg); 1272 } 1273 1274 public void setDisplayPadding(Rect padding) { 1275 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 1276 mCaller.sendMessage(msg); 1277 } 1278 1279 public void setVisibility(boolean visible) { 1280 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 1281 visible ? 1 : 0); 1282 mCaller.sendMessage(msg); 1283 } 1284 1285 @Override 1286 public void setInAmbientMode(boolean inAmbientDisplay, boolean animated) 1287 throws RemoteException { 1288 Message msg = mCaller.obtainMessageII(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0, 1289 animated ? 1 : 0); 1290 mCaller.sendMessage(msg); 1291 } 1292 1293 public void dispatchPointer(MotionEvent event) { 1294 if (mEngine != null) { 1295 mEngine.dispatchPointer(event); 1296 } else { 1297 event.recycle(); 1298 } 1299 } 1300 1301 public void dispatchWallpaperCommand(String action, int x, int y, 1302 int z, Bundle extras) { 1303 if (mEngine != null) { 1304 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 1305 } 1306 } 1307 1308 public void reportShown() { 1309 if (!mShownReported) { 1310 mShownReported = true; 1311 try { 1312 mConnection.engineShown(this); 1313 } catch (RemoteException e) { 1314 Log.w(TAG, "Wallpaper host disappeared", e); 1315 return; 1316 } 1317 } 1318 } 1319 1320 public void requestWallpaperColors() { 1321 Message msg = mCaller.obtainMessage(MSG_REQUEST_WALLPAPER_COLORS); 1322 mCaller.sendMessage(msg); 1323 } 1324 1325 public void destroy() { 1326 Message msg = mCaller.obtainMessage(DO_DETACH); 1327 mCaller.sendMessage(msg); 1328 } 1329 1330 @Override 1331 public void executeMessage(Message message) { 1332 switch (message.what) { 1333 case DO_ATTACH: { 1334 try { 1335 mConnection.attachEngine(this); 1336 } catch (RemoteException e) { 1337 Log.w(TAG, "Wallpaper host disappeared", e); 1338 return; 1339 } 1340 Engine engine = onCreateEngine(); 1341 mEngine = engine; 1342 mActiveEngines.add(engine); 1343 engine.attach(this); 1344 return; 1345 } 1346 case DO_DETACH: { 1347 mActiveEngines.remove(mEngine); 1348 mEngine.detach(); 1349 return; 1350 } 1351 case DO_SET_DESIRED_SIZE: { 1352 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 1353 return; 1354 } 1355 case DO_SET_DISPLAY_PADDING: { 1356 mEngine.doDisplayPaddingChanged((Rect) message.obj); 1357 return; 1358 } 1359 case DO_IN_AMBIENT_MODE: { 1360 mEngine.doAmbientModeChanged(message.arg1 != 0, message.arg2 != 0); 1361 return; 1362 } 1363 case MSG_UPDATE_SURFACE: 1364 mEngine.updateSurface(true, false, false); 1365 break; 1366 case MSG_VISIBILITY_CHANGED: 1367 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 1368 + ": " + message.arg1); 1369 mEngine.doVisibilityChanged(message.arg1 != 0); 1370 break; 1371 case MSG_WALLPAPER_OFFSETS: { 1372 mEngine.doOffsetsChanged(true); 1373 } break; 1374 case MSG_WALLPAPER_COMMAND: { 1375 WallpaperCommand cmd = (WallpaperCommand)message.obj; 1376 mEngine.doCommand(cmd); 1377 } break; 1378 case MSG_WINDOW_RESIZED: { 1379 final boolean reportDraw = message.arg1 != 0; 1380 mEngine.mOutsets.set((Rect) message.obj); 1381 mEngine.updateSurface(true, false, reportDraw); 1382 mEngine.doOffsetsChanged(true); 1383 } break; 1384 case MSG_WINDOW_MOVED: { 1385 // Do nothing. What does it mean for a Wallpaper to move? 1386 } break; 1387 case MSG_TOUCH_EVENT: { 1388 boolean skip = false; 1389 MotionEvent ev = (MotionEvent)message.obj; 1390 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 1391 synchronized (mEngine.mLock) { 1392 if (mEngine.mPendingMove == ev) { 1393 mEngine.mPendingMove = null; 1394 } else { 1395 // this is not the motion event we are looking for.... 1396 skip = true; 1397 } 1398 } 1399 } 1400 if (!skip) { 1401 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 1402 mEngine.onTouchEvent(ev); 1403 } 1404 ev.recycle(); 1405 } break; 1406 case MSG_REQUEST_WALLPAPER_COLORS: { 1407 if (mConnection == null) { 1408 break; 1409 } 1410 try { 1411 mConnection.onWallpaperColorsChanged(mEngine.onComputeColors()); 1412 } catch (RemoteException e) { 1413 // Connection went away, nothing to do in here. 1414 } 1415 } break; 1416 default : 1417 Log.w(TAG, "Unknown message type " + message.what); 1418 } 1419 } 1420 } 1421 1422 /** 1423 * Implements the internal {@link IWallpaperService} interface to convert 1424 * incoming calls to it back to calls on an {@link WallpaperService}. 1425 */ 1426 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 1427 private final WallpaperService mTarget; 1428 1429 public IWallpaperServiceWrapper(WallpaperService context) { 1430 mTarget = context; 1431 } 1432 1433 @Override 1434 public void attach(IWallpaperConnection conn, IBinder windowToken, 1435 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1436 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 1437 windowType, isPreview, reqWidth, reqHeight, padding); 1438 } 1439 } 1440 1441 @Override 1442 public void onCreate() { 1443 super.onCreate(); 1444 } 1445 1446 @Override 1447 public void onDestroy() { 1448 super.onDestroy(); 1449 for (int i=0; i<mActiveEngines.size(); i++) { 1450 mActiveEngines.get(i).detach(); 1451 } 1452 mActiveEngines.clear(); 1453 } 1454 1455 /** 1456 * Implement to return the implementation of the internal accessibility 1457 * service interface. Subclasses should not override. 1458 */ 1459 @Override 1460 public final IBinder onBind(Intent intent) { 1461 return new IWallpaperServiceWrapper(this); 1462 } 1463 1464 /** 1465 * Must be implemented to return a new instance of the wallpaper's engine. 1466 * Note that multiple instances may be active at the same time, such as 1467 * when the wallpaper is currently set as the active wallpaper and the user 1468 * is in the wallpaper picker viewing a preview of it as well. 1469 */ 1470 public abstract Engine onCreateEngine(); 1471 1472 @Override 1473 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 1474 out.print("State of wallpaper "); out.print(this); out.println(":"); 1475 for (int i=0; i<mActiveEngines.size(); i++) { 1476 Engine engine = mActiveEngines.get(i); 1477 out.print(" Engine "); out.print(engine); out.println(":"); 1478 engine.dump(" ", fd, out, args); 1479 } 1480 } 1481 } 1482