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