WallpaperService.java revision 98365d7663cbd82979a5700faf0050220b01084d
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.annotation.SdkConstant;
24import android.annotation.SdkConstant.SdkConstantType;
25import android.app.Service;
26import android.app.WallpaperManager;
27import android.content.BroadcastReceiver;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
31import android.content.res.Configuration;
32import android.graphics.PixelFormat;
33import android.graphics.Rect;
34import android.os.Bundle;
35import android.os.IBinder;
36import android.os.Looper;
37import android.os.Message;
38import android.os.PowerManager;
39import android.os.RemoteException;
40import android.util.Log;
41import android.util.LogPrinter;
42import android.view.Display;
43import android.view.Gravity;
44import android.view.IWindowSession;
45import android.view.InputChannel;
46import android.view.InputDevice;
47import android.view.InputEvent;
48import android.view.InputEventReceiver;
49import android.view.MotionEvent;
50import android.view.SurfaceHolder;
51import android.view.View;
52import android.view.ViewGroup;
53import android.view.WindowManager;
54import android.view.WindowManagerGlobal;
55
56import java.io.FileDescriptor;
57import java.io.PrintWriter;
58import java.util.ArrayList;
59
60/**
61 * A wallpaper service is responsible for showing a live wallpaper behind
62 * applications that would like to sit on top of it.  This service object
63 * itself does very little -- its only purpose is to generate instances of
64 * {@link Engine} as needed.  Implementing a wallpaper thus
65 * involves subclassing from this, subclassing an Engine implementation,
66 * and implementing {@link #onCreateEngine()} to return a new instance of
67 * your engine.
68 */
69public abstract class WallpaperService extends Service {
70    /**
71     * The {@link Intent} that must be declared as handled by the service.
72     * To be supported, the service must also require the
73     * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
74     * that other applications can not abuse it.
75     */
76    @SdkConstant(SdkConstantType.SERVICE_ACTION)
77    public static final String SERVICE_INTERFACE =
78            "android.service.wallpaper.WallpaperService";
79
80    /**
81     * Name under which a WallpaperService component publishes information
82     * about itself.  This meta-data must reference an XML resource containing
83     * a <code>&lt;{@link android.R.styleable#Wallpaper wallpaper}&gt;</code>
84     * tag.
85     */
86    public static final String SERVICE_META_DATA = "android.service.wallpaper";
87
88    static final String TAG = "WallpaperService";
89    static final boolean DEBUG = false;
90
91    private static final int DO_ATTACH = 10;
92    private static final int DO_DETACH = 20;
93    private static final int DO_SET_DESIRED_SIZE = 30;
94
95    private static final int MSG_UPDATE_SURFACE = 10000;
96    private static final int MSG_VISIBILITY_CHANGED = 10010;
97    private static final int MSG_WALLPAPER_OFFSETS = 10020;
98    private static final int MSG_WALLPAPER_COMMAND = 10025;
99    private static final int MSG_WINDOW_RESIZED = 10030;
100    private static final int MSG_WINDOW_MOVED = 10035;
101    private static final int MSG_TOUCH_EVENT = 10040;
102
103    private Looper mCallbackLooper;
104    private final ArrayList<Engine> mActiveEngines
105            = new ArrayList<Engine>();
106
107    static final class WallpaperCommand {
108        String action;
109        int x;
110        int y;
111        int z;
112        Bundle extras;
113        boolean sync;
114    }
115
116    /**
117     * The actual implementation of a wallpaper.  A wallpaper service may
118     * have multiple instances running (for example as a real wallpaper
119     * and as a preview), each of which is represented by its own Engine
120     * instance.  You must implement {@link WallpaperService#onCreateEngine()}
121     * to return your concrete Engine implementation.
122     */
123    public class Engine {
124        IWallpaperEngineWrapper mIWallpaperEngine;
125
126        // Copies from mIWallpaperEngine.
127        HandlerCaller mCaller;
128        IWallpaperConnection mConnection;
129        IBinder mWindowToken;
130
131        boolean mInitializing = true;
132        boolean mVisible;
133        boolean mScreenOn = true;
134        boolean mReportedVisible;
135        boolean mDestroyed;
136
137        // Current window state.
138        boolean mCreated;
139        boolean mSurfaceCreated;
140        boolean mIsCreating;
141        boolean mDrawingAllowed;
142        boolean mOffsetsChanged;
143        boolean mFixedSizeAllowed;
144        int mWidth;
145        int mHeight;
146        int mFormat;
147        int mType;
148        int mCurWidth;
149        int mCurHeight;
150        int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
151        int mWindowPrivateFlags =
152                WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
153        int mCurWindowFlags = mWindowFlags;
154        int mCurWindowPrivateFlags = mWindowPrivateFlags;
155        final Rect mVisibleInsets = new Rect();
156        final Rect mWinFrame = new Rect();
157        final Rect mContentInsets = new Rect();
158        final Configuration mConfiguration = new Configuration();
159
160        final WindowManager.LayoutParams mLayout
161                = new WindowManager.LayoutParams();
162        IWindowSession mSession;
163        InputChannel mInputChannel;
164
165        final Object mLock = new Object();
166        boolean mOffsetMessageEnqueued;
167        float mPendingXOffset;
168        float mPendingYOffset;
169        float mPendingXOffsetStep;
170        float mPendingYOffsetStep;
171        boolean mPendingSync;
172        MotionEvent mPendingMove;
173
174        final BroadcastReceiver mReceiver = new BroadcastReceiver() {
175            @Override
176            public void onReceive(Context context, Intent intent) {
177                if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
178                    mScreenOn = true;
179                    reportVisibility();
180                } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
181                    mScreenOn = false;
182                    reportVisibility();
183                }
184            }
185        };
186
187        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
188            {
189                mRequestedFormat = PixelFormat.RGBX_8888;
190            }
191
192            @Override
193            public boolean onAllowLockCanvas() {
194                return mDrawingAllowed;
195            }
196
197            @Override
198            public void onRelayoutContainer() {
199                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
200                mCaller.sendMessage(msg);
201            }
202
203            @Override
204            public void onUpdateSurface() {
205                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
206                mCaller.sendMessage(msg);
207            }
208
209            public boolean isCreating() {
210                return mIsCreating;
211            }
212
213            @Override
214            public void setFixedSize(int width, int height) {
215                if (!mFixedSizeAllowed) {
216                    // Regular apps can't do this.  It can only work for
217                    // certain designs of window animations, so you can't
218                    // rely on it.
219                    throw new UnsupportedOperationException(
220                            "Wallpapers currently only support sizing from layout");
221                }
222                super.setFixedSize(width, height);
223            }
224
225            public void setKeepScreenOn(boolean screenOn) {
226                throw new UnsupportedOperationException(
227                        "Wallpapers do not support keep screen on");
228            }
229
230        };
231
232        final class WallpaperInputEventReceiver extends InputEventReceiver {
233            public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) {
234                super(inputChannel, looper);
235            }
236
237            @Override
238            public void onInputEvent(InputEvent event) {
239                boolean handled = false;
240                try {
241                    if (event instanceof MotionEvent
242                            && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
243                        MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event);
244                        dispatchPointer(dup);
245                        handled = true;
246                    }
247                } finally {
248                    finishInputEvent(event, handled);
249                }
250            }
251        }
252        WallpaperInputEventReceiver mInputEventReceiver;
253
254        final BaseIWindow mWindow = new BaseIWindow() {
255            @Override
256            public void resized(Rect frame, Rect contentInsets,
257                    Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
258                Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
259                        reportDraw ? 1 : 0);
260                mCaller.sendMessage(msg);
261            }
262
263            @Override
264            public void moved(int newX, int newY) {
265                Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY);
266                mCaller.sendMessage(msg);
267            }
268
269            @Override
270            public void dispatchAppVisibility(boolean visible) {
271                // We don't do this in preview mode; we'll let the preview
272                // activity tell us when to run.
273                if (!mIWallpaperEngine.mIsPreview) {
274                    Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
275                            visible ? 1 : 0);
276                    mCaller.sendMessage(msg);
277                }
278            }
279
280            @Override
281            public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
282                    boolean sync) {
283                synchronized (mLock) {
284                    if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
285                    mPendingXOffset = x;
286                    mPendingYOffset = y;
287                    mPendingXOffsetStep = xStep;
288                    mPendingYOffsetStep = yStep;
289                    if (sync) {
290                        mPendingSync = true;
291                    }
292                    if (!mOffsetMessageEnqueued) {
293                        mOffsetMessageEnqueued = true;
294                        Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
295                        mCaller.sendMessage(msg);
296                    }
297                }
298            }
299
300            @Override
301            public void dispatchWallpaperCommand(String action, int x, int y,
302                    int z, Bundle extras, boolean sync) {
303                synchronized (mLock) {
304                    if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y);
305                    WallpaperCommand cmd = new WallpaperCommand();
306                    cmd.action = action;
307                    cmd.x = x;
308                    cmd.y = y;
309                    cmd.z = z;
310                    cmd.extras = extras;
311                    cmd.sync = sync;
312                    Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND);
313                    msg.obj = cmd;
314                    mCaller.sendMessage(msg);
315                }
316            }
317        };
318
319        /**
320         * Provides access to the surface in which this wallpaper is drawn.
321         */
322        public SurfaceHolder getSurfaceHolder() {
323            return mSurfaceHolder;
324        }
325
326        /**
327         * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
328         * WallpaperManager.getDesiredMinimumWidth()}, returning the width
329         * that the system would like this wallpaper to run in.
330         */
331        public int getDesiredMinimumWidth() {
332            return mIWallpaperEngine.mReqWidth;
333        }
334
335        /**
336         * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
337         * WallpaperManager.getDesiredMinimumHeight()}, returning the height
338         * that the system would like this wallpaper to run in.
339         */
340        public int getDesiredMinimumHeight() {
341            return mIWallpaperEngine.mReqHeight;
342        }
343
344        /**
345         * Return whether the wallpaper is currently visible to the user,
346         * this is the last value supplied to
347         * {@link #onVisibilityChanged(boolean)}.
348         */
349        public boolean isVisible() {
350            return mReportedVisible;
351        }
352
353        /**
354         * Returns true if this engine is running in preview mode -- that is,
355         * it is being shown to the user before they select it as the actual
356         * wallpaper.
357         */
358        public boolean isPreview() {
359            return mIWallpaperEngine.mIsPreview;
360        }
361
362        /**
363         * Control whether this wallpaper will receive raw touch events
364         * from the window manager as the user interacts with the window
365         * that is currently displaying the wallpaper.  By default they
366         * are turned off.  If enabled, the events will be received in
367         * {@link #onTouchEvent(MotionEvent)}.
368         */
369        public void setTouchEventsEnabled(boolean enabled) {
370            mWindowFlags = enabled
371                    ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
372                    : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
373            if (mCreated) {
374                updateSurface(false, false, false);
375            }
376        }
377
378        /**
379         * Control whether this wallpaper will receive notifications when the wallpaper
380         * has been scrolled. By default, wallpapers will receive notifications, although
381         * the default static image wallpapers do not. It is a performance optimization to
382         * set this to false.
383         *
384         * @param enabled whether the wallpaper wants to receive offset notifications
385         */
386        public void setOffsetNotificationsEnabled(boolean enabled) {
387            mWindowPrivateFlags = enabled
388                    ? (mWindowPrivateFlags |
389                        WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS)
390                    : (mWindowPrivateFlags &
391                        ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS);
392            if (mCreated) {
393                updateSurface(false, false, false);
394            }
395        }
396
397        /** {@hide} */
398        public void setFixedSizeAllowed(boolean allowed) {
399            mFixedSizeAllowed = allowed;
400        }
401
402        /**
403         * Called once to initialize the engine.  After returning, the
404         * engine's surface will be created by the framework.
405         */
406        public void onCreate(SurfaceHolder surfaceHolder) {
407        }
408
409        /**
410         * Called right before the engine is going away.  After this the
411         * surface will be destroyed and this Engine object is no longer
412         * valid.
413         */
414        public void onDestroy() {
415        }
416
417        /**
418         * Called to inform you of the wallpaper becoming visible or
419         * hidden.  <em>It is very important that a wallpaper only use
420         * CPU while it is visible.</em>.
421         */
422        public void onVisibilityChanged(boolean visible) {
423        }
424
425        /**
426         * Called as the user performs touch-screen interaction with the
427         * window that is currently showing this wallpaper.  Note that the
428         * events you receive here are driven by the actual application the
429         * user is interacting with, so if it is slow you will get fewer
430         * move events.
431         */
432        public void onTouchEvent(MotionEvent event) {
433        }
434
435        /**
436         * Called to inform you of the wallpaper's offsets changing
437         * within its contain, corresponding to the container's
438         * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
439         * WallpaperManager.setWallpaperOffsets()}.
440         */
441        public void onOffsetsChanged(float xOffset, float yOffset,
442                float xOffsetStep, float yOffsetStep,
443                int xPixelOffset, int yPixelOffset) {
444        }
445
446        /**
447         * Process a command that was sent to the wallpaper with
448         * {@link WallpaperManager#sendWallpaperCommand}.
449         * The default implementation does nothing, and always returns null
450         * as the result.
451         *
452         * @param action The name of the command to perform.  This tells you
453         * what to do and how to interpret the rest of the arguments.
454         * @param x Generic integer parameter.
455         * @param y Generic integer parameter.
456         * @param z Generic integer parameter.
457         * @param extras Any additional parameters.
458         * @param resultRequested If true, the caller is requesting that
459         * a result, appropriate for the command, be returned back.
460         * @return If returning a result, create a Bundle and place the
461         * result data in to it.  Otherwise return null.
462         */
463        public Bundle onCommand(String action, int x, int y, int z,
464                Bundle extras, boolean resultRequested) {
465            return null;
466        }
467
468        /**
469         * Called when an application has changed the desired virtual size of
470         * the wallpaper.
471         */
472        public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
473        }
474
475        /**
476         * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
477         * SurfaceHolder.Callback.surfaceChanged()}.
478         */
479        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
480        }
481
482        /**
483         * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded
484         * SurfaceHolder.Callback.surfaceRedrawNeeded()}.
485         */
486        public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
487        }
488
489        /**
490         * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
491         * SurfaceHolder.Callback.surfaceCreated()}.
492         */
493        public void onSurfaceCreated(SurfaceHolder holder) {
494        }
495
496        /**
497         * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
498         * SurfaceHolder.Callback.surfaceDestroyed()}.
499         */
500        public void onSurfaceDestroyed(SurfaceHolder holder) {
501        }
502
503        protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
504            out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
505                    out.print(" mDestroyed="); out.println(mDestroyed);
506            out.print(prefix); out.print("mVisible="); out.print(mVisible);
507                    out.print(" mScreenOn="); out.print(mScreenOn);
508                    out.print(" mReportedVisible="); out.println(mReportedVisible);
509            out.print(prefix); out.print("mCreated="); out.print(mCreated);
510                    out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
511                    out.print(" mIsCreating="); out.print(mIsCreating);
512                    out.print(" mDrawingAllowed="); out.println(mDrawingAllowed);
513            out.print(prefix); out.print("mWidth="); out.print(mWidth);
514                    out.print(" mCurWidth="); out.print(mCurWidth);
515                    out.print(" mHeight="); out.print(mHeight);
516                    out.print(" mCurHeight="); out.println(mCurHeight);
517            out.print(prefix); out.print("mType="); out.print(mType);
518                    out.print(" mWindowFlags="); out.print(mWindowFlags);
519                    out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
520                    out.print(" mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
521                    out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags);
522            out.print(prefix); out.print("mVisibleInsets=");
523                    out.print(mVisibleInsets.toShortString());
524                    out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
525                    out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
526            out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration);
527            out.print(prefix); out.print("mLayout="); out.println(mLayout);
528            synchronized (mLock) {
529                out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
530                        out.print(" mPendingXOffset="); out.println(mPendingXOffset);
531                out.print(prefix); out.print("mPendingXOffsetStep=");
532                        out.print(mPendingXOffsetStep);
533                        out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep);
534                out.print(prefix); out.print("mOffsetMessageEnqueued=");
535                        out.print(mOffsetMessageEnqueued);
536                        out.print(" mPendingSync="); out.println(mPendingSync);
537                if (mPendingMove != null) {
538                    out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove);
539                }
540            }
541        }
542
543        private void dispatchPointer(MotionEvent event) {
544            if (event.isTouchEvent()) {
545                synchronized (mLock) {
546                    if (event.getAction() == MotionEvent.ACTION_MOVE) {
547                        mPendingMove = event;
548                    } else {
549                        mPendingMove = null;
550                    }
551                }
552                Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
553                mCaller.sendMessage(msg);
554            } else {
555                event.recycle();
556            }
557        }
558
559        void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
560            if (mDestroyed) {
561                Log.w(TAG, "Ignoring updateSurface: destroyed");
562            }
563
564            int myWidth = mSurfaceHolder.getRequestedWidth();
565            if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
566            int myHeight = mSurfaceHolder.getRequestedHeight();
567            if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
568
569            final boolean creating = !mCreated;
570            final boolean surfaceCreating = !mSurfaceCreated;
571            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
572            boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
573            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
574            final boolean flagsChanged = mCurWindowFlags != mWindowFlags ||
575                    mCurWindowPrivateFlags != mWindowPrivateFlags;
576            if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
577                    || typeChanged || flagsChanged || redrawNeeded) {
578
579                if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
580                        + " format=" + formatChanged + " size=" + sizeChanged);
581
582                try {
583                    mWidth = myWidth;
584                    mHeight = myHeight;
585                    mFormat = mSurfaceHolder.getRequestedFormat();
586                    mType = mSurfaceHolder.getRequestedType();
587
588                    mLayout.x = 0;
589                    mLayout.y = 0;
590                    mLayout.width = myWidth;
591                    mLayout.height = myHeight;
592
593                    mLayout.format = mFormat;
594
595                    mCurWindowFlags = mWindowFlags;
596                    mLayout.flags = mWindowFlags
597                            | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
598                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
599                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
600                            ;
601                    mCurWindowPrivateFlags = mWindowPrivateFlags;
602                    mLayout.privateFlags = mWindowPrivateFlags;
603
604                    mLayout.memoryType = mType;
605                    mLayout.token = mWindowToken;
606
607                    if (!mCreated) {
608                        mLayout.type = mIWallpaperEngine.mWindowType;
609                        mLayout.gravity = Gravity.START|Gravity.TOP;
610                        mLayout.setTitle(WallpaperService.this.getClass().getName());
611                        mLayout.windowAnimations =
612                                com.android.internal.R.style.Animation_Wallpaper;
613                        mInputChannel = new InputChannel();
614                        if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
615                            Display.DEFAULT_DISPLAY, mContentInsets, mInputChannel) < 0) {
616                            Log.w(TAG, "Failed to add window while updating wallpaper surface.");
617                            return;
618                        }
619                        mCreated = true;
620
621                        mInputEventReceiver = new WallpaperInputEventReceiver(
622                                mInputChannel, Looper.myLooper());
623                    }
624
625                    mSurfaceHolder.mSurfaceLock.lock();
626                    mDrawingAllowed = true;
627
628                    final int relayoutResult = mSession.relayout(
629                        mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
630                            View.VISIBLE, 0, mWinFrame, mContentInsets,
631                            mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
632
633                    if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
634                            + ", frame=" + mWinFrame);
635
636                    int w = mWinFrame.width();
637                    if (mCurWidth != w) {
638                        sizeChanged = true;
639                        mCurWidth = w;
640                    }
641                    int h = mWinFrame.height();
642                    if (mCurHeight != h) {
643                        sizeChanged = true;
644                        mCurHeight = h;
645                    }
646
647                    mSurfaceHolder.setSurfaceFrameSize(w, h);
648                    mSurfaceHolder.mSurfaceLock.unlock();
649
650                    if (!mSurfaceHolder.mSurface.isValid()) {
651                        reportSurfaceDestroyed();
652                        if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
653                        return;
654                    }
655
656                    boolean didSurface = false;
657
658                    try {
659                        mSurfaceHolder.ungetCallbacks();
660
661                        if (surfaceCreating) {
662                            mIsCreating = true;
663                            didSurface = true;
664                            if (DEBUG) Log.v(TAG, "onSurfaceCreated("
665                                    + mSurfaceHolder + "): " + this);
666                            onSurfaceCreated(mSurfaceHolder);
667                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
668                            if (callbacks != null) {
669                                for (SurfaceHolder.Callback c : callbacks) {
670                                    c.surfaceCreated(mSurfaceHolder);
671                                }
672                            }
673                        }
674
675                        redrawNeeded |= creating || (relayoutResult
676                                & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
677
678                        if (forceReport || creating || surfaceCreating
679                                || formatChanged || sizeChanged) {
680                            if (DEBUG) {
681                                RuntimeException e = new RuntimeException();
682                                e.fillInStackTrace();
683                                Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
684                                        + " formatChanged=" + formatChanged
685                                        + " sizeChanged=" + sizeChanged, e);
686                            }
687                            if (DEBUG) Log.v(TAG, "onSurfaceChanged("
688                                    + mSurfaceHolder + ", " + mFormat
689                                    + ", " + mCurWidth + ", " + mCurHeight
690                                    + "): " + this);
691                            didSurface = true;
692                            onSurfaceChanged(mSurfaceHolder, mFormat,
693                                    mCurWidth, mCurHeight);
694                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
695                            if (callbacks != null) {
696                                for (SurfaceHolder.Callback c : callbacks) {
697                                    c.surfaceChanged(mSurfaceHolder, mFormat,
698                                            mCurWidth, mCurHeight);
699                                }
700                            }
701                        }
702
703                        if (redrawNeeded) {
704                            onSurfaceRedrawNeeded(mSurfaceHolder);
705                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
706                            if (callbacks != null) {
707                                for (SurfaceHolder.Callback c : callbacks) {
708                                    if (c instanceof SurfaceHolder.Callback2) {
709                                        ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
710                                                mSurfaceHolder);
711                                    }
712                                }
713                            }
714                        }
715
716                        if (didSurface && !mReportedVisible) {
717                            // This wallpaper is currently invisible, but its
718                            // surface has changed.  At this point let's tell it
719                            // again that it is invisible in case the report about
720                            // the surface caused it to start running.  We really
721                            // don't want wallpapers running when not visible.
722                            if (mIsCreating) {
723                                // Some wallpapers will ignore this call if they
724                                // had previously been told they were invisble,
725                                // so if we are creating a new surface then toggle
726                                // the state to get them to notice.
727                                if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
728                                        + this);
729                                onVisibilityChanged(true);
730                            }
731                            if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
732                                        + this);
733                            onVisibilityChanged(false);
734                        }
735
736                    } finally {
737                        mIsCreating = false;
738                        mSurfaceCreated = true;
739                        if (redrawNeeded) {
740                            mSession.finishDrawing(mWindow);
741                        }
742                    }
743                } catch (RemoteException ex) {
744                }
745                if (DEBUG) Log.v(
746                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
747                    " w=" + mLayout.width + " h=" + mLayout.height);
748            }
749        }
750
751        void attach(IWallpaperEngineWrapper wrapper) {
752            if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
753            if (mDestroyed) {
754                return;
755            }
756
757            mIWallpaperEngine = wrapper;
758            mCaller = wrapper.mCaller;
759            mConnection = wrapper.mConnection;
760            mWindowToken = wrapper.mWindowToken;
761            mSurfaceHolder.setSizeFromLayout();
762            mInitializing = true;
763            mSession = WindowManagerGlobal.getWindowSession(getMainLooper());
764
765            mWindow.setSession(mSession);
766
767            mScreenOn = ((PowerManager)getSystemService(Context.POWER_SERVICE)).isScreenOn();
768
769            IntentFilter filter = new IntentFilter();
770            filter.addAction(Intent.ACTION_SCREEN_ON);
771            filter.addAction(Intent.ACTION_SCREEN_OFF);
772            registerReceiver(mReceiver, filter);
773
774            if (DEBUG) Log.v(TAG, "onCreate(): " + this);
775            onCreate(mSurfaceHolder);
776
777            mInitializing = false;
778            mReportedVisible = false;
779            updateSurface(false, false, false);
780        }
781
782        void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
783            if (!mDestroyed) {
784                if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
785                        + desiredWidth + "," + desiredHeight + "): " + this);
786                mIWallpaperEngine.mReqWidth = desiredWidth;
787                mIWallpaperEngine.mReqHeight = desiredHeight;
788                onDesiredSizeChanged(desiredWidth, desiredHeight);
789                doOffsetsChanged(true);
790            }
791        }
792
793        void doVisibilityChanged(boolean visible) {
794            if (!mDestroyed) {
795                mVisible = visible;
796                reportVisibility();
797            }
798        }
799
800        void reportVisibility() {
801            if (!mDestroyed) {
802                boolean visible = mVisible && mScreenOn;
803                if (mReportedVisible != visible) {
804                    mReportedVisible = visible;
805                    if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
806                            + "): " + this);
807                    if (visible) {
808                        // If becoming visible, in preview mode the surface
809                        // may have been destroyed so now we need to make
810                        // sure it is re-created.
811                        doOffsetsChanged(false);
812                        updateSurface(false, false, false);
813                    }
814                    onVisibilityChanged(visible);
815                }
816            }
817        }
818
819        void doOffsetsChanged(boolean always) {
820            if (mDestroyed) {
821                return;
822            }
823
824            if (!always && !mOffsetsChanged) {
825                return;
826            }
827
828            float xOffset;
829            float yOffset;
830            float xOffsetStep;
831            float yOffsetStep;
832            boolean sync;
833            synchronized (mLock) {
834                xOffset = mPendingXOffset;
835                yOffset = mPendingYOffset;
836                xOffsetStep = mPendingXOffsetStep;
837                yOffsetStep = mPendingYOffsetStep;
838                sync = mPendingSync;
839                mPendingSync = false;
840                mOffsetMessageEnqueued = false;
841            }
842
843            if (mSurfaceCreated) {
844                if (mReportedVisible) {
845                    if (DEBUG) Log.v(TAG, "Offsets change in " + this
846                            + ": " + xOffset + "," + yOffset);
847                    final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
848                    final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
849                    final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
850                    final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
851                    onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
852                } else {
853                    mOffsetsChanged = true;
854                }
855            }
856
857            if (sync) {
858                try {
859                    if (DEBUG) Log.v(TAG, "Reporting offsets change complete");
860                    mSession.wallpaperOffsetsComplete(mWindow.asBinder());
861                } catch (RemoteException e) {
862                }
863            }
864        }
865
866        void doCommand(WallpaperCommand cmd) {
867            Bundle result;
868            if (!mDestroyed) {
869                result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
870                        cmd.extras, cmd.sync);
871            } else {
872                result = null;
873            }
874            if (cmd.sync) {
875                try {
876                    if (DEBUG) Log.v(TAG, "Reporting command complete");
877                    mSession.wallpaperCommandComplete(mWindow.asBinder(), result);
878                } catch (RemoteException e) {
879                }
880            }
881        }
882
883        void reportSurfaceDestroyed() {
884            if (mSurfaceCreated) {
885                mSurfaceCreated = false;
886                mSurfaceHolder.ungetCallbacks();
887                SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
888                if (callbacks != null) {
889                    for (SurfaceHolder.Callback c : callbacks) {
890                        c.surfaceDestroyed(mSurfaceHolder);
891                    }
892                }
893                if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
894                        + mSurfaceHolder + "): " + this);
895                onSurfaceDestroyed(mSurfaceHolder);
896            }
897        }
898
899        void detach() {
900            if (mDestroyed) {
901                return;
902            }
903
904            mDestroyed = true;
905
906            if (mVisible) {
907                mVisible = false;
908                if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
909                onVisibilityChanged(false);
910            }
911
912            reportSurfaceDestroyed();
913
914            if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
915            onDestroy();
916
917            unregisterReceiver(mReceiver);
918
919            if (mCreated) {
920                try {
921                    if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
922                            + mSurfaceHolder.getSurface() + " of: " + this);
923
924                    if (mInputEventReceiver != null) {
925                        mInputEventReceiver.dispose();
926                        mInputEventReceiver = null;
927                    }
928
929                    mSession.remove(mWindow);
930                } catch (RemoteException e) {
931                }
932                mSurfaceHolder.mSurface.release();
933                mCreated = false;
934
935                // Dispose the input channel after removing the window so the Window Manager
936                // doesn't interpret the input channel being closed as an abnormal termination.
937                if (mInputChannel != null) {
938                    mInputChannel.dispose();
939                    mInputChannel = null;
940                }
941            }
942        }
943    }
944
945    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
946            implements HandlerCaller.Callback {
947        private final HandlerCaller mCaller;
948
949        final IWallpaperConnection mConnection;
950        final IBinder mWindowToken;
951        final int mWindowType;
952        final boolean mIsPreview;
953        int mReqWidth;
954        int mReqHeight;
955
956        Engine mEngine;
957
958        IWallpaperEngineWrapper(WallpaperService context,
959                IWallpaperConnection conn, IBinder windowToken,
960                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
961            if (DEBUG && mCallbackLooper != null) {
962                mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
963            }
964            mCaller = new HandlerCaller(context,
965                    mCallbackLooper != null
966                            ? mCallbackLooper : context.getMainLooper(),
967                    this);
968            mConnection = conn;
969            mWindowToken = windowToken;
970            mWindowType = windowType;
971            mIsPreview = isPreview;
972            mReqWidth = reqWidth;
973            mReqHeight = reqHeight;
974
975            Message msg = mCaller.obtainMessage(DO_ATTACH);
976            mCaller.sendMessage(msg);
977        }
978
979        public void setDesiredSize(int width, int height) {
980            Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
981            mCaller.sendMessage(msg);
982        }
983
984        public void setVisibility(boolean visible) {
985            Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
986                    visible ? 1 : 0);
987            mCaller.sendMessage(msg);
988        }
989
990        public void dispatchPointer(MotionEvent event) {
991            if (mEngine != null) {
992                mEngine.dispatchPointer(event);
993            } else {
994                event.recycle();
995            }
996        }
997
998        public void dispatchWallpaperCommand(String action, int x, int y,
999                int z, Bundle extras) {
1000            if (mEngine != null) {
1001                mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false);
1002            }
1003        }
1004
1005        public void destroy() {
1006            Message msg = mCaller.obtainMessage(DO_DETACH);
1007            mCaller.sendMessage(msg);
1008        }
1009
1010        public void executeMessage(Message message) {
1011            switch (message.what) {
1012                case DO_ATTACH: {
1013                    try {
1014                        mConnection.attachEngine(this);
1015                    } catch (RemoteException e) {
1016                        Log.w(TAG, "Wallpaper host disappeared", e);
1017                        return;
1018                    }
1019                    Engine engine = onCreateEngine();
1020                    mEngine = engine;
1021                    mActiveEngines.add(engine);
1022                    engine.attach(this);
1023                    return;
1024                }
1025                case DO_DETACH: {
1026                    mActiveEngines.remove(mEngine);
1027                    mEngine.detach();
1028                    return;
1029                }
1030                case DO_SET_DESIRED_SIZE: {
1031                    mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
1032                    return;
1033                }
1034                case MSG_UPDATE_SURFACE:
1035                    mEngine.updateSurface(true, false, false);
1036                    break;
1037                case MSG_VISIBILITY_CHANGED:
1038                    if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
1039                            + ": " + message.arg1);
1040                    mEngine.doVisibilityChanged(message.arg1 != 0);
1041                    break;
1042                case MSG_WALLPAPER_OFFSETS: {
1043                    mEngine.doOffsetsChanged(true);
1044                } break;
1045                case MSG_WALLPAPER_COMMAND: {
1046                    WallpaperCommand cmd = (WallpaperCommand)message.obj;
1047                    mEngine.doCommand(cmd);
1048                } break;
1049                case MSG_WINDOW_RESIZED: {
1050                    final boolean reportDraw = message.arg1 != 0;
1051                    mEngine.updateSurface(true, false, reportDraw);
1052                    mEngine.doOffsetsChanged(true);
1053                } break;
1054                case MSG_WINDOW_MOVED: {
1055                    // Do nothing. What does it mean for a Wallpaper to move?
1056                } break;
1057                case MSG_TOUCH_EVENT: {
1058                    boolean skip = false;
1059                    MotionEvent ev = (MotionEvent)message.obj;
1060                    if (ev.getAction() == MotionEvent.ACTION_MOVE) {
1061                        synchronized (mEngine.mLock) {
1062                            if (mEngine.mPendingMove == ev) {
1063                                mEngine.mPendingMove = null;
1064                            } else {
1065                                // this is not the motion event we are looking for....
1066                                skip = true;
1067                            }
1068                        }
1069                    }
1070                    if (!skip) {
1071                        if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
1072                        mEngine.onTouchEvent(ev);
1073                    }
1074                    ev.recycle();
1075                } break;
1076                default :
1077                    Log.w(TAG, "Unknown message type " + message.what);
1078            }
1079        }
1080    }
1081
1082    /**
1083     * Implements the internal {@link IWallpaperService} interface to convert
1084     * incoming calls to it back to calls on an {@link WallpaperService}.
1085     */
1086    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
1087        private final WallpaperService mTarget;
1088
1089        public IWallpaperServiceWrapper(WallpaperService context) {
1090            mTarget = context;
1091        }
1092
1093        public void attach(IWallpaperConnection conn, IBinder windowToken,
1094                int windowType, boolean isPreview, int reqWidth, int reqHeight) {
1095            new IWallpaperEngineWrapper(mTarget, conn, windowToken,
1096                    windowType, isPreview, reqWidth, reqHeight);
1097        }
1098    }
1099
1100    @Override
1101    public void onCreate() {
1102        super.onCreate();
1103    }
1104
1105    @Override
1106    public void onDestroy() {
1107        super.onDestroy();
1108        for (int i=0; i<mActiveEngines.size(); i++) {
1109            mActiveEngines.get(i).detach();
1110        }
1111        mActiveEngines.clear();
1112    }
1113
1114    /**
1115     * Implement to return the implementation of the internal accessibility
1116     * service interface.  Subclasses should not override.
1117     */
1118    @Override
1119    public final IBinder onBind(Intent intent) {
1120        return new IWallpaperServiceWrapper(this);
1121    }
1122
1123    /**
1124     * This allows subclasses to change the thread that most callbacks
1125     * occur on.  Currently hidden because it is mostly needed for the
1126     * image wallpaper (which runs in the system process and doesn't want
1127     * to get stuck running on that seriously in use main thread).  Not
1128     * exposed right now because the semantics of this are not totally
1129     * well defined and some callbacks can still happen on the main thread).
1130     * @hide
1131     */
1132    public void setCallbackLooper(Looper looper) {
1133        mCallbackLooper = looper;
1134    }
1135
1136    /**
1137     * Must be implemented to return a new instance of the wallpaper's engine.
1138     * Note that multiple instances may be active at the same time, such as
1139     * when the wallpaper is currently set as the active wallpaper and the user
1140     * is in the wallpaper picker viewing a preview of it as well.
1141     */
1142    public abstract Engine onCreateEngine();
1143
1144    @Override
1145    protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
1146        out.print("State of wallpaper "); out.print(this); out.println(":");
1147        for (int i=0; i<mActiveEngines.size(); i++) {
1148            Engine engine = mActiveEngines.get(i);
1149            out.print("  Engine "); out.print(engine); out.println(":");
1150            engine.dump("    ", fd, out, args);
1151        }
1152    }
1153}
1154