WallpaperService.java revision 4c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3
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.content.Intent;
25import android.graphics.Rect;
26import android.os.Handler;
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.ViewRoot;
36import android.view.WindowManager;
37import android.view.WindowManagerImpl;
38
39/**
40 * A wallpaper service is responsible for showing a live wallpaper behind
41 * applications that would like to sit on top of it.
42 * @hide Live Wallpaper
43 */
44public abstract class WallpaperService extends Service {
45    /**
46     * The {@link Intent} that must be declared as handled by the service.
47     */
48    public static final String SERVICE_INTERFACE =
49        "android.service.wallpaper.WallpaperService";
50
51    static final String TAG = "WallpaperService";
52    static final boolean DEBUG = true;
53
54    private static final int DO_ATTACH = 10;
55    private static final int DO_DETACH = 20;
56
57    private static final int MSG_UPDATE_SURFACE = 10000;
58
59    /**
60     * The actual implementation of a wallpaper.  A wallpaper service may
61     * have multiple instances running (for example as a real wallpaper
62     * and as a preview), each of which is represented by its own Engine
63     * instance.
64     */
65    public class Engine {
66        IWallpaperEngineWrapper mIWallpaperEngine;
67
68        // Copies from mIWallpaperEngine.
69        HandlerCaller mCaller;
70        IWallpaperConnection mConnection;
71        IBinder mWindowToken;
72
73        boolean mInitializing = true;
74
75        // Current window state.
76        boolean mCreated;
77        boolean mIsCreating;
78        boolean mDrawingAllowed;
79        int mWidth;
80        int mHeight;
81        int mFormat;
82        int mType;
83        boolean mDestroyReportNeeded;
84        final Rect mVisibleInsets = new Rect();
85        final Rect mWinFrame = new Rect();
86        final Rect mContentInsets = new Rect();
87
88        final WindowManager.LayoutParams mLayout
89                = new WindowManager.LayoutParams();
90        IWindowSession mSession;
91
92        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
93
94            @Override
95            public boolean onAllowLockCanvas() {
96                return mDrawingAllowed;
97            }
98
99            @Override
100            public void onRelayoutContainer() {
101                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
102                mCaller.sendMessage(msg);
103            }
104
105            @Override
106            public void onUpdateSurface() {
107                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
108                mCaller.sendMessage(msg);
109            }
110
111            public boolean isCreating() {
112                return mIsCreating;
113            }
114
115            public void setKeepScreenOn(boolean screenOn) {
116                // Ignore.
117            }
118
119        };
120
121        final BaseIWindow mWindow = new BaseIWindow() {
122
123        };
124
125        public void onAttach(SurfaceHolder surfaceHolder) {
126        }
127
128        public void onDetach() {
129        }
130
131        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
132        }
133
134        public void onSurfaceCreated(SurfaceHolder holder) {
135        }
136
137        public void onSurfaceDestroyed(SurfaceHolder holder) {
138        }
139
140        void updateSurface(boolean force) {
141            int myWidth = mSurfaceHolder.getRequestedWidth();
142            if (myWidth <= 0) myWidth = mIWallpaperEngine.mReqWidth;
143            int myHeight = mSurfaceHolder.getRequestedHeight();
144            if (myHeight <= 0) myHeight = mIWallpaperEngine.mReqHeight;
145
146            final boolean creating = !mCreated;
147            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
148            final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
149            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
150            if (force || creating || formatChanged || sizeChanged || typeChanged) {
151
152                if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
153                        + " format=" + formatChanged + " size=" + sizeChanged);
154
155                try {
156                    mWidth = myWidth;
157                    mHeight = myHeight;
158                    mFormat = mSurfaceHolder.getRequestedFormat();
159                    mType = mSurfaceHolder.getRequestedType();
160
161                    // Scaling/Translate window's layout here because mLayout is not used elsewhere.
162
163                    // Places the window relative
164                    mLayout.x = 0;
165                    mLayout.y = 0;
166                    mLayout.width = myWidth;
167                    mLayout.height = myHeight;
168
169                    mLayout.format = mFormat;
170                    mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
171                                  | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
172                                  | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
173                                  | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
174                                  ;
175
176                    mLayout.memoryType = mType;
177                    mLayout.token = mWindowToken;
178
179                    if (!mCreated) {
180                        mLayout.type = WindowManager.LayoutParams.TYPE_WALLPAPER;
181                        mLayout.gravity = Gravity.LEFT|Gravity.TOP;
182                        mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
183                    }
184
185                    mSurfaceHolder.mSurfaceLock.lock();
186                    mDrawingAllowed = true;
187
188                    final int relayoutResult = mSession.relayout(
189                        mWindow, mLayout, mWidth, mHeight,
190                            View.VISIBLE, false, mWinFrame, mContentInsets,
191                            mVisibleInsets, mSurfaceHolder.mSurface);
192
193                    if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface
194                            + ", frame=" + mWinFrame);
195
196                    mSurfaceHolder.mSurfaceLock.unlock();
197
198                    try {
199                        mDestroyReportNeeded = true;
200
201                        SurfaceHolder.Callback callbacks[] = null;
202                        synchronized (mSurfaceHolder.mCallbacks) {
203                            final int N = mSurfaceHolder.mCallbacks.size();
204                            if (N > 0) {
205                                callbacks = new SurfaceHolder.Callback[N];
206                                mSurfaceHolder.mCallbacks.toArray(callbacks);
207                            }
208                        }
209
210                        if (!mCreated) {
211                            mIsCreating = true;
212                            onSurfaceCreated(mSurfaceHolder);
213                            if (callbacks != null) {
214                                for (SurfaceHolder.Callback c : callbacks) {
215                                    c.surfaceCreated(mSurfaceHolder);
216                                }
217                            }
218                        }
219                        if (creating || formatChanged || sizeChanged) {
220                            onSurfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
221                            if (callbacks != null) {
222                                for (SurfaceHolder.Callback c : callbacks) {
223                                    c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
224                                }
225                            }
226                        }
227                    } finally {
228                        mIsCreating = false;
229                        mCreated = true;
230                        if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
231                            mSession.finishDrawing(mWindow);
232                        }
233                    }
234                } catch (RemoteException ex) {
235                }
236                if (DEBUG) Log.v(
237                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
238                    " w=" + mLayout.width + " h=" + mLayout.height);
239            }
240        }
241
242        void attach(IWallpaperEngineWrapper wrapper) {
243            mIWallpaperEngine = wrapper;
244            mCaller = wrapper.mCaller;
245            mConnection = wrapper.mConnection;
246            mWindowToken = wrapper.mWindowToken;
247            mSurfaceHolder.setSizeFromLayout();
248            mInitializing = true;
249            mSession = ViewRoot.getWindowSession(getMainLooper());
250            mWindow.setSession(mSession);
251
252            onAttach(mSurfaceHolder);
253
254            mInitializing = false;
255            updateSurface(false);
256        }
257
258        void detach() {
259            onDetach();
260            if (mDestroyReportNeeded) {
261                mDestroyReportNeeded = false;
262                SurfaceHolder.Callback callbacks[];
263                synchronized (mSurfaceHolder.mCallbacks) {
264                    callbacks = new SurfaceHolder.Callback[
265                            mSurfaceHolder.mCallbacks.size()];
266                    mSurfaceHolder.mCallbacks.toArray(callbacks);
267                }
268                for (SurfaceHolder.Callback c : callbacks) {
269                    c.surfaceDestroyed(mSurfaceHolder);
270                }
271            }
272            if (mCreated) {
273                try {
274                    mSession.remove(mWindow);
275                } catch (RemoteException e) {
276                }
277                mSurfaceHolder.mSurface.clear();
278                mCreated = false;
279            }
280        }
281    }
282
283    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
284            implements HandlerCaller.Callback {
285        private final HandlerCaller mCaller;
286
287        final IWallpaperConnection mConnection;
288        final IBinder mWindowToken;
289        int mReqWidth;
290        int mReqHeight;
291
292        Engine mEngine;
293
294        IWallpaperEngineWrapper(WallpaperService context,
295                IWallpaperConnection conn, IBinder windowToken,
296                int reqWidth, int reqHeight) {
297            mCaller = new HandlerCaller(context, this);
298            mConnection = conn;
299            mWindowToken = windowToken;
300            mReqWidth = reqWidth;
301            mReqHeight = reqHeight;
302
303            try {
304                conn.attachEngine(this);
305            } catch (RemoteException e) {
306                destroy();
307            }
308
309            Message msg = mCaller.obtainMessage(DO_ATTACH);
310            mCaller.sendMessage(msg);
311        }
312
313        public void destroy() {
314            Message msg = mCaller.obtainMessage(DO_DETACH);
315            mCaller.sendMessage(msg);
316        }
317
318        public void executeMessage(Message message) {
319            switch (message.what) {
320                case DO_ATTACH: {
321                    Engine engine = onCreateEngine();
322                    mEngine = engine;
323                    engine.attach(this);
324                    return;
325                }
326                case DO_DETACH: {
327                    mEngine.detach();
328                    return;
329                }
330                case MSG_UPDATE_SURFACE:
331                    mEngine.updateSurface(false);
332                    break;
333                default :
334                    Log.w(TAG, "Unknown message type " + message.what);
335            }
336        }
337    }
338
339    /**
340     * Implements the internal {@link IWallpaperService} interface to convert
341     * incoming calls to it back to calls on an {@link WallpaperService}.
342     */
343    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
344        private final WallpaperService mTarget;
345
346        public IWallpaperServiceWrapper(WallpaperService context) {
347            mTarget = context;
348        }
349
350        public void attach(IWallpaperConnection conn,
351                IBinder windowToken, int reqWidth, int reqHeight) {
352            new IWallpaperEngineWrapper(
353                    mTarget, conn, windowToken, reqWidth, reqHeight);
354        }
355    }
356
357    /**
358     * Implement to return the implementation of the internal accessibility
359     * service interface.  Subclasses should not override.
360     */
361    @Override
362    public final IBinder onBind(Intent intent) {
363        return new IWallpaperServiceWrapper(this);
364    }
365
366    public abstract Engine onCreateEngine();
367}
368