Very primitive wallpapers in a surface.

This is all of the basic pieces:

- The WallpaperService now creates a surface with the window manager for its
  contents.
- There is a simple service that displays a bitmap.
- The wallpaper manager takes care of starting and stopping the service.
- The window manager knows about wallpaper windows and how to layer them with
  the windows that want to be shown on top of wallpaper.

Lots and lots of issues remain, but at this point you can actually write a
wallpaper service, select it in the UI, and see it behind an activity.
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 8ac9557..7e71088 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -514,12 +514,12 @@
     
     @Override
     public Drawable getWallpaper() {
-        return getWallpaperManager().get();
+        return getWallpaperManager().getDrawable();
     }
 
     @Override
     public Drawable peekWallpaper() {
-        return getWallpaperManager().peek();
+        return getWallpaperManager().peekDrawable();
     }
 
     @Override
@@ -534,12 +534,12 @@
 
     @Override
     public void setWallpaper(Bitmap bitmap) throws IOException  {
-        getWallpaperManager().set(bitmap);
+        getWallpaperManager().setBitmap(bitmap);
     }
 
     @Override
     public void setWallpaper(InputStream data) throws IOException {
-        getWallpaperManager().set(data);
+        getWallpaperManager().setStream(data);
     }
 
     @Override
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 1ed9b9f..7741668 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -18,6 +18,7 @@
 
 import android.os.ParcelFileDescriptor;
 import android.app.IWallpaperManagerCallback;
+import android.content.ComponentName;
 
 /** @hide */
 interface IWallpaperManager {
@@ -28,6 +29,11 @@
     ParcelFileDescriptor setWallpaper(String name);
     
     /**
+     * Set the live wallpaper.
+     */
+    void setWallpaperComponent(in ComponentName name);
+    
+    /**
      * Get the wallpaper.
      */
     ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb);
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 405db83..9019b54 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package android.app;
 
 import android.content.Context;
@@ -21,6 +37,14 @@
     private static String TAG = "WallpaperManager";
     private static boolean DEBUG = false;
 
+    /**
+     * Launch an activity for the user to pick the current global live
+     * wallpaper.
+     * @hide
+     */
+    public static final String ACTION_LIVE_WALLPAPER_CHOOSER
+            = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER";
+    
     private final Context mContext;
     
     static class Globals extends IWallpaperManagerCallback.Stub {
@@ -88,13 +112,26 @@
     }
 
     /**
-     * Like {@link #peek}, but always returns a valid Drawable.  If
+     * Retrieve a WallpaperManager associated with the given Context.
+     */
+    public static WallpaperManager getInstance(Context context) {
+        return (WallpaperManager)context.getSystemService(
+                Context.WALLPAPER_SERVICE);
+    }
+    
+    /** @hide */
+    public IWallpaperManager getIWallpaperManager() {
+        return getGlobals().mService;
+    }
+    
+    /**
+     * Like {@link #peekDrawable}, but always returns a valid Drawable.  If
      * no wallpaper is set, the system default wallpaper is returned.
      *
      * @return Returns a Drawable object that will draw the wallpaper.
      */
-    public Drawable get() {
-        Drawable dr = peek();
+    public Drawable getDrawable() {
+        Drawable dr = peekDrawable();
         return dr != null ? dr : Resources.getSystem().getDrawable(
                 com.android.internal.R.drawable.default_wallpaper);
     }
@@ -108,7 +145,7 @@
      * @return Returns a Drawable object that will draw the wallpaper or a
      * null pointer if these is none.
      */
-    public Drawable peek() {
+    public Drawable peekDrawable() {
         return getGlobals().peekWallpaper(mContext);
     }
 
@@ -123,7 +160,7 @@
      * @throws IOException If an error occurs reverting to the default
      * wallpaper.
      */
-    public void set(int resid) throws IOException {
+    public void setResource(int resid) throws IOException {
         try {
             Resources resources = mContext.getResources();
             /* Set the wallpaper to the default values */
@@ -154,7 +191,7 @@
      * @throws IOException If an error occurs reverting to the default
      * wallpaper.
      */
-    public void set(Bitmap bitmap) throws IOException {
+    public void setBitmap(Bitmap bitmap) throws IOException {
         try {
             ParcelFileDescriptor fd = getGlobals().mService.setWallpaper(null);
             if (fd == null) {
@@ -185,7 +222,7 @@
      * @throws IOException If an error occurs reverting to the default
      * wallpaper.
      */
-    public void set(InputStream data) throws IOException {
+    public void setStream(InputStream data) throws IOException {
         try {
             ParcelFileDescriptor fd = getGlobals().mService.setWallpaper(null);
             if (fd == null) {
@@ -215,8 +252,8 @@
 
     /**
      * Returns the desired minimum width for the wallpaper. Callers of
-     * {@link #set(android.graphics.Bitmap)} or
-     * {@link #set(java.io.InputStream)} should check this value
+     * {@link #setBitmap(android.graphics.Bitmap)} or
+     * {@link #setStream(java.io.InputStream)} should check this value
      * beforehand to make sure the supplied wallpaper respects the desired
      * minimum width.
      *
@@ -238,8 +275,8 @@
 
     /**
      * Returns the desired minimum height for the wallpaper. Callers of
-     * {@link #set(android.graphics.Bitmap)} or
-     * {@link #set(java.io.InputStream)} should check this value
+     * {@link #setBitmap(android.graphics.Bitmap)} or
+     * {@link #setStream(java.io.InputStream)} should check this value
      * beforehand to make sure the supplied wallpaper respects the desired
      * minimum height.
      *
@@ -267,7 +304,7 @@
      * @param minimumWidth Desired minimum width
      * @param minimumHeight Desired minimum height
      */
-    public void setDimensionHints(int minimumWidth, int minimumHeight) {
+    public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
         try {
             getGlobals().mService.setDimensionHints(minimumWidth, minimumHeight);
         } catch (RemoteException e) {
@@ -283,6 +320,6 @@
      * wallpaper.
      */
     public void clear() throws IOException {
-        set(com.android.internal.R.drawable.default_wallpaper);
+        setResource(com.android.internal.R.drawable.default_wallpaper);
     }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1105899..dbe6fb0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -488,13 +488,13 @@
     public abstract String[] databaseList();
 
     /**
-     * @deprecated Use {@link android.app.WallpaperManager#get
+     * @deprecated Use {@link android.app.WallpaperManager#getDrawable
      * WallpaperManager.get()} instead.
      */
     public abstract Drawable getWallpaper();
 
     /**
-     * @deprecated Use {@link android.app.WallpaperManager#peek
+     * @deprecated Use {@link android.app.WallpaperManager#peekDrawable
      * WallpaperManager.peek()} instead.
      */
     public abstract Drawable peekWallpaper();
@@ -512,13 +512,13 @@
     public abstract int getWallpaperDesiredMinimumHeight();
 
     /**
-     * @deprecated Use {@link android.app.WallpaperManager#set(Bitmap)
+     * @deprecated Use {@link android.app.WallpaperManager#setBitmap(Bitmap)
      * WallpaperManager.set()} instead.
      */
     public abstract void setWallpaper(Bitmap bitmap) throws IOException;
 
     /**
-     * @deprecated Use {@link android.app.WallpaperManager#set(InputStream)
+     * @deprecated Use {@link android.app.WallpaperManager#setStream(InputStream)
      * WallpaperManager.set()} instead.
      */
     public abstract void setWallpaper(InputStream data) throws IOException;
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
new file mode 100644
index 0000000..b09ccab
--- /dev/null
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wallpaper;
+
+import android.os.ParcelFileDescriptor;
+import android.service.wallpaper.IWallpaperEngine;
+
+/**
+ * @hide
+ */
+interface IWallpaperConnection {
+	void attachEngine(IWallpaperEngine engine);
+    ParcelFileDescriptor setWallpaper(String name);
+}
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
new file mode 100644
index 0000000..9586e34
--- /dev/null
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wallpaper;
+
+/**
+ * @hide
+ */
+oneway interface IWallpaperEngine {
+	void destroy();
+}
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index 97e032b..eb58c3b 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -16,9 +16,12 @@
 
 package android.service.wallpaper;
 
+import android.service.wallpaper.IWallpaperConnection;
+
 /**
  * @hide
  */
 oneway interface IWallpaperService {
-    void onInterrupt();
+    void attach(IWallpaperConnection connection,
+    		IBinder windowToken, int reqWidth, int reqHeight);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a729ed5..dbec1e6 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -17,17 +17,29 @@
 package android.service.wallpaper;
 
 import com.android.internal.os.HandlerCaller;
+import com.android.internal.view.BaseIWindow;
+import com.android.internal.view.BaseSurfaceHolder;
 
 import android.app.Service;
 import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.Gravity;
+import android.view.IWindowSession;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.view.ViewRoot;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
 
 /**
  * A wallpaper service is responsible for showing a live wallpaper behind
  * applications that would like to sit on top of it.
+ * @hide Live Wallpaper
  */
 public abstract class WallpaperService extends Service {
     /**
@@ -36,8 +48,312 @@
     public static final String SERVICE_INTERFACE =
         "android.service.wallpaper.WallpaperService";
 
-    private static final String LOG_TAG = "WallpaperService";
+    static final String TAG = "WallpaperService";
+    static final boolean DEBUG = true;
+    
+    private static final int DO_ATTACH = 10;
+    private static final int DO_DETACH = 20;
+    
+    private static final int MSG_UPDATE_SURFACE = 10000;
+    
+    /**
+     * The actual implementation of a wallpaper.  A wallpaper service may
+     * have multiple instances running (for example as a real wallpaper
+     * and as a preview), each of which is represented by its own Engine
+     * instance.
+     */
+    public class Engine {
+        IWallpaperEngineWrapper mIWallpaperEngine;
+        
+        // Copies from mIWallpaperEngine.
+        HandlerCaller mCaller;
+        IWallpaperConnection mConnection;
+        IBinder mWindowToken;
+        
+        boolean mInitializing = true;
+        
+        // Current window state.
+        boolean mCreated;
+        boolean mIsCreating;
+        boolean mDrawingAllowed;
+        int mWidth;
+        int mHeight;
+        int mFormat;
+        int mType;
+        boolean mDestroyReportNeeded;
+        final Rect mVisibleInsets = new Rect();
+        final Rect mWinFrame = new Rect();
+        final Rect mContentInsets = new Rect();
+        
+        final WindowManager.LayoutParams mLayout
+                = new WindowManager.LayoutParams();
+        IWindowSession mSession;
 
+        final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
+
+            @Override
+            public boolean onAllowLockCanvas() {
+                return mDrawingAllowed;
+            }
+
+            @Override
+            public void onRelayoutContainer() {
+                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
+                mCaller.sendMessage(msg);
+            }
+
+            @Override
+            public void onUpdateSurface() {
+                Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
+                mCaller.sendMessage(msg);
+            }
+
+            public boolean isCreating() {
+                return mIsCreating;
+            }
+
+            public void setKeepScreenOn(boolean screenOn) {
+                // Ignore.
+            }
+            
+        };
+        
+        final BaseIWindow mWindow = new BaseIWindow() {
+            
+        };
+        
+        public void onAttach(SurfaceHolder surfaceHolder) {
+        }
+        
+        public void onDetach() {
+        }
+        
+        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        }
+
+        public void onSurfaceCreated(SurfaceHolder holder) {
+        }
+
+        public void onSurfaceDestroyed(SurfaceHolder holder) {
+        }
+
+        void updateSurface(boolean force) {
+            int myWidth = mSurfaceHolder.getRequestedWidth();
+            if (myWidth <= 0) myWidth = mIWallpaperEngine.mReqWidth;
+            int myHeight = mSurfaceHolder.getRequestedHeight();
+            if (myHeight <= 0) myHeight = mIWallpaperEngine.mReqHeight;
+            
+            final boolean creating = !mCreated;
+            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
+            final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
+            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
+            if (force || creating || formatChanged || sizeChanged || typeChanged) {
+
+                if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
+                        + " format=" + formatChanged + " size=" + sizeChanged);
+
+                try {
+                    mWidth = myWidth;
+                    mHeight = myHeight;
+                    mFormat = mSurfaceHolder.getRequestedFormat();
+                    mType = mSurfaceHolder.getRequestedType();
+
+                    // Scaling/Translate window's layout here because mLayout is not used elsewhere.
+                    
+                    // Places the window relative
+                    mLayout.x = 0;
+                    mLayout.y = 0;
+                    mLayout.width = myWidth;
+                    mLayout.height = myHeight;
+                    
+                    mLayout.format = mFormat;
+                    mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                                  | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                                  | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                                  | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                                  ;
+
+                    mLayout.memoryType = mType;
+                    mLayout.token = mWindowToken;
+
+                    if (!mCreated) {
+                        mLayout.type = WindowManager.LayoutParams.TYPE_WALLPAPER;
+                        mLayout.gravity = Gravity.LEFT|Gravity.TOP;
+                        mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
+                    }
+                    
+                    mSurfaceHolder.mSurfaceLock.lock();
+                    mDrawingAllowed = true;
+
+                    final int relayoutResult = mSession.relayout(
+                        mWindow, mLayout, mWidth, mHeight,
+                            View.VISIBLE, false, mWinFrame, mContentInsets,
+                            mVisibleInsets, mSurfaceHolder.mSurface);
+
+                    if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface
+                            + ", frame=" + mWinFrame);
+                    
+                    mSurfaceHolder.mSurfaceLock.unlock();
+
+                    try {
+                        mDestroyReportNeeded = true;
+
+                        SurfaceHolder.Callback callbacks[] = null;
+                        synchronized (mSurfaceHolder.mCallbacks) {
+                            final int N = mSurfaceHolder.mCallbacks.size();
+                            if (N > 0) {
+                                callbacks = new SurfaceHolder.Callback[N];
+                                mSurfaceHolder.mCallbacks.toArray(callbacks);
+                            }
+                        }
+
+                        if (!mCreated) {
+                            mIsCreating = true;
+                            onSurfaceCreated(mSurfaceHolder);
+                            if (callbacks != null) {
+                                for (SurfaceHolder.Callback c : callbacks) {
+                                    c.surfaceCreated(mSurfaceHolder);
+                                }
+                            }
+                        }
+                        if (creating || formatChanged || sizeChanged) {
+                            onSurfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
+                            if (callbacks != null) {
+                                for (SurfaceHolder.Callback c : callbacks) {
+                                    c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
+                                }
+                            }
+                        }
+                    } finally {
+                        mIsCreating = false;
+                        mCreated = true;
+                        if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+                            mSession.finishDrawing(mWindow);
+                        }
+                    }
+                } catch (RemoteException ex) {
+                }
+                if (DEBUG) Log.v(
+                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
+                    " w=" + mLayout.width + " h=" + mLayout.height);
+            }
+        }
+        
+        void attach(IWallpaperEngineWrapper wrapper) {
+            mIWallpaperEngine = wrapper;
+            mCaller = wrapper.mCaller;
+            mConnection = wrapper.mConnection;
+            mWindowToken = wrapper.mWindowToken;
+            mSurfaceHolder.setSizeFromLayout();
+            mInitializing = true;
+            mSession = ViewRoot.getWindowSession(getMainLooper());
+            mWindow.setSession(mSession);
+            
+            onAttach(mSurfaceHolder);
+            
+            mInitializing = false;
+            updateSurface(false);
+        }
+        
+        void detach() {
+            onDetach();
+            if (mDestroyReportNeeded) {
+                mDestroyReportNeeded = false;
+                SurfaceHolder.Callback callbacks[];
+                synchronized (mSurfaceHolder.mCallbacks) {
+                    callbacks = new SurfaceHolder.Callback[
+                            mSurfaceHolder.mCallbacks.size()];
+                    mSurfaceHolder.mCallbacks.toArray(callbacks);
+                }
+                for (SurfaceHolder.Callback c : callbacks) {
+                    c.surfaceDestroyed(mSurfaceHolder);
+                }
+            }
+            if (mCreated) {
+                try {
+                    mSession.remove(mWindow);
+                } catch (RemoteException e) {
+                }
+                mSurfaceHolder.mSurface.clear();
+                mCreated = false;
+            }
+        }
+    }
+    
+    class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
+            implements HandlerCaller.Callback {
+        private final HandlerCaller mCaller;
+
+        final IWallpaperConnection mConnection;
+        final IBinder mWindowToken;
+        int mReqWidth;
+        int mReqHeight;
+        
+        Engine mEngine;
+        
+        IWallpaperEngineWrapper(WallpaperService context,
+                IWallpaperConnection conn, IBinder windowToken,
+                int reqWidth, int reqHeight) {
+            mCaller = new HandlerCaller(context, this);
+            mConnection = conn;
+            mWindowToken = windowToken;
+            mReqWidth = reqWidth;
+            mReqHeight = reqHeight;
+            
+            try {
+                conn.attachEngine(this);
+            } catch (RemoteException e) {
+                destroy();
+            }
+            
+            Message msg = mCaller.obtainMessage(DO_ATTACH);
+            mCaller.sendMessage(msg);
+        }
+        
+        public void destroy() {
+            Message msg = mCaller.obtainMessage(DO_DETACH);
+            mCaller.sendMessage(msg);
+        }
+
+        public void executeMessage(Message message) {
+            switch (message.what) {
+                case DO_ATTACH: {
+                    Engine engine = onCreateEngine();
+                    mEngine = engine;
+                    engine.attach(this);
+                    return;
+                }
+                case DO_DETACH: {
+                    mEngine.detach();
+                    return;
+                }
+                case MSG_UPDATE_SURFACE:
+                    mEngine.updateSurface(false);
+                    break;
+                default :
+                    Log.w(TAG, "Unknown message type " + message.what);
+            }
+        }
+    }
+
+    /**
+     * Implements the internal {@link IWallpaperService} interface to convert
+     * incoming calls to it back to calls on an {@link WallpaperService}.
+     */
+    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
+        private final WallpaperService mTarget;
+
+        public IWallpaperServiceWrapper(WallpaperService context) {
+            mTarget = context;
+        }
+
+        public void attach(IWallpaperConnection conn,
+                IBinder windowToken, int reqWidth, int reqHeight) {
+            new IWallpaperEngineWrapper(
+                    mTarget, conn, windowToken, reqWidth, reqHeight);
+        }
+    }
+    
     /**
      * Implement to return the implementation of the internal accessibility
      * service interface.  Subclasses should not override.
@@ -46,38 +362,6 @@
     public final IBinder onBind(Intent intent) {
         return new IWallpaperServiceWrapper(this);
     }
-
-    /**
-     * Implements the internal {@link IWallpaperService} interface to convert
-     * incoming calls to it back to calls on an {@link WallpaperService}.
-     */
-    class IWallpaperServiceWrapper extends IWallpaperService.Stub
-            implements HandlerCaller.Callback {
-
-        private static final int DO_ON_INTERRUPT = 10;
-        
-        private final HandlerCaller mCaller;
-
-        private WallpaperService mTarget;
-
-        public IWallpaperServiceWrapper(WallpaperService context) {
-            mTarget = context;
-            mCaller = new HandlerCaller(context, this);
-        }
-
-        public void onInterrupt() {
-            Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
-            mCaller.sendMessage(message);
-        }
-
-        public void executeMessage(Message message) {
-            switch (message.what) {
-                case DO_ON_INTERRUPT :
-                    //mTarget.onInterrupt();
-                    return;
-                default :
-                    Log.w(LOG_TAG, "Unknown message type " + message.what);
-            }
-        }
-    }
+    
+    public abstract Engine onCreateEngine();
 }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 0d44b4e..216fc5e 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -192,6 +192,22 @@
 
     private final int mDensity;
 
+    public static IWindowSession getWindowSession(Looper mainLooper) {
+        synchronized (mStaticInit) {
+            if (!mInitialized) {
+                try {
+                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
+                    sWindowSession = IWindowManager.Stub.asInterface(
+                            ServiceManager.getService("window"))
+                            .openSession(imm.getClient(), imm.getInputContext());
+                    mInitialized = true;
+                } catch (RemoteException e) {
+                }
+            }
+            return sWindowSession;
+        }
+    }
+    
     public ViewRoot(Context context) {
         super();
 
@@ -204,19 +220,8 @@
         // Initialize the statics when this class is first instantiated. This is
         // done here instead of in the static block because Zygote does not
         // allow the spawning of threads.
-        synchronized (mStaticInit) {
-            if (!mInitialized) {
-                try {
-                    InputMethodManager imm = InputMethodManager.getInstance(context);
-                    sWindowSession = IWindowManager.Stub.asInterface(
-                            ServiceManager.getService("window"))
-                            .openSession(imm.getClient(), imm.getInputContext());
-                    mInitialized = true;
-                } catch (RemoteException e) {
-                }
-            }
-        }
-
+        getWindowSession(context.getMainLooper());
+        
         mThread = Thread.currentThread();
         mLocation = new WindowLeaked(null);
         mLocation.fillInStackTrace();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c0be9e8..35d7cc9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -314,6 +314,12 @@
         public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
 
         /**
+         * Window type: wallpaper window, placed behind any window that wants
+         * to sit on top of the wallpaper.
+         */
+        public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
@@ -479,16 +485,23 @@
          * key guard or any other lock screens. Can be used with
          * {@link #FLAG_KEEP_SCREEN_ON} to turn screen on and display windows
          * directly before showing the key guard window
-         *
-         * {@hide} */
+         */
         public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
 
+        /** Window flag: ask that the system wallpaper be shown behind
+         * your window.  The window surface must be translucent to be able
+         * to actually see the wallpaper behind it; this flag just ensures
+         * that the wallpaper surface will be there if this window actually
+         * has translucent regions.
+         */
+        public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
+        
         /** Window flag: special flag to limit the size of the window to be
          * original size ([320x480] x density). Used to create window for applications
          * running under compatibility mode.
          *
          * {@hide} */
-        public static final int FLAG_COMPATIBLE_WINDOW = 0x00100000;
+        public static final int FLAG_COMPATIBLE_WINDOW = 0x20000000;
 
         /** Window flag: a special option intended for system dialogs.  When
          * this flag is set, the window will demand focus unconditionally when
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d797890..e30687f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -446,13 +446,22 @@
      * @hide
      */
     static public InputMethodManager getInstance(Context context) {
+        return getInstance(context.getMainLooper());
+    }
+    
+    /**
+     * Internally, the input method manager can't be context-dependent, so
+     * we have this here for the places that need it.
+     * @hide
+     */
+    static public InputMethodManager getInstance(Looper mainLooper) {
         synchronized (mInstanceSync) {
             if (mInstance != null) {
                 return mInstance;
             }
             IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
             IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
-            mInstance = new InputMethodManager(service, context.getMainLooper());
+            mInstance = new InputMethodManager(service, mainLooper);
         }
         return mInstance;
     }
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 932555d..2ab9e09 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -132,6 +132,14 @@
         return mH.obtainMessage(what, arg1, arg2, arg3);
     }
     
+    public Message obtainMessageIIOO(int what, int arg1, int arg2,
+            Object arg3, Object arg4) {
+        SomeArgs args = obtainArgs();
+        args.arg1 = arg3;
+        args.arg2 = arg4;
+        return mH.obtainMessage(what, arg1, arg2, args);
+    }
+    
     public Message obtainMessageIOO(int what, int arg1, Object arg2, Object arg3) {
         SomeArgs args = obtainArgs();
         args.arg1 = arg2;
diff --git a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
new file mode 100644
index 0000000..9c99e05
--- /dev/null
+++ b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.service.wallpaper;
+
+import android.app.WallpaperManager;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.service.wallpaper.WallpaperService;
+import android.view.SurfaceHolder;
+
+/**
+ * Default built-in wallpaper that simply shows a static image.
+ */
+public class ImageWallpaper extends WallpaperService {
+    public WallpaperManager mWallpaperManager;
+    
+    class MyEngine extends Engine {
+
+        Drawable mBackground;
+        
+        @Override
+        public void onAttach(SurfaceHolder surfaceHolder) {
+            super.onAttach(surfaceHolder);
+            mBackground = mWallpaperManager.getDrawable();
+        }
+
+        @Override
+        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+            super.onSurfaceChanged(holder, format, width, height);
+            Canvas c = holder.lockCanvas();
+            mBackground.setBounds(0, 0, width, height);
+            mBackground.draw(c);
+            Paint paint = new Paint();
+            paint.setAntiAlias(true);
+            final float density = getResources().getDisplayMetrics().density;
+            paint.setTextSize(30 * density);
+            paint.setShadowLayer(5*density, 3*density, 3*density, 0xff000000);
+            paint.setARGB(255, 255, 255, 255);
+            c.drawText("Am I live?", 10, 60*density, paint);
+            holder.unlockCanvasAndPost(c);
+        }
+
+        @Override
+        public void onSurfaceCreated(SurfaceHolder holder) {
+            super.onSurfaceCreated(holder);
+        }
+
+        @Override
+        public void onSurfaceDestroyed(SurfaceHolder holder) {
+            super.onSurfaceDestroyed(holder);
+        }
+        
+    }
+    
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mWallpaperManager = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
+    }
+    
+    public Engine onCreateEngine() {
+        return new MyEngine();
+    }
+}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
new file mode 100644
index 0000000..7449067
--- /dev/null
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -0,0 +1,68 @@
+package com.android.internal.view;
+
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class BaseIWindow extends IWindow.Stub {
+    private IWindowSession mSession;
+    
+    public void setSession(IWindowSession session) {
+        mSession = session;
+    }
+    
+    public void resized(int w, int h, Rect coveredInsets,
+            Rect visibleInsets, boolean reportDraw) {
+        if (reportDraw) {
+            try {
+                mSession.finishDrawing(this);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    public void dispatchKey(KeyEvent event) {
+        try {
+            mSession.finishKey(this);
+        } catch (RemoteException ex) {
+        }
+    }
+
+    public void dispatchPointer(MotionEvent event, long eventTime) {
+        try {
+            if (event == null) {
+                event = mSession.getPendingPointerMove(this);
+            } else if (event.getAction() != MotionEvent.ACTION_OUTSIDE) {
+                mSession.finishKey(this);
+            }
+        } catch (RemoteException ex) {
+        }
+    }
+
+    public void dispatchTrackball(MotionEvent event, long eventTime) {
+        try {
+            if (event == null) {
+                event = mSession.getPendingTrackballMove(this);
+            } else if (event.getAction() != MotionEvent.ACTION_OUTSIDE) {
+                mSession.finishKey(this);
+            }
+        } catch (RemoteException ex) {
+        }
+    }
+
+    public void dispatchAppVisibility(boolean visible) {
+    }
+
+    public void dispatchGetNewSurface() {
+    }
+
+    public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
+    }
+
+    public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+    }
+}
diff --git a/core/java/com/android/internal/view/BaseSurfaceHolder.java b/core/java/com/android/internal/view/BaseSurfaceHolder.java
new file mode 100644
index 0000000..2364ae4
--- /dev/null
+++ b/core/java/com/android/internal/view/BaseSurfaceHolder.java
@@ -0,0 +1,169 @@
+package com.android.internal.view;
+
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
+
+public abstract class BaseSurfaceHolder implements SurfaceHolder {
+    private static final String TAG = "BaseSurfaceHolder";
+    static final boolean DEBUG = false;
+
+    public final ArrayList<SurfaceHolder.Callback> mCallbacks
+            = new ArrayList<SurfaceHolder.Callback>();
+
+    public final ReentrantLock mSurfaceLock = new ReentrantLock();
+    public final Surface mSurface = new Surface();
+
+    int mRequestedWidth = -1;
+    int mRequestedHeight = -1;
+    int mRequestedFormat = PixelFormat.OPAQUE;
+    int mRequestedType = -1;
+
+    long mLastLockTime = 0;
+    
+    int mType = -1;
+    final Rect mSurfaceFrame = new Rect();
+    
+    public abstract void onUpdateSurface();
+    public abstract void onRelayoutContainer();
+    public abstract boolean onAllowLockCanvas();
+    
+    public int getRequestedWidth() {
+        return mRequestedWidth;
+    }
+    
+    public int getRequestedHeight() {
+        return mRequestedHeight;
+    }
+    
+    public int getRequestedFormat() {
+        return mRequestedFormat;
+    }
+    
+    public int getRequestedType() {
+        return mRequestedType;
+    }
+    
+    public void addCallback(Callback callback) {
+        synchronized (mCallbacks) {
+            // This is a linear search, but in practice we'll 
+            // have only a couple callbacks, so it doesn't matter.
+            if (mCallbacks.contains(callback) == false) {      
+                mCallbacks.add(callback);
+            }
+        }
+    }
+
+    public void removeCallback(Callback callback) {
+        synchronized (mCallbacks) {
+            mCallbacks.remove(callback);
+        }
+    }
+    
+    public void setFixedSize(int width, int height) {
+        if (mRequestedWidth != width || mRequestedHeight != height) {
+            mRequestedWidth = width;
+            mRequestedHeight = height;
+            onRelayoutContainer();
+        }
+    }
+
+    public void setSizeFromLayout() {
+        if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+            mRequestedWidth = mRequestedHeight = -1;
+            onRelayoutContainer();
+        }
+    }
+
+    public void setFormat(int format) {
+        if (mRequestedFormat != format) {
+            mRequestedFormat = format;
+            onUpdateSurface();
+        }
+    }
+
+    public void setType(int type) {
+        switch (type) {
+        case SURFACE_TYPE_NORMAL:
+        case SURFACE_TYPE_HARDWARE:
+        case SURFACE_TYPE_GPU:
+        case SURFACE_TYPE_PUSH_BUFFERS:
+            if (mRequestedType != type) {
+                mRequestedType = type;
+                onUpdateSurface();
+            }
+            break;
+        }
+    }
+
+    public Canvas lockCanvas() {
+        return internalLockCanvas(null);
+    }
+
+    public Canvas lockCanvas(Rect dirty) {
+        return internalLockCanvas(dirty);
+    }
+
+    private final Canvas internalLockCanvas(Rect dirty) {
+        if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
+            throw new BadSurfaceTypeException(
+                    "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
+        }
+        mSurfaceLock.lock();
+
+        if (DEBUG) Log.i(TAG, "Locking canvas..,");
+
+        Canvas c = null;
+        if (onAllowLockCanvas()) {
+            Rect frame = dirty != null ? dirty : mSurfaceFrame;
+            try {
+                c = mSurface.lockCanvas(frame);
+            } catch (Exception e) {
+                Log.e(TAG, "Exception locking surface", e);
+            }
+        }
+
+        if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
+        if (c != null) {
+            mLastLockTime = SystemClock.uptimeMillis();
+            return c;
+        }
+        
+        // If the Surface is not ready to be drawn, then return null,
+        // but throttle calls to this function so it isn't called more
+        // than every 100ms.
+        long now = SystemClock.uptimeMillis();
+        long nextTime = mLastLockTime + 100;
+        if (nextTime > now) {
+            try {
+                Thread.sleep(nextTime-now);
+            } catch (InterruptedException e) {
+            }
+            now = SystemClock.uptimeMillis();
+        }
+        mLastLockTime = now;
+        mSurfaceLock.unlock();
+        
+        return null;
+    }
+
+    public void unlockCanvasAndPost(Canvas canvas) {
+        mSurface.unlockCanvasAndPost(canvas);
+        mSurfaceLock.unlock();
+    }
+
+    public Surface getSurface() {
+        return mSurface;
+    }
+
+    public Rect getSurfaceFrame() {
+        return mSurfaceFrame;
+    }
+};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 69ef96c..cf85af5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -894,6 +894,13 @@
         android:description="@string/permdesc_bindInputMethod"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by wallpaper services, to ensure that only the
+         system can bind to them. -->
+    <permission android:name="android.permission.BIND_WALLPAPER"
+        android:label="@string/permlab_bindWallpaper"
+        android:description="@string/permdesc_bindWallpaper"
+        android:protectionLevel="signature" />
+
     <!-- Allows low-level access to setting the orientation (actually
          rotation) of the screen.  Not for use by normal applications. -->
     <permission android:name="android.permission.SET_ORIENTATION"
@@ -1082,6 +1089,12 @@
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signature" />
 
+    <!-- Allows applications to set a live wallpaper.
+         @hide -->
+    <permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -1140,7 +1153,14 @@
         </activity>
 
         <service android:name="com.android.server.LoadAverageService"
-            android:exported="true" />
+                android:exported="true" />
+
+        <service android:name="com.android.internal.service.wallpaper.ImageWallpaper"
+                android:permission="android.permission.BIND_WALLPAPER">
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService" />
+            </intent-filter>
+        </service>
 
         <receiver android:name="com.android.server.BootReceiver" >
             <intent-filter>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 558d91e..68f2070 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -599,6 +599,12 @@
         interface of an input method. Should never be needed for normal applications.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindWallpaper">bind to a wallpaper</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindWallpaper">Allows the holder to bind to the top-level
+        interface of a wallpaper. Should never be needed for normal applications.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setOrientation">change screen orientation</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_setOrientation">Allows an application to change