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