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