diff --git a/Android.mk b/Android.mk
index 15ba27ff9759c..a9caa20e61fc1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -114,6 +114,8 @@ LOCAL_SRC_FILES += \
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 c2960a0487292..853ac9fce56af 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -177,6 +177,17 @@
visibility="public"
>
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
mCallbacks
+ = new ArrayList();
+
+ 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 69ef96c8c5ce8..cf85af53592d5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -894,6 +894,13 @@
android:description="@string/permdesc_bindInputMethod"
android:protectionLevel="signature" />
+
+
+
+
+
+
+ android:exported="true" />
+
+
+
+
+
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 558d91e514fa9..68f20700ea52f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -598,6 +598,12 @@
Allows the holder to bind to the top-level
interface of an input method. Should never be needed for normal applications.
+
+ bind to a wallpaper
+
+ Allows the holder to bind to the top-level
+ interface of a wallpaper. Should never be needed for normal applications.
+
change screen orientation
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index c5fd9850dff3f..06565c74432c9 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -22,19 +22,30 @@ import static android.os.ParcelFileDescriptor.*;
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.File;
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 org.xmlpull.v1.XmlSerializer;
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 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
};
- 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 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
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 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
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 ris = mContext.getPackageManager()
+ .queryIntentServices(intent, 0);
+ for (int i=0; i mInputMethodDialogs = new ArrayList();
+ final ArrayList mWallpaperTokens = new ArrayList();
+
AppWindowToken mFocusedApp = null;
PowerManagerService mPowerManager;
@@ -1167,6 +1171,83 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
+ 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 @@ public class WindowManagerService extends IWindowManager.Stub
+ 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 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
|| ((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 @@ public class WindowManagerService extends IWindowManager.Stub
assignLayers = true;
}
}
+ if (wallpaperMayMove) {
+ if (adjustWallpaperWindowsLocked()) {
+ assignLayers = true;
+ }
+ }
mLayoutNeeded = true;
win.mGivenInsetsPending = insetsPending;
@@ -2010,6 +2119,9 @@ public class WindowManagerService extends IWindowManager.Stub
wtoken = new WindowToken(token, type, true);
mTokenMap.put(token, wtoken);
mTokenList.add(wtoken);
+ if (type == TYPE_WALLPAPER) {
+ mWallpaperTokens.add(wtoken);
+ }
}
}
@@ -2055,6 +2167,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (delayed) {
mExitingTokens.add(wtoken);
+ } else if (wtoken.windowType == TYPE_WALLPAPER) {
+ mWallpaperTokens.remove(wtoken);
}
}
@@ -5719,6 +5833,8 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
mAttachedWindow = null;
mLayoutAttached = false;
mIsImWindow = false;
+ mIsWallpaper = false;
+ mIsFloatingLayer = false;
mBaseLayer = 0;
mSubLayer = 0;
return;
@@ -5896,6 +6014,8 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
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 @@ public class WindowManagerService extends IWindowManager.Stub
for (i=0; i 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:");