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