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