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