WallpaperService.java revision 759a39e8d2a8b27ef07e102394629dce68aa186b
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.Handler;
28import android.os.IBinder;
29import android.os.Message;
30import android.os.RemoteException;
31import android.util.Log;
32import android.view.Gravity;
33import android.view.IWindowSession;
34import android.view.SurfaceHolder;
35import android.view.View;
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 = true;
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
61    /**
62     * The actual implementation of a wallpaper.  A wallpaper service may
63     * have multiple instances running (for example as a real wallpaper
64     * and as a preview), each of which is represented by its own Engine
65     * instance.  You must implement {@link WallpaperService#onCreateEngine()}
66     * to return your concrete Engine implementation.
67     */
68    public class Engine {
69        IWallpaperEngineWrapper mIWallpaperEngine;
70
71        // Copies from mIWallpaperEngine.
72        HandlerCaller mCaller;
73        IWallpaperConnection mConnection;
74        IBinder mWindowToken;
75
76        boolean mInitializing = true;
77
78        // Current window state.
79        boolean mCreated;
80        boolean mIsCreating;
81        boolean mDrawingAllowed;
82        int mWidth;
83        int mHeight;
84        int mFormat;
85        int mType;
86        boolean mDestroyReportNeeded;
87        final Rect mVisibleInsets = new Rect();
88        final Rect mWinFrame = new Rect();
89        final Rect mContentInsets = new Rect();
90
91        final WindowManager.LayoutParams mLayout
92                = new WindowManager.LayoutParams();
93        IWindowSession mSession;
94
95        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
96
97            @Override
98            public boolean onAllowLockCanvas() {
99                return mDrawingAllowed;
100            }
101
102            @Override
103            public void onRelayoutContainer() {
104                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
105                mCaller.sendMessage(msg);
106            }
107
108            @Override
109            public void onUpdateSurface() {
110                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
111                mCaller.sendMessage(msg);
112            }
113
114            public boolean isCreating() {
115                return mIsCreating;
116            }
117
118            public void setKeepScreenOn(boolean screenOn) {
119                // Ignore.
120            }
121
122        };
123
124        final BaseIWindow mWindow = new BaseIWindow() {
125            public void dispatchAppVisibility(boolean visible) {
126                Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
127                        visible ? 1 : 0);
128                mCaller.sendMessage(msg);
129            }
130        };
131
132        /**
133         * Provides access to the surface in which this wallpaper is drawn.
134         */
135        public SurfaceHolder getSurfaceHolder() {
136            return mSurfaceHolder;
137        }
138
139        /**
140         * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
141         * WallpaperManager.getDesiredMinimumWidth()}, returning the width
142         * that the system would like this wallpaper to run in.
143         */
144        public int getDesiredMinimumWidth() {
145            return mIWallpaperEngine.mReqWidth;
146        }
147
148        /**
149         * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
150         * WallpaperManager.getDesiredMinimumHeight()}, returning the height
151         * that the system would like this wallpaper to run in.
152         */
153        public int getDesiredMinimumHeight() {
154            return mIWallpaperEngine.mReqHeight;
155        }
156
157        /**
158         * Called once to initialize the engine.  After returning, the
159         * engine's surface will be created by the framework.
160         */
161        public void onCreate(SurfaceHolder surfaceHolder) {
162        }
163
164        /**
165         * Called right before the engine is going away.  After this the
166         * surface will be destroyed and this Engine object is no longer
167         * valid.
168         */
169        public void onDestroy() {
170        }
171
172        /**
173         * Called to inform you of the wallpaper becoming visible or
174         * hidden.  <em>It is very important that a wallpaper only use
175         * CPU while it is visible.</em>.
176         */
177        public void onVisibilityChanged(boolean visible) {
178        }
179
180        /**
181         * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
182         * SurfaceHolder.Callback.surfaceChanged()}.
183         */
184        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
185        }
186
187        /**
188         * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
189         * SurfaceHolder.Callback.surfaceCreated()}.
190         */
191        public void onSurfaceCreated(SurfaceHolder holder) {
192        }
193
194        /**
195         * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
196         * SurfaceHolder.Callback.surfaceDestroyed()}.
197         */
198        public void onSurfaceDestroyed(SurfaceHolder holder) {
199        }
200
201        void updateSurface(boolean force) {
202            int myWidth = mSurfaceHolder.getRequestedWidth();
203            if (myWidth <= 0) myWidth = mIWallpaperEngine.mReqWidth;
204            int myHeight = mSurfaceHolder.getRequestedHeight();
205            if (myHeight <= 0) myHeight = mIWallpaperEngine.mReqHeight;
206
207            final boolean creating = !mCreated;
208            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
209            final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
210            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
211            if (force || creating || formatChanged || sizeChanged || typeChanged) {
212
213                if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
214                        + " format=" + formatChanged + " size=" + sizeChanged);
215
216                try {
217                    mWidth = myWidth;
218                    mHeight = myHeight;
219                    mFormat = mSurfaceHolder.getRequestedFormat();
220                    mType = mSurfaceHolder.getRequestedType();
221
222                    // Scaling/Translate window's layout here because mLayout is not used elsewhere.
223
224                    // Places the window relative
225                    mLayout.x = 0;
226                    mLayout.y = 0;
227                    mLayout.width = myWidth;
228                    mLayout.height = myHeight;
229
230                    mLayout.format = mFormat;
231                    mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
232                                  | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
233                                  | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
234                                  | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
235                                  ;
236
237                    mLayout.memoryType = mType;
238                    mLayout.token = mWindowToken;
239
240                    if (!mCreated) {
241                        mLayout.type = WindowManager.LayoutParams.TYPE_WALLPAPER;
242                        mLayout.gravity = Gravity.LEFT|Gravity.TOP;
243                        mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
244                    }
245
246                    mSurfaceHolder.mSurfaceLock.lock();
247                    mDrawingAllowed = true;
248
249                    final int relayoutResult = mSession.relayout(
250                        mWindow, mLayout, mWidth, mHeight,
251                            View.VISIBLE, false, mWinFrame, mContentInsets,
252                            mVisibleInsets, mSurfaceHolder.mSurface);
253
254                    if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface
255                            + ", frame=" + mWinFrame);
256
257                    mSurfaceHolder.mSurfaceLock.unlock();
258
259                    try {
260                        mDestroyReportNeeded = true;
261
262                        SurfaceHolder.Callback callbacks[] = null;
263                        synchronized (mSurfaceHolder.mCallbacks) {
264                            final int N = mSurfaceHolder.mCallbacks.size();
265                            if (N > 0) {
266                                callbacks = new SurfaceHolder.Callback[N];
267                                mSurfaceHolder.mCallbacks.toArray(callbacks);
268                            }
269                        }
270
271                        if (!mCreated) {
272                            mIsCreating = true;
273                            onSurfaceCreated(mSurfaceHolder);
274                            if (callbacks != null) {
275                                for (SurfaceHolder.Callback c : callbacks) {
276                                    c.surfaceCreated(mSurfaceHolder);
277                                }
278                            }
279                        }
280                        if (creating || formatChanged || sizeChanged) {
281                            onSurfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
282                            if (callbacks != null) {
283                                for (SurfaceHolder.Callback c : callbacks) {
284                                    c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
285                                }
286                            }
287                        }
288                    } finally {
289                        mIsCreating = false;
290                        mCreated = true;
291                        if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
292                            mSession.finishDrawing(mWindow);
293                        }
294                    }
295                } catch (RemoteException ex) {
296                }
297                if (DEBUG) Log.v(
298                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
299                    " w=" + mLayout.width + " h=" + mLayout.height);
300            }
301        }
302
303        void attach(IWallpaperEngineWrapper wrapper) {
304            mIWallpaperEngine = wrapper;
305            mCaller = wrapper.mCaller;
306            mConnection = wrapper.mConnection;
307            mWindowToken = wrapper.mWindowToken;
308            mSurfaceHolder.setSizeFromLayout();
309            mInitializing = true;
310            mSession = ViewRoot.getWindowSession(getMainLooper());
311            mWindow.setSession(mSession);
312
313            onCreate(mSurfaceHolder);
314
315            mInitializing = false;
316            updateSurface(false);
317        }
318
319        void detach() {
320            onDestroy();
321            if (mDestroyReportNeeded) {
322                mDestroyReportNeeded = false;
323                SurfaceHolder.Callback callbacks[];
324                synchronized (mSurfaceHolder.mCallbacks) {
325                    callbacks = new SurfaceHolder.Callback[
326                            mSurfaceHolder.mCallbacks.size()];
327                    mSurfaceHolder.mCallbacks.toArray(callbacks);
328                }
329                for (SurfaceHolder.Callback c : callbacks) {
330                    c.surfaceDestroyed(mSurfaceHolder);
331                }
332            }
333            if (mCreated) {
334                try {
335                    mSession.remove(mWindow);
336                } catch (RemoteException e) {
337                }
338                mSurfaceHolder.mSurface.clear();
339                mCreated = false;
340            }
341        }
342    }
343
344    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
345            implements HandlerCaller.Callback {
346        private final HandlerCaller mCaller;
347
348        final IWallpaperConnection mConnection;
349        final IBinder mWindowToken;
350        int mReqWidth;
351        int mReqHeight;
352
353        Engine mEngine;
354
355        IWallpaperEngineWrapper(WallpaperService context,
356                IWallpaperConnection conn, IBinder windowToken,
357                int reqWidth, int reqHeight) {
358            mCaller = new HandlerCaller(context, this);
359            mConnection = conn;
360            mWindowToken = windowToken;
361            mReqWidth = reqWidth;
362            mReqHeight = reqHeight;
363
364            try {
365                conn.attachEngine(this);
366            } catch (RemoteException e) {
367                destroy();
368            }
369
370            Message msg = mCaller.obtainMessage(DO_ATTACH);
371            mCaller.sendMessage(msg);
372        }
373
374        public void destroy() {
375            Message msg = mCaller.obtainMessage(DO_DETACH);
376            mCaller.sendMessage(msg);
377        }
378
379        public void executeMessage(Message message) {
380            switch (message.what) {
381                case DO_ATTACH: {
382                    Engine engine = onCreateEngine();
383                    mEngine = engine;
384                    engine.attach(this);
385                    return;
386                }
387                case DO_DETACH: {
388                    mEngine.detach();
389                    return;
390                }
391                case MSG_UPDATE_SURFACE:
392                    mEngine.updateSurface(false);
393                    break;
394                case MSG_VISIBILITY_CHANGED:
395                    if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
396                            + ": " + message.arg1);
397                    mEngine.onVisibilityChanged(message.arg1 != 0);
398                    break;
399                default :
400                    Log.w(TAG, "Unknown message type " + message.what);
401            }
402        }
403    }
404
405    /**
406     * Implements the internal {@link IWallpaperService} interface to convert
407     * incoming calls to it back to calls on an {@link WallpaperService}.
408     */
409    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
410        private final WallpaperService mTarget;
411
412        public IWallpaperServiceWrapper(WallpaperService context) {
413            mTarget = context;
414        }
415
416        public void attach(IWallpaperConnection conn,
417                IBinder windowToken, int reqWidth, int reqHeight) {
418            new IWallpaperEngineWrapper(
419                    mTarget, conn, windowToken, reqWidth, reqHeight);
420        }
421    }
422
423    /**
424     * Implement to return the implementation of the internal accessibility
425     * service interface.  Subclasses should not override.
426     */
427    @Override
428    public final IBinder onBind(Intent intent) {
429        return new IWallpaperServiceWrapper(this);
430    }
431
432    public abstract Engine onCreateEngine();
433}
434