WallpaperService.java revision bcbcaa7edd32ba67c6290d79f7e7821c4b5b39ac
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
17package android.service.wallpaper;
18
19import com.android.internal.os.HandlerCaller;
20import com.android.internal.view.BaseIWindow;
21import com.android.internal.view.BaseSurfaceHolder;
22
23import android.app.Service;
24import android.app.WallpaperManager;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.graphics.Rect;
30import android.os.IBinder;
31import android.os.Message;
32import android.os.RemoteException;
33import android.util.Log;
34import android.view.Gravity;
35import android.view.IWindowSession;
36import android.view.MotionEvent;
37import android.view.SurfaceHolder;
38import android.view.View;
39import android.view.ViewGroup;
40import android.view.ViewRoot;
41import android.view.WindowManager;
42import android.view.WindowManagerImpl;
43
44/**
45 * A wallpaper service is responsible for showing a live wallpaper behind
46 * applications that would like to sit on top of it.
47 * @hide Live Wallpaper
48 */
49public abstract class WallpaperService extends Service {
50    /**
51     * The {@link Intent} that must be declared as handled by the service.
52     */
53    public static final String SERVICE_INTERFACE =
54        "android.service.wallpaper.WallpaperService";
55
56    /**
57     * Name under which a WallpaperService component publishes information
58     * about itself.  This meta-data must reference an XML resource containing
59     * a <code>&lt;{@link android.R.styleable#Wallpaper wallpaper}&gt;</code>
60     * tag.
61     */
62    public static final String SERVICE_META_DATA = "android.service.wallpaper";
63
64    static final String TAG = "WallpaperService";
65    static final boolean DEBUG = false;
66
67    private static final int DO_ATTACH = 10;
68    private static final int DO_DETACH = 20;
69    private static final int DO_SET_DESIRED_SIZE = 30;
70
71    private static final int MSG_UPDATE_SURFACE = 10000;
72    private static final int MSG_VISIBILITY_CHANGED = 10010;
73    private static final int MSG_WALLPAPER_OFFSETS = 10020;
74    private static final int MSG_WINDOW_RESIZED = 10030;
75    private static final int MSG_TOUCH_EVENT = 10040;
76
77    /**
78     * The actual implementation of a wallpaper.  A wallpaper service may
79     * have multiple instances running (for example as a real wallpaper
80     * and as a preview), each of which is represented by its own Engine
81     * instance.  You must implement {@link WallpaperService#onCreateEngine()}
82     * to return your concrete Engine implementation.
83     */
84    public class Engine {
85        IWallpaperEngineWrapper mIWallpaperEngine;
86
87        // Copies from mIWallpaperEngine.
88        HandlerCaller mCaller;
89        IWallpaperConnection mConnection;
90        IBinder mWindowToken;
91
92        boolean mInitializing = true;
93        boolean mVisible;
94        boolean mScreenOn = true;
95        boolean mReportedVisible;
96        boolean mDestroyed;
97
98        // Current window state.
99        boolean mCreated;
100        boolean mIsCreating;
101        boolean mDrawingAllowed;
102        int mWidth;
103        int mHeight;
104        int mFormat;
105        int mType;
106        int mCurWidth;
107        int mCurHeight;
108        int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
109        int mCurWindowFlags = mWindowFlags;
110        boolean mDestroyReportNeeded;
111        final Rect mVisibleInsets = new Rect();
112        final Rect mWinFrame = new Rect();
113        final Rect mContentInsets = new Rect();
114
115        final WindowManager.LayoutParams mLayout
116                = new WindowManager.LayoutParams();
117        IWindowSession mSession;
118
119        final Object mLock = new Object();
120        boolean mOffsetMessageEnqueued;
121        float mPendingXOffset;
122        float mPendingYOffset;
123        MotionEvent mPendingMove;
124
125        final BroadcastReceiver mReceiver = new BroadcastReceiver() {
126            @Override
127            public void onReceive(Context context, Intent intent) {
128                if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
129                    mScreenOn = true;
130                    reportVisibility();
131                } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
132                    mScreenOn = false;
133                    reportVisibility();
134                }
135            }
136        };
137
138        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
139
140            @Override
141            public boolean onAllowLockCanvas() {
142                return mDrawingAllowed;
143            }
144
145            @Override
146            public void onRelayoutContainer() {
147                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
148                mCaller.sendMessage(msg);
149            }
150
151            @Override
152            public void onUpdateSurface() {
153                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
154                mCaller.sendMessage(msg);
155            }
156
157            public boolean isCreating() {
158                return mIsCreating;
159            }
160
161            @Override
162            public void setFixedSize(int width, int height) {
163                throw new UnsupportedOperationException(
164                        "Wallpapers currently only support sizing from layout");
165            }
166
167            public void setKeepScreenOn(boolean screenOn) {
168                throw new UnsupportedOperationException(
169                        "Wallpapers do not support keep screen on");
170            }
171
172        };
173
174        final BaseIWindow mWindow = new BaseIWindow() {
175            @Override
176            public boolean onDispatchPointer(MotionEvent event, long eventTime,
177                    boolean callWhenDone) {
178                synchronized (mLock) {
179                    if (event.getAction() == MotionEvent.ACTION_MOVE) {
180                        if (mPendingMove != null) {
181                            mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
182                            mPendingMove.recycle();
183                        }
184                        mPendingMove = event;
185                    } else {
186                        mPendingMove = null;
187                    }
188                    Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT,
189                            event);
190                    mCaller.sendMessage(msg);
191                }
192                return false;
193            }
194
195            @Override
196            public void resized(int w, int h, Rect coveredInsets,
197                    Rect visibleInsets, boolean reportDraw) {
198                Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
199                        reportDraw ? 1 : 0);
200                mCaller.sendMessage(msg);
201            }
202
203            @Override
204            public void dispatchAppVisibility(boolean visible) {
205                // We don't do this in preview mode; we'll let the preview
206                // activity tell us when to run.
207                if (!mIWallpaperEngine.mIsPreview) {
208                    Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
209                            visible ? 1 : 0);
210                    mCaller.sendMessage(msg);
211                }
212            }
213
214            @Override
215            public void dispatchWallpaperOffsets(float x, float y) {
216                synchronized (mLock) {
217                    mPendingXOffset = x;
218                    mPendingYOffset = y;
219                    if (!mOffsetMessageEnqueued) {
220                        mOffsetMessageEnqueued = true;
221                        Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
222                        mCaller.sendMessage(msg);
223                    }
224                }
225            }
226
227        };
228
229        /**
230         * Provides access to the surface in which this wallpaper is drawn.
231         */
232        public SurfaceHolder getSurfaceHolder() {
233            return mSurfaceHolder;
234        }
235
236        /**
237         * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
238         * WallpaperManager.getDesiredMinimumWidth()}, returning the width
239         * that the system would like this wallpaper to run in.
240         */
241        public int getDesiredMinimumWidth() {
242            return mIWallpaperEngine.mReqWidth;
243        }
244
245        /**
246         * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
247         * WallpaperManager.getDesiredMinimumHeight()}, returning the height
248         * that the system would like this wallpaper to run in.
249         */
250        public int getDesiredMinimumHeight() {
251            return mIWallpaperEngine.mReqHeight;
252        }
253
254        /**
255         * Return whether the wallpaper is currently visible to the user,
256         * this is the last value supplied to
257         * {@link #onVisibilityChanged(boolean)}.
258         */
259        public boolean isVisible() {
260            return mReportedVisible;
261        }
262
263        /**
264         * Returns true if this engine is running in preview mode -- that is,
265         * it is being shown to the user before they select it as the actual
266         * wallpaper.
267         */
268        public boolean isPreview() {
269            return mIWallpaperEngine.mIsPreview;
270        }
271
272        /**
273         * Control whether this wallpaper will receive raw touch events
274         * from the window manager as the user interacts with the window
275         * that is currently displaying the wallpaper.  By default they
276         * are turned off.  If enabled, the events will be received in
277         * {@link #onTouchEvent(MotionEvent)}.
278         */
279        public void setTouchEventsEnabled(boolean enabled) {
280            mWindowFlags = enabled
281                    ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
282                    : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
283            if (mCreated) {
284                updateSurface(false, false);
285            }
286        }
287
288        /**
289         * Called once to initialize the engine.  After returning, the
290         * engine's surface will be created by the framework.
291         */
292        public void onCreate(SurfaceHolder surfaceHolder) {
293        }
294
295        /**
296         * Called right before the engine is going away.  After this the
297         * surface will be destroyed and this Engine object is no longer
298         * valid.
299         */
300        public void onDestroy() {
301        }
302
303        /**
304         * Called to inform you of the wallpaper becoming visible or
305         * hidden.  <em>It is very important that a wallpaper only use
306         * CPU while it is visible.</em>.
307         */
308        public void onVisibilityChanged(boolean visible) {
309        }
310
311        /**
312         * Called as the user performs touch-screen interaction with the
313         * window that is currently showing this wallpaper.  Note that the
314         * events you receive here are driven by the actual application the
315         * user is interacting with, so if it is slow you will get viewer
316         * move events.
317         */
318        public void onTouchEvent(MotionEvent event) {
319        }
320
321        /**
322         * Called to inform you of the wallpaper's offsets changing
323         * within its contain, corresponding to the container's
324         * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
325         * WallpaperManager.setWallpaperOffsets()}.
326         */
327        public void onOffsetsChanged(float xOffset, float yOffset,
328                int xPixelOffset, int yPixelOffset) {
329        }
330
331        /**
332         * Called when an application has changed the desired virtual size of
333         * the wallpaper.
334         */
335        public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
336        }
337
338        /**
339         * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
340         * SurfaceHolder.Callback.surfaceChanged()}.
341         */
342        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
343        }
344
345        /**
346         * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
347         * SurfaceHolder.Callback.surfaceCreated()}.
348         */
349        public void onSurfaceCreated(SurfaceHolder holder) {
350        }
351
352        /**
353         * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
354         * SurfaceHolder.Callback.surfaceDestroyed()}.
355         */
356        public void onSurfaceDestroyed(SurfaceHolder holder) {
357        }
358
359        void updateSurface(boolean forceRelayout, boolean forceReport) {
360            if (mDestroyed) {
361                Log.w(TAG, "Ignoring updateSurface: destroyed");
362            }
363
364            int myWidth = mSurfaceHolder.getRequestedWidth();
365            if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT;
366            int myHeight = mSurfaceHolder.getRequestedHeight();
367            if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT;
368
369            final boolean creating = !mCreated;
370            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
371            boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
372            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
373            final boolean flagsChanged = mCurWindowFlags != mWindowFlags;
374            if (forceRelayout || creating || formatChanged || sizeChanged
375                    || typeChanged || flagsChanged) {
376
377                if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
378                        + " format=" + formatChanged + " size=" + sizeChanged);
379
380                try {
381                    mWidth = myWidth;
382                    mHeight = myHeight;
383                    mFormat = mSurfaceHolder.getRequestedFormat();
384                    mType = mSurfaceHolder.getRequestedType();
385
386                    mLayout.x = 0;
387                    mLayout.y = 0;
388                    mLayout.width = myWidth;
389                    mLayout.height = myHeight;
390
391                    mLayout.format = mFormat;
392
393                    mCurWindowFlags = mWindowFlags;
394                    mLayout.flags = mWindowFlags
395                            | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
396                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
397                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
398                            ;
399
400                    mLayout.memoryType = mType;
401                    mLayout.token = mWindowToken;
402
403                    if (!mCreated) {
404                        mLayout.type = mIWallpaperEngine.mWindowType;
405                        mLayout.gravity = Gravity.LEFT|Gravity.TOP;
406                        mLayout.setTitle(WallpaperService.this.getClass().getName());
407                        mLayout.windowAnimations =
408                                com.android.internal.R.style.Animation_Wallpaper;
409                        mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
410                    }
411
412                    mSurfaceHolder.mSurfaceLock.lock();
413                    mDrawingAllowed = true;
414
415                    final int relayoutResult = mSession.relayout(
416                        mWindow, mLayout, mWidth, mHeight,
417                            View.VISIBLE, false, mWinFrame, mContentInsets,
418                            mVisibleInsets, mSurfaceHolder.mSurface);
419
420                    if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
421                            + ", frame=" + mWinFrame);
422
423                    int w = mWinFrame.width();
424                    if (mCurWidth != w) {
425                        sizeChanged = true;
426                        mCurWidth = w;
427                    }
428                    int h = mWinFrame.height();
429                    if (mCurHeight != h) {
430                        sizeChanged = true;
431                        mCurHeight = h;
432                    }
433
434                    mSurfaceHolder.mSurfaceLock.unlock();
435
436                    try {
437                        mDestroyReportNeeded = true;
438
439                        SurfaceHolder.Callback callbacks[] = null;
440                        synchronized (mSurfaceHolder.mCallbacks) {
441                            final int N = mSurfaceHolder.mCallbacks.size();
442                            if (N > 0) {
443                                callbacks = new SurfaceHolder.Callback[N];
444                                mSurfaceHolder.mCallbacks.toArray(callbacks);
445                            }
446                        }
447
448                        if (!mCreated) {
449                            mIsCreating = true;
450                            if (DEBUG) Log.v(TAG, "onSurfaceCreated("
451                                    + mSurfaceHolder + "): " + this);
452                            onSurfaceCreated(mSurfaceHolder);
453                            if (callbacks != null) {
454                                for (SurfaceHolder.Callback c : callbacks) {
455                                    c.surfaceCreated(mSurfaceHolder);
456                                }
457                            }
458                        }
459                        if (forceReport || creating || formatChanged || sizeChanged) {
460                            if (DEBUG) {
461                                RuntimeException e = new RuntimeException();
462                                e.fillInStackTrace();
463                                Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
464                                        + " formatChanged=" + formatChanged
465                                        + " sizeChanged=" + sizeChanged, e);
466                            }
467                            if (DEBUG) Log.v(TAG, "onSurfaceChanged("
468                                    + mSurfaceHolder + ", " + mFormat
469                                    + ", " + mCurWidth + ", " + mCurHeight
470                                    + "): " + this);
471                            onSurfaceChanged(mSurfaceHolder, mFormat,
472                                    mCurWidth, mCurHeight);
473                            if (callbacks != null) {
474                                for (SurfaceHolder.Callback c : callbacks) {
475                                    c.surfaceChanged(mSurfaceHolder, mFormat,
476                                            mCurWidth, mCurHeight);
477                                }
478                            }
479                        }
480                    } finally {
481                        mIsCreating = false;
482                        mCreated = true;
483                        if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
484                            mSession.finishDrawing(mWindow);
485                        }
486                    }
487                } catch (RemoteException ex) {
488                }
489                if (DEBUG) Log.v(
490                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
491                    " w=" + mLayout.width + " h=" + mLayout.height);
492            }
493        }
494
495        void attach(IWallpaperEngineWrapper wrapper) {
496            if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
497            if (mDestroyed) {
498                return;
499            }
500
501            mIWallpaperEngine = wrapper;
502            mCaller = wrapper.mCaller;
503            mConnection = wrapper.mConnection;
504            mWindowToken = wrapper.mWindowToken;
505            mSurfaceHolder.setSizeFromLayout();
506            mInitializing = true;
507            mSession = ViewRoot.getWindowSession(getMainLooper());
508            mWindow.setSession(mSession);
509
510            IntentFilter filter = new IntentFilter();
511            filter.addAction(Intent.ACTION_SCREEN_ON);
512            filter.addAction(Intent.ACTION_SCREEN_OFF);
513            registerReceiver(mReceiver, filter);
514
515            if (DEBUG) Log.v(TAG, "onCreate(): " + this);
516            onCreate(mSurfaceHolder);
517
518            mInitializing = false;
519            updateSurface(false, false);
520        }
521
522        void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
523            if (!mDestroyed) {
524                if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
525                        + desiredWidth + "," + desiredHeight + "): " + this);
526                onDesiredSizeChanged(desiredWidth, desiredHeight);
527            }
528        }
529
530        void doVisibilityChanged(boolean visible) {
531            mVisible = visible;
532            reportVisibility();
533        }
534
535        void reportVisibility() {
536            if (!mDestroyed) {
537                boolean visible = mVisible && mScreenOn;
538                if (mReportedVisible != visible) {
539                    mReportedVisible = visible;
540                    if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
541                            + "): " + this);
542                    onVisibilityChanged(visible);
543                }
544            }
545        }
546
547        void doOffsetsChanged() {
548            if (mDestroyed) {
549                return;
550            }
551
552            float xOffset;
553            float yOffset;
554            synchronized (mLock) {
555                xOffset = mPendingXOffset;
556                yOffset = mPendingYOffset;
557                mOffsetMessageEnqueued = false;
558            }
559            if (DEBUG) Log.v(TAG, "Offsets change in " + this
560                    + ": " + xOffset + "," + yOffset);
561            final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
562            final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
563            final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
564            final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
565            onOffsetsChanged(xOffset, yOffset, xPixels, yPixels);
566        }
567
568        void detach() {
569            mDestroyed = true;
570
571            if (mVisible) {
572                mVisible = false;
573                if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
574                onVisibilityChanged(false);
575            }
576
577            if (mDestroyReportNeeded) {
578                mDestroyReportNeeded = false;
579                SurfaceHolder.Callback callbacks[];
580                synchronized (mSurfaceHolder.mCallbacks) {
581                    callbacks = new SurfaceHolder.Callback[
582                            mSurfaceHolder.mCallbacks.size()];
583                    mSurfaceHolder.mCallbacks.toArray(callbacks);
584                }
585                for (SurfaceHolder.Callback c : callbacks) {
586                    c.surfaceDestroyed(mSurfaceHolder);
587                }
588                if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
589                        + mSurfaceHolder + "): " + this);
590                onSurfaceDestroyed(mSurfaceHolder);
591            }
592
593            if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
594            onDestroy();
595
596            unregisterReceiver(mReceiver);
597
598            if (mCreated) {
599                try {
600                    mSession.remove(mWindow);
601                } catch (RemoteException e) {
602                }
603                mSurfaceHolder.mSurface.release();
604                mCreated = false;
605            }
606        }
607    }
608
609    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
610            implements HandlerCaller.Callback {
611        private final HandlerCaller mCaller;
612
613        final IWallpaperConnection mConnection;
614        final IBinder mWindowToken;
615        final int mWindowType;
616        final boolean mIsPreview;
617        int mReqWidth;
618        int mReqHeight;
619
620        Engine mEngine;
621
622        IWallpaperEngineWrapper(WallpaperService context,
623                IWallpaperConnection conn, IBinder windowToken,
624                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
625            mCaller = new HandlerCaller(context, this);
626            mConnection = conn;
627            mWindowToken = windowToken;
628            mWindowType = windowType;
629            mIsPreview = isPreview;
630            mReqWidth = reqWidth;
631            mReqHeight = reqHeight;
632
633            Message msg = mCaller.obtainMessage(DO_ATTACH);
634            mCaller.sendMessage(msg);
635        }
636
637        public void setDesiredSize(int width, int height) {
638            Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
639            mCaller.sendMessage(msg);
640        }
641
642        public void setVisibility(boolean visible) {
643            Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
644                    visible ? 1 : 0);
645            mCaller.sendMessage(msg);
646        }
647
648        public void destroy() {
649            Message msg = mCaller.obtainMessage(DO_DETACH);
650            mCaller.sendMessage(msg);
651        }
652
653        public void executeMessage(Message message) {
654            switch (message.what) {
655                case DO_ATTACH: {
656                    try {
657                        mConnection.attachEngine(this);
658                    } catch (RemoteException e) {
659                        Log.w(TAG, "Wallpaper host disappeared", e);
660                        return;
661                    }
662                    Engine engine = onCreateEngine();
663                    mEngine = engine;
664                    engine.attach(this);
665                    return;
666                }
667                case DO_DETACH: {
668                    mEngine.detach();
669                    return;
670                }
671                case DO_SET_DESIRED_SIZE: {
672                    mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
673                    return;
674                }
675                case MSG_UPDATE_SURFACE:
676                    mEngine.updateSurface(true, false);
677                    break;
678                case MSG_VISIBILITY_CHANGED:
679                    if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
680                            + ": " + message.arg1);
681                    mEngine.doVisibilityChanged(message.arg1 != 0);
682                    break;
683                case MSG_WALLPAPER_OFFSETS: {
684                    mEngine.doOffsetsChanged();
685                } break;
686                case MSG_WINDOW_RESIZED: {
687                    final boolean reportDraw = message.arg1 != 0;
688                    mEngine.updateSurface(true, false);
689                    if (reportDraw) {
690                        try {
691                            mEngine.mSession.finishDrawing(mEngine.mWindow);
692                        } catch (RemoteException e) {
693                        }
694                    }
695                } break;
696                case MSG_TOUCH_EVENT: {
697                    MotionEvent ev = (MotionEvent)message.obj;
698                    synchronized (mEngine.mLock) {
699                        if (mEngine.mPendingMove == ev) {
700                            mEngine.mPendingMove = null;
701                        }
702                    }
703                    mEngine.onTouchEvent(ev);
704                    ev.recycle();
705                } break;
706                default :
707                    Log.w(TAG, "Unknown message type " + message.what);
708            }
709        }
710    }
711
712    /**
713     * Implements the internal {@link IWallpaperService} interface to convert
714     * incoming calls to it back to calls on an {@link WallpaperService}.
715     */
716    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
717        private final WallpaperService mTarget;
718
719        public IWallpaperServiceWrapper(WallpaperService context) {
720            mTarget = context;
721        }
722
723        public void attach(IWallpaperConnection conn, IBinder windowToken,
724                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
725            new IWallpaperEngineWrapper(mTarget, conn, windowToken,
726                    windowType, isPreview, reqWidth, reqHeight);
727        }
728    }
729
730    /**
731     * Implement to return the implementation of the internal accessibility
732     * service interface.  Subclasses should not override.
733     */
734    @Override
735    public final IBinder onBind(Intent intent) {
736        return new IWallpaperServiceWrapper(this);
737    }
738
739    public abstract Engine onCreateEngine();
740}
741