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/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:");