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