Merge "More file-based encryption work."
diff --git a/api/current.txt b/api/current.txt
index 1a7139c..e17bf28 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2732,6 +2732,7 @@
method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
method public final android.os.IBinder getIBinder();
method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
}
@@ -2796,6 +2797,7 @@
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
method public void setPassword(android.accounts.Account, java.lang.String);
method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
+ method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
@@ -2812,6 +2814,8 @@
field public static final java.lang.String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
field public static final java.lang.String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
field public static final java.lang.String KEY_ACCOUNT_NAME = "authAccount";
+ field public static final java.lang.String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
+ field public static final java.lang.String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
field public static final java.lang.String KEY_ACCOUNT_TYPE = "accountType";
field public static final java.lang.String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
field public static final java.lang.String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -3773,6 +3777,8 @@
}
public class ActivityOptions {
+ method public android.graphics.Rect getLaunchBounds();
+ method public boolean hasLaunchBounds();
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -3782,6 +3788,7 @@
method public static android.app.ActivityOptions makeTaskLaunchBehind();
method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
method public void requestUsageTimeReport(android.app.PendingIntent);
+ method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
diff --git a/api/system-current.txt b/api/system-current.txt
index f2784410..d8b9b6f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2831,6 +2831,7 @@
method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
method public final android.os.IBinder getIBinder();
method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
+ method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
}
@@ -2895,6 +2896,7 @@
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
method public void setPassword(android.accounts.Account, java.lang.String);
method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
+ method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
@@ -2911,6 +2913,8 @@
field public static final java.lang.String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
field public static final java.lang.String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
field public static final java.lang.String KEY_ACCOUNT_NAME = "authAccount";
+ field public static final java.lang.String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
+ field public static final java.lang.String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
field public static final java.lang.String KEY_ACCOUNT_TYPE = "accountType";
field public static final java.lang.String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
field public static final java.lang.String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -3882,6 +3886,8 @@
}
public class ActivityOptions {
+ method public android.graphics.Rect getLaunchBounds();
+ method public boolean hasLaunchBounds();
method public static android.app.ActivityOptions makeBasic();
method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -3891,6 +3897,7 @@
method public static android.app.ActivityOptions makeTaskLaunchBehind();
method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
method public void requestUsageTimeReport(android.app.PendingIntent);
+ method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
method public android.os.Bundle toBundle();
method public void update(android.app.ActivityOptions);
field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 9c401c7f..185ceb4 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -116,6 +116,25 @@
*/
public static final String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
+ /**
+ * Bundle key used for the {@link String} account type in session bundle.
+ * This is used in the default implementation of
+ * {@link #startAddAccountSession}. TODO: and startUpdateCredentialsSession.
+ */
+ private static final String KEY_AUTH_TOKEN_TYPE = "android.accounts.KEY_AUTH_TOKEN_TYPE";
+ /**
+ * Bundle key used for the {@link String} array of required features in
+ * session bundle. This is used in the default implementation of
+ * {@link #startAddAccountSession}. TODO: and startUpdateCredentialsSession.
+ */
+ private static final String KEY_REQUIRED_FEATURES = "android.accounts.AbstractAccountAuthenticator.KEY_REQUIRED_FEATURES";
+ /**
+ * Bundle key used for the {@link Bundle} options in session bundle. This is
+ * used in default implementation of {@link #startAddAccountSession}. TODO:
+ * and startUpdateCredentialsSession.
+ */
+ private static final String KEY_OPTIONS = "android.accounts.AbstractAccountAuthenticator.KEY_OPTIONS";
+
private final Context mContext;
public AbstractAccountAuthenticator(Context context) {
@@ -336,6 +355,36 @@
handleException(response, "addAccountFromCredentials", account.toString(), e);
}
}
+
+ @Override
+ public void startAddAccountSession(IAccountAuthenticatorResponse response,
+ String accountType, String authTokenType, String[] features, Bundle options)
+ throws RemoteException {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "startAddAccountSession: accountType " + accountType
+ + ", authTokenType " + authTokenType
+ + ", features " + (features == null ? "[]" : Arrays.toString(features)));
+ }
+ checkBinderPermission();
+ try {
+ final Bundle result = AbstractAccountAuthenticator.this.startAddAccountSession(
+ new AccountAuthenticatorResponse(response), accountType, authTokenType,
+ features, options);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ if (result != null) {
+ result.keySet(); // force it to be unparcelled
+ }
+ Log.v(TAG, "startAddAccountSession: result "
+ + AccountManager.sanitizeResult(result));
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ } catch (Exception e) {
+ handleException(response, "startAddAccountSession", accountType, e);
+ }
+ }
}
private void handleException(IAccountAuthenticatorResponse response, String method,
@@ -603,4 +652,52 @@
}).start();
return null;
}
+
+ /**
+ * Starts the add account session to authenticate user to an account of the
+ * specified accountType.
+ *
+ * @param response to send the result back to the AccountManager, will never
+ * be null
+ * @param accountType the type of account to authenticate with, will never
+ * be null
+ * @param authTokenType the type of auth token to retrieve after
+ * authenticating with the account, may be null
+ * @param requiredFeatures a String array of authenticator-specific features
+ * that the account authenticated with must support, may be null
+ * @param options a Bundle of authenticator-specific options, may be null
+ * @return a Bundle result or null if the result is to be returned via the
+ * response. The result will contain either:
+ * <ul>
+ * <li>{@link AccountManager#KEY_INTENT}, or
+ * <li>{@link AccountManager#KEY_ACCOUNT_SESSION_BUNDLE} for adding
+ * the account to device later, and if account is authenticated,
+ * optional {@link AccountManager#KEY_PASSWORD} and
+ * {@link AccountManager#KEY_ACCOUNT_STATUS_TOKEN} for checking the
+ * status of the account, or
+ * <li>{@link AccountManager#KEY_ERROR_CODE} and
+ * {@link AccountManager#KEY_ERROR_MESSAGE} to indicate an error
+ * </ul>
+ * @throws NetworkErrorException if the authenticator could not honor the
+ * request due to a network error
+ */
+ public Bundle startAddAccountSession(final AccountAuthenticatorResponse response,
+ final String accountType, final String authTokenType, final String[] requiredFeatures,
+ final Bundle options)
+ throws NetworkErrorException {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Bundle sessionBundle = new Bundle();
+ sessionBundle.putString(KEY_AUTH_TOKEN_TYPE, authTokenType);
+ sessionBundle.putStringArray(KEY_REQUIRED_FEATURES, requiredFeatures);
+ sessionBundle.putBundle(KEY_OPTIONS, options);
+ Bundle result = new Bundle();
+ result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+ response.onResult(result);
+ }
+
+ }).start();
+ return null;
+ }
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 0a7568a..42e5e2a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -240,6 +240,20 @@
*/
public static final String KEY_NOTIFY_ON_FAILURE = "notifyOnAuthFailure";
+ /**
+ * Bundle key used for a {@link Bundle} in result from
+ * {@link #startAddAccountSession} and friends which returns session data
+ * for installing an account later.
+ */
+ public static final String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
+
+ /**
+ * Bundle key used for the {@link String} account status token in result
+ * from {@link #startAddAccountSession} and friends which returns
+ * information about a particular account.
+ */
+ public static final String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
+
public static final String ACTION_AUTHENTICATOR_INTENT =
"android.accounts.AccountAuthenticator";
public static final String AUTHENTICATOR_META_DATA_NAME =
@@ -2590,4 +2604,84 @@
}
}
}
+
+ /**
+ * Asks the user to authenticate with an account of a specified type. The
+ * authenticator for this account type processes this request with the
+ * appropriate user interface. If the user does elect to authenticate with a
+ * new account, a bundle of session data for installing the account later is
+ * returned with optional account password and account status token.
+ * <p>
+ * This method may be called from any thread, but the returned
+ * {@link AccountManagerFuture} must not be used on the main thread.
+ * <p>
+ * <p>
+ * <b>NOTE:</b> The account will not be installed to the device by calling
+ * this api alone.
+ *
+ * @param accountType The type of account to add; must not be null
+ * @param authTokenType The type of auth token (see {@link #getAuthToken})
+ * this account will need to be able to generate, null for none
+ * @param requiredFeatures The features (see {@link #hasFeatures}) this
+ * account must have, null for none
+ * @param options Authenticator-specific options for the request, may be
+ * null or empty
+ * @param activity The {@link Activity} context to use for launching a new
+ * authenticator-defined sub-Activity to prompt the user to
+ * create an account; used only to call startActivity(); if null,
+ * the prompt will not be launched directly, but the necessary
+ * {@link Intent} will be returned to the caller instead
+ * @param callback Callback to invoke when the request completes, null for
+ * no callback
+ * @param handler {@link Handler} identifying the callback thread, null for
+ * the main thread
+ * @return An {@link AccountManagerFuture} which resolves to a Bundle with
+ * these fields if activity was specified and user was authenticated
+ * with an account:
+ * <ul>
+ * <li>{@link #KEY_ACCOUNT_SESSION_BUNDLE} - encrypted Bundle for
+ * adding the the to the device later.
+ * <li>{@link #KEY_PASSWORD} - optional, the password or password
+ * hash of the account.
+ * <li>{@link #KEY_ACCOUNT_STATUS_TOKEN} - optional, token to check
+ * status of the account
+ * </ul>
+ * If no activity was specified, the returned Bundle contains only
+ * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+ * actual account creation process. If authenticator doesn't support
+ * this method, the returned Bundle contains only
+ * {@link #KEY_ACCOUNT_SESSION_BUNDLE} with encrypted
+ * {@code options} needed to add account later. If an error
+ * occurred, {@link AccountManagerFuture#getResult()} throws:
+ * <ul>
+ * <li>{@link AuthenticatorException} if no authenticator was
+ * registered for this account type or the authenticator failed to
+ * respond
+ * <li>{@link OperationCanceledException} if the operation was
+ * canceled for any reason, including the user canceling the
+ * creation process or adding accounts (of this type) has been
+ * disabled by policy
+ * <li>{@link IOException} if the authenticator experienced an I/O
+ * problem creating a new account, usually because of network
+ * trouble
+ * </ul>
+ */
+ public AccountManagerFuture<Bundle> startAddAccountSession(final String accountType,
+ final String authTokenType, final String[] requiredFeatures, final Bundle options,
+ final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
+ final Bundle optionsIn = new Bundle();
+ if (options != null) {
+ optionsIn.putAll(options);
+ }
+ optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
+
+ return new AmsTask(activity, handler, callback) {
+ @Override
+ public void doWork() throws RemoteException {
+ mService.startAddAccountSession(mResponse, accountType, authTokenType,
+ requiredFeatures, activity != null, optionsIn);
+ }
+ }.start();
+ }
}
diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl
index 58612da..b326070 100644
--- a/core/java/android/accounts/IAccountAuthenticator.aidl
+++ b/core/java/android/accounts/IAccountAuthenticator.aidl
@@ -83,4 +83,11 @@
*/
void addAccountFromCredentials(in IAccountAuthenticatorResponse response, in Account account,
in Bundle accountCredentials);
+
+ /**
+ * Starts the add account session by prompting the user for account information
+ * and return a Bundle containing data to finish the session later.
+ */
+ void startAddAccountSession(in IAccountAuthenticatorResponse response, String accountType,
+ String authTokenType, in String[] requiredFeatures, in Bundle options);
}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 0d95db1..5de311e 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -83,4 +83,9 @@
String getPreviousName(in Account account);
boolean renameSharedAccountAsUser(in Account accountToRename, String newName, int userId);
+ /* Add account in two steps. */
+ void startAddAccountSession(in IAccountManagerResponse response, String accountType,
+ String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
+ in Bundle options);
+
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 00bba2d..f7aee75 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -528,6 +528,15 @@
return stackId == FULLSCREEN_WORKSPACE_STACK_ID
|| stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
}
+
+ /**
+ * Returns true if animation specs should be constructed for app transition that moves
+ * the task to the specified stack.
+ */
+ public static boolean useAnimationSpecForAppTransition(int stackId) {
+ return stackId == FREEFORM_WORKSPACE_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID || stackId == DOCKED_STACK_ID;
+ }
}
/**
@@ -884,7 +893,7 @@
if (mIcon != null) {
return mIcon;
}
- return loadTaskDescriptionIcon(mIconFilename);
+ return loadTaskDescriptionIcon(mIconFilename, UserHandle.myUserId());
}
/** @hide */
@@ -898,11 +907,11 @@
}
/** @hide */
- public static Bitmap loadTaskDescriptionIcon(String iconFilename) {
+ public static Bitmap loadTaskDescriptionIcon(String iconFilename, int userId) {
if (iconFilename != null) {
try {
return ActivityManagerNative.getDefault().
- getTaskDescriptionIcon(iconFilename);
+ getTaskDescriptionIcon(iconFilename, userId);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 73b81cd..0b7b6fc 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2500,7 +2500,8 @@
case GET_TASK_DESCRIPTION_ICON_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String filename = data.readString();
- Bitmap icon = getTaskDescriptionIcon(filename);
+ int userId = data.readInt();
+ Bitmap icon = getTaskDescriptionIcon(filename, userId);
reply.writeNoException();
if (icon == null) {
reply.writeInt(0);
@@ -6022,11 +6023,12 @@
}
@Override
- public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException {
+ public Bitmap getTaskDescriptionIcon(String filename, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(filename);
+ data.writeInt(userId);
mRemote.transact(GET_TASK_DESCRIPTION_ICON_TRANSACTION, data, reply, 0);
reply.readException();
final Bitmap icon = reply.readInt() == 0 ? null : Bitmap.CREATOR.createFromParcel(reply);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 57900aa..cee1aa5 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -64,11 +64,13 @@
public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
/**
- * The bounds that the activity should be started in. Set to null explicitly
- * for full screen. If the key is not found, previous bounds will be preserved.
+ * The bounds (window size) that the activity should be launched in. Set to null explicitly for
+ * full screen. If the key is not found, previous bounds will be preserved.
+ * NOTE: This value is ignored on devices that don't have
+ * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} enabled.
* @hide
*/
- public static final String KEY_BOUNDS = "android:activity.bounds";
+ public static final String KEY_LAUNCH_BOUNDS = "android:activity.launchBounds";
/**
* Type of animation that arguments specify.
@@ -193,8 +195,8 @@
public static final int ANIM_CLIP_REVEAL = 11;
private String mPackageName;
- private boolean mHasBounds;
- private Rect mBounds;
+ private boolean mHasLaunchBounds;
+ private Rect mLaunchBounds;
private int mAnimationType = ANIM_NONE;
private int mCustomEnterResId;
private int mCustomExitResId;
@@ -705,9 +707,9 @@
} catch (RuntimeException e) {
Slog.w(TAG, e);
}
- mHasBounds = opts.containsKey(KEY_BOUNDS);
- if (mHasBounds) {
- mBounds = opts.getParcelable(KEY_BOUNDS);
+ mHasLaunchBounds = opts.containsKey(KEY_LAUNCH_BOUNDS);
+ if (mHasLaunchBounds) {
+ mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
}
mAnimationType = opts.getInt(KEY_ANIM_TYPE);
switch (mAnimationType) {
@@ -766,10 +768,15 @@
}
}
- /** @hide */
- public ActivityOptions setBounds(Rect bounds) {
- mHasBounds = true;
- mBounds = bounds;
+ /**
+ * Sets the bounds (window size) that the activity should be launched in. Set to null explicitly
+ * for full screen.
+ * NOTE: This value is ignored on devices that don't have
+ * {@link android.content.pm.PackageManager#FEATURE_FREEFORM_WINDOW_MANAGEMENT} enabled.
+ */
+ public ActivityOptions setLaunchBounds(Rect launchBounds) {
+ mHasLaunchBounds = true;
+ mLaunchBounds = launchBounds;
return this;
}
@@ -778,14 +785,12 @@
return mPackageName;
}
- /** @hide */
- public boolean hasBounds() {
- return mHasBounds;
+ public boolean hasLaunchBounds() {
+ return mHasLaunchBounds;
}
- /** @hide */
- public Rect getBounds(){
- return mBounds;
+ public Rect getLaunchBounds(){
+ return mLaunchBounds;
}
/** @hide */
@@ -997,8 +1002,8 @@
if (mPackageName != null) {
b.putString(KEY_PACKAGE_NAME, mPackageName);
}
- if (mHasBounds) {
- b.putParcelable(KEY_BOUNDS, mBounds);
+ if (mHasLaunchBounds) {
+ b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
}
b.putInt(KEY_ANIM_TYPE, mAnimationType);
if (mUsageTimeReport != null) {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index c0cc566..20f3495 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -575,14 +575,20 @@
setGhostVisibility(View.INVISIBLE);
mHasStopped = true;
mIsCanceled = true;
+ clearState();
+ return super.cancelPendingTransitions();
+ }
+
+ @Override
+ protected void clearState() {
+ mSharedElementsBundle = null;
+ mEnterViewsTransition = null;
mResultReceiver = null;
if (mBackgroundAnimator != null) {
mBackgroundAnimator.cancel();
mBackgroundAnimator = null;
}
- mActivity = null;
- clearState();
- return super.cancelPendingTransitions();
+ super.clearState();
}
private void makeOpaque() {
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 4b670cd..e93b40e 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -470,6 +470,11 @@
mActivity = null;
}
// Clear the state so that we can't hold any references accidentally and leak memory.
+ clearState();
+ }
+
+ @Override
+ protected void clearState() {
mHandler = null;
mSharedElementBundle = null;
if (mBackgroundAnimator != null) {
@@ -477,7 +482,7 @@
mBackgroundAnimator = null;
}
mExitSharedElementBundle = null;
- clearState();
+ super.clearState();
}
@Override
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 3027e9c..db4f5c1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -498,7 +498,7 @@
public void resizeTask(int taskId, Rect bounds, int resizeMode) throws RemoteException;
public Rect getTaskBounds(int taskId) throws RemoteException;
- public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
+ public Bitmap getTaskDescriptionIcon(String filename, int userId) throws RemoteException;
public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts)
throws RemoteException;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 30232da..84ddd9f 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -50,9 +50,6 @@
void setPackagePriority(String pkg, int uid, int priority);
int getPackagePriority(String pkg, int uid);
- void setPackagePeekable(String pkg, int uid, boolean peekable);
- boolean getPackagePeekable(String pkg, int uid);
-
void setPackageVisibilityOverride(String pkg, int uid, int visibility);
int getPackageVisibilityOverride(String pkg, int uid);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 390c280..4e6548b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -838,13 +838,6 @@
public static final String EXTRA_PEOPLE = "android.people";
/**
- * {@link #extras} key: used to provide hints about the appropriateness of
- * displaying this notification as a heads-up notification.
- * @hide
- */
- public static final String EXTRA_AS_HEADS_UP = "headsup";
-
- /**
* Allow certain system-generated notifications to appear before the device is provisioned.
* Only available to notifications coming from the android package.
* @hide
@@ -887,32 +880,6 @@
*/
public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
- /**
- * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
- * displayed in the heads up space.
- *
- * <p>
- * If this notification has a {@link #fullScreenIntent}, then it will always launch the
- * full-screen intent when posted.
- * </p>
- * @hide
- */
- public static final int HEADS_UP_NEVER = 0;
-
- /**
- * Default value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification may be
- * displayed as a heads up.
- * @hide
- */
- public static final int HEADS_UP_ALLOWED = 1;
-
- /**
- * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification is a
- * good candidate for display as a heads up.
- * @hide
- */
- public static final int HEADS_UP_REQUESTED = 2;
-
private Icon mSmallIcon;
private Icon mLargeIcon;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 9428f44..19a98f3 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -540,6 +540,13 @@
void deactivate() {
mActive = false;
}
+
+ @Override
+ public String toString() {
+ return "ControlledInputConnectionWrapper{mActive=" + mActive
+ + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
+ + "}";
+ }
}
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 65e8058..bd41c5d 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -580,7 +580,8 @@
char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX];
char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX];
char usejitOptsBuf[sizeof("-Xusejit:")-1 + PROPERTY_VALUE_MAX];
- char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX];
+ char jitmaxsizeOptsBuf[sizeof("-Xjitmaxsize:")-1 + PROPERTY_VALUE_MAX];
+ char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX];
char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX];
char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
@@ -684,7 +685,8 @@
* JIT related options.
*/
parseRuntimeOption("dalvik.vm.usejit", usejitOptsBuf, "-Xusejit:");
- parseRuntimeOption("dalvik.vm.jitcodecachesize", jitcodecachesizeOptsBuf, "-Xjitcodecachesize:");
+ parseRuntimeOption("dalvik.vm.jitmaxsize", jitmaxsizeOptsBuf, "-Xjitmaxsize:");
+ parseRuntimeOption("dalvik.vm.jitinitialsize", jitinitialsizeOptsBuf, "-Xjitinitialsize:");
parseRuntimeOption("dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:");
property_get("ro.config.low_ram", propBuf, "");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3f1be45..73c7487 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -21,6 +21,7 @@
#include <linux/fs.h>
#include <list>
+#include <sstream>
#include <string>
#include <fcntl.h>
@@ -74,8 +75,10 @@
MOUNT_EXTERNAL_WRITE = 3,
};
-static void RuntimeAbort(JNIEnv* env) {
- env->FatalError("RuntimeAbort");
+static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
+ std::ostringstream oss;
+ oss << __FILE__ << ":" << line << ": " << msg;
+ env->FatalError(oss.str().c_str());
}
// This signal handler is for zygote mode, since the zygote must reap its children
@@ -169,12 +172,11 @@
ScopedIntArrayRO gids(env, javaGids);
if (gids.get() == NULL) {
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Getting gids int array failed");
}
int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
if (rc == -1) {
- ALOGE("setgroups failed");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "setgroups failed");
}
}
@@ -194,8 +196,7 @@
ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i));
ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get()));
if (javaRlimit.size() != 3) {
- ALOGE("rlimits array must have a second dimension of size 3");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "rlimits array must have a second dimension of size 3");
}
rlim.rlim_cur = javaRlimit[1];
@@ -205,7 +206,7 @@
if (rc == -1) {
ALOGE("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur,
rlim.rlim_max);
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "setrlimit failed");
}
}
}
@@ -216,8 +217,7 @@
static void EnableKeepCapabilities(JNIEnv* env) {
int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
if (rc == -1) {
- ALOGE("prctl(PR_SET_KEEPCAPS) failed");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "prctl(PR_SET_KEEPCAPS) failed");
}
}
@@ -229,8 +229,7 @@
ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
"your kernel is compiled with file capabilities support");
} else {
- ALOGE("prctl(PR_CAPBSET_DROP) failed");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "prctl(PR_CAPBSET_DROP) failed");
}
}
}
@@ -251,7 +250,7 @@
if (capset(&capheader, &capdata[0]) == -1) {
ALOGE("capset(%" PRId64 ", %" PRId64 ") failed", permitted, effective);
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "capset failed");
}
}
@@ -259,7 +258,7 @@
errno = -set_sched_policy(0, SP_DEFAULT);
if (errno != 0) {
ALOGE("set_sched_policy(0, SP_DEFAULT) failed");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "set_sched_policy(0, SP_DEFAULT) failed");
}
}
@@ -370,8 +369,7 @@
jsize count = env->GetArrayLength(fdsToClose);
ScopedIntArrayRO ar(env, fdsToClose);
if (ar.get() == NULL) {
- ALOGE("Bad fd array");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Bad fd array");
}
jsize i;
int devnull;
@@ -379,13 +377,13 @@
devnull = open("/dev/null", O_RDWR);
if (devnull < 0) {
ALOGE("Failed to open /dev/null: %s", strerror(errno));
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Failed to open /dev/null");
continue;
}
ALOGV("Switching descriptor %d to /dev/null: %s", ar[i], strerror(errno));
if (dup2(devnull, ar[i]) < 0) {
ALOGE("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno));
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Failed dup2()");
}
close(devnull);
}
@@ -493,8 +491,7 @@
// FUSE hasn't been created yet by init.
// In either case, continue without external storage.
} else {
- ALOGE("Cannot continue without emulated storage");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Cannot continue without emulated storage");
}
}
@@ -522,13 +519,13 @@
int rc = setresgid(gid, gid, gid);
if (rc == -1) {
ALOGE("setresgid(%d) failed: %s", gid, strerror(errno));
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "setresgid failed");
}
rc = setresuid(uid, uid, uid);
if (rc == -1) {
ALOGE("setresuid(%d) failed: %s", uid, strerror(errno));
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "setresuid failed");
}
if (NeedsNoRandomizeWorkaround()) {
@@ -550,8 +547,7 @@
se_info = new ScopedUtfChars(env, java_se_info);
se_info_c_str = se_info->c_str();
if (se_info_c_str == NULL) {
- ALOGE("se_info_c_str == NULL");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "se_info_c_str == NULL");
}
}
const char* se_name_c_str = NULL;
@@ -560,15 +556,14 @@
se_name = new ScopedUtfChars(env, java_se_name);
se_name_c_str = se_name->c_str();
if (se_name_c_str == NULL) {
- ALOGE("se_name_c_str == NULL");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "se_name_c_str == NULL");
}
}
rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
if (rc == -1) {
ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
is_system_server, se_info_c_str, se_name_c_str);
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "selinux_android_setcontext failed");
}
// Make it easier to debug audit logs by setting the main thread's name to the
@@ -588,8 +583,7 @@
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
is_system_server ? NULL : instructionSet);
if (env->ExceptionCheck()) {
- ALOGE("Error calling post fork hooks.");
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
}
} else if (pid > 0) {
// the parent process
@@ -641,7 +635,7 @@
int status;
if (waitpid(pid, &status, WNOHANG) == pid) {
ALOGE("System server process %d has died. Restarting Zygote!", pid);
- RuntimeAbort(env);
+ RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}
}
return pid;
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index a50c945..0ee877e 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1424,7 +1424,7 @@
}
}
- static int resolveDensity(@NonNull Resources r, int parentDensity) {
+ static int resolveDensity(@Nullable Resources r, int parentDensity) {
final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi;
return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index f961a59..f630055e 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -231,6 +231,9 @@
private int mDpiScaledHeight = 0;
private Insets mDpiScaledInsets = Insets.NONE;
+ /** Whether DPI-scaled width, height, and insets need to be updated. */
+ private boolean mDpiScaledDirty = true;
+
// Temp variable, only for saving "new" operation at the draw() time.
private final float[] mTmpFloats = new float[9];
private final Matrix mTmpMatrix = new Matrix();
@@ -259,9 +262,13 @@
* displayed, or {@code null} to use the constant state defaults
*/
private void updateLocalState(Resources res) {
- mTargetDensity = Drawable.resolveDensity(res, mVectorState.mVPathRenderer.mSourceDensity);
+ final int density = Drawable.resolveDensity(res, mVectorState.mVPathRenderer.mDensity);
+ if (mTargetDensity != density) {
+ mTargetDensity = density;
+ mDpiScaledDirty = true;
+ }
+
mTintFilter = updateTintFilter(mTintFilter, mVectorState.mTint, mVectorState.mTintMode);
- computeVectorSize();
}
@Override
@@ -422,17 +429,26 @@
@Override
public int getIntrinsicWidth() {
+ if (mDpiScaledDirty) {
+ computeVectorSize();
+ }
return mDpiScaledWidth;
}
@Override
public int getIntrinsicHeight() {
+ if (mDpiScaledDirty) {
+ computeVectorSize();
+ }
return mDpiScaledHeight;
}
/** @hide */
@Override
public Insets getOpticalInsets() {
+ if (mDpiScaledDirty) {
+ computeVectorSize();
+ }
return mDpiScaledInsets;
}
@@ -444,7 +460,7 @@
final VPathRenderer pathRenderer = mVectorState.mVPathRenderer;
final Insets opticalInsets = pathRenderer.mOpticalInsets;
- final int sourceDensity = pathRenderer.mSourceDensity;
+ final int sourceDensity = pathRenderer.mDensity;
final int targetDensity = mTargetDensity;
if (targetDensity != sourceDensity) {
mDpiScaledWidth = Drawable.scaleFromDensity(
@@ -465,6 +481,8 @@
mDpiScaledHeight = (int) pathRenderer.mBaseHeight;
mDpiScaledInsets = opticalInsets;
}
+
+ mDpiScaledDirty = false;
}
@Override
@@ -481,6 +499,11 @@
return;
}
+ final VPathRenderer path = state.mVPathRenderer;
+ final boolean changedDensity = path.setDensity(
+ Drawable.resolveDensity(t.getResources(), 0));
+ mDpiScaledDirty |= changedDensity;
+
if (state.mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrs, R.styleable.VectorDrawable);
@@ -492,6 +515,9 @@
} finally {
a.recycle();
}
+
+ // May have changed size.
+ mDpiScaledDirty = true;
}
// Apply theme to contained color state list.
@@ -499,7 +525,6 @@
state.mTint = state.mTint.obtainForTheme(t);
}
- final VPathRenderer path = state.mVPathRenderer;
if (path != null && path.canApplyTheme()) {
path.applyTheme(t);
}
@@ -565,21 +590,24 @@
}
@Override
- public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final VectorDrawableState state = mVectorState;
- final VPathRenderer pathRenderer = new VPathRenderer();
- state.mVPathRenderer = pathRenderer;
+ state.mVPathRenderer = new VPathRenderer();
+ state.mVPathRenderer.setDensity(Drawable.resolveDensity(r, 0));
- final TypedArray a = obtainAttributes(res, theme, attrs, R.styleable.VectorDrawable);
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable);
updateStateFromTypedArray(a);
a.recycle();
+ mDpiScaledDirty = true;
+
state.mCacheDirty = true;
- inflateInternal(res, parser, attrs, theme);
+ inflateChildElements(r, parser, attrs, theme);
// Update local properties.
- updateLocalState(res);
+ updateLocalState(r);
}
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
@@ -592,13 +620,6 @@
// Extract the theme attributes, if any.
state.mThemeAttrs = a.extractThemeAttrs();
- // The density may have changed since the last update (if any). Any
- // dimension-type attributes will need their default values scaled.
- final int targetDensity = Drawable.resolveDensity(a.getResources(), 0);
- final int sourceDensity = pathRenderer.mSourceDensity;
- final float densityScale = targetDensity / (float) sourceDensity;
- pathRenderer.mSourceDensity = targetDensity;
-
final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
if (tintMode != -1) {
state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
@@ -626,11 +647,9 @@
}
pathRenderer.mBaseWidth = a.getDimension(
- R.styleable.VectorDrawable_width,
- pathRenderer.mBaseWidth * densityScale);
+ R.styleable.VectorDrawable_width, pathRenderer.mBaseWidth);
pathRenderer.mBaseHeight = a.getDimension(
- R.styleable.VectorDrawable_height,
- pathRenderer.mBaseHeight * densityScale);
+ R.styleable.VectorDrawable_height, pathRenderer.mBaseHeight);
if (pathRenderer.mBaseWidth <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
@@ -641,21 +660,17 @@
}
final int insetLeft = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetLeft,
- (int) (pathRenderer.mOpticalInsets.left * densityScale));
+ R.styleable.VectorDrawable_opticalInsetLeft, pathRenderer.mOpticalInsets.left);
final int insetTop = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetTop,
- (int) (pathRenderer.mOpticalInsets.top * densityScale));
+ R.styleable.VectorDrawable_opticalInsetTop, pathRenderer.mOpticalInsets.top);
final int insetRight = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetRight,
- (int) (pathRenderer.mOpticalInsets.right * densityScale));
+ R.styleable.VectorDrawable_opticalInsetRight, pathRenderer.mOpticalInsets.right);
final int insetBottom = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetBottom,
- (int) (pathRenderer.mOpticalInsets.bottom * densityScale));
+ R.styleable.VectorDrawable_opticalInsetBottom, pathRenderer.mOpticalInsets.bottom);
pathRenderer.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
- final float alphaInFloat = a.getFloat(R.styleable.VectorDrawable_alpha,
- pathRenderer.getAlpha());
+ final float alphaInFloat = a.getFloat(
+ R.styleable.VectorDrawable_alpha, pathRenderer.getAlpha());
pathRenderer.setAlpha(alphaInFloat);
final String name = a.getString(R.styleable.VectorDrawable_name);
@@ -665,7 +680,7 @@
}
}
- private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
+ private void inflateChildElements(Resources res, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
final VectorDrawableState state = mVectorState;
final VPathRenderer pathRenderer = state.mVPathRenderer;
@@ -929,7 +944,7 @@
int mRootAlpha = 0xFF;
String mRootName = null;
- int mSourceDensity = DisplayMetrics.DENSITY_DEFAULT;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
@@ -966,12 +981,37 @@
mChangingConfigurations = copy.mChangingConfigurations;
mRootAlpha = copy.mRootAlpha;
mRootName = copy.mRootName;
- mSourceDensity = copy.mSourceDensity;
+ mDensity = copy.mDensity;
if (copy.mRootName != null) {
mVGTargetsMap.put(copy.mRootName, this);
}
}
+ public final boolean setDensity(int targetDensity) {
+ if (mDensity != targetDensity) {
+ final int sourceDensity = mDensity;
+ mDensity = targetDensity;
+ applyDensityScaling(sourceDensity, targetDensity);
+ return true;
+ }
+ return false;
+ }
+
+ private void applyDensityScaling(int sourceDensity, int targetDensity) {
+ mBaseWidth = Drawable.scaleFromDensity(mBaseWidth, sourceDensity, targetDensity);
+ mBaseHeight = Drawable.scaleFromDensity(mBaseHeight, sourceDensity, targetDensity);
+
+ final int insetLeft = Drawable.scaleFromDensity(
+ mOpticalInsets.left, sourceDensity, targetDensity, false);
+ final int insetTop = Drawable.scaleFromDensity(
+ mOpticalInsets.top, sourceDensity, targetDensity, false);
+ final int insetRight = Drawable.scaleFromDensity(
+ mOpticalInsets.right, sourceDensity, targetDensity, false);
+ final int insetBottom = Drawable.scaleFromDensity(
+ mOpticalInsets.bottom, sourceDensity, targetDensity, false);
+ mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
+ }
+
public boolean canApplyTheme() {
return mRootGroup.canApplyTheme();
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index f0f8161..7c0676f 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -31,6 +31,7 @@
import android.provider.DocumentsProvider;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.FileNotFoundException;
@@ -59,6 +60,7 @@
private MtpManager mMtpManager;
private ContentResolver mResolver;
+ @GuardedBy("mDeviceToolkits")
private Map<Integer, DeviceToolkit> mDeviceToolkits;
private RootScanner mRootScanner;
private Resources mResources;
@@ -222,9 +224,11 @@
@Override
public void onTrimMemory(int level) {
- for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
- toolkit.mDocumentLoader.clearCompletedTasks();
- }
+ synchronized (mDeviceToolkits) {
+ for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
+ toolkit.mDocumentLoader.clearCompletedTasks();
+ }
+ }
}
@Override
@@ -254,21 +258,28 @@
}
void openDevice(int deviceId) throws IOException {
- mMtpManager.openDevice(deviceId);
- mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase));
- mRootScanner.scanNow();
+ synchronized (mDeviceToolkits) {
+ mMtpManager.openDevice(deviceId);
+ mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase));
+ }
+ mRootScanner.resume();
}
- void closeDevice(int deviceId) throws IOException {
+ void closeDevice(int deviceId) throws IOException, InterruptedException {
// TODO: Flush the device before closing (if not closed externally).
- getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
- mDeviceToolkits.remove(deviceId);
- mDatabase.removeDeviceRows(deviceId);
- mMtpManager.closeDevice(deviceId);
+ synchronized (mDeviceToolkits) {
+ getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
+ mDeviceToolkits.remove(deviceId);
+ mDatabase.removeDeviceRows(deviceId);
+ mMtpManager.closeDevice(deviceId);
+ }
mRootScanner.notifyChange();
+ if (!hasOpenedDevices()) {
+ mRootScanner.pause();
+ }
}
- void close() throws InterruptedException {
+ synchronized void closeAllDevices() throws InterruptedException {
boolean closed = false;
for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
try {
@@ -282,15 +293,30 @@
}
if (closed) {
mRootScanner.notifyChange();
+ mRootScanner.pause();
}
- mRootScanner.close();
- mDatabase.close();
}
boolean hasOpenedDevices() {
return mMtpManager.getOpenedDeviceIds().length != 0;
}
+ /**
+ * Finalize the content provider for unit tests.
+ */
+ @Override
+ public void shutdown() {
+ try {
+ closeAllDevices();
+ } catch (InterruptedException e) {
+ // It should fail unit tests by throwing runtime exception.
+ throw new RuntimeException(e.getMessage());
+ } finally {
+ mDatabase.close();
+ super.shutdown();
+ }
+ }
+
private void notifyChildDocumentsChange(String parentDocumentId) {
mResolver.notifyChange(
DocumentsContract.buildChildDocumentsUri(AUTHORITY, parentDocumentId),
@@ -299,11 +325,13 @@
}
private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException {
- final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId);
- if (toolkit == null) {
- throw new FileNotFoundException();
+ synchronized (mDeviceToolkits) {
+ final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId);
+ if (toolkit == null) {
+ throw new FileNotFoundException();
+ }
+ return toolkit;
}
- return toolkit;
}
private PipeManager getPipeManager(Identifier identifier) throws FileNotFoundException {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
index 2d1af26..723dc14 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java
@@ -67,10 +67,10 @@
provider.openDevice(device.getDeviceId());
return START_STICKY;
} catch (IOException error) {
- Log.d(MtpDocumentsProvider.TAG, error.getMessage());
+ Log.e(MtpDocumentsProvider.TAG, error.getMessage());
}
} else {
- Log.d(MtpDocumentsProvider.TAG, "Received unknown intent action.");
+ Log.w(MtpDocumentsProvider.TAG, "Received unknown intent action.");
}
stopSelfIfNeeded();
return Service.START_NOT_STICKY;
@@ -82,7 +82,7 @@
unregisterReceiver(mReceiver);
mReceiver = null;
try {
- provider.close();
+ provider.closeAllDevices();
} catch (InterruptedException e) {
Log.e(MtpDocumentsProvider.TAG, e.getMessage());
}
@@ -105,8 +105,8 @@
final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance();
try {
provider.closeDevice(device.getDeviceId());
- } catch (IOException error) {
- Log.d(MtpDocumentsProvider.TAG, error.getMessage());
+ } catch (IOException | InterruptedException error) {
+ Log.e(MtpDocumentsProvider.TAG, error.getMessage());
}
stopSelfIfNeeded();
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index d9ed4ab..b0962dd 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -9,6 +9,10 @@
import android.util.Log;
import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
final class RootScanner {
/**
@@ -27,13 +31,18 @@
*/
private final static long SHORT_POLLING_TIMES = 10;
+ /**
+ * Milliseconds we wait for background thread when pausing.
+ */
+ private final static long AWAIT_TERMINATION_TIMEOUT = 2000;
+
final ContentResolver mResolver;
final Resources mResources;
final MtpManager mManager;
final MtpDatabase mDatabase;
- boolean mClosed = false;
- int mPollingCount;
- Thread mBackgroundThread;
+
+ ExecutorService mExecutor;
+ FutureTask<Void> mCurrentTask;
RootScanner(
ContentResolver resolver,
@@ -46,6 +55,9 @@
mDatabase = database;
}
+ /**
+ * Notifies a change of the roots list via ContentResolver.
+ */
void notifyChange() {
final Uri uri =
DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
@@ -56,74 +68,81 @@
* Starts to check new changes right away.
* If the background thread has already gone, it restarts another background thread.
*/
- synchronized void scanNow() {
- if (mClosed) {
+ synchronized void resume() {
+ if (mExecutor == null) {
+ // Only single thread updates the database.
+ mExecutor = Executors.newSingleThreadExecutor();
+ }
+ if (mCurrentTask != null) {
+ // Cancel previous task.
+ mCurrentTask.cancel(true);
+ }
+ mCurrentTask = new FutureTask<Void>(new UpdateRootsRunnable(), null);
+ mExecutor.submit(mCurrentTask);
+ }
+
+ /**
+ * Stops background thread and wait for its termination.
+ * @throws InterruptedException
+ */
+ synchronized void pause() throws InterruptedException {
+ if (mExecutor == null) {
return;
}
- mPollingCount = 0;
- if (mBackgroundThread == null) {
- mBackgroundThread = new BackgroundLoaderThread();
- mBackgroundThread.start();
- } else {
- notify();
+ mExecutor.shutdownNow();
+ if (!mExecutor.awaitTermination(AWAIT_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ Log.e(MtpDocumentsProvider.TAG, "Failed to terminate RootScanner's background thread.");
}
+ mExecutor = null;
}
- void close() throws InterruptedException {
- Thread thread;
- synchronized (this) {
- mClosed = true;
- thread = mBackgroundThread;
- if (mBackgroundThread == null) {
- return;
- }
- notify();
- }
- thread.join();
- }
-
- private final class BackgroundLoaderThread extends Thread {
+ /**
+ * Runnable to scan roots and update the database information.
+ */
+ private final class UpdateRootsRunnable implements Runnable {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- synchronized (RootScanner.this) {
- while (!mClosed) {
- final int[] deviceIds = mManager.getOpenedDeviceIds();
- if (deviceIds.length == 0) {
- break;
- }
- boolean changed = false;
- for (int deviceId : deviceIds) {
+ int pollingCount = 0;
+ while (!Thread.interrupted()) {
+ final int[] deviceIds = mManager.getOpenedDeviceIds();
+ if (deviceIds.length == 0) {
+ return;
+ }
+ boolean changed = false;
+ for (int deviceId : deviceIds) {
+ try {
+ final MtpRoot[] roots = mManager.getRoots(deviceId);
mDatabase.startAddingRootDocuments(deviceId);
try {
- changed = mDatabase.putRootDocuments(
- deviceId, mResources, mManager.getRoots(deviceId)) || changed;
- } catch (IOException|SQLiteException exp) {
- // The error may happen on the device. We would like to continue getting
- // roots for other devices.
- Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
- continue;
+ if (mDatabase.putRootDocuments(deviceId, mResources, roots)) {
+ changed = true;
+ }
} finally {
- changed = mDatabase.stopAddingRootDocuments(deviceId) || changed;
+ if (mDatabase.stopAddingRootDocuments(deviceId)) {
+ changed = true;
+ }
}
- }
- if (changed) {
- notifyChange();
- }
- mPollingCount++;
- try {
- // Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
- // more likely to add new root just after the device is added.
- // TODO: Use short interval only for a device that is just added.
- RootScanner.this.wait(
- mPollingCount > SHORT_POLLING_TIMES ?
- LONG_POLLING_INTERVAL : SHORT_POLLING_INTERVAL);
- } catch (InterruptedException exception) {
- break;
+ } catch (IOException | SQLiteException exception) {
+ // The error may happen on the device. We would like to continue getting
+ // roots for other devices.
+ Log.e(MtpDocumentsProvider.TAG, exception.getMessage());
}
}
-
- mBackgroundThread = null;
+ if (changed) {
+ notifyChange();
+ }
+ pollingCount++;
+ try {
+ // Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
+ // more likely to add new root just after the device is added.
+ // TODO: Use short interval only for a device that is just added.
+ Thread.sleep(pollingCount > SHORT_POLLING_TIMES ?
+ LONG_POLLING_INTERVAL : SHORT_POLLING_INTERVAL);
+ } catch (InterruptedException exp) {
+ // The while condition handles the interrupted flag.
+ continue;
+ }
}
}
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 82e08cd..cabb08d 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -49,11 +49,7 @@
@Override
public void tearDown() {
- try {
- mProvider.close();
- } catch (InterruptedException e) {
- fail();
- }
+ mProvider.shutdown();
}
public void testOpenAndCloseDevice() throws Exception {
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index c7cf61a..5a6f1d1 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -61,7 +61,7 @@
<activity
android:name=".ui.PrintActivity"
- android:configChanges="orientation|screenSize"
+ android:configChanges="screenSize|smallestScreenSize|orientation"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:theme="@style/PrintActivity">
<intent-filter>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index fe67fd9..965e7a67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -239,7 +239,8 @@
SystemServicesProxy ssp, Resources res) {
Bitmap tdIcon = iconBitmap != null
? iconBitmap
- : ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename);
+ : ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename,
+ taskKey.userId);
if (tdIcon != null) {
return ssp.getBadgedIcon(new BitmapDrawable(res, tdIcon), taskKey.userId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 4ecb80a..f3c66a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -17,6 +17,8 @@
package com.android.systemui.recents.views;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.content.Context;
import android.graphics.Bitmap;
@@ -31,7 +33,6 @@
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.recents.Constants;
import com.android.systemui.recents.ExitRecentsWindowFirstAnimationFrameEvent;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsDebugFlags;
@@ -48,6 +49,7 @@
import java.util.ArrayList;
import java.util.List;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
@@ -93,7 +95,7 @@
final boolean lockToTask, final Rect bounds, int destinationStack) {
final ActivityOptions opts = ActivityOptions.makeBasic();
if (bounds != null) {
- opts.setBounds(bounds.isEmpty() ? null : bounds);
+ opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
}
final ActivityOptions.OnAnimationStartedListener animStartedListener;
@@ -244,8 +246,7 @@
// Ensure we have a valid target stack id
final int targetStackId = destinationStack != INVALID_STACK_ID ?
destinationStack : task.key.stackId;
- if (targetStackId != FREEFORM_WORKSPACE_STACK_ID
- && targetStackId != FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (!StackId.useAnimationSpecForAppTransition(targetStackId)) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index d3d9bef..f8ab35f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -2161,13 +2161,10 @@
boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
boolean isFullscreen = notification.fullScreenIntent != null;
boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
- boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
- Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
boolean accessibilityForcesLaunch = isFullscreen
&& mAccessibilityManager.isTouchExplorationEnabled();
boolean justLaunchedFullScreenIntent = entry.hasJustLaunchedFullScreenIntent();
boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
- && isAllowed
&& !accessibilityForcesLaunch
&& !justLaunchedFullScreenIntent
&& mPowerManager.isScreenOn()
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index ae9ddc0..40f7c5c 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1196,6 +1196,9 @@
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
clearClientSessionLocked(cs);
+ if (mCurClient == cs) {
+ mCurClient = null;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 9ac4ba3..37d47c3 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -88,6 +88,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
@@ -114,6 +115,7 @@
public class AccountManagerService
extends IAccountManager.Stub
implements RegisteredServicesCacheListener<AuthenticatorDescription> {
+
private static final String TAG = "AccountManagerService";
private static final String DATABASE_NAME = "accounts.db";
@@ -2283,6 +2285,193 @@
}
}
+ @Override
+ public void startAddAccountSession(final IAccountManagerResponse response, final String accountType,
+ final String authTokenType, final String[] requiredFeatures,
+ final boolean expectActivityLaunch,
+ final Bundle optionsIn) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "startAddAccountSession: accountType " + accountType
+ + ", response " + response
+ + ", authTokenType " + authTokenType
+ + ", requiredFeatures " + stringArrayToString(requiredFeatures)
+ + ", expectActivityLaunch " + expectActivityLaunch
+ + ", caller's uid " + Binder.getCallingUid()
+ + ", pid " + Binder.getCallingPid());
+ }
+ if (response == null) {
+ throw new IllegalArgumentException("response is null");
+ }
+ if (accountType == null) {
+ throw new IllegalArgumentException("accountType is null");
+ }
+
+ int userId = Binder.getCallingUserHandle().getIdentifier();
+ if (!canUserModifyAccounts(userId)) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
+ "User is not allowed to add an account!");
+ } catch (RemoteException re) {
+ }
+ showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
+ return;
+ }
+ if (!canUserModifyAccountsForType(userId, accountType)) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+ "User cannot modify accounts of this type (policy).");
+ } catch (RemoteException re) {
+ }
+ showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
+ userId);
+ return;
+ }
+
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
+ options.putInt(AccountManager.KEY_CALLER_UID, uid);
+ options.putInt(AccountManager.KEY_CALLER_PID, pid);
+
+ int usrId = UserHandle.getCallingUserId();
+ long identityToken = clearCallingIdentity();
+ try {
+ UserAccounts accounts = getUserAccounts(usrId);
+ logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
+ TABLE_ACCOUNTS, uid);
+ new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
+ null /* accountName */, false /* authDetailsRequired */,
+ true /* updateLastAuthenticationTime */) {
+ @Override
+ public void run() throws RemoteException {
+ mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
+ requiredFeatures, options);
+ }
+
+ @Override
+ protected String toDebugString(long now) {
+ String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
+ return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
+ + accountType + ", requiredFeatures "
+ + (requiredFeatures != null ? requiredFeaturesStr : null);
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
+ private abstract class StartAccountSession extends Session {
+
+ public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
+ String accountType, boolean expectActivityLaunch, String accountName,
+ boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
+ super(accounts, response, accountType, expectActivityLaunch,
+ true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
+ updateLastAuthenticationTime);
+ }
+
+ @Override
+ public void onResult(Bundle result) {
+ mNumResults++;
+ Intent intent = null;
+
+ if (result != null
+ && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
+ /*
+ * The Authenticator API allows third party authenticators to
+ * supply arbitrary intents to other apps that they can run,
+ * this can be very bad when those apps are in the system like
+ * the System Settings.
+ */
+ int authenticatorUid = Binder.getCallingUid();
+ long bid = Binder.clearCallingIdentity();
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
+ int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
+ if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid,
+ targetUid)) {
+ throw new SecurityException("Activity to be started with KEY_INTENT must "
+ + "share Authenticator's signatures");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(bid);
+ }
+ }
+
+ IAccountManagerResponse response;
+ if (mExpectActivityLaunch && result != null
+ && result.containsKey(AccountManager.KEY_INTENT)) {
+ response = mResponse;
+ } else {
+ response = getResponseAndClose();
+ }
+ if (response == null) {
+ return;
+ }
+ if (result == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
+ + response);
+ }
+ sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "null bundle returned");
+ return;
+ }
+
+ if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
+ // All AccountManager error codes are greater
+ // than 0
+ sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
+ result.getString(AccountManager.KEY_ERROR_MESSAGE));
+ return;
+ }
+
+ // Strip auth token from result.
+ result.remove(AccountManager.KEY_AUTHTOKEN);
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ getClass().getSimpleName() + " calling onResult() on response " + response);
+ }
+
+ // Get the session bundle created by authenticator. The
+ // bundle contains data necessary for finishing the session
+ // later. The session bundle will be encrypted here and
+ // decrypted later when trying to finish the session.
+ Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+ if (sessionBundle != null) {
+ String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
+ if (TextUtils.isEmpty(accountType)
+ && !mAccountType.equalsIgnoreCase(mAccountType)) {
+ Log.w(TAG, "Account type in session bundle doesn't match request.");
+ }
+ // Add accountType info to session bundle. This will
+ // override any value set by authenticator.
+ sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
+
+ // Encrypt session bundle before returning to caller.
+ try {
+ CryptoHelper cryptoHelper = CryptoHelper.getInstance();
+ Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
+ result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
+ } catch (GeneralSecurityException e) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.v(TAG, "Failed to encrypt session bundle!", e);
+ }
+ sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
+ "failed to encrypt session bundle");
+ return;
+ }
+ }
+
+ sendResponse(response, result);
+ }
+ }
+
private void showCantAddAccount(int errorCode, int userId) {
Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
@@ -3336,6 +3525,11 @@
private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
+ // TODO: This action doesn't add account to accountdb. Account is only
+ // added in finishAddAccount or finishAddAccountAsUser which may be in
+ // a different user profile.
+ private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
+
private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static void createDebugTable(SQLiteDatabase db) {
@@ -4300,4 +4494,29 @@
return mContext;
}
}
+
+ private void sendResponse(IAccountManagerResponse response, Bundle result) {
+ try {
+ response.onResult(result);
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote
+ // exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
+
+ private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
+ String errorMessage) {
+ try {
+ response.onError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote
+ // exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/accounts/CryptoHelper.java b/services/core/java/com/android/server/accounts/CryptoHelper.java
new file mode 100644
index 0000000..2b59b74
--- /dev/null
+++ b/services/core/java/com/android/server/accounts/CryptoHelper.java
@@ -0,0 +1,140 @@
+package com.android.server.accounts;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * A crypto helper for encrypting and decrypting bundle with in-memory symmetric
+ * key for {@link AccountManagerService}.
+ */
+/* default */ class CryptoHelper {
+ private static final String TAG = "Account";
+
+ private static final String KEY_CIPHER = "cipher";
+ private static final String KEY_MAC = "mac";
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
+ private static final String MAC_ALGORITHM = "HMACSHA256";
+ private static final int IV_LENGTH = 16;
+
+ private static CryptoHelper sInstance;
+ // Keys used for encrypting and decrypting data returned in a Bundle.
+ private final SecretKeySpec mCipherKeySpec;
+ private final SecretKeySpec mMacKeySpec;
+ private final IvParameterSpec mIv;
+
+ /* default */ synchronized static CryptoHelper getInstance() throws NoSuchAlgorithmException {
+ if (sInstance == null) {
+ sInstance = new CryptoHelper();
+ }
+ return sInstance;
+ }
+
+ private CryptoHelper() throws NoSuchAlgorithmException {
+ KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
+ SecretKey skey = kgen.generateKey();
+ mCipherKeySpec = new SecretKeySpec(skey.getEncoded(), KEY_ALGORITHM);
+
+ kgen = KeyGenerator.getInstance(MAC_ALGORITHM);
+ skey = kgen.generateKey();
+ mMacKeySpec = new SecretKeySpec(skey.getEncoded(), MAC_ALGORITHM);
+
+ // Create random iv
+ byte[] iv = new byte[IV_LENGTH];
+ SecureRandom secureRandom = new SecureRandom();
+ secureRandom.nextBytes(iv);
+ mIv = new IvParameterSpec(iv);
+ }
+
+ @NonNull
+ /* default */ Bundle encryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
+ Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
+ Parcel parcel = Parcel.obtain();
+ bundle.writeToParcel(parcel, 0);
+ byte[] bytes = parcel.marshall();
+ parcel.recycle();
+
+ Bundle encryptedBundle = new Bundle();
+
+ byte[] cipher = encrypt(bytes);
+ byte[] mac = createMac(cipher);
+
+ encryptedBundle.putByteArray(KEY_CIPHER, cipher);
+ encryptedBundle.putByteArray(KEY_MAC, mac);
+
+ return encryptedBundle;
+ }
+
+ @Nullable
+ /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
+ Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
+ byte[] cipherArray = bundle.getByteArray(KEY_CIPHER);
+ byte[] macArray = bundle.getByteArray(KEY_MAC);
+
+ if (!verifyMac(cipherArray, macArray)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Escrow mac mismatched!");
+ }
+ return null;
+ }
+
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ cipher.init(Cipher.DECRYPT_MODE, mCipherKeySpec, mIv);
+ byte[] decryptedBytes = cipher.doFinal(cipherArray);
+
+ Parcel decryptedParcel = Parcel.obtain();
+ decryptedParcel.unmarshall(decryptedBytes, 0, decryptedBytes.length);
+ decryptedParcel.setDataPosition(0);
+ Bundle decryptedBundle = new Bundle();
+ decryptedBundle.readFromParcel(decryptedParcel);
+ decryptedParcel.recycle();
+ return decryptedBundle;
+ }
+
+ private boolean verifyMac(@Nullable byte[] cipherArray, @Nullable byte[] macArray)
+ throws GeneralSecurityException {
+
+ if (cipherArray == null || cipherArray.length == 0 || macArray == null
+ || macArray.length == 0) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Cipher or MAC is empty!");
+ }
+ return false;
+ }
+ Mac mac = Mac.getInstance(MAC_ALGORITHM);
+ mac.init(mMacKeySpec);
+ mac.update(cipherArray);
+ return Arrays.equals(macArray, mac.doFinal());
+ }
+
+ @NonNull
+ private byte[] encrypt(@NonNull byte[] data) throws GeneralSecurityException {
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ cipher.init(Cipher.ENCRYPT_MODE, mCipherKeySpec, mIv);
+ return cipher.doFinal(data);
+ }
+
+ @NonNull
+ private byte[] createMac(@NonNull byte[] cipher) throws GeneralSecurityException {
+ Mac mac = Mac.getInstance(MAC_ALGORITHM);
+ mac.init(mMacKeySpec);
+ return mac.doFinal(cipher);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4b37205..3a0d80b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,72 +16,8 @@
package com.android.server.am;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
-import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
-import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
-
-import android.Manifest;
-import android.app.ActivityManager.StackId;
-import android.app.AppOpsManager;
-import android.app.ApplicationThreadNative;
-import android.app.BroadcastOptions;
-import android.app.IActivityContainer;
-import android.app.IActivityContainerCallback;
-import android.app.IAppTask;
-import android.app.ITaskStackListener;
-import android.app.ProfilerInfo;
-import android.app.assist.AssistContent;
-import android.app.assist.AssistStructure;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManagerInternal;
-import android.appwidget.AppWidgetManager;
-import android.content.pm.PermissionInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-import android.os.PowerManager;
-import android.os.ResultReceiver;
-import android.os.Trace;
-import android.os.TransactionTooLargeException;
-import android.os.WorkSource;
-import android.os.storage.IMountService;
-import android.os.storage.MountServiceInternal;
-import android.os.storage.StorageManager;
-import android.provider.Settings.Global;
-import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.VoiceInteractionSession;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.DebugUtils;
-import android.view.Display;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -118,19 +54,16 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManager.TaskThumbnailInfo;
import android.app.ActivityManagerInternal;
@@ -140,24 +73,37 @@
import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.ApplicationErrorReport;
+import android.app.ApplicationThreadNative;
+import android.app.BroadcastOptions;
import android.app.Dialog;
+import android.app.IActivityContainer;
+import android.app.IActivityContainerCallback;
import android.app.IActivityController;
+import android.app.IAppTask;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
-import android.app.IUidObserver;
+import android.app.ITaskStackListener;
import android.app.IUiAutomationConnection;
+import android.app.IUidObserver;
import android.app.IUserSwitchObserver;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.backup.IBackupManager;
+import android.app.ProfilerInfo;
import android.app.admin.DevicePolicyManager;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.backup.IBackupManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ClipData;
@@ -181,18 +127,24 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
import android.content.pm.PathPermission;
+import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -210,21 +162,35 @@
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.TransactionTooLargeException;
import android.os.UpdateLock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.WorkSource;
+import android.os.storage.IMountService;
+import android.os.storage.MountServiceInternal;
+import android.os.storage.StorageManager;
import android.provider.Settings;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VoiceInteractionSession;
import android.text.format.DateUtils;
import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.DebugUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
@@ -233,13 +199,12 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.Xml;
+import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
-import dalvik.system.VMRuntime;
-
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
@@ -269,6 +234,100 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
+import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
+import static android.provider.Settings.Global.DEBUG_APP;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
+import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKSCREEN;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -1211,7 +1270,8 @@
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
- boolean mForceResizableActivites;
+ boolean mForceResizableActivities;
+ boolean mSupportsFreeformWindowManagement;
IActivityController mController = null;
String mProfileApp = null;
ProcessRecord mProfileProc = null;
@@ -8827,12 +8887,19 @@
}
@Override
- public Bitmap getTaskDescriptionIcon(String filename) {
- if (!FileUtils.isValidExtFilename(filename)
- || !filename.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
- throw new IllegalArgumentException("Bad filename: " + filename);
+ public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
+ if (userId != UserHandle.getCallingUserId()) {
+ enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "getTaskDescriptionIcon");
}
- return mTaskPersister.getTaskDescriptionIcon(filename);
+ final File passedIconFile = new File(filePath);
+ final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
+ passedIconFile.getName());
+ if (!legitIconFile.getPath().equals(filePath)
+ || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
+ throw new IllegalArgumentException("Bad file path: " + filePath);
+ }
+ return mTaskPersister.getTaskDescriptionIcon(filePath);
}
@Override
@@ -11805,21 +11872,21 @@
private void retrieveSettings() {
final ContentResolver resolver = mContext.getContentResolver();
- String debugApp = Settings.Global.getString(resolver, Settings.Global.DEBUG_APP);
- boolean waitForDebugger = Settings.Global.getInt(
- resolver, Settings.Global.WAIT_FOR_DEBUGGER, 0) != 0;
- boolean alwaysFinishActivities = Settings.Global.getInt(
- resolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0;
- boolean forceRtl = Settings.Global.getInt(
- resolver, Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0;
- int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
- boolean forceResizable = Settings.Global.getInt(
- resolver, Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
- defaultForceResizable) != 0;
- // Transfer any global setting for forcing RTL layout, into a System Property
- SystemProperties.set(Settings.Global.DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+ final boolean freeformWindowManagement =
+ mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT);
- Configuration configuration = new Configuration();
+ final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
+ final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
+ final boolean alwaysFinishActivities =
+ Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
+ final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
+ final int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
+ final boolean forceResizable = Settings.Global.getInt(
+ resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, defaultForceResizable) != 0;
+ // Transfer any global setting for forcing RTL layout, into a System Property
+ SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+
+ final Configuration configuration = new Configuration();
Settings.System.getConfiguration(resolver, configuration);
if (forceRtl) {
// This will take care of setting the correct layout direction flags
@@ -11830,7 +11897,8 @@
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
- mForceResizableActivites = forceResizable;
+ mForceResizableActivities = forceResizable;
+ mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
// This happens before any activities are started, so we can
// change mConfiguration in-place.
updateConfigurationLocked(configuration, null, true);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index aa04bd7..ea8a12f 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -61,6 +61,7 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -1212,8 +1213,10 @@
if (_taskDescription.getIconFilename() == null &&
(icon = _taskDescription.getIcon()) != null) {
final String iconFilename = createImageFilename(createTime, task.taskId);
- mStackSupervisor.mService.mTaskPersister.saveImage(icon, iconFilename);
- _taskDescription.setIconFilename(iconFilename);
+ final File iconFile = new File(TaskPersister.getUserImagesDir(userId), iconFilename);
+ final String iconFilePath = iconFile.getAbsolutePath();
+ mStackSupervisor.mService.mTaskPersister.saveImage(icon, iconFilePath);
+ _taskDescription.setIconFilename(iconFilePath);
}
taskDescription = _taskDescription;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 35b7c78..124d2ef 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -99,7 +99,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
@@ -1924,9 +1923,9 @@
boolean overrideBounds = false;
Rect newBounds = null;
if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
- if (options.hasBounds()) {
+ if (canUseActivityOptionsLaunchBounds(options)) {
overrideBounds = true;
- newBounds = options.getBounds();
+ newBounds = options.getLaunchBounds();
}
}
@@ -2928,8 +2927,8 @@
}
if (task.mResizeable && options != null) {
- if (options.hasBounds()) {
- Rect bounds = options.getBounds();
+ if (canUseActivityOptionsLaunchBounds(options)) {
+ Rect bounds = options.getLaunchBounds();
task.updateOverrideConfiguration(bounds);
final int stackId = task.getLaunchStackId();
if (stackId != task.stack.mStackId) {
@@ -2953,6 +2952,12 @@
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
}
+ private boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
+ // We use the launch bounds in the activity options is the device supports freeform
+ // window management.
+ return options.hasLaunchBounds() && mService.mSupportsFreeformWindowManagement;
+ }
+
ActivityStack getStack(int stackId) {
return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 150baf0..9a00075 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,10 +16,11 @@
package com.android.server.am;
-import android.content.pm.IPackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Debug;
+import android.os.Environment;
+import android.os.FileUtils;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -27,7 +28,6 @@
import android.util.Xml;
import android.os.Process;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -44,6 +44,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.List;
import libcore.io.IoUtils;
@@ -54,8 +55,10 @@
/** When not flushing don't write out files faster than this */
private static final long INTER_WRITE_DELAY_MS = 500;
- /** When not flushing delay this long before writing the first file out. This gives the next
- * task being launched a chance to load its resources without this occupying IO bandwidth. */
+ /**
+ * When not flushing delay this long before writing the first file out. This gives the next task
+ * being launched a chance to load its resources without this occupying IO bandwidth.
+ */
private static final long PRE_TASK_DELAY_MS = 3000;
/** The maximum number of entries to keep in the queue before draining it automatically. */
@@ -72,24 +75,23 @@
private static final String TAG_TASK = "task";
- static File sImagesDir;
- static File sTasksDir;
-
private final ActivityManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
private final RecentTasks mRecentTasks;
- /** Value determines write delay mode as follows:
- * < 0 We are Flushing. No delays between writes until the image queue is drained and all
- * tasks needing persisting are written to disk. There is no delay between writes.
- * == 0 We are Idle. Next writes will be delayed by #PRE_TASK_DELAY_MS.
- * > 0 We are Actively writing. Next write will be at this time. Subsequent writes will be
- * delayed by #INTER_WRITE_DELAY_MS. */
+ /**
+ * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
+ * until the image queue is drained and all tasks needing persisting are written to disk. There
+ * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
+ * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
+ * writes will be delayed by #INTER_WRITE_DELAY_MS.
+ */
private long mNextWriteTime = 0;
private final LazyTaskWriterThread mLazyTaskWriterThread;
private static class WriteQueueItem {}
+
private static class TaskWriteQueueItem extends WriteQueueItem {
final TaskRecord mTask;
@@ -97,12 +99,13 @@
mTask = task;
}
}
+
private static class ImageWriteQueueItem extends WriteQueueItem {
- final String mFilename;
+ final String mFilePath;
Bitmap mImage;
- ImageWriteQueueItem(String filename, Bitmap image) {
- mFilename = filename;
+ ImageWriteQueueItem(String filePath, Bitmap image) {
+ mFilePath = filePath;
mImage = image;
}
}
@@ -111,19 +114,18 @@
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
RecentTasks recentTasks) {
- sTasksDir = new File(systemDir, TASKS_DIRNAME);
- if (!sTasksDir.exists()) {
- if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
- if (!sTasksDir.mkdir()) {
- Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
+
+ final File legacyImagesDir = new File(systemDir, IMAGES_DIRNAME);
+ if (legacyImagesDir.exists()) {
+ if (!FileUtils.deleteContents(legacyImagesDir) || !legacyImagesDir.delete()) {
+ Slog.i(TAG, "Failure deleting legacy images directory: " + legacyImagesDir);
}
}
- sImagesDir = new File(systemDir, IMAGES_DIRNAME);
- if (!sImagesDir.exists()) {
- if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
- if (!sImagesDir.mkdir()) {
- Slog.e(TAG, "Failure creating images directory " + sImagesDir);
+ final File legacyTasksDir = new File(systemDir, TASKS_DIRNAME);
+ if (legacyTasksDir.exists()) {
+ if (!FileUtils.deleteContents(legacyTasksDir) || !legacyTasksDir.delete()) {
+ Slog.i(TAG, "Failure deleting legacy tasks directory: " + legacyTasksDir);
}
}
@@ -144,8 +146,8 @@
for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof ImageWriteQueueItem &&
- ((ImageWriteQueueItem) item).mFilename.startsWith(taskString)) {
- if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilename +
+ ((ImageWriteQueueItem) item).mFilePath.startsWith(taskString)) {
+ if (DEBUG) Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath +
" from write queue");
mWriteQueue.remove(queueNdx);
}
@@ -213,14 +215,14 @@
}
}
- void saveImage(Bitmap image, String filename) {
+ void saveImage(Bitmap image, String filePath) {
synchronized (this) {
int queueNdx;
for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof ImageWriteQueueItem) {
ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- if (imageWriteQueueItem.mFilename.equals(filename)) {
+ if (imageWriteQueueItem.mFilePath.equals(filePath)) {
// replace the Bitmap with the new one.
imageWriteQueueItem.mImage = image;
break;
@@ -228,14 +230,14 @@
}
}
if (queueNdx < 0) {
- mWriteQueue.add(new ImageWriteQueueItem(filename, image));
+ mWriteQueue.add(new ImageWriteQueueItem(filePath, image));
}
if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
mNextWriteTime = FLUSH_QUEUE;
} else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
- if (DEBUG) Slog.d(TAG, "saveImage: filename=" + filename + " now=" +
+ if (DEBUG) Slog.d(TAG, "saveImage: filePath=" + filePath + " now=" +
SystemClock.uptimeMillis() + " mNextWriteTime=" +
mNextWriteTime + " Callers=" + Debug.getCallers(4));
notifyAll();
@@ -244,22 +246,22 @@
yieldIfQueueTooDeep();
}
- Bitmap getTaskDescriptionIcon(String filename) {
+ Bitmap getTaskDescriptionIcon(String filePath) {
// See if it is in the write queue
- final Bitmap icon = getImageFromWriteQueue(filename);
+ final Bitmap icon = getImageFromWriteQueue(filePath);
if (icon != null) {
return icon;
}
- return restoreImage(filename);
+ return restoreImage(filePath);
}
- Bitmap getImageFromWriteQueue(String filename) {
+ Bitmap getImageFromWriteQueue(String filePath) {
synchronized (this) {
for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof ImageWriteQueueItem) {
ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- if (imageWriteQueueItem.mFilename.equals(filename)) {
+ if (imageWriteQueueItem.mFilePath.equals(filePath)) {
return imageWriteQueueItem.mImage;
}
}
@@ -275,7 +277,7 @@
xmlSerializer.setOutput(stringWriter);
if (DEBUG) xmlSerializer.setFeature(
- "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
// save task
xmlSerializer.startDocument(null, true);
@@ -321,19 +323,22 @@
return null;
}
- ArrayList<TaskRecord> restoreTasksLocked(final int [] validUserIds) {
- final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
+ private List<TaskRecord> restoreTasksForUserLocked(final int userId) {
+ final List<TaskRecord> tasks = new ArrayList<TaskRecord>();
ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
- File[] recentFiles = sTasksDir.listFiles();
+ File userTasksDir = getUserTasksDir(userId);
+
+ File[] recentFiles = userTasksDir.listFiles();
if (recentFiles == null) {
- Slog.e(TAG, "Unable to list files from " + sTasksDir);
+ Slog.e(TAG, "restoreTasksForUser: Unable to list files from " + userTasksDir);
return tasks;
}
for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
File taskFile = recentFiles[taskNdx];
- if (DEBUG) Slog.d(TAG, "restoreTasksLocked: taskFile=" + taskFile.getName());
+ if (DEBUG) Slog.d(TAG, "restoreTasksForUser: userId=" + userId
+ + ", taskFile=" + taskFile.getName());
BufferedReader reader = null;
boolean deleteFile = false;
try {
@@ -348,30 +353,29 @@
if (event == XmlPullParser.START_TAG) {
if (DEBUG) Slog.d(TAG, "restoreTasksLocked: START_TAG name=" + name);
if (TAG_TASK.equals(name)) {
- final TaskRecord task =
- TaskRecord.restoreFromXml(in, mStackSupervisor);
- if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
- task);
+ final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor);
+ if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task="
+ + task);
if (task != null) {
// XXX Don't add to write queue... there is no reason to write
// out the stuff we just read, if we don't write it we will
// read the same thing again.
- //mWriteQueue.add(new TaskWriteQueueItem(task));
+ // mWriteQueue.add(new TaskWriteQueueItem(task));
final int taskId = task.taskId;
mStackSupervisor.setNextTaskId(taskId);
// Check if it's a valid user id. Don't add tasks for removed users.
- if (ArrayUtils.contains(validUserIds, task.userId)) {
+ if (userId == task.userId) {
task.isPersistable = true;
tasks.add(task);
recoveredTaskIds.add(taskId);
}
} else {
- Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " +
- fileToString(taskFile));
+ Slog.e(TAG, "restoreTasksForUser: Unable to restore taskFile="
+ + taskFile + ": " + fileToString(taskFile));
}
} else {
- Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event +
- " name=" + name);
+ Slog.wtf(TAG, "restoreTasksForUser: Unknown xml event=" + event
+ + " name=" + name);
}
}
XmlUtils.skipCurrentTag(in);
@@ -390,10 +394,19 @@
}
if (!DEBUG) {
- removeObsoleteFiles(recoveredTaskIds);
+ removeObsoleteFiles(recoveredTaskIds, userTasksDir.listFiles());
+ }
+ return tasks;
+ }
+
+ ArrayList<TaskRecord> restoreTasksLocked(final int[] validUserIds) {
+ final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
+
+ for (int userId : validUserIds) {
+ tasks.addAll(restoreTasksForUserLocked(userId));
}
- // Fixup task affiliation from taskIds
+ // Fix up task affiliation from taskIds
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
task.setPrevAffiliate(taskIdToTask(task.mPrevAffiliateTaskId, tasks));
@@ -420,7 +433,7 @@
}
private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: persistentTaskIds=" + persistentTaskIds +
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: persistentTaskIds=" + persistentTaskIds +
" files=" + files);
if (files == null) {
Slog.e(TAG, "File error accessing recents directory (too many files open?).");
@@ -434,14 +447,14 @@
final int taskId;
try {
taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Found taskId=" + taskId);
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: Found taskId=" + taskId);
} catch (Exception e) {
- Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
+ Slog.wtf(TAG, "removeObsoleteFiles: Can't parse file=" + file.getName());
file.delete();
continue;
}
if (!persistentTaskIds.contains(taskId)) {
- if (DEBUG) Slog.d(TAG, "removeObsoleteFile: deleting file=" + file.getName());
+ if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: deleting file=" + file.getName());
file.delete();
}
}
@@ -449,13 +462,39 @@
}
private void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds) {
- removeObsoleteFiles(persistentTaskIds, sTasksDir.listFiles());
- removeObsoleteFiles(persistentTaskIds, sImagesDir.listFiles());
+ for (int userId : mService.getRunningUserIds()) {
+ removeObsoleteFiles(persistentTaskIds, getUserImagesDir(userId).listFiles());
+ removeObsoleteFiles(persistentTaskIds, getUserTasksDir(userId).listFiles());
+ }
}
static Bitmap restoreImage(String filename) {
if (DEBUG) Slog.d(TAG, "restoreImage: restoring " + filename);
- return BitmapFactory.decodeFile(sImagesDir + File.separator + filename);
+ return BitmapFactory.decodeFile(filename);
+ }
+
+ static File getUserTasksDir(int userId) {
+ File userTasksDir = new File(Environment.getUserSystemDirectory(userId), TASKS_DIRNAME);
+
+ if (!userTasksDir.exists()) {
+ if (!userTasksDir.mkdir()) {
+ Slog.e(TAG, "Failure creating tasks directory for user " + userId + ": "
+ + userTasksDir);
+ }
+ }
+ return userTasksDir;
+ }
+
+ static File getUserImagesDir(int userId) {
+ File userImagesDir = new File(Environment.getUserSystemDirectory(userId), IMAGES_DIRNAME);
+
+ if (!userImagesDir.exists()) {
+ if (!userImagesDir.mkdir()) {
+ Slog.e(TAG, "Failure creating images directory for user " + userId + ": "
+ + userImagesDir);
+ }
+ }
+ return userImagesDir;
}
private class LazyTaskWriterThread extends Thread {
@@ -508,7 +547,6 @@
INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
}
-
while (mWriteQueue.isEmpty()) {
if (mNextWriteTime != 0) {
mNextWriteTime = 0; // idle.
@@ -542,15 +580,15 @@
if (item instanceof ImageWriteQueueItem) {
ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- final String filename = imageWriteQueueItem.mFilename;
+ final String filePath = imageWriteQueueItem.mFilePath;
final Bitmap bitmap = imageWriteQueueItem.mImage;
- if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filename);
+ if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
FileOutputStream imageFile = null;
try {
- imageFile = new FileOutputStream(new File(sImagesDir, filename));
+ imageFile = new FileOutputStream(new File(filePath));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
} catch (Exception e) {
- Slog.e(TAG, "saveImage: unable to save " + filename, e);
+ Slog.e(TAG, "saveImage: unable to save " + filePath, e);
} finally {
IoUtils.closeQuietly(imageFile);
}
@@ -575,18 +613,21 @@
FileOutputStream file = null;
AtomicFile atomicFile = null;
try {
- atomicFile = new AtomicFile(new File(sTasksDir, String.valueOf(
- task.taskId) + RECENTS_FILENAME + TASK_EXTENSION));
+ atomicFile = new AtomicFile(new File(
+ getUserTasksDir(task.userId),
+ String.valueOf(task.taskId) + RECENTS_FILENAME
+ + TASK_EXTENSION));
file = atomicFile.startWrite();
file.write(stringWriter.toString().getBytes());
file.write('\n');
atomicFile.finishWrite(file);
+
} catch (IOException e) {
if (file != null) {
atomicFile.failWrite(file);
}
- Slog.e(TAG, "Unable to open " + atomicFile + " for persisting. " +
- e);
+ Slog.e(TAG,
+ "Unable to open " + atomicFile + " for persisting. " + e);
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index b214080..3fc6846 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -27,15 +27,23 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.StackId;
-import android.app.ActivityManager.TaskThumbnail;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityManager.TaskThumbnail;
import android.app.ActivityManager.TaskThumbnailInfo;
@@ -58,8 +66,10 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
import android.util.Slog;
+
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.util.XmlUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -237,7 +247,8 @@
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
- mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ userId = UserHandle.getUserId(info.applicationInfo.uid);
+ mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(userId), mFilename);
mLastThumbnailInfo = new TaskThumbnailInfo();
taskId = _taskId;
mAffiliatedTaskId = _taskId;
@@ -256,7 +267,8 @@
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
- mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ userId = UserHandle.getUserId(info.applicationInfo.uid);
+ mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(userId), mFilename);
mLastThumbnailInfo = thumbnailInfo;
taskId = _taskId;
mAffiliatedTaskId = _taskId;
@@ -276,7 +288,6 @@
taskType = APPLICATION_ACTIVITY_TYPE;
mTaskToReturnTo = HOME_ACTIVITY_TYPE;
- userId = UserHandle.getUserId(info.applicationInfo.uid);
lastTaskDescription = _taskDescription;
mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
}
@@ -294,7 +305,7 @@
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
- mLastThumbnailFile = new File(TaskPersister.sImagesDir, mFilename);
+ mLastThumbnailFile = new File(TaskPersister.getUserImagesDir(_userId), mFilename);
mLastThumbnailInfo = lastThumbnailInfo;
taskId = _taskId;
intent = _intent;
@@ -326,7 +337,7 @@
mNextAffiliateTaskId = nextTaskId;
mCallingUid = callingUid;
mCallingPackage = callingPackage;
- mResizeable = resizeable || mService.mForceResizableActivites;
+ mResizeable = resizeable || mService.mForceResizableActivities;
mPrivileged = privileged;
ActivityInfo info = (mActivities.size() > 0) ? mActivities.get(0).info : null;
mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
@@ -428,7 +439,7 @@
} else {
autoRemoveRecents = false;
}
- mResizeable = info.resizeable || mService.mForceResizableActivites;
+ mResizeable = info.resizeable || mService.mForceResizableActivities;
mLockTaskMode = info.lockTaskLaunchMode;
mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
setLockTaskAuth();
@@ -537,7 +548,7 @@
mLastThumbnailFile.delete();
}
} else {
- mService.mTaskPersister.saveImage(thumbnail, mFilename);
+ mService.mTaskPersister.saveImage(thumbnail, mLastThumbnailFile.getAbsolutePath());
}
return true;
}
@@ -549,7 +560,8 @@
thumbs.thumbnailInfo = mLastThumbnailInfo;
thumbs.thumbnailFileDescriptor = null;
if (mLastThumbnail == null) {
- thumbs.mainThumbnail = mService.mTaskPersister.getImageFromWriteQueue(mFilename);
+ thumbs.mainThumbnail = mService.mTaskPersister.getImageFromWriteQueue(
+ mLastThumbnailFile.getAbsolutePath());
}
// Only load the thumbnail file if we don't have a thumbnail
if (thumbs.mainThumbnail == null && mLastThumbnailFile.exists()) {
@@ -682,7 +694,7 @@
// Only set this based on the first activity
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
- if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivites) {
+ if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivities) {
mResizeable = r.info.resizeable;
}
isPersistable = r.isPersistable();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fd1e9dd..4424838 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1224,19 +1224,6 @@
}
@Override
- public void setPackagePeekable(String pkg, int uid, boolean peekable) {
- checkCallerIsSystem();
-
- mRankingHelper.setPackagePeekable(pkg, uid, peekable);
- }
-
- @Override
- public boolean getPackagePeekable(String pkg, int uid) {
- checkCallerIsSystem();
- return mRankingHelper.getPackagePeekable(pkg, uid);
- }
-
- @Override
public void setPackageVisibilityOverride(String pkg, int uid, int visibility) {
checkCallerIsSystem();
mRankingHelper.setPackageVisibilityOverride(pkg, uid, visibility);
@@ -2157,14 +2144,6 @@
notification.priority = Notification.PRIORITY_HIGH;
}
}
- // force no heads up per package config
- if (!mRankingHelper.getPackagePeekable(pkg, callingUid)) {
- if (notification.extras == null) {
- notification.extras = new Bundle();
- }
- notification.extras.putInt(Notification.EXTRA_AS_HEADS_UP,
- Notification.HEADS_UP_NEVER);
- }
// 1. initial score: buckets of 10, around the app [-20..20]
final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 803db10..aea137b 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -20,10 +20,6 @@
void setPackagePriority(String packageName, int uid, int priority);
- boolean getPackagePeekable(String packageName, int uid);
-
- void setPackagePeekable(String packageName, int uid, boolean peekable);
-
int getPackageVisibilityOverride(String packageName, int uid);
void setPackageVisibilityOverride(String packageName, int uid, int visibility);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 66381f5..f8b661f 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -49,11 +49,9 @@
private static final String ATT_NAME = "name";
private static final String ATT_UID = "uid";
private static final String ATT_PRIORITY = "priority";
- private static final String ATT_PEEKABLE = "peekable";
private static final String ATT_VISIBILITY = "visibility";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
- private static final boolean DEFAULT_PEEKABLE = true;
private static final int DEFAULT_VISIBILITY =
NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
@@ -141,7 +139,6 @@
if (TAG_PACKAGE.equals(tag)) {
int uid = safeInt(parser, ATT_UID, Record.UNKNOWN_UID);
int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
- boolean peekable = safeBool(parser, ATT_PEEKABLE, DEFAULT_PEEKABLE);
int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
String name = parser.getAttributeValue(null, ATT_NAME);
@@ -167,9 +164,6 @@
if (priority != DEFAULT_PRIORITY) {
r.priority = priority;
}
- if (peekable != DEFAULT_PEEKABLE) {
- r.peekable = peekable;
- }
if (vis != DEFAULT_VISIBILITY) {
r.visibility = vis;
}
@@ -200,8 +194,7 @@
final int N = mRecords.size();
for (int i = N - 1; i >= 0; i--) {
final Record r = mRecords.valueAt(i);
- if (r.priority == DEFAULT_PRIORITY && r.peekable == DEFAULT_PEEKABLE
- && r.visibility == DEFAULT_VISIBILITY) {
+ if (r.priority == DEFAULT_PRIORITY && r.visibility == DEFAULT_VISIBILITY) {
mRecords.remove(i);
}
}
@@ -223,9 +216,6 @@
if (r.priority != DEFAULT_PRIORITY) {
out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
}
- if (r.peekable != DEFAULT_PEEKABLE) {
- out.attribute(null, ATT_PEEKABLE, Boolean.toString(r.peekable));
- }
if (r.visibility != DEFAULT_VISIBILITY) {
out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
}
@@ -348,22 +338,6 @@
}
@Override
- public boolean getPackagePeekable(String packageName, int uid) {
- final Record r = mRecords.get(recordKey(packageName, uid));
- return r != null ? r.peekable : DEFAULT_PEEKABLE;
- }
-
- @Override
- public void setPackagePeekable(String packageName, int uid, boolean peekable) {
- if (peekable == getPackagePeekable(packageName, uid)) {
- return;
- }
- getOrCreateRecord(packageName, uid).peekable = peekable;
- removeDefaultRecords();
- updateConfig();
- }
-
- @Override
public int getPackageVisibilityOverride(String packageName, int uid) {
final Record r = mRecords.get(recordKey(packageName, uid));
return r != null ? r.visibility : DEFAULT_VISIBILITY;
@@ -415,10 +389,6 @@
pw.print(" priority=");
pw.print(Notification.priorityToString(r.priority));
}
- if (r.peekable != DEFAULT_PEEKABLE) {
- pw.print(" peekable=");
- pw.print(r.peekable);
- }
if (r.visibility != DEFAULT_VISIBILITY) {
pw.print(" visibility=");
pw.print(Notification.visibilityToString(r.visibility));
@@ -460,7 +430,6 @@
String pkg;
int uid = UNKNOWN_UID;
int priority = DEFAULT_PRIORITY;
- boolean peekable = DEFAULT_PEEKABLE;
int visibility = DEFAULT_VISIBILITY;
}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index 0420269..c9e1315 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -41,6 +41,9 @@
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import android.os.SystemClock;
+import com.android.internal.logging.MetricsLogger;
+
/**
* This {@link NotificationSignalExtractor} attempts to validate
* people references. Also elevates the priority of real people.
@@ -218,6 +221,7 @@
private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
float[] affinityOut) {
+ long start = SystemClock.elapsedRealtime();
float affinity = NONE;
if (extras == null) {
return null;
@@ -251,6 +255,9 @@
// record the best available data, so far:
affinityOut[0] = affinity;
+ MetricsLogger.histogram(mBaseContext, "validate_people_cache_latency",
+ (int) (SystemClock.elapsedRealtime() - start));
+
if (pendingLookups.isEmpty()) {
if (VERBOSE) Slog.i(TAG, "final affinity: " + affinity);
return null;
@@ -430,6 +437,7 @@
@Override
public void work() {
+ long start = SystemClock.elapsedRealtime();
if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
long timeStartMs = System.currentTimeMillis();
for (final String handle: mPendingLookups) {
@@ -468,6 +476,9 @@
mUsageStats.registerPeopleAffinity(mRecord, mContactAffinity > NONE,
mContactAffinity == STARRED_CONTACT, false /* cached */);
}
+
+ MetricsLogger.histogram(mBaseContext, "validate_people_lookup_latency",
+ (int) (SystemClock.elapsedRealtime() - start));
}
@Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 121ef21..ae6874f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5318,11 +5318,9 @@
}
private boolean shouldDispatchInputWhenNonInteractive() {
- if (mDisplay == null || mDisplay.getState() == Display.STATE_OFF) {
- return false;
- }
- // Send events to keyguard while the screen is on and it's showing.
- if (isKeyguardShowingAndNotOccluded()) {
+ // Send events to keyguard while the screen is on.
+ if (isKeyguardShowingAndNotOccluded() && mDisplay != null
+ && mDisplay.getState() != Display.STATE_OFF) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 943c9ed..8b1a830 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1480,7 +1480,9 @@
mNextAppTransitionFutureCallback, null /* finishedCallback */,
mNextAppTransitionScaleUp);
mNextAppTransitionFutureCallback = null;
- mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
+ if (specs != null) {
+ mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
+ }
}
mService.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3ad2610..253fdad 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -55,6 +55,7 @@
import android.Manifest;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
@@ -3700,14 +3701,10 @@
}
}
- void prolongAnimationsFromSpecs(AppTransitionAnimationSpec[] specs, boolean scaleUp) {
+ void prolongAnimationsFromSpecs(@NonNull AppTransitionAnimationSpec[] specs, boolean scaleUp) {
// This is used by freeform <-> recents windows transition. We need to synchronize
// the animation with the appearance of the content of recents, so we will make
// animation stay on the first or last frame a little longer.
- if (specs == null) {
- Slog.wtf(TAG, "prolongAnimationsFromSpecs: AppTransitionAnimationSpec is null!");
- return;
- }
mTmpTaskIds.clear();
for (int i = specs.length - 1; i >= 0; i--) {
mTmpTaskIds.put(specs[i].taskId, 0);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index e0f95cf..c734fab 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -772,7 +772,8 @@
}
private void updateUsbNotification() {
- if (mNotificationManager == null || !mUseUsbNotification) return;
+ if (mNotificationManager == null || !mUseUsbNotification
+ || ("0".equals(SystemProperties.get("persist.charging.notify")))) return;
int id = 0;
Resources r = mContext.getResources();
if (mConnected || mHostConnected) {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
index ae4a57d..7ef7566 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -17,6 +17,7 @@
package com.android.tools.layoutlib.create;
import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -40,6 +41,7 @@
private final String mClassName;
private final Set<String> mDelegateMethods;
private final Log mLog;
+ private boolean mIsStaticInnerClass;
/**
* Creates a new {@link DelegateClassAdapter} that can transform some methods
@@ -62,16 +64,30 @@
mLog = log;
mClassName = className;
mDelegateMethods = delegateMethods;
+ // If this is an inner class, by default, we assume it's static. If it's not we will detect
+ // by looking at the fields (see visitField)
+ mIsStaticInnerClass = className.contains("$");
}
//----------------------------------
// Methods from the ClassAdapter
@Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature,
+ Object value) {
+ if (mIsStaticInnerClass && "this$0".equals(name)) {
+ // Having a "this$0" field, proves that this class is not a static inner class.
+ mIsStaticInnerClass = false;
+ }
+
+ return super.visitField(access, name, desc, signature, value);
+ }
+
+ @Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
- boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+ boolean isStaticMethod = (access & Opcodes.ACC_STATIC) != 0;
boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) ||
@@ -96,7 +112,8 @@
MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions);
DelegateMethodAdapter a = new DelegateMethodAdapter(
- mLog, null, mwDelegate, mClassName, name, desc, isStatic);
+ mLog, null, mwDelegate, mClassName, name, desc, isStaticMethod,
+ mIsStaticInnerClass);
// A native has no code to visit, so we need to generate it directly.
a.generateDelegateCode();
@@ -120,6 +137,7 @@
desc, signature, exceptions);
return new DelegateMethodAdapter(
- mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic);
+ mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStaticMethod,
+ mIsStaticInnerClass);
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
index 12690db..cca9e57 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -85,6 +85,8 @@
private String mDesc;
/** True if the original method is static. */
private final boolean mIsStatic;
+ /** True if the method is contained in a static inner class */
+ private final boolean mIsStaticInnerClass;
/** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
private final String mClassName;
/** The method name. */
@@ -120,7 +122,8 @@
String className,
String methodName,
String desc,
- boolean isStatic) {
+ boolean isStatic,
+ boolean isStaticClass) {
super(Opcodes.ASM4);
mLog = log;
mOrgWriter = mvOriginal;
@@ -129,6 +132,7 @@
mMethodName = methodName;
mDesc = desc;
mIsStatic = isStatic;
+ mIsStaticInnerClass = isStaticClass;
}
/**
@@ -206,7 +210,7 @@
// by the 'this' of any outer class, if any.
if (!mIsStatic) {
- if (outerType != null) {
+ if (outerType != null && !mIsStaticInnerClass) {
// The first-level inner class has a package-protected member called 'this$0'
// that points to the outer class.
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index 648cea43..e37a09b 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -27,6 +27,7 @@
import com.android.tools.layoutlib.create.dataclass.ClassWithNative;
import com.android.tools.layoutlib.create.dataclass.OuterClass;
import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;
import org.junit.Before;
import org.junit.Test;
@@ -56,6 +57,8 @@
private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName();
private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" +
InnerClass.class.getSimpleName();
+ private static final String STATIC_INNER_CLASS_NAME =
+ OuterClass.class.getCanonicalName() + "$" + StaticInnerClass.class.getSimpleName();
@Before
public void setUp() throws Exception {
@@ -294,6 +297,61 @@
}
}
+ @Test
+ public void testDelegateStaticInner() throws Throwable {
+ // We'll delegate the "get" method of both the inner and outer class.
+ HashSet<String> delegateMethods = new HashSet<String>();
+ delegateMethods.add("get");
+
+ // Generate the delegate for the outer class.
+ ClassWriter cwOuter = new ClassWriter(0 /*flags*/);
+ String outerClassName = OUTER_CLASS_NAME.replace('.', '/');
+ DelegateClassAdapter cvOuter = new DelegateClassAdapter(
+ mLog, cwOuter, outerClassName, delegateMethods);
+ ClassReader cr = new ClassReader(OUTER_CLASS_NAME);
+ cr.accept(cvOuter, 0 /* flags */);
+
+ // Generate the delegate for the static inner class.
+ ClassWriter cwInner = new ClassWriter(0 /*flags*/);
+ String innerClassName = STATIC_INNER_CLASS_NAME.replace('.', '/');
+ DelegateClassAdapter cvInner = new DelegateClassAdapter(
+ mLog, cwInner, innerClassName, delegateMethods);
+ cr = new ClassReader(STATIC_INNER_CLASS_NAME);
+ cr.accept(cvInner, 0 /* flags */);
+
+ // Load the generated classes in a different class loader and try them
+ ClassLoader2 cl2 = null;
+ try {
+ cl2 = new ClassLoader2() {
+ @Override
+ public void testModifiedInstance() throws Exception {
+
+ // Check the outer class
+ Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME);
+ Object o2 = outerClazz2.newInstance();
+ assertNotNull(o2);
+
+ // Check the inner class. Since it's not a static inner class, we need
+ // to use the hidden constructor that takes the outer class as first parameter.
+ Class<?> innerClazz2 = loadClass(STATIC_INNER_CLASS_NAME);
+ Constructor<?> innerCons = innerClazz2.getConstructor();
+ Object i2 = innerCons.newInstance();
+ assertNotNull(i2);
+
+ // The original StaticInner.get returns 100+10+20,
+ // but the delegate makes it return 6+10+20
+ assertEquals(6+10+20, callGet(i2, 10, 20));
+ assertEquals(100+10+20, callGet_Original(i2, 10, 20));
+ }
+ };
+ cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray());
+ cl2.add(STATIC_INNER_CLASS_NAME, cwInner.toByteArray());
+ cl2.testModifiedInstance();
+ } catch (Throwable t) {
+ throw dumpGeneratedClass(t, cl2);
+ }
+ }
+
//-------
/**
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
index f083e76..6dfb816 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
@@ -45,6 +45,16 @@
}
}
+ public static class StaticInnerClass {
+ public StaticInnerClass() {
+ }
+
+ // StaticInnerClass.get returns 100 + a + b
+ public int get(int a, long b) {
+ return 100 + a + (int) b;
+ }
+ }
+
@SuppressWarnings("unused")
private String privateMethod() {
return "outerPrivateMethod";
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
new file mode 100644
index 0000000..a29439e
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 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 com.android.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;
+
+/**
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass_StaticInnerClass_Delegate {
+ // The delegate override of Inner.get return 6 + a + b
+ public static int get(StaticInnerClass inner, int a, long b) {
+ return 6 + a + (int) b;
+ }
+}