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