WallpaperService.java revision 72c82ab9923025a91bbabb32e56bfea27bfd083b
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.service.wallpaper;
18
19import com.android.internal.os.HandlerCaller;
20import com.android.internal.view.BaseIWindow;
21import com.android.internal.view.BaseSurfaceHolder;
22
23import android.app.Service;
24import android.app.WallpaperManager;
25import android.content.Intent;
26import android.graphics.Rect;
27import android.os.IBinder;
28import android.os.Message;
29import android.os.RemoteException;
30import android.util.Log;
31import android.view.Gravity;
32import android.view.IWindowSession;
33import android.view.SurfaceHolder;
34import android.view.View;
35import android.view.ViewGroup;
36import android.view.ViewRoot;
37import android.view.WindowManager;
38import android.view.WindowManagerImpl;
39
40/**
41 * A wallpaper service is responsible for showing a live wallpaper behind
42 * applications that would like to sit on top of it.
43 * @hide Live Wallpaper
44 */
45public abstract class WallpaperService extends Service {
46    /**
47     * The {@link Intent} that must be declared as handled by the service.
48     */
49    public static final String SERVICE_INTERFACE =
50        "android.service.wallpaper.WallpaperService";
51
52    static final String TAG = "WallpaperService";
53    static final boolean DEBUG = false;
54
55    private static final int DO_ATTACH = 10;
56    private static final int DO_DETACH = 20;
57
58    private static final int MSG_UPDATE_SURFACE = 10000;
59    private static final int MSG_VISIBILITY_CHANGED = 10010;
60    private static final int MSG_WALLPAPER_OFFSETS = 10020;
61
62    /**
63     * The actual implementation of a wallpaper.  A wallpaper service may
64     * have multiple instances running (for example as a real wallpaper
65     * and as a preview), each of which is represented by its own Engine
66     * instance.  You must implement {@link WallpaperService#onCreateEngine()}
67     * to return your concrete Engine implementation.
68     */
69    public class Engine {
70        IWallpaperEngineWrapper mIWallpaperEngine;
71
72        // Copies from mIWallpaperEngine.
73        HandlerCaller mCaller;
74        IWallpaperConnection mConnection;
75        IBinder mWindowToken;
76
77        boolean mInitializing = true;
78
79        // Current window state.
80        boolean mCreated;
81        boolean mIsCreating;
82        boolean mDrawingAllowed;
83        int mWidth;
84        int mHeight;
85        int mFormat;
86        int mType;
87        int mCurWidth;
88        int mCurHeight;
89        boolean mDestroyReportNeeded;
90        final Rect mVisibleInsets = new Rect();
91        final Rect mWinFrame = new Rect();
92        final Rect mContentInsets = new Rect();
93
94        final WindowManager.LayoutParams mLayout
95                = new WindowManager.LayoutParams();
96        IWindowSession mSession;
97
98        final Object mLock = new Object();
99        boolean mOffsetMessageEnqueued;
100        float mPendingXOffset;
101        float mPendingYOffset;
102
103        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
104
105            @Override
106            public boolean onAllowLockCanvas() {
107                return mDrawingAllowed;
108            }
109
110            @Override
111            public void onRelayoutContainer() {
112                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
113                mCaller.sendMessage(msg);
114            }
115
116            @Override
117            public void onUpdateSurface() {
118                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
119                mCaller.sendMessage(msg);
120            }
121
122            public boolean isCreating() {
123                return mIsCreating;
124            }
125
126            public void setKeepScreenOn(boolean screenOn) {
127                // Ignore.
128            }
129
130        };
131
132        final BaseIWindow mWindow = new BaseIWindow() {
133            public void dispatchAppVisibility(boolean visible) {
134                Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
135                        visible ? 1 : 0);
136                mCaller.sendMessage(msg);
137            }
138
139            @Override
140            public void dispatchWallpaperOffsets(float x, float y) {
141                synchronized (mLock) {
142                    mPendingXOffset = x;
143                    mPendingYOffset = y;
144                    if (!mOffsetMessageEnqueued) {
145                        mOffsetMessageEnqueued = true;
146                        Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
147                        mCaller.sendMessage(msg);
148                    }
149                }
150            }
151
152        };
153
154        /**
155         * Provides access to the surface in which this wallpaper is drawn.
156         */
157        public SurfaceHolder getSurfaceHolder() {
158            return mSurfaceHolder;
159        }
160
161        /**
162         * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
163         * WallpaperManager.getDesiredMinimumWidth()}, returning the width
164         * that the system would like this wallpaper to run in.
165         */
166        public int getDesiredMinimumWidth() {
167            return mIWallpaperEngine.mReqWidth;
168        }
169
170        /**
171         * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
172         * WallpaperManager.getDesiredMinimumHeight()}, returning the height
173         * that the system would like this wallpaper to run in.
174         */
175        public int getDesiredMinimumHeight() {
176            return mIWallpaperEngine.mReqHeight;
177        }
178
179        /**
180         * Called once to initialize the engine.  After returning, the
181         * engine's surface will be created by the framework.
182         */
183        public void onCreate(SurfaceHolder surfaceHolder) {
184        }
185
186        /**
187         * Called right before the engine is going away.  After this the
188         * surface will be destroyed and this Engine object is no longer
189         * valid.
190         */
191        public void onDestroy() {
192        }
193
194        /**
195         * Called to inform you of the wallpaper becoming visible or
196         * hidden.  <em>It is very important that a wallpaper only use
197         * CPU while it is visible.</em>.
198         */
199        public void onVisibilityChanged(boolean visible) {
200        }
201
202        /**
203         * Called to inform you of the wallpaper's offsets changing
204         * within its contain, corresponding to the container's
205         * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
206         * WallpaperManager.setWallpaperOffsets()}.
207         */
208        public void onOffsetsChanged(float xOffset, float yOffset,
209                int xPixelOffset, int yPixelOffset) {
210        }
211
212        /**
213         * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
214         * SurfaceHolder.Callback.surfaceChanged()}.
215         */
216        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
217        }
218
219        /**
220         * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
221         * SurfaceHolder.Callback.surfaceCreated()}.
222         */
223        public void onSurfaceCreated(SurfaceHolder holder) {
224        }
225
226        /**
227         * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
228         * SurfaceHolder.Callback.surfaceDestroyed()}.
229         */
230        public void onSurfaceDestroyed(SurfaceHolder holder) {
231        }
232
233        void updateSurface(boolean force) {
234            int myWidth = mSurfaceHolder.getRequestedWidth();
235            if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT;
236            int myHeight = mSurfaceHolder.getRequestedHeight();
237            if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT;
238
239            final boolean creating = !mCreated;
240            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
241            final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
242            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
243            if (force || creating || formatChanged || sizeChanged || typeChanged) {
244
245                if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
246                        + " format=" + formatChanged + " size=" + sizeChanged);
247
248                try {
249                    mWidth = myWidth;
250                    mHeight = myHeight;
251                    mFormat = mSurfaceHolder.getRequestedFormat();
252                    mType = mSurfaceHolder.getRequestedType();
253
254                    // Scaling/Translate window's layout here because mLayout is not used elsewhere.
255
256                    // Places the window relative
257                    mLayout.x = 0;
258                    mLayout.y = 0;
259                    mLayout.width = myWidth;
260                    mLayout.height = myHeight;
261
262                    mLayout.format = mFormat;
263                    mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
264                                  | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
265                                  | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
266                                  | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
267                                  ;
268
269                    mLayout.memoryType = mType;
270                    mLayout.token = mWindowToken;
271
272                    if (!mCreated) {
273                        mLayout.type = WindowManager.LayoutParams.TYPE_WALLPAPER;
274                        mLayout.gravity = Gravity.LEFT|Gravity.TOP;
275                        mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
276                    }
277
278                    mSurfaceHolder.mSurfaceLock.lock();
279                    mDrawingAllowed = true;
280
281                    final int relayoutResult = mSession.relayout(
282                        mWindow, mLayout, mWidth, mHeight,
283                            View.VISIBLE, false, mWinFrame, mContentInsets,
284                            mVisibleInsets, mSurfaceHolder.mSurface);
285
286                    if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface
287                            + ", frame=" + mWinFrame);
288
289                    mCurWidth = mWinFrame.width();
290                    mCurHeight = mWinFrame.height();
291
292                    mSurfaceHolder.mSurfaceLock.unlock();
293
294                    try {
295                        mDestroyReportNeeded = true;
296
297                        SurfaceHolder.Callback callbacks[] = null;
298                        synchronized (mSurfaceHolder.mCallbacks) {
299                            final int N = mSurfaceHolder.mCallbacks.size();
300                            if (N > 0) {
301                                callbacks = new SurfaceHolder.Callback[N];
302                                mSurfaceHolder.mCallbacks.toArray(callbacks);
303                            }
304                        }
305
306                        if (!mCreated) {
307                            mIsCreating = true;
308                            onSurfaceCreated(mSurfaceHolder);
309                            if (callbacks != null) {
310                                for (SurfaceHolder.Callback c : callbacks) {
311                                    c.surfaceCreated(mSurfaceHolder);
312                                }
313                            }
314                        }
315                        if (creating || formatChanged || sizeChanged) {
316                            onSurfaceChanged(mSurfaceHolder, mFormat,
317                                    mCurWidth, mCurHeight);
318                            if (callbacks != null) {
319                                for (SurfaceHolder.Callback c : callbacks) {
320                                    c.surfaceChanged(mSurfaceHolder, mFormat,
321                                            mCurWidth, mCurHeight);
322                                }
323                            }
324                        }
325                    } finally {
326                        mIsCreating = false;
327                        mCreated = true;
328                        if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
329                            mSession.finishDrawing(mWindow);
330                        }
331                    }
332                } catch (RemoteException ex) {
333                }
334                if (DEBUG) Log.v(
335                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
336                    " w=" + mLayout.width + " h=" + mLayout.height);
337            }
338        }
339
340        void attach(IWallpaperEngineWrapper wrapper) {
341            mIWallpaperEngine = wrapper;
342            mCaller = wrapper.mCaller;
343            mConnection = wrapper.mConnection;
344            mWindowToken = wrapper.mWindowToken;
345            // XXX temp -- should run in size from layout (screen) mode.
346            mSurfaceHolder.setFixedSize(mIWallpaperEngine.mReqWidth,
347                    mIWallpaperEngine.mReqHeight);
348            //mSurfaceHolder.setSizeFromLayout();
349            mInitializing = true;
350            mSession = ViewRoot.getWindowSession(getMainLooper());
351            mWindow.setSession(mSession);
352
353            onCreate(mSurfaceHolder);
354
355            mInitializing = false;
356            updateSurface(false);
357        }
358
359        void detach() {
360            onDestroy();
361            if (mDestroyReportNeeded) {
362                mDestroyReportNeeded = false;
363                SurfaceHolder.Callback callbacks[];
364                synchronized (mSurfaceHolder.mCallbacks) {
365                    callbacks = new SurfaceHolder.Callback[
366                            mSurfaceHolder.mCallbacks.size()];
367                    mSurfaceHolder.mCallbacks.toArray(callbacks);
368                }
369                for (SurfaceHolder.Callback c : callbacks) {
370                    c.surfaceDestroyed(mSurfaceHolder);
371                }
372            }
373            if (mCreated) {
374                try {
375                    mSession.remove(mWindow);
376                } catch (RemoteException e) {
377                }
378                mSurfaceHolder.mSurface.clear();
379                mCreated = false;
380            }
381        }
382    }
383
384    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
385            implements HandlerCaller.Callback {
386        private final HandlerCaller mCaller;
387
388        final IWallpaperConnection mConnection;
389        final IBinder mWindowToken;
390        int mReqWidth;
391        int mReqHeight;
392
393        Engine mEngine;
394
395        IWallpaperEngineWrapper(WallpaperService context,
396                IWallpaperConnection conn, IBinder windowToken,
397                int reqWidth, int reqHeight) {
398            mCaller = new HandlerCaller(context, this);
399            mConnection = conn;
400            mWindowToken = windowToken;
401            mReqWidth = reqWidth;
402            mReqHeight = reqHeight;
403
404            try {
405                conn.attachEngine(this);
406            } catch (RemoteException e) {
407                destroy();
408            }
409
410            Message msg = mCaller.obtainMessage(DO_ATTACH);
411            mCaller.sendMessage(msg);
412        }
413
414        public void destroy() {
415            Message msg = mCaller.obtainMessage(DO_DETACH);
416            mCaller.sendMessage(msg);
417        }
418
419        public void executeMessage(Message message) {
420            switch (message.what) {
421                case DO_ATTACH: {
422                    Engine engine = onCreateEngine();
423                    mEngine = engine;
424                    engine.attach(this);
425                    return;
426                }
427                case DO_DETACH: {
428                    mEngine.detach();
429                    return;
430                }
431                case MSG_UPDATE_SURFACE:
432                    mEngine.updateSurface(false);
433                    break;
434                case MSG_VISIBILITY_CHANGED:
435                    if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
436                            + ": " + message.arg1);
437                    mEngine.onVisibilityChanged(message.arg1 != 0);
438                    break;
439                case MSG_WALLPAPER_OFFSETS: {
440                    float xOffset;
441                    float yOffset;
442                    synchronized (mEngine.mLock) {
443                        xOffset = mEngine.mPendingXOffset;
444                        yOffset = mEngine.mPendingYOffset;
445                        mEngine.mOffsetMessageEnqueued = false;
446                    }
447                    if (DEBUG) Log.v(TAG, "Offsets change in " + mEngine
448                            + ": " + xOffset + "," + yOffset);
449                    final int availw = mReqWidth-mEngine.mCurWidth;
450                    final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
451                    final int availh = mReqHeight-mEngine.mCurHeight;
452                    final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
453                    mEngine.onOffsetsChanged(xOffset, yOffset, xPixels, yPixels);
454                } break;
455                default :
456                    Log.w(TAG, "Unknown message type " + message.what);
457            }
458        }
459    }
460
461    /**
462     * Implements the internal {@link IWallpaperService} interface to convert
463     * incoming calls to it back to calls on an {@link WallpaperService}.
464     */
465    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
466        private final WallpaperService mTarget;
467
468        public IWallpaperServiceWrapper(WallpaperService context) {
469            mTarget = context;
470        }
471
472        public void attach(IWallpaperConnection conn,
473                IBinder windowToken, int reqWidth, int reqHeight) {
474            new IWallpaperEngineWrapper(
475                    mTarget, conn, windowToken, reqWidth, reqHeight);
476        }
477    }
478
479    /**
480     * Implement to return the implementation of the internal accessibility
481     * service interface.  Subclasses should not override.
482     */
483    @Override
484    public final IBinder onBind(Intent intent) {
485        return new IWallpaperServiceWrapper(this);
486    }
487
488    public abstract Engine onCreateEngine();
489}
490