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