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