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