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