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