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