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 android.annotation.FloatRange;
20 import android.annotation.Nullable;
21 import android.annotation.SdkConstant;
22 import android.annotation.SdkConstant.SdkConstantType;
23 import android.annotation.SystemApi;
24 import android.app.Service;
25 import android.app.WallpaperColors;
26 import android.app.WallpaperInfo;
27 import android.app.WallpaperManager;
28 import android.compat.annotation.UnsupportedAppUsage;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.res.TypedArray;
32 import android.graphics.Bitmap;
33 import android.graphics.Canvas;
34 import android.graphics.PixelFormat;
35 import android.graphics.Point;
36 import android.graphics.Rect;
37 import android.graphics.drawable.Drawable;
38 import android.hardware.display.DisplayManager;
39 import android.hardware.display.DisplayManager.DisplayListener;
40 import android.os.Build;
41 import android.os.Bundle;
42 import android.os.Handler;
43 import android.os.IBinder;
44 import android.os.Looper;
45 import android.os.Message;
46 import android.os.RemoteException;
47 import android.os.SystemClock;
48 import android.util.Log;
49 import android.util.MergedConfiguration;
50 import android.view.Display;
51 import android.view.DisplayCutout;
52 import android.view.Gravity;
53 import android.view.IWindowSession;
54 import android.view.InputChannel;
55 import android.view.InputDevice;
56 import android.view.InputEvent;
57 import android.view.InputEventReceiver;
58 import android.view.InsetsSourceControl;
59 import android.view.InsetsState;
60 import android.view.MotionEvent;
61 import android.view.SurfaceControl;
62 import android.view.SurfaceHolder;
63 import android.view.View;
64 import android.view.ViewGroup;
65 import android.view.WindowInsets;
66 import android.view.WindowManager;
67 import android.view.WindowManagerGlobal;
68 
69 import com.android.internal.annotations.VisibleForTesting;
70 import com.android.internal.os.HandlerCaller;
71 import com.android.internal.view.BaseIWindow;
72 import com.android.internal.view.BaseSurfaceHolder;
73 
74 import java.io.FileDescriptor;
75 import java.io.PrintWriter;
76 import java.util.ArrayList;
77 import java.util.concurrent.atomic.AtomicBoolean;
78 import java.util.function.Supplier;
79 
80 /**
81  * A wallpaper service is responsible for showing a live wallpaper behind
82  * applications that would like to sit on top of it.  This service object
83  * itself does very little -- its only purpose is to generate instances of
84  * {@link Engine} as needed.  Implementing a wallpaper thus
85  * involves subclassing from this, subclassing an Engine implementation,
86  * and implementing {@link #onCreateEngine()} to return a new instance of
87  * your engine.
88  */
89 public abstract class WallpaperService extends Service {
90     /**
91      * The {@link Intent} that must be declared as handled by the service.
92      * To be supported, the service must also require the
93      * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
94      * that other applications can not abuse it.
95      */
96     @SdkConstant(SdkConstantType.SERVICE_ACTION)
97     public static final String SERVICE_INTERFACE =
98             "android.service.wallpaper.WallpaperService";
99 
100     /**
101      * Name under which a WallpaperService component publishes information
102      * about itself.  This meta-data must reference an XML resource containing
103      * a <code>&lt;{@link android.R.styleable#Wallpaper wallpaper}&gt;</code>
104      * tag.
105      */
106     public static final String SERVICE_META_DATA = "android.service.wallpaper";
107 
108     static final String TAG = "WallpaperService";
109     static final boolean DEBUG = false;
110 
111     private static final int DO_ATTACH = 10;
112     private static final int DO_DETACH = 20;
113     private static final int DO_SET_DESIRED_SIZE = 30;
114     private static final int DO_SET_DISPLAY_PADDING = 40;
115     private static final int DO_IN_AMBIENT_MODE = 50;
116 
117     private static final int MSG_UPDATE_SURFACE = 10000;
118     private static final int MSG_VISIBILITY_CHANGED = 10010;
119     private static final int MSG_WALLPAPER_OFFSETS = 10020;
120     private static final int MSG_WALLPAPER_COMMAND = 10025;
121     @UnsupportedAppUsage
122     private static final int MSG_WINDOW_RESIZED = 10030;
123     private static final int MSG_WINDOW_MOVED = 10035;
124     private static final int MSG_TOUCH_EVENT = 10040;
125     private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050;
126     private static final int MSG_SCALE = 10100;
127 
128     private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
129 
130     private final ArrayList<Engine> mActiveEngines
131             = new ArrayList<Engine>();
132 
133     static final class WallpaperCommand {
134         String action;
135         int x;
136         int y;
137         int z;
138         Bundle extras;
139         boolean sync;
140     }
141 
142     /**
143      * The actual implementation of a wallpaper.  A wallpaper service may
144      * have multiple instances running (for example as a real wallpaper
145      * and as a preview), each of which is represented by its own Engine
146      * instance.  You must implement {@link WallpaperService#onCreateEngine()}
147      * to return your concrete Engine implementation.
148      */
149     public class Engine {
150         IWallpaperEngineWrapper mIWallpaperEngine;
151 
152         // Copies from mIWallpaperEngine.
153         HandlerCaller mCaller;
154         IWallpaperConnection mConnection;
155         IBinder mWindowToken;
156 
157         boolean mInitializing = true;
158         boolean mVisible;
159         boolean mReportedVisible;
160         boolean mDestroyed;
161 
162         // Current window state.
163         boolean mCreated;
164         boolean mSurfaceCreated;
165         boolean mIsCreating;
166         boolean mDrawingAllowed;
167         boolean mOffsetsChanged;
168         boolean mFixedSizeAllowed;
169         int mWidth;
170         int mHeight;
171         int mFormat;
172         int mType;
173         int mCurWidth;
174         int mCurHeight;
175         float mZoom = 0f;
176         int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
177         int mWindowPrivateFlags =
178                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
179         int mCurWindowFlags = mWindowFlags;
180         int mCurWindowPrivateFlags = mWindowPrivateFlags;
181         final Rect mVisibleInsets = new Rect();
182         final Rect mWinFrame = new Rect();
183         final Rect mContentInsets = new Rect();
184         final Rect mStableInsets = new Rect();
185         final Rect mDispatchedContentInsets = new Rect();
186         final Rect mDispatchedStableInsets = new Rect();
187         final Rect mFinalSystemInsets = new Rect();
188         final Rect mFinalStableInsets = new Rect();
189         final Rect mBackdropFrame = new Rect();
190         final DisplayCutout.ParcelableWrapper mDisplayCutout =
191                 new DisplayCutout.ParcelableWrapper();
192         DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
193         final InsetsState mInsetsState = new InsetsState();
194         final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
195         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
196         private final Point mSurfaceSize = new Point();
197 
198         final WindowManager.LayoutParams mLayout
199                 = new WindowManager.LayoutParams();
200         IWindowSession mSession;
201 
202         final Object mLock = new Object();
203         boolean mOffsetMessageEnqueued;
204         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
205         float mPendingXOffset;
206         float mPendingYOffset;
207         float mPendingXOffsetStep;
208         float mPendingYOffsetStep;
209         boolean mPendingSync;
210         MotionEvent mPendingMove;
211         boolean mIsInAmbientMode;
212 
213         // Needed for throttling onComputeColors.
214         private long mLastColorInvalidation;
215         private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
216         private final Supplier<Long> mClockFunction;
217         private final Handler mHandler;
218 
219         private Display mDisplay;
220         private Context mDisplayContext;
221         private int mDisplayState;
222 
223         SurfaceControl mSurfaceControl = new SurfaceControl();
224 
225         // Unused relayout out-param
226         SurfaceControl mTmpSurfaceControl = new SurfaceControl();
227 
228         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
229             {
230                 mRequestedFormat = PixelFormat.RGBX_8888;
231             }
232 
233             @Override
234             public boolean onAllowLockCanvas() {
235                 return mDrawingAllowed;
236             }
237 
238             @Override
239             public void onRelayoutContainer() {
240                 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
241                 mCaller.sendMessage(msg);
242             }
243 
244             @Override
245             public void onUpdateSurface() {
246                 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
247                 mCaller.sendMessage(msg);
248             }
249 
250             public boolean isCreating() {
251                 return mIsCreating;
252             }
253 
254             @Override
255             public void setFixedSize(int width, int height) {
256                 if (!mFixedSizeAllowed) {
257                     // Regular apps can't do this.  It can only work for
258                     // certain designs of window animations, so you can't
259                     // rely on it.
260                     throw new UnsupportedOperationException(
261                             "Wallpapers currently only support sizing from layout");
262                 }
263                 super.setFixedSize(width, height);
264             }
265 
266             public void setKeepScreenOn(boolean screenOn) {
267                 throw new UnsupportedOperationException(
268                         "Wallpapers do not support keep screen on");
269             }
270 
271             private void prepareToDraw() {
272                 if (mDisplayState == Display.STATE_DOZE
273                         || mDisplayState == Display.STATE_DOZE_SUSPEND) {
274                     try {
275                         mSession.pokeDrawLock(mWindow);
276                     } catch (RemoteException e) {
277                         // System server died, can be ignored.
278                     }
279                 }
280             }
281 
282             @Override
283             public Canvas lockCanvas() {
284                 prepareToDraw();
285                 return super.lockCanvas();
286             }
287 
288             @Override
289             public Canvas lockCanvas(Rect dirty) {
290                 prepareToDraw();
291                 return super.lockCanvas(dirty);
292             }
293 
294             @Override
295             public Canvas lockHardwareCanvas() {
296                 prepareToDraw();
297                 return super.lockHardwareCanvas();
298             }
299         };
300 
301         final class WallpaperInputEventReceiver extends InputEventReceiver {
WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper)302             public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) {
303                 super(inputChannel, looper);
304             }
305 
306             @Override
onInputEvent(InputEvent event)307             public void onInputEvent(InputEvent event) {
308                 boolean handled = false;
309                 try {
310                     if (event instanceof MotionEvent
311                             && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
312                         MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event);
313                         dispatchPointer(dup);
314                         handled = true;
315                     }
316                 } finally {
317                     finishInputEvent(event, handled);
318                 }
319             }
320         }
321         WallpaperInputEventReceiver mInputEventReceiver;
322 
323         final BaseIWindow mWindow = new BaseIWindow() {
324             @Override
325             public void resized(Rect frame, Rect contentInsets,
326                     Rect visibleInsets, Rect stableInsets, boolean reportDraw,
327                     MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout,
328                     boolean alwaysConsumeSystemBars, int displayId,
329                     DisplayCutout.ParcelableWrapper displayCutout) {
330                 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
331                         reportDraw ? 1 : 0);
332                 mCaller.sendMessage(msg);
333             }
334 
335             @Override
336             public void moved(int newX, int newY) {
337                 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY);
338                 mCaller.sendMessage(msg);
339             }
340 
341             @Override
342             public void dispatchAppVisibility(boolean visible) {
343                 // We don't do this in preview mode; we'll let the preview
344                 // activity tell us when to run.
345                 if (!mIWallpaperEngine.mIsPreview) {
346                     Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
347                             visible ? 1 : 0);
348                     mCaller.sendMessage(msg);
349                 }
350             }
351 
352             @Override
353             public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
354                     float zoom, boolean sync) {
355                 synchronized (mLock) {
356                     if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
357                     mPendingXOffset = x;
358                     mPendingYOffset = y;
359                     mPendingXOffsetStep = xStep;
360                     mPendingYOffsetStep = yStep;
361                     if (sync) {
362                         mPendingSync = true;
363                     }
364                     if (!mOffsetMessageEnqueued) {
365                         mOffsetMessageEnqueued = true;
366                         Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
367                         mCaller.sendMessage(msg);
368                     }
369                     Message msg = mCaller.obtainMessageI(MSG_SCALE, Float.floatToIntBits(zoom));
370                     mCaller.sendMessage(msg);
371                 }
372             }
373 
374             @Override
375             public void dispatchWallpaperCommand(String action, int x, int y,
376                     int z, Bundle extras, boolean sync) {
377                 synchronized (mLock) {
378                     if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y);
379                     WallpaperCommand cmd = new WallpaperCommand();
380                     cmd.action = action;
381                     cmd.x = x;
382                     cmd.y = y;
383                     cmd.z = z;
384                     cmd.extras = extras;
385                     cmd.sync = sync;
386                     Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND);
387                     msg.obj = cmd;
388                     mCaller.sendMessage(msg);
389                 }
390             }
391         };
392 
393         /**
394          * Default constructor
395          */
Engine()396         public Engine() {
397             this(SystemClock::elapsedRealtime, Handler.getMain());
398         }
399 
400         /**
401          * Constructor used for test purposes.
402          *
403          * @param clockFunction Supplies current times in millis.
404          * @param handler Used for posting/deferring asynchronous calls.
405          * @hide
406          */
407         @VisibleForTesting
Engine(Supplier<Long> clockFunction, Handler handler)408         public Engine(Supplier<Long> clockFunction, Handler handler) {
409            mClockFunction = clockFunction;
410            mHandler = handler;
411         }
412 
413         /**
414          * Provides access to the surface in which this wallpaper is drawn.
415          */
getSurfaceHolder()416         public SurfaceHolder getSurfaceHolder() {
417             return mSurfaceHolder;
418         }
419 
420         /**
421          * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
422          * WallpaperManager.getDesiredMinimumWidth()}, returning the width
423          * that the system would like this wallpaper to run in.
424          */
getDesiredMinimumWidth()425         public int getDesiredMinimumWidth() {
426             return mIWallpaperEngine.mReqWidth;
427         }
428 
429         /**
430          * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
431          * WallpaperManager.getDesiredMinimumHeight()}, returning the height
432          * that the system would like this wallpaper to run in.
433          */
getDesiredMinimumHeight()434         public int getDesiredMinimumHeight() {
435             return mIWallpaperEngine.mReqHeight;
436         }
437 
438         /**
439          * Return whether the wallpaper is currently visible to the user,
440          * this is the last value supplied to
441          * {@link #onVisibilityChanged(boolean)}.
442          */
isVisible()443         public boolean isVisible() {
444             return mReportedVisible;
445         }
446 
447         /**
448          * Returns true if this engine is running in preview mode -- that is,
449          * it is being shown to the user before they select it as the actual
450          * wallpaper.
451          */
isPreview()452         public boolean isPreview() {
453             return mIWallpaperEngine.mIsPreview;
454         }
455 
456         /**
457          * Returns true if this engine is running in ambient mode -- that is,
458          * it is being shown in low power mode, on always on display.
459          * @hide
460          */
461         @SystemApi
isInAmbientMode()462         public boolean isInAmbientMode() {
463             return mIsInAmbientMode;
464         }
465 
466         /**
467          * This will be called when the wallpaper is first started. If true is returned, the system
468          * will zoom in the wallpaper by default and zoom it out as the user interacts,
469          * to create depth. Otherwise, zoom will have to be handled manually
470          * in {@link #onZoomChanged(float)}.
471          *
472          * @hide
473          */
shouldZoomOutWallpaper()474         public boolean shouldZoomOutWallpaper() {
475             return false;
476         }
477 
478         /**
479          * Control whether this wallpaper will receive raw touch events
480          * from the window manager as the user interacts with the window
481          * that is currently displaying the wallpaper.  By default they
482          * are turned off.  If enabled, the events will be received in
483          * {@link #onTouchEvent(MotionEvent)}.
484          */
setTouchEventsEnabled(boolean enabled)485         public void setTouchEventsEnabled(boolean enabled) {
486             mWindowFlags = enabled
487                     ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
488                     : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
489             if (mCreated) {
490                 updateSurface(false, false, false);
491             }
492         }
493 
494         /**
495          * Control whether this wallpaper will receive notifications when the wallpaper
496          * has been scrolled. By default, wallpapers will receive notifications, although
497          * the default static image wallpapers do not. It is a performance optimization to
498          * set this to false.
499          *
500          * @param enabled whether the wallpaper wants to receive offset notifications
501          */
setOffsetNotificationsEnabled(boolean enabled)502         public void setOffsetNotificationsEnabled(boolean enabled) {
503             mWindowPrivateFlags = enabled
504                     ? (mWindowPrivateFlags |
505                         WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS)
506                     : (mWindowPrivateFlags &
507                         ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS);
508             if (mCreated) {
509                 updateSurface(false, false, false);
510             }
511         }
512 
513         /** {@hide} */
514         @UnsupportedAppUsage
setFixedSizeAllowed(boolean allowed)515         public void setFixedSizeAllowed(boolean allowed) {
516             mFixedSizeAllowed = allowed;
517         }
518 
519         /**
520          * Returns the current scale of the surface
521          * @hide
522          */
523         @VisibleForTesting
getZoom()524         public float getZoom() {
525             return mZoom;
526         }
527 
528         /**
529          * Called once to initialize the engine.  After returning, the
530          * engine's surface will be created by the framework.
531          */
onCreate(SurfaceHolder surfaceHolder)532         public void onCreate(SurfaceHolder surfaceHolder) {
533         }
534 
535         /**
536          * Called right before the engine is going away.  After this the
537          * surface will be destroyed and this Engine object is no longer
538          * valid.
539          */
onDestroy()540         public void onDestroy() {
541         }
542 
543         /**
544          * Called to inform you of the wallpaper becoming visible or
545          * hidden.  <em>It is very important that a wallpaper only use
546          * CPU while it is visible.</em>.
547          */
onVisibilityChanged(boolean visible)548         public void onVisibilityChanged(boolean visible) {
549         }
550 
551         /**
552          * Called with the current insets that are in effect for the wallpaper.
553          * This gives you the part of the overall wallpaper surface that will
554          * generally be visible to the user (ignoring position offsets applied to it).
555          *
556          * @param insets Insets to apply.
557          */
onApplyWindowInsets(WindowInsets insets)558         public void onApplyWindowInsets(WindowInsets insets) {
559         }
560 
561         /**
562          * Called as the user performs touch-screen interaction with the
563          * window that is currently showing this wallpaper.  Note that the
564          * events you receive here are driven by the actual application the
565          * user is interacting with, so if it is slow you will get fewer
566          * move events.
567          */
onTouchEvent(MotionEvent event)568         public void onTouchEvent(MotionEvent event) {
569         }
570 
571         /**
572          * Called to inform you of the wallpaper's offsets changing
573          * within its contain, corresponding to the container's
574          * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
575          * WallpaperManager.setWallpaperOffsets()}.
576          */
onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset)577         public void onOffsetsChanged(float xOffset, float yOffset,
578                 float xOffsetStep, float yOffsetStep,
579                 int xPixelOffset, int yPixelOffset) {
580         }
581 
582         /**
583          * Process a command that was sent to the wallpaper with
584          * {@link WallpaperManager#sendWallpaperCommand}.
585          * The default implementation does nothing, and always returns null
586          * as the result.
587          *
588          * @param action The name of the command to perform.  This tells you
589          * what to do and how to interpret the rest of the arguments.
590          * @param x Generic integer parameter.
591          * @param y Generic integer parameter.
592          * @param z Generic integer parameter.
593          * @param extras Any additional parameters.
594          * @param resultRequested If true, the caller is requesting that
595          * a result, appropriate for the command, be returned back.
596          * @return If returning a result, create a Bundle and place the
597          * result data in to it.  Otherwise return null.
598          */
onCommand(String action, int x, int y, int z, Bundle extras, boolean resultRequested)599         public Bundle onCommand(String action, int x, int y, int z,
600                 Bundle extras, boolean resultRequested) {
601             return null;
602         }
603 
604         /**
605          * Called when the device enters or exits ambient mode.
606          *
607          * @param inAmbientMode {@code true} if in ambient mode.
608          * @param animationDuration How long the transition animation to change the ambient state
609          *                          should run, in milliseconds. If 0 is passed as the argument
610          *                          here, the state should be switched immediately.
611          *
612          * @see #isInAmbientMode()
613          * @see WallpaperInfo#supportsAmbientMode()
614          * @hide
615          */
616         @SystemApi
onAmbientModeChanged(boolean inAmbientMode, long animationDuration)617         public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
618         }
619 
620         /**
621          * Called when an application has changed the desired virtual size of
622          * the wallpaper.
623          */
onDesiredSizeChanged(int desiredWidth, int desiredHeight)624         public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
625         }
626 
627         /**
628          * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
629          * SurfaceHolder.Callback.surfaceChanged()}.
630          */
onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)631         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
632         }
633 
634         /**
635          * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded
636          * SurfaceHolder.Callback.surfaceRedrawNeeded()}.
637          */
onSurfaceRedrawNeeded(SurfaceHolder holder)638         public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
639         }
640 
641         /**
642          * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
643          * SurfaceHolder.Callback.surfaceCreated()}.
644          */
onSurfaceCreated(SurfaceHolder holder)645         public void onSurfaceCreated(SurfaceHolder holder) {
646         }
647 
648         /**
649          * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
650          * SurfaceHolder.Callback.surfaceDestroyed()}.
651          */
onSurfaceDestroyed(SurfaceHolder holder)652         public void onSurfaceDestroyed(SurfaceHolder holder) {
653         }
654 
655         /**
656          * Called when the zoom level of the wallpaper changed.
657          * This method will be called with the initial zoom level when the surface is created.
658          *
659          * @param zoom the zoom level, between 0 indicating fully zoomed in and 1 indicating fully
660          *             zoomed out.
661          */
onZoomChanged(@loatRangefrom = 0f, to = 1f) float zoom)662         public void onZoomChanged(@FloatRange(from = 0f, to = 1f) float zoom) {
663         }
664 
665         /**
666          * Notifies the engine that wallpaper colors changed significantly.
667          * This will trigger a {@link #onComputeColors()} call.
668          */
notifyColorsChanged()669         public void notifyColorsChanged() {
670             final long now = mClockFunction.get();
671             if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) {
672                 Log.w(TAG, "This call has been deferred. You should only call "
673                         + "notifyColorsChanged() once every "
674                         + (NOTIFY_COLORS_RATE_LIMIT_MS / 1000f) + " seconds.");
675                 if (!mHandler.hasCallbacks(mNotifyColorsChanged)) {
676                     mHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS);
677                 }
678                 return;
679             }
680             mLastColorInvalidation = now;
681             mHandler.removeCallbacks(mNotifyColorsChanged);
682 
683             try {
684                 final WallpaperColors newColors = onComputeColors();
685                 if (mConnection != null) {
686                     mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId());
687                 } else {
688                     Log.w(TAG, "Can't notify system because wallpaper connection "
689                             + "was not established.");
690                 }
691             } catch (RemoteException e) {
692                 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
693             }
694         }
695 
696         /**
697          * Called by the system when it needs to know what colors the wallpaper is using.
698          * You might return null if no color information is available at the moment.
699          * In that case you might want to call {@link #notifyColorsChanged()} when
700          * color information becomes available.
701          * <p>
702          * The simplest way of creating a {@link android.app.WallpaperColors} object is by using
703          * {@link android.app.WallpaperColors#fromBitmap(Bitmap)} or
704          * {@link android.app.WallpaperColors#fromDrawable(Drawable)}, but you can also specify
705          * your main colors by constructing a {@link android.app.WallpaperColors} object manually.
706          *
707          * @return Wallpaper colors.
708          */
onComputeColors()709         public @Nullable WallpaperColors onComputeColors() {
710             return null;
711         }
712 
713         /**
714          * Sets internal engine state. Only for testing.
715          * @param created {@code true} or {@code false}.
716          * @hide
717          */
718         @VisibleForTesting
setCreated(boolean created)719         public void setCreated(boolean created) {
720             mCreated = created;
721         }
722 
dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args)723         protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
724             out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
725                     out.print(" mDestroyed="); out.println(mDestroyed);
726             out.print(prefix); out.print("mVisible="); out.print(mVisible);
727                     out.print(" mReportedVisible="); out.println(mReportedVisible);
728             out.print(prefix); out.print("mDisplay="); out.println(mDisplay);
729             out.print(prefix); out.print("mCreated="); out.print(mCreated);
730                     out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
731                     out.print(" mIsCreating="); out.print(mIsCreating);
732                     out.print(" mDrawingAllowed="); out.println(mDrawingAllowed);
733             out.print(prefix); out.print("mWidth="); out.print(mWidth);
734                     out.print(" mCurWidth="); out.print(mCurWidth);
735                     out.print(" mHeight="); out.print(mHeight);
736                     out.print(" mCurHeight="); out.println(mCurHeight);
737             out.print(prefix); out.print("mType="); out.print(mType);
738                     out.print(" mWindowFlags="); out.print(mWindowFlags);
739                     out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
740             out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
741                     out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags);
742             out.print(prefix); out.print("mVisibleInsets=");
743                     out.print(mVisibleInsets.toShortString());
744                     out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
745                     out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
746             out.print(prefix); out.print("mConfiguration=");
747                     out.println(mMergedConfiguration.getMergedConfiguration());
748             out.print(prefix); out.print("mLayout="); out.println(mLayout);
749             out.print(prefix); out.print("mZoom="); out.println(mZoom);
750             synchronized (mLock) {
751                 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
752                         out.print(" mPendingXOffset="); out.println(mPendingXOffset);
753                 out.print(prefix); out.print("mPendingXOffsetStep=");
754                         out.print(mPendingXOffsetStep);
755                         out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep);
756                 out.print(prefix); out.print("mOffsetMessageEnqueued=");
757                         out.print(mOffsetMessageEnqueued);
758                         out.print(" mPendingSync="); out.println(mPendingSync);
759                 if (mPendingMove != null) {
760                     out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove);
761                 }
762             }
763         }
764 
765         /**
766          * Set the wallpaper zoom to the given value. This value will be ignored when in ambient
767          * mode (and zoom will be reset to 0).
768          * @hide
769          * @param zoom between 0 and 1 (inclusive) indicating fully zoomed in to fully zoomed out
770          *              respectively.
771          */
772         @VisibleForTesting
setZoom(float zoom)773         public void setZoom(float zoom) {
774             if (DEBUG) {
775                 Log.v(TAG, "set zoom received: " + zoom);
776             }
777             boolean updated = false;
778             synchronized (mLock) {
779                 if (DEBUG) {
780                     Log.v(TAG, "mZoom: " + mZoom + " updated: " + zoom);
781                 }
782                 if (mIsInAmbientMode) {
783                     mZoom = 0;
784                 }
785                 if (Float.compare(zoom, mZoom) != 0) {
786                     mZoom = zoom;
787                     updated = true;
788                 }
789             }
790             if (DEBUG) Log.v(TAG, "setZoom updated? " + updated);
791             if (updated && !mDestroyed) {
792                 onZoomChanged(mZoom);
793             }
794         }
795 
dispatchPointer(MotionEvent event)796         private void dispatchPointer(MotionEvent event) {
797             if (event.isTouchEvent()) {
798                 synchronized (mLock) {
799                     if (event.getAction() == MotionEvent.ACTION_MOVE) {
800                         mPendingMove = event;
801                     } else {
802                         mPendingMove = null;
803                     }
804                 }
805                 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
806                 mCaller.sendMessage(msg);
807             } else {
808                 event.recycle();
809             }
810         }
811 
updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded)812         void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
813             if (mDestroyed) {
814                 Log.w(TAG, "Ignoring updateSurface: destroyed");
815             }
816 
817             boolean fixedSize = false;
818             int myWidth = mSurfaceHolder.getRequestedWidth();
819             if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
820             else fixedSize = true;
821             int myHeight = mSurfaceHolder.getRequestedHeight();
822             if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
823             else fixedSize = true;
824 
825             final boolean creating = !mCreated;
826             final boolean surfaceCreating = !mSurfaceCreated;
827             final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
828             boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
829             boolean insetsChanged = !mCreated;
830             final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
831             final boolean flagsChanged = mCurWindowFlags != mWindowFlags ||
832                     mCurWindowPrivateFlags != mWindowPrivateFlags;
833             if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
834                     || typeChanged || flagsChanged || redrawNeeded
835                     || !mIWallpaperEngine.mShownReported) {
836 
837                 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
838                         + " format=" + formatChanged + " size=" + sizeChanged);
839 
840                 try {
841                     mWidth = myWidth;
842                     mHeight = myHeight;
843                     mFormat = mSurfaceHolder.getRequestedFormat();
844                     mType = mSurfaceHolder.getRequestedType();
845 
846                     mLayout.x = 0;
847                     mLayout.y = 0;
848 
849                     mLayout.width = myWidth;
850                     mLayout.height = myHeight;
851                     mLayout.format = mFormat;
852 
853                     mCurWindowFlags = mWindowFlags;
854                     mLayout.flags = mWindowFlags
855                             | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
856                             | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
857                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
858                             | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
859                     mCurWindowPrivateFlags = mWindowPrivateFlags;
860                     mLayout.privateFlags = mWindowPrivateFlags;
861 
862                     mLayout.memoryType = mType;
863                     mLayout.token = mWindowToken;
864 
865                     if (!mCreated) {
866                         // Retrieve watch round info
867                         TypedArray windowStyle = obtainStyledAttributes(
868                                 com.android.internal.R.styleable.Window);
869                         windowStyle.recycle();
870 
871                         // Add window
872                         mLayout.type = mIWallpaperEngine.mWindowType;
873                         mLayout.gravity = Gravity.START|Gravity.TOP;
874                         mLayout.setFitInsetsTypes(0 /* types */);
875                         mLayout.setTitle(WallpaperService.this.getClass().getName());
876                         mLayout.windowAnimations =
877                                 com.android.internal.R.style.Animation_Wallpaper;
878                         InputChannel inputChannel = new InputChannel();
879 
880                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
881                                 mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
882                                 mDisplayCutout, inputChannel,
883                                 mInsetsState, mTempControls) < 0) {
884                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
885                             return;
886                         }
887                         mSession.setShouldZoomOutWallpaper(mWindow, shouldZoomOutWallpaper());
888                         mCreated = true;
889 
890                         mInputEventReceiver = new WallpaperInputEventReceiver(
891                                 inputChannel, Looper.myLooper());
892                     }
893 
894                     mSurfaceHolder.mSurfaceLock.lock();
895                     mDrawingAllowed = true;
896 
897                     if (!fixedSize) {
898                         mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding);
899                     } else {
900                         mLayout.surfaceInsets.set(0, 0, 0, 0);
901                     }
902 
903                     final int relayoutResult = mSession.relayout(
904                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
905                             View.VISIBLE, 0, -1, mWinFrame, mContentInsets,
906                             mVisibleInsets, mStableInsets, mBackdropFrame,
907                             mDisplayCutout, mMergedConfiguration, mSurfaceControl,
908                             mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl);
909                     if (mSurfaceControl.isValid()) {
910                         mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
911                         mSurfaceControl.release();
912                     }
913 
914                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
915                             + ", frame=" + mWinFrame);
916 
917                     int w = mWinFrame.width();
918                     int h = mWinFrame.height();
919 
920                     if (!fixedSize) {
921                         final Rect padding = mIWallpaperEngine.mDisplayPadding;
922                         w += padding.left + padding.right;
923                         h += padding.top + padding.bottom;
924                         mContentInsets.left += padding.left;
925                         mContentInsets.top += padding.top;
926                         mContentInsets.right += padding.right;
927                         mContentInsets.bottom += padding.bottom;
928                         mStableInsets.left += padding.left;
929                         mStableInsets.top += padding.top;
930                         mStableInsets.right += padding.right;
931                         mStableInsets.bottom += padding.bottom;
932                         mDisplayCutout.set(mDisplayCutout.get().inset(-padding.left, -padding.top,
933                                 -padding.right, -padding.bottom));
934                     } else {
935                         w = myWidth;
936                         h = myHeight;
937                     }
938 
939                     if (mCurWidth != w) {
940                         sizeChanged = true;
941                         mCurWidth = w;
942                     }
943                     if (mCurHeight != h) {
944                         sizeChanged = true;
945                         mCurHeight = h;
946                     }
947 
948                     if (DEBUG) {
949                         Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight);
950                     }
951 
952                     insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
953                     insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);
954                     insetsChanged |= !mDispatchedDisplayCutout.equals(mDisplayCutout.get());
955 
956                     mSurfaceHolder.setSurfaceFrameSize(w, h);
957                     mSurfaceHolder.mSurfaceLock.unlock();
958 
959                     if (!mSurfaceHolder.mSurface.isValid()) {
960                         reportSurfaceDestroyed();
961                         if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
962                         return;
963                     }
964 
965                     boolean didSurface = false;
966 
967                     try {
968                         mSurfaceHolder.ungetCallbacks();
969 
970                         if (surfaceCreating) {
971                             mIsCreating = true;
972                             didSurface = true;
973                             if (DEBUG) Log.v(TAG, "onSurfaceCreated("
974                                     + mSurfaceHolder + "): " + this);
975                             onSurfaceCreated(mSurfaceHolder);
976                             SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
977                             if (callbacks != null) {
978                                 for (SurfaceHolder.Callback c : callbacks) {
979                                     c.surfaceCreated(mSurfaceHolder);
980                                 }
981                             }
982                         }
983 
984                         redrawNeeded |= creating || (relayoutResult
985                                 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
986 
987                         if (forceReport || creating || surfaceCreating
988                                 || formatChanged || sizeChanged) {
989                             if (DEBUG) {
990                                 RuntimeException e = new RuntimeException();
991                                 e.fillInStackTrace();
992                                 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
993                                         + " formatChanged=" + formatChanged
994                                         + " sizeChanged=" + sizeChanged, e);
995                             }
996                             if (DEBUG) Log.v(TAG, "onSurfaceChanged("
997                                     + mSurfaceHolder + ", " + mFormat
998                                     + ", " + mCurWidth + ", " + mCurHeight
999                                     + "): " + this);
1000                             didSurface = true;
1001                             onSurfaceChanged(mSurfaceHolder, mFormat,
1002                                     mCurWidth, mCurHeight);
1003                             SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
1004                             if (callbacks != null) {
1005                                 for (SurfaceHolder.Callback c : callbacks) {
1006                                     c.surfaceChanged(mSurfaceHolder, mFormat,
1007                                             mCurWidth, mCurHeight);
1008                                 }
1009                             }
1010                         }
1011 
1012                         if (insetsChanged) {
1013                             mDispatchedContentInsets.set(mContentInsets);
1014                             mDispatchedStableInsets.set(mStableInsets);
1015                             mDispatchedDisplayCutout = mDisplayCutout.get();
1016                             mFinalStableInsets.set(mDispatchedStableInsets);
1017                             WindowInsets insets = new WindowInsets(mFinalSystemInsets,
1018                                     mFinalStableInsets,
1019                                     getResources().getConfiguration().isScreenRound(), false,
1020                                     mDispatchedDisplayCutout);
1021                             if (DEBUG) {
1022                                 Log.v(TAG, "dispatching insets=" + insets);
1023                             }
1024                             onApplyWindowInsets(insets);
1025                         }
1026 
1027                         if (redrawNeeded) {
1028                             onSurfaceRedrawNeeded(mSurfaceHolder);
1029                             SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
1030                             if (callbacks != null) {
1031                                 for (SurfaceHolder.Callback c : callbacks) {
1032                                     if (c instanceof SurfaceHolder.Callback2) {
1033                                         ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
1034                                                 mSurfaceHolder);
1035                                     }
1036                                 }
1037                             }
1038                         }
1039 
1040                         if (didSurface && !mReportedVisible) {
1041                             // This wallpaper is currently invisible, but its
1042                             // surface has changed.  At this point let's tell it
1043                             // again that it is invisible in case the report about
1044                             // the surface caused it to start running.  We really
1045                             // don't want wallpapers running when not visible.
1046                             if (mIsCreating) {
1047                                 // Some wallpapers will ignore this call if they
1048                                 // had previously been told they were invisble,
1049                                 // so if we are creating a new surface then toggle
1050                                 // the state to get them to notice.
1051                                 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
1052                                         + this);
1053                                 onVisibilityChanged(true);
1054                             }
1055                             if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
1056                                         + this);
1057                             onVisibilityChanged(false);
1058                         }
1059 
1060                     } finally {
1061                         mIsCreating = false;
1062                         mSurfaceCreated = true;
1063                         if (redrawNeeded) {
1064                             mSession.finishDrawing(mWindow, null /* postDrawTransaction */);
1065                         }
1066                         mIWallpaperEngine.reportShown();
1067                     }
1068                 } catch (RemoteException ex) {
1069                 }
1070                 if (DEBUG) Log.v(
1071                     TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
1072                     " w=" + mLayout.width + " h=" + mLayout.height);
1073             }
1074         }
1075 
attach(IWallpaperEngineWrapper wrapper)1076         void attach(IWallpaperEngineWrapper wrapper) {
1077             if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
1078             if (mDestroyed) {
1079                 return;
1080             }
1081 
1082             mIWallpaperEngine = wrapper;
1083             mCaller = wrapper.mCaller;
1084             mConnection = wrapper.mConnection;
1085             mWindowToken = wrapper.mWindowToken;
1086             mSurfaceHolder.setSizeFromLayout();
1087             mInitializing = true;
1088             mSession = WindowManagerGlobal.getWindowSession();
1089 
1090             mWindow.setSession(mSession);
1091 
1092             mLayout.packageName = getPackageName();
1093             mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
1094                     mCaller.getHandler());
1095             mDisplay = mIWallpaperEngine.mDisplay;
1096             mDisplayContext = createDisplayContext(mDisplay);
1097             mDisplayState = mDisplay.getState();
1098 
1099             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
1100             onCreate(mSurfaceHolder);
1101 
1102             mInitializing = false;
1103 
1104             mReportedVisible = false;
1105             updateSurface(false, false, false);
1106         }
1107 
1108         /**
1109          * The {@link Context} with resources that match the current display the wallpaper is on.
1110          * For multiple display environment, multiple engines can be created to render on each
1111          * display, but these displays may have different densities. Use this context to get the
1112          * corresponding resources for currently display, avoiding the context of the service.
1113          * <p>
1114          * The display context will never be {@code null} after
1115          * {@link Engine#onCreate(SurfaceHolder)} has been called.
1116          *
1117          * @return A {@link Context} for current display.
1118          */
1119         @Nullable
getDisplayContext()1120         public Context getDisplayContext() {
1121             return mDisplayContext;
1122         }
1123 
1124         /**
1125          * Executes life cycle event and updates internal ambient mode state based on
1126          * message sent from handler.
1127          *
1128          * @param inAmbientMode {@code true} if in ambient mode.
1129          * @param animationDuration For how long the transition will last, in ms.
1130          * @hide
1131          */
1132         @VisibleForTesting
doAmbientModeChanged(boolean inAmbientMode, long animationDuration)1133         public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
1134             if (!mDestroyed) {
1135                 if (DEBUG) {
1136                     Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", "
1137                             + animationDuration + "): " + this);
1138                 }
1139                 mIsInAmbientMode = inAmbientMode;
1140                 if (mCreated) {
1141                     onAmbientModeChanged(inAmbientMode, animationDuration);
1142                 }
1143             }
1144         }
1145 
doDesiredSizeChanged(int desiredWidth, int desiredHeight)1146         void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
1147             if (!mDestroyed) {
1148                 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
1149                         + desiredWidth + "," + desiredHeight + "): " + this);
1150                 mIWallpaperEngine.mReqWidth = desiredWidth;
1151                 mIWallpaperEngine.mReqHeight = desiredHeight;
1152                 onDesiredSizeChanged(desiredWidth, desiredHeight);
1153                 doOffsetsChanged(true);
1154             }
1155         }
1156 
doDisplayPaddingChanged(Rect padding)1157         void doDisplayPaddingChanged(Rect padding) {
1158             if (!mDestroyed) {
1159                 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this);
1160                 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) {
1161                     mIWallpaperEngine.mDisplayPadding.set(padding);
1162                     updateSurface(true, false, false);
1163                 }
1164             }
1165         }
1166 
doVisibilityChanged(boolean visible)1167         void doVisibilityChanged(boolean visible) {
1168             if (!mDestroyed) {
1169                 mVisible = visible;
1170                 reportVisibility();
1171             }
1172         }
1173 
reportVisibility()1174         void reportVisibility() {
1175             if (!mDestroyed) {
1176                 mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState();
1177                 boolean visible = mVisible && mDisplayState != Display.STATE_OFF;
1178                 if (mReportedVisible != visible) {
1179                     mReportedVisible = visible;
1180                     if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
1181                             + "): " + this);
1182                     if (visible) {
1183                         // If becoming visible, in preview mode the surface
1184                         // may have been destroyed so now we need to make
1185                         // sure it is re-created.
1186                         doOffsetsChanged(false);
1187                         // force relayout to get new surface
1188                         updateSurface(true, false, false);
1189                     }
1190                     onVisibilityChanged(visible);
1191                 }
1192             }
1193         }
1194 
doOffsetsChanged(boolean always)1195         void doOffsetsChanged(boolean always) {
1196             if (mDestroyed) {
1197                 return;
1198             }
1199 
1200             if (!always && !mOffsetsChanged) {
1201                 return;
1202             }
1203 
1204             float xOffset;
1205             float yOffset;
1206             float xOffsetStep;
1207             float yOffsetStep;
1208             boolean sync;
1209             synchronized (mLock) {
1210                 xOffset = mPendingXOffset;
1211                 yOffset = mPendingYOffset;
1212                 xOffsetStep = mPendingXOffsetStep;
1213                 yOffsetStep = mPendingYOffsetStep;
1214                 sync = mPendingSync;
1215                 mPendingSync = false;
1216                 mOffsetMessageEnqueued = false;
1217             }
1218 
1219             if (mSurfaceCreated) {
1220                 if (mReportedVisible) {
1221                     if (DEBUG) Log.v(TAG, "Offsets change in " + this
1222                             + ": " + xOffset + "," + yOffset);
1223                     final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
1224                     final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
1225                     final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
1226                     final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
1227                     onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
1228                 } else {
1229                     mOffsetsChanged = true;
1230                 }
1231             }
1232 
1233             if (sync) {
1234                 try {
1235                     if (DEBUG) Log.v(TAG, "Reporting offsets change complete");
1236                     mSession.wallpaperOffsetsComplete(mWindow.asBinder());
1237                 } catch (RemoteException e) {
1238                 }
1239             }
1240         }
1241 
doCommand(WallpaperCommand cmd)1242         void doCommand(WallpaperCommand cmd) {
1243             Bundle result;
1244             if (!mDestroyed) {
1245                 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
1246                         cmd.extras, cmd.sync);
1247             } else {
1248                 result = null;
1249             }
1250             if (cmd.sync) {
1251                 try {
1252                     if (DEBUG) Log.v(TAG, "Reporting command complete");
1253                     mSession.wallpaperCommandComplete(mWindow.asBinder(), result);
1254                 } catch (RemoteException e) {
1255                 }
1256             }
1257         }
1258 
reportSurfaceDestroyed()1259         void reportSurfaceDestroyed() {
1260             if (mSurfaceCreated) {
1261                 mSurfaceCreated = false;
1262                 mSurfaceHolder.ungetCallbacks();
1263                 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
1264                 if (callbacks != null) {
1265                     for (SurfaceHolder.Callback c : callbacks) {
1266                         c.surfaceDestroyed(mSurfaceHolder);
1267                     }
1268                 }
1269                 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
1270                         + mSurfaceHolder + "): " + this);
1271                 onSurfaceDestroyed(mSurfaceHolder);
1272             }
1273         }
1274 
detach()1275         void detach() {
1276             if (mDestroyed) {
1277                 return;
1278             }
1279 
1280             mDestroyed = true;
1281 
1282             if (mIWallpaperEngine.mDisplayManager != null) {
1283                 mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener);
1284             }
1285 
1286             if (mVisible) {
1287                 mVisible = false;
1288                 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
1289                 onVisibilityChanged(false);
1290             }
1291 
1292             reportSurfaceDestroyed();
1293 
1294             if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
1295             onDestroy();
1296 
1297             if (mCreated) {
1298                 try {
1299                     if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
1300                             + mSurfaceHolder.getSurface() + " of: " + this);
1301 
1302                     if (mInputEventReceiver != null) {
1303                         mInputEventReceiver.dispose();
1304                         mInputEventReceiver = null;
1305                     }
1306 
1307                     mSession.remove(mWindow);
1308                 } catch (RemoteException e) {
1309                 }
1310                 mSurfaceHolder.mSurface.release();
1311                 mCreated = false;
1312             }
1313         }
1314 
1315         private final DisplayListener mDisplayListener = new DisplayListener() {
1316             @Override
1317             public void onDisplayChanged(int displayId) {
1318                 if (mDisplay.getDisplayId() == displayId) {
1319                     reportVisibility();
1320                 }
1321             }
1322 
1323             @Override
1324             public void onDisplayRemoved(int displayId) {
1325             }
1326 
1327             @Override
1328             public void onDisplayAdded(int displayId) {
1329             }
1330         };
1331     }
1332 
1333     class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
1334             implements HandlerCaller.Callback {
1335         private final HandlerCaller mCaller;
1336 
1337         final IWallpaperConnection mConnection;
1338         final IBinder mWindowToken;
1339         final int mWindowType;
1340         final boolean mIsPreview;
1341         boolean mShownReported;
1342         int mReqWidth;
1343         int mReqHeight;
1344         final Rect mDisplayPadding = new Rect();
1345         final int mDisplayId;
1346         final DisplayManager mDisplayManager;
1347         final Display mDisplay;
1348         private final AtomicBoolean mDetached = new AtomicBoolean();
1349 
1350         Engine mEngine;
1351 
IWallpaperEngineWrapper(WallpaperService context, IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId)1352         IWallpaperEngineWrapper(WallpaperService context,
1353                 IWallpaperConnection conn, IBinder windowToken,
1354                 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
1355                 int displayId) {
1356             mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
1357             mConnection = conn;
1358             mWindowToken = windowToken;
1359             mWindowType = windowType;
1360             mIsPreview = isPreview;
1361             mReqWidth = reqWidth;
1362             mReqHeight = reqHeight;
1363             mDisplayPadding.set(padding);
1364             mDisplayId = displayId;
1365 
1366             // Create a display context before onCreateEngine.
1367             mDisplayManager = getSystemService(DisplayManager.class);
1368             mDisplay = mDisplayManager.getDisplay(mDisplayId);
1369 
1370             if (mDisplay == null) {
1371                 // Ignore this engine.
1372                 throw new IllegalArgumentException("Cannot find display with id" + mDisplayId);
1373             }
1374             Message msg = mCaller.obtainMessage(DO_ATTACH);
1375             mCaller.sendMessage(msg);
1376         }
1377 
setDesiredSize(int width, int height)1378         public void setDesiredSize(int width, int height) {
1379             Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
1380             mCaller.sendMessage(msg);
1381         }
1382 
setDisplayPadding(Rect padding)1383         public void setDisplayPadding(Rect padding) {
1384             Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding);
1385             mCaller.sendMessage(msg);
1386         }
1387 
setVisibility(boolean visible)1388         public void setVisibility(boolean visible) {
1389             Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
1390                     visible ? 1 : 0);
1391             mCaller.sendMessage(msg);
1392         }
1393 
1394         @Override
setInAmbientMode(boolean inAmbientDisplay, long animationDuration)1395         public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration)
1396                 throws RemoteException {
1397             Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
1398                     animationDuration);
1399             mCaller.sendMessage(msg);
1400         }
1401 
dispatchPointer(MotionEvent event)1402         public void dispatchPointer(MotionEvent event) {
1403             if (mEngine != null) {
1404                 mEngine.dispatchPointer(event);
1405             } else {
1406                 event.recycle();
1407             }
1408         }
1409 
dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras)1410         public void dispatchWallpaperCommand(String action, int x, int y,
1411                 int z, Bundle extras) {
1412             if (mEngine != null) {
1413                 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false);
1414             }
1415         }
1416 
setZoomOut(float scale)1417         public void setZoomOut(float scale) {
1418             Message msg = mCaller.obtainMessageI(MSG_SCALE, Float.floatToIntBits(scale));
1419             mCaller.sendMessage(msg);
1420         }
1421 
reportShown()1422         public void reportShown() {
1423             if (!mShownReported) {
1424                 mShownReported = true;
1425                 try {
1426                     mConnection.engineShown(this);
1427                 } catch (RemoteException e) {
1428                     Log.w(TAG, "Wallpaper host disappeared", e);
1429                     return;
1430                 }
1431             }
1432         }
1433 
requestWallpaperColors()1434         public void requestWallpaperColors() {
1435             Message msg = mCaller.obtainMessage(MSG_REQUEST_WALLPAPER_COLORS);
1436             mCaller.sendMessage(msg);
1437         }
1438 
destroy()1439         public void destroy() {
1440             Message msg = mCaller.obtainMessage(DO_DETACH);
1441             mCaller.sendMessage(msg);
1442         }
1443 
detach()1444         public void detach() {
1445             mDetached.set(true);
1446         }
1447 
doDetachEngine()1448         private void doDetachEngine() {
1449             mActiveEngines.remove(mEngine);
1450             mEngine.detach();
1451         }
1452 
1453         @Override
executeMessage(Message message)1454         public void executeMessage(Message message) {
1455             if (mDetached.get()) {
1456                 if (mActiveEngines.contains(mEngine)) {
1457                     doDetachEngine();
1458                 }
1459                 return;
1460             }
1461             switch (message.what) {
1462                 case DO_ATTACH: {
1463                     try {
1464                         mConnection.attachEngine(this, mDisplayId);
1465                     } catch (RemoteException e) {
1466                         Log.w(TAG, "Wallpaper host disappeared", e);
1467                         return;
1468                     }
1469                     Engine engine = onCreateEngine();
1470                     mEngine = engine;
1471                     mActiveEngines.add(engine);
1472                     engine.attach(this);
1473                     return;
1474                 }
1475                 case DO_DETACH: {
1476                     doDetachEngine();
1477                     return;
1478                 }
1479                 case DO_SET_DESIRED_SIZE: {
1480                     mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
1481                     return;
1482                 }
1483                 case DO_SET_DISPLAY_PADDING: {
1484                     mEngine.doDisplayPaddingChanged((Rect) message.obj);
1485                     return;
1486                 }
1487                 case DO_IN_AMBIENT_MODE: {
1488                     mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj);
1489                     return;
1490                 }
1491                 case MSG_UPDATE_SURFACE:
1492                     mEngine.updateSurface(true, false, false);
1493                     break;
1494                 case MSG_SCALE:
1495                     mEngine.setZoom(Float.intBitsToFloat(message.arg1));
1496                     break;
1497                 case MSG_VISIBILITY_CHANGED:
1498                     if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
1499                             + ": " + message.arg1);
1500                     mEngine.doVisibilityChanged(message.arg1 != 0);
1501                     break;
1502                 case MSG_WALLPAPER_OFFSETS: {
1503                     mEngine.doOffsetsChanged(true);
1504                 } break;
1505                 case MSG_WALLPAPER_COMMAND: {
1506                     WallpaperCommand cmd = (WallpaperCommand)message.obj;
1507                     mEngine.doCommand(cmd);
1508                 } break;
1509                 case MSG_WINDOW_RESIZED: {
1510                     final boolean reportDraw = message.arg1 != 0;
1511                     mEngine.updateSurface(true, false, reportDraw);
1512                     mEngine.doOffsetsChanged(true);
1513                 } break;
1514                 case MSG_WINDOW_MOVED: {
1515                     // Do nothing. What does it mean for a Wallpaper to move?
1516                 } break;
1517                 case MSG_TOUCH_EVENT: {
1518                     boolean skip = false;
1519                     MotionEvent ev = (MotionEvent)message.obj;
1520                     if (ev.getAction() == MotionEvent.ACTION_MOVE) {
1521                         synchronized (mEngine.mLock) {
1522                             if (mEngine.mPendingMove == ev) {
1523                                 mEngine.mPendingMove = null;
1524                             } else {
1525                                 // this is not the motion event we are looking for....
1526                                 skip = true;
1527                             }
1528                         }
1529                     }
1530                     if (!skip) {
1531                         if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
1532                         mEngine.onTouchEvent(ev);
1533                     }
1534                     ev.recycle();
1535                 } break;
1536                 case MSG_REQUEST_WALLPAPER_COLORS: {
1537                     if (mConnection == null) {
1538                         break;
1539                     }
1540                     try {
1541                         mConnection.onWallpaperColorsChanged(mEngine.onComputeColors(), mDisplayId);
1542                     } catch (RemoteException e) {
1543                         // Connection went away, nothing to do in here.
1544                     }
1545                 } break;
1546                 default :
1547                     Log.w(TAG, "Unknown message type " + message.what);
1548             }
1549         }
1550     }
1551 
1552     /**
1553      * Implements the internal {@link IWallpaperService} interface to convert
1554      * incoming calls to it back to calls on an {@link WallpaperService}.
1555      */
1556     class IWallpaperServiceWrapper extends IWallpaperService.Stub {
1557         private final WallpaperService mTarget;
1558         private IWallpaperEngineWrapper mEngineWrapper;
1559 
IWallpaperServiceWrapper(WallpaperService context)1560         public IWallpaperServiceWrapper(WallpaperService context) {
1561             mTarget = context;
1562         }
1563 
1564         @Override
attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId)1565         public void attach(IWallpaperConnection conn, IBinder windowToken,
1566                 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
1567                 int displayId) {
1568             mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken,
1569                     windowType, isPreview, reqWidth, reqHeight, padding, displayId);
1570         }
1571 
1572         @Override
detach()1573         public void detach() {
1574             mEngineWrapper.detach();
1575         }
1576     }
1577 
1578     @Override
onCreate()1579     public void onCreate() {
1580         super.onCreate();
1581     }
1582 
1583     @Override
onDestroy()1584     public void onDestroy() {
1585         super.onDestroy();
1586         for (int i=0; i<mActiveEngines.size(); i++) {
1587             mActiveEngines.get(i).detach();
1588         }
1589         mActiveEngines.clear();
1590     }
1591 
1592     /**
1593      * Implement to return the implementation of the internal accessibility
1594      * service interface.  Subclasses should not override.
1595      */
1596     @Override
onBind(Intent intent)1597     public final IBinder onBind(Intent intent) {
1598         return new IWallpaperServiceWrapper(this);
1599     }
1600 
1601     /**
1602      * Must be implemented to return a new instance of the wallpaper's engine.
1603      * Note that multiple instances may be active at the same time, such as
1604      * when the wallpaper is currently set as the active wallpaper and the user
1605      * is in the wallpaper picker viewing a preview of it as well.
1606      */
onCreateEngine()1607     public abstract Engine onCreateEngine();
1608 
1609     @Override
dump(FileDescriptor fd, PrintWriter out, String[] args)1610     protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
1611         out.print("State of wallpaper "); out.print(this); out.println(":");
1612         for (int i=0; i<mActiveEngines.size(); i++) {
1613             Engine engine = mActiveEngines.get(i);
1614             out.print("  Engine "); out.print(engine); out.println(":");
1615             engine.dump("    ", fd, out, args);
1616         }
1617     }
1618 }
1619