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