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