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