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