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