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