WallpaperService.java revision 3be63c09309b21c01b535271625d4c39045690e5
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.MotionEvent;
34import android.view.SurfaceHolder;
35import android.view.View;
36import android.view.ViewGroup;
37import android.view.ViewRoot;
38import android.view.WindowManager;
39import android.view.WindowManagerImpl;
40
41/**
42 * A wallpaper service is responsible for showing a live wallpaper behind
43 * applications that would like to sit on top of it.
44 * @hide Live Wallpaper
45 */
46public abstract class WallpaperService extends Service {
47    /**
48     * The {@link Intent} that must be declared as handled by the service.
49     */
50    public static final String SERVICE_INTERFACE =
51        "android.service.wallpaper.WallpaperService";
52
53    static final String TAG = "WallpaperService";
54    static final boolean DEBUG = false;
55
56    private static final int DO_ATTACH = 10;
57    private static final int DO_DETACH = 20;
58
59    private static final int MSG_UPDATE_SURFACE = 10000;
60    private static final int MSG_VISIBILITY_CHANGED = 10010;
61    private static final int MSG_WALLPAPER_OFFSETS = 10020;
62    private static final int MSG_WINDOW_RESIZED = 10030;
63    private static final int MSG_TOUCH_EVENT = 10040;
64
65    /**
66     * The actual implementation of a wallpaper.  A wallpaper service may
67     * have multiple instances running (for example as a real wallpaper
68     * and as a preview), each of which is represented by its own Engine
69     * instance.  You must implement {@link WallpaperService#onCreateEngine()}
70     * to return your concrete Engine implementation.
71     */
72    public class Engine {
73        IWallpaperEngineWrapper mIWallpaperEngine;
74
75        // Copies from mIWallpaperEngine.
76        HandlerCaller mCaller;
77        IWallpaperConnection mConnection;
78        IBinder mWindowToken;
79
80        boolean mInitializing = true;
81
82        // Current window state.
83        boolean mCreated;
84        boolean mIsCreating;
85        boolean mDrawingAllowed;
86        int mWidth;
87        int mHeight;
88        int mFormat;
89        int mType;
90        int mCurWidth;
91        int mCurHeight;
92        int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
93        int mCurWindowFlags = mWindowFlags;
94        boolean mDestroyReportNeeded;
95        final Rect mVisibleInsets = new Rect();
96        final Rect mWinFrame = new Rect();
97        final Rect mContentInsets = new Rect();
98
99        final WindowManager.LayoutParams mLayout
100                = new WindowManager.LayoutParams();
101        IWindowSession mSession;
102
103        final Object mLock = new Object();
104        boolean mOffsetMessageEnqueued;
105        float mPendingXOffset;
106        float mPendingYOffset;
107        MotionEvent mPendingMove;
108
109        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
110
111            @Override
112            public boolean onAllowLockCanvas() {
113                return mDrawingAllowed;
114            }
115
116            @Override
117            public void onRelayoutContainer() {
118                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
119                mCaller.sendMessage(msg);
120            }
121
122            @Override
123            public void onUpdateSurface() {
124                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
125                mCaller.sendMessage(msg);
126            }
127
128            public boolean isCreating() {
129                return mIsCreating;
130            }
131
132            public void setKeepScreenOn(boolean screenOn) {
133                // Ignore.
134            }
135
136        };
137
138        final BaseIWindow mWindow = new BaseIWindow() {
139            @Override
140            public boolean onDispatchPointer(MotionEvent event, long eventTime,
141                    boolean callWhenDone) {
142                synchronized (mLock) {
143                    if (event.getAction() == MotionEvent.ACTION_MOVE) {
144                        if (mPendingMove != null) {
145                            mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
146                            mPendingMove.recycle();
147                        }
148                        mPendingMove = event;
149                    } else {
150                        mPendingMove = null;
151                    }
152                    Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT,
153                            event);
154                    mCaller.sendMessage(msg);
155                }
156                return false;
157            }
158
159            @Override
160            public void resized(int w, int h, Rect coveredInsets,
161                    Rect visibleInsets, boolean reportDraw) {
162                Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
163                        reportDraw ? 1 : 0);
164                mCaller.sendMessage(msg);
165            }
166
167            @Override
168            public void dispatchAppVisibility(boolean visible) {
169                Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
170                        visible ? 1 : 0);
171                mCaller.sendMessage(msg);
172            }
173
174            @Override
175            public void dispatchWallpaperOffsets(float x, float y) {
176                synchronized (mLock) {
177                    mPendingXOffset = x;
178                    mPendingYOffset = y;
179                    if (!mOffsetMessageEnqueued) {
180                        mOffsetMessageEnqueued = true;
181                        Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
182                        mCaller.sendMessage(msg);
183                    }
184                }
185            }
186
187        };
188
189        /**
190         * Provides access to the surface in which this wallpaper is drawn.
191         */
192        public SurfaceHolder getSurfaceHolder() {
193            return mSurfaceHolder;
194        }
195
196        /**
197         * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
198         * WallpaperManager.getDesiredMinimumWidth()}, returning the width
199         * that the system would like this wallpaper to run in.
200         */
201        public int getDesiredMinimumWidth() {
202            return mIWallpaperEngine.mReqWidth;
203        }
204
205        /**
206         * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
207         * WallpaperManager.getDesiredMinimumHeight()}, returning the height
208         * that the system would like this wallpaper to run in.
209         */
210        public int getDesiredMinimumHeight() {
211            return mIWallpaperEngine.mReqHeight;
212        }
213
214        /**
215         * Returns true if this engine is running in preview mode -- that is,
216         * it is being shown to the user before they select it as the actual
217         * wallpaper.
218         */
219        public boolean isPreview() {
220            return mIWallpaperEngine.mIsPreview;
221        }
222
223        /**
224         * Control whether this wallpaper will receive raw touch events
225         * from the window manager as the user interacts with the window
226         * that is currently displaying the wallpaper.  By default they
227         * are turned off.  If enabled, the events will be received in
228         * {@link #onTouchEvent(MotionEvent)}.
229         */
230        public void setTouchEventsEnabled(boolean enabled) {
231            mWindowFlags = enabled
232                    ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
233                    : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
234            if (mCreated) {
235                updateSurface(false, false);
236            }
237        }
238
239        /**
240         * Called once to initialize the engine.  After returning, the
241         * engine's surface will be created by the framework.
242         */
243        public void onCreate(SurfaceHolder surfaceHolder) {
244        }
245
246        /**
247         * Called right before the engine is going away.  After this the
248         * surface will be destroyed and this Engine object is no longer
249         * valid.
250         */
251        public void onDestroy() {
252        }
253
254        /**
255         * Called to inform you of the wallpaper becoming visible or
256         * hidden.  <em>It is very important that a wallpaper only use
257         * CPU while it is visible.</em>.
258         */
259        public void onVisibilityChanged(boolean visible) {
260        }
261
262        /**
263         * Called as the user performs touch-screen interaction with the
264         * window that is currently showing this wallpaper.  Note that the
265         * events you receive here are driven by the actual application the
266         * user is interacting with, so if it is slow you will get viewer
267         * move events.
268         */
269        public void onTouchEvent(MotionEvent event) {
270        }
271
272        /**
273         * Called to inform you of the wallpaper's offsets changing
274         * within its contain, corresponding to the container's
275         * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
276         * WallpaperManager.setWallpaperOffsets()}.
277         */
278        public void onOffsetsChanged(float xOffset, float yOffset,
279                int xPixelOffset, int yPixelOffset) {
280        }
281
282        /**
283         * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
284         * SurfaceHolder.Callback.surfaceChanged()}.
285         */
286        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
287        }
288
289        /**
290         * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
291         * SurfaceHolder.Callback.surfaceCreated()}.
292         */
293        public void onSurfaceCreated(SurfaceHolder holder) {
294        }
295
296        /**
297         * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
298         * SurfaceHolder.Callback.surfaceDestroyed()}.
299         */
300        public void onSurfaceDestroyed(SurfaceHolder holder) {
301        }
302
303        void updateSurface(boolean forceRelayout, boolean forceReport) {
304            int myWidth = mSurfaceHolder.getRequestedWidth();
305            if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT;
306            int myHeight = mSurfaceHolder.getRequestedHeight();
307            if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT;
308
309            final boolean creating = !mCreated;
310            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
311            boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
312            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
313            final boolean flagsChanged = mCurWindowFlags != mWindowFlags;
314            if (forceRelayout || creating || formatChanged || sizeChanged
315                    || typeChanged || flagsChanged) {
316
317                if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
318                        + " format=" + formatChanged + " size=" + sizeChanged);
319
320                try {
321                    mWidth = myWidth;
322                    mHeight = myHeight;
323                    mFormat = mSurfaceHolder.getRequestedFormat();
324                    mType = mSurfaceHolder.getRequestedType();
325
326                    mLayout.x = 0;
327                    mLayout.y = 0;
328                    mLayout.width = myWidth;
329                    mLayout.height = myHeight;
330
331                    mLayout.format = mFormat;
332
333                    mCurWindowFlags = mWindowFlags;
334                    mLayout.flags = mWindowFlags
335                            | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
336                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
337                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
338                            ;
339
340                    mLayout.memoryType = mType;
341                    mLayout.token = mWindowToken;
342
343                    if (!mCreated) {
344                        mLayout.type = mIWallpaperEngine.mWindowType;
345                        mLayout.gravity = Gravity.LEFT|Gravity.TOP;
346                        mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
347                    }
348
349                    mSurfaceHolder.mSurfaceLock.lock();
350                    mDrawingAllowed = true;
351
352                    final int relayoutResult = mSession.relayout(
353                        mWindow, mLayout, mWidth, mHeight,
354                            View.VISIBLE, false, mWinFrame, mContentInsets,
355                            mVisibleInsets, mSurfaceHolder.mSurface);
356
357                    if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface
358                            + ", frame=" + mWinFrame);
359
360                    int w = mWinFrame.width();
361                    if (mCurWidth != w) {
362                        sizeChanged = true;
363                        mCurWidth = w;
364                    }
365                    int h = mWinFrame.height();
366                    if (mCurHeight != h) {
367                        sizeChanged = true;
368                        mCurHeight = h;
369                    }
370
371                    mSurfaceHolder.mSurfaceLock.unlock();
372
373                    try {
374                        mDestroyReportNeeded = true;
375
376                        SurfaceHolder.Callback callbacks[] = null;
377                        synchronized (mSurfaceHolder.mCallbacks) {
378                            final int N = mSurfaceHolder.mCallbacks.size();
379                            if (N > 0) {
380                                callbacks = new SurfaceHolder.Callback[N];
381                                mSurfaceHolder.mCallbacks.toArray(callbacks);
382                            }
383                        }
384
385                        if (!mCreated) {
386                            mIsCreating = true;
387                            onSurfaceCreated(mSurfaceHolder);
388                            if (callbacks != null) {
389                                for (SurfaceHolder.Callback c : callbacks) {
390                                    c.surfaceCreated(mSurfaceHolder);
391                                }
392                            }
393                        }
394                        if (forceReport || creating || formatChanged || sizeChanged) {
395                            if (DEBUG) {
396                                RuntimeException e = new RuntimeException();
397                                e.fillInStackTrace();
398                                Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
399                                        + " formatChanged=" + formatChanged
400                                        + " sizeChanged=" + sizeChanged, e);
401                            }
402                            onSurfaceChanged(mSurfaceHolder, mFormat,
403                                    mCurWidth, mCurHeight);
404                            if (callbacks != null) {
405                                for (SurfaceHolder.Callback c : callbacks) {
406                                    c.surfaceChanged(mSurfaceHolder, mFormat,
407                                            mCurWidth, mCurHeight);
408                                }
409                            }
410                        }
411                    } finally {
412                        mIsCreating = false;
413                        mCreated = true;
414                        if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
415                            mSession.finishDrawing(mWindow);
416                        }
417                    }
418                } catch (RemoteException ex) {
419                }
420                if (DEBUG) Log.v(
421                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
422                    " w=" + mLayout.width + " h=" + mLayout.height);
423            }
424        }
425
426        void attach(IWallpaperEngineWrapper wrapper) {
427            if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
428            mIWallpaperEngine = wrapper;
429            mCaller = wrapper.mCaller;
430            mConnection = wrapper.mConnection;
431            mWindowToken = wrapper.mWindowToken;
432            // XXX temp -- should run in size from layout (screen) mode.
433            mSurfaceHolder.setFixedSize(mIWallpaperEngine.mReqWidth,
434                    mIWallpaperEngine.mReqHeight);
435            //mSurfaceHolder.setSizeFromLayout();
436            mInitializing = true;
437            mSession = ViewRoot.getWindowSession(getMainLooper());
438            mWindow.setSession(mSession);
439
440            onCreate(mSurfaceHolder);
441
442            mInitializing = false;
443            updateSurface(false, false);
444        }
445
446        void detach() {
447            onDestroy();
448            if (mDestroyReportNeeded) {
449                mDestroyReportNeeded = false;
450                SurfaceHolder.Callback callbacks[];
451                synchronized (mSurfaceHolder.mCallbacks) {
452                    callbacks = new SurfaceHolder.Callback[
453                            mSurfaceHolder.mCallbacks.size()];
454                    mSurfaceHolder.mCallbacks.toArray(callbacks);
455                }
456                for (SurfaceHolder.Callback c : callbacks) {
457                    c.surfaceDestroyed(mSurfaceHolder);
458                }
459            }
460            if (mCreated) {
461                try {
462                    mSession.remove(mWindow);
463                } catch (RemoteException e) {
464                }
465                mSurfaceHolder.mSurface.clear();
466                mCreated = false;
467            }
468        }
469    }
470
471    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
472            implements HandlerCaller.Callback {
473        private final HandlerCaller mCaller;
474
475        final IWallpaperConnection mConnection;
476        final IBinder mWindowToken;
477        final int mWindowType;
478        final boolean mIsPreview;
479        int mReqWidth;
480        int mReqHeight;
481
482        Engine mEngine;
483
484        IWallpaperEngineWrapper(WallpaperService context,
485                IWallpaperConnection conn, IBinder windowToken,
486                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
487            mCaller = new HandlerCaller(context, this);
488            mConnection = conn;
489            mWindowToken = windowToken;
490            mWindowType = windowType;
491            mIsPreview = isPreview;
492            mReqWidth = reqWidth;
493            mReqHeight = reqHeight;
494
495            try {
496                conn.attachEngine(this);
497            } catch (RemoteException e) {
498                destroy();
499            }
500
501            Message msg = mCaller.obtainMessage(DO_ATTACH);
502            mCaller.sendMessage(msg);
503        }
504
505        public void destroy() {
506            Message msg = mCaller.obtainMessage(DO_DETACH);
507            mCaller.sendMessage(msg);
508        }
509
510        public void executeMessage(Message message) {
511            switch (message.what) {
512                case DO_ATTACH: {
513                    Engine engine = onCreateEngine();
514                    mEngine = engine;
515                    engine.attach(this);
516                    return;
517                }
518                case DO_DETACH: {
519                    mEngine.detach();
520                    return;
521                }
522                case MSG_UPDATE_SURFACE:
523                    mEngine.updateSurface(true, false);
524                    break;
525                case MSG_VISIBILITY_CHANGED:
526                    if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
527                            + ": " + message.arg1);
528                    mEngine.onVisibilityChanged(message.arg1 != 0);
529                    break;
530                case MSG_WALLPAPER_OFFSETS: {
531                    float xOffset;
532                    float yOffset;
533                    synchronized (mEngine.mLock) {
534                        xOffset = mEngine.mPendingXOffset;
535                        yOffset = mEngine.mPendingYOffset;
536                        mEngine.mOffsetMessageEnqueued = false;
537                    }
538                    if (DEBUG) Log.v(TAG, "Offsets change in " + mEngine
539                            + ": " + xOffset + "," + yOffset);
540                    final int availw = mReqWidth-mEngine.mCurWidth;
541                    final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
542                    final int availh = mReqHeight-mEngine.mCurHeight;
543                    final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
544                    mEngine.onOffsetsChanged(xOffset, yOffset, xPixels, yPixels);
545                } break;
546                case MSG_WINDOW_RESIZED: {
547                    final boolean reportDraw = message.arg1 != 0;
548                    mEngine.updateSurface(true, false);
549                    if (reportDraw) {
550                        try {
551                            mEngine.mSession.finishDrawing(mEngine.mWindow);
552                        } catch (RemoteException e) {
553                        }
554                    }
555                } break;
556                case MSG_TOUCH_EVENT: {
557                    MotionEvent ev = (MotionEvent)message.obj;
558                    synchronized (mEngine.mLock) {
559                        if (mEngine.mPendingMove == ev) {
560                            mEngine.mPendingMove = null;
561                        }
562                    }
563                    mEngine.onTouchEvent(ev);
564                    ev.recycle();
565                } break;
566                default :
567                    Log.w(TAG, "Unknown message type " + message.what);
568            }
569        }
570    }
571
572    /**
573     * Implements the internal {@link IWallpaperService} interface to convert
574     * incoming calls to it back to calls on an {@link WallpaperService}.
575     */
576    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
577        private final WallpaperService mTarget;
578
579        public IWallpaperServiceWrapper(WallpaperService context) {
580            mTarget = context;
581        }
582
583        public void attach(IWallpaperConnection conn, IBinder windowToken,
584                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
585            new IWallpaperEngineWrapper(mTarget, conn, windowToken,
586                    windowType, isPreview, reqWidth, reqHeight);
587        }
588    }
589
590    /**
591     * Implement to return the implementation of the internal accessibility
592     * service interface.  Subclasses should not override.
593     */
594    @Override
595    public final IBinder onBind(Intent intent) {
596        return new IWallpaperServiceWrapper(this);
597    }
598
599    public abstract Engine onCreateEngine();
600}
601