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 com.android.internal.os.HandlerCaller;
     20 import com.android.internal.view.BaseIWindow;
     21 import com.android.internal.view.BaseSurfaceHolder;
     22 
     23 import android.annotation.SdkConstant;
     24 import android.annotation.SdkConstant.SdkConstantType;
     25 import android.app.Service;
     26 import android.app.WallpaperManager;
     27 import android.content.BroadcastReceiver;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.content.res.Configuration;
     32 import android.graphics.Rect;
     33 import android.os.Bundle;
     34 import android.os.IBinder;
     35 import android.os.Looper;
     36 import android.os.Message;
     37 import android.os.RemoteException;
     38 import android.util.Log;
     39 import android.util.LogPrinter;
     40 import android.view.Gravity;
     41 import android.view.IWindowSession;
     42 import android.view.MotionEvent;
     43 import android.view.SurfaceHolder;
     44 import android.view.View;
     45 import android.view.ViewGroup;
     46 import android.view.ViewRoot;
     47 import android.view.WindowManager;
     48 import android.view.WindowManagerImpl;
     49 
     50 import java.util.ArrayList;
     51 
     52 /**
     53  * A wallpaper service is responsible for showing a live wallpaper behind
     54  * applications that would like to sit on top of it.  This service object
     55  * itself does very little -- its only purpose is to generate instances of
     56  * {@link Engine} as needed.  Implementing a wallpaper thus
     57  * involves subclassing from this, subclassing an Engine implementation,
     58  * and implementing {@link #onCreateEngine()} to return a new instance of
     59  * your engine.
     60  */
     61 public abstract class WallpaperService extends Service {
     62     /**
     63      * The {@link Intent} that must be declared as handled by the service.
     64      * To be supported, the service must also require the
     65      * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
     66      * that other applications can not abuse it.
     67      */
     68     @SdkConstant(SdkConstantType.SERVICE_ACTION)
     69     public static final String SERVICE_INTERFACE =
     70             "android.service.wallpaper.WallpaperService";
     71 
     72     /**
     73      * Name under which a WallpaperService component publishes information
     74      * about itself.  This meta-data must reference an XML resource containing
     75      * a <code>&lt;{@link android.R.styleable#Wallpaper wallpaper}&gt;</code>
     76      * tag.
     77      */
     78     public static final String SERVICE_META_DATA = "android.service.wallpaper";
     79 
     80     static final String TAG = "WallpaperService";
     81     static final boolean DEBUG = false;
     82 
     83     private static final int DO_ATTACH = 10;
     84     private static final int DO_DETACH = 20;
     85     private static final int DO_SET_DESIRED_SIZE = 30;
     86 
     87     private static final int MSG_UPDATE_SURFACE = 10000;
     88     private static final int MSG_VISIBILITY_CHANGED = 10010;
     89     private static final int MSG_WALLPAPER_OFFSETS = 10020;
     90     private static final int MSG_WALLPAPER_COMMAND = 10025;
     91     private static final int MSG_WINDOW_RESIZED = 10030;
     92     private static final int MSG_TOUCH_EVENT = 10040;
     93 
     94     private Looper mCallbackLooper;
     95     private final ArrayList<Engine> mActiveEngines
     96             = new ArrayList<Engine>();
     97 
     98     static final class WallpaperCommand {
     99         String action;
    100         int x;
    101         int y;
    102         int z;
    103         Bundle extras;
    104         boolean sync;
    105     }
    106 
    107     /**
    108      * The actual implementation of a wallpaper.  A wallpaper service may
    109      * have multiple instances running (for example as a real wallpaper
    110      * and as a preview), each of which is represented by its own Engine
    111      * instance.  You must implement {@link WallpaperService#onCreateEngine()}
    112      * to return your concrete Engine implementation.
    113      */
    114     public class Engine {
    115         IWallpaperEngineWrapper mIWallpaperEngine;
    116 
    117         // Copies from mIWallpaperEngine.
    118         HandlerCaller mCaller;
    119         IWallpaperConnection mConnection;
    120         IBinder mWindowToken;
    121 
    122         boolean mInitializing = true;
    123         boolean mVisible;
    124         boolean mScreenOn = true;
    125         boolean mReportedVisible;
    126         boolean mDestroyed;
    127 
    128         // Current window state.
    129         boolean mCreated;
    130         boolean mSurfaceCreated;
    131         boolean mIsCreating;
    132         boolean mDrawingAllowed;
    133         int mWidth;
    134         int mHeight;
    135         int mFormat;
    136         int mType;
    137         int mCurWidth;
    138         int mCurHeight;
    139         int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
    140         int mCurWindowFlags = mWindowFlags;
    141         final Rect mVisibleInsets = new Rect();
    142         final Rect mWinFrame = new Rect();
    143         final Rect mContentInsets = new Rect();
    144         final Configuration mConfiguration = new Configuration();
    145 
    146         final WindowManager.LayoutParams mLayout
    147                 = new WindowManager.LayoutParams();
    148         IWindowSession mSession;
    149 
    150         final Object mLock = new Object();
    151         boolean mOffsetMessageEnqueued;
    152         float mPendingXOffset;
    153         float mPendingYOffset;
    154         float mPendingXOffsetStep;
    155         float mPendingYOffsetStep;
    156         boolean mPendingSync;
    157         MotionEvent mPendingMove;
    158 
    159         final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    160             @Override
    161             public void onReceive(Context context, Intent intent) {
    162                 if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
    163                     mScreenOn = true;
    164                     reportVisibility();
    165                 } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
    166                     mScreenOn = false;
    167                     reportVisibility();
    168                 }
    169             }
    170         };
    171 
    172         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
    173 
    174             @Override
    175             public boolean onAllowLockCanvas() {
    176                 return mDrawingAllowed;
    177             }
    178 
    179             @Override
    180             public void onRelayoutContainer() {
    181                 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
    182                 mCaller.sendMessage(msg);
    183             }
    184 
    185             @Override
    186             public void onUpdateSurface() {
    187                 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
    188                 mCaller.sendMessage(msg);
    189             }
    190 
    191             public boolean isCreating() {
    192                 return mIsCreating;
    193             }
    194 
    195             @Override
    196             public void setFixedSize(int width, int height) {
    197                 throw new UnsupportedOperationException(
    198                         "Wallpapers currently only support sizing from layout");
    199             }
    200 
    201             public void setKeepScreenOn(boolean screenOn) {
    202                 throw new UnsupportedOperationException(
    203                         "Wallpapers do not support keep screen on");
    204             }
    205 
    206         };
    207 
    208         final BaseIWindow mWindow = new BaseIWindow() {
    209             @Override
    210             public boolean onDispatchPointer(MotionEvent event, long eventTime,
    211                     boolean callWhenDone) {
    212                 synchronized (mLock) {
    213                     if (event.getAction() == MotionEvent.ACTION_MOVE) {
    214                         if (mPendingMove != null) {
    215                             mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
    216                             mPendingMove.recycle();
    217                         }
    218                         mPendingMove = event;
    219                     } else {
    220                         mPendingMove = null;
    221                     }
    222                     Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT,
    223                             event);
    224                     mCaller.sendMessage(msg);
    225                 }
    226                 return false;
    227             }
    228 
    229             @Override
    230             public void resized(int w, int h, Rect coveredInsets,
    231                     Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
    232                 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
    233                         reportDraw ? 1 : 0);
    234                 mCaller.sendMessage(msg);
    235             }
    236 
    237             @Override
    238             public void dispatchAppVisibility(boolean visible) {
    239                 // We don't do this in preview mode; we'll let the preview
    240                 // activity tell us when to run.
    241                 if (!mIWallpaperEngine.mIsPreview) {
    242                     Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
    243                             visible ? 1 : 0);
    244                     mCaller.sendMessage(msg);
    245                 }
    246             }
    247 
    248             @Override
    249             public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
    250                     boolean sync) {
    251                 synchronized (mLock) {
    252                     if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
    253                     mPendingXOffset = x;
    254                     mPendingYOffset = y;
    255                     mPendingXOffsetStep = xStep;
    256                     mPendingYOffsetStep = yStep;
    257                     if (sync) {
    258                         mPendingSync = true;
    259                     }
    260                     if (!mOffsetMessageEnqueued) {
    261                         mOffsetMessageEnqueued = true;
    262                         Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
    263                         mCaller.sendMessage(msg);
    264                     }
    265                 }
    266             }
    267 
    268             public void dispatchWallpaperCommand(String action, int x, int y,
    269                     int z, Bundle extras, boolean sync) {
    270                 synchronized (mLock) {
    271                     if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y);
    272                     WallpaperCommand cmd = new WallpaperCommand();
    273                     cmd.action = action;
    274                     cmd.x = x;
    275                     cmd.y = y;
    276                     cmd.z = z;
    277                     cmd.extras = extras;
    278                     cmd.sync = sync;
    279                     Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND);
    280                     msg.obj = cmd;
    281                     mCaller.sendMessage(msg);
    282                 }
    283             }
    284         };
    285 
    286         /**
    287          * Provides access to the surface in which this wallpaper is drawn.
    288          */
    289         public SurfaceHolder getSurfaceHolder() {
    290             return mSurfaceHolder;
    291         }
    292 
    293         /**
    294          * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
    295          * WallpaperManager.getDesiredMinimumWidth()}, returning the width
    296          * that the system would like this wallpaper to run in.
    297          */
    298         public int getDesiredMinimumWidth() {
    299             return mIWallpaperEngine.mReqWidth;
    300         }
    301 
    302         /**
    303          * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
    304          * WallpaperManager.getDesiredMinimumHeight()}, returning the height
    305          * that the system would like this wallpaper to run in.
    306          */
    307         public int getDesiredMinimumHeight() {
    308             return mIWallpaperEngine.mReqHeight;
    309         }
    310 
    311         /**
    312          * Return whether the wallpaper is currently visible to the user,
    313          * this is the last value supplied to
    314          * {@link #onVisibilityChanged(boolean)}.
    315          */
    316         public boolean isVisible() {
    317             return mReportedVisible;
    318         }
    319 
    320         /**
    321          * Returns true if this engine is running in preview mode -- that is,
    322          * it is being shown to the user before they select it as the actual
    323          * wallpaper.
    324          */
    325         public boolean isPreview() {
    326             return mIWallpaperEngine.mIsPreview;
    327         }
    328 
    329         /**
    330          * Control whether this wallpaper will receive raw touch events
    331          * from the window manager as the user interacts with the window
    332          * that is currently displaying the wallpaper.  By default they
    333          * are turned off.  If enabled, the events will be received in
    334          * {@link #onTouchEvent(MotionEvent)}.
    335          */
    336         public void setTouchEventsEnabled(boolean enabled) {
    337             mWindowFlags = enabled
    338                     ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
    339                     : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
    340             if (mCreated) {
    341                 updateSurface(false, false);
    342             }
    343         }
    344 
    345         /**
    346          * Called once to initialize the engine.  After returning, the
    347          * engine's surface will be created by the framework.
    348          */
    349         public void onCreate(SurfaceHolder surfaceHolder) {
    350         }
    351 
    352         /**
    353          * Called right before the engine is going away.  After this the
    354          * surface will be destroyed and this Engine object is no longer
    355          * valid.
    356          */
    357         public void onDestroy() {
    358         }
    359 
    360         /**
    361          * Called to inform you of the wallpaper becoming visible or
    362          * hidden.  <em>It is very important that a wallpaper only use
    363          * CPU while it is visible.</em>.
    364          */
    365         public void onVisibilityChanged(boolean visible) {
    366         }
    367 
    368         /**
    369          * Called as the user performs touch-screen interaction with the
    370          * window that is currently showing this wallpaper.  Note that the
    371          * events you receive here are driven by the actual application the
    372          * user is interacting with, so if it is slow you will get fewer
    373          * move events.
    374          */
    375         public void onTouchEvent(MotionEvent event) {
    376         }
    377 
    378         /**
    379          * Called to inform you of the wallpaper's offsets changing
    380          * within its contain, corresponding to the container's
    381          * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
    382          * WallpaperManager.setWallpaperOffsets()}.
    383          */
    384         public void onOffsetsChanged(float xOffset, float yOffset,
    385                 float xOffsetStep, float yOffsetStep,
    386                 int xPixelOffset, int yPixelOffset) {
    387         }
    388 
    389         /**
    390          * Process a command that was sent to the wallpaper with
    391          * {@link WallpaperManager#sendWallpaperCommand}.
    392          * The default implementation does nothing, and always returns null
    393          * as the result.
    394          *
    395          * @param action The name of the command to perform.  This tells you
    396          * what to do and how to interpret the rest of the arguments.
    397          * @param x Generic integer parameter.
    398          * @param y Generic integer parameter.
    399          * @param z Generic integer parameter.
    400          * @param extras Any additional parameters.
    401          * @param resultRequested If true, the caller is requesting that
    402          * a result, appropriate for the command, be returned back.
    403          * @return If returning a result, create a Bundle and place the
    404          * result data in to it.  Otherwise return null.
    405          */
    406         public Bundle onCommand(String action, int x, int y, int z,
    407                 Bundle extras, boolean resultRequested) {
    408             return null;
    409         }
    410 
    411         /**
    412          * Called when an application has changed the desired virtual size of
    413          * the wallpaper.
    414          */
    415         public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
    416         }
    417 
    418         /**
    419          * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
    420          * SurfaceHolder.Callback.surfaceChanged()}.
    421          */
    422         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    423         }
    424 
    425         /**
    426          * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
    427          * SurfaceHolder.Callback.surfaceCreated()}.
    428          */
    429         public void onSurfaceCreated(SurfaceHolder holder) {
    430         }
    431 
    432         /**
    433          * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
    434          * SurfaceHolder.Callback.surfaceDestroyed()}.
    435          */
    436         public void onSurfaceDestroyed(SurfaceHolder holder) {
    437         }
    438 
    439         void updateSurface(boolean forceRelayout, boolean forceReport) {
    440             if (mDestroyed) {
    441                 Log.w(TAG, "Ignoring updateSurface: destroyed");
    442             }
    443 
    444             int myWidth = mSurfaceHolder.getRequestedWidth();
    445             if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
    446             int myHeight = mSurfaceHolder.getRequestedHeight();
    447             if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
    448 
    449             final boolean creating = !mCreated;
    450             final boolean surfaceCreating = !mSurfaceCreated;
    451             final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
    452             boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
    453             final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
    454             final boolean flagsChanged = mCurWindowFlags != mWindowFlags;
    455             if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
    456                     || typeChanged || flagsChanged) {
    457 
    458                 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
    459                         + " format=" + formatChanged + " size=" + sizeChanged);
    460 
    461                 try {
    462                     mWidth = myWidth;
    463                     mHeight = myHeight;
    464                     mFormat = mSurfaceHolder.getRequestedFormat();
    465                     mType = mSurfaceHolder.getRequestedType();
    466 
    467                     mLayout.x = 0;
    468                     mLayout.y = 0;
    469                     mLayout.width = myWidth;
    470                     mLayout.height = myHeight;
    471 
    472                     mLayout.format = mFormat;
    473 
    474                     mCurWindowFlags = mWindowFlags;
    475                     mLayout.flags = mWindowFlags
    476                             | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
    477                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
    478                             | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
    479                             ;
    480 
    481                     mLayout.memoryType = mType;
    482                     mLayout.token = mWindowToken;
    483 
    484                     if (!mCreated) {
    485                         mLayout.type = mIWallpaperEngine.mWindowType;
    486                         mLayout.gravity = Gravity.LEFT|Gravity.TOP;
    487                         mLayout.setTitle(WallpaperService.this.getClass().getName());
    488                         mLayout.windowAnimations =
    489                                 com.android.internal.R.style.Animation_Wallpaper;
    490                         mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
    491                         mCreated = true;
    492                     }
    493 
    494                     mSurfaceHolder.mSurfaceLock.lock();
    495                     mDrawingAllowed = true;
    496 
    497                     final int relayoutResult = mSession.relayout(
    498                         mWindow, mLayout, mWidth, mHeight,
    499                             View.VISIBLE, false, mWinFrame, mContentInsets,
    500                             mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
    501 
    502                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
    503                             + ", frame=" + mWinFrame);
    504 
    505                     int w = mWinFrame.width();
    506                     if (mCurWidth != w) {
    507                         sizeChanged = true;
    508                         mCurWidth = w;
    509                     }
    510                     int h = mWinFrame.height();
    511                     if (mCurHeight != h) {
    512                         sizeChanged = true;
    513                         mCurHeight = h;
    514                     }
    515 
    516                     mSurfaceHolder.mSurfaceLock.unlock();
    517 
    518                     if (!mSurfaceHolder.mSurface.isValid()) {
    519                         reportSurfaceDestroyed();
    520                         if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
    521                         return;
    522                     }
    523 
    524                     try {
    525                         SurfaceHolder.Callback callbacks[] = null;
    526                         synchronized (mSurfaceHolder.mCallbacks) {
    527                             final int N = mSurfaceHolder.mCallbacks.size();
    528                             if (N > 0) {
    529                                 callbacks = new SurfaceHolder.Callback[N];
    530                                 mSurfaceHolder.mCallbacks.toArray(callbacks);
    531                             }
    532                         }
    533 
    534                         if (surfaceCreating) {
    535                             mIsCreating = true;
    536                             if (DEBUG) Log.v(TAG, "onSurfaceCreated("
    537                                     + mSurfaceHolder + "): " + this);
    538                             onSurfaceCreated(mSurfaceHolder);
    539                             if (callbacks != null) {
    540                                 for (SurfaceHolder.Callback c : callbacks) {
    541                                     c.surfaceCreated(mSurfaceHolder);
    542                                 }
    543                             }
    544                         }
    545                         if (forceReport || creating || surfaceCreating
    546                                 || formatChanged || sizeChanged) {
    547                             if (DEBUG) {
    548                                 RuntimeException e = new RuntimeException();
    549                                 e.fillInStackTrace();
    550                                 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
    551                                         + " formatChanged=" + formatChanged
    552                                         + " sizeChanged=" + sizeChanged, e);
    553                             }
    554                             if (DEBUG) Log.v(TAG, "onSurfaceChanged("
    555                                     + mSurfaceHolder + ", " + mFormat
    556                                     + ", " + mCurWidth + ", " + mCurHeight
    557                                     + "): " + this);
    558                             onSurfaceChanged(mSurfaceHolder, mFormat,
    559                                     mCurWidth, mCurHeight);
    560                             if (callbacks != null) {
    561                                 for (SurfaceHolder.Callback c : callbacks) {
    562                                     c.surfaceChanged(mSurfaceHolder, mFormat,
    563                                             mCurWidth, mCurHeight);
    564                                 }
    565                             }
    566                         }
    567                     } finally {
    568                         mIsCreating = false;
    569                         mSurfaceCreated = true;
    570                         if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
    571                             mSession.finishDrawing(mWindow);
    572                         }
    573                     }
    574                 } catch (RemoteException ex) {
    575                 }
    576                 if (DEBUG) Log.v(
    577                     TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
    578                     " w=" + mLayout.width + " h=" + mLayout.height);
    579             }
    580         }
    581 
    582         void attach(IWallpaperEngineWrapper wrapper) {
    583             if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
    584             if (mDestroyed) {
    585                 return;
    586             }
    587 
    588             mIWallpaperEngine = wrapper;
    589             mCaller = wrapper.mCaller;
    590             mConnection = wrapper.mConnection;
    591             mWindowToken = wrapper.mWindowToken;
    592             mSurfaceHolder.setSizeFromLayout();
    593             mInitializing = true;
    594             mSession = ViewRoot.getWindowSession(getMainLooper());
    595             mWindow.setSession(mSession);
    596 
    597             IntentFilter filter = new IntentFilter();
    598             filter.addAction(Intent.ACTION_SCREEN_ON);
    599             filter.addAction(Intent.ACTION_SCREEN_OFF);
    600             registerReceiver(mReceiver, filter);
    601 
    602             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
    603             onCreate(mSurfaceHolder);
    604 
    605             mInitializing = false;
    606             updateSurface(false, false);
    607         }
    608 
    609         void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
    610             if (!mDestroyed) {
    611                 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
    612                         + desiredWidth + "," + desiredHeight + "): " + this);
    613                 onDesiredSizeChanged(desiredWidth, desiredHeight);
    614             }
    615         }
    616 
    617         void doVisibilityChanged(boolean visible) {
    618             if (!mDestroyed) {
    619                 mVisible = visible;
    620                 reportVisibility();
    621             }
    622         }
    623 
    624         void reportVisibility() {
    625             if (!mDestroyed) {
    626                 boolean visible = mVisible && mScreenOn;
    627                 if (mReportedVisible != visible) {
    628                     mReportedVisible = visible;
    629                     if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
    630                             + "): " + this);
    631                     if (visible) {
    632                         // If becoming visible, in preview mode the surface
    633                         // may have been destroyed so now we need to make
    634                         // sure it is re-created.
    635                         updateSurface(false, false);
    636                     }
    637                     onVisibilityChanged(visible);
    638                 }
    639             }
    640         }
    641 
    642         void doOffsetsChanged() {
    643             if (mDestroyed) {
    644                 return;
    645             }
    646 
    647             float xOffset;
    648             float yOffset;
    649             float xOffsetStep;
    650             float yOffsetStep;
    651             boolean sync;
    652             synchronized (mLock) {
    653                 xOffset = mPendingXOffset;
    654                 yOffset = mPendingYOffset;
    655                 xOffsetStep = mPendingXOffsetStep;
    656                 yOffsetStep = mPendingYOffsetStep;
    657                 sync = mPendingSync;
    658                 mPendingSync = false;
    659                 mOffsetMessageEnqueued = false;
    660             }
    661 
    662             if (mSurfaceCreated) {
    663                 if (DEBUG) Log.v(TAG, "Offsets change in " + this
    664                         + ": " + xOffset + "," + yOffset);
    665                 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
    666                 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
    667                 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
    668                 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
    669                 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
    670             }
    671 
    672             if (sync) {
    673                 try {
    674                     if (DEBUG) Log.v(TAG, "Reporting offsets change complete");
    675                     mSession.wallpaperOffsetsComplete(mWindow.asBinder());
    676                 } catch (RemoteException e) {
    677                 }
    678             }
    679         }
    680 
    681         void doCommand(WallpaperCommand cmd) {
    682             Bundle result;
    683             if (!mDestroyed) {
    684                 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
    685                         cmd.extras, cmd.sync);
    686             } else {
    687                 result = null;
    688             }
    689             if (cmd.sync) {
    690                 try {
    691                     if (DEBUG) Log.v(TAG, "Reporting command complete");
    692                     mSession.wallpaperCommandComplete(mWindow.asBinder(), result);
    693                 } catch (RemoteException e) {
    694                 }
    695             }
    696         }
    697 
    698         void reportSurfaceDestroyed() {
    699             if (mSurfaceCreated) {
    700                 mSurfaceCreated = false;
    701                 SurfaceHolder.Callback callbacks[];
    702                 synchronized (mSurfaceHolder.mCallbacks) {
    703                     callbacks = new SurfaceHolder.Callback[
    704                             mSurfaceHolder.mCallbacks.size()];
    705                     mSurfaceHolder.mCallbacks.toArray(callbacks);
    706                 }
    707                 for (SurfaceHolder.Callback c : callbacks) {
    708                     c.surfaceDestroyed(mSurfaceHolder);
    709                 }
    710                 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
    711                         + mSurfaceHolder + "): " + this);
    712                 onSurfaceDestroyed(mSurfaceHolder);
    713             }
    714         }
    715 
    716         void detach() {
    717             if (mDestroyed) {
    718                 return;
    719             }
    720 
    721             mDestroyed = true;
    722 
    723             if (mVisible) {
    724                 mVisible = false;
    725                 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
    726                 onVisibilityChanged(false);
    727             }
    728 
    729             reportSurfaceDestroyed();
    730 
    731             if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
    732             onDestroy();
    733 
    734             unregisterReceiver(mReceiver);
    735 
    736             if (mCreated) {
    737                 try {
    738                     if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
    739                             + mSurfaceHolder.getSurface() + " of: " + this);
    740                     mSession.remove(mWindow);
    741                 } catch (RemoteException e) {
    742                 }
    743                 mSurfaceHolder.mSurface.release();
    744                 mCreated = false;
    745             }
    746         }
    747     }
    748 
    749     class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
    750             implements HandlerCaller.Callback {
    751         private final HandlerCaller mCaller;
    752 
    753         final IWallpaperConnection mConnection;
    754         final IBinder mWindowToken;
    755         final int mWindowType;
    756         final boolean mIsPreview;
    757         int mReqWidth;
    758         int mReqHeight;
    759 
    760         Engine mEngine;
    761 
    762         IWallpaperEngineWrapper(WallpaperService context,
    763                 IWallpaperConnection conn, IBinder windowToken,
    764                 int windowType, boolean isPreview, int reqWidth, int reqHeight) {
    765             if (DEBUG && mCallbackLooper != null) {
    766                 mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
    767             }
    768             mCaller = new HandlerCaller(context,
    769                     mCallbackLooper != null
    770                             ? mCallbackLooper : context.getMainLooper(),
    771                     this);
    772             mConnection = conn;
    773             mWindowToken = windowToken;
    774             mWindowType = windowType;
    775             mIsPreview = isPreview;
    776             mReqWidth = reqWidth;
    777             mReqHeight = reqHeight;
    778 
    779             Message msg = mCaller.obtainMessage(DO_ATTACH);
    780             mCaller.sendMessage(msg);
    781         }
    782 
    783         public void setDesiredSize(int width, int height) {
    784             Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
    785             mCaller.sendMessage(msg);
    786         }
    787 
    788         public void setVisibility(boolean visible) {
    789             Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
    790                     visible ? 1 : 0);
    791             mCaller.sendMessage(msg);
    792         }
    793 
    794         public void dispatchPointer(MotionEvent event) {
    795             if (mEngine != null) {
    796                 mEngine.mWindow.onDispatchPointer(event, event.getEventTime(), false);
    797             }
    798         }
    799 
    800         public void destroy() {
    801             Message msg = mCaller.obtainMessage(DO_DETACH);
    802             mCaller.sendMessage(msg);
    803         }
    804 
    805         public void executeMessage(Message message) {
    806             switch (message.what) {
    807                 case DO_ATTACH: {
    808                     try {
    809                         mConnection.attachEngine(this);
    810                     } catch (RemoteException e) {
    811                         Log.w(TAG, "Wallpaper host disappeared", e);
    812                         return;
    813                     }
    814                     Engine engine = onCreateEngine();
    815                     mEngine = engine;
    816                     mActiveEngines.add(engine);
    817                     engine.attach(this);
    818                     return;
    819                 }
    820                 case DO_DETACH: {
    821                     mActiveEngines.remove(mEngine);
    822                     mEngine.detach();
    823                     return;
    824                 }
    825                 case DO_SET_DESIRED_SIZE: {
    826                     mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
    827                     return;
    828                 }
    829                 case MSG_UPDATE_SURFACE:
    830                     mEngine.updateSurface(true, false);
    831                     break;
    832                 case MSG_VISIBILITY_CHANGED:
    833                     if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
    834                             + ": " + message.arg1);
    835                     mEngine.doVisibilityChanged(message.arg1 != 0);
    836                     break;
    837                 case MSG_WALLPAPER_OFFSETS: {
    838                     mEngine.doOffsetsChanged();
    839                 } break;
    840                 case MSG_WALLPAPER_COMMAND: {
    841                     WallpaperCommand cmd = (WallpaperCommand)message.obj;
    842                     mEngine.doCommand(cmd);
    843                 } break;
    844                 case MSG_WINDOW_RESIZED: {
    845                     final boolean reportDraw = message.arg1 != 0;
    846                     mEngine.updateSurface(true, false);
    847                     mEngine.doOffsetsChanged();
    848                     if (reportDraw) {
    849                         try {
    850                             mEngine.mSession.finishDrawing(mEngine.mWindow);
    851                         } catch (RemoteException e) {
    852                         }
    853                     }
    854                 } break;
    855                 case MSG_TOUCH_EVENT: {
    856                     MotionEvent ev = (MotionEvent)message.obj;
    857                     synchronized (mEngine.mLock) {
    858                         if (mEngine.mPendingMove == ev) {
    859                             mEngine.mPendingMove = null;
    860                         }
    861                     }
    862                     if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
    863                     mEngine.onTouchEvent(ev);
    864                     ev.recycle();
    865                 } break;
    866                 default :
    867                     Log.w(TAG, "Unknown message type " + message.what);
    868             }
    869         }
    870     }
    871 
    872     /**
    873      * Implements the internal {@link IWallpaperService} interface to convert
    874      * incoming calls to it back to calls on an {@link WallpaperService}.
    875      */
    876     class IWallpaperServiceWrapper extends IWallpaperService.Stub {
    877         private final WallpaperService mTarget;
    878 
    879         public IWallpaperServiceWrapper(WallpaperService context) {
    880             mTarget = context;
    881         }
    882 
    883         public void attach(IWallpaperConnection conn, IBinder windowToken,
    884                 int windowType, boolean isPreview, int reqWidth, int reqHeight) {
    885             new IWallpaperEngineWrapper(mTarget, conn, windowToken,
    886                     windowType, isPreview, reqWidth, reqHeight);
    887         }
    888     }
    889 
    890     @Override
    891     public void onCreate() {
    892         super.onCreate();
    893     }
    894 
    895     @Override
    896     public void onDestroy() {
    897         super.onDestroy();
    898         for (int i=0; i<mActiveEngines.size(); i++) {
    899             mActiveEngines.get(i).detach();
    900         }
    901         mActiveEngines.clear();
    902     }
    903 
    904     /**
    905      * Implement to return the implementation of the internal accessibility
    906      * service interface.  Subclasses should not override.
    907      */
    908     @Override
    909     public final IBinder onBind(Intent intent) {
    910         return new IWallpaperServiceWrapper(this);
    911     }
    912 
    913     /**
    914      * This allows subclasses to change the thread that most callbacks
    915      * occur on.  Currently hidden because it is mostly needed for the
    916      * image wallpaper (which runs in the system process and doesn't want
    917      * to get stuck running on that seriously in use main thread).  Not
    918      * exposed right now because the semantics of this are not totally
    919      * well defined and some callbacks can still happen on the main thread).
    920      * @hide
    921      */
    922     public void setCallbackLooper(Looper looper) {
    923         mCallbackLooper = looper;
    924     }
    925 
    926     /**
    927      * Must be implemented to return a new instance of the wallpaper's engine.
    928      * Note that multiple instances may be active at the same time, such as
    929      * when the wallpaper is currently set as the active wallpaper and the user
    930      * is in the wallpaper picker viewing a preview of it as well.
    931      */
    932     public abstract Engine onCreateEngine();
    933 }
    934