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