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