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