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