From 4c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sat, 8 Aug 2009 20:40:27 -0700 Subject: [PATCH] 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. --- Android.mk | 2 + api/current.xml | 143 +++---- core/java/android/app/ApplicationContext.java | 8 +- core/java/android/app/IWallpaperManager.aidl | 6 + core/java/android/app/WallpaperManager.java | 63 +++- core/java/android/content/Context.java | 8 +- .../wallpaper/IWallpaperConnection.aidl | 28 ++ .../service/wallpaper/IWallpaperEngine.aidl | 24 ++ .../service/wallpaper/IWallpaperService.aidl | 5 +- .../service/wallpaper/WallpaperService.java | 354 ++++++++++++++++-- core/java/android/view/ViewRoot.java | 31 +- core/java/android/view/WindowManager.java | 19 +- .../view/inputmethod/InputMethodManager.java | 11 +- .../android/internal/os/HandlerCaller.java | 8 + .../service/wallpaper/ImageWallpaper.java | 79 ++++ .../android/internal/view/BaseIWindow.java | 68 ++++ .../internal/view/BaseSurfaceHolder.java | 169 +++++++++ core/res/AndroidManifest.xml | 22 +- core/res/res/values/strings.xml | 6 + .../server/WallpaperManagerService.java | 193 +++++++++- .../android/server/WindowManagerService.java | 146 +++++++- 21 files changed, 1229 insertions(+), 164 deletions(-) create mode 100644 core/java/android/service/wallpaper/IWallpaperConnection.aidl create mode 100644 core/java/android/service/wallpaper/IWallpaperEngine.aidl create mode 100644 core/java/com/android/internal/service/wallpaper/ImageWallpaper.java create mode 100644 core/java/com/android/internal/view/BaseIWindow.java create mode 100644 core/java/com/android/internal/view/BaseSurfaceHolder.java 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:");