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