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