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