2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.service.wallpaper;
19 import com.android.internal.os.HandlerCaller;
20 import com.android.internal.view.BaseIWindow;
21 import com.android.internal.view.BaseSurfaceHolder;
23 import android.annotation.SdkConstant;
24 import android.annotation.SdkConstant.SdkConstantType;
25 import android.app.Service;
26 import android.app.WallpaperManager;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.res.Configuration;
32 import android.graphics.PixelFormat;
33 import android.graphics.Rect;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.os.Looper;
37 import android.os.Message;
38 import android.os.PowerManager;
39 import android.os.RemoteException;
40 import android.util.Log;
41 import android.util.LogPrinter;
42 import android.view.Display;
43 import android.view.Gravity;
44 import android.view.IWindowSession;
45 import android.view.InputChannel;
46 import android.view.InputDevice;
47 import android.view.InputEvent;
48 import android.view.InputEventReceiver;
49 import android.view.MotionEvent;
50 import android.view.SurfaceHolder;
51 import android.view.View;
52 import android.view.ViewGroup;
53 import android.view.WindowManager;
54 import android.view.WindowManagerGlobal;
56 import java.io.FileDescriptor;
57 import java.io.PrintWriter;
58 import java.util.ArrayList;
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
69 public abstract class WallpaperService extends Service {
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.
76 @SdkConstant(SdkConstantType.SERVICE_ACTION)
77 public static final String SERVICE_INTERFACE =
78 "android.service.wallpaper.WallpaperService";
81 * Name under which a WallpaperService component publishes information
82 * about itself. This meta-data must reference an XML resource containing
83 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code>
86 public static final String SERVICE_META_DATA = "android.service.wallpaper";
88 static final String TAG = "WallpaperService";
89 static final boolean DEBUG = false;
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;
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_WINDOW_MOVED = 10035;
101 private static final int MSG_TOUCH_EVENT = 10040;
103 private Looper mCallbackLooper;
104 private final ArrayList<Engine> mActiveEngines
105 = new ArrayList<Engine>();
107 static final class WallpaperCommand {
117 * The actual implementation of a wallpaper. A wallpaper service may
118 * have multiple instances running (for example as a real wallpaper
119 * and as a preview), each of which is represented by its own Engine
120 * instance. You must implement {@link WallpaperService#onCreateEngine()}
121 * to return your concrete Engine implementation.
123 public class Engine {
124 IWallpaperEngineWrapper mIWallpaperEngine;
126 // Copies from mIWallpaperEngine.
127 HandlerCaller mCaller;
128 IWallpaperConnection mConnection;
129 IBinder mWindowToken;
131 boolean mInitializing = true;
133 boolean mScreenOn = true;
134 boolean mReportedVisible;
137 // Current window state.
139 boolean mSurfaceCreated;
141 boolean mDrawingAllowed;
142 boolean mOffsetsChanged;
143 boolean mFixedSizeAllowed;
150 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
151 int mWindowPrivateFlags =
152 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
153 int mCurWindowFlags = mWindowFlags;
154 int mCurWindowPrivateFlags = mWindowPrivateFlags;
155 final Rect mVisibleInsets = new Rect();
156 final Rect mWinFrame = new Rect();
157 final Rect mContentInsets = new Rect();
158 final Configuration mConfiguration = new Configuration();
160 final WindowManager.LayoutParams mLayout
161 = new WindowManager.LayoutParams();
162 IWindowSession mSession;
163 InputChannel mInputChannel;
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;
174 final BroadcastReceiver mReceiver = new BroadcastReceiver() {
176 public void onReceive(Context context, Intent intent) {
177 if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
180 } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
187 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
189 mRequestedFormat = PixelFormat.RGBX_8888;
193 public boolean onAllowLockCanvas() {
194 return mDrawingAllowed;
198 public void onRelayoutContainer() {
199 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
200 mCaller.sendMessage(msg);
204 public void onUpdateSurface() {
205 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
206 mCaller.sendMessage(msg);
209 public boolean isCreating() {
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
219 throw new UnsupportedOperationException(
220 "Wallpapers currently only support sizing from layout");
222 super.setFixedSize(width, height);
225 public void setKeepScreenOn(boolean screenOn) {
226 throw new UnsupportedOperationException(
227 "Wallpapers do not support keep screen on");
232 final class WallpaperInputEventReceiver extends InputEventReceiver {
233 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) {
234 super(inputChannel, looper);
238 public void onInputEvent(InputEvent event) {
239 boolean handled = false;
241 if (event instanceof MotionEvent
242 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
243 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event);
244 dispatchPointer(dup);
248 finishInputEvent(event, handled);
252 WallpaperInputEventReceiver mInputEventReceiver;
254 final BaseIWindow mWindow = new BaseIWindow() {
256 public void resized(Rect frame, Rect contentInsets,
257 Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
258 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
260 mCaller.sendMessage(msg);
264 public void moved(int newX, int newY) {
265 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY);
266 mCaller.sendMessage(msg);
270 public void dispatchAppVisibility(boolean visible) {
271 // We don't do this in preview mode; we'll let the preview
272 // activity tell us when to run.
273 if (!mIWallpaperEngine.mIsPreview) {
274 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
276 mCaller.sendMessage(msg);
281 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
283 synchronized (mLock) {
284 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
287 mPendingXOffsetStep = xStep;
288 mPendingYOffsetStep = yStep;
292 if (!mOffsetMessageEnqueued) {
293 mOffsetMessageEnqueued = true;
294 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
295 mCaller.sendMessage(msg);
301 public void dispatchWallpaperCommand(String action, int x, int y,
302 int z, Bundle extras, boolean sync) {
303 synchronized (mLock) {
304 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y);
305 WallpaperCommand cmd = new WallpaperCommand();
312 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND);
314 mCaller.sendMessage(msg);
320 * Provides access to the surface in which this wallpaper is drawn.
322 public SurfaceHolder getSurfaceHolder() {
323 return mSurfaceHolder;
327 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
328 * WallpaperManager.getDesiredMinimumWidth()}, returning the width
329 * that the system would like this wallpaper to run in.
331 public int getDesiredMinimumWidth() {
332 return mIWallpaperEngine.mReqWidth;
336 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
337 * WallpaperManager.getDesiredMinimumHeight()}, returning the height
338 * that the system would like this wallpaper to run in.
340 public int getDesiredMinimumHeight() {
341 return mIWallpaperEngine.mReqHeight;
345 * Return whether the wallpaper is currently visible to the user,
346 * this is the last value supplied to
347 * {@link #onVisibilityChanged(boolean)}.
349 public boolean isVisible() {
350 return mReportedVisible;
354 * Returns true if this engine is running in preview mode -- that is,
355 * it is being shown to the user before they select it as the actual
358 public boolean isPreview() {
359 return mIWallpaperEngine.mIsPreview;
363 * Control whether this wallpaper will receive raw touch events
364 * from the window manager as the user interacts with the window
365 * that is currently displaying the wallpaper. By default they
366 * are turned off. If enabled, the events will be received in
367 * {@link #onTouchEvent(MotionEvent)}.
369 public void setTouchEventsEnabled(boolean enabled) {
370 mWindowFlags = enabled
371 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
372 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
374 updateSurface(false, false, false);
379 * Control whether this wallpaper will receive notifications when the wallpaper
380 * has been scrolled. By default, wallpapers will receive notifications, although
381 * the default static image wallpapers do not. It is a performance optimization to
384 * @param enabled whether the wallpaper wants to receive offset notifications
386 public void setOffsetNotificationsEnabled(boolean enabled) {
387 mWindowPrivateFlags = enabled
388 ? (mWindowPrivateFlags |
389 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS)
390 : (mWindowPrivateFlags &
391 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS);
393 updateSurface(false, false, false);
398 public void setFixedSizeAllowed(boolean allowed) {
399 mFixedSizeAllowed = allowed;
403 * Called once to initialize the engine. After returning, the
404 * engine's surface will be created by the framework.
406 public void onCreate(SurfaceHolder surfaceHolder) {
410 * Called right before the engine is going away. After this the
411 * surface will be destroyed and this Engine object is no longer
414 public void onDestroy() {
418 * Called to inform you of the wallpaper becoming visible or
419 * hidden. <em>It is very important that a wallpaper only use
420 * CPU while it is visible.</em>.
422 public void onVisibilityChanged(boolean visible) {
426 * Called as the user performs touch-screen interaction with the
427 * window that is currently showing this wallpaper. Note that the
428 * events you receive here are driven by the actual application the
429 * user is interacting with, so if it is slow you will get fewer
432 public void onTouchEvent(MotionEvent event) {
436 * Called to inform you of the wallpaper's offsets changing
437 * within its contain, corresponding to the container's
438 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
439 * WallpaperManager.setWallpaperOffsets()}.
441 public void onOffsetsChanged(float xOffset, float yOffset,
442 float xOffsetStep, float yOffsetStep,
443 int xPixelOffset, int yPixelOffset) {
447 * Process a command that was sent to the wallpaper with
448 * {@link WallpaperManager#sendWallpaperCommand}.
449 * The default implementation does nothing, and always returns null
452 * @param action The name of the command to perform. This tells you
453 * what to do and how to interpret the rest of the arguments.
454 * @param x Generic integer parameter.
455 * @param y Generic integer parameter.
456 * @param z Generic integer parameter.
457 * @param extras Any additional parameters.
458 * @param resultRequested If true, the caller is requesting that
459 * a result, appropriate for the command, be returned back.
460 * @return If returning a result, create a Bundle and place the
461 * result data in to it. Otherwise return null.
463 public Bundle onCommand(String action, int x, int y, int z,
464 Bundle extras, boolean resultRequested) {
469 * Called when an application has changed the desired virtual size of
472 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
476 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
477 * SurfaceHolder.Callback.surfaceChanged()}.
479 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
483 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded
484 * SurfaceHolder.Callback.surfaceRedrawNeeded()}.
486 public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
490 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
491 * SurfaceHolder.Callback.surfaceCreated()}.
493 public void onSurfaceCreated(SurfaceHolder holder) {
497 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
498 * SurfaceHolder.Callback.surfaceDestroyed()}.
500 public void onSurfaceDestroyed(SurfaceHolder holder) {
503 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
504 out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
505 out.print(" mDestroyed="); out.println(mDestroyed);
506 out.print(prefix); out.print("mVisible="); out.print(mVisible);
507 out.print(" mScreenOn="); out.print(mScreenOn);
508 out.print(" mReportedVisible="); out.println(mReportedVisible);
509 out.print(prefix); out.print("mCreated="); out.print(mCreated);
510 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
511 out.print(" mIsCreating="); out.print(mIsCreating);
512 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed);
513 out.print(prefix); out.print("mWidth="); out.print(mWidth);
514 out.print(" mCurWidth="); out.print(mCurWidth);
515 out.print(" mHeight="); out.print(mHeight);
516 out.print(" mCurHeight="); out.println(mCurHeight);
517 out.print(prefix); out.print("mType="); out.print(mType);
518 out.print(" mWindowFlags="); out.print(mWindowFlags);
519 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
520 out.print(" mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
521 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags);
522 out.print(prefix); out.print("mVisibleInsets=");
523 out.print(mVisibleInsets.toShortString());
524 out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
525 out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
526 out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration);
527 out.print(prefix); out.print("mLayout="); out.println(mLayout);
528 synchronized (mLock) {
529 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
530 out.print(" mPendingXOffset="); out.println(mPendingXOffset);
531 out.print(prefix); out.print("mPendingXOffsetStep=");
532 out.print(mPendingXOffsetStep);
533 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep);
534 out.print(prefix); out.print("mOffsetMessageEnqueued=");
535 out.print(mOffsetMessageEnqueued);
536 out.print(" mPendingSync="); out.println(mPendingSync);
537 if (mPendingMove != null) {
538 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove);
543 private void dispatchPointer(MotionEvent event) {
544 if (event.isTouchEvent()) {
545 synchronized (mLock) {
546 if (event.getAction() == MotionEvent.ACTION_MOVE) {
547 mPendingMove = event;
552 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
553 mCaller.sendMessage(msg);
559 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
561 Log.w(TAG, "Ignoring updateSurface: destroyed");
564 int myWidth = mSurfaceHolder.getRequestedWidth();
565 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
566 int myHeight = mSurfaceHolder.getRequestedHeight();
567 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
569 final boolean creating = !mCreated;
570 final boolean surfaceCreating = !mSurfaceCreated;
571 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
572 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
573 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
574 final boolean flagsChanged = mCurWindowFlags != mWindowFlags ||
575 mCurWindowPrivateFlags != mWindowPrivateFlags;
576 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
577 || typeChanged || flagsChanged || redrawNeeded
578 || !mIWallpaperEngine.mShownReported) {
580 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
581 + " format=" + formatChanged + " size=" + sizeChanged);
586 mFormat = mSurfaceHolder.getRequestedFormat();
587 mType = mSurfaceHolder.getRequestedType();
591 mLayout.width = myWidth;
592 mLayout.height = myHeight;
594 mLayout.format = mFormat;
596 mCurWindowFlags = mWindowFlags;
597 mLayout.flags = mWindowFlags
598 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
599 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
600 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
602 mCurWindowPrivateFlags = mWindowPrivateFlags;
603 mLayout.privateFlags = mWindowPrivateFlags;
605 mLayout.memoryType = mType;
606 mLayout.token = mWindowToken;
609 mLayout.type = mIWallpaperEngine.mWindowType;
610 mLayout.gravity = Gravity.START|Gravity.TOP;
611 mLayout.setTitle(WallpaperService.this.getClass().getName());
612 mLayout.windowAnimations =
613 com.android.internal.R.style.Animation_Wallpaper;
614 mInputChannel = new InputChannel();
615 if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
616 Display.DEFAULT_DISPLAY, mContentInsets, mInputChannel) < 0) {
617 Log.w(TAG, "Failed to add window while updating wallpaper surface.");
622 mInputEventReceiver = new WallpaperInputEventReceiver(
623 mInputChannel, Looper.myLooper());
626 mSurfaceHolder.mSurfaceLock.lock();
627 mDrawingAllowed = true;
629 final int relayoutResult = mSession.relayout(
630 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
631 View.VISIBLE, 0, mWinFrame, mContentInsets,
632 mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
634 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
635 + ", frame=" + mWinFrame);
637 int w = mWinFrame.width();
638 if (mCurWidth != w) {
642 int h = mWinFrame.height();
643 if (mCurHeight != h) {
648 mSurfaceHolder.setSurfaceFrameSize(w, h);
649 mSurfaceHolder.mSurfaceLock.unlock();
651 if (!mSurfaceHolder.mSurface.isValid()) {
652 reportSurfaceDestroyed();
653 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
657 boolean didSurface = false;
660 mSurfaceHolder.ungetCallbacks();
662 if (surfaceCreating) {
665 if (DEBUG) Log.v(TAG, "onSurfaceCreated("
666 + mSurfaceHolder + "): " + this);
667 onSurfaceCreated(mSurfaceHolder);
668 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
669 if (callbacks != null) {
670 for (SurfaceHolder.Callback c : callbacks) {
671 c.surfaceCreated(mSurfaceHolder);
676 redrawNeeded |= creating || (relayoutResult
677 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
679 if (forceReport || creating || surfaceCreating
680 || formatChanged || sizeChanged) {
682 RuntimeException e = new RuntimeException();
683 e.fillInStackTrace();
684 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
685 + " formatChanged=" + formatChanged
686 + " sizeChanged=" + sizeChanged, e);
688 if (DEBUG) Log.v(TAG, "onSurfaceChanged("
689 + mSurfaceHolder + ", " + mFormat
690 + ", " + mCurWidth + ", " + mCurHeight
693 onSurfaceChanged(mSurfaceHolder, mFormat,
694 mCurWidth, mCurHeight);
695 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
696 if (callbacks != null) {
697 for (SurfaceHolder.Callback c : callbacks) {
698 c.surfaceChanged(mSurfaceHolder, mFormat,
699 mCurWidth, mCurHeight);
705 onSurfaceRedrawNeeded(mSurfaceHolder);
706 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
707 if (callbacks != null) {
708 for (SurfaceHolder.Callback c : callbacks) {
709 if (c instanceof SurfaceHolder.Callback2) {
710 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
717 if (didSurface && !mReportedVisible) {
718 // This wallpaper is currently invisible, but its
719 // surface has changed. At this point let's tell it
720 // again that it is invisible in case the report about
721 // the surface caused it to start running. We really
722 // don't want wallpapers running when not visible.
724 // Some wallpapers will ignore this call if they
725 // had previously been told they were invisble,
726 // so if we are creating a new surface then toggle
727 // the state to get them to notice.
728 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
730 onVisibilityChanged(true);
732 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
734 onVisibilityChanged(false);
739 mSurfaceCreated = true;
741 mSession.finishDrawing(mWindow);
743 mIWallpaperEngine.reportShown();
745 } catch (RemoteException ex) {
748 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
749 " w=" + mLayout.width + " h=" + mLayout.height);
753 void attach(IWallpaperEngineWrapper wrapper) {
754 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
759 mIWallpaperEngine = wrapper;
760 mCaller = wrapper.mCaller;
761 mConnection = wrapper.mConnection;
762 mWindowToken = wrapper.mWindowToken;
763 mSurfaceHolder.setSizeFromLayout();
764 mInitializing = true;
765 mSession = WindowManagerGlobal.getWindowSession(getMainLooper());
767 mWindow.setSession(mSession);
769 mScreenOn = ((PowerManager)getSystemService(Context.POWER_SERVICE)).isScreenOn();
771 IntentFilter filter = new IntentFilter();
772 filter.addAction(Intent.ACTION_SCREEN_ON);
773 filter.addAction(Intent.ACTION_SCREEN_OFF);
774 registerReceiver(mReceiver, filter);
776 if (DEBUG) Log.v(TAG, "onCreate(): " + this);
777 onCreate(mSurfaceHolder);
779 mInitializing = false;
780 mReportedVisible = false;
781 updateSurface(false, false, false);
784 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
786 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
787 + desiredWidth + "," + desiredHeight + "): " + this);
788 mIWallpaperEngine.mReqWidth = desiredWidth;
789 mIWallpaperEngine.mReqHeight = desiredHeight;
790 onDesiredSizeChanged(desiredWidth, desiredHeight);
791 doOffsetsChanged(true);
795 void doVisibilityChanged(boolean visible) {
802 void reportVisibility() {
804 boolean visible = mVisible && mScreenOn;
805 if (mReportedVisible != visible) {
806 mReportedVisible = visible;
807 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
810 // If becoming visible, in preview mode the surface
811 // may have been destroyed so now we need to make
812 // sure it is re-created.
813 doOffsetsChanged(false);
814 updateSurface(false, false, false);
816 onVisibilityChanged(visible);
821 void doOffsetsChanged(boolean always) {
826 if (!always && !mOffsetsChanged) {
835 synchronized (mLock) {
836 xOffset = mPendingXOffset;
837 yOffset = mPendingYOffset;
838 xOffsetStep = mPendingXOffsetStep;
839 yOffsetStep = mPendingYOffsetStep;
841 mPendingSync = false;
842 mOffsetMessageEnqueued = false;
845 if (mSurfaceCreated) {
846 if (mReportedVisible) {
847 if (DEBUG) Log.v(TAG, "Offsets change in " + this
848 + ": " + xOffset + "," + yOffset);
849 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
850 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
851 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
852 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
853 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
855 mOffsetsChanged = true;
861 if (DEBUG) Log.v(TAG, "Reporting offsets change complete");
862 mSession.wallpaperOffsetsComplete(mWindow.asBinder());
863 } catch (RemoteException e) {
868 void doCommand(WallpaperCommand cmd) {
871 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
872 cmd.extras, cmd.sync);
878 if (DEBUG) Log.v(TAG, "Reporting command complete");
879 mSession.wallpaperCommandComplete(mWindow.asBinder(), result);
880 } catch (RemoteException e) {
885 void reportSurfaceDestroyed() {
886 if (mSurfaceCreated) {
887 mSurfaceCreated = false;
888 mSurfaceHolder.ungetCallbacks();
889 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
890 if (callbacks != null) {
891 for (SurfaceHolder.Callback c : callbacks) {
892 c.surfaceDestroyed(mSurfaceHolder);
895 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
896 + mSurfaceHolder + "): " + this);
897 onSurfaceDestroyed(mSurfaceHolder);
910 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
911 onVisibilityChanged(false);
914 reportSurfaceDestroyed();
916 if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
919 unregisterReceiver(mReceiver);
923 if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
924 + mSurfaceHolder.getSurface() + " of: " + this);
926 if (mInputEventReceiver != null) {
927 mInputEventReceiver.dispose();
928 mInputEventReceiver = null;
931 mSession.remove(mWindow);
932 } catch (RemoteException e) {
934 mSurfaceHolder.mSurface.release();
937 // Dispose the input channel after removing the window so the Window Manager
938 // doesn't interpret the input channel being closed as an abnormal termination.
939 if (mInputChannel != null) {
940 mInputChannel.dispose();
941 mInputChannel = null;
947 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
948 implements HandlerCaller.Callback {
949 private final HandlerCaller mCaller;
951 final IWallpaperConnection mConnection;
952 final IBinder mWindowToken;
953 final int mWindowType;
954 final boolean mIsPreview;
955 boolean mShownReported;
961 IWallpaperEngineWrapper(WallpaperService context,
962 IWallpaperConnection conn, IBinder windowToken,
963 int windowType, boolean isPreview, int reqWidth, int reqHeight) {
964 if (DEBUG && mCallbackLooper != null) {
965 mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
967 mCaller = new HandlerCaller(context,
968 mCallbackLooper != null
969 ? mCallbackLooper : context.getMainLooper(),
972 mWindowToken = windowToken;
973 mWindowType = windowType;
974 mIsPreview = isPreview;
975 mReqWidth = reqWidth;
976 mReqHeight = reqHeight;
978 Message msg = mCaller.obtainMessage(DO_ATTACH);
979 mCaller.sendMessage(msg);
982 public void setDesiredSize(int width, int height) {
983 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
984 mCaller.sendMessage(msg);
987 public void setVisibility(boolean visible) {
988 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
990 mCaller.sendMessage(msg);
993 public void dispatchPointer(MotionEvent event) {
994 if (mEngine != null) {
995 mEngine.dispatchPointer(event);
1001 public void dispatchWallpaperCommand(String action, int x, int y,
1002 int z, Bundle extras) {
1003 if (mEngine != null) {
1004 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false);
1008 public void reportShown() {
1009 if (!mShownReported) {
1010 mShownReported = true;
1012 mConnection.engineShown(this);
1013 } catch (RemoteException e) {
1014 Log.w(TAG, "Wallpaper host disappeared", e);
1020 public void destroy() {
1021 Message msg = mCaller.obtainMessage(DO_DETACH);
1022 mCaller.sendMessage(msg);
1025 public void executeMessage(Message message) {
1026 switch (message.what) {
1029 mConnection.attachEngine(this);
1030 } catch (RemoteException e) {
1031 Log.w(TAG, "Wallpaper host disappeared", e);
1034 Engine engine = onCreateEngine();
1036 mActiveEngines.add(engine);
1037 engine.attach(this);
1041 mActiveEngines.remove(mEngine);
1045 case DO_SET_DESIRED_SIZE: {
1046 mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
1049 case MSG_UPDATE_SURFACE:
1050 mEngine.updateSurface(true, false, false);
1052 case MSG_VISIBILITY_CHANGED:
1053 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
1054 + ": " + message.arg1);
1055 mEngine.doVisibilityChanged(message.arg1 != 0);
1057 case MSG_WALLPAPER_OFFSETS: {
1058 mEngine.doOffsetsChanged(true);
1060 case MSG_WALLPAPER_COMMAND: {
1061 WallpaperCommand cmd = (WallpaperCommand)message.obj;
1062 mEngine.doCommand(cmd);
1064 case MSG_WINDOW_RESIZED: {
1065 final boolean reportDraw = message.arg1 != 0;
1066 mEngine.updateSurface(true, false, reportDraw);
1067 mEngine.doOffsetsChanged(true);
1069 case MSG_WINDOW_MOVED: {
1070 // Do nothing. What does it mean for a Wallpaper to move?
1072 case MSG_TOUCH_EVENT: {
1073 boolean skip = false;
1074 MotionEvent ev = (MotionEvent)message.obj;
1075 if (ev.getAction() == MotionEvent.ACTION_MOVE) {
1076 synchronized (mEngine.mLock) {
1077 if (mEngine.mPendingMove == ev) {
1078 mEngine.mPendingMove = null;
1080 // this is not the motion event we are looking for....
1086 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
1087 mEngine.onTouchEvent(ev);
1092 Log.w(TAG, "Unknown message type " + message.what);
1098 * Implements the internal {@link IWallpaperService} interface to convert
1099 * incoming calls to it back to calls on an {@link WallpaperService}.
1101 class IWallpaperServiceWrapper extends IWallpaperService.Stub {
1102 private final WallpaperService mTarget;
1104 public IWallpaperServiceWrapper(WallpaperService context) {
1108 public void attach(IWallpaperConnection conn, IBinder windowToken,
1109 int windowType, boolean isPreview, int reqWidth, int reqHeight) {
1110 new IWallpaperEngineWrapper(mTarget, conn, windowToken,
1111 windowType, isPreview, reqWidth, reqHeight);
1116 public void onCreate() {
1121 public void onDestroy() {
1123 for (int i=0; i<mActiveEngines.size(); i++) {
1124 mActiveEngines.get(i).detach();
1126 mActiveEngines.clear();
1130 * Implement to return the implementation of the internal accessibility
1131 * service interface. Subclasses should not override.
1134 public final IBinder onBind(Intent intent) {
1135 return new IWallpaperServiceWrapper(this);
1139 * This allows subclasses to change the thread that most callbacks
1140 * occur on. Currently hidden because it is mostly needed for the
1141 * image wallpaper (which runs in the system process and doesn't want
1142 * to get stuck running on that seriously in use main thread). Not
1143 * exposed right now because the semantics of this are not totally
1144 * well defined and some callbacks can still happen on the main thread).
1147 public void setCallbackLooper(Looper looper) {
1148 mCallbackLooper = looper;
1152 * Must be implemented to return a new instance of the wallpaper's engine.
1153 * Note that multiple instances may be active at the same time, such as
1154 * when the wallpaper is currently set as the active wallpaper and the user
1155 * is in the wallpaper picker viewing a preview of it as well.
1157 public abstract Engine onCreateEngine();
1160 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
1161 out.print("State of wallpaper "); out.print(this); out.println(":");
1162 for (int i=0; i<mActiveEngines.size(); i++) {
1163 Engine engine = mActiveEngines.get(i);
1164 out.print(" Engine "); out.print(engine); out.println(":");
1165 engine.dump(" ", fd, out, args);