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.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING; 20 import static android.app.Flags.liveWallpaperContentHandling; 21 import static android.app.WallpaperManager.COMMAND_FREEZE; 22 import static android.app.WallpaperManager.COMMAND_UNFREEZE; 23 import static android.app.WallpaperManager.SetWallpaperFlags; 24 import static android.graphics.Matrix.MSCALE_X; 25 import static android.graphics.Matrix.MSCALE_Y; 26 import static android.graphics.Matrix.MSKEW_X; 27 import static android.graphics.Matrix.MSKEW_Y; 28 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; 29 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 30 import static android.view.flags.Flags.disableDrawWakeLock; 31 32 import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION; 33 import static com.android.window.flags.Flags.noDuplicateSurfaceDestroyedEvents; 34 import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents; 35 import static com.android.window.flags.Flags.noVisibilityEventOnDisplayStateChange; 36 import static com.android.window.flags.Flags.offloadColorExtraction; 37 38 import android.animation.AnimationHandler; 39 import android.animation.Animator; 40 import android.animation.AnimatorListenerAdapter; 41 import android.animation.ValueAnimator; 42 import android.annotation.FlaggedApi; 43 import android.annotation.FloatRange; 44 import android.annotation.MainThread; 45 import android.annotation.NonNull; 46 import android.annotation.Nullable; 47 import android.annotation.SdkConstant; 48 import android.annotation.SdkConstant.SdkConstantType; 49 import android.annotation.SystemApi; 50 import android.app.Service; 51 import android.app.WallpaperColors; 52 import android.app.WallpaperInfo; 53 import android.app.WallpaperManager; 54 import android.app.compat.CompatChanges; 55 import android.app.wallpaper.WallpaperDescription; 56 import android.compat.annotation.ChangeId; 57 import android.compat.annotation.EnabledAfter; 58 import android.compat.annotation.EnabledSince; 59 import android.compat.annotation.UnsupportedAppUsage; 60 import android.content.Context; 61 import android.content.Intent; 62 import android.content.pm.PackageManager; 63 import android.content.res.Configuration; 64 import android.graphics.BLASTBufferQueue; 65 import android.graphics.Bitmap; 66 import android.graphics.Canvas; 67 import android.graphics.Matrix; 68 import android.graphics.PixelFormat; 69 import android.graphics.Point; 70 import android.graphics.Rect; 71 import android.graphics.RectF; 72 import android.graphics.drawable.Drawable; 73 import android.hardware.HardwareBuffer; 74 import android.hardware.display.DisplayManager; 75 import android.hardware.display.DisplayManager.DisplayListener; 76 import android.os.Binder; 77 import android.os.Build; 78 import android.os.Bundle; 79 import android.os.Handler; 80 import android.os.HandlerThread; 81 import android.os.IBinder; 82 import android.os.Looper; 83 import android.os.Message; 84 import android.os.Process; 85 import android.os.RemoteException; 86 import android.os.SystemClock; 87 import android.os.SystemProperties; 88 import android.os.Trace; 89 import android.util.ArrayMap; 90 import android.util.ArraySet; 91 import android.util.Log; 92 import android.util.MergedConfiguration; 93 import android.util.Slog; 94 import android.view.Display; 95 import android.view.DisplayCutout; 96 import android.view.Gravity; 97 import android.view.IWindowSession; 98 import android.view.InputChannel; 99 import android.view.InputDevice; 100 import android.view.InputEvent; 101 import android.view.InputEventReceiver; 102 import android.view.InsetsSourceControl; 103 import android.view.InsetsState; 104 import android.view.MotionEvent; 105 import android.view.PixelCopy; 106 import android.view.Surface; 107 import android.view.SurfaceControl; 108 import android.view.SurfaceHolder; 109 import android.view.View; 110 import android.view.ViewGroup; 111 import android.view.WindowInsets; 112 import android.view.WindowLayout; 113 import android.view.WindowManager; 114 import android.view.WindowManagerGlobal; 115 import android.view.WindowRelayoutResult; 116 import android.window.ActivityWindowInfo; 117 import android.window.ClientWindowFrames; 118 import android.window.ScreenCapture; 119 120 import com.android.internal.R; 121 import com.android.internal.annotations.GuardedBy; 122 import com.android.internal.annotations.VisibleForTesting; 123 import com.android.internal.os.HandlerCaller; 124 import com.android.internal.view.BaseIWindow; 125 import com.android.internal.view.BaseSurfaceHolder; 126 127 import java.io.FileDescriptor; 128 import java.io.PrintWriter; 129 import java.util.HashSet; 130 import java.util.List; 131 import java.util.Objects; 132 import java.util.Set; 133 import java.util.concurrent.TimeUnit; 134 import java.util.concurrent.atomic.AtomicBoolean; 135 import java.util.concurrent.atomic.AtomicInteger; 136 import java.util.function.Supplier; 137 138 /** 139 * A wallpaper service is responsible for showing a live wallpaper behind 140 * applications that would like to sit on top of it. This service object 141 * itself does very little -- its only purpose is to generate instances of 142 * {@link Engine} as needed. Implementing a wallpaper thus 143 * involves subclassing from this, subclassing an Engine implementation, 144 * and implementing {@link #onCreateEngine()} to return a new instance of 145 * your engine. 146 */ 147 public abstract class WallpaperService extends Service { 148 /** 149 * The {@link Intent} that must be declared as handled by the service. 150 * To be supported, the service must also require the 151 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so 152 * that other applications can not abuse it. 153 */ 154 @SdkConstant(SdkConstantType.SERVICE_ACTION) 155 public static final String SERVICE_INTERFACE = 156 "android.service.wallpaper.WallpaperService"; 157 158 /** 159 * Name under which a WallpaperService component publishes information 160 * about itself. This meta-data must reference an XML resource containing 161 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 162 * tag. 163 */ 164 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 165 166 static final String TAG = "WallpaperService"; 167 static final boolean DEBUG = false; 168 static final float MIN_PAGE_ALLOWED_MARGIN = .05f; 169 private static final int MIN_BITMAP_SCREENSHOT_WIDTH = 64; 170 private static final long DEFAULT_UPDATE_SCREENSHOT_DURATION = 60 * 1000; //Once per minute 171 private static final @NonNull RectF LOCAL_COLOR_BOUNDS = 172 new RectF(0, 0, 1, 1); 173 174 private static final int DO_ATTACH = 10; 175 private static final int DO_DETACH = 20; 176 private static final int DO_SET_DESIRED_SIZE = 30; 177 private static final int DO_SET_DISPLAY_PADDING = 40; 178 private static final int DO_IN_AMBIENT_MODE = 50; 179 180 private static final int MSG_UPDATE_SURFACE = 10000; 181 private static final int MSG_VISIBILITY_CHANGED = 10010; 182 private static final int MSG_WALLPAPER_OFFSETS = 10020; 183 private static final int MSG_WALLPAPER_COMMAND = 10025; 184 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 185 private static final int MSG_WINDOW_RESIZED = 10030; 186 private static final int MSG_WINDOW_MOVED = 10035; 187 private static final int MSG_TOUCH_EVENT = 10040; 188 private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; 189 private static final int MSG_ZOOM = 10100; 190 private static final int MSG_RESIZE_PREVIEW = 10110; 191 private static final int MSG_REPORT_SHOWN = 10150; 192 private static final int MSG_UPDATE_SCREEN_TURNING_ON = 10170; 193 private static final int MSG_UPDATE_DIMMING = 10200; 194 private static final int MSG_WALLPAPER_FLAGS_CHANGED = 10210; 195 196 /** limit calls to {@link Engine#onComputeColors} to at most once per second */ 197 private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; 198 199 /** limit calls to {@link Engine#processLocalColorsInternal} to at most once per 2 seconds */ 200 private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 2000; 201 202 private static final boolean ENABLE_WALLPAPER_DIMMING = 203 SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true); 204 205 private static final long DIMMING_ANIMATION_DURATION_MS = 300L; 206 207 @GuardedBy("itself") 208 private final ArrayMap<IBinder, IWallpaperEngineWrapper> mActiveEngines = new ArrayMap<>(); 209 210 private Handler mBackgroundHandler; 211 private HandlerThread mBackgroundThread; 212 213 // TODO (b/287037772) remove this flag and the forceReport argument in reportVisibility 214 private boolean mIsWearOs; 215 216 /** 217 * This change disables the {@code DRAW_WAKE_LOCK}, an internal wakelock acquired per-frame 218 * duration display DOZE. It was added to allow animation during AOD. This wakelock consumes 219 * battery severely if the animation is too heavy, so, it will be removed. 220 */ 221 @ChangeId 222 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) 223 private static final long DISABLE_DRAW_WAKE_LOCK_WALLPAPER = 361433696L; 224 225 /** 226 * Wear products currently force a slight scaling transition to wallpapers 227 * when the QSS is opened. However, on Wear 7 (SDK 37) and above, 1P watch faces 228 * will be expected to either implement their own scaling, or to override this 229 * method to allow the WallpaperController to continue to scale for them. 230 * 231 * @hide 232 */ 233 @ChangeId 234 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.BAKLAVA) 235 public static final long WEAROS_WALLPAPER_HANDLES_SCALING = 272527315L; 236 237 static final class WallpaperCommand { 238 String action; 239 int x; 240 int y; 241 int z; 242 Bundle extras; 243 boolean sync; 244 } 245 246 /** 247 * The actual implementation of a wallpaper. A wallpaper service may 248 * have multiple instances running (for example as a real wallpaper 249 * and as a preview), each of which is represented by its own Engine 250 * instance. You must implement {@link WallpaperService#onCreateEngine()} 251 * to return your concrete Engine implementation. 252 */ 253 public class Engine { 254 IWallpaperEngineWrapper mIWallpaperEngine; 255 256 // Copies from mIWallpaperEngine. 257 HandlerCaller mCaller; 258 IWallpaperConnection mConnection; 259 IBinder mWindowToken; 260 261 boolean mInitializing = true; 262 boolean mVisible; 263 /** 264 * Whether the screen is turning on. 265 * After the display is powered on, brightness is initially off. It is turned on only after 266 * all windows have been drawn, and sysui notifies that it's ready (See 267 * {@link com.android.internal.policy.IKeyguardDrawnCallback}). 268 * As some wallpapers use visibility as a signal to start animations, this makes sure 269 * {@link Engine#onVisibilityChanged} is invoked only when the display is both on and 270 * visible (with brightness on). 271 */ 272 private boolean mIsScreenTurningOn; 273 boolean mReportedVisible; 274 boolean mReportedSurfaceCreated; 275 boolean mDestroyed; 276 // Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false 277 // after receiving WallpaperManager#COMMAND_UNFREEZE. COMMAND_FREEZE is fully applied once 278 // mScreenshotSurfaceControl isn't null. When this happens, then Engine is notified through 279 // doVisibilityChanged that main wallpaper surface is no longer visible and the wallpaper 280 // host receives onVisibilityChanged(false) callback. 281 private boolean mFrozenRequested = false; 282 283 // Current window state. 284 boolean mCreated; 285 boolean mSurfaceCreated; 286 boolean mIsCreating; 287 boolean mDrawingAllowed; 288 boolean mOffsetsChanged; 289 boolean mFixedSizeAllowed; 290 // Whether the wallpaper should be dimmed by default (when no additional dimming is applied) 291 // based on its color hints 292 boolean mShouldDimByDefault; 293 int mWidth; 294 int mHeight; 295 int mFormat; 296 int mType; 297 int mCurWidth; 298 int mCurHeight; 299 float mZoom = 0f; 300 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 301 int mWindowPrivateFlags = 302 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; 303 int mCurWindowFlags = mWindowFlags; 304 int mCurWindowPrivateFlags = mWindowPrivateFlags; 305 Rect mPreviewSurfacePosition; 306 final ClientWindowFrames mWinFrames = new ClientWindowFrames(); 307 final Rect mDispatchedContentInsets = new Rect(); 308 final Rect mDispatchedStableInsets = new Rect(); 309 DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT; 310 final InsetsState mInsetsState = new InsetsState(); 311 final InsetsSourceControl.Array mTempControls = new InsetsSourceControl.Array(); 312 final MergedConfiguration mMergedConfiguration = new MergedConfiguration(); 313 314 SurfaceControl mSurfaceControl = new SurfaceControl(); 315 WindowRelayoutResult mRelayoutResult = new WindowRelayoutResult( 316 mWinFrames, mMergedConfiguration, mSurfaceControl, mInsetsState, mTempControls); 317 318 private final Point mSurfaceSize = new Point(); 319 private final Point mLastSurfaceSize = new Point(); 320 private final Matrix mTmpMatrix = new Matrix(); 321 private final float[] mTmpValues = new float[9]; 322 323 final WindowManager.LayoutParams mLayout 324 = new WindowManager.LayoutParams(); 325 IWindowSession mSession; 326 327 final Object mLock = new Object(); 328 private final Object mSurfaceReleaseLock = new Object(); 329 boolean mOffsetMessageEnqueued; 330 331 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 332 @GuardedBy("mLock") 333 private float mPendingXOffset; 334 @GuardedBy("mLock") 335 private float mPendingYOffset; 336 @GuardedBy("mLock") 337 private float mPendingXOffsetStep; 338 @GuardedBy("mLock") 339 private float mPendingYOffsetStep; 340 341 /** 342 * local color extraction related fields. When a user calls `addLocalColorAreas` 343 */ 344 @GuardedBy("mLock") 345 private final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4); 346 347 @GuardedBy("mLock") 348 private final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4); 349 private long mLastProcessLocalColorsTimestamp; 350 private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false); 351 private int mPixelCopyCount = 0; 352 // 2D matrix [x][y] to represent a page of a portion of a window 353 @GuardedBy("mLock") 354 private EngineWindowPage[] mWindowPages = new EngineWindowPage[0]; 355 private Bitmap mLastScreenshot; 356 private boolean mResetWindowPages; 357 358 boolean mPendingSync; 359 MotionEvent mPendingMove; 360 boolean mIsInAmbientMode; 361 362 // used to throttle onComputeColors 363 private long mLastColorInvalidation; 364 private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; 365 366 private final Supplier<Long> mClockFunction; 367 private final Handler mHandler; 368 private Display mDisplay; 369 private Context mDisplayContext; 370 private int mDisplayState; 371 372 private float mCustomDimAmount = 0f; 373 private float mWallpaperDimAmount = 0f; 374 private float mPreviousWallpaperDimAmount = mWallpaperDimAmount; 375 private float mDefaultDimAmount = 0.05f; 376 SurfaceControl mBbqSurfaceControl; 377 BLASTBufferQueue mBlastBufferQueue; 378 IBinder mBbqApplyToken = new Binder(); 379 private SurfaceControl mScreenshotSurfaceControl; 380 private Point mScreenshotSize = new Point(); 381 382 private final boolean mDisableDrawWakeLock; 383 384 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 385 { 386 mRequestedFormat = PixelFormat.RGBX_8888; 387 } 388 389 @Override 390 public boolean onAllowLockCanvas() { 391 return mDrawingAllowed; 392 } 393 394 @Override 395 public void onRelayoutContainer() { 396 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 397 mCaller.sendMessage(msg); 398 } 399 400 @Override 401 public void onUpdateSurface() { 402 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 403 mCaller.sendMessage(msg); 404 } 405 406 public boolean isCreating() { 407 return mIsCreating; 408 } 409 410 @Override 411 public void setFixedSize(int width, int height) { 412 if (!mFixedSizeAllowed && !mIWallpaperEngine.mIsPreview) { 413 // Regular apps can't do this. It can only work for 414 // certain designs of window animations, so you can't 415 // rely on it. 416 throw new UnsupportedOperationException( 417 "Wallpapers currently only support sizing from layout"); 418 } 419 super.setFixedSize(width, height); 420 } 421 422 public void setKeepScreenOn(boolean screenOn) { 423 throw new UnsupportedOperationException( 424 "Wallpapers do not support keep screen on"); 425 } 426 427 private void prepareToDraw() { 428 if (mDisableDrawWakeLock) { 429 return; 430 } 431 if (mDisplayState == Display.STATE_DOZE) { 432 try { 433 mSession.pokeDrawLock(mWindow); 434 } catch (RemoteException e) { 435 // System server died, can be ignored. 436 } 437 } 438 } 439 440 @Override 441 public Canvas lockCanvas() { 442 prepareToDraw(); 443 return super.lockCanvas(); 444 } 445 446 @Override 447 public Canvas lockCanvas(Rect dirty) { 448 prepareToDraw(); 449 return super.lockCanvas(dirty); 450 } 451 452 @Override 453 public Canvas lockHardwareCanvas() { 454 prepareToDraw(); 455 return super.lockHardwareCanvas(); 456 } 457 }; 458 459 final class WallpaperInputEventReceiver extends InputEventReceiver { WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper)460 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) { 461 super(inputChannel, looper); 462 } 463 464 @Override onInputEvent(InputEvent event)465 public void onInputEvent(InputEvent event) { 466 boolean handled = false; 467 try { 468 if (event instanceof MotionEvent 469 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 470 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event); 471 dispatchPointer(dup); 472 handled = true; 473 } 474 } finally { 475 finishInputEvent(event, handled); 476 } 477 } 478 } 479 WallpaperInputEventReceiver mInputEventReceiver; 480 481 final BaseIWindow mWindow = new BaseIWindow() { 482 @Override 483 public void resized(ClientWindowFrames frames, boolean reportDraw, 484 MergedConfiguration mergedConfiguration, InsetsState insetsState, 485 boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, 486 int syncSeqId, boolean dragResizing, 487 @Nullable ActivityWindowInfo activityWindowInfo) { 488 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED, 489 reportDraw ? 1 : 0, 490 mergedConfiguration); 491 mIWallpaperEngine.mPendingResizeCount.incrementAndGet(); 492 mCaller.sendMessage(msg); 493 } 494 495 @Override 496 public void moved(int newX, int newY) { 497 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY); 498 mCaller.sendMessage(msg); 499 } 500 501 @Override 502 public void dispatchAppVisibility(boolean visible) { 503 // We don't do this in preview mode; we'll let the preview 504 // activity tell us when to run. 505 if (!mIWallpaperEngine.mIsPreview) { 506 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 507 visible ? 1 : 0); 508 mCaller.sendMessage(msg); 509 } 510 } 511 512 @Override 513 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 514 float zoom, boolean sync) { 515 synchronized (mLock) { 516 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 517 mPendingXOffset = x; 518 mPendingYOffset = y; 519 mPendingXOffsetStep = xStep; 520 mPendingYOffsetStep = yStep; 521 if (sync) { 522 mPendingSync = true; 523 } 524 if (!mOffsetMessageEnqueued) { 525 mOffsetMessageEnqueued = true; 526 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 527 mCaller.sendMessage(msg); 528 } 529 Message msg = mCaller.obtainMessageI(MSG_ZOOM, Float.floatToIntBits(zoom)); 530 mCaller.sendMessage(msg); 531 } 532 } 533 534 @Override 535 public void dispatchWallpaperCommand(String action, int x, int y, 536 int z, Bundle extras, boolean sync) { 537 synchronized (mLock) { 538 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 539 WallpaperCommand cmd = new WallpaperCommand(); 540 cmd.action = action; 541 cmd.x = x; 542 cmd.y = y; 543 cmd.z = z; 544 cmd.extras = extras; 545 cmd.sync = sync; 546 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 547 msg.obj = cmd; 548 mCaller.sendMessage(msg); 549 } 550 } 551 }; 552 553 /** 554 * Default constructor 555 */ Engine()556 public Engine() { 557 this(SystemClock::elapsedRealtime, Handler.getMain()); 558 } 559 560 /** 561 * Constructor used for test purposes. 562 * 563 * @param clockFunction Supplies current times in millis. 564 * @param handler Used for posting/deferring asynchronous calls. 565 * @hide 566 */ 567 @VisibleForTesting Engine(Supplier<Long> clockFunction, Handler handler)568 public Engine(Supplier<Long> clockFunction, Handler handler) { 569 mClockFunction = clockFunction; 570 mHandler = handler; 571 mDisableDrawWakeLock = CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK_WALLPAPER) 572 && disableDrawWakeLock(); 573 } 574 575 /** 576 * Provides access to the surface in which this wallpaper is drawn. 577 */ getSurfaceHolder()578 public SurfaceHolder getSurfaceHolder() { 579 return mSurfaceHolder; 580 } 581 582 /** 583 * Returns the current wallpaper flags indicating which screen this Engine is rendering to. 584 */ getWallpaperFlags()585 @SetWallpaperFlags public int getWallpaperFlags() { 586 return mIWallpaperEngine.mWhich; 587 } 588 589 /** 590 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 591 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 592 * that the system would like this wallpaper to run in. 593 */ getDesiredMinimumWidth()594 public int getDesiredMinimumWidth() { 595 return mIWallpaperEngine.mReqWidth; 596 } 597 598 /** 599 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 600 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 601 * that the system would like this wallpaper to run in. 602 */ getDesiredMinimumHeight()603 public int getDesiredMinimumHeight() { 604 return mIWallpaperEngine.mReqHeight; 605 } 606 607 /** 608 * Return whether the wallpaper is currently visible to the user, 609 * this is the last value supplied to 610 * {@link #onVisibilityChanged(boolean)}. 611 */ isVisible()612 public boolean isVisible() { 613 return mReportedVisible; 614 } 615 616 /** 617 * Return whether the wallpaper is capable of extracting local colors in a rectangle area, 618 * Must implement without calling super: 619 * {@link #addLocalColorsAreas(List)} 620 * {@link #removeLocalColorsAreas(List)} 621 * When local colors change, call {@link #notifyLocalColorsChanged(List, List)} 622 * See {@link com.android.systemui.wallpapers.ImageWallpaper} for an example 623 * @hide 624 */ supportsLocalColorExtraction()625 public boolean supportsLocalColorExtraction() { 626 return false; 627 } 628 629 /** 630 * Returns true if this engine is running in preview mode -- that is, 631 * it is being shown to the user before they select it as the actual 632 * wallpaper. 633 */ isPreview()634 public boolean isPreview() { 635 return mIWallpaperEngine.mIsPreview; 636 } 637 638 /** 639 * Returns true if this engine is running in ambient mode -- that is, 640 * it is being shown in low power mode, on always on display. 641 * @hide 642 */ 643 @SystemApi isInAmbientMode()644 public boolean isInAmbientMode() { 645 return mIsInAmbientMode; 646 } 647 648 /** 649 * This will be called when the wallpaper is first started. If true is returned, the system 650 * will zoom in the wallpaper by default and zoom it out as the user interacts, 651 * to create depth. Otherwise, zoom will have to be handled manually 652 * in {@link #onZoomChanged(float)}. 653 * 654 * @hide 655 */ shouldZoomOutWallpaper()656 public boolean shouldZoomOutWallpaper() { 657 return mIsWearOs && !CompatChanges.isChangeEnabled(WEAROS_WALLPAPER_HANDLES_SCALING); 658 } 659 660 /** 661 * This will be called in the end of {@link #updateSurface(boolean, boolean, boolean)}. 662 * If true is returned, the engine will not report shown until rendering finished is 663 * reported. Otherwise, the engine will report shown immediately right after redraw phase 664 * in {@link #updateSurface(boolean, boolean, boolean)}. 665 * 666 * @hide 667 */ shouldWaitForEngineShown()668 public boolean shouldWaitForEngineShown() { 669 return false; 670 } 671 672 /** 673 * Reports the rendering is finished, stops waiting, then invokes 674 * {@link IWallpaperEngineWrapper#reportShown()}. 675 * 676 * @hide 677 */ reportEngineShown(boolean waitForEngineShown)678 public void reportEngineShown(boolean waitForEngineShown) { 679 if (mIWallpaperEngine.mShownReported) return; 680 Trace.beginSection("WPMS.reportEngineShown-" + waitForEngineShown); 681 Log.d(TAG, "reportEngineShown: shouldWait=" + waitForEngineShown); 682 if (!waitForEngineShown) { 683 Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN); 684 mCaller.removeMessages(MSG_REPORT_SHOWN); 685 mCaller.sendMessage(message); 686 } else { 687 // if we are already waiting, no need to reset the timeout. 688 if (!mCaller.hasMessages(MSG_REPORT_SHOWN)) { 689 Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN); 690 mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(5)); 691 } 692 } 693 Trace.endSection(); 694 } 695 696 /** 697 * Control whether this wallpaper will receive raw touch events 698 * from the window manager as the user interacts with the window 699 * that is currently displaying the wallpaper. By default they 700 * are turned off. If enabled, the events will be received in 701 * {@link #onTouchEvent(MotionEvent)}. 702 */ setTouchEventsEnabled(boolean enabled)703 public void setTouchEventsEnabled(boolean enabled) { 704 mWindowFlags = enabled 705 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 706 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 707 if (mCreated) { 708 updateSurface(false, false, false); 709 } 710 } 711 712 /** 713 * Control whether this wallpaper will receive notifications when the wallpaper 714 * has been scrolled. By default, wallpapers will receive notifications, although 715 * the default static image wallpapers do not. It is a performance optimization to 716 * set this to false. 717 * 718 * @param enabled whether the wallpaper wants to receive offset notifications 719 */ setOffsetNotificationsEnabled(boolean enabled)720 public void setOffsetNotificationsEnabled(boolean enabled) { 721 mWindowPrivateFlags = enabled 722 ? (mWindowPrivateFlags | 723 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) 724 : (mWindowPrivateFlags & 725 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS); 726 if (mCreated) { 727 updateSurface(false, false, false); 728 } 729 } 730 731 /** @hide */ setShowForAllUsers(boolean show)732 public void setShowForAllUsers(boolean show) { 733 mWindowPrivateFlags = show 734 ? (mWindowPrivateFlags 735 | WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) 736 : (mWindowPrivateFlags 737 & ~WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); 738 if (mCreated) { 739 updateSurface(false, false, false); 740 } 741 } 742 743 /** {@hide} */ 744 @UnsupportedAppUsage setFixedSizeAllowed(boolean allowed)745 public void setFixedSizeAllowed(boolean allowed) { 746 mFixedSizeAllowed = allowed; 747 } 748 749 /** 750 * Returns the current scale of the surface 751 * @hide 752 */ 753 @VisibleForTesting getZoom()754 public float getZoom() { 755 return mZoom; 756 } 757 758 /** 759 * Called once to initialize the engine. After returning, the 760 * engine's surface will be created by the framework. 761 */ 762 @MainThread onCreate(SurfaceHolder surfaceHolder)763 public void onCreate(SurfaceHolder surfaceHolder) { 764 } 765 766 /** 767 * Called right before the engine is going away. After this the 768 * surface will be destroyed and this Engine object is no longer 769 * valid. 770 */ 771 @MainThread onDestroy()772 public void onDestroy() { 773 } 774 775 /** 776 * Called to inform you of the wallpaper becoming visible or 777 * hidden. <em>It is very important that a wallpaper only use 778 * CPU while it is visible.</em>. 779 */ 780 @MainThread onVisibilityChanged(boolean visible)781 public void onVisibilityChanged(boolean visible) { 782 } 783 784 /** 785 * Called with the current insets that are in effect for the wallpaper. 786 * This gives you the part of the overall wallpaper surface that will 787 * generally be visible to the user (ignoring position offsets applied to it). 788 * 789 * @param insets Insets to apply. 790 */ 791 @MainThread onApplyWindowInsets(WindowInsets insets)792 public void onApplyWindowInsets(WindowInsets insets) { 793 } 794 795 /** 796 * Called as the user performs touch-screen interaction with the 797 * window that is currently showing this wallpaper. Note that the 798 * events you receive here are driven by the actual application the 799 * user is interacting with, so if it is slow you will get fewer 800 * move events. 801 */ 802 @MainThread onTouchEvent(MotionEvent event)803 public void onTouchEvent(MotionEvent event) { 804 } 805 806 /** 807 * Called to inform you of the wallpaper's offsets changing 808 * within its contain, corresponding to the container's 809 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 810 * WallpaperManager.setWallpaperOffsets()}. 811 */ 812 @MainThread onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset)813 public void onOffsetsChanged(float xOffset, float yOffset, 814 float xOffsetStep, float yOffsetStep, 815 int xPixelOffset, int yPixelOffset) { 816 } 817 818 /** 819 * Process a command that was sent to the wallpaper with 820 * {@link WallpaperManager#sendWallpaperCommand}. 821 * The default implementation does nothing, and always returns null 822 * as the result. 823 * 824 * @param action The name of the command to perform. This tells you 825 * what to do and how to interpret the rest of the arguments. 826 * @param x Generic integer parameter. 827 * @param y Generic integer parameter. 828 * @param z Generic integer parameter. 829 * @param extras Any additional parameters. 830 * @param resultRequested If true, the caller is requesting that 831 * a result, appropriate for the command, be returned back. 832 * @return If returning a result, create a Bundle and place the 833 * result data in to it. Otherwise return null. 834 */ 835 @MainThread onCommand(String action, int x, int y, int z, Bundle extras, boolean resultRequested)836 public Bundle onCommand(String action, int x, int y, int z, 837 Bundle extras, boolean resultRequested) { 838 return null; 839 } 840 841 /** 842 * Called when the device enters or exits ambient mode. 843 * 844 * @param inAmbientMode {@code true} if in ambient mode. 845 * @param animationDuration How long the transition animation to change the ambient state 846 * should run, in milliseconds. If 0 is passed as the argument 847 * here, the state should be switched immediately. 848 * 849 * @see #isInAmbientMode() 850 * @see WallpaperInfo#supportsAmbientMode() 851 * @hide 852 */ 853 @SystemApi 854 @MainThread onAmbientModeChanged(boolean inAmbientMode, long animationDuration)855 public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { 856 } 857 858 /** 859 * Called when the dim amount of the wallpaper changed. This can be used to recompute the 860 * wallpaper colors based on the new dim, and call {@link #notifyColorsChanged()}. 861 * @hide 862 */ 863 @FlaggedApi(FLAG_OFFLOAD_COLOR_EXTRACTION) onDimAmountChanged(float dimAmount)864 public void onDimAmountChanged(float dimAmount) { 865 } 866 867 /** 868 * Called when an application has changed the desired virtual size of 869 * the wallpaper. 870 */ 871 @MainThread onDesiredSizeChanged(int desiredWidth, int desiredHeight)872 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 873 } 874 875 /** 876 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 877 * SurfaceHolder.Callback.surfaceChanged()}. 878 */ 879 @MainThread onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)880 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 881 } 882 883 /** 884 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded 885 * SurfaceHolder.Callback.surfaceRedrawNeeded()}. 886 */ 887 @MainThread onSurfaceRedrawNeeded(SurfaceHolder holder)888 public void onSurfaceRedrawNeeded(SurfaceHolder holder) { 889 } 890 891 /** 892 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 893 * SurfaceHolder.Callback.surfaceCreated()}. 894 */ 895 @MainThread onSurfaceCreated(SurfaceHolder holder)896 public void onSurfaceCreated(SurfaceHolder holder) { 897 } 898 899 /** 900 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 901 * SurfaceHolder.Callback.surfaceDestroyed()}. 902 */ 903 @MainThread onSurfaceDestroyed(SurfaceHolder holder)904 public void onSurfaceDestroyed(SurfaceHolder holder) { 905 } 906 907 /** 908 * Called when the current wallpaper flags change. 909 * 910 * @param which The new flag value 911 * @see #getWallpaperFlags() 912 */ 913 @MainThread onWallpaperFlagsChanged(@etWallpaperFlags int which)914 public void onWallpaperFlagsChanged(@SetWallpaperFlags int which) { 915 } 916 917 /** 918 * Called when the zoom level of the wallpaper changed. 919 * This method will be called with the initial zoom level when the surface is created. 920 * 921 * @param zoom the zoom level, between 0 indicating fully zoomed in and 1 indicating fully 922 * zoomed out. 923 */ 924 @MainThread onZoomChanged(@loatRangefrom = 0f, to = 1f) float zoom)925 public void onZoomChanged(@FloatRange(from = 0f, to = 1f) float zoom) { 926 } 927 928 /** 929 * Called when the wallpaper preview rendered by this engine is about to be persisted as 930 * a selected wallpaper. The returned WallpaperDescription (if any) will be persisted by 931 * the system and passed into subsequent calls to 932 * {@link WallpaperService#onCreateEngine(WallpaperDescription)}. This allows the Engine 933 * to perform any necessary bookkeeping before a wallpaper being previewed is set on 934 * the device, and update the description if necessary. 935 * 936 * @param which Specifies wallpaper destination: home, lock, or both 937 * @return the description of the applied wallpaper, or {@code null} if description is 938 * unchanged 939 */ 940 @Nullable 941 @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) onApplyWallpaper(@etWallpaperFlags int which)942 public WallpaperDescription onApplyWallpaper(@SetWallpaperFlags int which) { 943 return null; 944 } 945 946 /** 947 * Notifies the engine that wallpaper colors changed significantly. 948 * This will trigger a {@link #onComputeColors()} call. 949 */ notifyColorsChanged()950 public void notifyColorsChanged() { 951 if (mDestroyed) { 952 Log.i(TAG, "Ignoring notifyColorsChanged(), Engine has already been destroyed."); 953 return; 954 } 955 956 final long now = mClockFunction.get(); 957 if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) { 958 Log.w(TAG, "This call has been deferred. You should only call " 959 + "notifyColorsChanged() once every " 960 + (NOTIFY_COLORS_RATE_LIMIT_MS / 1000f) + " seconds."); 961 if (!mHandler.hasCallbacks(mNotifyColorsChanged)) { 962 mHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS); 963 } 964 return; 965 } 966 mLastColorInvalidation = now; 967 mHandler.removeCallbacks(mNotifyColorsChanged); 968 969 try { 970 final WallpaperColors newColors = onComputeColors(); 971 if (mConnection != null) { 972 mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId()); 973 } else { 974 Log.w(TAG, "Can't notify system because wallpaper connection " 975 + "was not established."); 976 } 977 mResetWindowPages = true; 978 processLocalColors(); 979 } catch (RemoteException e) { 980 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); 981 } 982 } 983 984 /** 985 * Called by the system when it needs to know what colors the wallpaper is using. 986 * You might return null if no color information is available at the moment. 987 * In that case you might want to call {@link #notifyColorsChanged()} when 988 * color information becomes available. 989 * <p> 990 * The simplest way of creating a {@link android.app.WallpaperColors} object is by using 991 * {@link android.app.WallpaperColors#fromBitmap(Bitmap)} or 992 * {@link android.app.WallpaperColors#fromDrawable(Drawable)}, but you can also specify 993 * your main colors by constructing a {@link android.app.WallpaperColors} object manually. 994 * 995 * @return Wallpaper colors. 996 */ 997 @MainThread onComputeColors()998 public @Nullable WallpaperColors onComputeColors() { 999 return null; 1000 } 1001 1002 /** 1003 * Send the changed local color areas for the connection 1004 * @param regions 1005 * @param colors 1006 * @hide 1007 */ notifyLocalColorsChanged(@onNull List<RectF> regions, @NonNull List<WallpaperColors> colors)1008 public void notifyLocalColorsChanged(@NonNull List<RectF> regions, 1009 @NonNull List<WallpaperColors> colors) 1010 throws RuntimeException { 1011 for (int i = 0; i < regions.size() && i < colors.size(); i++) { 1012 WallpaperColors color = colors.get(i); 1013 RectF area = regions.get(i); 1014 if (color == null || area == null) { 1015 if (DEBUG) { 1016 Log.e(TAG, "notifyLocalColorsChanged null values. color: " 1017 + color + " area " + area); 1018 } 1019 continue; 1020 } 1021 try { 1022 mConnection.onLocalWallpaperColorsChanged( 1023 area, 1024 color, 1025 mDisplayContext.getDisplayId() 1026 ); 1027 } catch (RemoteException e) { 1028 throw new RuntimeException(e); 1029 } 1030 } 1031 WallpaperColors primaryColors = mIWallpaperEngine.mWallpaperManager 1032 .getWallpaperColors(WallpaperManager.FLAG_SYSTEM); 1033 setPrimaryWallpaperColors(primaryColors); 1034 } 1035 setPrimaryWallpaperColors(WallpaperColors colors)1036 private void setPrimaryWallpaperColors(WallpaperColors colors) { 1037 if (colors == null) { 1038 return; 1039 } 1040 int colorHints = colors.getColorHints(); 1041 mShouldDimByDefault = ((colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0 1042 && (colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) == 0); 1043 1044 // Recompute dim in case it changed compared to the previous WallpaperService 1045 updateWallpaperDimming(mCustomDimAmount); 1046 } 1047 1048 /** 1049 * Update the dim amount of the wallpaper by updating the surface. 1050 * 1051 * @param dimAmount Float amount between [0.0, 1.0] to dim the wallpaper. 1052 */ updateWallpaperDimming(float dimAmount)1053 private void updateWallpaperDimming(float dimAmount) { 1054 mCustomDimAmount = Math.min(1f, dimAmount); 1055 1056 // If default dim is enabled, the actual dim amount is at least the default dim amount 1057 mWallpaperDimAmount = (!mShouldDimByDefault) ? mCustomDimAmount 1058 : Math.max(mDefaultDimAmount, mCustomDimAmount); 1059 1060 if (!ENABLE_WALLPAPER_DIMMING 1061 || mBbqSurfaceControl == null || !mBbqSurfaceControl.isValid() 1062 || mWallpaperDimAmount == mPreviousWallpaperDimAmount) { 1063 return; 1064 } 1065 1066 SurfaceControl.Transaction surfaceControlTransaction = new SurfaceControl.Transaction(); 1067 // TODO: apply the dimming to preview as well once surface transparency works in 1068 // preview mode. 1069 if (!isPreview()) { 1070 Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount); 1071 1072 // Animate dimming to gradually change the wallpaper alpha from the previous 1073 // dim amount to the new amount only if the dim amount changed. 1074 ValueAnimator animator = ValueAnimator.ofFloat( 1075 mPreviousWallpaperDimAmount, mWallpaperDimAmount); 1076 animator.setDuration(DIMMING_ANIMATION_DURATION_MS); 1077 animator.addUpdateListener((ValueAnimator va) -> { 1078 final float dimValue = (float) va.getAnimatedValue(); 1079 synchronized (mSurfaceReleaseLock) { 1080 if (mBbqSurfaceControl != null && mBbqSurfaceControl.isValid()) { 1081 surfaceControlTransaction 1082 .setAlpha(mBbqSurfaceControl, 1 - dimValue).apply(); 1083 } 1084 } 1085 }); 1086 animator.addListener(new AnimatorListenerAdapter() { 1087 @Override 1088 public void onAnimationEnd(Animator animation) { 1089 updateSurface(false, false, true); 1090 } 1091 }); 1092 animator.start(); 1093 } else { 1094 Log.v(TAG, "Setting wallpaper dimming: " + 0); 1095 surfaceControlTransaction.setAlpha(mBbqSurfaceControl, 1.0f).apply(); 1096 updateSurface(false, false, true); 1097 } 1098 1099 mPreviousWallpaperDimAmount = mWallpaperDimAmount; 1100 1101 // after the dim changes, allow colors to be immediately recomputed 1102 mLastColorInvalidation = 0; 1103 if (offloadColorExtraction()) onDimAmountChanged(mWallpaperDimAmount); 1104 } 1105 1106 /** 1107 * Sets internal engine state. Only for testing. 1108 * @param created {@code true} or {@code false}. 1109 * @hide 1110 */ 1111 @VisibleForTesting setCreated(boolean created)1112 public void setCreated(boolean created) { 1113 mCreated = created; 1114 } 1115 dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args)1116 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 1117 out.print(prefix); out.print("mInitializing="); out.print(mInitializing); 1118 out.print(" mDestroyed="); out.println(mDestroyed); 1119 out.print(prefix); out.print("mVisible="); out.print(mVisible); 1120 out.print(" mReportedVisible="); out.println(mReportedVisible); 1121 out.print(" mIsScreenTurningOn="); out.println(mIsScreenTurningOn); 1122 out.print(prefix); out.print("mDisplay="); out.println(mDisplay); 1123 out.print(prefix); out.print("mCreated="); out.print(mCreated); 1124 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); 1125 if (noDuplicateSurfaceDestroyedEvents()) { 1126 out.print(" mReportedSurfaceCreated="); out.print(mReportedSurfaceCreated); 1127 } 1128 out.print(" mIsCreating="); out.print(mIsCreating); 1129 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); 1130 out.print(prefix); out.print("mWidth="); out.print(mWidth); 1131 out.print(" mCurWidth="); out.print(mCurWidth); 1132 out.print(" mHeight="); out.print(mHeight); 1133 out.print(" mCurHeight="); out.println(mCurHeight); 1134 out.print(prefix); out.print("mType="); out.print(mType); 1135 out.print(" mWindowFlags="); out.print(mWindowFlags); 1136 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); 1137 out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags); 1138 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags); 1139 out.print(prefix); out.println("mWinFrames="); out.println(mWinFrames); 1140 out.print(prefix); out.print("mConfiguration="); 1141 out.println(mMergedConfiguration.getMergedConfiguration()); 1142 out.print(prefix); out.print("mLayout="); out.println(mLayout); 1143 out.print(prefix); out.print("mZoom="); out.println(mZoom); 1144 out.print(prefix); out.print("mPreviewSurfacePosition="); 1145 out.println(mPreviewSurfacePosition); 1146 final int pendingCount = mIWallpaperEngine.mPendingResizeCount.get(); 1147 if (pendingCount != 0) { 1148 out.print(prefix); out.print("mPendingResizeCount="); out.println(pendingCount); 1149 } 1150 synchronized (mLock) { 1151 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); 1152 out.print(" mPendingXOffset="); out.println(mPendingXOffset); 1153 out.print(prefix); out.print("mPendingXOffsetStep="); 1154 out.print(mPendingXOffsetStep); 1155 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); 1156 out.print(prefix); out.print("mOffsetMessageEnqueued="); 1157 out.print(mOffsetMessageEnqueued); 1158 out.print(" mPendingSync="); out.println(mPendingSync); 1159 if (mPendingMove != null) { 1160 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); 1161 } 1162 } 1163 } 1164 1165 /** 1166 * Set the wallpaper zoom to the given value. This value will be ignored when in ambient 1167 * mode (and zoom will be reset to 0). 1168 * @hide 1169 * @param zoom between 0 and 1 (inclusive) indicating fully zoomed in to fully zoomed out 1170 * respectively. 1171 */ 1172 @VisibleForTesting setZoom(float zoom)1173 public void setZoom(float zoom) { 1174 if (DEBUG) { 1175 Log.v(TAG, "set zoom received: " + zoom); 1176 } 1177 boolean updated = false; 1178 synchronized (mLock) { 1179 if (DEBUG) { 1180 Log.v(TAG, "mZoom: " + mZoom + " updated: " + zoom); 1181 } 1182 if (mIsInAmbientMode) { 1183 mZoom = 0; 1184 } 1185 if (Float.compare(zoom, mZoom) != 0) { 1186 mZoom = zoom; 1187 updated = true; 1188 } 1189 } 1190 if (DEBUG) Log.v(TAG, "setZoom updated? " + updated); 1191 if (updated && !mDestroyed) { 1192 onZoomChanged(mZoom); 1193 } 1194 } 1195 dispatchPointer(MotionEvent event)1196 private void dispatchPointer(MotionEvent event) { 1197 if (event.isTouchEvent()) { 1198 synchronized (mLock) { 1199 if (event.getAction() == MotionEvent.ACTION_MOVE) { 1200 mPendingMove = event; 1201 } else { 1202 mPendingMove = null; 1203 } 1204 } 1205 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 1206 mCaller.sendMessage(msg); 1207 } else { 1208 event.recycle(); 1209 } 1210 } 1211 updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded)1212 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { 1213 if (mDestroyed) { 1214 Log.w(TAG, "Ignoring updateSurface due to destroyed"); 1215 return; 1216 } 1217 1218 boolean fixedSize = false; 1219 int myWidth = mSurfaceHolder.getRequestedWidth(); 1220 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 1221 else fixedSize = true; 1222 int myHeight = mSurfaceHolder.getRequestedHeight(); 1223 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 1224 else fixedSize = true; 1225 1226 final boolean creating = !mCreated; 1227 final boolean surfaceCreating = !mSurfaceCreated; 1228 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 1229 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 1230 boolean insetsChanged = !mCreated; 1231 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 1232 final boolean flagsChanged = mCurWindowFlags != mWindowFlags || 1233 mCurWindowPrivateFlags != mWindowPrivateFlags; 1234 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 1235 || typeChanged || flagsChanged || redrawNeeded 1236 || !mIWallpaperEngine.mShownReported) { 1237 1238 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 1239 + " format=" + formatChanged + " size=" + sizeChanged); 1240 1241 try { 1242 mWidth = myWidth; 1243 mHeight = myHeight; 1244 mFormat = mSurfaceHolder.getRequestedFormat(); 1245 mType = mSurfaceHolder.getRequestedType(); 1246 1247 mLayout.x = 0; 1248 mLayout.y = 0; 1249 1250 mLayout.format = mFormat; 1251 1252 mCurWindowFlags = mWindowFlags; 1253 mLayout.flags = mWindowFlags 1254 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 1255 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 1256 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1257 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 1258 1259 final Configuration config = mMergedConfiguration.getMergedConfiguration(); 1260 final Rect maxBounds = new Rect(config.windowConfiguration.getMaxBounds()); 1261 if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT 1262 && myHeight == ViewGroup.LayoutParams.MATCH_PARENT) { 1263 mLayout.width = myWidth; 1264 mLayout.height = myHeight; 1265 mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SCALED; 1266 } else { 1267 final float layoutScale = Math.max( 1268 maxBounds.width() / (float) myWidth, 1269 maxBounds.height() / (float) myHeight); 1270 mLayout.width = (int) (myWidth * layoutScale + .5f); 1271 mLayout.height = (int) (myHeight * layoutScale + .5f); 1272 mLayout.flags |= WindowManager.LayoutParams.FLAG_SCALED; 1273 } 1274 1275 mCurWindowPrivateFlags = mWindowPrivateFlags; 1276 mLayout.privateFlags = mWindowPrivateFlags; 1277 1278 mLayout.memoryType = mType; 1279 mLayout.token = mWindowToken; 1280 1281 if (!mCreated) { 1282 // Add window 1283 mLayout.type = mIWallpaperEngine.mWindowType; 1284 mLayout.gravity = Gravity.START|Gravity.TOP; 1285 mLayout.setFitInsetsTypes(0 /* types */); 1286 mLayout.setTitle(WallpaperService.this.getClass().getName()); 1287 mLayout.windowAnimations = 1288 com.android.internal.R.style.Animation_Wallpaper; 1289 InputChannel inputChannel = new InputChannel(); 1290 1291 if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE, 1292 mDisplay.getDisplayId(), WindowInsets.Type.defaultVisible(), 1293 inputChannel, mInsetsState, mTempControls, new Rect(), 1294 new float[1]) < 0) { 1295 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 1296 return; 1297 } 1298 mSession.setShouldZoomOutWallpaper(mWindow, shouldZoomOutWallpaper()); 1299 mCreated = true; 1300 1301 mInputEventReceiver = new WallpaperInputEventReceiver( 1302 inputChannel, Looper.myLooper()); 1303 } 1304 1305 mSurfaceHolder.mSurfaceLock.lock(); 1306 mDrawingAllowed = true; 1307 1308 if (!fixedSize) { 1309 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 1310 } else { 1311 mLayout.surfaceInsets.set(0, 0, 0, 0); 1312 } 1313 final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight, 1314 View.VISIBLE, 0, 0, 0, mRelayoutResult); 1315 final Rect outMaxBounds = mMergedConfiguration.getMergedConfiguration() 1316 .windowConfiguration.getMaxBounds(); 1317 if (!outMaxBounds.equals(maxBounds)) { 1318 Log.i(TAG, "Retry updateSurface because bounds changed from relayout: " 1319 + maxBounds + " -> " + outMaxBounds); 1320 mSurfaceHolder.mSurfaceLock.unlock(); 1321 mDrawingAllowed = false; 1322 mCaller.sendMessage(mCaller.obtainMessageI(MSG_WINDOW_RESIZED, 1323 redrawNeeded ? 1 : 0)); 1324 return; 1325 } 1326 WindowLayout.computeSurfaceSize(mLayout, maxBounds, mWidth, mHeight, 1327 mWinFrames.frame, false /* dragResizing */, mSurfaceSize); 1328 1329 if (mSurfaceControl.isValid()) { 1330 final int transformHint = SurfaceControl.rotationToBufferTransform( 1331 (mDisplay.getInstallOrientation() + mDisplay.getRotation()) % 4); 1332 mSurfaceControl.setTransformHint(transformHint); 1333 if (mBbqSurfaceControl == null) { 1334 mBbqSurfaceControl = new SurfaceControl.Builder() 1335 .setName("Wallpaper BBQ wrapper") 1336 .setHidden(false) 1337 .setBLASTLayer() 1338 .setParent(mSurfaceControl) 1339 .setCallsite("Wallpaper#relayout") 1340 .build(); 1341 SurfaceControl.Transaction transaction = 1342 new SurfaceControl.Transaction(); 1343 final int frameRateCompat = getResources().getInteger( 1344 R.integer.config_wallpaperFrameRateCompatibility); 1345 if (DEBUG) { 1346 Log.d(TAG, "Set frame rate compatibility value for Wallpaper: " 1347 + frameRateCompat); 1348 } 1349 transaction.setDefaultFrameRateCompatibility(mBbqSurfaceControl, 1350 frameRateCompat).apply(); 1351 } 1352 // Propagate transform hint from WM, so we can use the right hint for the 1353 // first frame. 1354 mBbqSurfaceControl.setTransformHint(transformHint); 1355 Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x, 1356 mSurfaceSize.y, mFormat); 1357 // If blastSurface == null that means it hasn't changed since the last 1358 // time we called. In this situation, avoid calling transferFrom as we 1359 // would then inc the generation ID and cause EGL resources to be recreated. 1360 if (blastSurface != null) { 1361 mSurfaceHolder.mSurface.transferFrom(blastSurface); 1362 } 1363 } 1364 if (!mLastSurfaceSize.equals(mSurfaceSize)) { 1365 mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y); 1366 } 1367 1368 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 1369 + ", frame=" + mWinFrames); 1370 1371 int w = mWinFrames.frame.width(); 1372 int h = mWinFrames.frame.height(); 1373 1374 final DisplayCutout rawCutout = mInsetsState.getDisplayCutout(); 1375 final Rect visibleFrame = new Rect(mWinFrames.frame); 1376 visibleFrame.intersect(mInsetsState.getDisplayFrame()); 1377 WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame, 1378 null /* ignoringVisibilityState */, config.isScreenRound(), 1379 mLayout.softInputMode, mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, 1380 mLayout.type, config.windowConfiguration.getActivityType(), 1381 null /* idSideMap */); 1382 1383 if (!fixedSize) { 1384 final Rect padding = mIWallpaperEngine.mDisplayPadding; 1385 w += padding.left + padding.right; 1386 h += padding.top + padding.bottom; 1387 windowInsets = windowInsets.insetUnchecked( 1388 -padding.left, -padding.top, -padding.right, -padding.bottom); 1389 } else { 1390 w = myWidth; 1391 h = myHeight; 1392 } 1393 1394 if (mCurWidth != w) { 1395 sizeChanged = true; 1396 mCurWidth = w; 1397 } 1398 if (mCurHeight != h) { 1399 sizeChanged = true; 1400 mCurHeight = h; 1401 } 1402 1403 if (DEBUG) { 1404 Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight); 1405 } 1406 1407 final Rect contentInsets = windowInsets.getSystemWindowInsets().toRect(); 1408 final Rect stableInsets = windowInsets.getStableInsets().toRect(); 1409 final DisplayCutout displayCutout = windowInsets.getDisplayCutout() != null 1410 ? windowInsets.getDisplayCutout() : rawCutout; 1411 insetsChanged |= !mDispatchedContentInsets.equals(contentInsets); 1412 insetsChanged |= !mDispatchedStableInsets.equals(stableInsets); 1413 insetsChanged |= !mDispatchedDisplayCutout.equals(displayCutout); 1414 1415 mSurfaceHolder.setSurfaceFrameSize(w, h); 1416 mSurfaceHolder.mSurfaceLock.unlock(); 1417 1418 if (!mSurfaceHolder.mSurface.isValid()) { 1419 reportSurfaceDestroyed(); 1420 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 1421 return; 1422 } 1423 1424 boolean didSurface = false; 1425 1426 try { 1427 mSurfaceHolder.ungetCallbacks(); 1428 1429 if (surfaceCreating) { 1430 mIsCreating = true; 1431 didSurface = true; 1432 mReportedSurfaceCreated = true; 1433 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 1434 + mSurfaceHolder + "): " + this); 1435 Trace.beginSection("WPMS.Engine.onSurfaceCreated"); 1436 onSurfaceCreated(mSurfaceHolder); 1437 Trace.endSection(); 1438 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1439 if (callbacks != null) { 1440 for (SurfaceHolder.Callback c : callbacks) { 1441 c.surfaceCreated(mSurfaceHolder); 1442 } 1443 } 1444 } 1445 1446 redrawNeeded |= creating || (relayoutResult 1447 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 1448 1449 if (forceReport || creating || surfaceCreating 1450 || formatChanged || sizeChanged) { 1451 if (DEBUG) { 1452 RuntimeException e = new RuntimeException(); 1453 e.fillInStackTrace(); 1454 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 1455 + " formatChanged=" + formatChanged 1456 + " sizeChanged=" + sizeChanged, e); 1457 } 1458 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 1459 + mSurfaceHolder + ", " + mFormat 1460 + ", " + mCurWidth + ", " + mCurHeight 1461 + "): " + this); 1462 didSurface = true; 1463 Trace.beginSection("WPMS.Engine.onSurfaceChanged"); 1464 onSurfaceChanged(mSurfaceHolder, mFormat, 1465 mCurWidth, mCurHeight); 1466 Trace.endSection(); 1467 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1468 if (callbacks != null) { 1469 for (SurfaceHolder.Callback c : callbacks) { 1470 c.surfaceChanged(mSurfaceHolder, mFormat, 1471 mCurWidth, mCurHeight); 1472 } 1473 } 1474 } 1475 1476 if (insetsChanged) { 1477 mDispatchedContentInsets.set(contentInsets); 1478 mDispatchedStableInsets.set(stableInsets); 1479 mDispatchedDisplayCutout = displayCutout; 1480 if (DEBUG) { 1481 Log.v(TAG, "dispatching insets=" + windowInsets); 1482 } 1483 Trace.beginSection("WPMS.Engine.onApplyWindowInsets"); 1484 onApplyWindowInsets(windowInsets); 1485 Trace.endSection(); 1486 } 1487 1488 if (redrawNeeded || sizeChanged) { 1489 Trace.beginSection("WPMS.Engine.onSurfaceRedrawNeeded"); 1490 onSurfaceRedrawNeeded(mSurfaceHolder); 1491 Trace.endSection(); 1492 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1493 if (callbacks != null) { 1494 for (SurfaceHolder.Callback c : callbacks) { 1495 if (c instanceof SurfaceHolder.Callback2) { 1496 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 1497 mSurfaceHolder); 1498 } 1499 } 1500 } 1501 } 1502 1503 if (didSurface && !mReportedVisible) { 1504 if (mIsCreating) { 1505 // The surface has been created, but the wallpaper isn't visible. 1506 // Trigger onVisibilityChanged(true) then onVisibilityChanged(false) 1507 // to make sure the wallpaper is stopped even after the events 1508 // onSurfaceCreated() and onSurfaceChanged(). 1509 if (noConsecutiveVisibilityEvents()) { 1510 if (DEBUG) Log.v(TAG, "toggling onVisibilityChanged"); 1511 Trace.beginSection("WPMS.Engine.onVisibilityChanged-true"); 1512 onVisibilityChanged(true); 1513 Trace.endSection(); 1514 Trace.beginSection("WPMS.Engine.onVisibilityChanged-false"); 1515 onVisibilityChanged(false); 1516 Trace.endSection(); 1517 } else { 1518 if (DEBUG) { 1519 Log.v(TAG, "onVisibilityChanged(true) at surface: " + this); 1520 } 1521 Trace.beginSection("WPMS.Engine.onVisibilityChanged-true"); 1522 onVisibilityChanged(true); 1523 Trace.endSection(); 1524 } 1525 } 1526 if (!noConsecutiveVisibilityEvents()) { 1527 if (DEBUG) { 1528 Log.v(TAG, "onVisibilityChanged(false) at surface: " + this); 1529 } 1530 Trace.beginSection("WPMS.Engine.onVisibilityChanged-false"); 1531 onVisibilityChanged(false); 1532 Trace.endSection(); 1533 } 1534 } 1535 } finally { 1536 mIsCreating = false; 1537 mSurfaceCreated = true; 1538 if (redrawNeeded) { 1539 mSession.finishDrawing(mWindow, null /* postDrawTransaction */, 1540 Integer.MAX_VALUE); 1541 processLocalColors(); 1542 } 1543 reposition(); 1544 reportEngineShown(shouldWaitForEngineShown()); 1545 } 1546 } catch (RemoteException ex) { 1547 } 1548 if (DEBUG) Log.v( 1549 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 1550 " w=" + mLayout.width + " h=" + mLayout.height); 1551 } 1552 } 1553 resizePreview(Rect position)1554 private void resizePreview(Rect position) { 1555 if (position != null) { 1556 mSurfaceHolder.setFixedSize(position.width(), position.height()); 1557 } 1558 } 1559 reposition()1560 private void reposition() { 1561 if (mPreviewSurfacePosition == null) { 1562 return; 1563 } 1564 if (DEBUG) { 1565 Log.i(TAG, "reposition: rect: " + mPreviewSurfacePosition); 1566 } 1567 1568 mTmpMatrix.setTranslate(mPreviewSurfacePosition.left, mPreviewSurfacePosition.top); 1569 mTmpMatrix.postScale(((float) mPreviewSurfacePosition.width()) / mCurWidth, 1570 ((float) mPreviewSurfacePosition.height()) / mCurHeight); 1571 mTmpMatrix.getValues(mTmpValues); 1572 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 1573 t.setPosition(mSurfaceControl, mPreviewSurfacePosition.left, 1574 mPreviewSurfacePosition.top); 1575 t.setMatrix(mSurfaceControl, mTmpValues[MSCALE_X], mTmpValues[MSKEW_Y], 1576 mTmpValues[MSKEW_X], mTmpValues[MSCALE_Y]); 1577 t.apply(); 1578 } 1579 attach(IWallpaperEngineWrapper wrapper)1580 void attach(IWallpaperEngineWrapper wrapper) { 1581 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 1582 if (mDestroyed) { 1583 return; 1584 } 1585 1586 mIWallpaperEngine = wrapper; 1587 mCaller = wrapper.mCaller; 1588 mConnection = wrapper.mConnection; 1589 mWindowToken = wrapper.mWindowToken; 1590 mSurfaceHolder.setSizeFromLayout(); 1591 mInitializing = true; 1592 mSession = WindowManagerGlobal.getWindowSession(); 1593 1594 mWindow.setSession(mSession); 1595 1596 mLayout.packageName = getPackageName(); 1597 if (com.android.server.display.feature.flags.Flags 1598 .displayListenerPerformanceImprovements() 1599 && com.android.server.display.feature.flags.Flags 1600 .committedStateSeparateEvent()) { 1601 mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener, 1602 mCaller.getHandler(), DisplayManager.EVENT_TYPE_DISPLAY_CHANGED, 1603 DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_COMMITTED_STATE_CHANGED); 1604 } else { 1605 mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener, 1606 mCaller.getHandler()); 1607 } 1608 mDisplay = mIWallpaperEngine.mDisplay; 1609 // Use window context of TYPE_WALLPAPER so client can access UI resources correctly. 1610 mDisplayContext = createDisplayContext(mDisplay) 1611 .createWindowContext(TYPE_WALLPAPER, null /* options */); 1612 mDefaultDimAmount = mDisplayContext.getResources().getFloat( 1613 com.android.internal.R.dimen.config_wallpaperDimAmount); 1614 mDisplayState = mDisplay.getCommittedState(); 1615 mMergedConfiguration.setOverrideConfiguration( 1616 mDisplayContext.getResources().getConfiguration()); 1617 1618 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 1619 Trace.beginSection("WPMS.Engine.onCreate"); 1620 onCreate(mSurfaceHolder); 1621 Trace.endSection(); 1622 1623 mInitializing = false; 1624 1625 mReportedVisible = false; 1626 Trace.beginSection("WPMS.Engine.updateSurface"); 1627 updateSurface(false, false, false); 1628 Trace.endSection(); 1629 } 1630 1631 /** 1632 * The {@link Context} with resources that match the current display the wallpaper is on. 1633 * For multiple display environment, multiple engines can be created to render on each 1634 * display, but these displays may have different densities. Use this context to get the 1635 * corresponding resources for currently display, avoiding the context of the service. 1636 * <p> 1637 * The display context will never be {@code null} after 1638 * {@link Engine#onCreate(SurfaceHolder)} has been called. 1639 * 1640 * @return A {@link Context} for current display. 1641 */ 1642 @Nullable getDisplayContext()1643 public Context getDisplayContext() { 1644 return mDisplayContext; 1645 } 1646 1647 /** 1648 * Executes life cycle event and updates internal ambient mode state based on 1649 * message sent from handler. 1650 * 1651 * @param inAmbientMode {@code true} if in ambient mode. 1652 * @param animationDuration For how long the transition will last, in ms. 1653 * @hide 1654 */ 1655 @VisibleForTesting doAmbientModeChanged(boolean inAmbientMode, long animationDuration)1656 public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) { 1657 if (!mDestroyed) { 1658 if (DEBUG) { 1659 Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", " 1660 + animationDuration + "): " + this); 1661 } 1662 mIsInAmbientMode = inAmbientMode; 1663 if (mCreated) { 1664 onAmbientModeChanged(inAmbientMode, animationDuration); 1665 } 1666 } 1667 } 1668 doDesiredSizeChanged(int desiredWidth, int desiredHeight)1669 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 1670 if (!mDestroyed) { 1671 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 1672 + desiredWidth + "," + desiredHeight + "): " + this); 1673 mIWallpaperEngine.mReqWidth = desiredWidth; 1674 mIWallpaperEngine.mReqHeight = desiredHeight; 1675 onDesiredSizeChanged(desiredWidth, desiredHeight); 1676 doOffsetsChanged(true); 1677 } 1678 } 1679 doDisplayPaddingChanged(Rect padding)1680 void doDisplayPaddingChanged(Rect padding) { 1681 if (!mDestroyed) { 1682 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 1683 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 1684 mIWallpaperEngine.mDisplayPadding.set(padding); 1685 updateSurface(true, false, false); 1686 } 1687 } 1688 } 1689 onScreenTurningOnChanged(boolean isScreenTurningOn)1690 void onScreenTurningOnChanged(boolean isScreenTurningOn) { 1691 if (!mDestroyed) { 1692 mIsScreenTurningOn = isScreenTurningOn; 1693 reportVisibility(false); 1694 } 1695 } 1696 doVisibilityChanged(boolean visible)1697 void doVisibilityChanged(boolean visible) { 1698 if (!mDestroyed) { 1699 mVisible = visible; 1700 reportVisibility(false); 1701 if (mReportedVisible) processLocalColors(); 1702 } else { 1703 AnimationHandler.requestAnimatorsEnabled(visible, this); 1704 } 1705 } 1706 reportVisibility(boolean forceReport)1707 void reportVisibility(boolean forceReport) { 1708 if (mScreenshotSurfaceControl != null && mVisible) { 1709 if (DEBUG) Log.v(TAG, "Frozen so don't report visibility change"); 1710 return; 1711 } 1712 if (!mDestroyed) { 1713 mDisplayState = 1714 mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getCommittedState(); 1715 boolean displayFullyOn = Display.isOnState(mDisplayState) && !mIsScreenTurningOn; 1716 boolean supportsAmbientMode = 1717 mIWallpaperEngine.mInfo == null 1718 ? false 1719 : mIWallpaperEngine.mInfo.supportsAmbientMode(); 1720 // Report visibility only if display is fully on or wallpaper supports ambient mode. 1721 final boolean visible = mVisible && (displayFullyOn || supportsAmbientMode); 1722 if (DEBUG) { 1723 Log.v( 1724 TAG, 1725 "reportVisibility" 1726 + " mReportedVisible=" 1727 + mReportedVisible 1728 + " mVisible=" 1729 + mVisible 1730 + " mDisplayState=" 1731 + mDisplayState); 1732 } 1733 if (mReportedVisible != visible || forceReport) { 1734 mReportedVisible = visible; 1735 if (DEBUG) { 1736 Log.v( 1737 TAG, 1738 "onVisibilityChanged(" 1739 + visible 1740 + "): " 1741 + this 1742 + " forceReport=" 1743 + forceReport); 1744 } 1745 if (visible) { 1746 // If becoming visible, in preview mode the surface 1747 // may have been destroyed so now we need to make 1748 // sure it is re-created. 1749 doOffsetsChanged(false); 1750 // It will check mSurfaceCreated so no need to force relayout. 1751 updateSurface(false /* forceRelayout */, false /* forceReport */, 1752 false /* redrawNeeded */); 1753 } 1754 onVisibilityChanged(visible); 1755 if (mReportedVisible && mFrozenRequested) { 1756 if (DEBUG) Log.v(TAG, "Freezing wallpaper after visibility update"); 1757 freeze(); 1758 } 1759 AnimationHandler.requestAnimatorsEnabled(visible, this); 1760 } 1761 } 1762 } 1763 doOffsetsChanged(boolean always)1764 void doOffsetsChanged(boolean always) { 1765 if (mDestroyed) { 1766 return; 1767 } 1768 1769 if (!always && !mOffsetsChanged) { 1770 return; 1771 } 1772 1773 float xOffset; 1774 float yOffset; 1775 float xOffsetStep; 1776 float yOffsetStep; 1777 boolean sync; 1778 synchronized (mLock) { 1779 xOffset = mPendingXOffset; 1780 yOffset = mPendingYOffset; 1781 xOffsetStep = mPendingXOffsetStep; 1782 yOffsetStep = mPendingYOffsetStep; 1783 sync = mPendingSync; 1784 mPendingSync = false; 1785 mOffsetMessageEnqueued = false; 1786 } 1787 1788 if (mSurfaceCreated) { 1789 if (mReportedVisible) { 1790 if (DEBUG) Log.v(TAG, "Offsets change in " + this 1791 + ": " + xOffset + "," + yOffset); 1792 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 1793 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 1794 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 1795 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 1796 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 1797 } else { 1798 mOffsetsChanged = true; 1799 } 1800 } 1801 1802 if (sync) { 1803 try { 1804 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 1805 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 1806 } catch (RemoteException e) { 1807 } 1808 } 1809 1810 // setup local color extraction data 1811 processLocalColors(); 1812 } 1813 1814 /** 1815 * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of 1816 * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls. 1817 */ processLocalColors()1818 private void processLocalColors() { 1819 if (mProcessLocalColorsPending.compareAndSet(false, true)) { 1820 final long now = mClockFunction.get(); 1821 final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp; 1822 final long timeToWait = Math.max(0, 1823 PROCESS_LOCAL_COLORS_INTERVAL_MS - timeSinceLastColorProcess); 1824 1825 mHandler.postDelayed(() -> { 1826 mLastProcessLocalColorsTimestamp = now + timeToWait; 1827 mProcessLocalColorsPending.set(false); 1828 processLocalColorsInternal(); 1829 }, timeToWait); 1830 } 1831 } 1832 1833 /** 1834 * Default implementation of the local color extraction. 1835 * This will take a screenshot of the whole wallpaper on the main thread. 1836 * Then, in a background thread, for each launcher page, for each area that needs color 1837 * extraction in this page, creates a sub-bitmap and call {@link WallpaperColors#fromBitmap} 1838 * to extract the colors. Every time a launcher page has been processed, call 1839 * {@link #notifyLocalColorsChanged} with the color and areas of this page. 1840 */ processLocalColorsInternal()1841 private void processLocalColorsInternal() { 1842 if (supportsLocalColorExtraction()) return; 1843 float xOffset; 1844 float xOffsetStep; 1845 float wallpaperDimAmount; 1846 int xPage; 1847 int xPages; 1848 Set<RectF> areas; 1849 EngineWindowPage current; 1850 1851 synchronized (mLock) { 1852 xOffset = mPendingXOffset; 1853 xOffsetStep = mPendingXOffsetStep; 1854 wallpaperDimAmount = mWallpaperDimAmount; 1855 1856 if (DEBUG) { 1857 Log.d(TAG, "processLocalColors " + xOffset + " of step " 1858 + xOffsetStep); 1859 } 1860 if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN 1861 || !mSurfaceHolder.getSurface().isValid()) return; 1862 int xCurrentPage; 1863 if (!validStep(xOffsetStep)) { 1864 if (DEBUG) { 1865 Log.w(TAG, "invalid offset step " + xOffsetStep); 1866 } 1867 xOffset = 0; 1868 xOffsetStep = 1; 1869 xCurrentPage = 0; 1870 xPages = 1; 1871 } else { 1872 xPages = Math.round(1 / xOffsetStep) + 1; 1873 xOffsetStep = (float) 1 / (float) xPages; 1874 float shrink = (float) (xPages - 1) / (float) xPages; 1875 xOffset *= shrink; 1876 xCurrentPage = Math.round(xOffset / xOffsetStep); 1877 } 1878 if (DEBUG) { 1879 Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage); 1880 Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset); 1881 } 1882 1883 float finalXOffsetStep = xOffsetStep; 1884 float finalXOffset = xOffset; 1885 1886 resetWindowPages(); 1887 xPage = xCurrentPage; 1888 if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { 1889 mWindowPages = new EngineWindowPage[xPages]; 1890 initWindowPages(mWindowPages, finalXOffsetStep); 1891 } 1892 if (mLocalColorsToAdd.size() != 0) { 1893 for (RectF colorArea : mLocalColorsToAdd) { 1894 if (!isValid(colorArea)) continue; 1895 mLocalColorAreas.add(colorArea); 1896 int colorPage = getRectFPage(colorArea, finalXOffsetStep); 1897 EngineWindowPage currentPage = mWindowPages[colorPage]; 1898 currentPage.setLastUpdateTime(0); 1899 currentPage.removeColor(colorArea); 1900 } 1901 mLocalColorsToAdd.clear(); 1902 } 1903 if (xPage >= mWindowPages.length) { 1904 if (DEBUG) { 1905 Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage); 1906 Log.e(TAG, "error on page " + xPage + " out of " + xPages); 1907 Log.e(TAG, 1908 "error on xOffsetStep " + finalXOffsetStep 1909 + " xOffset " + finalXOffset); 1910 } 1911 xPage = mWindowPages.length - 1; 1912 } 1913 current = mWindowPages[xPage]; 1914 areas = new HashSet<>(current.getAreas()); 1915 } 1916 updatePage(current, areas, xPage, xPages, wallpaperDimAmount); 1917 } 1918 1919 @GuardedBy("mLock") initWindowPages(EngineWindowPage[] windowPages, float step)1920 private void initWindowPages(EngineWindowPage[] windowPages, float step) { 1921 for (int i = 0; i < windowPages.length; i++) { 1922 windowPages[i] = new EngineWindowPage(); 1923 } 1924 mLocalColorAreas.addAll(mLocalColorsToAdd); 1925 mLocalColorsToAdd.clear(); 1926 for (RectF area: mLocalColorAreas) { 1927 if (!isValid(area)) { 1928 mLocalColorAreas.remove(area); 1929 continue; 1930 } 1931 int pageNum = getRectFPage(area, step); 1932 windowPages[pageNum].addArea(area); 1933 } 1934 } 1935 updatePage(EngineWindowPage currentPage, Set<RectF> areas, int pageIndx, int numPages, float wallpaperDimAmount)1936 void updatePage(EngineWindowPage currentPage, Set<RectF> areas, int pageIndx, int numPages, 1937 float wallpaperDimAmount) { 1938 1939 // in case the clock is zero, we start with negative time 1940 long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION; 1941 long lapsed = current - currentPage.getLastUpdateTime(); 1942 // Always update the page when the last update time is <= 0 1943 // This is important especially when the device first boots 1944 if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return; 1945 1946 Surface surface = mSurfaceHolder.getSurface(); 1947 if (!surface.isValid()) return; 1948 boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y; 1949 int smaller = widthIsLarger ? mSurfaceSize.x 1950 : mSurfaceSize.y; 1951 float ratio = (float) MIN_BITMAP_SCREENSHOT_WIDTH / (float) smaller; 1952 int width = (int) (ratio * mSurfaceSize.x); 1953 int height = (int) (ratio * mSurfaceSize.y); 1954 if (width <= 0 || height <= 0) { 1955 Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); 1956 return; 1957 } 1958 final String pixelCopySectionName = "WallpaperService#pixelCopy"; 1959 final int pixelCopyCount = mPixelCopyCount++; 1960 Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount); 1961 Bitmap screenShot = Bitmap.createBitmap(width, height, 1962 Bitmap.Config.ARGB_8888); 1963 final Bitmap finalScreenShot = screenShot; 1964 try { 1965 // TODO(b/274427458) check if this can be done in the background. 1966 PixelCopy.request(surface, screenShot, (res) -> { 1967 Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount); 1968 if (DEBUG) { 1969 Log.d(TAG, "result of pixel copy is: " 1970 + (res == PixelCopy.SUCCESS ? "SUCCESS" : "FAILURE")); 1971 } 1972 if (res != PixelCopy.SUCCESS) { 1973 Bitmap lastBitmap = currentPage.getBitmap(); 1974 // assign the last bitmap taken for now 1975 currentPage.setBitmap(mLastScreenshot); 1976 Bitmap lastScreenshot = mLastScreenshot; 1977 if (lastScreenshot != null && !Objects.equals(lastBitmap, lastScreenshot)) { 1978 updatePageColors( 1979 currentPage, areas, pageIndx, numPages, wallpaperDimAmount); 1980 } 1981 } else { 1982 mLastScreenshot = finalScreenShot; 1983 currentPage.setBitmap(finalScreenShot); 1984 currentPage.setLastUpdateTime(current); 1985 updatePageColors( 1986 currentPage, areas, pageIndx, numPages, wallpaperDimAmount); 1987 } 1988 }, mBackgroundHandler); 1989 } catch (IllegalArgumentException e) { 1990 // this can potentially happen if the surface is invalidated right between the 1991 // surface.isValid() check and the PixelCopy operation. 1992 // in this case, stop: we'll compute colors on the next processLocalColors call. 1993 Log.w(TAG, "Cancelling processLocalColors: exception caught during PixelCopy"); 1994 } 1995 } 1996 // locked by the passed page updatePageColors(EngineWindowPage page, Set<RectF> areas, int pageIndx, int numPages, float wallpaperDimAmount)1997 private void updatePageColors(EngineWindowPage page, Set<RectF> areas, 1998 int pageIndx, int numPages, float wallpaperDimAmount) { 1999 if (page.getBitmap() == null) return; 2000 if (!mBackgroundHandler.getLooper().isCurrentThread()) { 2001 throw new IllegalStateException( 2002 "ProcessLocalColors should be called from the background thread"); 2003 } 2004 Trace.beginSection("WallpaperService#updatePageColors"); 2005 if (DEBUG) { 2006 Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " 2007 + page.getAreas().size() + " and bitmap size of " 2008 + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); 2009 } 2010 for (RectF area: areas) { 2011 if (area == null) continue; 2012 RectF subArea = generateSubRect(area, pageIndx, numPages); 2013 Bitmap b = page.getBitmap(); 2014 int x = Math.round(b.getWidth() * subArea.left); 2015 int y = Math.round(b.getHeight() * subArea.top); 2016 int width = Math.round(b.getWidth() * subArea.width()); 2017 int height = Math.round(b.getHeight() * subArea.height()); 2018 Bitmap target; 2019 try { 2020 target = Bitmap.createBitmap(b, x, y, width, height); 2021 } catch (Exception e) { 2022 Log.e(TAG, "Error creating page local color bitmap", e); 2023 continue; 2024 } 2025 WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount); 2026 target.recycle(); 2027 WallpaperColors currentColor = page.getColors(area); 2028 2029 if (DEBUG) { 2030 Log.d(TAG, "getting local bitmap area x " + x + " y " + y 2031 + " width " + width + " height " + height + " for sub area " + subArea 2032 + " and with page " + pageIndx + " of " + numPages); 2033 2034 } 2035 if (currentColor == null || !color.equals(currentColor)) { 2036 page.addWallpaperColors(area, color); 2037 if (DEBUG) { 2038 Log.d(TAG, "onLocalWallpaperColorsChanged" 2039 + " local color callback for area" + area + " for page " + pageIndx 2040 + " of " + numPages); 2041 } 2042 mHandler.post(() -> { 2043 try { 2044 mConnection.onLocalWallpaperColorsChanged(area, color, 2045 mDisplayContext.getDisplayId()); 2046 } catch (RemoteException e) { 2047 Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); 2048 } 2049 }); 2050 } 2051 } 2052 Trace.endSection(); 2053 } 2054 generateSubRect(RectF in, int pageInx, int numPages)2055 private RectF generateSubRect(RectF in, int pageInx, int numPages) { 2056 float minLeft = (float) (pageInx) / (float) (numPages); 2057 float maxRight = (float) (pageInx + 1) / (float) (numPages); 2058 float left = in.left; 2059 float right = in.right; 2060 2061 // bound rect 2062 if (left < minLeft) left = minLeft; 2063 if (right > maxRight) right = maxRight; 2064 2065 // scale up the sub area then trim 2066 left = (left * (float) numPages) % 1f; 2067 right = (right * (float) numPages) % 1f; 2068 if (right == 0f) { 2069 right = 1f; 2070 } 2071 2072 return new RectF(left, in.top, right, in.bottom); 2073 } 2074 2075 @GuardedBy("mLock") resetWindowPages()2076 private void resetWindowPages() { 2077 if (supportsLocalColorExtraction()) return; 2078 if (!mResetWindowPages) return; 2079 mResetWindowPages = false; 2080 for (int i = 0; i < mWindowPages.length; i++) { 2081 mWindowPages[i].setLastUpdateTime(0L); 2082 } 2083 } 2084 2085 @GuardedBy("mLock") getRectFPage(RectF area, float step)2086 private int getRectFPage(RectF area, float step) { 2087 if (!isValid(area)) return 0; 2088 if (!validStep(step)) return 0; 2089 int pages = Math.round(1 / step); 2090 int page = Math.round(area.centerX() * pages); 2091 if (page == pages) return pages - 1; 2092 if (page == mWindowPages.length) page = mWindowPages.length - 1; 2093 return page; 2094 } 2095 2096 /** 2097 * Add local colors areas of interest 2098 * @param regions list of areas 2099 * @hide 2100 */ addLocalColorsAreas(@onNull List<RectF> regions)2101 public void addLocalColorsAreas(@NonNull List<RectF> regions) { 2102 if (supportsLocalColorExtraction()) return; 2103 if (DEBUG) { 2104 Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); 2105 } 2106 mBackgroundHandler.post(() -> { 2107 synchronized (mLock) { 2108 mLocalColorsToAdd.addAll(regions); 2109 } 2110 processLocalColors(); 2111 }); 2112 } 2113 2114 /** 2115 * Remove local colors areas of interest if they exist 2116 * @param regions list of areas 2117 * @hide 2118 */ removeLocalColorsAreas(@onNull List<RectF> regions)2119 public void removeLocalColorsAreas(@NonNull List<RectF> regions) { 2120 if (supportsLocalColorExtraction()) return; 2121 mBackgroundHandler.post(() -> { 2122 synchronized (mLock) { 2123 float step = mPendingXOffsetStep; 2124 mLocalColorsToAdd.removeAll(regions); 2125 mLocalColorAreas.removeAll(regions); 2126 if (!validStep(step)) { 2127 return; 2128 } 2129 for (int i = 0; i < mWindowPages.length; i++) { 2130 for (int j = 0; j < regions.size(); j++) { 2131 mWindowPages[i].removeArea(regions.get(j)); 2132 } 2133 } 2134 } 2135 }); 2136 } 2137 2138 // fix the rect to be included within the bounds of the bitmap fixRect(Bitmap b, Rect r)2139 private Rect fixRect(Bitmap b, Rect r) { 2140 r.left = r.left >= r.right || r.left >= b.getWidth() || r.left > 0 2141 ? 0 2142 : r.left; 2143 r.right = r.left >= r.right || r.right > b.getWidth() 2144 ? b.getWidth() 2145 : r.right; 2146 return r; 2147 } 2148 validStep(float step)2149 private boolean validStep(float step) { 2150 return !Float.isNaN(step) && step > 0f && step <= 1f; 2151 } 2152 doCommand(WallpaperCommand cmd)2153 void doCommand(WallpaperCommand cmd) { 2154 Bundle result; 2155 if (!mDestroyed) { 2156 if (COMMAND_FREEZE.equals(cmd.action) || COMMAND_UNFREEZE.equals(cmd.action)) { 2157 updateFrozenState(/* frozenRequested= */ !COMMAND_UNFREEZE.equals(cmd.action)); 2158 } 2159 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 2160 cmd.extras, cmd.sync); 2161 } else { 2162 result = null; 2163 } 2164 if (cmd.sync) { 2165 try { 2166 if (DEBUG) Log.v(TAG, "Reporting command complete"); 2167 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 2168 } catch (RemoteException e) { 2169 } 2170 } 2171 } 2172 updateFrozenState(boolean frozenRequested)2173 private void updateFrozenState(boolean frozenRequested) { 2174 if (mIWallpaperEngine.mInfo == null 2175 // Procees the unfreeze command in case the wallaper became static while 2176 // being paused. 2177 && frozenRequested) { 2178 if (DEBUG) Log.v(TAG, "Ignoring the freeze command for static wallpapers"); 2179 return; 2180 } 2181 mFrozenRequested = frozenRequested; 2182 boolean isFrozen = mScreenshotSurfaceControl != null; 2183 if (mFrozenRequested == isFrozen) { 2184 return; 2185 } 2186 if (mFrozenRequested) { 2187 freeze(); 2188 } else { 2189 unfreeze(); 2190 } 2191 } 2192 freeze()2193 private void freeze() { 2194 if (!mReportedVisible || mDestroyed) { 2195 // Screenshot can't be taken until visibility is reported to the wallpaper host. 2196 return; 2197 } 2198 if (!showScreenshotOfWallpaper()) { 2199 return; 2200 } 2201 // Prevent a wallpaper host from rendering wallpaper behind a screeshot. 2202 doVisibilityChanged(false); 2203 // Remember that visibility is requested since it's not guaranteed that 2204 // mWindow#dispatchAppVisibility will be called when letterboxed application with 2205 // wallpaper background transitions to the Home screen. 2206 mVisible = true; 2207 } 2208 unfreeze()2209 private void unfreeze() { 2210 cleanUpScreenshotSurfaceControl(); 2211 if (mVisible) { 2212 doVisibilityChanged(true); 2213 } 2214 } 2215 cleanUpScreenshotSurfaceControl()2216 private void cleanUpScreenshotSurfaceControl() { 2217 // TODO(b/194399558): Add crossfade transition. 2218 if (mScreenshotSurfaceControl != null) { 2219 new SurfaceControl.Transaction() 2220 .remove(mScreenshotSurfaceControl) 2221 .show(mBbqSurfaceControl) 2222 .apply(); 2223 mScreenshotSurfaceControl = null; 2224 } 2225 } 2226 scaleAndCropScreenshot()2227 void scaleAndCropScreenshot() { 2228 if (mScreenshotSurfaceControl == null) { 2229 return; 2230 } 2231 if (mScreenshotSize.x <= 0 || mScreenshotSize.y <= 0) { 2232 Log.w(TAG, "Unexpected screenshot size: " + mScreenshotSize); 2233 return; 2234 } 2235 // Don't scale down and using the same scaling factor for both dimensions to 2236 // avoid stretching wallpaper image. 2237 float scaleFactor = Math.max(1, Math.max( 2238 ((float) mSurfaceSize.x) / mScreenshotSize.x, 2239 ((float) mSurfaceSize.y) / mScreenshotSize.y)); 2240 int diffX = ((int) (mScreenshotSize.x * scaleFactor)) - mSurfaceSize.x; 2241 int diffY = ((int) (mScreenshotSize.y * scaleFactor)) - mSurfaceSize.y; 2242 if (DEBUG) { 2243 Log.v(TAG, "Adjusting screenshot: scaleFactor=" + scaleFactor 2244 + " diffX=" + diffX + " diffY=" + diffY + " mSurfaceSize=" + mSurfaceSize 2245 + " mScreenshotSize=" + mScreenshotSize); 2246 } 2247 new SurfaceControl.Transaction() 2248 .setMatrix( 2249 mScreenshotSurfaceControl, 2250 /* dsdx= */ scaleFactor, /* dtdx= */ 0, 2251 /* dtdy= */ 0, /* dsdy= */ scaleFactor) 2252 .setWindowCrop( 2253 mScreenshotSurfaceControl, 2254 new Rect( 2255 /* left= */ diffX / 2, 2256 /* top= */ diffY / 2, 2257 /* right= */ diffX / 2 + mScreenshotSize.x, 2258 /* bottom= */ diffY / 2 + mScreenshotSize.y)) 2259 .setPosition(mScreenshotSurfaceControl, -diffX / 2, -diffY / 2) 2260 .apply(); 2261 } 2262 showScreenshotOfWallpaper()2263 private boolean showScreenshotOfWallpaper() { 2264 if (mDestroyed || mSurfaceControl == null || !mSurfaceControl.isValid()) { 2265 if (DEBUG) Log.v(TAG, "Failed to screenshot wallpaper: surface isn't valid"); 2266 return false; 2267 } 2268 2269 final Rect bounds = new Rect(0, 0, mSurfaceSize.x, mSurfaceSize.y); 2270 if (bounds.isEmpty()) { 2271 Log.w(TAG, "Failed to screenshot wallpaper: surface bounds are empty"); 2272 return false; 2273 } 2274 2275 if (mScreenshotSurfaceControl != null) { 2276 Log.e(TAG, "Screenshot is unexpectedly not null"); 2277 // Destroying previous screenshot since it can have different size. 2278 cleanUpScreenshotSurfaceControl(); 2279 } 2280 2281 ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = 2282 ScreenCapture.captureLayers( 2283 new ScreenCapture.LayerCaptureArgs.Builder(mSurfaceControl) 2284 // Needed because SurfaceFlinger#validateScreenshotPermissions 2285 // uses this parameter to check whether a caller only attempts 2286 // to screenshot itself when call doesn't come from the system. 2287 .setUid(Process.myUid()) 2288 .setChildrenOnly(false) 2289 .setSourceCrop(bounds) 2290 .build()); 2291 2292 if (screenshotBuffer == null) { 2293 Log.w(TAG, "Failed to screenshot wallpaper: screenshotBuffer is null"); 2294 return false; 2295 } 2296 2297 final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); 2298 2299 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 2300 2301 // TODO(b/194399558): Add crossfade transition. 2302 mScreenshotSurfaceControl = new SurfaceControl.Builder() 2303 .setName("Wallpaper snapshot for engine " + this) 2304 .setFormat(hardwareBuffer.getFormat()) 2305 .setParent(mSurfaceControl) 2306 .setSecure(screenshotBuffer.containsSecureLayers()) 2307 .setCallsite("WallpaperService.Engine.showScreenshotOfWallpaper") 2308 .setBLASTLayer() 2309 .build(); 2310 2311 mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y); 2312 2313 t.setBuffer(mScreenshotSurfaceControl, hardwareBuffer); 2314 t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace()); 2315 // Place on top everything else. 2316 t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE); 2317 t.show(mScreenshotSurfaceControl); 2318 t.hide(mBbqSurfaceControl); 2319 t.apply(); 2320 2321 return true; 2322 } 2323 reportSurfaceDestroyed()2324 void reportSurfaceDestroyed() { 2325 if ((!noDuplicateSurfaceDestroyedEvents() && mSurfaceCreated) 2326 || (noDuplicateSurfaceDestroyedEvents() && mReportedSurfaceCreated)) { 2327 mSurfaceCreated = false; 2328 mReportedSurfaceCreated = false; 2329 mSurfaceHolder.ungetCallbacks(); 2330 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 2331 if (callbacks != null) { 2332 for (SurfaceHolder.Callback c : callbacks) { 2333 c.surfaceDestroyed(mSurfaceHolder); 2334 } 2335 } 2336 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 2337 + mSurfaceHolder + "): " + this); 2338 onSurfaceDestroyed(mSurfaceHolder); 2339 } 2340 } 2341 2342 /** 2343 * @hide 2344 */ 2345 @VisibleForTesting detach()2346 public void detach() { 2347 if (mDestroyed) { 2348 return; 2349 } 2350 2351 AnimationHandler.removeRequestor(this); 2352 2353 mDestroyed = true; 2354 2355 if (mIWallpaperEngine != null && mIWallpaperEngine.mDisplayManager != null) { 2356 mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener); 2357 } 2358 2359 if (mVisible) { 2360 mVisible = false; 2361 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 2362 onVisibilityChanged(false); 2363 } 2364 2365 reportSurfaceDestroyed(); 2366 2367 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 2368 onDestroy(); 2369 2370 synchronized (mSurfaceReleaseLock) { 2371 if (mCreated) { 2372 try { 2373 if (DEBUG) { 2374 Log.v(TAG, "Removing window and destroying surface " 2375 + mSurfaceHolder.getSurface() + " of: " + this); 2376 } 2377 2378 if (mInputEventReceiver != null) { 2379 mInputEventReceiver.dispose(); 2380 mInputEventReceiver = null; 2381 } 2382 2383 mSession.remove(mWindow.asBinder()); 2384 } catch (RemoteException e) { 2385 } 2386 mSurfaceHolder.mSurface.release(); 2387 if (mBlastBufferQueue != null) { 2388 mBlastBufferQueue.destroy(); 2389 mBlastBufferQueue = null; 2390 } 2391 if (mBbqSurfaceControl != null) { 2392 new SurfaceControl.Transaction().remove(mBbqSurfaceControl).apply(); 2393 mBbqSurfaceControl = null; 2394 } 2395 mCreated = false; 2396 } 2397 2398 if (mSurfaceControl != null) { 2399 mSurfaceControl.release(); 2400 mSurfaceControl = null; 2401 mRelayoutResult = null; 2402 } 2403 } 2404 } 2405 2406 private final DisplayListener mDisplayListener = 2407 new DisplayListener() { 2408 @Override 2409 public void onDisplayChanged(int displayId) { 2410 if (mDisplay.getDisplayId() == displayId) { 2411 boolean forceReport = 2412 !noVisibilityEventOnDisplayStateChange() 2413 && mIsWearOs 2414 && mDisplay.getState() != Display.STATE_DOZE_SUSPEND; 2415 reportVisibility(forceReport); 2416 } 2417 } 2418 2419 @Override 2420 public void onDisplayRemoved(int displayId) {} 2421 2422 @Override 2423 public void onDisplayAdded(int displayId) {} 2424 }; 2425 getOrCreateBLASTSurface(int width, int height, int format)2426 private Surface getOrCreateBLASTSurface(int width, int height, int format) { 2427 if (mBbqSurfaceControl == null || !mBbqSurfaceControl.isValid()) { 2428 Log.w(TAG, "Skipping BlastBufferQueue update/create" 2429 + " - invalid surface control"); 2430 return null; 2431 } 2432 2433 Surface ret = null; 2434 if (mBlastBufferQueue == null) { 2435 mBlastBufferQueue = new BLASTBufferQueue("Wallpaper", 2436 true /* updateDestinationFrame */); 2437 mBlastBufferQueue.setApplyToken(mBbqApplyToken); 2438 mBlastBufferQueue.update(mBbqSurfaceControl, width, height, format); 2439 // We only return the Surface the first time, as otherwise 2440 // it hasn't changed and there is no need to update. 2441 ret = mBlastBufferQueue.createSurface(); 2442 } else { 2443 mBlastBufferQueue.update(mBbqSurfaceControl, width, height, format); 2444 } 2445 2446 return ret; 2447 } 2448 } 2449 2450 /** 2451 * Returns a Looper which messages such as {@link WallpaperService#DO_ATTACH}, 2452 * {@link WallpaperService#DO_DETACH} etc. are sent to. 2453 * By default, returns the process's main looper. 2454 * @hide 2455 */ 2456 @NonNull onProvideEngineLooper()2457 public Looper onProvideEngineLooper() { 2458 return super.getMainLooper(); 2459 } 2460 isValid(RectF area)2461 private boolean isValid(RectF area) { 2462 if (area == null) return false; 2463 boolean valid = area.bottom > area.top && area.left < area.right 2464 && LOCAL_COLOR_BOUNDS.contains(area); 2465 return valid; 2466 } 2467 2468 private boolean inRectFRange(float number) { 2469 return number >= 0f && number <= 1f; 2470 } 2471 2472 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 2473 implements HandlerCaller.Callback { 2474 private final HandlerCaller mCaller; 2475 2476 final IWallpaperConnection mConnection; 2477 final IBinder mWindowToken; 2478 final int mWindowType; 2479 final boolean mIsPreview; 2480 final AtomicInteger mPendingResizeCount = new AtomicInteger(); 2481 boolean mReportDraw; 2482 boolean mShownReported; 2483 int mReqWidth; 2484 int mReqHeight; 2485 final Rect mDisplayPadding = new Rect(); 2486 final int mDisplayId; 2487 final DisplayManager mDisplayManager; 2488 final Display mDisplay; 2489 final WallpaperManager mWallpaperManager; 2490 @Nullable final WallpaperInfo mInfo; 2491 @NonNull final WallpaperDescription mDescription; 2492 2493 Engine mEngine; 2494 @SetWallpaperFlags int mWhich; 2495 IWallpaperEngineWrapper(WallpaperService service, IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId, @SetWallpaperFlags int which, @Nullable WallpaperInfo info, @NonNull WallpaperDescription description)2496 IWallpaperEngineWrapper(WallpaperService service, 2497 IWallpaperConnection conn, IBinder windowToken, 2498 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, 2499 int displayId, @SetWallpaperFlags int which, @Nullable WallpaperInfo info, 2500 @NonNull WallpaperDescription description) { 2501 mWallpaperManager = getSystemService(WallpaperManager.class); 2502 mCaller = new HandlerCaller(service, service.onProvideEngineLooper(), this, true); 2503 mConnection = conn; 2504 mWindowToken = windowToken; 2505 mWindowType = windowType; 2506 mIsPreview = isPreview; 2507 mReqWidth = reqWidth; 2508 mReqHeight = reqHeight; 2509 mDisplayPadding.set(padding); 2510 mDisplayId = displayId; 2511 mWhich = which; 2512 mInfo = info; 2513 mDescription = description; 2514 2515 // Create a display context before onCreateEngine. 2516 mDisplayManager = getSystemService(DisplayManager.class); 2517 mDisplay = mDisplayManager.getDisplay(mDisplayId); 2518 2519 if (mDisplay == null) { 2520 // Ignore this engine. 2521 throw new IllegalArgumentException("Cannot find display with id" + mDisplayId); 2522 } 2523 Message msg = mCaller.obtainMessage(DO_ATTACH); 2524 mCaller.sendMessage(msg); 2525 } 2526 setDesiredSize(int width, int height)2527 public void setDesiredSize(int width, int height) { 2528 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 2529 mCaller.sendMessage(msg); 2530 } 2531 setDisplayPadding(Rect padding)2532 public void setDisplayPadding(Rect padding) { 2533 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 2534 mCaller.sendMessage(msg); 2535 } 2536 setVisibility(boolean visible)2537 public void setVisibility(boolean visible) { 2538 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 2539 visible ? 1 : 0); 2540 mCaller.sendMessage(msg); 2541 } 2542 2543 @Override setWallpaperFlags(@etWallpaperFlags int which)2544 public void setWallpaperFlags(@SetWallpaperFlags int which) { 2545 if (which == mWhich) { 2546 return; 2547 } 2548 mWhich = which; 2549 Message msg = mCaller.obtainMessageI(MSG_WALLPAPER_FLAGS_CHANGED, which); 2550 mCaller.sendMessage(msg); 2551 } 2552 2553 @Override setInAmbientMode(boolean inAmbientDisplay, long animationDuration)2554 public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration) 2555 throws RemoteException { 2556 Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0, 2557 animationDuration); 2558 mCaller.sendMessage(msg); 2559 } 2560 dispatchPointer(MotionEvent event)2561 public void dispatchPointer(MotionEvent event) { 2562 if (mEngine != null) { 2563 mEngine.dispatchPointer(event); 2564 } else { 2565 event.recycle(); 2566 } 2567 } 2568 dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras)2569 public void dispatchWallpaperCommand(String action, int x, int y, 2570 int z, Bundle extras) { 2571 if (mEngine != null) { 2572 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 2573 } 2574 } 2575 setZoomOut(float scale)2576 public void setZoomOut(float scale) { 2577 Message msg = mCaller.obtainMessageI(MSG_ZOOM, Float.floatToIntBits(scale)); 2578 mCaller.sendMessage(msg); 2579 } 2580 reportShown()2581 public void reportShown() { 2582 if (mEngine == null) { 2583 Log.i(TAG, "Can't report null engine as shown."); 2584 return; 2585 } 2586 if (mEngine.mDestroyed) { 2587 Log.i(TAG, "Engine was destroyed before we could draw."); 2588 return; 2589 } 2590 if (!mShownReported) { 2591 mShownReported = true; 2592 Trace.beginSection("WPMS.mConnection.engineShown"); 2593 try { 2594 mConnection.engineShown(this); 2595 Log.d(TAG, "Wallpaper has updated the surface:" + mInfo); 2596 } catch (RemoteException e) { 2597 Log.w(TAG, "Wallpaper host disappeared", e); 2598 } 2599 Trace.endSection(); 2600 } 2601 } 2602 requestWallpaperColors()2603 public void requestWallpaperColors() { 2604 Message msg = mCaller.obtainMessage(MSG_REQUEST_WALLPAPER_COLORS); 2605 mCaller.sendMessage(msg); 2606 } 2607 addLocalColorsAreas(List<RectF> regions)2608 public void addLocalColorsAreas(List<RectF> regions) { 2609 mEngine.addLocalColorsAreas(regions); 2610 } 2611 removeLocalColorsAreas(List<RectF> regions)2612 public void removeLocalColorsAreas(List<RectF> regions) { 2613 mEngine.removeLocalColorsAreas(regions); 2614 } 2615 applyDimming(float dimAmount)2616 public void applyDimming(float dimAmount) throws RemoteException { 2617 Message msg = mCaller.obtainMessageI(MSG_UPDATE_DIMMING, 2618 Float.floatToIntBits(dimAmount)); 2619 mCaller.sendMessage(msg); 2620 } 2621 destroy()2622 public void destroy() { 2623 Message msg = mCaller.obtainMessage(DO_DETACH); 2624 mCaller.getHandler().removeCallbacksAndMessages(null); 2625 mCaller.sendMessage(msg); 2626 } 2627 resizePreview(Rect position)2628 public void resizePreview(Rect position) { 2629 Message msg = mCaller.obtainMessageO(MSG_RESIZE_PREVIEW, position); 2630 mCaller.sendMessage(msg); 2631 } 2632 2633 @Nullable mirrorSurfaceControl()2634 public SurfaceControl mirrorSurfaceControl() { 2635 return mEngine == null ? null : SurfaceControl.mirrorSurface(mEngine.mSurfaceControl); 2636 } 2637 2638 @Nullable onApplyWallpaper(@etWallpaperFlags int which)2639 public WallpaperDescription onApplyWallpaper(@SetWallpaperFlags int which) { 2640 return mEngine != null ? mEngine.onApplyWallpaper(which) : null; 2641 } 2642 doAttachEngine()2643 private void doAttachEngine() { 2644 Trace.beginSection("WPMS.onCreateEngine"); 2645 Engine engine; 2646 if (liveWallpaperContentHandling()) { 2647 engine = onCreateEngine(mDescription); 2648 } else { 2649 engine = onCreateEngine(); 2650 } 2651 Trace.endSection(); 2652 mEngine = engine; 2653 Trace.beginSection("WPMS.mConnection.attachEngine-" + mDisplayId); 2654 try { 2655 mConnection.attachEngine(this, mDisplayId); 2656 } catch (RemoteException e) { 2657 engine.detach(); 2658 Log.w(TAG, "Wallpaper host disappeared", e); 2659 return; 2660 } catch (IllegalStateException e) { 2661 Log.w(TAG, "Connector instance already destroyed, " 2662 + "can't attach engine to non existing connector", e); 2663 return; 2664 } finally { 2665 Trace.endSection(); 2666 } 2667 Trace.beginSection("WPMS.engine.attach"); 2668 engine.attach(this); 2669 Trace.endSection(); 2670 } 2671 doDetachEngine()2672 private void doDetachEngine() { 2673 // Some wallpapers will not trigger the rendering threads of the remaining engines even 2674 // if they are visible, so we need to toggle the state to get their attention. 2675 if (mEngine != null && !mEngine.mDestroyed) { 2676 mEngine.detach(); 2677 synchronized (mActiveEngines) { 2678 for (IWallpaperEngineWrapper engineWrapper : mActiveEngines.values()) { 2679 if (engineWrapper.mEngine != null && engineWrapper.mEngine.mVisible) { 2680 engineWrapper.mEngine.doVisibilityChanged(false); 2681 engineWrapper.mEngine.doVisibilityChanged(true); 2682 } 2683 } 2684 } 2685 } 2686 } 2687 updateScreenTurningOn(boolean isScreenTurningOn)2688 public void updateScreenTurningOn(boolean isScreenTurningOn) { 2689 Message msg = mCaller.obtainMessageBO(MSG_UPDATE_SCREEN_TURNING_ON, isScreenTurningOn, 2690 null); 2691 mCaller.sendMessage(msg); 2692 } 2693 onScreenTurningOn()2694 public void onScreenTurningOn() throws RemoteException { 2695 updateScreenTurningOn(true); 2696 } 2697 onScreenTurnedOn()2698 public void onScreenTurnedOn() throws RemoteException { 2699 updateScreenTurningOn(false); 2700 } 2701 2702 @Override executeMessage(Message message)2703 public void executeMessage(Message message) { 2704 switch (message.what) { 2705 case DO_ATTACH: { 2706 Trace.beginSection("WPMS.DO_ATTACH"); 2707 doAttachEngine(); 2708 Trace.endSection(); 2709 return; 2710 } 2711 case DO_DETACH: { 2712 Trace.beginSection("WPMS.DO_DETACH"); 2713 doDetachEngine(); 2714 Trace.endSection(); 2715 return; 2716 } 2717 case DO_SET_DESIRED_SIZE: { 2718 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 2719 return; 2720 } 2721 case DO_SET_DISPLAY_PADDING: { 2722 mEngine.doDisplayPaddingChanged((Rect) message.obj); 2723 return; 2724 } 2725 case DO_IN_AMBIENT_MODE: { 2726 mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj); 2727 return; 2728 } 2729 case MSG_UPDATE_SURFACE: 2730 mEngine.updateSurface(true, false, false); 2731 break; 2732 case MSG_ZOOM: 2733 mEngine.setZoom(Float.intBitsToFloat(message.arg1)); 2734 break; 2735 case MSG_UPDATE_DIMMING: 2736 mEngine.updateWallpaperDimming(Float.intBitsToFloat(message.arg1)); 2737 break; 2738 case MSG_RESIZE_PREVIEW: 2739 mEngine.resizePreview((Rect) message.obj); 2740 break; 2741 case MSG_VISIBILITY_CHANGED: 2742 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 2743 + ": " + message.arg1); 2744 mEngine.doVisibilityChanged(message.arg1 != 0); 2745 break; 2746 case MSG_UPDATE_SCREEN_TURNING_ON: 2747 if (DEBUG) { 2748 Log.v(TAG, 2749 message.arg1 != 0 ? "Screen turning on" : "Screen turned on"); 2750 } 2751 mEngine.onScreenTurningOnChanged(/* isScreenTurningOn= */ message.arg1 != 0); 2752 break; 2753 case MSG_WALLPAPER_OFFSETS: { 2754 mEngine.doOffsetsChanged(true); 2755 } break; 2756 case MSG_WALLPAPER_COMMAND: { 2757 WallpaperCommand cmd = (WallpaperCommand)message.obj; 2758 mEngine.doCommand(cmd); 2759 } break; 2760 case MSG_WINDOW_RESIZED: { 2761 handleResized((MergedConfiguration) message.obj, message.arg1 != 0); 2762 } break; 2763 case MSG_WINDOW_MOVED: { 2764 // Do nothing. What does it mean for a Wallpaper to move? 2765 } break; 2766 case MSG_TOUCH_EVENT: { 2767 boolean skip = false; 2768 MotionEvent ev = (MotionEvent)message.obj; 2769 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 2770 synchronized (mEngine.mLock) { 2771 if (mEngine.mPendingMove == ev) { 2772 mEngine.mPendingMove = null; 2773 } else { 2774 // this is not the motion event we are looking for.... 2775 skip = true; 2776 } 2777 } 2778 } 2779 if (!skip) { 2780 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 2781 mEngine.onTouchEvent(ev); 2782 } 2783 ev.recycle(); 2784 } break; 2785 case MSG_REQUEST_WALLPAPER_COLORS: { 2786 if (mConnection == null) { 2787 break; 2788 } 2789 try { 2790 WallpaperColors colors = mEngine.onComputeColors(); 2791 mEngine.setPrimaryWallpaperColors(colors); 2792 mConnection.onWallpaperColorsChanged(colors, mDisplayId); 2793 } catch (RemoteException e) { 2794 // Connection went away, nothing to do in here. 2795 } 2796 } break; 2797 case MSG_REPORT_SHOWN: { 2798 Trace.beginSection("WPMS.MSG_REPORT_SHOWN"); 2799 reportShown(); 2800 Trace.endSection(); 2801 } break; 2802 case MSG_WALLPAPER_FLAGS_CHANGED: { 2803 mEngine.onWallpaperFlagsChanged(message.arg1); 2804 } break; 2805 default : 2806 Log.w(TAG, "Unknown message type " + message.what); 2807 } 2808 } 2809 2810 /** 2811 * In general this performs relayout for IWindow#resized. If there are several pending 2812 * (in the message queue) MSG_WINDOW_RESIZED from server side, only the last one will be 2813 * handled (ignore intermediate states). Note that this procedure cannot be skipped if the 2814 * configuration is not changed because this is also used to dispatch insets changes. 2815 */ handleResized(MergedConfiguration config, boolean reportDraw)2816 private void handleResized(MergedConfiguration config, boolean reportDraw) { 2817 // The config can be null when retrying for a changed config from relayout, otherwise 2818 // it is from IWindow#resized which always sends non-null config. 2819 final int pendingCount = config != null ? mPendingResizeCount.decrementAndGet() : -1; 2820 if (reportDraw) { 2821 mReportDraw = true; 2822 } 2823 if (pendingCount > 0) { 2824 if (DEBUG) { 2825 Log.d(TAG, "Skip outdated resize, bounds=" 2826 + config.getMergedConfiguration().windowConfiguration.getMaxBounds() 2827 + " pendingCount=" + pendingCount); 2828 } 2829 return; 2830 } 2831 if (config != null) { 2832 if (DEBUG) { 2833 Log.d(TAG, "Update config from resized, bounds=" 2834 + config.getMergedConfiguration().windowConfiguration.getMaxBounds()); 2835 } 2836 mEngine.mMergedConfiguration.setTo(config); 2837 } 2838 mEngine.updateSurface(true /* forceRelayout */, false /* forceReport */, mReportDraw); 2839 mReportDraw = false; 2840 mEngine.doOffsetsChanged(true); 2841 mEngine.scaleAndCropScreenshot(); 2842 } 2843 } 2844 2845 /** 2846 * Implements the internal {@link IWallpaperService} interface to convert 2847 * incoming calls to it back to calls on an {@link WallpaperService}. 2848 */ 2849 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 2850 private final WallpaperService mTarget; 2851 IWallpaperServiceWrapper(WallpaperService context)2852 public IWallpaperServiceWrapper(WallpaperService context) { 2853 mTarget = context; 2854 } 2855 2856 @Override attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId, @SetWallpaperFlags int which, WallpaperInfo info, @NonNull WallpaperDescription description)2857 public void attach(IWallpaperConnection conn, IBinder windowToken, 2858 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, 2859 int displayId, @SetWallpaperFlags int which, WallpaperInfo info, 2860 @NonNull WallpaperDescription description) { 2861 Trace.beginSection("WPMS.ServiceWrapper.attach"); 2862 IWallpaperEngineWrapper engineWrapper = 2863 new IWallpaperEngineWrapper(mTarget, conn, windowToken, windowType, 2864 isPreview, reqWidth, reqHeight, padding, displayId, which, info, 2865 description); 2866 synchronized (mActiveEngines) { 2867 mActiveEngines.put(windowToken, engineWrapper); 2868 } 2869 if (DEBUG) { 2870 Slog.v(TAG, "IWallpaperServiceWrapper Attaching window token " + windowToken); 2871 } 2872 Trace.endSection(); 2873 } 2874 2875 @Override detach(IBinder windowToken)2876 public void detach(IBinder windowToken) { 2877 IWallpaperEngineWrapper engineWrapper; 2878 synchronized (mActiveEngines) { 2879 engineWrapper = mActiveEngines.remove(windowToken); 2880 } 2881 if (engineWrapper == null) { 2882 Log.w(TAG, "Engine for window token " + windowToken + " already detached"); 2883 return; 2884 } 2885 if (DEBUG) { 2886 Slog.v(TAG, "IWallpaperServiceWrapper Detaching window token " + windowToken); 2887 } 2888 engineWrapper.destroy(); 2889 } 2890 } 2891 2892 @Override onCreate()2893 public void onCreate() { 2894 Trace.beginSection("WPMS.onCreate"); 2895 mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor"); 2896 mBackgroundThread.start(); 2897 mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); 2898 mIsWearOs = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); 2899 super.onCreate(); 2900 Trace.endSection(); 2901 } 2902 2903 @Override onDestroy()2904 public void onDestroy() { 2905 Trace.beginSection("WPMS.onDestroy"); 2906 super.onDestroy(); 2907 synchronized (mActiveEngines) { 2908 for (IWallpaperEngineWrapper engineWrapper : mActiveEngines.values()) { 2909 engineWrapper.destroy(); 2910 } 2911 mActiveEngines.clear(); 2912 } 2913 if (mBackgroundThread != null) { 2914 // onDestroy might be called without a previous onCreate if WallpaperService was 2915 // instantiated manually. While this is a misuse of the API, some things break 2916 // if here we don't take into consideration this scenario. 2917 mBackgroundThread.quitSafely(); 2918 } 2919 Trace.endSection(); 2920 } 2921 2922 /** 2923 * Implement to return the implementation of the internal accessibility 2924 * service interface. Subclasses should not override. 2925 */ 2926 @Override onBind(Intent intent)2927 public final IBinder onBind(Intent intent) { 2928 return new IWallpaperServiceWrapper(this); 2929 } 2930 2931 /** 2932 * Must be implemented to return a new instance of the wallpaper's engine. 2933 * Note that multiple instances may be active at the same time, such as 2934 * when the wallpaper is currently set as the active wallpaper and the user 2935 * is in the wallpaper picker viewing a preview of it as well. 2936 */ 2937 @MainThread onCreateEngine()2938 public abstract Engine onCreateEngine(); 2939 2940 /** 2941 * Creates a new engine instance to show the given content. See also {@link #onCreateEngine()}. 2942 * 2943 * @param description content to display 2944 * @return the rendering engine 2945 */ 2946 @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) 2947 @MainThread 2948 @Nullable onCreateEngine(@onNull WallpaperDescription description)2949 public Engine onCreateEngine(@NonNull WallpaperDescription description) { 2950 return onCreateEngine(); 2951 } 2952 2953 @Override dump(FileDescriptor fd, PrintWriter out, String[] args)2954 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 2955 out.print("State of wallpaper "); out.print(this); out.println(":"); 2956 synchronized (mActiveEngines) { 2957 for (IWallpaperEngineWrapper engineWrapper : mActiveEngines.values()) { 2958 Engine engine = engineWrapper.mEngine; 2959 if (engine == null) { 2960 Slog.w(TAG, "Engine for wrapper " + engineWrapper + " not attached"); 2961 continue; 2962 } 2963 out.print(" Engine "); 2964 out.print(engine); 2965 out.println(":"); 2966 engine.dump(" ", fd, out, args); 2967 } 2968 } 2969 } 2970 } 2971