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