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