diff options
29 files changed, 962 insertions, 83 deletions
diff --git a/api/current.txt b/api/current.txt index 4398ea32be57..8747e9a44933 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2744,6 +2744,7 @@ package android.app { method public static boolean isUserAMonkey(); method public void killBackgroundProcesses(java.lang.String); method public void moveTaskToFront(int, int); + method public void moveTaskToFront(int, int, android.os.Bundle); method public deprecated void restartPackage(java.lang.String); field public static final int MOVE_TASK_NO_USER_ACTION = 2; // 0x2 field public static final int MOVE_TASK_WITH_HOME = 1; // 0x1 @@ -2867,9 +2868,14 @@ package android.app { public class ActivityOptions { method public void join(android.app.ActivityOptions); method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int); + method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int, android.app.ActivityOptions.OnAnimationStartedListener); method public android.os.Bundle toBundle(); } + public static abstract interface ActivityOptions.OnAnimationStartedListener { + method public abstract void onAnimationStarted(); + } + public class AlarmManager { method public void cancel(android.app.PendingIntent); method public void set(int, long, android.app.PendingIntent); @@ -7358,6 +7364,7 @@ package android.database.sqlite { method public static int releaseMemory(); method public long replace(java.lang.String, java.lang.String, android.content.ContentValues); method public long replaceOrThrow(java.lang.String, java.lang.String, android.content.ContentValues) throws android.database.SQLException; + method public void setForeignKeyConstraintsEnabled(boolean); method public void setLocale(java.util.Locale); method public deprecated void setLockingEnabled(boolean); method public void setMaxSqlCacheSize(int); @@ -7437,6 +7444,7 @@ package android.database.sqlite { method public java.lang.String getDatabaseName(); method public android.database.sqlite.SQLiteDatabase getReadableDatabase(); method public android.database.sqlite.SQLiteDatabase getWritableDatabase(); + method public void onConfigure(android.database.sqlite.SQLiteDatabase); method public abstract void onCreate(android.database.sqlite.SQLiteDatabase); method public void onDowngrade(android.database.sqlite.SQLiteDatabase, int, int); method public void onOpen(android.database.sqlite.SQLiteDatabase); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ea3274595ad4..b277efb625d9 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3639,7 +3639,7 @@ public class Activity extends ContextThemeWrapper */ public void startActivityFromChild(Activity child, Intent intent, int requestCode) { - startActivityFromChild(child, intent, requestCode); + startActivityFromChild(child, intent, requestCode, null); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d056b171d453..531a6952c21d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -31,6 +31,7 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Point; import android.os.Binder; +import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.Parcel; @@ -816,6 +817,19 @@ public class ActivityManager { public static final int MOVE_TASK_NO_USER_ACTION = 0x00000002; /** + * Equivalent to calling {@link #moveTaskToFront(int, int, Bundle)} + * with a null options argument. + * + * @param taskId The identifier of the task to be moved, as found in + * {@link RunningTaskInfo} or {@link RecentTaskInfo}. + * @param flags Additional operational flags, 0 or more of + * {@link #MOVE_TASK_WITH_HOME}. + */ + public void moveTaskToFront(int taskId, int flags) { + moveTaskToFront(taskId, flags, null); + } + + /** * Ask that the task associated with a given task ID be moved to the * front of the stack, so it is now visible to the user. Requires that * the caller hold permission {@link android.Manifest.permission#REORDER_TASKS} @@ -825,10 +839,13 @@ public class ActivityManager { * {@link RunningTaskInfo} or {@link RecentTaskInfo}. * @param flags Additional operational flags, 0 or more of * {@link #MOVE_TASK_WITH_HOME}. + * @param options Additional options for the operation, either null or + * as per {@link Context#startActivity(Intent, android.os.Bundle) + * Context.startActivity(Intent, Bundle)}. */ - public void moveTaskToFront(int taskId, int flags) { + public void moveTaskToFront(int taskId, int flags, Bundle options) { try { - ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags); + ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options); } catch (RemoteException e) { // System dead, we will be dead too soon! } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a3cc352f75d5..c4023298920d 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -510,7 +510,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); int task = data.readInt(); int fl = data.readInt(); - moveTaskToFront(task, fl); + Bundle options = data.readInt() != 0 + ? Bundle.CREATOR.createFromParcel(data) : null; + moveTaskToFront(task, fl, options); reply.writeNoException(); return true; } @@ -1055,6 +1057,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String reason = data.readString(); + boolean res = killProcessesBelowForeground(reason); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + case START_RUNNING_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String pkg = data.readString(); @@ -2134,13 +2145,19 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return list; } - public void moveTaskToFront(int task, int flags) throws RemoteException + public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(task); data.writeInt(flags); + if (options != null) { + data.writeInt(1); + options.writeToParcel(data, 0); + } else { + data.writeInt(0); + } mRemote.transact(MOVE_TASK_TO_FRONT_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); @@ -2902,6 +2919,18 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return res; } + @Override + public boolean killProcessesBelowForeground(String reason) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(reason); + mRemote.transact(KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION, data, reply, 0); + boolean res = reply.readInt() != 0; + data.recycle(); + reply.recycle(); + return res; + } public void startRunning(String pkg, String cls, String action, String indata) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 03bc338b28ef..c637df0ba07d 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -17,7 +17,13 @@ package android.app; import android.content.Context; +import android.graphics.Bitmap; import android.os.Bundle; +import android.os.Handler; +import android.os.IRemoteCallback; +import android.os.Message; +import android.os.RemoteException; +import android.view.View; /** * Helper class for building an options Bundle that can be used with @@ -32,6 +38,12 @@ public class ActivityOptions { public static final String KEY_PACKAGE_NAME = "android:packageName"; /** + * Type of animation that arguments specify. + * @hide + */ + public static final String KEY_ANIM_TYPE = "android:animType"; + + /** * Custom enter animation resource ID. * @hide */ @@ -43,10 +55,45 @@ public class ActivityOptions { */ public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes"; + /** + * Bitmap for thumbnail animation. + * @hide + */ + public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail"; + + /** + * Start X position of thumbnail animation. + * @hide + */ + public static final String KEY_ANIM_START_X = "android:animStartX"; + + /** + * Start Y position of thumbnail animation. + * @hide + */ + public static final String KEY_ANIM_START_Y = "android:animStartY"; + + /** + * Callback for when animation is started. + * @hide + */ + public static final String KEY_ANIM_START_LISTENER = "android:animStartListener"; + + /** @hide */ + public static final int ANIM_NONE = 0; + /** @hide */ + public static final int ANIM_CUSTOM = 1; + /** @hide */ + public static final int ANIM_THUMBNAIL = 2; + private String mPackageName; - private boolean mIsCustomAnimation; + private int mAnimationType = ANIM_NONE; private int mCustomEnterResId; private int mCustomExitResId; + private Bitmap mThumbnail; + private int mStartX; + private int mStartY; + private IRemoteCallback mAnimationStartedListener; /** * Create an ActivityOptions specifying a custom animation to run when @@ -65,22 +112,79 @@ public class ActivityOptions { int enterResId, int exitResId) { ActivityOptions opts = new ActivityOptions(); opts.mPackageName = context.getPackageName(); - opts.mIsCustomAnimation = true; + opts.mAnimationType = ANIM_CUSTOM; opts.mCustomEnterResId = enterResId; opts.mCustomExitResId = exitResId; return opts; } + /** + * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation} + * to find out when the given animation has started running. + */ + public interface OnAnimationStartedListener { + void onAnimationStarted(); + } + + /** + * Create an ActivityOptions specifying an animation where a thumbnail + * is scaled from a given position to the new activity window that is + * being started. + * + * @param source The View that this thumbnail is animating from. This + * defines the coordinate space for <var>startX</var> and <var>startY</var>. + * @param thumbnail The bitmap that will be shown as the initial thumbnail + * of the animation. + * @param startX The x starting location of the bitmap, in screen coordiantes. + * @param startY The y starting location of the bitmap, in screen coordinates. + * @param listener Optional OnAnimationStartedListener to find out when the + * requested animation has started running. If for some reason the animation + * is not executed, the callback will happen immediately. + * @return Returns a new ActivityOptions object that you can use to + * supply these options as the options Bundle when starting an activity. + */ + public static ActivityOptions makeThumbnailScaleUpAnimation(View source, + Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { + ActivityOptions opts = new ActivityOptions(); + opts.mPackageName = source.getContext().getPackageName(); + opts.mAnimationType = ANIM_THUMBNAIL; + opts.mThumbnail = thumbnail; + int[] pts = new int[2]; + source.getLocationOnScreen(pts); + opts.mStartX = pts[0] + startX; + opts.mStartY = pts[1] + startY; + if (listener != null) { + final Handler h = source.getHandler(); + final OnAnimationStartedListener finalListener = listener; + opts.mAnimationStartedListener = new IRemoteCallback.Stub() { + @Override public void sendResult(Bundle data) throws RemoteException { + h.post(new Runnable() { + @Override public void run() { + finalListener.onAnimationStarted(); + } + }); + } + }; + } + return opts; + } + private ActivityOptions() { } /** @hide */ public ActivityOptions(Bundle opts) { mPackageName = opts.getString(KEY_PACKAGE_NAME); - if (opts.containsKey(KEY_ANIM_ENTER_RES_ID)) { - mIsCustomAnimation = true; + mAnimationType = opts.getInt(KEY_ANIM_TYPE); + if (mAnimationType == ANIM_CUSTOM) { mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0); mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0); + } else if (mAnimationType == ANIM_THUMBNAIL) { + mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL); + mStartX = opts.getInt(KEY_ANIM_START_X, 0); + mStartY = opts.getInt(KEY_ANIM_START_Y, 0); + mAnimationStartedListener = IRemoteCallback.Stub.asInterface( + opts.getIBinder(KEY_ANIM_START_LISTENER)); } } @@ -90,8 +194,8 @@ public class ActivityOptions { } /** @hide */ - public boolean isCustomAnimation() { - return mIsCustomAnimation; + public int getAnimationType() { + return mAnimationType; } /** @hide */ @@ -104,6 +208,43 @@ public class ActivityOptions { return mCustomExitResId; } + /** @hide */ + public Bitmap getThumbnail() { + return mThumbnail; + } + + /** @hide */ + public int getStartX() { + return mStartX; + } + + /** @hide */ + public int getStartY() { + return mStartY; + } + + /** @hide */ + public IRemoteCallback getOnAnimationStartListener() { + return mAnimationStartedListener; + } + + /** @hide */ + public void abort() { + if (mAnimationStartedListener != null) { + try { + mAnimationStartedListener.sendResult(null); + } catch (RemoteException e) { + } + } + } + + /** @hide */ + public static void abort(Bundle options) { + if (options != null) { + (new ActivityOptions(options)).abort(); + } + } + /** * Join the values in <var>otherOptions</var> in to this one. Any values * defined in <var>otherOptions</var> replace those in the base options. @@ -112,10 +253,27 @@ public class ActivityOptions { if (otherOptions.mPackageName != null) { mPackageName = otherOptions.mPackageName; } - if (otherOptions.mIsCustomAnimation) { - mIsCustomAnimation = true; - mCustomEnterResId = otherOptions.mCustomEnterResId; - mCustomExitResId = otherOptions.mCustomExitResId; + switch (otherOptions.mAnimationType) { + case ANIM_CUSTOM: + mAnimationType = otherOptions.mAnimationType; + mCustomEnterResId = otherOptions.mCustomEnterResId; + mCustomExitResId = otherOptions.mCustomExitResId; + mThumbnail = null; + mAnimationStartedListener = null; + break; + case ANIM_THUMBNAIL: + mAnimationType = otherOptions.mAnimationType; + mThumbnail = otherOptions.mThumbnail; + mStartX = otherOptions.mStartX; + mStartY = otherOptions.mStartY; + if (otherOptions.mAnimationStartedListener != null) { + try { + otherOptions.mAnimationStartedListener.sendResult(null); + } catch (RemoteException e) { + } + } + mAnimationStartedListener = otherOptions.mAnimationStartedListener; + break; } } @@ -132,9 +290,19 @@ public class ActivityOptions { if (mPackageName != null) { b.putString(KEY_PACKAGE_NAME, mPackageName); } - if (mIsCustomAnimation) { - b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId); - b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId); + switch (mAnimationType) { + case ANIM_CUSTOM: + b.putInt(KEY_ANIM_TYPE, mAnimationType); + b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId); + b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId); + break; + case ANIM_THUMBNAIL: + b.putInt(KEY_ANIM_TYPE, mAnimationType); + b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); + b.putInt(KEY_ANIM_START_X, mStartX); + b.putInt(KEY_ANIM_START_Y, mStartY); + b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener + != null ? mAnimationStartedListener.asBinder() : null); } return b; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 31066b5bb584..1d994d8be3f1 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -105,7 +105,7 @@ public interface IActivityManager extends IInterface { public List getServices(int maxNum, int flags) throws RemoteException; public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() throws RemoteException; - public void moveTaskToFront(int task, int flags) throws RemoteException; + public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException; public void moveTaskToBack(int task) throws RemoteException; public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException; public void moveTaskBackwards(int task) throws RemoteException; @@ -216,9 +216,10 @@ public interface IActivityManager extends IInterface { public void enterSafeMode() throws RemoteException; public void noteWakeupAlarm(IIntentSender sender) throws RemoteException; - + public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException; - + public boolean killProcessesBelowForeground(String reason) throws RemoteException; + // Special low-level communication with activity manager. public void startRunning(String pkg, String cls, String action, String data) throws RemoteException; @@ -573,4 +574,5 @@ public interface IActivityManager extends IInterface { int GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+140; int REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+141; int GET_MY_MEMORY_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+142; + int KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+143; } diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index e999316b5504..254f652d8987 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -211,6 +211,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME); setPageSize(); + setForeignKeyModeFromConfiguration(); setWalModeFromConfiguration(); setJournalSizeLimit(); setAutoCheckpointInterval(); @@ -267,6 +268,16 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } } + private void setForeignKeyModeFromConfiguration() { + if (!mIsReadOnlyConnection) { + final long newValue = mConfiguration.foreignKeyConstraintsEnabled ? 1 : 0; + long value = executeForLong("PRAGMA foreign_keys", null, null); + if (value != newValue) { + execute("PRAGMA foreign_keys=" + newValue, null, null); + } + } + } + private void setWalModeFromConfiguration() { if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) { if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) { @@ -389,6 +400,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } // Remember what changed. + boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled + != mConfiguration.foreignKeyConstraintsEnabled; boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags) & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0; boolean localeChanged = !configuration.locale.equals(mConfiguration.locale); @@ -399,6 +412,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen // Update prepared statement cache size. mPreparedStatementCache.resize(configuration.maxSqlCacheSize); + // Update foreign key mode. + if (foreignKeyModeChanged) { + setForeignKeyModeFromConfiguration(); + } + // Update WAL. if (walModeChanged) { setWalModeFromConfiguration(); diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index 0538ce454f74..5c8e38bfa186 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -277,6 +277,20 @@ public final class SQLiteConnectionPool implements Closeable { assert mAvailableNonPrimaryConnections.isEmpty(); } + boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled + != mConfiguration.foreignKeyConstraintsEnabled; + if (foreignKeyModeChanged) { + // Foreign key constraints can only be changed if there are no transactions + // in progress. To make this clear, we throw an exception if there are + // any acquired connections. + if (!mAcquiredConnections.isEmpty()) { + throw new IllegalStateException("Foreign Key Constraints cannot " + + "be enabled or disabled while there are transactions in " + + "progress. Finish all transactions and release all active " + + "database connections first."); + } + } + if (mConfiguration.openFlags != configuration.openFlags) { // If we are changing open flags and WAL mode at the same time, then // we have no choice but to close the primary connection beforehand diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 049a615f8dc2..7bd0c8d92acc 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -1793,6 +1793,53 @@ public final class SQLiteDatabase extends SQLiteClosable { } /** + * Sets whether foreign key constraints are enabled for the database. + * <p> + * By default, foreign key constraints are not enforced by the database. + * This method allows an application to enable foreign key constraints. + * It must be called each time the database is opened to ensure that foreign + * key constraints are enabled for the session. + * </p><p> + * A good time to call this method is right after calling {@link #openOrCreateDatabase} + * or in the {@link SQLiteOpenHelper#onConfigure} callback. + * </p><p> + * When foreign key constraints are disabled, the database does not check whether + * changes to the database will violate foreign key constraints. Likewise, when + * foreign key constraints are disabled, the database will not execute cascade + * delete or update triggers. As a result, it is possible for the database + * state to become inconsistent. To perform a database integrity check, + * call {@link #isDatabaseIntegrityOk}. + * </p><p> + * This method must not be called while a transaction is in progress. + * </p><p> + * See also <a href="http://sqlite.org/foreignkeys.html">SQLite Foreign Key Constraints</a> + * for more details about foreign key constraint support. + * </p> + * + * @param enable True to enable foreign key constraints, false to disable them. + * + * @throws IllegalStateException if the are transactions is in progress + * when this method is called. + */ + public void setForeignKeyConstraintsEnabled(boolean enable) { + synchronized (mLock) { + throwIfNotOpenLocked(); + + if (mConfigurationLocked.foreignKeyConstraintsEnabled == enable) { + return; + } + + mConfigurationLocked.foreignKeyConstraintsEnabled = enable; + try { + mConnectionPoolLocked.reconfigure(mConfigurationLocked); + } catch (RuntimeException ex) { + mConfigurationLocked.foreignKeyConstraintsEnabled = !enable; + throw ex; + } + } + } + + /** * This method enables parallel execution of queries from multiple threads on the * same database. It does this by opening multiple connections to the database * and using a different database connection for each query. The database diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index 123c2c6c2bcd..549ab902fdd1 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -77,6 +77,13 @@ public final class SQLiteDatabaseConfiguration { public Locale locale; /** + * True if foreign key constraints are enabled. + * + * Default is false. + */ + public boolean foreignKeyConstraintsEnabled; + + /** * The custom functions to register. */ public final ArrayList<SQLiteCustomFunction> customFunctions = @@ -136,6 +143,7 @@ public final class SQLiteDatabaseConfiguration { openFlags = other.openFlags; maxSqlCacheSize = other.maxSqlCacheSize; locale = other.locale; + foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled; customFunctions.clear(); customFunctions.addAll(other.customFunctions); } diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index fe37b8f4a89d..431eca2f98e4 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -237,6 +237,8 @@ public abstract class SQLiteOpenHelper { } } + onConfigure(db); + final int version = db.getVersion(); if (version != mNewVersion) { if (db.isReadOnly()) { @@ -261,6 +263,7 @@ public abstract class SQLiteOpenHelper { db.endTransaction(); } } + onOpen(db); if (db.isReadOnly()) { @@ -290,6 +293,25 @@ public abstract class SQLiteOpenHelper { } /** + * Called when the database connection is being configured, to enable features + * such as write-ahead logging or foreign key support. + * <p> + * This method is called before {@link #onCreate}, {@link #onUpgrade}, + * {@link #onDowngrade}, or {@link #onOpen} are called. It should not modify + * the database except to configure the database connection as required. + * </p><p> + * This method should only call methods that configure the parameters of the + * database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging} + * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, + * {@link SQLiteDatabase#setLocale}, {@link SQLiteDatabase#setMaximumSize}, + * or executing PRAGMA statements. + * </p> + * + * @param db The database. + */ + public void onConfigure(SQLiteDatabase db) {} + + /** * Called when the database is created for the first time. This is where the * creation of tables and the initial population of the tables should happen. * @@ -302,11 +324,16 @@ public abstract class SQLiteOpenHelper { * should use this method to drop tables, add tables, or do anything else it * needs to upgrade to the new schema version. * - * <p>The SQLite ALTER TABLE documentation can be found + * <p> + * The SQLite ALTER TABLE documentation can be found * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns * you can use ALTER TABLE to rename the old table, then create the new table and then * populate the new table with the contents of the old table. + * </p><p> + * This method executes within a transaction. If an exception is thrown, all changes + * will automatically be rolled back. + * </p> * * @param db The database. * @param oldVersion The old database version. @@ -316,11 +343,16 @@ public abstract class SQLiteOpenHelper { /** * Called when the database needs to be downgraded. This is strictly similar to - * onUpgrade() method, but is called whenever current version is newer than requested one. + * {@link #onUpgrade} method, but is called whenever current version is newer than requested one. * However, this method is not abstract, so it is not mandatory for a customer to * implement it. If not overridden, default implementation will reject downgrade and * throws SQLiteException * + * <p> + * This method executes within a transaction. If an exception is thrown, all changes + * will automatically be rolled back. + * </p> + * * @param db The database. * @param oldVersion The old database version. * @param newVersion The new database version. @@ -334,6 +366,12 @@ public abstract class SQLiteOpenHelper { * Called when the database has been opened. The implementation * should check {@link SQLiteDatabase#isReadOnly} before updating the * database. + * <p> + * This method is called after the database connection has been configured + * and after the database schema has been created, upgraded or downgraded as necessary. + * If the database connection must be configured in some way before the schema + * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead. + * </p> * * @param db The database. */ diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index c54d09e168fc..14cd48f118e2 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -89,6 +89,8 @@ interface IWindowManager void prepareAppTransition(int transit, boolean alwaysKeepCurrent); int getPendingAppTransition(); void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim); + void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY, + IRemoteCallback startedCallback); void executeAppTransition(); void setAppStartingWindow(IBinder token, String pkg, int theme, in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index dc8c71b723f0..d92ebcd338b0 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -875,7 +875,7 @@ public abstract class Animation implements Cloneable { * otherwise. * * @param currentTime Where we are in the animation. This is wall clock time. - * @param outTransformation A tranformation object that is provided by the + * @param outTransformation A transformation object that is provided by the * caller and will be filled in by the animation. * @param scale Scaling factor to apply to any inputs to the transform operation, such * pivot points being rotated or scaled around. diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java index cf210c8b614d..e8c1d231dbfb 100644 --- a/core/java/android/view/animation/Transformation.java +++ b/core/java/android/view/animation/Transformation.java @@ -112,6 +112,16 @@ public class Transformation { } /** + * Like {@link #compose(Transformation)} but does this.postConcat(t) of + * the transformation matrix. + * @hide + */ + public void postCompose(Transformation t) { + mAlpha *= t.getAlpha(); + mMatrix.postConcat(t.getMatrix()); + } + + /** * @return The 3x3 Matrix representing the trnasformation to apply to the * coordinates of the object being animated */ diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java index ef7e651f644d..3d46cdd3bebe 100644 --- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java +++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java @@ -123,7 +123,7 @@ public class HeavyWeightSwitcherActivity extends Activity { private OnClickListener mSwitchOldListener = new OnClickListener() { public void onClick(View v) { try { - ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0); + ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0, null); } catch (RemoteException e) { } finish(); diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java index adeeacaf5dc6..a76a628e68a8 100644 --- a/graphics/java/android/renderscript/Allocation.java +++ b/graphics/java/android/renderscript/Allocation.java @@ -188,6 +188,40 @@ public class Allocation extends BaseObj { return getID(); } + + /** + * Get the element of the type of the Allocation. + * + * @hide + * @return Element + * + */ + public Element getElement() { + return mType.getElement(); + } + + /** + * Get the usage flags of the Allocation. + * + * @hide + * @return usage + * + */ + public int getUsage() { + return mUsage; + } + + /** + * Get the size of the Allocation in bytes. + * + * @hide + * @return sizeInBytes + * + */ + public int getSizeBytes() { + return mType.getCount() * mType.getElement().getSizeBytes(); + } + private void updateCacheInfo(Type t) { mCurrentDimX = t.getX(); mCurrentDimY = t.getY(); @@ -294,10 +328,21 @@ public class Allocation extends BaseObj { } } + /** + * Get the type of the Allocation. + * + * @return Type + * + */ public Type getType() { return mType; } + /** + * Propagate changes from one usage of the allocation to the + * remaining usages of the allocation. + * + */ public void syncAll(int srcLocation) { switch (srcLocation) { case USAGE_SCRIPT: @@ -343,6 +388,11 @@ public class Allocation extends BaseObj { mRS.nAllocationIoReceive(getID()); } + /** + * Copy an array of RS objects to the allocation. + * + * @param d Source array. + */ public void copyFrom(BaseObj[] d) { mRS.validate(); validateIsObject(); diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index 66cb32c48f18..564b07bd6c05 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -19,6 +19,7 @@ package com.android.systemui.recent; import android.animation.Animator; import android.animation.LayoutTransition; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -615,10 +616,11 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener if (!mFirstScreenful && tasks.size() == 0) { return; } - mNumItemsWaitingForThumbnailsAndIcons = - mFirstScreenful ? tasks.size() : mRecentTaskDescriptions.size(); + mNumItemsWaitingForThumbnailsAndIcons = mFirstScreenful + ? tasks.size() : mRecentTaskDescriptions == null + ? 0 : mRecentTaskDescriptions.size(); if (mRecentTaskDescriptions == null) { - mRecentTaskDescriptions = new ArrayList(tasks); + mRecentTaskDescriptions = new ArrayList<TaskDescription>(tasks); } else { mRecentTaskDescriptions.addAll(tasks); } @@ -656,22 +658,33 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } public void handleOnClick(View view) { - TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription; + ViewHolder holder = (ViewHolder)view.getTag(); + TaskDescription ad = holder.taskDescription; final Context context = view.getContext(); final ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + holder.thumbnailViewImage.setDrawingCacheEnabled(true); + Bitmap bm = holder.thumbnailViewImage.getDrawingCache(); + ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation( + holder.thumbnailViewImage, bm, 0, 0, + new ActivityOptions.OnAnimationStartedListener() { + @Override public void onAnimationStarted() { + hide(true); + } + }); if (ad.taskId >= 0) { // This is an active task; it should just go to the foreground. - am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME); + am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME, + opts.toBundle()); } else { Intent intent = ad.intent; intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_TASK_ON_HOME | Intent.FLAG_ACTIVITY_NEW_TASK); if (DEBUG) Log.v(TAG, "Starting activity " + intent); - context.startActivity(intent); + context.startActivity(intent, opts.toBundle()); } - hide(true); + holder.thumbnailViewImage.setDrawingCacheEnabled(false); } public void onItemClick(AdapterView<?> parent, View view, int position, long id) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index b422678840af..60749b3e568d 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -34,6 +34,7 @@ import dalvik.system.Zygote; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.ActivityOptions; import android.app.ActivityThread; import android.app.AlertDialog; import android.app.AppGlobals; @@ -2353,10 +2354,12 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { ActivityRecord r = mMainStack.isInStackLocked(callingActivity); if (r == null) { + ActivityOptions.abort(options); return false; } if (r.app == null || r.app.thread == null) { // The caller is not running... d'oh! + ActivityOptions.abort(options); return false; } intent = new Intent(intent); @@ -2393,6 +2396,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (aInfo == null) { // Nobody who is next! + ActivityOptions.abort(options); return false; } @@ -2422,8 +2426,6 @@ public final class ActivityManagerService extends ActivityManagerNative } final long origId = Binder.clearCallingIdentity(); - // XXX we are not dealing with propagating grantedUriPermissions... - // those are not yet exposed to user code, so there is no need. int res = mMainStack.startActivityLocked(r.app.thread, intent, r.resolvedType, aInfo, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1, r.launchedFromUid, 0, @@ -3653,7 +3655,7 @@ public final class ActivityManagerService extends ActivityManagerNative } lastTask = r.task; if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, - null, "force-stop")) { + null, "force-stop", true)) { i--; } } @@ -5686,13 +5688,14 @@ public final class ActivityManagerService extends ActivityManagerNative /** * TODO: Add mController hook */ - public void moveTaskToFront(int task, int flags) { + public void moveTaskToFront(int task, int flags, Bundle options) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()"); synchronized(this) { if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), Binder.getCallingUid(), "Task to front")) { + ActivityOptions.abort(options); return; } final long origId = Binder.clearCallingIdentity(); @@ -5707,7 +5710,7 @@ public final class ActivityManagerService extends ActivityManagerNative // we'll just move the home task to the top first. mMainStack.moveHomeToFrontLocked(); } - mMainStack.moveTaskToFrontLocked(tr, null); + mMainStack.moveTaskToFrontLocked(tr, null, options); return; } for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { @@ -5721,13 +5724,14 @@ public final class ActivityManagerService extends ActivityManagerNative // we'll just move the home task to the top first. mMainStack.moveHomeToFrontLocked(); } - mMainStack.moveTaskToFrontLocked(hr.task, null); + mMainStack.moveTaskToFrontLocked(hr.task, null, options); return; } } } finally { Binder.restoreCallingIdentity(origId); } + ActivityOptions.abort(options); } } @@ -6993,7 +6997,43 @@ public final class ActivityManagerService extends ActivityManagerNative } return killed; } - + + @Override + public boolean killProcessesBelowForeground(String reason) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("killProcessesBelowForeground() only available to system"); + } + + return killProcessesBelowAdj(ProcessList.FOREGROUND_APP_ADJ, reason); + } + + private boolean killProcessesBelowAdj(int belowAdj, String reason) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("killProcessesBelowAdj() only available to system"); + } + + boolean killed = false; + synchronized (mPidsSelfLocked) { + final int size = mPidsSelfLocked.size(); + for (int i = 0; i < size; i++) { + final int pid = mPidsSelfLocked.keyAt(i); + final ProcessRecord proc = mPidsSelfLocked.valueAt(i); + if (proc == null) continue; + + final int adj = proc.setAdj; + if (adj > belowAdj && !proc.killedBackground) { + Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason); + EventLog.writeEvent( + EventLogTags.AM_KILL, proc.pid, proc.processName, adj, reason); + killed = true; + proc.killedBackground = true; + Process.killProcessQuiet(pid); + } + } + } + return killed; + } + public final void startRunning(String pkg, String cls, String action, String data) { synchronized(this) { diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 53cb2b07a337..d60ff2b3b182 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -26,7 +26,6 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; -import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; import android.os.Build; @@ -542,24 +541,38 @@ final class ActivityRecord { void updateOptionsLocked(Bundle options) { if (options != null) { + if (pendingOptions != null) { + pendingOptions.abort(); + } pendingOptions = new ActivityOptions(options); } } void applyOptionsLocked() { if (pendingOptions != null) { - if (pendingOptions.isCustomAnimation()) { - service.mWindowManager.overridePendingAppTransition( - pendingOptions.getPackageName(), - pendingOptions.getCustomEnterResId(), - pendingOptions.getCustomExitResId()); + switch (pendingOptions.getAnimationType()) { + case ActivityOptions.ANIM_CUSTOM: + service.mWindowManager.overridePendingAppTransition( + pendingOptions.getPackageName(), + pendingOptions.getCustomEnterResId(), + pendingOptions.getCustomExitResId()); + break; + case ActivityOptions.ANIM_THUMBNAIL: + service.mWindowManager.overridePendingAppTransitionThumb( + pendingOptions.getThumbnail(), + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getOnAnimationStartListener()); + break; } pendingOptions = null; } } void clearOptionsLocked() { - pendingOptions = null; + if (pendingOptions != null) { + pendingOptions.abort(); + pendingOptions = null; + } } void removeUriPermissionsLocked() { diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 48b4f4ffec6a..a01ed25bb703 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1695,6 +1695,7 @@ final class ActivityStack { if (VALIDATE_TOKENS) { validateAppTokensLocked(); } + ActivityOptions.abort(options); return; } break; @@ -1797,6 +1798,7 @@ final class ActivityStack { // because there is nothing for it to animate on top of. mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen); + ActivityOptions.abort(options); } if (VALIDATE_TOKENS) { validateAppTokensLocked(); @@ -2344,6 +2346,7 @@ final class ActivityStack { // Transfer the result target from the source activity to the new // one being started, including any failures. if (requestCode >= 0) { + ActivityOptions.abort(options); return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT; } resultRecord = sourceRecord.resultTo; @@ -2375,6 +2378,7 @@ final class ActivityStack { Activity.RESULT_CANCELED, null); } mDismissKeyguardOnNextActivity = false; + ActivityOptions.abort(options); return err; } @@ -2425,6 +2429,7 @@ final class ActivityStack { // We pretend to the caller that it was really started, but // they will just get a cancel result. mDismissKeyguardOnNextActivity = false; + ActivityOptions.abort(options); return ActivityManager.START_SUCCESS; } } @@ -2447,6 +2452,7 @@ final class ActivityStack { pal.startFlags = startFlags; mService.mPendingActivityLaunches.add(pal); mDismissKeyguardOnNextActivity = false; + ActivityOptions.abort(options); return ActivityManager.START_SWITCHES_CANCELED; } } @@ -2601,8 +2607,7 @@ final class ActivityStack { // We really do want to push this one into the // user's face, right now. moveHomeToFrontFromLaunchLocked(launchFlags); - r.updateOptionsLocked(options); - moveTaskToFrontLocked(taskTop.task, r); + moveTaskToFrontLocked(taskTop.task, r, options); } } // If the caller has requested that the target task be @@ -2618,6 +2623,7 @@ final class ActivityStack { if (doResume) { resumeTopActivityLocked(null); } + ActivityOptions.abort(options); return ActivityManager.START_RETURN_INTENT_TO_CALLER; } if ((launchFlags & @@ -2705,6 +2711,7 @@ final class ActivityStack { if (doResume) { resumeTopActivityLocked(null); } + ActivityOptions.abort(options); return ActivityManager.START_TASK_TO_FRONT; } } @@ -2734,6 +2741,7 @@ final class ActivityStack { if (doResume) { resumeTopActivityLocked(null); } + ActivityOptions.abort(options); if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) { // We don't need to start a new activity, and // the client said not to do anything if that @@ -2753,6 +2761,7 @@ final class ActivityStack { r.resultTo, r.resultWho, r.requestCode, Activity.RESULT_CANCELED, null); } + ActivityOptions.abort(options); return ActivityManager.START_CLASS_NOT_FOUND; } @@ -2794,6 +2803,7 @@ final class ActivityStack { if (doResume) { resumeTopActivityLocked(null); } + ActivityOptions.abort(options); return ActivityManager.START_DELIVERED_TO_TOP; } } else if (!addingToTask && @@ -2948,6 +2958,7 @@ final class ActivityStack { Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + realCallingPid + ") when starting: " + intent.toString()); + ActivityOptions.abort(options); return ActivityManager.START_PERMISSION_DENIED; } } @@ -3480,6 +3491,15 @@ final class ActivityStack { */ final boolean finishActivityLocked(ActivityRecord r, int index, int resultCode, Intent resultData, String reason) { + return finishActivityLocked(r, index, resultCode, resultData, reason, false); + } + + /** + * @return Returns true if this activity has been removed from the history + * list, or false if it is still in the list and will be removed later. + */ + final boolean finishActivityLocked(ActivityRecord r, int index, + int resultCode, Intent resultData, String reason, boolean immediate) { if (r.finishing) { Slog.w(TAG, "Duplicate finish request for " + r); return false; @@ -3521,7 +3541,10 @@ final class ActivityStack { mService.mCancelledThumbnails.add(r); } - if (mResumedActivity == r) { + if (immediate) { + return finishCurrentActivityLocked(r, index, + FINISH_IMMEDIATELY) == null; + } else if (mResumedActivity == r) { boolean endTask = index <= 0 || (mHistory.get(index-1)).task != r.task; if (DEBUG_TRANSITION) Slog.v(TAG, @@ -3887,12 +3910,12 @@ final class ActivityStack { } } if (homeTask != null) { - moveTaskToFrontLocked(homeTask, null); + moveTaskToFrontLocked(homeTask, null, null); } } - final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) { + final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) { if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr); final int task = tr.taskId; @@ -3900,6 +3923,7 @@ final class ActivityStack { if (top < 0 || (mHistory.get(top)).task.taskId == task) { // nothing to do! + ActivityOptions.abort(options); return; } @@ -3941,7 +3965,16 @@ final class ActivityStack { if (r != null) { mNoAnimActivities.add(r); } + ActivityOptions.abort(options); } else { + if (options != null) { + ActivityRecord r = topRunningActivityLocked(null); + if (r != null && r.state != ActivityState.RESUMED) { + r.updateOptionsLocked(options); + } else { + ActivityOptions.abort(options); + } + } mService.mWindowManager.prepareAppTransition( WindowManagerPolicy.TRANSIT_TASK_TO_FRONT, false); } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 95666c0abd13..067bf285e4dd 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -1587,7 +1587,16 @@ public class PackageManagerService extends IPackageManager.Stub { if (p != null) { final PackageSetting ps = (PackageSetting)p.mExtras; final SharedUserSetting suid = ps.sharedUser; - return suid != null ? suid.gids : ps.gids; + int[] gids = suid != null ? suid.gids : ps.gids; + + // include GIDs for any unenforced permissions + if (!isPermissionEnforcedLocked(READ_EXTERNAL_STORAGE)) { + final BasePermission basePerm = mSettings.mPermissions.get( + READ_EXTERNAL_STORAGE); + gids = appendInts(gids, basePerm.gids); + } + + return gids; } } // stupid thing to indicate an error. @@ -8890,6 +8899,19 @@ public class PackageManagerService extends IPackageManager.Stub { if (mSettings.mReadExternalStorageEnforcement != enforcement) { mSettings.mReadExternalStorageEnforcement = enforcement; mSettings.writeLPr(); + + // kill any non-foreground processes so we restart them and + // grant/revoke the GID. + final IActivityManager am = ActivityManagerNative.getDefault(); + if (am != null) { + final long token = Binder.clearCallingIdentity(); + try { + am.killProcessesBelowForeground("setPermissionEnforcement"); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(token); + } + } } } } else { diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java index 0e110be18272..c29ef3fce4fc 100644 --- a/services/java/com/android/server/wm/AppWindowToken.java +++ b/services/java/com/android/server/wm/AppWindowToken.java @@ -21,10 +21,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import com.android.server.wm.WindowManagerService.H; import android.content.pm.ActivityInfo; +import android.graphics.Matrix; import android.os.Message; import android.os.RemoteException; import android.util.Slog; import android.view.IApplicationToken; +import android.view.Surface; import android.view.View; import android.view.WindowManager; import android.view.WindowManagerPolicy; @@ -90,6 +92,7 @@ class AppWindowToken extends WindowToken { boolean animating; Animation animation; + boolean animInitialized; boolean hasTransformation; final Transformation transformation = new Transformation(); @@ -105,6 +108,15 @@ class AppWindowToken extends WindowToken { boolean startingMoved; boolean firstWindowDrawn; + // Special surface for thumbnail animation. + Surface thumbnail; + int thumbnailTransactionSeq; + int thumbnailX; + int thumbnailY; + int thumbnailLayer; + Animation thumbnailAnimation; + final Transformation thumbnailTransformation = new Transformation(); + // Input application handle used by the input dispatcher. final InputApplicationHandle mInputApplicationHandle; @@ -116,11 +128,12 @@ class AppWindowToken extends WindowToken { mInputApplicationHandle = new InputApplicationHandle(this); } - public void setAnimation(Animation anim) { + public void setAnimation(Animation anim, boolean initialized) { if (WindowManagerService.localLOGV) Slog.v( WindowManagerService.TAG, "Setting animation in " + this + ": " + anim); animation = anim; animating = false; + animInitialized = initialized; anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION); anim.scaleCurrentDuration(service.mTransitionAnimationScale); int zorder = anim.getZAdjustment(); @@ -146,6 +159,7 @@ class AppWindowToken extends WindowToken { if (WindowManagerService.localLOGV) Slog.v( WindowManagerService.TAG, "Setting dummy animation in " + this); animation = WindowManagerService.sDummyAnimation; + animInitialized = false; } } @@ -153,15 +167,28 @@ class AppWindowToken extends WindowToken { if (animation != null) { animation = null; animating = true; + animInitialized = false; + } + clearThumbnail(); + } + + public void clearThumbnail() { + if (thumbnail != null) { + thumbnail.destroy(); + thumbnail = null; } } void updateLayers() { final int N = allAppWindows.size(); final int adj = animLayerAdjustment; + thumbnailLayer = -1; for (int i=0; i<N; i++) { WindowState w = allAppWindows.get(i); w.mAnimLayer = w.mLayer + adj; + if (w.mAnimLayer > thumbnailLayer) { + thumbnailLayer = w.mAnimLayer; + } if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": " + w.mAnimLayer); if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) { @@ -203,6 +230,38 @@ class AppWindowToken extends WindowToken { return isAnimating; } + private void stepThumbnailAnimation(long currentTime) { + thumbnailTransformation.clear(); + thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation); + thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY); + final boolean screenAnimation = service.mAnimator.mScreenRotationAnimation != null + && service.mAnimator.mScreenRotationAnimation.isAnimating(); + if (screenAnimation) { + thumbnailTransformation.postCompose( + service.mAnimator.mScreenRotationAnimation.getEnterTransformation()); + } + // cache often used attributes locally + final float tmpFloats[] = service.mTmpFloats; + thumbnailTransformation.getMatrix().getValues(tmpFloats); + if (WindowManagerService.SHOW_TRANSACTIONS) service.logSurface(thumbnail, + "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X] + + ", " + tmpFloats[Matrix.MTRANS_Y], null); + thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]); + if (WindowManagerService.SHOW_TRANSACTIONS) service.logSurface(thumbnail, + "thumbnail", "alpha=" + thumbnailTransformation.getAlpha() + + " layer=" + thumbnailLayer + + " matrix=[" + tmpFloats[Matrix.MSCALE_X] + + "," + tmpFloats[Matrix.MSKEW_Y] + + "][" + tmpFloats[Matrix.MSKEW_X] + + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null); + thumbnail.setAlpha(thumbnailTransformation.getAlpha()); + // The thumbnail is layered below the window immediately above this + // token's anim layer. + thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER + - WindowManagerService.LAYER_OFFSET_THUMBNAIL); + thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y], + tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]); + } private boolean stepAnimation(long currentTime) { if (animation == null) { @@ -215,6 +274,7 @@ class AppWindowToken extends WindowToken { ": more=" + more + ", xform=" + transformation); if (!more) { animation = null; + clearThumbnail(); if (WindowManagerService.DEBUG_ANIM) Slog.v( WindowManagerService.TAG, "Finished animation in " + this + " @ " + currentTime); @@ -243,12 +303,22 @@ class AppWindowToken extends WindowToken { " @ " + currentTime + ": dw=" + dw + " dh=" + dh + " scale=" + service.mTransitionAnimationScale + " allDrawn=" + allDrawn + " animating=" + animating); - animation.initialize(dw, dh, dw, dh); + if (!animInitialized) { + animation.initialize(dw, dh, dw, dh); + } animation.setStartTime(currentTime); animating = true; + if (thumbnail != null) { + thumbnail.show(); + thumbnailAnimation.setStartTime(currentTime); + } } if (stepAnimation(currentTime)) { - // we're done! + // animation isn't over, step any thumbnail and that's + // it for now. + if (thumbnail != null) { + stepThumbnailAnimation(currentTime); + } return true; } } @@ -440,6 +510,15 @@ class AppWindowToken extends WindowToken { pw.print(" startingDisplayed="); pw.print(startingDisplayed); pw.print(" startingMoved"); pw.println(startingMoved); } + if (thumbnail != null) { + pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail); + pw.print(" x="); pw.print(thumbnailX); + pw.print(" y="); pw.print(thumbnailY); + pw.print(" layer="); pw.println(thumbnailLayer); + pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation); + pw.print(prefix); pw.print("thumbnailTransformation="); + pw.println(thumbnailTransformation.toShortString()); + } } @Override diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java index b3dbee192cfd..7aa671647ece 100644 --- a/services/java/com/android/server/wm/WindowAnimator.java +++ b/services/java/com/android/server/wm/WindowAnimator.java @@ -23,7 +23,7 @@ import com.android.internal.policy.impl.PhoneWindowManager; * on behalf of WindowManagerService. */ public class WindowAnimator { - private static final String TAG = "WindowAnimations"; + private static final String TAG = "WindowAnimator"; final WindowManagerService mService; final Context mContext; @@ -67,8 +67,24 @@ public class WindowAnimator { final int NAT = mService.mAppTokens.size(); for (i=0; i<NAT; i++) { final AppWindowToken appToken = mService.mAppTokens.get(i); + final boolean wasAnimating = appToken.animation != null; if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) { mAnimating = true; + } else if (wasAnimating) { + // stopped animating, do one more pass through the layout + mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; + } + } + + final int NEAT = mService.mExitingAppTokens.size(); + for (i=0; i<NEAT; i++) { + final AppWindowToken appToken = mService.mExitingAppTokens.get(i); + final boolean wasAnimating = appToken.animation != null; + if (appToken.stepAnimationLocked(mCurrentTime, mInnerDw, mInnerDh)) { + mAnimating = true; + } else if (wasAnimating) { + // stopped animating, do one more pass through the layout + mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } } @@ -273,6 +289,15 @@ public class WindowAnimator { w.performShowLocked(); mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; } + if (atoken != null && atoken.thumbnail != null) { + if (atoken.thumbnailTransactionSeq != mTransactionSequence) { + atoken.thumbnailTransactionSeq = mTransactionSequence; + atoken.thumbnailLayer = 0; + } + if (atoken.thumbnailLayer < w.mAnimLayer) { + atoken.thumbnailLayer = w.mAnimLayer; + } + } } // end forall windows } @@ -517,6 +542,7 @@ public class WindowAnimator { } void animate() { + mPendingLayoutChanges = 0; mCurrentTime = SystemClock.uptimeMillis(); // Update animations of all applications, including those diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 26367d29e30b..a978b353ff72 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -117,8 +117,12 @@ import android.view.WindowManagerImpl; import android.view.WindowManagerPolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManagerPolicy.FakeWindow; +import android.view.animation.AlphaAnimation; import android.view.animation.Animation; +import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.view.animation.ScaleAnimation; import android.view.animation.Transformation; import java.io.BufferedWriter; @@ -194,6 +198,18 @@ public class WindowManagerService extends IWindowManager.Stub static final int LAYER_OFFSET_DIM = 1; /** + * Blur surface layer is immediately below dim layer. + */ + static final int LAYER_OFFSET_BLUR = 2; + + /** + * Animation thumbnail is as far as possible below the window above + * the thumbnail (or in other words as far as possible above the window + * below it). + */ + static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER-1; + + /** * Layer at which to put the rotation freeze snapshot. */ static final int FREEZE_LAYER = (TYPE_LAYER_MULTIPLIER * 200) + 1; @@ -479,8 +495,12 @@ public class WindowManagerService extends IWindowManager.Stub // made visible or hidden at the next transition. int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; String mNextAppTransitionPackage; + Bitmap mNextAppTransitionThumbnail; + IRemoteCallback mNextAppTransitionCallback; int mNextAppTransitionEnter; int mNextAppTransitionExit; + int mNextAppTransitionStartX; + int mNextAppTransitionStartY; boolean mAppTransitionReady = false; boolean mAppTransitionRunning = false; boolean mAppTransitionTimeout = false; @@ -588,6 +608,9 @@ public class WindowManagerService extends IWindowManager.Stub } LayoutAndSurfaceFields mInnerFields = new LayoutAndSurfaceFields(); + /** Only do a maximum of 6 repeated layouts. After that quit */ + private int mLayoutRepeatCount; + private final class AnimationRunnable implements Runnable { @Override public void run() { @@ -1897,7 +1920,7 @@ public class WindowManagerService extends IWindowManager.Stub rawChanged = true; } - if (rawChanged && (wallpaperWin.getAttrs().privateFlags & + if (rawChanged && (wallpaperWin.mAttrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) != 0) { try { if (DEBUG_WALLPAPER) Slog.v(TAG, "Report new wp offset " @@ -2296,7 +2319,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wasVisible) { int transit = WindowManagerPolicy.TRANSIT_EXIT; - if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { + if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } // Try starting an animation. @@ -2441,6 +2464,15 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG, str); } } + + static void logSurface(Surface s, String title, String msg, RuntimeException where) { + String str = " SURFACE " + s + ": " + msg + " / " + title; + if (where != null) { + Slog.i(TAG, str, where); + } else { + Slog.i(TAG, str); + } + } void setTransparentRegionWindow(Session session, IWindow client, Region region) { long origId = Binder.clearCallingIdentity(); @@ -2761,7 +2793,7 @@ public class WindowManagerService extends IWindowManager.Stub // Try starting an animation; if there isn't one, we // can destroy the surface right away. int transit = WindowManagerPolicy.TRANSIT_EXIT; - if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { + if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() && @@ -3081,6 +3113,63 @@ public class WindowManagerService extends IWindowManager.Stub return null; } + private Animation createThumbnailAnimationLocked(int transit, + boolean enter, boolean thumb) { + Animation a; + final float thumbWidth = mNextAppTransitionThumbnail.getWidth(); + final float thumbHeight = mNextAppTransitionThumbnail.getHeight(); + // Pick the desired duration. If this is an inter-activity transition, + // it is the standard duration for that. Otherwise we use the longer + // task transition duration. + int duration; + switch (transit) { + case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN: + case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE: + duration = mContext.getResources().getInteger( + com.android.internal.R.integer.config_shortAnimTime); + break; + default: + duration = 500; + break; + + } + if (thumb) { + // Animation for zooming thumbnail from its initial size to + // filling the screen. + Animation scale = new ScaleAnimation( + 1, mAppDisplayWidth/thumbWidth, + 1, mAppDisplayHeight/thumbHeight, + mNextAppTransitionStartX + thumbWidth/2, + mNextAppTransitionStartY + thumbHeight/2); + AnimationSet set = new AnimationSet(true); + Animation alpha = new AlphaAnimation(1, 0); + scale.setDuration(duration); + set.addAnimation(scale); + alpha.setDuration(duration); + set.addAnimation(alpha); + a = set; + } else if (enter) { + // Entering app zooms out from the center of the thumbnail. + a = new ScaleAnimation( + thumbWidth/mAppDisplayWidth, 1, + thumbHeight/mAppDisplayHeight, 1, + mNextAppTransitionStartX + thumbWidth/2, + mNextAppTransitionStartY + thumbHeight/2); + a.setDuration(duration); + } else { + // Exiting app just holds in place. + a = new AlphaAnimation(1, 1); + a.setDuration(duration); + } + a.setFillAfter(true); + final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext, + com.android.internal.R.interpolator.decelerate_quint); + a.setInterpolator(interpolator); + a.initialize(mAppDisplayWidth, mAppDisplayHeight, + mAppDisplayWidth, mAppDisplayHeight); + return a; + } + private boolean applyAnimationLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, int transit, boolean enter) { // Only apply an animation if the display isn't frozen. If it is @@ -3089,7 +3178,11 @@ public class WindowManagerService extends IWindowManager.Stub // is running. if (okToDisplay()) { Animation a; - if (mNextAppTransitionPackage != null) { + boolean initialized = false; + if (mNextAppTransitionThumbnail != null) { + a = createThumbnailAnimationLocked(transit, enter, false); + initialized = true; + } else if (mNextAppTransitionPackage != null) { a = loadAnimation(mNextAppTransitionPackage, enter ? mNextAppTransitionEnter : mNextAppTransitionExit); } else { @@ -3161,7 +3254,7 @@ public class WindowManagerService extends IWindowManager.Stub } Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e); } - wtoken.setAnimation(a); + wtoken.setAnimation(a, initialized); } } else { wtoken.clearAnimation(); @@ -3689,11 +3782,23 @@ public class WindowManagerService extends IWindowManager.Stub int enterAnim, int exitAnim) { if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mNextAppTransitionPackage = packageName; + mNextAppTransitionThumbnail = null; mNextAppTransitionEnter = enterAnim; mNextAppTransitionExit = exitAnim; } } + public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, + int startY, IRemoteCallback startedCallback) { + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { + mNextAppTransitionPackage = null; + mNextAppTransitionThumbnail = srcThumb; + mNextAppTransitionStartX = startX; + mNextAppTransitionStartY = startY; + mNextAppTransitionCallback = startedCallback; + } + } + public void executeAppTransition() { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "executeAppTransition()")) { @@ -3837,6 +3942,19 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendMessageAtFrontOfQueue(m); return; } + if (ttoken.thumbnail != null) { + // The old token is animating with a thumbnail, transfer + // that to the new token. + if (wtoken.thumbnail != null) { + wtoken.thumbnail.destroy(); + } + wtoken.thumbnail = ttoken.thumbnail; + wtoken.thumbnailX = ttoken.thumbnailX; + wtoken.thumbnailY = ttoken.thumbnailY; + wtoken.thumbnailLayer = ttoken.thumbnailLayer; + wtoken.thumbnailAnimation = ttoken.thumbnailAnimation; + ttoken.thumbnail = null; + } } } @@ -4232,6 +4350,7 @@ public class WindowManagerService extends IWindowManager.Stub // Make sure there is no animation running on this token, // so any windows associated with it will be removed as // soon as their animations are complete + wtoken.clearAnimation(); wtoken.animation = null; wtoken.animating = false; } @@ -7459,10 +7578,25 @@ public class WindowManagerService extends IWindowManager.Stub } else { mInLayout = false; - if (mLayoutNeeded) { + } + + if (mLayoutNeeded) { + if (++mLayoutRepeatCount < 6) { requestTraversalLocked(); + } else { + Slog.e(TAG, "Performed 6 layouts in a row. Skipping"); + mLayoutRepeatCount = 0; } + } else { + mLayoutRepeatCount = 0; + } + + if (mAnimator.mAnimating) { + // Do this even if requestTraversalLocked was called above so we get a frame drawn + // at the proper time as well as the one drawn early. + scheduleAnimationLocked(); } + if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) { mH.removeMessages(H.REPORT_WINDOWS_CHANGE); mH.sendMessage(mH.obtainMessage(H.REPORT_WINDOWS_CHANGE)); @@ -7784,11 +7918,15 @@ public class WindowManagerService extends IWindowManager.Stub animLp = null; } + AppWindowToken topOpeningApp = null; + int topOpeningLayer = 0; + NN = mOpeningApps.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mOpeningApps.get(i); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); + wtoken.clearThumbnail(); wtoken.reportedVisible = false; wtoken.inPendingTransaction = false; wtoken.animation = null; @@ -7797,12 +7935,26 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.updateReportedVisibilityLocked(); wtoken.waitingToShow = false; mAnimator.mAnimating |= wtoken.showAllWindowsLocked(); + if (animLp != null) { + int layer = -1; + for (int j=0; j<wtoken.windows.size(); j++) { + WindowState win = wtoken.windows.get(j); + if (win.mAnimLayer > layer) { + layer = win.mAnimLayer; + } + } + if (topOpeningApp == null || layer > topOpeningLayer) { + topOpeningApp = wtoken; + topOpeningLayer = layer; + } + } } NN = mClosingApps.size(); for (i=0; i<NN; i++) { AppWindowToken wtoken = mClosingApps.get(i); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app" + wtoken); + wtoken.clearThumbnail(); wtoken.inPendingTransaction = false; wtoken.animation = null; setTokenVisibilityLocked(wtoken, animLp, false, @@ -7815,7 +7967,47 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.allDrawn = true; } + if (mNextAppTransitionThumbnail != null && topOpeningApp != null + && topOpeningApp.animation != null) { + // This thumbnail animation is very special, we need to have + // an extra surface with the thumbnail included with the animation. + Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(), + mNextAppTransitionThumbnail.getHeight()); + try { + Surface surface = new Surface(mFxSession, Process.myPid(), + "thumbnail anim", 0, dirty.width(), dirty.height(), + PixelFormat.TRANSLUCENT, Surface.HIDDEN); + topOpeningApp.thumbnail = surface; + if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL " + + surface + ": CREATE"); + Surface drawSurface = new Surface(); + drawSurface.copyFrom(surface); + Canvas c = drawSurface.lockCanvas(dirty); + c.drawBitmap(mNextAppTransitionThumbnail, 0, 0, null); + drawSurface.unlockCanvasAndPost(c); + drawSurface.release(); + topOpeningApp.thumbnailLayer = topOpeningLayer; + Animation anim = createThumbnailAnimationLocked(transit, true, true); + topOpeningApp.thumbnailAnimation = anim; + anim.restrictDuration(MAX_ANIMATION_DURATION); + anim.scaleCurrentDuration(mTransitionAnimationScale); + topOpeningApp.thumbnailX = mNextAppTransitionStartX; + topOpeningApp.thumbnailY = mNextAppTransitionStartY; + } catch (Surface.OutOfResourcesException e) { + Slog.e(TAG, "Can't allocate thumbnail surface w=" + dirty.width() + + " h=" + dirty.height(), e); + topOpeningApp.clearThumbnail(); + } + } + mNextAppTransitionPackage = null; + mNextAppTransitionThumbnail = null; + if (mNextAppTransitionCallback != null) { + try { + mNextAppTransitionCallback.sendResult(null); + } catch (RemoteException e) { + } + } mOpeningApps.clear(); mClosingApps.clear(); @@ -8192,6 +8384,7 @@ public class WindowManagerService extends IWindowManager.Stub mLayoutNeeded = true; } } + if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout"); if (updateOrientationFromAppTokensLocked(true)) { @@ -8199,6 +8392,7 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } } + if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) { mLayoutNeeded = true; } @@ -8400,6 +8594,7 @@ public class WindowManagerService extends IWindowManager.Stub // Make sure there is no animation running on this token, // so any windows associated with it will be removed as // soon as their animations are complete + token.clearAnimation(); token.animation = null; token.animating = false; if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, @@ -8409,8 +8604,6 @@ public class WindowManagerService extends IWindowManager.Stub } } - boolean needRelayout = false; - if (!mAnimator.mAnimating && mAppTransitionRunning) { // We have finished the animation of an app transition. To do // this, we have delayed a lot of operations like showing and @@ -8419,7 +8612,7 @@ public class WindowManagerService extends IWindowManager.Stub // be out of sync with it. So here we will just rebuild the // entire app window list. Fun! mAppTransitionRunning = false; - needRelayout = true; + mLayoutNeeded = true; rebuildAppWindowListLocked(); assignLayersLocked(); // Clear information about apps that were moving. @@ -8430,19 +8623,10 @@ public class WindowManagerService extends IWindowManager.Stub mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS); } if (wallpaperDestroyed) { - needRelayout = adjustWallpaperWindowsLocked() != 0; + mLayoutNeeded |= adjustWallpaperWindowsLocked() != 0; } - if ((mPendingLayoutChanges & ( - WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER | - ADJUST_WALLPAPER_LAYERS_CHANGED | - WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG | - WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT)) != 0) { - needRelayout = true; - } - if (needRelayout) { - requestTraversalLocked(); - } else if (mAnimator.mAnimating) { - scheduleAnimationLocked(); + if (mPendingLayoutChanges != 0) { + mLayoutNeeded = true; } // Finally update all input windows now that the window changes have stabilized. @@ -8485,7 +8669,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mInnerFields.mOrientationChangeComplete && !needRelayout && + if (mInnerFields.mOrientationChangeComplete && !mLayoutNeeded && !mInnerFields.mUpdateRotation) { checkDrawnWindowsLocked(); } @@ -8839,6 +9023,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET; mNextAppTransitionPackage = null; + mNextAppTransitionThumbnail = null; mAppTransitionReady = true; } @@ -9399,6 +9584,12 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mNextAppTransitionExit=0x"); pw.print(Integer.toHexString(mNextAppTransitionExit)); } + if (mNextAppTransitionThumbnail != null) { + pw.print(" mNextAppTransitionThumbnail="); + pw.print(mNextAppTransitionThumbnail); + pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX); + pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY); + } pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition); pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation); } diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java index 4cacbc49c60d..cceed16ff2d5 100644 --- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java @@ -39,7 +39,7 @@ public class ActivityManagerPermissionTests extends TestCase { @SmallTest public void testREORDER_TASKS() { try { - mAm.moveTaskToFront(0, 0); + mAm.moveTaskToFront(0, 0, null); fail("IActivityManager.moveTaskToFront did not throw SecurityException as" + " expected"); } catch (SecurityException e) { diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java index 945b3cd7ca22..5256b583f8d3 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java @@ -85,15 +85,18 @@ import java.io.InputStream; // ------ Native Delegates ------ @LayoutlibDelegate - /*package*/ static void nativeSetDefaultConfig(int nativeConfig) { - // pass + /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage, + Rect padding, Options opts) { + return nativeDecodeStream(is, storage, padding, opts, false, 1.f); } @LayoutlibDelegate - /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage, - Rect padding, Options opts) { + /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage, + Rect padding, Options opts, boolean applyScale, float scale) { Bitmap bm = null; + //TODO support rescaling + Density density = Density.MEDIUM; if (opts != null) { density = Density.getEnum(opts.inDensity); @@ -147,6 +150,13 @@ import java.io.InputStream; } @LayoutlibDelegate + /*package*/ static Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts, + boolean applyScale, float scale) { + opts.inBitmap = null; + return null; + } + + @LayoutlibDelegate /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset, int length, Options opts) { opts.inBitmap = null; diff --git a/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Delegate.java new file mode 100644 index 000000000000..14b84ef5b20c --- /dev/null +++ b/tools/layoutlib/bridge/src/android/view/ViewRootImpl_Delegate.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 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.view; + +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +/** + * Delegate used to provide new implementation of a select few methods of {@link ViewRootImpl} + * + * Through the layoutlib_create tool, the original methods of ViewRootImpl have been replaced + * by calls to methods of the same name in this delegate class. + * + */ +public class ViewRootImpl_Delegate { + + @LayoutlibDelegate + /*package*/ static boolean isInTouchMode() { + return false; // this allows displaying selection. + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java index bef2c95b4d6a..8b1d41ad49e0 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java @@ -340,6 +340,12 @@ public class BridgeWindowManager implements IWindowManager { } @Override + public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, + IRemoteCallback startedCallback) throws RemoteException { + // TODO Auto-generated method stub + } + + @Override public void pauseKeyDispatching(IBinder arg0) throws RemoteException { // TODO Auto-generated method stub diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 170cd6a8b5cb..79e02c8bddd1 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -114,6 +114,7 @@ public final class CreateInfo implements ICreateInfo { "android.view.LayoutInflater#rInflate", "android.view.LayoutInflater#parseInclude", "android.view.View#isInEditMode", + "android.view.ViewRootImpl#isInTouchMode", "android.view.inputmethod.InputMethodManager#getInstance", "android.util.Log#println_native", "com.android.internal.util.XmlUtils#convertValueToInt", |