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