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