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/Android.mk b/Android.mk
index 15ba27f..a9caa20 100644
--- a/Android.mk
+++ b/Android.mk
@@ -114,6 +114,8 @@
 	core/java/android/os/IParentalControlCallback.aidl \
 	core/java/android/os/IPermissionController.aidl \
 	core/java/android/os/IPowerManager.aidl \
+    core/java/android/service/wallpaper/IWallpaperConnection.aidl \
+    core/java/android/service/wallpaper/IWallpaperEngine.aidl \
     core/java/android/service/wallpaper/IWallpaperService.aidl \
 	core/java/android/text/IClipboard.aidl \
 	core/java/android/view/accessibility/IAccessibilityManager.aidl \
diff --git a/api/current.xml b/api/current.xml
index c2960a0..853ac9f 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -177,6 +177,17 @@
  visibility="public"
 >
 </field>
+<field name="BIND_WALLPAPER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.BIND_WALLPAPER&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="BLUETOOTH"
  type="java.lang.String"
  transient="false"
@@ -26763,17 +26774,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="get"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getDesiredMinimumHeight"
  return="int"
  abstract="false"
@@ -26796,7 +26796,7 @@
  visibility="public"
 >
 </method>
-<method name="peek"
+<method name="getDrawable"
  return="android.graphics.drawable.Drawable"
  abstract="false"
  native="false"
@@ -26807,8 +26807,21 @@
  visibility="public"
 >
 </method>
-<method name="set"
- return="void"
+<method name="getInstance"
+ return="android.app.WallpaperManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="peekDrawable"
+ return="android.graphics.drawable.Drawable"
  abstract="false"
  native="false"
  synchronized="false"
@@ -26817,12 +26830,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="resid" type="int">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
 </method>
-<method name="set"
+<method name="setBitmap"
  return="void"
  abstract="false"
  native="false"
@@ -26837,7 +26846,22 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="set"
+<method name="setResource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resid" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setStream"
  return="void"
  abstract="false"
  native="false"
@@ -26852,7 +26876,7 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="setDimensionHints"
+<method name="suggestDesiredDimensions"
  return="void"
  abstract="false"
  native="false"
@@ -115993,50 +116017,6 @@
 </implements>
 </interface>
 </package>
-<package name="android.service.wallpaper"
->
-<class name="WallpaperService"
- extends="android.app.Service"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="WallpaperService"
- type="android.service.wallpaper.WallpaperService"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="onBind"
- return="android.os.IBinder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-</method>
-<field name="SERVICE_INTERFACE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.service.wallpaper.WallpaperService&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-</package>
 <package name="android.speech"
 >
 <class name="RecognizerIntent"
@@ -157363,6 +157343,28 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_SHOW_WALLPAPER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1048576"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_SHOW_WHEN_LOCKED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="524288"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_TOUCHABLE_WHEN_WAKING"
  type="int"
  transient="false"
@@ -157913,6 +157915,17 @@
  visibility="public"
 >
 </field>
+<field name="TYPE_WALLPAPER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2013"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="alpha"
  type="float"
  transient="false"
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
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index c5fd985..06565c7 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -22,19 +22,30 @@
 import android.app.IWallpaperManager;
 import android.app.IWallpaperManagerCallback;
 import android.backup.BackupManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.FileObserver;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallbackList;
-import android.util.Config;
+import android.os.ServiceManager;
+import android.service.wallpaper.IWallpaperConnection;
+import android.service.wallpaper.IWallpaperEngine;
+import android.service.wallpaper.IWallpaperService;
+import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.util.Xml;
+import android.view.IWindowManager;
+import android.view.WindowManager;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -42,6 +53,7 @@
 import java.io.FileNotFoundException;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.util.List;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -50,9 +62,10 @@
 import com.android.internal.util.FastXmlSerializer;
 
 class WallpaperManagerService extends IWallpaperManager.Stub {
-    private static final String TAG = "WallpaperService";
+    static final String TAG = "WallpaperService";
+    static final boolean DEBUG = true;
 
-    private Object mLock = new Object();
+    Object mLock = new Object();
 
     private static final File WALLPAPER_DIR = new File(
             "/data/data/com.android.settings/files");
@@ -94,15 +107,60 @@
                 }
             };
     
-    private final Context mContext;
+    final Context mContext;
+    final IWindowManager mIWindowManager;
 
-    private int mWidth = -1;
-    private int mHeight = -1;
-    private String mName = "";
+    int mWidth = -1;
+    int mHeight = -1;
+    String mName = "";
+    ComponentName mWallpaperComponent;
+    WallpaperConnection mWallpaperConnection;
+    
+    class WallpaperConnection extends IWallpaperConnection.Stub
+            implements ServiceConnection {
+        final Binder mToken = new Binder();
+        IWallpaperService mService;
+        IWallpaperEngine mEngine;
 
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (mLock) {
+                if (mWallpaperConnection == this) {
+                    mService = IWallpaperService.Stub.asInterface(service);
+                    attachServiceLocked(this);
+                }
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            synchronized (mLock) {
+                mService = null;
+                mEngine = null;
+            }
+        }
+        
+        public void attachEngine(IWallpaperEngine engine) {
+            mEngine = engine;
+        }
+        
+        public ParcelFileDescriptor setWallpaper(String name) {
+            synchronized (mLock) {
+                if (mWallpaperConnection == this) {
+                    ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name);
+                    if (pfd != null) {
+                        saveSettingsLocked();
+                    }
+                    return pfd;
+                }
+                return null;
+            }
+        }
+    }
+    
     public WallpaperManagerService(Context context) {
-        if (Config.LOGD) Log.d(TAG, "WallpaperService startup");
+        if (DEBUG) Log.d(TAG, "WallpaperService startup");
         mContext = context;
+        mIWindowManager = IWindowManager.Stub.asInterface(
+                ServiceManager.getService(Context.WINDOW_SERVICE));
         WALLPAPER_DIR.mkdirs();
         loadSettingsLocked();
         mWallpaperObserver.startWatching();
@@ -162,7 +220,7 @@
                 return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
             } catch (FileNotFoundException e) {
                 /* Shouldn't happen as we check to see if the file exists */
-                if (Config.LOGD) Log.d(TAG, "Error getting wallpaper", e);
+                Log.w(TAG, "Error getting wallpaper", e);
             }
             return null;
         }
@@ -171,20 +229,108 @@
     public ParcelFileDescriptor setWallpaper(String name) {
         checkPermission(android.Manifest.permission.SET_WALLPAPER);
         synchronized (mLock) {
-            if (name == null) name = "";
-            mName = name;
-            saveSettingsLocked();
-            try {
-                ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
-                        MODE_CREATE|MODE_READ_WRITE);
-                return fd;
-            } catch (FileNotFoundException e) {
-                if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e);
+            ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name);
+            if (pfd != null) {
+                clearWallpaperComponentLocked();
+                saveSettingsLocked();
             }
-            return null;
+            return pfd;
         }
     }
 
+    ParcelFileDescriptor updateWallpaperBitmapLocked(String name) {
+        if (name == null) name = "";
+        try {
+            ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+                    MODE_CREATE|MODE_READ_WRITE);
+            mName = name;
+            return fd;
+        } catch (FileNotFoundException e) {
+            Log.w(TAG, "Error setting wallpaper", e);
+        }
+        return null;
+    }
+
+    public void setWallpaperComponent(ComponentName name) {
+        checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
+        synchronized (mLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                ServiceInfo si = mContext.getPackageManager().getServiceInfo(name,
+                        PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS);
+                if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
+                    throw new SecurityException("Selected service does not require "
+                            + android.Manifest.permission.BIND_WALLPAPER
+                            + ": " + name);
+                }
+                
+                // Make sure the selected service is actually a wallpaper service.
+                Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+                List<ResolveInfo> ris = mContext.getPackageManager()
+                        .queryIntentServices(intent, 0);
+                for (int i=0; i<ris.size(); i++) {
+                    ServiceInfo rsi = ris.get(i).serviceInfo;
+                    if (rsi.name.equals(si.name) &&
+                            rsi.packageName.equals(si.packageName)) {
+                        ris = null;
+                        break;
+                    }
+                }
+                if (ris != null) {
+                    throw new SecurityException("Selected service is not a wallpaper: "
+                            + name);
+                }
+                
+                // Bind the service!
+                WallpaperConnection newConn = new WallpaperConnection();
+                intent.setComponent(name);
+                if (!mContext.bindService(intent, newConn,
+                        Context.BIND_AUTO_CREATE)) {
+                    throw new IllegalArgumentException("Unable to bind service: "
+                            + name);
+                }
+                
+                clearWallpaperComponentLocked();
+                mWallpaperComponent = null;
+                mWallpaperConnection = newConn;
+                try {
+                    if (DEBUG) Log.v(TAG, "Adding window token: " + newConn.mToken);
+                    mIWindowManager.addWindowToken(newConn.mToken,
+                            WindowManager.LayoutParams.TYPE_WALLPAPER);
+                } catch (RemoteException e) {
+                }
+                
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new IllegalArgumentException("Unknown component " + name);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+    
+    void clearWallpaperComponentLocked() {
+        mWallpaperComponent = null;
+        if (mWallpaperConnection != null) {
+            if (mWallpaperConnection.mEngine != null) {
+                try {
+                    mWallpaperConnection.mEngine.destroy();
+                } catch (RemoteException e) {
+                }
+            }
+            mContext.unbindService(mWallpaperConnection);
+            mWallpaperConnection = null;
+        }
+    }
+    
+    void attachServiceLocked(WallpaperConnection conn) {
+        try {
+            conn.mService.attach(conn, conn.mToken, mWidth, mHeight);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed attaching wallpaper; clearing", e);
+            clearWallpaperComponentLocked();
+        }
+    }
+    
     private void notifyCallbacksLocked() {
         final int n = mCallbacks.beginBroadcast();
         for (int i = 0; i < n; i++) {
@@ -226,6 +372,10 @@
             out.attribute(null, "width", Integer.toString(mWidth));
             out.attribute(null, "height", Integer.toString(mHeight));
             out.attribute(null, "name", mName);
+            if (mWallpaperComponent != null) {
+                out.attribute(null, "component",
+                        mWallpaperComponent.flattenToShortString());
+            }
             out.endTag(null, "wp");
 
             out.endDocument();
@@ -254,7 +404,6 @@
             parser.setInput(stream, null);
 
             int type;
-            int providerIndex = 0;
             do {
                 type = parser.next();
                 if (type == XmlPullParser.START_TAG) {
@@ -263,6 +412,10 @@
                         mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
                         mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
                         mName = parser.getAttributeValue(null, "name");
+                        String comp = parser.getAttributeValue(null, "component");
+                        mWallpaperComponent = comp != null
+                                ? ComponentName.unflattenFromString(comp)
+                                : null;
                     }
                 }
             } while (type != XmlPullParser.END_DOCUMENT);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index ceb9c41..1b7efeb 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -31,6 +31,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_GPU;
@@ -40,6 +41,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.policy.PolicyManager;
@@ -399,6 +401,8 @@
     WindowState mInputMethodWindow = null;
     final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>();
 
+    final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>();
+    
     AppWindowToken mFocusedApp = null;
 
     PowerManagerService mPowerManager;
@@ -1167,6 +1171,83 @@
         moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
     }
 
+    boolean adjustWallpaperWindowsLocked() {
+        boolean changed = false;
+        
+        // First find top-most window that has asked to be on top of the
+        // wallpaper; all wallpapers go behind it.
+        final ArrayList localmWindows = mWindows;
+        int N = localmWindows.size();
+        WindowState w = null;
+        int i = N;
+        while (i > 0) {
+            i--;
+            w = (WindowState)localmWindows.get(i);
+            if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isVisibleOrAdding()) {
+                break;
+            }
+        }
+
+        if (w != null) {
+            // Now w is the window we are supposed to be behind...  but we
+            // need to be sure to also be behind any of its attached windows,
+            // AND any starting window associated with it.
+            while (i > 0) {
+                WindowState wb = (WindowState)localmWindows.get(i-1);
+                if (wb.mAttachedWindow != w &&
+                        (wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
+                                wb.mToken != w.mToken)) {
+                    // This window is not related to the previous one in any
+                    // interesting way, so stop here.
+                    break;
+                }
+                w = wb;
+                i--;
+            }
+        }
+        
+        // Okay i is the position immediately above the wallpaper.  Look at
+        // what is below it for later.
+        w = i > 0 ? (WindowState)localmWindows.get(i-1) : null;
+        
+        // Start stepping backwards from here, ensuring that our wallpaper windows
+        // are correctly placed.
+        int curTokenIndex = mWallpaperTokens.size();
+        while (curTokenIndex > 0) {
+            curTokenIndex--;
+            WindowToken token = mWallpaperTokens.get(curTokenIndex);
+            int curWallpaperIndex = token.windows.size();
+            while (curWallpaperIndex > 0) {
+                curWallpaperIndex--;
+                WindowState wallpaper = token.windows.get(curWallpaperIndex);
+                // First, if this window is at the current index, then all
+                // is well.
+                if (wallpaper == w) {
+                    i--;
+                    w = i > 0 ? (WindowState)localmWindows.get(i-1) : null;
+                    continue;
+                }
+                
+                // The window didn't match...  the current wallpaper window,
+                // wherever it is, is in the wrong place, so make sure it is
+                // not in the list.
+                int oldIndex = localmWindows.indexOf(wallpaper);
+                if (oldIndex >= 0) {
+                    localmWindows.remove(oldIndex);
+                    if (oldIndex < i) {
+                        i--;
+                    }
+                }
+                
+                // Now stick it in.
+                localmWindows.add(i, wallpaper);
+                changed = true;
+            }
+        }
+        
+        return changed;
+    }
+
     public int addWindow(Session session, IWindow client,
             WindowManager.LayoutParams attrs, int viewVisibility,
             Rect outContentInsets) {
@@ -1224,6 +1305,11 @@
                           + attrs.token + ".  Aborting.");
                     return WindowManagerImpl.ADD_BAD_APP_TOKEN;
                 }
+                if (attrs.type == TYPE_WALLPAPER) {
+                    Log.w(TAG, "Attempted to add wallpaper window with unknown token "
+                          + attrs.token + ".  Aborting.");
+                    return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+                }
                 token = new WindowToken(attrs.token, -1, false);
                 addToken = true;
             } else if (attrs.type >= FIRST_APPLICATION_WINDOW
@@ -1250,6 +1336,12 @@
                             + attrs.token + ".  Aborting.");
                       return WindowManagerImpl.ADD_BAD_APP_TOKEN;
                 }
+            } else if (attrs.type == TYPE_WALLPAPER) {
+                if (token.windowType != TYPE_WALLPAPER) {
+                    Log.w(TAG, "Attempted to add wallpaper window with bad token "
+                            + attrs.token + ".  Aborting.");
+                      return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+                }
             }
 
             win = new WindowState(session, client, token,
@@ -1300,6 +1392,10 @@
                 imMayMove = false;
             } else {
                 addWindowToListInOrderLocked(win, true);
+                if (attrs.type == TYPE_WALLPAPER ||
+                        (attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+                    adjustWallpaperWindowsLocked();
+                }
             }
 
             win.mEnterAnimationPending = true;
@@ -1461,6 +1557,11 @@
             mInputMethodDialogs.remove(win);
         }
 
+        if (win.mAttrs.type == TYPE_WALLPAPER ||
+                (win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+            adjustWallpaperWindowsLocked();
+        }
+        
         final WindowToken token = win.mToken;
         final AppWindowToken atoken = win.mAppToken;
         token.windows.remove(win);
@@ -1618,6 +1719,9 @@
                     || ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
                     || (!win.mRelayoutCalled);
 
+            boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
+                    && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+            
             win.mRelayoutCalled = true;
             final int oldVisibility = win.mViewVisibility;
             win.mViewVisibility = viewVisibility;
@@ -1715,6 +1819,11 @@
                     assignLayers = true;
                 }
             }
+            if (wallpaperMayMove) {
+                if (adjustWallpaperWindowsLocked()) {
+                    assignLayers = true;
+                }
+            }
 
             mLayoutNeeded = true;
             win.mGivenInsetsPending = insetsPending;
@@ -2010,6 +2119,9 @@
             wtoken = new WindowToken(token, type, true);
             mTokenMap.put(token, wtoken);
             mTokenList.add(wtoken);
+            if (type == TYPE_WALLPAPER) {
+                mWallpaperTokens.add(wtoken);
+            }
         }
     }
 
@@ -2055,6 +2167,8 @@
 
                     if (delayed) {
                         mExitingTokens.add(wtoken);
+                    } else if (wtoken.windowType == TYPE_WALLPAPER) {
+                        mWallpaperTokens.remove(wtoken);
                     }
                 }
 
@@ -5719,6 +5833,8 @@
         final int mSubLayer;
         final boolean mLayoutAttached;
         final boolean mIsImWindow;
+        final boolean mIsWallpaper;
+        final boolean mIsFloatingLayer;
         int mViewVisibility;
         boolean mPolicyVisibility = true;
         boolean mPolicyVisibilityAfterAnim = true;
@@ -5876,6 +5992,8 @@
                 mAttachedWindow = null;
                 mLayoutAttached = false;
                 mIsImWindow = false;
+                mIsWallpaper = false;
+                mIsFloatingLayer = false;
                 mBaseLayer = 0;
                 mSubLayer = 0;
                 return;
@@ -5896,6 +6014,8 @@
                         WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
                 mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD
                         || attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+                mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER;
+                mIsFloatingLayer = mIsImWindow || mIsWallpaper;
             } else {
                 // The multiplier here is to reserve space for multiple
                 // windows in the same type layer.
@@ -5907,6 +6027,8 @@
                 mLayoutAttached = false;
                 mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
                         || mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+                mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
+                mIsFloatingLayer = mIsImWindow || mIsWallpaper;
             }
 
             WindowState appWin = this;
@@ -6711,7 +6833,7 @@
 
         boolean isFullscreen(int screenWidth, int screenHeight) {
             return mFrame.left <= 0 && mFrame.top <= 0 &&
-                mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
+                    mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
         }
 
         void removeLocked() {
@@ -6801,8 +6923,10 @@
                 pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow);
                         pw.print(" mLayoutAttached="); pw.println(mLayoutAttached);
             }
-            if (mIsImWindow) {
-                pw.print(prefix); pw.print("mIsImWindow="); pw.println(mIsImWindow);
+            if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
+                pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow);
+                        pw.print(" mIsWallpaper="); pw.print(mIsWallpaper);
+                        pw.print(" mIsFloatingLayer="); pw.println(mIsFloatingLayer);
             }
             pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
                     pw.print(" mSubLayer="); pw.print(mSubLayer);
@@ -7838,7 +7962,7 @@
 
         for (i=0; i<N; i++) {
             WindowState w = (WindowState)mWindows.get(i);
-            if (w.mBaseLayer == curBaseLayer || w.mIsImWindow) {
+            if (w.mBaseLayer == curBaseLayer || w.mIsFloatingLayer) {
                 curLayer += WINDOW_LAYER_MULTIPLIER;
                 w.mLayer = curLayer;
             } else {
@@ -8242,6 +8366,7 @@
                         // This has changed the visibility of windows, so perform
                         // a new layout to get them all up-to-date.
                         mLayoutNeeded = true;
+                        adjustWallpaperWindowsLocked();
                         if (!moveInputMethodWindowsIfNeededLocked(true)) {
                             assignLayersLocked();
                         }
@@ -8701,6 +8826,9 @@
             WindowToken token = mExitingTokens.get(i);
             if (!token.hasVisible) {
                 mExitingTokens.remove(i);
+                if (token.windowType == TYPE_WALLPAPER) {
+                    mWallpaperTokens.remove(token);
+                }
             }
         }
 
@@ -9123,6 +9251,16 @@
                             pw.println(mTokenList.get(i));
                 }
             }
+            if (mWallpaperTokens.size() > 0) {
+                pw.println(" ");
+                pw.println("  Wallpaper tokens:");
+                for (int i=mWallpaperTokens.size()-1; i>=0; i--) {
+                    WindowToken token = mWallpaperTokens.get(i);
+                    pw.print("  Wallpaper #"); pw.print(i);
+                            pw.print(' '); pw.print(token); pw.println(':');
+                    token.dump(pw, "    ");
+                }
+            }
             if (mAppTokens.size() > 0) {
                 pw.println(" ");
                 pw.println("  Application tokens in Z order:");