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