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