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