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 static android.app.WallpaperManager.COMMAND_FREEZE; 20 import static android.app.WallpaperManager.COMMAND_UNFREEZE; 21 import static android.graphics.Matrix.MSCALE_X; 22 import static android.graphics.Matrix.MSCALE_Y; 23 import static android.graphics.Matrix.MSKEW_X; 24 import static android.graphics.Matrix.MSKEW_Y; 25 import static android.view.SurfaceControl.METADATA_WINDOW_TYPE; 26 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; 27 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 28 29 import android.annotation.FloatRange; 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.annotation.SdkConstant; 33 import android.annotation.SdkConstant.SdkConstantType; 34 import android.annotation.SystemApi; 35 import android.app.Service; 36 import android.app.WallpaperColors; 37 import android.app.WallpaperInfo; 38 import android.app.WallpaperManager; 39 import android.compat.annotation.UnsupportedAppUsage; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.res.Configuration; 43 import android.content.res.TypedArray; 44 import android.graphics.BLASTBufferQueue; 45 import android.graphics.Bitmap; 46 import android.graphics.Canvas; 47 import android.graphics.GraphicBuffer; 48 import android.graphics.Matrix; 49 import android.graphics.PixelFormat; 50 import android.graphics.Point; 51 import android.graphics.Rect; 52 import android.graphics.RectF; 53 import android.graphics.drawable.Drawable; 54 import android.hardware.HardwareBuffer; 55 import android.hardware.display.DisplayManager; 56 import android.hardware.display.DisplayManager.DisplayListener; 57 import android.os.Build; 58 import android.os.Bundle; 59 import android.os.Handler; 60 import android.os.IBinder; 61 import android.os.Looper; 62 import android.os.Message; 63 import android.os.Process; 64 import android.os.RemoteException; 65 import android.os.SystemClock; 66 import android.os.SystemProperties; 67 import android.os.Trace; 68 import android.util.ArraySet; 69 import android.util.Log; 70 import android.util.MergedConfiguration; 71 import android.view.Display; 72 import android.view.DisplayCutout; 73 import android.view.Gravity; 74 import android.view.IWindowSession; 75 import android.view.InputChannel; 76 import android.view.InputDevice; 77 import android.view.InputEvent; 78 import android.view.InputEventReceiver; 79 import android.view.InsetsSourceControl; 80 import android.view.InsetsState; 81 import android.view.InsetsVisibilities; 82 import android.view.MotionEvent; 83 import android.view.PixelCopy; 84 import android.view.Surface; 85 import android.view.SurfaceControl; 86 import android.view.SurfaceHolder; 87 import android.view.View; 88 import android.view.ViewGroup; 89 import android.view.WindowInsets; 90 import android.view.WindowManager; 91 import android.view.WindowManagerGlobal; 92 import android.window.ClientWindowFrames; 93 94 import com.android.internal.annotations.VisibleForTesting; 95 import com.android.internal.os.HandlerCaller; 96 import com.android.internal.view.BaseIWindow; 97 import com.android.internal.view.BaseSurfaceHolder; 98 99 import java.io.FileDescriptor; 100 import java.io.PrintWriter; 101 import java.util.ArrayList; 102 import java.util.Arrays; 103 import java.util.List; 104 import java.util.Objects; 105 import java.util.concurrent.TimeUnit; 106 import java.util.concurrent.atomic.AtomicBoolean; 107 import java.util.function.Supplier; 108 109 /** 110 * A wallpaper service is responsible for showing a live wallpaper behind 111 * applications that would like to sit on top of it. This service object 112 * itself does very little -- its only purpose is to generate instances of 113 * {@link Engine} as needed. Implementing a wallpaper thus 114 * involves subclassing from this, subclassing an Engine implementation, 115 * and implementing {@link #onCreateEngine()} to return a new instance of 116 * your engine. 117 */ 118 public abstract class WallpaperService extends Service { 119 /** 120 * The {@link Intent} that must be declared as handled by the service. 121 * To be supported, the service must also require the 122 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so 123 * that other applications can not abuse it. 124 */ 125 @SdkConstant(SdkConstantType.SERVICE_ACTION) 126 public static final String SERVICE_INTERFACE = 127 "android.service.wallpaper.WallpaperService"; 128 129 /** 130 * Name under which a WallpaperService component publishes information 131 * about itself. This meta-data must reference an XML resource containing 132 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 133 * tag. 134 */ 135 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 136 137 static final String TAG = "WallpaperService"; 138 static final boolean DEBUG = false; 139 static final float MIN_PAGE_ALLOWED_MARGIN = .05f; 140 private static final int MIN_BITMAP_SCREENSHOT_WIDTH = 64; 141 private static final long DEFAULT_UPDATE_SCREENSHOT_DURATION = 60 * 1000; //Once per minute 142 private static final @NonNull RectF LOCAL_COLOR_BOUNDS = 143 new RectF(0, 0, 1, 1); 144 145 private static final int DO_ATTACH = 10; 146 private static final int DO_DETACH = 20; 147 private static final int DO_SET_DESIRED_SIZE = 30; 148 private static final int DO_SET_DISPLAY_PADDING = 40; 149 private static final int DO_IN_AMBIENT_MODE = 50; 150 151 private static final int MSG_UPDATE_SURFACE = 10000; 152 private static final int MSG_VISIBILITY_CHANGED = 10010; 153 private static final int MSG_WALLPAPER_OFFSETS = 10020; 154 private static final int MSG_WALLPAPER_COMMAND = 10025; 155 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 156 private static final int MSG_WINDOW_RESIZED = 10030; 157 private static final int MSG_WINDOW_MOVED = 10035; 158 private static final int MSG_TOUCH_EVENT = 10040; 159 private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; 160 private static final int MSG_ZOOM = 10100; 161 private static final int MSG_SCALE_PREVIEW = 10110; 162 private static final int MSG_REPORT_SHOWN = 10150; 163 private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY, 164 Float.NEGATIVE_INFINITY); 165 166 private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; 167 168 private static final boolean ENABLE_WALLPAPER_DIMMING = 169 SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true); 170 171 private final ArrayList<Engine> mActiveEngines 172 = new ArrayList<Engine>(); 173 174 static final class WallpaperCommand { 175 String action; 176 int x; 177 int y; 178 int z; 179 Bundle extras; 180 boolean sync; 181 } 182 183 /** 184 * The actual implementation of a wallpaper. A wallpaper service may 185 * have multiple instances running (for example as a real wallpaper 186 * and as a preview), each of which is represented by its own Engine 187 * instance. You must implement {@link WallpaperService#onCreateEngine()} 188 * to return your concrete Engine implementation. 189 */ 190 public class Engine { 191 IWallpaperEngineWrapper mIWallpaperEngine; 192 final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4); 193 final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4); 194 195 // 2D matrix [x][y] to represent a page of a portion of a window 196 EngineWindowPage[] mWindowPages = new EngineWindowPage[1]; 197 Bitmap mLastScreenshot; 198 int mLastWindowPage = -1; 199 private boolean mResetWindowPages; 200 201 // Copies from mIWallpaperEngine. 202 HandlerCaller mCaller; 203 IWallpaperConnection mConnection; 204 IBinder mWindowToken; 205 206 boolean mInitializing = true; 207 boolean mVisible; 208 boolean mReportedVisible; 209 boolean mDestroyed; 210 // Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false 211 // after receiving WallpaperManager#COMMAND_UNFREEZE. COMMAND_FREEZE is fully applied once 212 // mScreenshotSurfaceControl isn't null. When this happens, then Engine is notified through 213 // doVisibilityChanged that main wallpaper surface is no longer visible and the wallpaper 214 // host receives onVisibilityChanged(false) callback. 215 private boolean mFrozenRequested = false; 216 217 // Current window state. 218 boolean mCreated; 219 boolean mSurfaceCreated; 220 boolean mIsCreating; 221 boolean mDrawingAllowed; 222 boolean mOffsetsChanged; 223 boolean mFixedSizeAllowed; 224 boolean mShouldDim; 225 int mWidth; 226 int mHeight; 227 int mFormat; 228 int mType; 229 int mCurWidth; 230 int mCurHeight; 231 float mZoom = 0f; 232 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 233 int mWindowPrivateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS 234 | WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; 235 int mCurWindowFlags = mWindowFlags; 236 int mCurWindowPrivateFlags = mWindowPrivateFlags; 237 Rect mPreviewSurfacePosition; 238 final ClientWindowFrames mWinFrames = new ClientWindowFrames(); 239 final Rect mDispatchedContentInsets = new Rect(); 240 final Rect mDispatchedStableInsets = new Rect(); 241 DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT; 242 final InsetsState mInsetsState = new InsetsState(); 243 final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities(); 244 final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0]; 245 final MergedConfiguration mMergedConfiguration = new MergedConfiguration(); 246 private final Point mSurfaceSize = new Point(); 247 private final Point mLastSurfaceSize = new Point(); 248 private final Matrix mTmpMatrix = new Matrix(); 249 private final float[] mTmpValues = new float[9]; 250 251 final WindowManager.LayoutParams mLayout 252 = new WindowManager.LayoutParams(); 253 IWindowSession mSession; 254 255 final Object mLock = new Object(); 256 boolean mOffsetMessageEnqueued; 257 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 258 float mPendingXOffset; 259 float mPendingYOffset; 260 float mPendingXOffsetStep; 261 float mPendingYOffsetStep; 262 boolean mPendingSync; 263 MotionEvent mPendingMove; 264 boolean mIsInAmbientMode; 265 266 // Needed for throttling onComputeColors. 267 private long mLastColorInvalidation; 268 private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; 269 private final Supplier<Long> mClockFunction; 270 private final Handler mHandler; 271 272 private Display mDisplay; 273 private Context mDisplayContext; 274 private int mDisplayState; 275 private float mWallpaperDimAmount = 0.05f; 276 277 SurfaceControl mSurfaceControl = new SurfaceControl(); 278 SurfaceControl mBbqSurfaceControl; 279 BLASTBufferQueue mBlastBufferQueue; 280 private SurfaceControl mScreenshotSurfaceControl; 281 private Point mScreenshotSize = new Point(); 282 283 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 284 { 285 mRequestedFormat = PixelFormat.RGBX_8888; 286 } 287 288 @Override 289 public boolean onAllowLockCanvas() { 290 return mDrawingAllowed; 291 } 292 293 @Override 294 public void onRelayoutContainer() { 295 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 296 mCaller.sendMessage(msg); 297 } 298 299 @Override 300 public void onUpdateSurface() { 301 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 302 mCaller.sendMessage(msg); 303 } 304 305 public boolean isCreating() { 306 return mIsCreating; 307 } 308 309 @Override 310 public void setFixedSize(int width, int height) { 311 if (!mFixedSizeAllowed) { 312 // Regular apps can't do this. It can only work for 313 // certain designs of window animations, so you can't 314 // rely on it. 315 throw new UnsupportedOperationException( 316 "Wallpapers currently only support sizing from layout"); 317 } 318 super.setFixedSize(width, height); 319 } 320 321 public void setKeepScreenOn(boolean screenOn) { 322 throw new UnsupportedOperationException( 323 "Wallpapers do not support keep screen on"); 324 } 325 326 private void prepareToDraw() { 327 if (mDisplayState == Display.STATE_DOZE 328 || mDisplayState == Display.STATE_DOZE_SUSPEND) { 329 try { 330 mSession.pokeDrawLock(mWindow); 331 } catch (RemoteException e) { 332 // System server died, can be ignored. 333 } 334 } 335 } 336 337 @Override 338 public Canvas lockCanvas() { 339 prepareToDraw(); 340 return super.lockCanvas(); 341 } 342 343 @Override 344 public Canvas lockCanvas(Rect dirty) { 345 prepareToDraw(); 346 return super.lockCanvas(dirty); 347 } 348 349 @Override 350 public Canvas lockHardwareCanvas() { 351 prepareToDraw(); 352 return super.lockHardwareCanvas(); 353 } 354 }; 355 356 final class WallpaperInputEventReceiver extends InputEventReceiver { WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper)357 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) { 358 super(inputChannel, looper); 359 } 360 361 @Override onInputEvent(InputEvent event)362 public void onInputEvent(InputEvent event) { 363 boolean handled = false; 364 try { 365 if (event instanceof MotionEvent 366 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 367 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event); 368 dispatchPointer(dup); 369 handled = true; 370 } 371 } finally { 372 finishInputEvent(event, handled); 373 } 374 } 375 } 376 WallpaperInputEventReceiver mInputEventReceiver; 377 378 final BaseIWindow mWindow = new BaseIWindow() { 379 @Override 380 public void resized(ClientWindowFrames frames, boolean reportDraw, 381 MergedConfiguration mergedConfiguration, boolean forceLayout, 382 boolean alwaysConsumeSystemBars, int displayId) { 383 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED, 384 reportDraw ? 1 : 0); 385 mCaller.sendMessage(msg); 386 } 387 388 @Override 389 public void moved(int newX, int newY) { 390 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY); 391 mCaller.sendMessage(msg); 392 } 393 394 @Override 395 public void dispatchAppVisibility(boolean visible) { 396 // We don't do this in preview mode; we'll let the preview 397 // activity tell us when to run. 398 if (!mIWallpaperEngine.mIsPreview) { 399 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 400 visible ? 1 : 0); 401 mCaller.sendMessage(msg); 402 } 403 } 404 405 @Override 406 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 407 float zoom, boolean sync) { 408 synchronized (mLock) { 409 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 410 mPendingXOffset = x; 411 mPendingYOffset = y; 412 mPendingXOffsetStep = xStep; 413 mPendingYOffsetStep = yStep; 414 if (sync) { 415 mPendingSync = true; 416 } 417 if (!mOffsetMessageEnqueued) { 418 mOffsetMessageEnqueued = true; 419 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 420 mCaller.sendMessage(msg); 421 } 422 Message msg = mCaller.obtainMessageI(MSG_ZOOM, Float.floatToIntBits(zoom)); 423 mCaller.sendMessage(msg); 424 } 425 } 426 427 @Override 428 public void dispatchWallpaperCommand(String action, int x, int y, 429 int z, Bundle extras, boolean sync) { 430 synchronized (mLock) { 431 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 432 WallpaperCommand cmd = new WallpaperCommand(); 433 cmd.action = action; 434 cmd.x = x; 435 cmd.y = y; 436 cmd.z = z; 437 cmd.extras = extras; 438 cmd.sync = sync; 439 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 440 msg.obj = cmd; 441 mCaller.sendMessage(msg); 442 } 443 } 444 }; 445 446 /** 447 * Default constructor 448 */ Engine()449 public Engine() { 450 this(SystemClock::elapsedRealtime, Handler.getMain()); 451 } 452 453 /** 454 * Constructor used for test purposes. 455 * 456 * @param clockFunction Supplies current times in millis. 457 * @param handler Used for posting/deferring asynchronous calls. 458 * @hide 459 */ 460 @VisibleForTesting Engine(Supplier<Long> clockFunction, Handler handler)461 public Engine(Supplier<Long> clockFunction, Handler handler) { 462 mClockFunction = clockFunction; 463 mHandler = handler; 464 } 465 466 /** 467 * Provides access to the surface in which this wallpaper is drawn. 468 */ getSurfaceHolder()469 public SurfaceHolder getSurfaceHolder() { 470 return mSurfaceHolder; 471 } 472 473 /** 474 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 475 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 476 * that the system would like this wallpaper to run in. 477 */ getDesiredMinimumWidth()478 public int getDesiredMinimumWidth() { 479 return mIWallpaperEngine.mReqWidth; 480 } 481 482 /** 483 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 484 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 485 * that the system would like this wallpaper to run in. 486 */ getDesiredMinimumHeight()487 public int getDesiredMinimumHeight() { 488 return mIWallpaperEngine.mReqHeight; 489 } 490 491 /** 492 * Return whether the wallpaper is currently visible to the user, 493 * this is the last value supplied to 494 * {@link #onVisibilityChanged(boolean)}. 495 */ isVisible()496 public boolean isVisible() { 497 return mReportedVisible; 498 } 499 500 /** 501 * Return whether the wallpaper is capable of extracting local colors in a rectangle area, 502 * Must implement without calling super: 503 * {@link #addLocalColorsAreas(List)} 504 * {@link #removeLocalColorsAreas(List)} 505 * When local colors change, call {@link #notifyLocalColorsChanged(List, List)} 506 * See {@link com.android.systemui.ImageWallpaper} for an example 507 * @hide 508 */ supportsLocalColorExtraction()509 public boolean supportsLocalColorExtraction() { 510 return false; 511 } 512 513 /** 514 * Returns true if this engine is running in preview mode -- that is, 515 * it is being shown to the user before they select it as the actual 516 * wallpaper. 517 */ isPreview()518 public boolean isPreview() { 519 return mIWallpaperEngine.mIsPreview; 520 } 521 522 /** 523 * Returns true if this engine is running in ambient mode -- that is, 524 * it is being shown in low power mode, on always on display. 525 * @hide 526 */ 527 @SystemApi isInAmbientMode()528 public boolean isInAmbientMode() { 529 return mIsInAmbientMode; 530 } 531 532 /** 533 * This will be called when the wallpaper is first started. If true is returned, the system 534 * will zoom in the wallpaper by default and zoom it out as the user interacts, 535 * to create depth. Otherwise, zoom will have to be handled manually 536 * in {@link #onZoomChanged(float)}. 537 * 538 * @hide 539 */ shouldZoomOutWallpaper()540 public boolean shouldZoomOutWallpaper() { 541 return false; 542 } 543 544 /** 545 * This will be called in the end of {@link #updateSurface(boolean, boolean, boolean)}. 546 * If true is returned, the engine will not report shown until rendering finished is 547 * reported. Otherwise, the engine will report shown immediately right after redraw phase 548 * in {@link #updateSurface(boolean, boolean, boolean)}. 549 * 550 * @hide 551 */ shouldWaitForEngineShown()552 public boolean shouldWaitForEngineShown() { 553 return false; 554 } 555 556 /** 557 * Reports the rendering is finished, stops waiting, then invokes 558 * {@link IWallpaperEngineWrapper#reportShown()}. 559 * 560 * @hide 561 */ reportEngineShown(boolean waitForEngineShown)562 public void reportEngineShown(boolean waitForEngineShown) { 563 if (mIWallpaperEngine.mShownReported) return; 564 if (!waitForEngineShown) { 565 Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN); 566 mCaller.removeMessages(MSG_REPORT_SHOWN); 567 mCaller.sendMessage(message); 568 } else { 569 // if we are already waiting, no need to reset the timeout. 570 if (!mCaller.hasMessages(MSG_REPORT_SHOWN)) { 571 Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN); 572 mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(5)); 573 } 574 } 575 } 576 577 /** 578 * Control whether this wallpaper will receive raw touch events 579 * from the window manager as the user interacts with the window 580 * that is currently displaying the wallpaper. By default they 581 * are turned off. If enabled, the events will be received in 582 * {@link #onTouchEvent(MotionEvent)}. 583 */ setTouchEventsEnabled(boolean enabled)584 public void setTouchEventsEnabled(boolean enabled) { 585 mWindowFlags = enabled 586 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 587 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 588 if (mCreated) { 589 updateSurface(false, false, false); 590 } 591 } 592 593 /** 594 * Control whether this wallpaper will receive notifications when the wallpaper 595 * has been scrolled. By default, wallpapers will receive notifications, although 596 * the default static image wallpapers do not. It is a performance optimization to 597 * set this to false. 598 * 599 * @param enabled whether the wallpaper wants to receive offset notifications 600 */ setOffsetNotificationsEnabled(boolean enabled)601 public void setOffsetNotificationsEnabled(boolean enabled) { 602 mWindowPrivateFlags = enabled 603 ? (mWindowPrivateFlags | 604 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) 605 : (mWindowPrivateFlags & 606 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS); 607 if (mCreated) { 608 updateSurface(false, false, false); 609 } 610 } 611 612 /** {@hide} */ 613 @UnsupportedAppUsage setFixedSizeAllowed(boolean allowed)614 public void setFixedSizeAllowed(boolean allowed) { 615 mFixedSizeAllowed = allowed; 616 } 617 618 /** 619 * Returns the current scale of the surface 620 * @hide 621 */ 622 @VisibleForTesting getZoom()623 public float getZoom() { 624 return mZoom; 625 } 626 627 /** 628 * Called once to initialize the engine. After returning, the 629 * engine's surface will be created by the framework. 630 */ onCreate(SurfaceHolder surfaceHolder)631 public void onCreate(SurfaceHolder surfaceHolder) { 632 } 633 634 /** 635 * Called right before the engine is going away. After this the 636 * surface will be destroyed and this Engine object is no longer 637 * valid. 638 */ onDestroy()639 public void onDestroy() { 640 } 641 642 /** 643 * Called to inform you of the wallpaper becoming visible or 644 * hidden. <em>It is very important that a wallpaper only use 645 * CPU while it is visible.</em>. 646 */ onVisibilityChanged(boolean visible)647 public void onVisibilityChanged(boolean visible) { 648 } 649 650 /** 651 * Called with the current insets that are in effect for the wallpaper. 652 * This gives you the part of the overall wallpaper surface that will 653 * generally be visible to the user (ignoring position offsets applied to it). 654 * 655 * @param insets Insets to apply. 656 */ onApplyWindowInsets(WindowInsets insets)657 public void onApplyWindowInsets(WindowInsets insets) { 658 } 659 660 /** 661 * Called as the user performs touch-screen interaction with the 662 * window that is currently showing this wallpaper. Note that the 663 * events you receive here are driven by the actual application the 664 * user is interacting with, so if it is slow you will get fewer 665 * move events. 666 */ onTouchEvent(MotionEvent event)667 public void onTouchEvent(MotionEvent event) { 668 } 669 670 /** 671 * Called to inform you of the wallpaper's offsets changing 672 * within its contain, corresponding to the container's 673 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 674 * WallpaperManager.setWallpaperOffsets()}. 675 */ onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset)676 public void onOffsetsChanged(float xOffset, float yOffset, 677 float xOffsetStep, float yOffsetStep, 678 int xPixelOffset, int yPixelOffset) { 679 } 680 681 /** 682 * Process a command that was sent to the wallpaper with 683 * {@link WallpaperManager#sendWallpaperCommand}. 684 * The default implementation does nothing, and always returns null 685 * as the result. 686 * 687 * @param action The name of the command to perform. This tells you 688 * what to do and how to interpret the rest of the arguments. 689 * @param x Generic integer parameter. 690 * @param y Generic integer parameter. 691 * @param z Generic integer parameter. 692 * @param extras Any additional parameters. 693 * @param resultRequested If true, the caller is requesting that 694 * a result, appropriate for the command, be returned back. 695 * @return If returning a result, create a Bundle and place the 696 * result data in to it. Otherwise return null. 697 */ onCommand(String action, int x, int y, int z, Bundle extras, boolean resultRequested)698 public Bundle onCommand(String action, int x, int y, int z, 699 Bundle extras, boolean resultRequested) { 700 return null; 701 } 702 703 /** 704 * Called when the device enters or exits ambient mode. 705 * 706 * @param inAmbientMode {@code true} if in ambient mode. 707 * @param animationDuration How long the transition animation to change the ambient state 708 * should run, in milliseconds. If 0 is passed as the argument 709 * here, the state should be switched immediately. 710 * 711 * @see #isInAmbientMode() 712 * @see WallpaperInfo#supportsAmbientMode() 713 * @hide 714 */ 715 @SystemApi onAmbientModeChanged(boolean inAmbientMode, long animationDuration)716 public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { 717 } 718 719 /** 720 * Called when an application has changed the desired virtual size of 721 * the wallpaper. 722 */ onDesiredSizeChanged(int desiredWidth, int desiredHeight)723 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 724 } 725 726 /** 727 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 728 * SurfaceHolder.Callback.surfaceChanged()}. 729 */ onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)730 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 731 } 732 733 /** 734 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded 735 * SurfaceHolder.Callback.surfaceRedrawNeeded()}. 736 */ onSurfaceRedrawNeeded(SurfaceHolder holder)737 public void onSurfaceRedrawNeeded(SurfaceHolder holder) { 738 } 739 740 /** 741 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 742 * SurfaceHolder.Callback.surfaceCreated()}. 743 */ onSurfaceCreated(SurfaceHolder holder)744 public void onSurfaceCreated(SurfaceHolder holder) { 745 } 746 747 /** 748 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 749 * SurfaceHolder.Callback.surfaceDestroyed()}. 750 */ onSurfaceDestroyed(SurfaceHolder holder)751 public void onSurfaceDestroyed(SurfaceHolder holder) { 752 } 753 754 /** 755 * Called when the zoom level of the wallpaper changed. 756 * This method will be called with the initial zoom level when the surface is created. 757 * 758 * @param zoom the zoom level, between 0 indicating fully zoomed in and 1 indicating fully 759 * zoomed out. 760 */ onZoomChanged(@loatRangefrom = 0f, to = 1f) float zoom)761 public void onZoomChanged(@FloatRange(from = 0f, to = 1f) float zoom) { 762 } 763 764 /** 765 * Notifies the engine that wallpaper colors changed significantly. 766 * This will trigger a {@link #onComputeColors()} call. 767 */ notifyColorsChanged()768 public void notifyColorsChanged() { 769 final long now = mClockFunction.get(); 770 if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) { 771 Log.w(TAG, "This call has been deferred. You should only call " 772 + "notifyColorsChanged() once every " 773 + (NOTIFY_COLORS_RATE_LIMIT_MS / 1000f) + " seconds."); 774 if (!mHandler.hasCallbacks(mNotifyColorsChanged)) { 775 mHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS); 776 } 777 return; 778 } 779 mLastColorInvalidation = now; 780 mHandler.removeCallbacks(mNotifyColorsChanged); 781 782 try { 783 final WallpaperColors newColors = onComputeColors(); 784 if (mConnection != null) { 785 mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId()); 786 } else { 787 Log.w(TAG, "Can't notify system because wallpaper connection " 788 + "was not established."); 789 } 790 mResetWindowPages = true; 791 processLocalColors(mPendingXOffset, mPendingXOffsetStep); 792 } catch (RemoteException e) { 793 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); 794 } 795 } 796 797 /** 798 * Called by the system when it needs to know what colors the wallpaper is using. 799 * You might return null if no color information is available at the moment. 800 * In that case you might want to call {@link #notifyColorsChanged()} when 801 * color information becomes available. 802 * <p> 803 * The simplest way of creating a {@link android.app.WallpaperColors} object is by using 804 * {@link android.app.WallpaperColors#fromBitmap(Bitmap)} or 805 * {@link android.app.WallpaperColors#fromDrawable(Drawable)}, but you can also specify 806 * your main colors by constructing a {@link android.app.WallpaperColors} object manually. 807 * 808 * @return Wallpaper colors. 809 */ onComputeColors()810 public @Nullable WallpaperColors onComputeColors() { 811 return null; 812 } 813 814 /** 815 * Send the changed local color areas for the connection 816 * @param regions 817 * @param colors 818 * @hide 819 */ notifyLocalColorsChanged(@onNull List<RectF> regions, @NonNull List<WallpaperColors> colors)820 public void notifyLocalColorsChanged(@NonNull List<RectF> regions, 821 @NonNull List<WallpaperColors> colors) 822 throws RuntimeException { 823 for (int i = 0; i < regions.size() && i < colors.size(); i++) { 824 WallpaperColors color = colors.get(i); 825 RectF area = regions.get(i); 826 if (color == null || area == null) { 827 if (DEBUG) { 828 Log.e(TAG, "notifyLocalColorsChanged null values. color: " 829 + color + " area " + area); 830 } 831 continue; 832 } 833 try { 834 mConnection.onLocalWallpaperColorsChanged( 835 area, 836 color, 837 mDisplayContext.getDisplayId() 838 ); 839 } catch (RemoteException e) { 840 throw new RuntimeException(e); 841 } 842 } 843 WallpaperColors primaryColors = mIWallpaperEngine.mWallpaperManager 844 .getWallpaperColors(WallpaperManager.FLAG_SYSTEM); 845 setPrimaryWallpaperColors(primaryColors); 846 } 847 setPrimaryWallpaperColors(WallpaperColors colors)848 private void setPrimaryWallpaperColors(WallpaperColors colors) { 849 if (colors == null) { 850 return; 851 } 852 int colorHints = colors.getColorHints(); 853 boolean shouldDim = ((colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0 854 && (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) == 0); 855 if (shouldDim != mShouldDim) { 856 mShouldDim = shouldDim; 857 updateSurfaceDimming(); 858 updateSurface(false, false, true); 859 } 860 } 861 updateSurfaceDimming()862 private void updateSurfaceDimming() { 863 if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null) { 864 return; 865 } 866 // TODO: apply the dimming to preview as well once surface transparency works in 867 // preview mode. 868 if (!isPreview() && mShouldDim) { 869 Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount); 870 new SurfaceControl.Transaction() 871 .setAlpha(mBbqSurfaceControl, 1 - mWallpaperDimAmount) 872 .apply(); 873 } else { 874 Log.v(TAG, "Setting wallpaper dimming: " + 0); 875 new SurfaceControl.Transaction() 876 .setAlpha(mBbqSurfaceControl, 1.0f) 877 .apply(); 878 } 879 } 880 881 /** 882 * Sets internal engine state. Only for testing. 883 * @param created {@code true} or {@code false}. 884 * @hide 885 */ 886 @VisibleForTesting setCreated(boolean created)887 public void setCreated(boolean created) { 888 mCreated = created; 889 } 890 dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args)891 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 892 out.print(prefix); out.print("mInitializing="); out.print(mInitializing); 893 out.print(" mDestroyed="); out.println(mDestroyed); 894 out.print(prefix); out.print("mVisible="); out.print(mVisible); 895 out.print(" mReportedVisible="); out.println(mReportedVisible); 896 out.print(prefix); out.print("mDisplay="); out.println(mDisplay); 897 out.print(prefix); out.print("mCreated="); out.print(mCreated); 898 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); 899 out.print(" mIsCreating="); out.print(mIsCreating); 900 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); 901 out.print(prefix); out.print("mWidth="); out.print(mWidth); 902 out.print(" mCurWidth="); out.print(mCurWidth); 903 out.print(" mHeight="); out.print(mHeight); 904 out.print(" mCurHeight="); out.println(mCurHeight); 905 out.print(prefix); out.print("mType="); out.print(mType); 906 out.print(" mWindowFlags="); out.print(mWindowFlags); 907 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); 908 out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags); 909 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags); 910 out.print(prefix); out.println("mWinFrames="); out.println(mWinFrames); 911 out.print(prefix); out.print("mConfiguration="); 912 out.println(mMergedConfiguration.getMergedConfiguration()); 913 out.print(prefix); out.print("mLayout="); out.println(mLayout); 914 out.print(prefix); out.print("mZoom="); out.println(mZoom); 915 out.print(prefix); out.print("mPreviewSurfacePosition="); 916 out.println(mPreviewSurfacePosition); 917 synchronized (mLock) { 918 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); 919 out.print(" mPendingXOffset="); out.println(mPendingXOffset); 920 out.print(prefix); out.print("mPendingXOffsetStep="); 921 out.print(mPendingXOffsetStep); 922 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); 923 out.print(prefix); out.print("mOffsetMessageEnqueued="); 924 out.print(mOffsetMessageEnqueued); 925 out.print(" mPendingSync="); out.println(mPendingSync); 926 if (mPendingMove != null) { 927 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); 928 } 929 } 930 } 931 932 /** 933 * Set the wallpaper zoom to the given value. This value will be ignored when in ambient 934 * mode (and zoom will be reset to 0). 935 * @hide 936 * @param zoom between 0 and 1 (inclusive) indicating fully zoomed in to fully zoomed out 937 * respectively. 938 */ 939 @VisibleForTesting setZoom(float zoom)940 public void setZoom(float zoom) { 941 if (DEBUG) { 942 Log.v(TAG, "set zoom received: " + zoom); 943 } 944 boolean updated = false; 945 synchronized (mLock) { 946 if (DEBUG) { 947 Log.v(TAG, "mZoom: " + mZoom + " updated: " + zoom); 948 } 949 if (mIsInAmbientMode) { 950 mZoom = 0; 951 } 952 if (Float.compare(zoom, mZoom) != 0) { 953 mZoom = zoom; 954 updated = true; 955 } 956 } 957 if (DEBUG) Log.v(TAG, "setZoom updated? " + updated); 958 if (updated && !mDestroyed) { 959 onZoomChanged(mZoom); 960 } 961 } 962 dispatchPointer(MotionEvent event)963 private void dispatchPointer(MotionEvent event) { 964 if (event.isTouchEvent()) { 965 synchronized (mLock) { 966 if (event.getAction() == MotionEvent.ACTION_MOVE) { 967 mPendingMove = event; 968 } else { 969 mPendingMove = null; 970 } 971 } 972 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 973 mCaller.sendMessage(msg); 974 } else { 975 event.recycle(); 976 } 977 } 978 updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded)979 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { 980 if (mDestroyed) { 981 Log.w(TAG, "Ignoring updateSurface due to destroyed"); 982 return; 983 } 984 985 boolean fixedSize = false; 986 int myWidth = mSurfaceHolder.getRequestedWidth(); 987 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 988 else fixedSize = true; 989 int myHeight = mSurfaceHolder.getRequestedHeight(); 990 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 991 else fixedSize = true; 992 993 final boolean creating = !mCreated; 994 final boolean surfaceCreating = !mSurfaceCreated; 995 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 996 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 997 boolean insetsChanged = !mCreated; 998 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 999 final boolean flagsChanged = mCurWindowFlags != mWindowFlags || 1000 mCurWindowPrivateFlags != mWindowPrivateFlags; 1001 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 1002 || typeChanged || flagsChanged || redrawNeeded 1003 || !mIWallpaperEngine.mShownReported) { 1004 1005 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 1006 + " format=" + formatChanged + " size=" + sizeChanged); 1007 1008 try { 1009 mWidth = myWidth; 1010 mHeight = myHeight; 1011 mFormat = mSurfaceHolder.getRequestedFormat(); 1012 mType = mSurfaceHolder.getRequestedType(); 1013 1014 mLayout.x = 0; 1015 mLayout.y = 0; 1016 1017 mLayout.width = myWidth; 1018 mLayout.height = myHeight; 1019 mLayout.format = mFormat; 1020 1021 mCurWindowFlags = mWindowFlags; 1022 mLayout.flags = mWindowFlags 1023 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 1024 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 1025 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1026 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 1027 mCurWindowPrivateFlags = mWindowPrivateFlags; 1028 mLayout.privateFlags = mWindowPrivateFlags; 1029 1030 mLayout.memoryType = mType; 1031 mLayout.token = mWindowToken; 1032 1033 if (!mCreated) { 1034 // Retrieve watch round info 1035 TypedArray windowStyle = obtainStyledAttributes( 1036 com.android.internal.R.styleable.Window); 1037 windowStyle.recycle(); 1038 1039 // Add window 1040 mLayout.type = mIWallpaperEngine.mWindowType; 1041 mLayout.gravity = Gravity.START|Gravity.TOP; 1042 mLayout.setFitInsetsTypes(0 /* types */); 1043 mLayout.setTitle(WallpaperService.this.getClass().getName()); 1044 mLayout.windowAnimations = 1045 com.android.internal.R.style.Animation_Wallpaper; 1046 InputChannel inputChannel = new InputChannel(); 1047 1048 if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE, 1049 mDisplay.getDisplayId(), mRequestedVisibilities, inputChannel, 1050 mInsetsState, mTempControls) < 0) { 1051 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 1052 return; 1053 } 1054 mSession.setShouldZoomOutWallpaper(mWindow, shouldZoomOutWallpaper()); 1055 mCreated = true; 1056 1057 mInputEventReceiver = new WallpaperInputEventReceiver( 1058 inputChannel, Looper.myLooper()); 1059 } 1060 1061 mSurfaceHolder.mSurfaceLock.lock(); 1062 mDrawingAllowed = true; 1063 1064 if (!fixedSize) { 1065 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 1066 } else { 1067 mLayout.surfaceInsets.set(0, 0, 0, 0); 1068 } 1069 1070 final int relayoutResult = mSession.relayout( 1071 mWindow, mLayout, mWidth, mHeight, 1072 View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl, 1073 mInsetsState, mTempControls, mSurfaceSize); 1074 if (mSurfaceControl.isValid()) { 1075 if (mBbqSurfaceControl == null) { 1076 mBbqSurfaceControl = new SurfaceControl.Builder() 1077 .setName("Wallpaper BBQ wrapper") 1078 .setHidden(false) 1079 // TODO(b/192291754) 1080 .setMetadata(METADATA_WINDOW_TYPE, TYPE_WALLPAPER) 1081 .setBLASTLayer() 1082 .setParent(mSurfaceControl) 1083 .setCallsite("Wallpaper#relayout") 1084 .build(); 1085 updateSurfaceDimming(); 1086 } 1087 // Propagate transform hint from WM so we can use the right hint for the 1088 // first frame. 1089 mBbqSurfaceControl.setTransformHint(mSurfaceControl.getTransformHint()); 1090 Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x, 1091 mSurfaceSize.y, mFormat); 1092 // If blastSurface == null that means it hasn't changed since the last 1093 // time we called. In this situation, avoid calling transferFrom as we 1094 // would then inc the generation ID and cause EGL resources to be recreated. 1095 if (blastSurface != null) { 1096 mSurfaceHolder.mSurface.transferFrom(blastSurface); 1097 } 1098 } 1099 if (!mLastSurfaceSize.equals(mSurfaceSize)) { 1100 mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y); 1101 } 1102 1103 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 1104 + ", frame=" + mWinFrames); 1105 1106 int w = mWinFrames.frame.width(); 1107 int h = mWinFrames.frame.height(); 1108 1109 final DisplayCutout rawCutout = mInsetsState.getDisplayCutout(); 1110 final Configuration config = getResources().getConfiguration(); 1111 final Rect visibleFrame = new Rect(mWinFrames.frame); 1112 visibleFrame.intersect(mInsetsState.getDisplayFrame()); 1113 WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame, 1114 null /* ignoringVisibilityState */, config.isScreenRound(), 1115 false /* alwaysConsumeSystemBars */, mLayout.softInputMode, 1116 mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type, 1117 config.windowConfiguration.getWindowingMode(), null /* typeSideMap */); 1118 1119 if (!fixedSize) { 1120 final Rect padding = mIWallpaperEngine.mDisplayPadding; 1121 w += padding.left + padding.right; 1122 h += padding.top + padding.bottom; 1123 windowInsets = windowInsets.insetUnchecked( 1124 -padding.left, -padding.top, -padding.right, -padding.bottom); 1125 } else { 1126 w = myWidth; 1127 h = myHeight; 1128 } 1129 1130 if (mCurWidth != w) { 1131 sizeChanged = true; 1132 mCurWidth = w; 1133 } 1134 if (mCurHeight != h) { 1135 sizeChanged = true; 1136 mCurHeight = h; 1137 } 1138 1139 if (DEBUG) { 1140 Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight); 1141 } 1142 1143 final Rect contentInsets = windowInsets.getSystemWindowInsets().toRect(); 1144 final Rect stableInsets = windowInsets.getStableInsets().toRect(); 1145 final DisplayCutout displayCutout = windowInsets.getDisplayCutout() != null 1146 ? windowInsets.getDisplayCutout() : rawCutout; 1147 insetsChanged |= !mDispatchedContentInsets.equals(contentInsets); 1148 insetsChanged |= !mDispatchedStableInsets.equals(stableInsets); 1149 insetsChanged |= !mDispatchedDisplayCutout.equals(displayCutout); 1150 1151 mSurfaceHolder.setSurfaceFrameSize(w, h); 1152 mSurfaceHolder.mSurfaceLock.unlock(); 1153 1154 if (!mSurfaceHolder.mSurface.isValid()) { 1155 reportSurfaceDestroyed(); 1156 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 1157 return; 1158 } 1159 1160 boolean didSurface = false; 1161 1162 try { 1163 mSurfaceHolder.ungetCallbacks(); 1164 1165 if (surfaceCreating) { 1166 mIsCreating = true; 1167 didSurface = true; 1168 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 1169 + mSurfaceHolder + "): " + this); 1170 onSurfaceCreated(mSurfaceHolder); 1171 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1172 if (callbacks != null) { 1173 for (SurfaceHolder.Callback c : callbacks) { 1174 c.surfaceCreated(mSurfaceHolder); 1175 } 1176 } 1177 } 1178 1179 redrawNeeded |= creating || (relayoutResult 1180 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 1181 1182 if (forceReport || creating || surfaceCreating 1183 || formatChanged || sizeChanged) { 1184 if (DEBUG) { 1185 RuntimeException e = new RuntimeException(); 1186 e.fillInStackTrace(); 1187 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 1188 + " formatChanged=" + formatChanged 1189 + " sizeChanged=" + sizeChanged, e); 1190 } 1191 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 1192 + mSurfaceHolder + ", " + mFormat 1193 + ", " + mCurWidth + ", " + mCurHeight 1194 + "): " + this); 1195 didSurface = true; 1196 onSurfaceChanged(mSurfaceHolder, mFormat, 1197 mCurWidth, mCurHeight); 1198 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1199 if (callbacks != null) { 1200 for (SurfaceHolder.Callback c : callbacks) { 1201 c.surfaceChanged(mSurfaceHolder, mFormat, 1202 mCurWidth, mCurHeight); 1203 } 1204 } 1205 } 1206 1207 if (insetsChanged) { 1208 mDispatchedContentInsets.set(contentInsets); 1209 mDispatchedStableInsets.set(stableInsets); 1210 mDispatchedDisplayCutout = displayCutout; 1211 if (DEBUG) { 1212 Log.v(TAG, "dispatching insets=" + windowInsets); 1213 } 1214 onApplyWindowInsets(windowInsets); 1215 } 1216 1217 if (redrawNeeded) { 1218 onSurfaceRedrawNeeded(mSurfaceHolder); 1219 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1220 if (callbacks != null) { 1221 for (SurfaceHolder.Callback c : callbacks) { 1222 if (c instanceof SurfaceHolder.Callback2) { 1223 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 1224 mSurfaceHolder); 1225 } 1226 } 1227 } 1228 } 1229 1230 if (didSurface && !mReportedVisible) { 1231 // This wallpaper is currently invisible, but its 1232 // surface has changed. At this point let's tell it 1233 // again that it is invisible in case the report about 1234 // the surface caused it to start running. We really 1235 // don't want wallpapers running when not visible. 1236 if (mIsCreating) { 1237 // Some wallpapers will ignore this call if they 1238 // had previously been told they were invisble, 1239 // so if we are creating a new surface then toggle 1240 // the state to get them to notice. 1241 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 1242 + this); 1243 onVisibilityChanged(true); 1244 } 1245 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 1246 + this); 1247 onVisibilityChanged(false); 1248 } 1249 } finally { 1250 mIsCreating = false; 1251 mSurfaceCreated = true; 1252 if (redrawNeeded) { 1253 resetWindowPages(); 1254 mSession.finishDrawing(mWindow, null /* postDrawTransaction */); 1255 processLocalColors(mPendingXOffset, mPendingXOffsetStep); 1256 } 1257 reposition(); 1258 reportEngineShown(shouldWaitForEngineShown()); 1259 } 1260 } catch (RemoteException ex) { 1261 } 1262 if (DEBUG) Log.v( 1263 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 1264 " w=" + mLayout.width + " h=" + mLayout.height); 1265 } 1266 } 1267 scalePreview(Rect position)1268 private void scalePreview(Rect position) { 1269 if (isPreview() && mPreviewSurfacePosition == null && position != null 1270 || mPreviewSurfacePosition != null 1271 && !mPreviewSurfacePosition.equals(position)) { 1272 mPreviewSurfacePosition = position; 1273 if (mSurfaceControl.isValid()) { 1274 reposition(); 1275 } else { 1276 updateSurface(false, false, false); 1277 } 1278 } 1279 } 1280 reposition()1281 private void reposition() { 1282 if (mPreviewSurfacePosition == null) { 1283 return; 1284 } 1285 if (DEBUG) { 1286 Log.i(TAG, "reposition: rect: " + mPreviewSurfacePosition); 1287 } 1288 1289 mTmpMatrix.setTranslate(mPreviewSurfacePosition.left, mPreviewSurfacePosition.top); 1290 mTmpMatrix.postScale(((float) mPreviewSurfacePosition.width()) / mCurWidth, 1291 ((float) mPreviewSurfacePosition.height()) / mCurHeight); 1292 mTmpMatrix.getValues(mTmpValues); 1293 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 1294 t.setPosition(mSurfaceControl, mPreviewSurfacePosition.left, 1295 mPreviewSurfacePosition.top); 1296 t.setMatrix(mSurfaceControl, mTmpValues[MSCALE_X], mTmpValues[MSKEW_Y], 1297 mTmpValues[MSKEW_X], mTmpValues[MSCALE_Y]); 1298 t.apply(); 1299 } 1300 attach(IWallpaperEngineWrapper wrapper)1301 void attach(IWallpaperEngineWrapper wrapper) { 1302 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 1303 if (mDestroyed) { 1304 return; 1305 } 1306 1307 mIWallpaperEngine = wrapper; 1308 mCaller = wrapper.mCaller; 1309 mConnection = wrapper.mConnection; 1310 mWindowToken = wrapper.mWindowToken; 1311 mSurfaceHolder.setSizeFromLayout(); 1312 mInitializing = true; 1313 mSession = WindowManagerGlobal.getWindowSession(); 1314 1315 mWindow.setSession(mSession); 1316 1317 mLayout.packageName = getPackageName(); 1318 mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener, 1319 mCaller.getHandler()); 1320 mDisplay = mIWallpaperEngine.mDisplay; 1321 // Use window context of TYPE_WALLPAPER so client can access UI resources correctly. 1322 mDisplayContext = createDisplayContext(mDisplay) 1323 .createWindowContext(TYPE_WALLPAPER, null /* options */); 1324 mWallpaperDimAmount = mDisplayContext.getResources().getFloat( 1325 com.android.internal.R.dimen.config_wallpaperDimAmount); 1326 mDisplayState = mDisplay.getState(); 1327 1328 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 1329 onCreate(mSurfaceHolder); 1330 1331 mInitializing = false; 1332 1333 mReportedVisible = false; 1334 updateSurface(false, false, false); 1335 } 1336 1337 /** 1338 * The {@link Context} with resources that match the current display the wallpaper is on. 1339 * For multiple display environment, multiple engines can be created to render on each 1340 * display, but these displays may have different densities. Use this context to get the 1341 * corresponding resources for currently display, avoiding the context of the service. 1342 * <p> 1343 * The display context will never be {@code null} after 1344 * {@link Engine#onCreate(SurfaceHolder)} has been called. 1345 * 1346 * @return A {@link Context} for current display. 1347 */ 1348 @Nullable getDisplayContext()1349 public Context getDisplayContext() { 1350 return mDisplayContext; 1351 } 1352 1353 /** 1354 * Executes life cycle event and updates internal ambient mode state based on 1355 * message sent from handler. 1356 * 1357 * @param inAmbientMode {@code true} if in ambient mode. 1358 * @param animationDuration For how long the transition will last, in ms. 1359 * @hide 1360 */ 1361 @VisibleForTesting doAmbientModeChanged(boolean inAmbientMode, long animationDuration)1362 public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) { 1363 if (!mDestroyed) { 1364 if (DEBUG) { 1365 Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", " 1366 + animationDuration + "): " + this); 1367 } 1368 mIsInAmbientMode = inAmbientMode; 1369 if (mCreated) { 1370 onAmbientModeChanged(inAmbientMode, animationDuration); 1371 } 1372 } 1373 } 1374 doDesiredSizeChanged(int desiredWidth, int desiredHeight)1375 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 1376 if (!mDestroyed) { 1377 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 1378 + desiredWidth + "," + desiredHeight + "): " + this); 1379 mIWallpaperEngine.mReqWidth = desiredWidth; 1380 mIWallpaperEngine.mReqHeight = desiredHeight; 1381 onDesiredSizeChanged(desiredWidth, desiredHeight); 1382 doOffsetsChanged(true); 1383 } 1384 } 1385 doDisplayPaddingChanged(Rect padding)1386 void doDisplayPaddingChanged(Rect padding) { 1387 if (!mDestroyed) { 1388 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 1389 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 1390 mIWallpaperEngine.mDisplayPadding.set(padding); 1391 updateSurface(true, false, false); 1392 } 1393 } 1394 } 1395 doVisibilityChanged(boolean visible)1396 void doVisibilityChanged(boolean visible) { 1397 if (!mDestroyed) { 1398 mVisible = visible; 1399 reportVisibility(); 1400 if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep); 1401 } 1402 } 1403 reportVisibility()1404 void reportVisibility() { 1405 if (mScreenshotSurfaceControl != null && mVisible) { 1406 if (DEBUG) Log.v(TAG, "Frozen so don't report visibility change"); 1407 return; 1408 } 1409 if (!mDestroyed) { 1410 mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState(); 1411 boolean visible = mVisible && mDisplayState != Display.STATE_OFF; 1412 if (mReportedVisible != visible) { 1413 mReportedVisible = visible; 1414 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 1415 + "): " + this); 1416 if (visible) { 1417 // If becoming visible, in preview mode the surface 1418 // may have been destroyed so now we need to make 1419 // sure it is re-created. 1420 doOffsetsChanged(false); 1421 // force relayout to get new surface 1422 updateSurface(true, false, false); 1423 } 1424 onVisibilityChanged(visible); 1425 if (mReportedVisible && mFrozenRequested) { 1426 if (DEBUG) Log.v(TAG, "Freezing wallpaper after visibility update"); 1427 freeze(); 1428 } 1429 } 1430 } 1431 } 1432 doOffsetsChanged(boolean always)1433 void doOffsetsChanged(boolean always) { 1434 if (mDestroyed) { 1435 return; 1436 } 1437 1438 if (!always && !mOffsetsChanged) { 1439 return; 1440 } 1441 1442 float xOffset; 1443 float yOffset; 1444 float xOffsetStep; 1445 float yOffsetStep; 1446 boolean sync; 1447 synchronized (mLock) { 1448 xOffset = mPendingXOffset; 1449 yOffset = mPendingYOffset; 1450 xOffsetStep = mPendingXOffsetStep; 1451 yOffsetStep = mPendingYOffsetStep; 1452 sync = mPendingSync; 1453 mPendingSync = false; 1454 mOffsetMessageEnqueued = false; 1455 } 1456 1457 if (mSurfaceCreated) { 1458 if (mReportedVisible) { 1459 if (DEBUG) Log.v(TAG, "Offsets change in " + this 1460 + ": " + xOffset + "," + yOffset); 1461 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 1462 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 1463 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 1464 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 1465 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 1466 } else { 1467 mOffsetsChanged = true; 1468 } 1469 } 1470 1471 if (sync) { 1472 try { 1473 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 1474 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 1475 } catch (RemoteException e) { 1476 } 1477 } 1478 1479 // setup local color extraction data 1480 processLocalColors(xOffset, xOffsetStep); 1481 } 1482 processLocalColors(float xOffset, float xOffsetStep)1483 private void processLocalColors(float xOffset, float xOffsetStep) { 1484 // implemented by the wallpaper 1485 if (supportsLocalColorExtraction()) return; 1486 if (DEBUG) { 1487 Log.d(TAG, "processLocalColors " + xOffset + " of step " 1488 + xOffsetStep); 1489 } 1490 //below is the default implementation 1491 if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN 1492 || !mSurfaceHolder.getSurface().isValid()) return; 1493 int xCurrentPage; 1494 int xPages; 1495 if (!validStep(xOffsetStep)) { 1496 if (DEBUG) { 1497 Log.w(TAG, "invalid offset step " + xOffsetStep); 1498 } 1499 xOffset = 0; 1500 xOffsetStep = 1; 1501 xCurrentPage = 0; 1502 xPages = 1; 1503 } else { 1504 xPages = Math.round(1 / xOffsetStep) + 1; 1505 xOffsetStep = (float) 1 / (float) xPages; 1506 float shrink = (float) (xPages - 1) / (float) xPages; 1507 xOffset *= shrink; 1508 xCurrentPage = Math.round(xOffset / xOffsetStep); 1509 } 1510 if (DEBUG) { 1511 Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage); 1512 Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset); 1513 } 1514 1515 float finalXOffsetStep = xOffsetStep; 1516 float finalXOffset = xOffset; 1517 mHandler.post(() -> { 1518 resetWindowPages(); 1519 int xPage = xCurrentPage; 1520 EngineWindowPage current; 1521 if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { 1522 mWindowPages = new EngineWindowPage[xPages]; 1523 initWindowPages(mWindowPages, finalXOffsetStep); 1524 } 1525 if (mLocalColorsToAdd.size() != 0) { 1526 for (RectF colorArea : mLocalColorsToAdd) { 1527 if (!isValid(colorArea)) continue; 1528 mLocalColorAreas.add(colorArea); 1529 int colorPage = getRectFPage(colorArea, finalXOffsetStep); 1530 EngineWindowPage currentPage = mWindowPages[colorPage]; 1531 if (currentPage == null) { 1532 currentPage = new EngineWindowPage(); 1533 currentPage.addArea(colorArea); 1534 mWindowPages[colorPage] = currentPage; 1535 } else { 1536 currentPage.setLastUpdateTime(0); 1537 currentPage.removeColor(colorArea); 1538 } 1539 } 1540 mLocalColorsToAdd.clear(); 1541 } 1542 if (xPage >= mWindowPages.length) { 1543 if (DEBUG) { 1544 Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage); 1545 Log.e(TAG, "error on page " + xPage + " out of " + xPages); 1546 Log.e(TAG, 1547 "error on xOffsetStep " + finalXOffsetStep 1548 + " xOffset " + finalXOffset); 1549 } 1550 xPage = mWindowPages.length - 1; 1551 } 1552 current = mWindowPages[xPage]; 1553 if (current == null) { 1554 if (DEBUG) Log.d(TAG, "making page " + xPage + " out of " + xPages); 1555 if (DEBUG) { 1556 Log.d(TAG, "xOffsetStep " + finalXOffsetStep + " xOffset " 1557 + finalXOffset); 1558 } 1559 current = new EngineWindowPage(); 1560 mWindowPages[xPage] = current; 1561 } 1562 updatePage(current, xPage, xPages, finalXOffsetStep); 1563 }); 1564 } 1565 initWindowPages(EngineWindowPage[] windowPages, float step)1566 private void initWindowPages(EngineWindowPage[] windowPages, float step) { 1567 for (int i = 0; i < windowPages.length; i++) { 1568 windowPages[i] = new EngineWindowPage(); 1569 } 1570 mLocalColorAreas.addAll(mLocalColorsToAdd); 1571 mLocalColorsToAdd.clear(); 1572 for (RectF area: mLocalColorAreas) { 1573 if (!isValid(area)) { 1574 mLocalColorAreas.remove(area); 1575 continue; 1576 } 1577 int pageNum = getRectFPage(area, step); 1578 windowPages[pageNum].addArea(area); 1579 } 1580 } 1581 updatePage(EngineWindowPage currentPage, int pageIndx, int numPages, float xOffsetStep)1582 void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages, 1583 float xOffsetStep) { 1584 // to save creating a runnable, check twice 1585 long current = System.currentTimeMillis(); 1586 long lapsed = current - currentPage.getLastUpdateTime(); 1587 // Always update the page when the last update time is <= 0 1588 // This is important especially when the device first boots 1589 if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION 1590 && currentPage.getLastUpdateTime() > 0) { 1591 return; 1592 } 1593 Surface surface = mSurfaceHolder.getSurface(); 1594 boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y; 1595 int smaller = widthIsLarger ? mSurfaceSize.x 1596 : mSurfaceSize.y; 1597 float ratio = (float) MIN_BITMAP_SCREENSHOT_WIDTH / (float) smaller; 1598 int width = (int) (ratio * mSurfaceSize.x); 1599 int height = (int) (ratio * mSurfaceSize.y); 1600 if (width <= 0 || height <= 0) { 1601 Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); 1602 return; 1603 } 1604 Bitmap screenShot = Bitmap.createBitmap(width, height, 1605 Bitmap.Config.ARGB_8888); 1606 final Bitmap finalScreenShot = screenShot; 1607 Trace.beginSection("WallpaperService#pixelCopy"); 1608 PixelCopy.request(surface, screenShot, (res) -> { 1609 Trace.endSection(); 1610 if (DEBUG) Log.d(TAG, "result of pixel copy is " + res); 1611 if (res != PixelCopy.SUCCESS) { 1612 Bitmap lastBitmap = currentPage.getBitmap(); 1613 // assign the last bitmap taken for now 1614 currentPage.setBitmap(mLastScreenshot); 1615 Bitmap lastScreenshot = mLastScreenshot; 1616 if (lastScreenshot != null && !lastScreenshot.isRecycled() 1617 && !Objects.equals(lastBitmap, lastScreenshot)) { 1618 updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); 1619 } 1620 } else { 1621 mLastScreenshot = finalScreenShot; 1622 // going to hold this lock for a while 1623 currentPage.setBitmap(finalScreenShot); 1624 currentPage.setLastUpdateTime(current); 1625 updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); 1626 } 1627 }, mHandler); 1628 1629 } 1630 // locked by the passed page updatePageColors(EngineWindowPage page, int pageIndx, int numPages, float xOffsetStep)1631 private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages, 1632 float xOffsetStep) { 1633 if (page.getBitmap() == null) return; 1634 if (DEBUG) { 1635 Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " 1636 + page.getAreas().size() + " and bitmap size of " 1637 + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); 1638 } 1639 for (RectF area: page.getAreas()) { 1640 if (area == null) continue; 1641 RectF subArea = generateSubRect(area, pageIndx, numPages); 1642 Bitmap b = page.getBitmap(); 1643 int x = Math.round(b.getWidth() * subArea.left); 1644 int y = Math.round(b.getHeight() * subArea.top); 1645 int width = Math.round(b.getWidth() * subArea.width()); 1646 int height = Math.round(b.getHeight() * subArea.height()); 1647 Bitmap target; 1648 try { 1649 target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height); 1650 } catch (Exception e) { 1651 Log.e(TAG, "Error creating page local color bitmap", e); 1652 continue; 1653 } 1654 WallpaperColors color = WallpaperColors.fromBitmap(target); 1655 target.recycle(); 1656 WallpaperColors currentColor = page.getColors(area); 1657 1658 if (DEBUG) { 1659 Log.d(TAG, "getting local bitmap area x " + x + " y " + y 1660 + " width " + width + " height " + height + " for sub area " + subArea 1661 + " and with page " + pageIndx + " of " + numPages); 1662 1663 } 1664 if (currentColor == null || !color.equals(currentColor)) { 1665 page.addWallpaperColors(area, color); 1666 if (DEBUG) { 1667 Log.d(TAG, "onLocalWallpaperColorsChanged" 1668 + " local color callback for area" + area + " for page " + pageIndx 1669 + " of " + numPages); 1670 } 1671 try { 1672 mConnection.onLocalWallpaperColorsChanged(area, color, 1673 mDisplayContext.getDisplayId()); 1674 } catch (RemoteException e) { 1675 Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); 1676 } 1677 } 1678 } 1679 } 1680 generateSubRect(RectF in, int pageInx, int numPages)1681 private RectF generateSubRect(RectF in, int pageInx, int numPages) { 1682 float minLeft = (float) (pageInx) / (float) (numPages); 1683 float maxRight = (float) (pageInx + 1) / (float) (numPages); 1684 float left = in.left; 1685 float right = in.right; 1686 1687 // bound rect 1688 if (left < minLeft) left = minLeft; 1689 if (right > maxRight) right = maxRight; 1690 1691 // scale up the sub area then trim 1692 left = (left * (float) numPages) % 1f; 1693 right = (right * (float) numPages) % 1f; 1694 if (right == 0f) { 1695 right = 1f; 1696 } 1697 1698 return new RectF(left, in.top, right, in.bottom); 1699 } 1700 resetWindowPages()1701 private void resetWindowPages() { 1702 if (supportsLocalColorExtraction()) return; 1703 if (!mResetWindowPages) return; 1704 mResetWindowPages = false; 1705 mLastWindowPage = -1; 1706 for (int i = 0; i < mWindowPages.length; i++) { 1707 EngineWindowPage page = mWindowPages[i]; 1708 if (page != null) { 1709 page.setLastUpdateTime(0L); 1710 } 1711 } 1712 } 1713 getRectFPage(RectF area, float step)1714 private int getRectFPage(RectF area, float step) { 1715 if (!isValid(area)) return 0; 1716 if (!validStep(step)) return 0; 1717 int pages = Math.round(1 / step); 1718 int page = Math.round(area.centerX() * pages); 1719 if (page == pages) return pages - 1; 1720 if (page == mWindowPages.length) page = mWindowPages.length - 1; 1721 return page; 1722 } 1723 1724 /** 1725 * Add local colors areas of interest 1726 * @param regions list of areas 1727 * @hide 1728 */ addLocalColorsAreas(@onNull List<RectF> regions)1729 public void addLocalColorsAreas(@NonNull List<RectF> regions) { 1730 if (supportsLocalColorExtraction()) return; 1731 if (DEBUG) { 1732 Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); 1733 } 1734 mHandler.post(() -> { 1735 mLocalColorsToAdd.addAll(regions); 1736 processLocalColors(mPendingXOffset, mPendingYOffset); 1737 }); 1738 1739 1740 } 1741 1742 /** 1743 * Remove local colors areas of interest if they exist 1744 * @param regions list of areas 1745 * @hide 1746 */ removeLocalColorsAreas(@onNull List<RectF> regions)1747 public void removeLocalColorsAreas(@NonNull List<RectF> regions) { 1748 if (supportsLocalColorExtraction()) return; 1749 mHandler.post(() -> { 1750 float step = mPendingXOffsetStep; 1751 mLocalColorsToAdd.removeAll(regions); 1752 mLocalColorAreas.removeAll(regions); 1753 if (!validStep(step)) { 1754 return; 1755 } 1756 for (int i = 0; i < mWindowPages.length; i++) { 1757 for (int j = 0; j < regions.size(); j++) { 1758 EngineWindowPage page = mWindowPages[i]; 1759 if (page != null) page.removeArea(regions.get(j)); 1760 } 1761 } 1762 }); 1763 } 1764 1765 // fix the rect to be included within the bounds of the bitmap fixRect(Bitmap b, Rect r)1766 private Rect fixRect(Bitmap b, Rect r) { 1767 r.left = r.left >= r.right || r.left >= b.getWidth() || r.left > 0 1768 ? 0 1769 : r.left; 1770 r.right = r.left >= r.right || r.right > b.getWidth() 1771 ? b.getWidth() 1772 : r.right; 1773 return r; 1774 } 1775 validStep(float step)1776 private boolean validStep(float step) { 1777 return !PROHIBITED_STEPS.contains(step) && step > 0. && step <= 1.; 1778 } 1779 doCommand(WallpaperCommand cmd)1780 void doCommand(WallpaperCommand cmd) { 1781 Bundle result; 1782 if (!mDestroyed) { 1783 if (COMMAND_FREEZE.equals(cmd.action) || COMMAND_UNFREEZE.equals(cmd.action)) { 1784 updateFrozenState(/* frozenRequested= */ !COMMAND_UNFREEZE.equals(cmd.action)); 1785 } 1786 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 1787 cmd.extras, cmd.sync); 1788 } else { 1789 result = null; 1790 } 1791 if (cmd.sync) { 1792 try { 1793 if (DEBUG) Log.v(TAG, "Reporting command complete"); 1794 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 1795 } catch (RemoteException e) { 1796 } 1797 } 1798 } 1799 updateFrozenState(boolean frozenRequested)1800 private void updateFrozenState(boolean frozenRequested) { 1801 if (mIWallpaperEngine.mWallpaperManager.getWallpaperInfo() == null 1802 // Procees the unfreeze command in case the wallaper became static while 1803 // being paused. 1804 && frozenRequested) { 1805 if (DEBUG) Log.v(TAG, "Ignoring the freeze command for static wallpapers"); 1806 return; 1807 } 1808 mFrozenRequested = frozenRequested; 1809 boolean isFrozen = mScreenshotSurfaceControl != null; 1810 if (mFrozenRequested == isFrozen) { 1811 return; 1812 } 1813 if (mFrozenRequested) { 1814 freeze(); 1815 } else { 1816 unfreeze(); 1817 } 1818 } 1819 freeze()1820 private void freeze() { 1821 if (!mReportedVisible || mDestroyed) { 1822 // Screenshot can't be taken until visibility is reported to the wallpaper host. 1823 return; 1824 } 1825 if (!showScreenshotOfWallpaper()) { 1826 return; 1827 } 1828 // Prevent a wallpaper host from rendering wallpaper behind a screeshot. 1829 doVisibilityChanged(false); 1830 // Remember that visibility is requested since it's not guaranteed that 1831 // mWindow#dispatchAppVisibility will be called when letterboxed application with 1832 // wallpaper background transitions to the Home screen. 1833 mVisible = true; 1834 } 1835 unfreeze()1836 private void unfreeze() { 1837 cleanUpScreenshotSurfaceControl(); 1838 if (mVisible) { 1839 doVisibilityChanged(true); 1840 } 1841 } 1842 cleanUpScreenshotSurfaceControl()1843 private void cleanUpScreenshotSurfaceControl() { 1844 // TODO(b/194399558): Add crossfade transition. 1845 if (mScreenshotSurfaceControl != null) { 1846 new SurfaceControl.Transaction() 1847 .remove(mScreenshotSurfaceControl) 1848 .show(mBbqSurfaceControl) 1849 .apply(); 1850 mScreenshotSurfaceControl = null; 1851 } 1852 } 1853 scaleAndCropScreenshot()1854 void scaleAndCropScreenshot() { 1855 if (mScreenshotSurfaceControl == null) { 1856 return; 1857 } 1858 if (mScreenshotSize.x <= 0 || mScreenshotSize.y <= 0) { 1859 Log.w(TAG, "Unexpected screenshot size: " + mScreenshotSize); 1860 return; 1861 } 1862 // Don't scale down and using the same scaling factor for both dimensions to 1863 // avoid stretching wallpaper image. 1864 float scaleFactor = Math.max(1, Math.max( 1865 ((float) mSurfaceSize.x) / mScreenshotSize.x, 1866 ((float) mSurfaceSize.y) / mScreenshotSize.y)); 1867 int diffX = ((int) (mScreenshotSize.x * scaleFactor)) - mSurfaceSize.x; 1868 int diffY = ((int) (mScreenshotSize.y * scaleFactor)) - mSurfaceSize.y; 1869 if (DEBUG) { 1870 Log.v(TAG, "Adjusting screenshot: scaleFactor=" + scaleFactor 1871 + " diffX=" + diffX + " diffY=" + diffY + " mSurfaceSize=" + mSurfaceSize 1872 + " mScreenshotSize=" + mScreenshotSize); 1873 } 1874 new SurfaceControl.Transaction() 1875 .setMatrix( 1876 mScreenshotSurfaceControl, 1877 /* dsdx= */ scaleFactor, /* dtdx= */ 0, 1878 /* dtdy= */ 0, /* dsdy= */ scaleFactor) 1879 .setWindowCrop( 1880 mScreenshotSurfaceControl, 1881 new Rect( 1882 /* left= */ diffX / 2, 1883 /* top= */ diffY / 2, 1884 /* right= */ diffX / 2 + mScreenshotSize.x, 1885 /* bottom= */ diffY / 2 + mScreenshotSize.y)) 1886 .setPosition(mScreenshotSurfaceControl, -diffX / 2, -diffY / 2) 1887 .apply(); 1888 } 1889 showScreenshotOfWallpaper()1890 private boolean showScreenshotOfWallpaper() { 1891 if (mDestroyed || mSurfaceControl == null || !mSurfaceControl.isValid()) { 1892 if (DEBUG) Log.v(TAG, "Failed to screenshot wallpaper: surface isn't valid"); 1893 return false; 1894 } 1895 1896 final Rect bounds = new Rect(0, 0, mSurfaceSize.x, mSurfaceSize.y); 1897 if (bounds.isEmpty()) { 1898 Log.w(TAG, "Failed to screenshot wallpaper: surface bounds are empty"); 1899 return false; 1900 } 1901 1902 if (mScreenshotSurfaceControl != null) { 1903 Log.e(TAG, "Screenshot is unexpectedly not null"); 1904 // Destroying previous screenshot since it can have different size. 1905 cleanUpScreenshotSurfaceControl(); 1906 } 1907 1908 SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = 1909 SurfaceControl.captureLayers( 1910 new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl) 1911 // Needed because SurfaceFlinger#validateScreenshotPermissions 1912 // uses this parameter to check whether a caller only attempts 1913 // to screenshot itself when call doesn't come from the system. 1914 .setUid(Process.myUid()) 1915 .setChildrenOnly(false) 1916 .setSourceCrop(bounds) 1917 .build()); 1918 1919 if (screenshotBuffer == null) { 1920 Log.w(TAG, "Failed to screenshot wallpaper: screenshotBuffer is null"); 1921 return false; 1922 } 1923 1924 final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); 1925 1926 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 1927 1928 // TODO(b/194399558): Add crossfade transition. 1929 mScreenshotSurfaceControl = new SurfaceControl.Builder() 1930 .setName("Wallpaper snapshot for engine " + this) 1931 .setFormat(hardwareBuffer.getFormat()) 1932 .setParent(mSurfaceControl) 1933 .setSecure(screenshotBuffer.containsSecureLayers()) 1934 .setCallsite("WallpaperService.Engine.showScreenshotOfWallpaper") 1935 .setBLASTLayer() 1936 .build(); 1937 1938 mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y); 1939 1940 GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(hardwareBuffer); 1941 1942 t.setBuffer(mScreenshotSurfaceControl, graphicBuffer); 1943 t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace()); 1944 // Place on top everything else. 1945 t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE); 1946 t.show(mScreenshotSurfaceControl); 1947 t.hide(mBbqSurfaceControl); 1948 t.apply(); 1949 1950 return true; 1951 } 1952 reportSurfaceDestroyed()1953 void reportSurfaceDestroyed() { 1954 if (mSurfaceCreated) { 1955 mSurfaceCreated = false; 1956 mSurfaceHolder.ungetCallbacks(); 1957 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1958 if (callbacks != null) { 1959 for (SurfaceHolder.Callback c : callbacks) { 1960 c.surfaceDestroyed(mSurfaceHolder); 1961 } 1962 } 1963 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 1964 + mSurfaceHolder + "): " + this); 1965 onSurfaceDestroyed(mSurfaceHolder); 1966 } 1967 } 1968 detach()1969 void detach() { 1970 if (mDestroyed) { 1971 return; 1972 } 1973 1974 mDestroyed = true; 1975 1976 if (mIWallpaperEngine.mDisplayManager != null) { 1977 mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener); 1978 } 1979 1980 if (mVisible) { 1981 mVisible = false; 1982 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 1983 onVisibilityChanged(false); 1984 } 1985 1986 reportSurfaceDestroyed(); 1987 1988 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 1989 onDestroy(); 1990 1991 if (mCreated) { 1992 try { 1993 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 1994 + mSurfaceHolder.getSurface() + " of: " + this); 1995 1996 if (mInputEventReceiver != null) { 1997 mInputEventReceiver.dispose(); 1998 mInputEventReceiver = null; 1999 } 2000 2001 mSession.remove(mWindow); 2002 } catch (RemoteException e) { 2003 } 2004 mSurfaceHolder.mSurface.release(); 2005 if (mBlastBufferQueue != null) { 2006 mBlastBufferQueue.destroy(); 2007 mBlastBufferQueue = null; 2008 } 2009 if (mBbqSurfaceControl != null) { 2010 new SurfaceControl.Transaction().remove(mBbqSurfaceControl).apply(); 2011 mBbqSurfaceControl = null; 2012 } 2013 mCreated = false; 2014 } 2015 } 2016 2017 private final DisplayListener mDisplayListener = new DisplayListener() { 2018 @Override 2019 public void onDisplayChanged(int displayId) { 2020 if (mDisplay.getDisplayId() == displayId) { 2021 reportVisibility(); 2022 } 2023 } 2024 2025 @Override 2026 public void onDisplayRemoved(int displayId) { 2027 } 2028 2029 @Override 2030 public void onDisplayAdded(int displayId) { 2031 } 2032 }; 2033 getOrCreateBLASTSurface(int width, int height, int format)2034 private Surface getOrCreateBLASTSurface(int width, int height, int format) { 2035 Surface ret = null; 2036 if (mBlastBufferQueue == null) { 2037 mBlastBufferQueue = new BLASTBufferQueue("Wallpaper", mBbqSurfaceControl, 2038 width, height, format); 2039 // We only return the Surface the first time, as otherwise 2040 // it hasn't changed and there is no need to update. 2041 ret = mBlastBufferQueue.createSurface(); 2042 } else { 2043 mBlastBufferQueue.update(mBbqSurfaceControl, width, height, format); 2044 } 2045 2046 return ret; 2047 } 2048 } 2049 isValid(RectF area)2050 private boolean isValid(RectF area) { 2051 if (area == null) return false; 2052 boolean valid = area.bottom > area.top && area.left < area.right 2053 && LOCAL_COLOR_BOUNDS.contains(area); 2054 return valid; 2055 } 2056 2057 private boolean inRectFRange(float number) { 2058 return number >= 0f && number <= 1f; 2059 } 2060 2061 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 2062 implements HandlerCaller.Callback { 2063 private final HandlerCaller mCaller; 2064 2065 final IWallpaperConnection mConnection; 2066 final IBinder mWindowToken; 2067 final int mWindowType; 2068 final boolean mIsPreview; 2069 boolean mShownReported; 2070 int mReqWidth; 2071 int mReqHeight; 2072 final Rect mDisplayPadding = new Rect(); 2073 final int mDisplayId; 2074 final DisplayManager mDisplayManager; 2075 final Display mDisplay; 2076 final WallpaperManager mWallpaperManager; 2077 private final AtomicBoolean mDetached = new AtomicBoolean(); 2078 2079 Engine mEngine; 2080 IWallpaperEngineWrapper(WallpaperService context, IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId)2081 IWallpaperEngineWrapper(WallpaperService context, 2082 IWallpaperConnection conn, IBinder windowToken, 2083 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, 2084 int displayId) { 2085 mWallpaperManager = getSystemService(WallpaperManager.class); 2086 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); 2087 mConnection = conn; 2088 mWindowToken = windowToken; 2089 mWindowType = windowType; 2090 mIsPreview = isPreview; 2091 mReqWidth = reqWidth; 2092 mReqHeight = reqHeight; 2093 mDisplayPadding.set(padding); 2094 mDisplayId = displayId; 2095 2096 // Create a display context before onCreateEngine. 2097 mDisplayManager = getSystemService(DisplayManager.class); 2098 mDisplay = mDisplayManager.getDisplay(mDisplayId); 2099 2100 if (mDisplay == null) { 2101 // Ignore this engine. 2102 throw new IllegalArgumentException("Cannot find display with id" + mDisplayId); 2103 } 2104 Message msg = mCaller.obtainMessage(DO_ATTACH); 2105 mCaller.sendMessage(msg); 2106 } 2107 setDesiredSize(int width, int height)2108 public void setDesiredSize(int width, int height) { 2109 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 2110 mCaller.sendMessage(msg); 2111 } 2112 setDisplayPadding(Rect padding)2113 public void setDisplayPadding(Rect padding) { 2114 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 2115 mCaller.sendMessage(msg); 2116 } 2117 setVisibility(boolean visible)2118 public void setVisibility(boolean visible) { 2119 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 2120 visible ? 1 : 0); 2121 mCaller.sendMessage(msg); 2122 } 2123 2124 @Override setInAmbientMode(boolean inAmbientDisplay, long animationDuration)2125 public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration) 2126 throws RemoteException { 2127 Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0, 2128 animationDuration); 2129 mCaller.sendMessage(msg); 2130 } 2131 dispatchPointer(MotionEvent event)2132 public void dispatchPointer(MotionEvent event) { 2133 if (mEngine != null) { 2134 mEngine.dispatchPointer(event); 2135 } else { 2136 event.recycle(); 2137 } 2138 } 2139 dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras)2140 public void dispatchWallpaperCommand(String action, int x, int y, 2141 int z, Bundle extras) { 2142 if (mEngine != null) { 2143 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 2144 } 2145 } 2146 setZoomOut(float scale)2147 public void setZoomOut(float scale) { 2148 Message msg = mCaller.obtainMessageI(MSG_ZOOM, Float.floatToIntBits(scale)); 2149 mCaller.sendMessage(msg); 2150 } 2151 reportShown()2152 public void reportShown() { 2153 if (!mShownReported) { 2154 mShownReported = true; 2155 try { 2156 mConnection.engineShown(this); 2157 Log.d(TAG, "Wallpaper has updated the surface:" 2158 + mWallpaperManager.getWallpaperInfo()); 2159 } catch (RemoteException e) { 2160 Log.w(TAG, "Wallpaper host disappeared", e); 2161 return; 2162 } 2163 } 2164 } 2165 requestWallpaperColors()2166 public void requestWallpaperColors() { 2167 Message msg = mCaller.obtainMessage(MSG_REQUEST_WALLPAPER_COLORS); 2168 mCaller.sendMessage(msg); 2169 } 2170 addLocalColorsAreas(List<RectF> regions)2171 public void addLocalColorsAreas(List<RectF> regions) { 2172 mEngine.addLocalColorsAreas(regions); 2173 } 2174 removeLocalColorsAreas(List<RectF> regions)2175 public void removeLocalColorsAreas(List<RectF> regions) { 2176 mEngine.removeLocalColorsAreas(regions); 2177 } 2178 destroy()2179 public void destroy() { 2180 Message msg = mCaller.obtainMessage(DO_DETACH); 2181 mCaller.sendMessage(msg); 2182 } 2183 detach()2184 public void detach() { 2185 mDetached.set(true); 2186 } 2187 scalePreview(Rect position)2188 public void scalePreview(Rect position) { 2189 Message msg = mCaller.obtainMessageO(MSG_SCALE_PREVIEW, position); 2190 mCaller.sendMessage(msg); 2191 } 2192 2193 @Nullable mirrorSurfaceControl()2194 public SurfaceControl mirrorSurfaceControl() { 2195 return mEngine == null ? null : SurfaceControl.mirrorSurface(mEngine.mSurfaceControl); 2196 } 2197 doDetachEngine()2198 private void doDetachEngine() { 2199 mActiveEngines.remove(mEngine); 2200 mEngine.detach(); 2201 // Some wallpapers will not trigger the rendering threads of the remaining engines even 2202 // if they are visible, so we need to toggle the state to get their attention. 2203 if (!mDetached.get()) { 2204 for (Engine eng : mActiveEngines) { 2205 if (eng.mVisible) { 2206 eng.doVisibilityChanged(false); 2207 eng.doVisibilityChanged(true); 2208 } 2209 } 2210 } 2211 } 2212 2213 @Override executeMessage(Message message)2214 public void executeMessage(Message message) { 2215 if (mDetached.get()) { 2216 if (mActiveEngines.contains(mEngine)) { 2217 doDetachEngine(); 2218 } 2219 return; 2220 } 2221 switch (message.what) { 2222 case DO_ATTACH: { 2223 Engine engine = onCreateEngine(); 2224 mEngine = engine; 2225 try { 2226 mConnection.attachEngine(this, mDisplayId); 2227 } catch (RemoteException e) { 2228 engine.detach(); 2229 Log.w(TAG, "Wallpaper host disappeared", e); 2230 return; 2231 } 2232 mActiveEngines.add(engine); 2233 engine.attach(this); 2234 return; 2235 } 2236 case DO_DETACH: { 2237 doDetachEngine(); 2238 return; 2239 } 2240 case DO_SET_DESIRED_SIZE: { 2241 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 2242 return; 2243 } 2244 case DO_SET_DISPLAY_PADDING: { 2245 mEngine.doDisplayPaddingChanged((Rect) message.obj); 2246 return; 2247 } 2248 case DO_IN_AMBIENT_MODE: { 2249 mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj); 2250 return; 2251 } 2252 case MSG_UPDATE_SURFACE: 2253 mEngine.updateSurface(true, false, false); 2254 break; 2255 case MSG_ZOOM: 2256 mEngine.setZoom(Float.intBitsToFloat(message.arg1)); 2257 break; 2258 case MSG_SCALE_PREVIEW: 2259 mEngine.scalePreview((Rect) message.obj); 2260 break; 2261 case MSG_VISIBILITY_CHANGED: 2262 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 2263 + ": " + message.arg1); 2264 mEngine.doVisibilityChanged(message.arg1 != 0); 2265 break; 2266 case MSG_WALLPAPER_OFFSETS: { 2267 mEngine.doOffsetsChanged(true); 2268 } break; 2269 case MSG_WALLPAPER_COMMAND: { 2270 WallpaperCommand cmd = (WallpaperCommand)message.obj; 2271 mEngine.doCommand(cmd); 2272 } break; 2273 case MSG_WINDOW_RESIZED: { 2274 final boolean reportDraw = message.arg1 != 0; 2275 mEngine.updateSurface(true, false, reportDraw); 2276 mEngine.doOffsetsChanged(true); 2277 mEngine.scaleAndCropScreenshot(); 2278 } break; 2279 case MSG_WINDOW_MOVED: { 2280 // Do nothing. What does it mean for a Wallpaper to move? 2281 } break; 2282 case MSG_TOUCH_EVENT: { 2283 boolean skip = false; 2284 MotionEvent ev = (MotionEvent)message.obj; 2285 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 2286 synchronized (mEngine.mLock) { 2287 if (mEngine.mPendingMove == ev) { 2288 mEngine.mPendingMove = null; 2289 } else { 2290 // this is not the motion event we are looking for.... 2291 skip = true; 2292 } 2293 } 2294 } 2295 if (!skip) { 2296 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 2297 mEngine.onTouchEvent(ev); 2298 } 2299 ev.recycle(); 2300 } break; 2301 case MSG_REQUEST_WALLPAPER_COLORS: { 2302 if (mConnection == null) { 2303 break; 2304 } 2305 try { 2306 WallpaperColors colors = mEngine.onComputeColors(); 2307 mEngine.setPrimaryWallpaperColors(colors); 2308 mConnection.onWallpaperColorsChanged(colors, mDisplayId); 2309 } catch (RemoteException e) { 2310 // Connection went away, nothing to do in here. 2311 } 2312 } break; 2313 case MSG_REPORT_SHOWN: { 2314 reportShown(); 2315 } break; 2316 default : 2317 Log.w(TAG, "Unknown message type " + message.what); 2318 } 2319 } 2320 } 2321 2322 /** 2323 * Implements the internal {@link IWallpaperService} interface to convert 2324 * incoming calls to it back to calls on an {@link WallpaperService}. 2325 */ 2326 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 2327 private final WallpaperService mTarget; 2328 private IWallpaperEngineWrapper mEngineWrapper; 2329 IWallpaperServiceWrapper(WallpaperService context)2330 public IWallpaperServiceWrapper(WallpaperService context) { 2331 mTarget = context; 2332 } 2333 2334 @Override attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId)2335 public void attach(IWallpaperConnection conn, IBinder windowToken, 2336 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, 2337 int displayId) { 2338 mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken, 2339 windowType, isPreview, reqWidth, reqHeight, padding, displayId); 2340 } 2341 2342 @Override detach()2343 public void detach() { 2344 mEngineWrapper.detach(); 2345 } 2346 } 2347 2348 @Override onCreate()2349 public void onCreate() { 2350 super.onCreate(); 2351 } 2352 2353 @Override onDestroy()2354 public void onDestroy() { 2355 super.onDestroy(); 2356 for (int i=0; i<mActiveEngines.size(); i++) { 2357 mActiveEngines.get(i).detach(); 2358 } 2359 mActiveEngines.clear(); 2360 } 2361 2362 /** 2363 * Implement to return the implementation of the internal accessibility 2364 * service interface. Subclasses should not override. 2365 */ 2366 @Override onBind(Intent intent)2367 public final IBinder onBind(Intent intent) { 2368 return new IWallpaperServiceWrapper(this); 2369 } 2370 2371 /** 2372 * Must be implemented to return a new instance of the wallpaper's engine. 2373 * Note that multiple instances may be active at the same time, such as 2374 * when the wallpaper is currently set as the active wallpaper and the user 2375 * is in the wallpaper picker viewing a preview of it as well. 2376 */ onCreateEngine()2377 public abstract Engine onCreateEngine(); 2378 2379 @Override dump(FileDescriptor fd, PrintWriter out, String[] args)2380 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 2381 out.print("State of wallpaper "); out.print(this); out.println(":"); 2382 for (int i=0; i<mActiveEngines.size(); i++) { 2383 Engine engine = mActiveEngines.get(i); 2384 out.print(" Engine "); out.print(engine); out.println(":"); 2385 engine.dump(" ", fd, out, args); 2386 } 2387 } 2388 } 2389