Merge "ImageSwitcher -> ImageView in appcompat inflate"
diff --git a/api/current.txt b/api/current.txt
index 64838a4..69a3000 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2844,6 +2844,7 @@
public class AccountManager {
method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(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 boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
+ method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, int[]);
method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
method public java.lang.String blockingGetAuthToken(android.accounts.Account, java.lang.String, boolean) throws android.accounts.AuthenticatorException, java.io.IOException, android.accounts.OperationCanceledException;
method public void clearPassword(android.accounts.Account);
@@ -2862,10 +2863,13 @@
method public android.accounts.AuthenticatorDescription[] getAuthenticatorTypes();
method public java.lang.String getPassword(android.accounts.Account);
method public java.lang.String getPreviousName(android.accounts.Account);
+ method public int[] getRequestingUidsForType(java.lang.String);
method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public void invalidateAuthToken(java.lang.String, java.lang.String);
+ method public boolean isAccountVisible(android.accounts.Account, int);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
+ method public boolean makeAccountVisible(android.accounts.Account, int);
method public static deprecated android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], boolean, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.List<android.accounts.Account>, java.lang.String[], java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
method public boolean notifyAccountAuthenticated(android.accounts.Account);
@@ -2873,6 +2877,7 @@
method public deprecated android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public boolean removeAccountExplicitly(android.accounts.Account);
+ method public boolean removeAccountVisibility(android.accounts.Account, int);
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
method public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
@@ -22572,8 +22577,10 @@
method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method public java.lang.CharSequence getQueueTitle();
method public int getRatingType();
+ method public int getRepeatMode();
method public android.app.PendingIntent getSessionActivity();
method public android.media.session.MediaSession.Token getSessionToken();
+ method public boolean getShuffleMode();
method public android.media.session.MediaController.TransportControls getTransportControls();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
@@ -22590,8 +22597,10 @@
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void onQueueTitleChanged(java.lang.CharSequence);
+ method public void onRepeatModeChanged(int);
method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
+ method public void onShuffleModeChanged(boolean);
}
public static final class MediaController.PlaybackInfo {
@@ -22620,6 +22629,8 @@
method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
+ method public void setRepeatMode(int);
+ method public void setShuffleMode(boolean);
method public void skipToNext();
method public void skipToPrevious();
method public void skipToQueueItem(long);
@@ -22646,7 +22657,9 @@
method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
method public void setRatingType(int);
+ method public void setRepeatMode(int);
method public void setSessionActivity(android.app.PendingIntent);
+ method public void setShuffleMode(boolean);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
@@ -22669,6 +22682,8 @@
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
+ method public void onSetRepeatMode(int);
+ method public void onSetShuffleMode(boolean);
method public void onSkipToNext();
method public void onSkipToPrevious();
method public void onSkipToQueueItem(long);
@@ -22729,12 +22744,17 @@
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
+ field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+ field public static final long ACTION_SET_SHUFFLE_MODE = 524288L; // 0x80000L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+ field public static final int REPEAT_MODE_ALL = 2; // 0x2
+ field public static final int REPEAT_MODE_NONE = 0; // 0x0
+ field public static final int REPEAT_MODE_ONE = 1; // 0x1
field public static final int STATE_BUFFERING = 6; // 0x6
field public static final int STATE_CONNECTING = 8; // 0x8
field public static final int STATE_ERROR = 7; // 0x7
diff --git a/api/system-current.txt b/api/system-current.txt
index c7a05b2..d5eb5fa 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2958,6 +2958,7 @@
public class AccountManager {
method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(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 boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
+ method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, int[]);
method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
method public java.lang.String blockingGetAuthToken(android.accounts.Account, java.lang.String, boolean) throws android.accounts.AuthenticatorException, java.io.IOException, android.accounts.OperationCanceledException;
method public void clearPassword(android.accounts.Account);
@@ -2977,10 +2978,13 @@
method public android.accounts.AuthenticatorDescription[] getAuthenticatorTypes();
method public java.lang.String getPassword(android.accounts.Account);
method public java.lang.String getPreviousName(android.accounts.Account);
+ method public int[] getRequestingUidsForType(java.lang.String);
method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public void invalidateAuthToken(java.lang.String, java.lang.String);
+ method public boolean isAccountVisible(android.accounts.Account, int);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
+ method public boolean makeAccountVisible(android.accounts.Account, int);
method public static deprecated android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], boolean, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.List<android.accounts.Account>, java.lang.String[], java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
method public boolean notifyAccountAuthenticated(android.accounts.Account);
@@ -2988,6 +2992,7 @@
method public deprecated android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public boolean removeAccountExplicitly(android.accounts.Account);
+ method public boolean removeAccountVisibility(android.accounts.Account, int);
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
method public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
@@ -24158,8 +24163,10 @@
method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method public java.lang.CharSequence getQueueTitle();
method public int getRatingType();
+ method public int getRepeatMode();
method public android.app.PendingIntent getSessionActivity();
method public android.media.session.MediaSession.Token getSessionToken();
+ method public boolean getShuffleMode();
method public android.media.session.MediaController.TransportControls getTransportControls();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
@@ -24176,8 +24183,10 @@
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void onQueueTitleChanged(java.lang.CharSequence);
+ method public void onRepeatModeChanged(int);
method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
+ method public void onShuffleModeChanged(boolean);
}
public static final class MediaController.PlaybackInfo {
@@ -24206,6 +24215,8 @@
method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
+ method public void setRepeatMode(int);
+ method public void setShuffleMode(boolean);
method public void skipToNext();
method public void skipToPrevious();
method public void skipToQueueItem(long);
@@ -24232,7 +24243,9 @@
method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
method public void setRatingType(int);
+ method public void setRepeatMode(int);
method public void setSessionActivity(android.app.PendingIntent);
+ method public void setShuffleMode(boolean);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
@@ -24255,6 +24268,8 @@
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
+ method public void onSetRepeatMode(int);
+ method public void onSetShuffleMode(boolean);
method public void onSkipToNext();
method public void onSkipToPrevious();
method public void onSkipToQueueItem(long);
@@ -24315,12 +24330,17 @@
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
+ field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+ field public static final long ACTION_SET_SHUFFLE_MODE = 524288L; // 0x80000L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+ field public static final int REPEAT_MODE_ALL = 2; // 0x2
+ field public static final int REPEAT_MODE_NONE = 0; // 0x0
+ field public static final int REPEAT_MODE_ONE = 1; // 0x1
field public static final int STATE_BUFFERING = 6; // 0x6
field public static final int STATE_CONNECTING = 8; // 0x8
field public static final int STATE_ERROR = 7; // 0x7
diff --git a/api/test-current.txt b/api/test-current.txt
index 881d290..ce7c674 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2844,6 +2844,7 @@
public class AccountManager {
method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(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 boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
+ method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, int[]);
method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
method public java.lang.String blockingGetAuthToken(android.accounts.Account, java.lang.String, boolean) throws android.accounts.AuthenticatorException, java.io.IOException, android.accounts.OperationCanceledException;
method public void clearPassword(android.accounts.Account);
@@ -2862,10 +2863,13 @@
method public android.accounts.AuthenticatorDescription[] getAuthenticatorTypes();
method public java.lang.String getPassword(android.accounts.Account);
method public java.lang.String getPreviousName(android.accounts.Account);
+ method public int[] getRequestingUidsForType(java.lang.String);
method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public void invalidateAuthToken(java.lang.String, java.lang.String);
+ method public boolean isAccountVisible(android.accounts.Account, int);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
+ method public boolean makeAccountVisible(android.accounts.Account, int);
method public static deprecated android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, java.lang.String[], boolean, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
method public static android.content.Intent newChooseAccountIntent(android.accounts.Account, java.util.List<android.accounts.Account>, java.lang.String[], java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle);
method public boolean notifyAccountAuthenticated(android.accounts.Account);
@@ -2873,6 +2877,7 @@
method public deprecated android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public boolean removeAccountExplicitly(android.accounts.Account);
+ method public boolean removeAccountVisibility(android.accounts.Account, int);
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
method public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
@@ -22642,8 +22647,10 @@
method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method public java.lang.CharSequence getQueueTitle();
method public int getRatingType();
+ method public int getRepeatMode();
method public android.app.PendingIntent getSessionActivity();
method public android.media.session.MediaSession.Token getSessionToken();
+ method public boolean getShuffleMode();
method public android.media.session.MediaController.TransportControls getTransportControls();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
@@ -22660,8 +22667,10 @@
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void onQueueTitleChanged(java.lang.CharSequence);
+ method public void onRepeatModeChanged(int);
method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
+ method public void onShuffleModeChanged(boolean);
}
public static final class MediaController.PlaybackInfo {
@@ -22690,6 +22699,8 @@
method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
+ method public void setRepeatMode(int);
+ method public void setShuffleMode(boolean);
method public void skipToNext();
method public void skipToPrevious();
method public void skipToQueueItem(long);
@@ -22716,7 +22727,9 @@
method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
method public void setRatingType(int);
+ method public void setRepeatMode(int);
method public void setSessionActivity(android.app.PendingIntent);
+ method public void setShuffleMode(boolean);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
@@ -22739,6 +22752,8 @@
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
+ method public void onSetRepeatMode(int);
+ method public void onSetShuffleMode(boolean);
method public void onSkipToNext();
method public void onSkipToPrevious();
method public void onSkipToQueueItem(long);
@@ -22799,12 +22814,17 @@
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
+ field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
+ field public static final long ACTION_SET_SHUFFLE_MODE = 524288L; // 0x80000L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
+ field public static final int REPEAT_MODE_ALL = 2; // 0x2
+ field public static final int REPEAT_MODE_NONE = 0; // 0x0
+ field public static final int REPEAT_MODE_ONE = 1; // 0x1
field public static final int STATE_BUFFERING = 6; // 0x6
field public static final int STATE_CONNECTING = 8; // 0x8
field public static final int STATE_ERROR = 7; // 0x7
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index d7f23cb..2e9d0d6 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -57,6 +57,7 @@
(new Media()).run(args);
}
+ @Override
public void onShowUsage(PrintStream out) {
out.println(
"usage: media [subcommand] [options]\n" +
@@ -73,6 +74,7 @@
);
}
+ @Override
public void onRun() throws Exception {
mSessionService = ISessionManager.Stub.asInterface(ServiceManager.checkService(
Context.MEDIA_SESSION_SERVICE));
@@ -222,6 +224,16 @@
System.out.println("onVolumeInfoChanged " + info);
}
+ @Override
+ public void onRepeatModeChanged(int repeatMode) throws RemoteException {
+ System.out.println("onRepeatModeChanged " + repeatMode);
+ }
+
+ @Override
+ public void onShuffleModeChanged(boolean shuffleMode) throws RemoteException {
+ System.out.println("onShuffleModeChanged " + shuffleMode);
+ }
+
void printUsageMessage() {
try {
System.out.println("V2Monitoring session " + mController.getTag()
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 88b1297..59d2f18 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -733,6 +733,115 @@
}
/**
+ * Adds an account directly to the AccountManager. Additionally this
+ * makes the Account visible to desired UIDs of applications on the device,
+ * and sends directed broadcasts to these individual applications.
+ * <p>Normally used by sign-up wizards associated with authenticators, not
+ * directly by applications.
+ * <p>Calling this method does not update the last authenticated timestamp,
+ * referred by {@link #KEY_LAST_AUTHENTICATED_TIME}. To update it, call
+ * {@link #notifyAccountAuthenticated(Account)} after getting success.
+ * <p>It is safe to call this method from the main thread.
+ * <p>This method requires the caller to have a signature match with the
+ * authenticator that owns the specified account.
+ *
+ * @param account The {@link Account} to add
+ * @param password The password to associate with the account, null for none
+ * @param extras String values to use for the account's userdata, null for
+ * none
+ * @param selectedUids Array of uids whose associated applications can access
+ * this account without any additional user approval.
+ *
+ * @return True if the account was successfully added, false if the account
+ * already exists, the account is null, or another error occurs.
+ */
+ public boolean addAccountExplicitly(Account account, String password, Bundle extras,
+ int[] selectedUids) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ try {
+ return mService.addAccountExplicitlyWithUid(account, password, extras, selectedUids);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns all UIDs for applications that requested the account type.
+ * <p>This method requires the caller to have a signature match with the authenticator
+ * that owns the specified account.
+ *
+ * @param accountType The account type to be authenticated.
+ *
+ * @return array of all UIDs that support accounts of this
+ * account type that seek approval (to be used to know which accounts for
+ * the authenticator to include in addAccountExplicitly). Null if none.
+ */
+ public int[] getRequestingUidsForType(String accountType) {
+ try {
+ return mService.getRequestingUidsForType(accountType);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gives a certain UID, represented a application, access to an account
+ * <p>This method requires the caller to have a signature match with the authenticator
+ * that owns the specified account.
+ *
+ * @param account Account to make visible.
+ * @param uid The UID of the application to add account access.
+ *
+ * @return True if account made visible to application and was not previously visible.
+ */
+ public boolean makeAccountVisible(Account account, int uid) {
+ try {
+ return mService.makeAccountVisible(account, uid);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes visibility of certain account of a process identified
+ * by a given UID to an application.
+ * This is called by the Authenticator.
+ * <p>This method requires the caller to have a signature match with the authenticator
+ * that owns the specified account.
+ *
+ * @param account Remove visibility of this account..
+ * @param uid The UID of the application to remove account access.
+ *
+ * @return True if application access to account removed and was previously visible.
+ */
+ public boolean removeAccountVisibility(Account account, int uid) {
+ try {
+ return mService.removeAccountVisibility(account, uid);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Checks visibility of certain account of a process identified
+ * by a given UID. This is called by the Authenticator.
+ * <p>This method requires the caller to have a signature match with the authenticator
+ * that owns the specified account.
+ *
+ * @param account Account to check visibility.
+ * @param uid The UID of the application to check account access.
+ *
+ * @return True if application has access to the account
+ */
+ public boolean isAccountVisible(Account account, int uid) {
+ try {
+ return mService.isAccountVisible(account, uid);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Notifies the system that the account has just been authenticated. This
* information may be used by other applications to verify the account. This
* should be called only when the user has entered correct credentials for
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 7199288..c52faeb 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -21,7 +21,6 @@
import android.accounts.AuthenticatorDescription;
import android.os.Bundle;
-
/**
* Central application service that provides account management.
* @hide
@@ -92,7 +91,8 @@
void startUpdateCredentialsSession(in IAccountManagerResponse response, in Account account,
String authTokenType, boolean expectActivityLaunch, in Bundle options);
- /* Finish session started by startAddAccountSession(...) or startUpdateCredentialsSession(...) for user */
+ /* Finish session started by startAddAccountSession(...) or startUpdateCredentialsSession(...)
+ for user */
void finishSessionAsUser(in IAccountManagerResponse response, in Bundle sessionBundle,
boolean expectActivityLaunch, in Bundle appInfo, int userId);
@@ -102,4 +102,17 @@
/* Check if credentials update is suggested */
void isCredentialsUpdateSuggested(in IAccountManagerResponse response, in Account account,
String statusToken);
+
+ /* Allows Authenticator to view what packages or UIDs on phone have requested it. */
+ int[] getRequestingUidsForType(String accountType);
+
+ /* Allows authenticator to add an account explicitly that is only visible to
+ certain uids; the authenticator learns of these UIDs */
+ boolean addAccountExplicitlyWithUid(in Account account, String password, in Bundle extras,
+ in int[] selectedUids);
+
+ /* Controls visibility of UIDs of applications to Accounts */
+ boolean removeAccountVisibility(in Account a, in int uid);
+ boolean makeAccountVisible(in Account a, in int uid);
+ boolean isAccountVisible(in Account a, in int uid);
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5d87528..8cc1bc4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2080,14 +2080,8 @@
@Override
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags) {
- setComponentEnabledSettingAsUser(componentName, newState, flags, mContext.getUserId());
- }
-
- @Override
- public void setComponentEnabledSettingAsUser(ComponentName componentName,
- int newState, int flags, int userId) {
try {
- mPM.setComponentEnabledSetting(componentName, newState, flags, userId);
+ mPM.setComponentEnabledSetting(componentName, newState, flags, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2105,16 +2099,9 @@
@Override
public void setApplicationEnabledSetting(String packageName,
int newState, int flags) {
- setApplicationEnabledSettingAsUser(packageName, newState, flags,
- mContext.getUserId());
- }
-
- @Override
- public void setApplicationEnabledSettingAsUser(String packageName,
- int newState, int flags, int userId) {
try {
mPM.setApplicationEnabledSetting(packageName, newState, flags,
- userId, mContext.getOpPackageName());
+ mContext.getUserId(), mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index b9b609b..6b23da9 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -492,7 +492,7 @@
* If the calling launcher application contains pinned shortcuts, they will still work,
* even though the caller no longer has the shortcut host permission.
*
- * <p>Returns {@code false} when the user is locked.
+ * @throws IllegalStateException when the user is locked.
*
* @see ShortcutManager
*/
@@ -510,13 +510,12 @@
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
- * <p>Returns am empty list when the user is locked, or when the {@code user} user
- * is locked or not running.
- *
* @param query result includes shortcuts matching this query.
* @param user The UserHandle of the profile.
*
* @return the IDs of {@link ShortcutInfo}s that match the query.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
* @see ShortcutManager
*/
@@ -556,12 +555,11 @@
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
- * <p>Call will be ignored when the user is locked, or when the {@code user} user
- * is locked or not running.
- *
* @param packageName The target package name.
* @param shortcutIds The IDs of the shortcut to be pinned.
* @param user The UserHandle of the profile.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
* @see ShortcutManager
*/
@@ -630,13 +628,12 @@
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
- * <p>Returns {@code null} when the user is locked, or when the user owning the shortcut
- * is locked or not running.
- *
* @param density The preferred density of the icon, zero for default density. Use
* density DPI values from {@link DisplayMetrics}.
*
* @return The drawable associated with the shortcut.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
* @see ShortcutManager
* @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
@@ -681,11 +678,10 @@
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
- * <p>Returns {@code 0} when the user is locked, or when the user owning the shortcut
- * is locked or not running.
- *
* @param density Optional density for the icon, or 0 to use the default density. Use
* @return A badged icon for the shortcut.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
* @see ShortcutManager
* @see #getShortcutIconDrawable(ShortcutInfo, int)
@@ -704,15 +700,13 @@
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
- * <p>Throws {@link android.content.ActivityNotFoundException}
- * when the user is locked, or when the {@code user} user
- * is locked or not running.
- *
* @param packageName The target shortcut package name.
* @param shortcutId The target shortcut ID.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
* @param user The UserHandle of the profile.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
* @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
* the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
@@ -730,13 +724,11 @@
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
- * <p>Throws {@link android.content.ActivityNotFoundException}
- * when the user is locked, or when the user owning the shortcut
- * is locked or not running.
- *
* @param shortcut The target shortcut.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
+ * @throws IllegalStateException when the user is locked, or when the {@code user} user
+ * is locked or not running.
*
* @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
* the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7ba067b..e8f0845 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5379,14 +5379,6 @@
int newState, int flags);
/**
- * Per-user version of {@link #setComponentEnabledSetting(ComponentName, int, int)}
- *
- * @hide
- */
- public abstract void setComponentEnabledSettingAsUser(ComponentName componentName,
- int newState, int flags, int userId);
-
- /**
* Return the enabled setting for a package component (activity,
* receiver, service, provider). This returns the last value set by
* {@link #setComponentEnabledSetting(ComponentName, int, int)}; in most
@@ -5425,14 +5417,6 @@
int newState, int flags);
/**
- * Per-user version of {@link #setApplicationEnabledSetting(String, int, int)}
- *
- * @hide
- */
- public abstract void setApplicationEnabledSettingAsUser(String packageName,
- int newState, int flags, int userId);
-
- /**
* Return the enabled setting for an application. This returns
* the last value set by
* {@link #setApplicationEnabledSetting(String, int, int)}; in most
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 370f7f9..95dd148 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -30,6 +30,7 @@
import android.telephony.SignalStrength;
import android.text.format.DateFormat;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.LongSparseArray;
import android.util.MutableBoolean;
import android.util.Pair;
@@ -48,6 +49,7 @@
* @hide
*/
public abstract class BatteryStats implements Parcelable {
+ private static final String TAG = "BatteryStats";
private static final boolean LOCAL_LOGV = false;
@@ -176,8 +178,11 @@
/**
* Current version of checkin data format.
+ *
+ * New in version 19:
+ * - Wakelock data (wl) gets current and max times.
*/
- static final String CHECKIN_VERSION = "18";
+ static final String CHECKIN_VERSION = "19";
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -353,6 +358,32 @@
public abstract long getTimeSinceMarkLocked(long elapsedRealtimeUs);
/**
+ * Returns the max duration if it is being tracked.
+ * Not all Timer subclasses track the max duration and the current duration.
+
+ */
+ public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
+ return -1;
+ }
+
+ /**
+ * Returns the current time the timer has been active, if it is being tracked.
+ * Not all Timer subclasses track the max duration and the current duration.
+ */
+ public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
+ return -1;
+ }
+
+ /**
+ * Returns whether the timer is currently running. Some types of timers
+ * (e.g. BatchTimers) don't know whether the event is currently active,
+ * and report false.
+ */
+ public boolean isRunningLocked() {
+ return false;
+ }
+
+ /**
* Temporary for debugging.
*/
public abstract void logState(Printer pw, String prefix);
@@ -2561,6 +2592,22 @@
sb.append('(');
sb.append(count);
sb.append(" times)");
+ final long maxDurationMs = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000);
+ if (maxDurationMs >= 0) {
+ sb.append(" max=");
+ sb.append(maxDurationMs);
+ }
+ if (timer.isRunningLocked()) {
+ final long currentMs = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
+ if (currentMs >= 0) {
+ sb.append(" (running for ");
+ sb.append(currentMs);
+ sb.append("ms)");
+ } else {
+ sb.append(" (running)");
+ }
+ }
+
return ", ";
}
}
@@ -2568,6 +2615,7 @@
}
/**
+ * Prints details about a timer, if its total time was greater than 0.
*
* @param pw a PrintWriter object to print to.
* @param sb a StringBuilder object.
@@ -2576,24 +2624,40 @@
* @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
* @param prefix a String to be prepended to each line of output.
* @param type the name of the timer.
+ * @return true if anything was printed.
*/
private static final boolean printTimer(PrintWriter pw, StringBuilder sb, Timer timer,
- long rawRealtime, int which, String prefix, String type) {
+ long rawRealtimeUs, int which, String prefix, String type) {
if (timer != null) {
// Convert from microseconds to milliseconds with rounding
- final long totalTime = (timer.getTotalTimeLocked(
- rawRealtime, which) + 500) / 1000;
+ final long totalTimeMs = (timer.getTotalTimeLocked(
+ rawRealtimeUs, which) + 500) / 1000;
final int count = timer.getCountLocked(which);
- if (totalTime != 0) {
+ if (totalTimeMs != 0) {
sb.setLength(0);
sb.append(prefix);
sb.append(" ");
sb.append(type);
sb.append(": ");
- formatTimeMs(sb, totalTime);
+ formatTimeMs(sb, totalTimeMs);
sb.append("realtime (");
sb.append(count);
sb.append(" times)");
+ final long maxDurationMs = timer.getMaxDurationMsLocked(rawRealtimeUs/1000);
+ if (maxDurationMs >= 0) {
+ sb.append(" max=");
+ sb.append(maxDurationMs);
+ }
+ if (timer.isRunningLocked()) {
+ final long currentMs = timer.getCurrentDurationMsLocked(rawRealtimeUs/1000);
+ if (currentMs >= 0) {
+ sb.append(" (running for ");
+ sb.append(currentMs);
+ sb.append("ms)");
+ } else {
+ sb.append(" (running)");
+ }
+ }
pw.println(sb.toString());
return true;
}
@@ -2616,15 +2680,23 @@
long elapsedRealtimeUs, String name, int which, String linePrefix) {
long totalTimeMicros = 0;
int count = 0;
+ long max = -1;
+ long current = -1;
if (timer != null) {
totalTimeMicros = timer.getTotalTimeLocked(elapsedRealtimeUs, which);
count = timer.getCountLocked(which);
+ current = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
+ max = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000);
}
sb.append(linePrefix);
sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding
sb.append(',');
sb.append(name != null ? name + "," : "");
sb.append(count);
+ sb.append(',');
+ sb.append(current);
+ sb.append(',');
+ sb.append(max);
return ",";
}
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 5c4450a..2b938d0 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -198,7 +198,7 @@
int SWAP_BUFFERS = 12;
int FRAME_COMPLETED = 13;
- int FRAME_STATS_COUNT = 14; // must always be last
+ int FRAME_STATS_COUNT = 16; // must always be last
}
/*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3186073..cdd7098 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12363,6 +12363,10 @@
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
+ // Report visibility changes, which can affect children, to accessibility
+ if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
mTransformationInfo.mAlpha = alpha;
if (onSetAlpha((int) (alpha * 255))) {
mPrivateFlags |= PFLAG_ALPHA_SET;
@@ -12373,8 +12377,6 @@
mPrivateFlags &= ~PFLAG_ALPHA_SET;
invalidateViewProperty(true, false);
mRenderNode.setAlpha(getFinalAlpha());
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
}
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 28d187c..583f037 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -113,6 +113,7 @@
* Creates a new {@link TabSpec} associated with this tab host.
*
* @param tag tag for the tab specification, must be non-null
+ * @throws IllegalArgumentException If the passed tag is null
*/
@NonNull
public TabSpec newTabSpec(@NonNull String tag) {
@@ -203,6 +204,8 @@
/**
* Add a tab.
* @param tabSpec Specifies how to create the indicator and content.
+ * @throws IllegalArgumentException If the passed tab spec has null indicator strategy and / or
+ * null content strategy.
*/
public void addTab(TabSpec tabSpec) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 91dcc16..fef4a53 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -111,7 +111,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 148 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 149 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -1576,6 +1576,186 @@
}
}
+
+ /**
+ * A StopwatchTimer that also tracks the total and max individual
+ * time spent active according to the given timebase. Whereas
+ * StopwatchTimer apportions the time amongst all in the pool,
+ * the total and max durations are not apportioned.
+ */
+ public static class DurationTimer extends StopwatchTimer {
+ /**
+ * The time (in ms) that the timer was last acquired or the time base
+ * last (re-)started. Increasing the nesting depth does not reset this time.
+ *
+ * -1 if the timer is currently not running or the time base is not running.
+ *
+ * If written to a parcel, the start time is reset, as is mNesting in the base class
+ * StopwatchTimer.
+ */
+ long mStartTimeMs = -1;
+
+ /**
+ * The longest time period (in ms) that the timer has been active.
+ */
+ long mMaxDurationMs;
+
+ /**
+ * The total time (in ms) that that the timer has been active since reset().
+ */
+ long mCurrentDurationMs;
+
+ public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ TimeBase timeBase, Parcel in) {
+ super(clocks, uid, type, timerPool, timeBase, in);
+ mMaxDurationMs = in.readLong();
+ }
+
+ public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ TimeBase timeBase) {
+ super(clocks, uid, type, timerPool, timeBase);
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
+ super.writeToParcel(out, elapsedRealtimeUs);
+ out.writeLong(mMaxDurationMs);
+ }
+
+ /**
+ * Write the summary to the parcel.
+ *
+ * Since the time base is probably meaningless after we come back, reading
+ * from this will have the effect of stopping the timer. So here all we write
+ * is the max duration.
+ */
+ @Override
+ public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) {
+ super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs);
+ out.writeLong(mMaxDurationMs);
+ }
+
+ /**
+ * Read the summary parcel.
+ *
+ * Has the side effect of stopping the timer.
+ */
+ @Override
+ public void readSummaryFromParcelLocked(Parcel in) {
+ super.readSummaryFromParcelLocked(in);
+ mMaxDurationMs = in.readLong();
+ mStartTimeMs = -1;
+ mCurrentDurationMs = 0;
+ }
+
+ /**
+ * The TimeBase time started (again).
+ *
+ * If the timer is also running, store the start time.
+ */
+ public void onTimeStarted(long elapsedRealtimeUs, long baseUptime, long baseRealtime) {
+ super.onTimeStarted(elapsedRealtimeUs, baseUptime, baseRealtime);
+ if (mNesting > 0) {
+ mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000;
+ }
+ }
+
+ /**
+ * The TimeBase stopped running.
+ *
+ * If the timer is running, add the duration into mCurrentDurationMs.
+ */
+ @Override
+ public void onTimeStopped(long elapsedRealtimeUs, long baseUptime, long baseRealtime) {
+ super.onTimeStopped(elapsedRealtimeUs, baseUptime, baseRealtime);
+ if (mNesting > 0) {
+ mCurrentDurationMs += (elapsedRealtimeUs / 1000) - mStartTimeMs;
+ }
+ mStartTimeMs = -1;
+ }
+
+ @Override
+ public void logState(Printer pw, String prefix) {
+ super.logState(pw, prefix);
+ }
+
+ @Override
+ public void startRunningLocked(long elapsedRealtimeMs) {
+ super.startRunningLocked(elapsedRealtimeMs);
+ if (mNesting == 1 && mTimeBase.isRunning()) {
+ // Just started
+ mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000;
+ }
+ }
+
+ /**
+ * Decrements the mNesting ref-count on this timer.
+ *
+ * If it actually stopped (mNesting went to 0), then possibly update
+ * mMaxDuration if the current duration was the longest ever.
+ */
+ @Override
+ public void stopRunningLocked(long elapsedRealtimeMs) {
+ super.stopRunningLocked(elapsedRealtimeMs);
+ if (mNesting == 0) {
+ final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs);
+ if (durationMs > mMaxDurationMs) {
+ mMaxDurationMs = durationMs;
+ }
+ mStartTimeMs = -1;
+ mCurrentDurationMs = 0;
+ }
+ }
+
+ @Override
+ public boolean reset(boolean detachIfReset) {
+ boolean result = super.reset(detachIfReset);
+ mMaxDurationMs = 0;
+ mCurrentDurationMs = 0;
+ if (mNesting > 0) {
+ mStartTimeMs = mTimeBase.getRealtime(mClocks.elapsedRealtime()*1000) / 1000;
+ } else {
+ mStartTimeMs = -1;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the max duration that this timer has ever seen.
+ *
+ * Note that this time is NOT split between the timers in the timer group that
+ * this timer is attached to. It is the TOTAL time.
+ */
+ @Override
+ public long getMaxDurationMsLocked(long elapsedRealtimeMs) {
+ if (mNesting > 0) {
+ final long durationMs = getCurrentDurationMsLocked(elapsedRealtimeMs);
+ if (durationMs > mMaxDurationMs) {
+ return durationMs;
+ }
+ }
+ return mMaxDurationMs;
+ }
+
+ /**
+ * Returns the time since the timer was started.
+ *
+ * Note that this time is NOT split between the timers in the timer group that
+ * this timer is attached to. It is the TOTAL time.
+ */
+ @Override
+ public long getCurrentDurationMsLocked(long elapsedRealtimeMs) {
+ long durationMs = mCurrentDurationMs;
+ if (mNesting > 0) {
+ if (mTimeBase.isRunning()) {
+ durationMs += (mTimeBase.getRealtime(elapsedRealtimeMs*1000)/1000)
+ - mStartTimeMs;
+ }
+ }
+ return durationMs;
+ }
+ }
+
/**
* State for keeping track of timing information.
*/
@@ -6554,7 +6734,7 @@
/**
* How long (in ms) this uid has been keeping the device partially awake.
*/
- StopwatchTimer mTimerPartial;
+ DurationTimer mTimerPartial;
/**
* How long (in ms) this uid has been keeping the device fully awake.
@@ -6583,8 +6763,8 @@
* @param in the Parcel to be read from.
* return a new Timer, or null.
*/
- private StopwatchTimer readTimerFromParcel(int type, ArrayList<StopwatchTimer> pool,
- TimeBase timeBase, Parcel in) {
+ private StopwatchTimer readStopwatchTimerFromParcel(int type,
+ ArrayList<StopwatchTimer> pool, TimeBase timeBase, Parcel in) {
if (in.readInt() == 0) {
return null;
}
@@ -6592,6 +6772,22 @@
return new StopwatchTimer(mBsi.mClocks, mUid, type, pool, timeBase, in);
}
+ /**
+ * Reads a possibly null Timer from a Parcel. The timer is associated with the
+ * proper timer pool from the given BatteryStatsImpl object.
+ *
+ * @param in the Parcel to be read from.
+ * return a new Timer, or null.
+ */
+ private DurationTimer readDurationTimerFromParcel(int type,
+ ArrayList<StopwatchTimer> pool, TimeBase timeBase, Parcel in) {
+ if (in.readInt() == 0) {
+ return null;
+ }
+
+ return new DurationTimer(mBsi.mClocks, mUid, type, pool, timeBase, in);
+ }
+
boolean reset() {
boolean wlactive = false;
if (mTimerFull != null) {
@@ -6628,11 +6824,14 @@
}
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
- mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL,
+ mTimerPartial = readDurationTimerFromParcel(WAKE_TYPE_PARTIAL,
mBsi.mPartialTimers, screenOffTimeBase, in);
- mTimerFull = readTimerFromParcel(WAKE_TYPE_FULL, mBsi.mFullTimers, timeBase, in);
- mTimerWindow = readTimerFromParcel(WAKE_TYPE_WINDOW, mBsi.mWindowTimers, timeBase, in);
- mTimerDraw = readTimerFromParcel(WAKE_TYPE_DRAW, mBsi.mDrawTimers, timeBase, in);
+ mTimerFull = readStopwatchTimerFromParcel(WAKE_TYPE_FULL,
+ mBsi.mFullTimers, timeBase, in);
+ mTimerWindow = readStopwatchTimerFromParcel(WAKE_TYPE_WINDOW,
+ mBsi.mWindowTimers, timeBase, in);
+ mTimerDraw = readStopwatchTimerFromParcel(WAKE_TYPE_DRAW,
+ mBsi.mDrawTimers, timeBase, in);
}
void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) {
@@ -6654,40 +6853,43 @@
}
public StopwatchTimer getStopwatchTimer(int type) {
- StopwatchTimer t;
switch (type) {
- case WAKE_TYPE_PARTIAL:
- t = mTimerPartial;
+ case WAKE_TYPE_PARTIAL: {
+ DurationTimer t = mTimerPartial;
if (t == null) {
- t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_PARTIAL,
+ t = new DurationTimer(mBsi.mClocks, mUid, WAKE_TYPE_PARTIAL,
mBsi.mPartialTimers, mBsi.mOnBatteryScreenOffTimeBase);
mTimerPartial = t;
}
return t;
- case WAKE_TYPE_FULL:
- t = mTimerFull;
+ }
+ case WAKE_TYPE_FULL: {
+ StopwatchTimer t = mTimerFull;
if (t == null) {
t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_FULL,
mBsi.mFullTimers, mBsi.mOnBatteryTimeBase);
mTimerFull = t;
}
return t;
- case WAKE_TYPE_WINDOW:
- t = mTimerWindow;
+ }
+ case WAKE_TYPE_WINDOW: {
+ StopwatchTimer t = mTimerWindow;
if (t == null) {
t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_WINDOW,
mBsi.mWindowTimers, mBsi.mOnBatteryTimeBase);
mTimerWindow = t;
}
return t;
- case WAKE_TYPE_DRAW:
- t = mTimerDraw;
+ }
+ case WAKE_TYPE_DRAW: {
+ StopwatchTimer t = mTimerDraw;
if (t == null) {
t = new StopwatchTimer(mBsi.mClocks, mUid, WAKE_TYPE_DRAW,
mBsi.mDrawTimers, mBsi.mOnBatteryTimeBase);
mTimerDraw = t;
}
return t;
+ }
default:
throw new IllegalArgumentException("type=" + type);
}
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index bb09d00..7213414 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -280,7 +280,7 @@
if (size) {
jchar* data = static_cast<jchar*>(env->GetPrimitiveArrayCritical(dataObj, NULL));
utf8_to_utf16_no_null_terminator(reinterpret_cast<const uint8_t*>(str), len,
- reinterpret_cast<char16_t*>(data));
+ reinterpret_cast<char16_t*>(data), (size_t) size);
env->ReleasePrimitiveArrayCritical(dataObj, data, 0);
}
env->SetIntField(bufferObj, gCharArrayBufferClassInfo.sizeCopied, size);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index a5c9d54..87a8ef8 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -242,6 +242,16 @@
mPausedVDAnimators.clear();
}
+ // Move all the animators to the paused list, and send a delayed message to notify the finished
+ // listener.
+ void pauseAnimators() {
+ mPausedVDAnimators.insert(mRunningVDAnimators.begin(), mRunningVDAnimators.end());
+ for (auto& anim : mRunningVDAnimators) {
+ detachVectorDrawableAnimator(anim.get());
+ }
+ mRunningVDAnimators.clear();
+ }
+
void doAttachAnimatingNodes(AnimationContext* context) {
for (size_t i = 0; i < mPendingAnimatingRenderNodes.size(); i++) {
RenderNode* node = mPendingAnimatingRenderNodes[i].get();
@@ -415,8 +425,8 @@
postOnFinishedEvents();
}
- virtual void detachAnimators() override {
- mRootNode->detachAnimators();
+ virtual void pauseAnimators() override {
+ mRootNode->pauseAnimators();
}
virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) {
@@ -426,7 +436,7 @@
virtual void destroy() {
AnimationContext::destroy();
- detachAnimators();
+ mRootNode->detachAnimators();
postOnFinishedEvents();
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 61b4c8d..3296371 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2119,7 +2119,7 @@
</string-array>
<!-- This string array can be overriden to add an additional DRM support for WebView EME. -->
- <!-- Array of "[keySystemName],[UuidOfMediaDrm]" @hide @SystemApi -->
+ <!-- Array of "[keySystemName],[UuidOfMediaDrm]" -->
<string-array name="config_keySystemUuidMapping" translatable="false">
<!-- Example:
<item>"x-com.microsoft.playready,9A04F079-9840-4286-AB92-E65BE0885F95"</item>
@@ -2532,4 +2532,6 @@
is installed. -->
<bool name="config_permissionReviewRequired">false</bool>
+ <string name="config_networkOverLimitComponent" translatable="false">com.android.systemui/com.android.systemui.net.NetworkOverLimitActivity</string>
+ <string name="config_dataUsageSummaryComponent" translatable="false">com.android.settings/com.android.settings.Settings$DataUsageSummaryActivity</string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a37db7e..7bce379 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2547,6 +2547,7 @@
<!-- @hide This really shouldn't be public; clients using it should use @* to ref it. -->
<public type="style" name="Theme.Leanback.FormWizard" id="0x010302d0" />
+ <!-- @hide @SystemApi This shouldn't be public. -->
<public type="array" name="config_keySystemUuidMapping" id="0x01070005" />
<!-- An interpolator which accelerates fast but decelerates slowly. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 54cb9cef2..b7087ec 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2648,6 +2648,10 @@
<!-- Colon separated list of package names that should be granted DND access -->
<java-symbol type="string" name="config_defaultDndAccessPackages" />
+ <!-- For NetworkPolicyManagerService -->
+ <java-symbol type="string" name="config_networkOverLimitComponent" />
+ <java-symbol type="string" name="config_dataUsageSummaryComponent" />
+
<java-symbol type="string" name="lockscreen_storage_locked" />
<!-- Used for MimeIconUtils. -->
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
new file mode 100644
index 0000000..a15e367
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 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.internal.os;
+
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mockito;
+
+/**
+ * Test BatteryStatsImpl.DurationTimer.
+ *
+ * In these tests, unless otherwise commented, the time increments by
+ * 2x + 100, to make the subtraction unlikely to alias to another time.
+ */
+public class BatteryStatsDurationTimerTest extends TestCase {
+
+ @SmallTest
+ public void testStartStop() throws Exception {
+ final MockClocks clocks = new MockClocks();
+
+ final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
+ timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
+
+ final BatteryStatsImpl.DurationTimer timer = new BatteryStatsImpl.DurationTimer(clocks,
+ null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase);
+
+ // TimeBase running, timer not running: current and max are 0
+ timeBase.setRunning(true, /* uptimeUs */ 0, /* realtimeUs */ 100*1000);
+ assertFalse(timer.isRunningLocked());
+ assertEquals(0, timer.getCurrentDurationMsLocked(300));
+ assertEquals(0, timer.getMaxDurationMsLocked(301));
+
+ // Start timer: current and max advance
+ timer.startRunningLocked(700);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(800, timer.getCurrentDurationMsLocked(1500));
+ assertEquals(801, timer.getMaxDurationMsLocked(1501));
+
+ // Stop timer: current resets to 0, max remains
+ timer.stopRunningLocked(3100);
+ assertFalse(timer.isRunningLocked());
+ assertEquals(0, timer.getCurrentDurationMsLocked(6300));
+ assertEquals(2400, timer.getMaxDurationMsLocked(6301));
+
+ // Start time again, but check with a short time, and make sure max doesn't
+ // increment.
+ timer.startRunningLocked(12700);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(100, timer.getCurrentDurationMsLocked(12800));
+ assertEquals(2400, timer.getMaxDurationMsLocked(12801));
+
+ // And stop it again, but with a short time, and make sure it doesn't increment.
+ timer.stopRunningLocked(12900);
+ assertFalse(timer.isRunningLocked());
+ assertEquals(0, timer.getCurrentDurationMsLocked(13000));
+ assertEquals(2400, timer.getMaxDurationMsLocked(13001));
+
+ // Now start and check that the time doesn't increase if the two times are the same.
+ timer.startRunningLocked(27000);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(0, timer.getCurrentDurationMsLocked(27000));
+ assertEquals(2400, timer.getMaxDurationMsLocked(27000));
+
+ // Stop the TimeBase. The values should be frozen.
+ timeBase.setRunning(false, /* uptimeUs */ 10, /* realtimeUs */ 55000*1000);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(28100, timer.getCurrentDurationMsLocked(110100)); // Why 28100 and not 28000?
+ assertEquals(28100, timer.getMaxDurationMsLocked(110101));
+
+ // Start the TimeBase. The values should be the old value plus the delta
+ // between when the timer restarted and the current time
+ timeBase.setRunning(true, /* uptimeUs */ 10, /* realtimeUs */ 220100*1000);
+ assertTrue(timer.isRunningLocked());
+ assertEquals(28300, timer.getCurrentDurationMsLocked(220300)); // extra 100 from above??
+ assertEquals(28301, timer.getMaxDurationMsLocked(220301));
+ }
+
+ @SmallTest
+ public void testReset() throws Exception {
+ }
+
+ @SmallTest
+ public void testParceling() throws Exception {
+ final MockClocks clocks = new MockClocks();
+
+ final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
+ timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
+
+ final BatteryStatsImpl.DurationTimer timer = new BatteryStatsImpl.DurationTimer(clocks,
+ null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase);
+
+ // Start running on battery.
+ clocks.realtime = 100;
+ clocks.uptime = 10;
+ timeBase.setRunning(true, clocks.uptimeMillis()*1000, clocks.elapsedRealtime()*1000);
+
+ timer.startRunningLocked(300);
+
+ // Check that it did start running
+ assertEquals(400, timer.getMaxDurationMsLocked(700));
+ assertEquals(401, timer.getCurrentDurationMsLocked(701));
+
+ // Write summary
+ final Parcel summaryParcel = Parcel.obtain();
+ timer.writeSummaryFromParcelLocked(summaryParcel, 1500*1000);
+ summaryParcel.setDataPosition(0);
+
+ // Read summary
+ final BatteryStatsImpl.DurationTimer summary = new BatteryStatsImpl.DurationTimer(clocks,
+ null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase);
+ summary.startRunningLocked(3100);
+ summary.readSummaryFromParcelLocked(summaryParcel);
+ // The new one shouldn't be running, and therefore 0 for current time
+ assertFalse(summary.isRunningLocked());
+ assertEquals(0, summary.getCurrentDurationMsLocked(6300));
+ // The new one should have the max duration that we had when we wrote it
+ assertEquals(1200, summary.getMaxDurationMsLocked(6301));
+
+ // Write full
+ final Parcel fullParcel = Parcel.obtain();
+ timer.writeToParcel(fullParcel, 1500*1000);
+ fullParcel.setDataPosition(0);
+
+ // Read full - Should be the same as the summary as far as DurationTimer is concerned.
+ final BatteryStatsImpl.DurationTimer full = new BatteryStatsImpl.DurationTimer(clocks,
+ null, BatteryStats.WAKE_TYPE_PARTIAL, null, timeBase, fullParcel);
+ // The new one shouldn't be running, and therefore 0 for current time
+ assertFalse(full.isRunningLocked());
+ assertEquals(0, full.getCurrentDurationMsLocked(6300));
+ // The new one should have the max duration that we had when we wrote it
+ assertEquals(1200, full.getMaxDurationMsLocked(6301));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 78bcbbc..9518219 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -5,6 +5,7 @@
@RunWith(Suite.class)
@Suite.SuiteClasses({
+ BatteryStatsDurationTimerTest.class,
BatteryStatsSamplingTimerTest.class,
BatteryStatsServTest.class,
BatteryStatsTimeBaseTest.class,
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index bf2648a..b423f6c 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -776,7 +776,7 @@
if (kDebugStringPoolNoisy) {
ALOGI("Caching UTF8 string: %s", u8str);
}
- utf8_to_utf16(u8str, u8len, u16str);
+ utf8_to_utf16(u8str, u8len, u16str, *u16len + 1);
mCache[idx] = u16str;
return u16str;
} else {
@@ -883,7 +883,8 @@
// the ordering, we need to convert strings in the pool to UTF-16.
// But we don't want to hit the cache, so instead we will have a
// local temporary allocation for the conversions.
- char16_t* convBuffer = (char16_t*)malloc(strLen+4);
+ size_t convBufferLen = strLen + 4;
+ char16_t* convBuffer = (char16_t*)calloc(convBufferLen, sizeof(char16_t));
ssize_t l = 0;
ssize_t h = mHeader->stringCount-1;
@@ -893,8 +894,7 @@
const uint8_t* s = (const uint8_t*)string8At(mid, &len);
int c;
if (s != NULL) {
- char16_t* end = utf8_to_utf16_n(s, len, convBuffer, strLen+3);
- *end = 0;
+ char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen);
c = strzcmp16(convBuffer, end-convBuffer, str, strLen);
} else {
c = -1;
diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h
index 97b53b0..71ee860 100644
--- a/libs/hwui/AnimationContext.h
+++ b/libs/hwui/AnimationContext.h
@@ -100,7 +100,7 @@
ANDROID_API virtual void destroy();
- ANDROID_API virtual void detachAnimators() {}
+ ANDROID_API virtual void pauseAnimators() {}
private:
friend class AnimationHandle;
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 09b3945..826f0bb 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -43,6 +43,9 @@
== static_cast<int>(FrameInfoIndex::NumIndexes),
"size mismatch: FrameInfoNames doesn't match the enum!");
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 16,
+ "Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
+
void FrameInfo::importUiThreadInfo(int64_t* info) {
memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
}
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 993b158..bac9d12d 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -52,6 +52,7 @@
QueueBufferDuration,
// Must be the last value!
+ // Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT
NumIndexes
};
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5f8c798..073643d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -323,7 +323,7 @@
void CanvasContext::stopDrawing() {
mRenderThread.removeFrameCallback(this);
- mAnimationContext->detachAnimators();
+ mAnimationContext->pauseAnimators();
}
void CanvasContext::notifyFramePending() {
diff --git a/libs/hwui/utils/NinePatchImpl.cpp b/libs/hwui/utils/NinePatchImpl.cpp
index 2b59ca9..d37126c 100644
--- a/libs/hwui/utils/NinePatchImpl.cpp
+++ b/libs/hwui/utils/NinePatchImpl.cpp
@@ -20,7 +20,6 @@
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
-#include "SkNinePatch.h"
#include "SkPaint.h"
#include "SkUnPreMultiply.h"
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index a56b1b8..4f2d9fbc 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -827,7 +827,8 @@
}
/**
- * Returns the media id for this item.
+ * Returns the media id in the {@link MediaDescription} for this item.
+ * @see android.media.MediaMetadata#METADATA_KEY_MEDIA_ID
*/
public @NonNull String getMediaId() {
return mDescription.getMediaId();
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 3affee5c0..a215493 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -45,6 +45,8 @@
void setQueueTitle(CharSequence title);
void setExtras(in Bundle extras);
void setRatingType(int type);
+ void setRepeatMode(int repeatMode);
+ void setShuffleMode(boolean shuffleMode);
// These commands relate to volume handling
void setPlaybackToLocal(in AudioAttributes attributes);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 893bd3c..b3c6d59 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -46,6 +46,8 @@
void onRewind();
void onSeekTo(long pos);
void onRate(in Rating rating);
+ void onRepeatMode(int repeatMode);
+ void onShuffleMode(boolean shuffleMode);
void onCustomAction(String action, in Bundle args);
// These callbacks are for volume handling
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 249bcdc..b73f167 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -48,6 +48,14 @@
ParcelableVolumeInfo getVolumeAttributes();
void adjustVolume(int direction, int flags, String packageName);
void setVolumeTo(int value, int flags, String packageName);
+ MediaMetadata getMetadata();
+ PlaybackState getPlaybackState();
+ ParceledListSlice getQueue();
+ CharSequence getQueueTitle();
+ Bundle getExtras();
+ int getRatingType();
+ int getRepeatMode();
+ boolean getShuffleMode();
// These commands are for the TransportControls
void prepare();
@@ -67,11 +75,7 @@
void rewind();
void seekTo(long pos);
void rate(in Rating rating);
+ void repeatMode(int repeatMode);
+ void shuffleMode(boolean shuffleMode);
void sendCustomAction(String action, in Bundle args);
- MediaMetadata getMetadata();
- PlaybackState getPlaybackState();
- ParceledListSlice getQueue();
- CharSequence getQueueTitle();
- Bundle getExtras();
- int getRatingType();
}
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index cf31767..9517c08 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -36,4 +36,6 @@
void onQueueTitleChanged(CharSequence title);
void onExtrasChanged(in Bundle extras);
void onVolumeInfoChanged(in ParcelableVolumeInfo info);
+ void onRepeatModeChanged(int repeatMode);
+ void onShuffleModeChanged(boolean shuffleMode);
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 622900f..ddff1b7 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -63,7 +63,9 @@
private static final int MSG_UPDATE_QUEUE = 5;
private static final int MSG_UPDATE_QUEUE_TITLE = 6;
private static final int MSG_UPDATE_EXTRAS = 7;
- private static final int MSG_DESTROYED = 8;
+ private static final int MSG_UPDATE_REPEAT_MODE = 8;
+ private static final int MSG_UPDATE_SHUFFLE_MODE = 9;
+ private static final int MSG_DESTROYED = 10;
private final ISessionController mSessionBinder;
@@ -234,6 +236,35 @@
}
/**
+ * Get the repeat mode for this session.
+ *
+ * @return The latest repeat mode set to the session, or
+ * {@link PlaybackState#REPEAT_MODE_NONE} if not set.
+ */
+ public int getRepeatMode() {
+ try {
+ return mSessionBinder.getRepeatMode();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getRepeatMode.", e);
+ return PlaybackState.REPEAT_MODE_NONE;
+ }
+ }
+
+ /**
+ * Get the shuffle mode for this session.
+ *
+ * @return The latest shuffle mode set to the session, or false if not set.
+ */
+ public boolean getShuffleMode() {
+ try {
+ return mSessionBinder.getShuffleMode();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getShuffleMode.", e);
+ return false;
+ }
+ }
+
+ /**
* Get the flags for this session. Flags are defined in {@link MediaSession}.
*
* @return The current set of flags for the session.
@@ -579,6 +610,26 @@
*/
public void onAudioInfoChanged(PlaybackInfo info) {
}
+
+ /**
+ * Override to handle changes to the repeat mode.
+ *
+ * @param repeatMode The repeat mode. It should be one of followings:
+ * {@link PlaybackState#REPEAT_MODE_NONE},
+ * {@link PlaybackState#REPEAT_MODE_ONE},
+ * {@link PlaybackState#REPEAT_MODE_ALL}
+ */
+ public void onRepeatModeChanged(@PlaybackState.RepeatMode int repeatMode) {
+ }
+
+ /**
+ * Override to handle changes to the shuffle mode.
+ *
+ * @param shuffleMode The shuffle mode. {@code true} if _the_ shuffle mode is on,
+ * {@code false} otherwise.
+ */
+ public void onShuffleModeChanged(boolean shuffleMode) {
+ }
}
/**
@@ -862,6 +913,35 @@
}
/**
+ * Set the repeat mode for this session.
+ *
+ * @param repeatMode The repeat mode. Must be one of the followings:
+ * {@link PlaybackState#REPEAT_MODE_NONE},
+ * {@link PlaybackState#REPEAT_MODE_ONE},
+ * {@link PlaybackState#REPEAT_MODE_ALL}
+ */
+ public void setRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
+ try {
+ mSessionBinder.repeatMode(repeatMode);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling setRepeatMode.", e);
+ }
+ }
+
+ /**
+ * Set the shuffle mode for this session.
+ *
+ * @param shuffleMode {@code true} if the shuffle mode is on, {@code false} otherwise.
+ */
+ public void setShuffleMode(boolean shuffleMode) {
+ try {
+ mSessionBinder.shuffleMode(shuffleMode);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling shuffleQueue.", e);
+ }
+ }
+
+ /**
* Send a custom action back for the {@link MediaSession} to perform.
*
* @param customAction The action to perform.
@@ -1062,6 +1142,21 @@
}
}
+ @Override
+ public void onRepeatModeChanged(int repeatMode) {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_UPDATE_REPEAT_MODE, repeatMode, null);
+ }
+ }
+
+ @Override
+ public void onShuffleModeChanged(boolean shuffleMode) {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_UPDATE_SHUFFLE_MODE, shuffleMode, null);
+ }
+ }
}
private final static class MessageHandler extends Handler {
@@ -1100,6 +1195,12 @@
case MSG_UPDATE_VOLUME:
mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
break;
+ case MSG_UPDATE_REPEAT_MODE:
+ mCallback.onRepeatModeChanged((int) msg.obj);
+ break;
+ case MSG_UPDATE_SHUFFLE_MODE:
+ mCallback.onShuffleModeChanged((boolean) msg.obj);
+ break;
case MSG_DESTROYED:
mCallback.onSessionDestroyed();
break;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 0959961..cab5d93 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -480,6 +480,41 @@
}
/**
+ * Set the repeat mode for this session.
+ * <p>
+ * Note that if this method is not called before, {@link MediaController#getRepeatMode}
+ * will return {@link PlaybackState#REPEAT_MODE_NONE}.
+ *
+ * @param repeatMode The repeat mode. Must be one of the followings:
+ * {@link PlaybackState#REPEAT_MODE_NONE},
+ * {@link PlaybackState#REPEAT_MODE_ONE},
+ * {@link PlaybackState#REPEAT_MODE_ALL}
+ */
+ public void setRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
+ try {
+ mBinder.setRepeatMode(repeatMode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setRepeatMode.", e);
+ }
+ }
+
+ /**
+ * Set the shuffle mode for this session.
+ * <p>
+ * Note that if this method is not called before, {@link MediaController#getShuffleMode}
+ * will return {@code false}.
+ *
+ * @param shuffleMode {@code true} if the shuffle mode is on, {@code false} otherwise.
+ */
+ public void setShuffleMode(boolean shuffleMode) {
+ try {
+ mBinder.setShuffleMode(shuffleMode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setShuffleMode.", e);
+ }
+ }
+
+ /**
* Set some extras that can be associated with the {@link MediaSession}. No assumptions should
* be made as to how a {@link MediaController} will handle these extras.
* Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
@@ -598,6 +633,14 @@
postToCallback(CallbackMessageHandler.MSG_RATE, rating);
}
+ private void dispatchRepeatMode(int repeatMode) {
+ postToCallback(CallbackMessageHandler.MSG_REPEAT_MODE, repeatMode);
+ }
+
+ private void dispatchShuffleMode(boolean shuffleMode) {
+ postToCallback(CallbackMessageHandler.MSG_SHUFFLE_MODE, shuffleMode);
+ }
+
private void dispatchCustomAction(String action, Bundle args) {
postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
}
@@ -964,6 +1007,33 @@
}
/**
+ * Override to handle the setting of the repeat mode.
+ * <p>
+ * You should call {@link #setRepeatMode} before end of this method in order to notify
+ * the change to the {@link MediaController}, or {@link MediaController#getRepeatMode}
+ * could return an invalid value.
+ *
+ * @param repeatMode The repeat mode which is one of followings:
+ * {@link PlaybackState#REPEAT_MODE_NONE},
+ * {@link PlaybackState#REPEAT_MODE_ONE},
+ * {@link PlaybackState#REPEAT_MODE_ALL}
+ */
+ public void onSetRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
+ }
+
+ /**
+ * Override to handle the setting of the shuffle mode.
+ * <p>
+ * You should call {@link #setShuffleMode} before end of this method in order to notify
+ * the change to the {@link MediaController}, or {@link MediaController#getShuffleMode}
+ * could return an invalid value.
+ *
+ * @param shuffleMode true if the shuffle mode is on, false otherwise.
+ */
+ public void onSetShuffleMode(boolean shuffleMode) {
+ }
+
+ /**
* Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
* performed.
*
@@ -1145,6 +1215,22 @@
}
@Override
+ public void onRepeatMode(int repeatMode) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchRepeatMode(repeatMode);
+ }
+ }
+
+ @Override
+ public void onShuffleMode(boolean shuffleMode) {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.dispatchShuffleMode(shuffleMode);
+ }
+ }
+
+ @Override
public void onCustomAction(String action, Bundle args) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1285,9 +1371,11 @@
private static final int MSG_REWIND = 17;
private static final int MSG_SEEK_TO = 18;
private static final int MSG_RATE = 19;
- private static final int MSG_CUSTOM_ACTION = 20;
- private static final int MSG_ADJUST_VOLUME = 21;
- private static final int MSG_SET_VOLUME = 22;
+ private static final int MSG_REPEAT_MODE = 20;
+ private static final int MSG_SHUFFLE_MODE = 21;
+ private static final int MSG_CUSTOM_ACTION = 22;
+ private static final int MSG_ADJUST_VOLUME = 23;
+ private static final int MSG_SET_VOLUME = 24;
private MediaSession.Callback mCallback;
@@ -1376,6 +1464,12 @@
case MSG_RATE:
mCallback.onSetRating((Rating) msg.obj);
break;
+ case MSG_REPEAT_MODE:
+ mCallback.onSetRepeatMode((int) msg.obj);
+ break;
+ case MSG_SHUFFLE_MODE:
+ mCallback.onSetShuffleMode((boolean) msg.obj);
+ break;
case MSG_CUSTOM_ACTION:
mCallback.onCustomAction((String) msg.obj, msg.getData());
break;
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 8283c8b..5cdd201 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -45,7 +45,8 @@
ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
- ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI})
+ ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI,
+ ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE})
@Retention(RetentionPolicy.SOURCE)
public @interface Actions {}
@@ -176,6 +177,20 @@
public static final long ACTION_PREPARE_FROM_URI = 1 << 17;
/**
+ * Indicates this session supports the set repeat mode command.
+ *
+ * @see Builder#setActions(long)
+ */
+ public static final long ACTION_SET_REPEAT_MODE = 1 << 18;
+
+ /**
+ * Indicates this session supports the set shuffle mode command.
+ *
+ * @see Builder#setActions(long)
+ */
+ public static final long ACTION_SET_SHUFFLE_MODE = 1 << 19;
+
+ /**
* @hide
*/
@IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
@@ -281,6 +296,30 @@
*/
public final static long PLAYBACK_POSITION_UNKNOWN = -1;
+ /**
+ * @hide
+ */
+ @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RepeatMode {}
+ /**
+ * Use this value with {@link MediaController.TransportControls#setRepeatMode}
+ * to indicate that the playback will be stopped at the end of the playing media list.
+ */
+ public final static int REPEAT_MODE_NONE = 0;
+
+ /**
+ * Use this value with {@link MediaController.TransportControls#setRepeatMode}
+ * to indicate that the playback of the current playing media item will be repeated.
+ */
+ public final static int REPEAT_MODE_ONE = 1;
+
+ /**
+ * Use this value with {@link MediaController.TransportControls#setRepeatMode}
+ * to indicate that the playback of the playing media list will be repeated.
+ */
+ public final static int REPEAT_MODE_ALL = 2;
+
private final int mState;
private final long mPosition;
private final long mBufferedPosition;
@@ -427,6 +466,8 @@
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
+ * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li>
+ * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE}</li>
* </ul>
*/
@Actions
@@ -961,6 +1002,8 @@
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
+ * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li>
+ * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE}</li>
* </ul>
*
* @param actions The set of actions allowed.
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 4211369..6d4461e 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -62,6 +62,7 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -80,7 +81,6 @@
import android.util.Log;
import android.util.Patterns;
import android.util.SparseArray;
-import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnFocusChangeListener;
@@ -775,7 +775,6 @@
}
msg = mContext.getString(R.string.bugreport_screenshot_taken);
} else {
- // TODO: try again using Framework APIs instead of relying on screencap.
msg = mContext.getString(R.string.bugreport_screenshot_failed);
Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
}
@@ -1359,22 +1358,24 @@
/**
* Takes a screenshot and save it to the given location.
*/
- private static boolean takeScreenshot(Context context, String screenshotFile) {
- final ProcessBuilder screencap = new ProcessBuilder()
- .command("/system/bin/screencap", "-p", screenshotFile);
- Log.d(TAG, "Taking screenshot using " + screencap.command());
- try {
- final int exitValue = screencap.start().waitFor();
- if (exitValue == 0) {
+ private static boolean takeScreenshot(Context context, String path) {
+ final Bitmap bitmap = Screenshooter.takeScreenshot();
+ if (bitmap == null) {
+ return false;
+ }
+ boolean status;
+ try (final FileOutputStream fos = new FileOutputStream(path)) {
+ if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) {
((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(150);
return true;
+ } else {
+ Log.e(TAG, "Failed to save screenshot on " + path);
}
- Log.e(TAG, "screencap (" + screencap.command() + ") failed: " + exitValue);
- } catch (IOException e) {
- Log.e(TAG, "screencap (" + screencap.command() + ") failed", e);
- } catch (InterruptedException e) {
- Log.w(TAG, "Thread interrupted while screencap still running");
- Thread.currentThread().interrupt();
+ } catch (IOException e ) {
+ Log.e(TAG, "Failed to save screenshot on " + path, e);
+ return false;
+ } finally {
+ bitmap.recycle();
}
return false;
}
diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java
new file mode 100644
index 0000000..3f4895b
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/Screenshooter.java
@@ -0,0 +1,139 @@
+/*
+ * 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.shell;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+/**
+ * Helper class used to take screenshots.
+ *
+ * TODO: logic below was copied and pasted from UiAutomation; it should be refactored into a common
+ * component that could be used by both (Shell and UiAutomation).
+ */
+final class Screenshooter {
+
+ private static final String TAG = "Screenshooter";
+
+ /** Rotation constant: Freeze rotation to 0 degrees (natural orientation) */
+ public static final int ROTATION_FREEZE_0 = Surface.ROTATION_0;
+
+ /** Rotation constant: Freeze rotation to 90 degrees . */
+ public static final int ROTATION_FREEZE_90 = Surface.ROTATION_90;
+
+ /** Rotation constant: Freeze rotation to 180 degrees . */
+ public static final int ROTATION_FREEZE_180 = Surface.ROTATION_180;
+
+ /** Rotation constant: Freeze rotation to 270 degrees . */
+ public static final int ROTATION_FREEZE_270 = Surface.ROTATION_270;
+
+ /**
+ * Takes a screenshot.
+ *
+ * @return The screenshot bitmap on success, null otherwise.
+ */
+ static Bitmap takeScreenshot() {
+ Display display = DisplayManagerGlobal.getInstance()
+ .getRealDisplay(Display.DEFAULT_DISPLAY);
+ Point displaySize = new Point();
+ display.getRealSize(displaySize);
+ final int displayWidth = displaySize.x;
+ final int displayHeight = displaySize.y;
+
+ final float screenshotWidth;
+ final float screenshotHeight;
+
+ final int rotation = display.getRotation();
+ switch (rotation) {
+ case ROTATION_FREEZE_0: {
+ screenshotWidth = displayWidth;
+ screenshotHeight = displayHeight;
+ } break;
+ case ROTATION_FREEZE_90: {
+ screenshotWidth = displayHeight;
+ screenshotHeight = displayWidth;
+ } break;
+ case ROTATION_FREEZE_180: {
+ screenshotWidth = displayWidth;
+ screenshotHeight = displayHeight;
+ } break;
+ case ROTATION_FREEZE_270: {
+ screenshotWidth = displayHeight;
+ screenshotHeight = displayWidth;
+ } break;
+ default: {
+ throw new IllegalArgumentException("Invalid rotation: "
+ + rotation);
+ }
+ }
+
+ Log.d(TAG, "Taking screenshot of dimensions " + displayWidth + " x " + displayHeight);
+ // Take the screenshot
+ Bitmap screenShot =
+ SurfaceControl.screenshot((int) screenshotWidth, (int) screenshotHeight);
+ if (screenShot == null) {
+ Log.e(TAG, "Failed to take screenshot of dimensions " + screenshotWidth + " x "
+ + screenshotHeight);
+ return null;
+ }
+
+ // Rotate the screenshot to the current orientation
+ if (rotation != ROTATION_FREEZE_0) {
+ Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight,
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(unrotatedScreenShot);
+ canvas.translate(unrotatedScreenShot.getWidth() / 2,
+ unrotatedScreenShot.getHeight() / 2);
+ canvas.rotate(getDegreesForRotation(rotation));
+ canvas.translate(- screenshotWidth / 2, - screenshotHeight / 2);
+ canvas.drawBitmap(screenShot, 0, 0, null);
+ canvas.setBitmap(null);
+ screenShot.recycle();
+ screenShot = unrotatedScreenShot;
+ }
+
+ // Optimization
+ screenShot.setHasAlpha(false);
+
+ return screenShot;
+ }
+
+ private static float getDegreesForRotation(int value) {
+ switch (value) {
+ case Surface.ROTATION_90: {
+ return 360f - 90f;
+ }
+ case Surface.ROTATION_180: {
+ return 360f - 180f;
+ }
+ case Surface.ROTATION_270: {
+ return 360f - 270f;
+ } default: {
+ return 0;
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f48039e..c2897cd 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -250,7 +250,7 @@
<integer name="doze_pulse_duration_in">900</integer>
<!-- Doze: pulse parameter - how long does it take to fade in after a pickup? -->
- <integer name="doze_pulse_duration_in_pickup">300</integer>
+ <integer name="doze_pulse_duration_in_pickup">130</integer>
<!-- Doze: pulse parameter - once faded in, how long does it stay visible? -->
<integer name="doze_pulse_duration_visible">3000</integer>
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 843b109..228c4fd 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -116,6 +116,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -263,6 +264,8 @@
private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
private final AtomicInteger mNotificationIds = new AtomicInteger(1);
+ private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
+
static class UserAccounts {
private final int userId;
private final DeDatabaseHelper openHelper;
@@ -285,6 +288,21 @@
/** protected by the {@link #cacheLock} */
private final TokenCache accountTokenCaches = new TokenCache();
+ /** protected by the {@link #cacheLock} */
+ private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
+ new HashMap<>();
+
+ /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
+ number. Another maps Account number to Account.*/
+
+ /** protected by the {@link #cacheLock} */
+ private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
+ new SparseArray<>();
+
+ //TODO: Instead of using Mock Account IDs, use the actual account IDs.
+ /** protected by the {@link #cacheLock} */
+ private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
+
/**
* protected by the {@link #cacheLock}
*
@@ -344,6 +362,8 @@
sThis.set(this);
+ addRequestsForPreInstalledApplications();
+
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
@@ -366,13 +386,51 @@
@Override
public void run() {
purgeOldGrantsAll();
+
+ /* clears application request's for account types supported */
+ int uidOfUninstalledApplication =
+ intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if(uidOfUninstalledApplication != -1) {
+ clearRequestedAccountVisibility(uidOfUninstalledApplication,
+ getUserAccounts(UserHandle.getUserId(
+ uidOfUninstalledApplication)));
+ }
+
+ /* removes visibility of previous UID of this uninstalled application*/
+ removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
+ getUserAccounts(UserHandle.getUserId(
+ uidOfUninstalledApplication)));
}
};
new Thread(r).start();
}
+
}
}, intentFilter);
+ IntentFilter packageAddedOrChangedFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageAddedOrChangedFilter.addDataScheme("package");
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context1, Intent intent) {
+ mMessageHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ int uidOfInstalledApplication =
+ intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if(uidOfInstalledApplication != -1) {
+ registerAccountTypesSupported(
+ uidOfInstalledApplication,
+ getUserAccounts(
+ UserHandle.getUserId(uidOfInstalledApplication)));
+ }
+ }
+ });
+ }
+ }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
+
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@@ -387,6 +445,475 @@
}
@Override
+ public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
+ int[] selectedUids) {
+ if(addAccountExplicitly(account,password,extras)) {
+ for(int thisUid : selectedUids) {
+ makeAccountVisible(account, thisUid);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int[] getRequestingUidsForType(String accountType) {
+ int callingUid = Binder.getCallingUid();
+ if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
+ String msg = String.format(
+ "uid %s cannot get secrets for accounts of type: %s",
+ callingUid,
+ accountType);
+ throw new SecurityException(msg);
+ }
+ return getRequestingUidsForType(accountType, getUserAccounts(
+ UserHandle.getUserId(callingUid)));
+ }
+
+ /**
+ * Returns all UIDs for applications that requested the account type. This method
+ * is called indirectly by the Authenticator and AccountManager
+ *
+ * @param accountType authenticator would like to know the requesting apps of
+ * @param ua UserAccount that currently hosts the account and application
+ *
+ * @return ArrayList of all UIDs that support accounts of this
+ * account type that seek approval (to be used to know which accounts for
+ * the authenticator to include in addAccountExplicitly). Null if none.
+ */
+ private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
+ synchronized(ua.cacheLock) {
+ Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
+ ua.mApplicationAccountRequestMappings;
+ ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
+ accountType);
+ if(allUidsForAccountType == null) {
+ return null;
+ }
+ int[] toReturn = new int[allUidsForAccountType.size()];
+ for(int i = 0 ; i < toReturn.length ; i++) {
+ toReturn[i] = allUidsForAccountType.get(i);
+ }
+ return toReturn;
+ }
+ }
+
+ @Override
+ public boolean isAccountVisible(Account a, int uid) {
+ int callingUid = Binder.getCallingUid();
+ if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
+ String msg = String.format(
+ "uid %s cannot get secrets for accounts of type: %s",
+ callingUid,
+ a.type);
+ throw new SecurityException(msg);
+ }
+ return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
+ }
+
+ /**
+ * Checks visibility of certain account of a process identified
+ * by a given UID. This is called by the Authenticator indirectly.
+ *
+ * @param a The account to check visibility of
+ * @param uid UID to check visibility of
+ * @param ua UserAccount that currently hosts the account and application
+ *
+ * @return True if application has access to the account
+ *
+ */
+ private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
+ int accountMapping = getMockAccountNumber(a, ua);
+ if(accountMapping < 0) {
+ return true;
+ }
+ synchronized(ua.cacheLock) {
+ SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
+ ua.mVisibleListUidToMockAccountNumbers;
+ ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
+ return linkedAccountsToUid != null && linkedAccountsToUid.contains(accountMapping);
+ }
+ }
+
+ @Override
+ public boolean makeAccountVisible(Account a, int uid) {
+ int callingUid = Binder.getCallingUid();
+ if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
+ String msg = String.format(
+ "uid %s cannot get secrets for accounts of type: %s",
+ callingUid,
+ a.type);
+ throw new SecurityException(msg);
+ }
+ return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
+ }
+
+ /**
+ * Gives a certain UID, represented a application, access to an account. This method
+ * is called indirectly by the Authenticator.
+ *
+ * @param a Account to make visible
+ * @param uid to add visibility of the Account from
+ * @param ua UserAccount that currently hosts the account and application
+ *
+ * @return True if account made visible to application and was not previously visible.
+ */
+ private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
+ int accountMapping = getMockAccountNumber(a, ua);
+ if(accountMapping < 0) {
+ accountMapping = makeAccountNumber(a, ua);
+ }
+
+ synchronized(ua.cacheLock) {
+ SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
+ ua.mVisibleListUidToMockAccountNumbers;
+ ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
+ if(linkedAccountsToUid == null) {
+ linkedAccountsToUid = new ArrayList<>();
+ linkedAccountsToUid.add(accountMapping);
+ userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
+ } else if(!linkedAccountsToUid.contains(accountMapping)) {
+ linkedAccountsToUid.add(accountMapping);
+ } else {
+ return false;
+ }
+ }
+
+ String[] subPackages = mPackageManager.getPackagesForUid(uid);
+ if(subPackages != null) {
+ for(String subPackage : subPackages) {
+ sendNotification(subPackage, a);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean removeAccountVisibility(Account a, int uid) {
+ int callingUid = Binder.getCallingUid();
+ if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
+ String msg = String.format(
+ "uid %s cannot get secrets for accounts of type: %s",
+ callingUid,
+ a.type);
+ throw new SecurityException(msg);
+ }
+ return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
+ }
+
+ /**
+ * Removes visibility of certain account of a process identified
+ * by a given UID to an application. This is called directly by the
+ * AccountManager and indirectly by the Authenticator.
+ *
+ * @param a Account to remove visibility from
+ * @param uid UID to remove visibility of the Account from
+ * @param ua UserAccount that hosts the account and application
+ *
+ * @return True if application access to account removed and was previously visible.
+ */
+ private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
+ int accountMapping = getMockAccountNumber(a, ua);
+ if(accountMapping < 0) {
+ return false;
+ }
+ synchronized(ua.cacheLock) {
+ SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
+ ua.mVisibleListUidToMockAccountNumbers;
+ ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
+ if(linkedAccountsToUid != null) {
+ boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
+ if(linkedAccountsToUid.size() == 0) {
+ userWlUidToMockAccountNums.remove(uid);
+ }
+ return toReturn;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Registers an application's preferences for supported account types for login. This is
+ * a helper method of requestAccountVisibility and indirectly called by AccountManager.
+ *
+ * @param accountTypes account types third party app is willing to support
+ * @param uid of application requesting account visibility
+ * @param ua UserAccount that hosts the account and application
+ */
+ private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
+ synchronized(ua.cacheLock) {
+ Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
+ ua.mApplicationAccountRequestMappings;
+ for(String accountType : accountTypes) {
+ ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
+ .get(accountType);
+ if(accountUidAppList == null) {
+ accountUidAppList = new ArrayList<>();
+ accountUidAppList.add(uid);
+ userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
+ } else if (!accountUidAppList.contains(uid)) {
+ accountUidAppList.add(uid);
+ }
+ }
+ }
+ }
+
+ /**
+ * Registers the requested login account types requested by all the applications already
+ * installed on the device.
+ */
+ private void addRequestsForPreInstalledApplications() {
+ List<PackageInfo> allInstalledPackages = mContext.getPackageManager().
+ getInstalledPackages(0);
+ for(PackageInfo pi : allInstalledPackages) {
+ int currentUid = pi.applicationInfo.uid;
+ if(currentUid != -1) {
+ registerAccountTypesSupported(currentUid,
+ getUserAccounts(UserHandle.getUserId(currentUid)));
+ }
+ }
+ }
+
+ /**
+ * Clears all preferences an application had for login account types it offered
+ * support for. This method is used by AccountManager after application is
+ * uninstalled.
+ *
+ * @param uid Uid of the application to clear account type preferences
+ * @param ua UserAccount that hosted the account and application
+ *
+ * @return true if any previous settings were overridden.
+ */
+ private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
+ boolean accountsDeleted = false;
+ ArrayList<String> accountTypesToRemove = new ArrayList<>();
+ synchronized(ua.cacheLock) {
+ Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
+ ua.mApplicationAccountRequestMappings;
+ Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
+ userApplicationAccountRequestMappings.entrySet();
+
+ for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
+ ArrayList<Integer> supportedApps = entry.getValue();
+ if(supportedApps.remove((Integer) uid)) {
+ accountsDeleted = true;
+ }
+
+ if(supportedApps.isEmpty()) {
+ accountTypesToRemove.add(entry.getKey());
+ }
+ }
+
+ for(String s : accountTypesToRemove) {
+ userApplicationAccountRequestMappings.remove(s);
+ }
+ }
+
+ return accountsDeleted;
+ }
+
+ /**
+ * Retrieves the mock account number associated with an Account in order to later retrieve
+ * the account from the Integer-Account Mapping. An account number is not the same as
+ * accountId in the database. This method can be indirectly called by AccountManager and
+ * indirectly by the Authenticator.
+ *
+ * @param a account to retrieve account number mapping
+ * @param ua UserAccount that currently hosts the account and application
+ *
+ * @return account number affiliated with the Account in question. Negative number if none.
+ */
+ private int getMockAccountNumber(Account a, UserAccounts ua) {
+ //TODO: Each account is linked to AccountId rather than generated mock account numbers
+ SparseArray<Account> userAcctIdToAcctMap =
+ ua.mMockAccountIdToAccount;
+ synchronized(ua.cacheLock) {
+ int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
+ if(indexOfAccount < 0) {
+ return -1;
+ }
+ return userAcctIdToAcctMap.keyAt(indexOfAccount);
+ }
+ }
+
+ /**
+ * Returns a full list of accounts that a certain UID is allowed access
+ * based on the visible list entries.
+ *
+ * @param uid of application to retrieve visible listed accounts for
+ * @param ua UserAccount that currently hosts the account and application
+ *
+ * @return array of Account values that are accessible by the given uids
+ */
+ private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
+ ArrayList<Account> visibleListedAccounts = new ArrayList<>();
+ synchronized(ua.cacheLock) {
+ SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
+ SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
+ ua.mVisibleListUidToMockAccountNumbers;
+ ArrayList<Integer> visibleListedUidAccountNumbers =
+ userWlUidToMockAccountNums.get(uid);
+ if(visibleListedUidAccountNumbers != null) {
+ for(Integer accountNumber : visibleListedUidAccountNumbers) {
+ Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
+ visibleListedAccounts.add(currentAccount);
+ }
+ }
+ }
+ Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
+ return visibleListedAccounts.toArray(arrVisibleListedAccounts);
+ }
+
+ /**
+ * Makes an account number for a given Account to be mapped to.
+ * This method is called by makeVisible if an Account does not have
+ * a mapping for the visible list. This method is thus indirectly
+ * called by the Authenticator.
+ *
+ * @param a account to make an account number mapping of
+ * @param ua UserAccount that currently hosts the account and application
+ *
+ * @return account number created to map to the given account
+ */
+ // TODO: Remove this method and use accountId from DB.
+ private int makeAccountNumber(Account a, UserAccounts ua) {
+ synchronized(ua.cacheLock) {
+ SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
+ int newAccountMapping = 0;
+ while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
+ newAccountMapping++;
+ }
+ userAcctIdToAcctMap.put(newAccountMapping, a);
+ return newAccountMapping;
+ }
+ }
+
+
+
+ /**
+ * Registers an application, represented by a UID, to support account types detailed in
+ * the applications manifest as well as allowing it to opt for notifications.
+ *
+ * @param uid UID of application
+ * @param ua UserAccount that currently hosts the account and application
+ */
+ private void registerAccountTypesSupported(int uid, UserAccounts ua) {
+ /* Account types supported are drawn from the Android Manifest of the Application */
+ String interestedPackages = null;
+ try {
+ String[] allPackages = mPackageManager.getPackagesForUid(uid);
+ for(String aPackage : allPackages) {
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
+ PackageManager.GET_META_DATA);
+ Bundle b = ai.metaData;
+ if(b == null) {
+ return;
+ }
+ interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d("NameNotFoundException", e.getMessage());
+ }
+ if(interestedPackages != null) {
+ /* request remote account types directly from here. Reads from Android Manifest */
+ requestAccountVisibility(interestedPackages.split(";"), uid, ua);
+ }
+ }
+
+ /**
+ * Allows AccountManager to register account types that an application has login
+ * support for. This method over-writes all of the application's previous settings
+ * for accounts it supported.
+ *
+ * @param accountTypes array of account types application wishes to support
+ * @param uid of application registering requested account types
+ * @param ua UserAccount that hosts the account and application
+ */
+ private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
+ if(accountTypes.length > 0) {
+ clearRequestedAccountVisibility(uid, ua);
+ addRequestedAccountsVisibility(accountTypes, uid, ua);
+ }
+ }
+
+ /**
+ * Removes visibility of all Accounts to this particular UID. This is called when an
+ * application is uninstalled so another application that is installed with the same
+ * UID cannot access Accounts. This is called by AccountManager.
+ *
+ * @param uid of application to remove all Account visibility to
+ * @param ua UserAccount that hosts the current Account
+ */
+ private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
+ synchronized(ua.cacheLock) {
+ SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
+ ua.mVisibleListUidToMockAccountNumbers;
+ SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
+ ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
+ if(allAccountNumbersList != null) {
+ Integer[] allAccountNumbers = allAccountNumbersList.toArray(
+ new Integer[allAccountNumbersList.size()]);
+ for(int accountNum : allAccountNumbers) {
+ removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes visible list functionality of a certain Account.
+ * This method is currently called by (1) addAccountExplicitly (as opposed to
+ * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
+ *
+ * @param a the account to clear the visible list functionality for
+ * @param ua currently UserAccounts profile containing Account
+ *
+ * @return true if account previously had visible list functionality
+ */
+ private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
+ int mockAccountNum = getMockAccountNumber(a, ua);
+ if(mockAccountNum < 0) {
+ return false;
+ }
+ synchronized(ua.cacheLock) {
+ SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
+ ua.mVisibleListUidToMockAccountNumbers;
+ SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
+
+ /* Removing mapping from account number to account removes visible list functionality*/
+ userAcctIdToAcctMap.remove(mockAccountNum);
+
+ for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
+ int uidKey = userWlUidToMockAccountNums.keyAt(i);
+ ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
+ if(allAccountNumbers != null) {
+ allAccountNumbers.remove(mockAccountNum);
+ if(allAccountNumbers.isEmpty()) {
+ userWlUidToMockAccountNums.remove(uidKey);
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Sends a direct intent to a package, notifying it of a visible account. This
+ * method is a helper method of makeAccountVisible.
+ *
+ * @param desiredPackage to send Account to
+ * @param visibleAccount to send to package
+ */
+ private void sendNotification(String desiredPackage, Account visibleAccount) {
+ Intent intent = new Intent();
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ intent.setAction(NEW_ACCOUNT_VISIBLE);
+ intent.setPackage(desiredPackage);
+ intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
+ mContext.sendBroadcast(intent);
+ }
+
+ @Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
@@ -563,7 +1090,7 @@
} else {
ArrayList<String> accountNames = accountNamesByType.get(accountType);
if (accountNames == null) {
- accountNames = new ArrayList<String>();
+ accountNames = new ArrayList<>();
accountNamesByType.put(accountType, accountNames);
}
accountNames.add(accountName);
@@ -968,6 +1495,9 @@
@Override
public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Bundle.setDefusable(extras, true);
+ // clears the visible list functionality for this account because this method allows
+ // default account access to all applications for account.
+
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "addAccountExplicitly: " + account
@@ -983,6 +1513,7 @@
account.type);
throw new SecurityException(msg);
}
+ removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
/*
* Child users are not allowed to add accounts. Only the accounts that are
* shared by the parent profile can be added to child profile.
@@ -1600,6 +2131,7 @@
account.type);
throw new SecurityException(msg);
}
+ removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
UserAccounts accounts = getUserAccountsForCaller();
SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
final long accountId = getAccountIdLocked(db, account);
@@ -3330,7 +3862,7 @@
null);
}
// check whether each account matches the requested features
- mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
+ mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
mCurrentAccount = 0;
checkAccount();
@@ -3515,7 +4047,6 @@
callingUid = packageUid;
opPackageName = callingPackage;
}
-
List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
opPackageName);
if (visibleAccountTypes.isEmpty()
@@ -3666,7 +4197,23 @@
@Override
@NonNull
public Account[] getAccounts(String type, String opPackageName) {
- return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
+ Account[] accessibleAccounts =
+ getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
+ /* Up until now, access to accounts is User Specific. With Push API, these accounts
+ can be filtered by application as well. Therefore, in this method specifically,
+ we will additionally add the visible listed accounts tied to the Application
+ specifically through visible listing, as approved by the AccountAuthenticator
+ for a specific application on a device. */
+ ArrayList<Account> allAccounts = new ArrayList<>(Arrays.asList(accessibleAccounts));
+ Account[] visibleListedAccounts = getVisibleListedAccounts(Binder.getCallingUid(),
+ getUserAccounts(UserHandle.getCallingUserId()));
+ for(Account visibleAccount : visibleListedAccounts) {
+ if(!allAccounts.contains(visibleAccount)) {
+ allAccounts.add(visibleAccount);
+ }
+ }
+ Account[] allAccountsArr = new Account[allAccounts.size()];
+ return allAccounts.toArray(allAccountsArr);
}
@Override
@@ -5374,7 +5921,7 @@
private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
if (oldAccountsForType != null) {
- ArrayList<Account> newAccountsList = new ArrayList<Account>();
+ ArrayList<Account> newAccountsList = new ArrayList<>();
for (Account curAccount : oldAccountsForType) {
if (!curAccount.equals(account)) {
newAccountsList.add(curAccount);
@@ -5416,17 +5963,17 @@
UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
if (user != null && user.isRestricted()) {
String[] packages = mPackageManager.getPackagesForUid(callingUid);
- // If any of the packages is a white listed package, return the full set,
+ // If any of the packages is a visible listed package, return the full set,
// otherwise return non-shared accounts only.
- // This might be a temporary way to specify a whitelist
- String whiteList = mContext.getResources().getString(
+ // This might be a temporary way to specify a visible list
+ String visibleList = mContext.getResources().getString(
com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
for (String packageName : packages) {
- if (whiteList.contains(";" + packageName + ";")) {
+ if (visibleList.contains(";" + packageName + ";")) {
return unfiltered;
}
}
- ArrayList<Account> allowed = new ArrayList<Account>();
+ ArrayList<Account> allowed = new ArrayList<>();
Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
String requiredAccountType = "";
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d496e7a..35b17d7 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -699,7 +699,7 @@
throw new IllegalArgumentException("null notification");
}
if (r.foregroundId != id) {
- r.cancelNotification();
+ cancelForegroudNotificationLocked(r);
r.foregroundId = id;
}
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
@@ -721,7 +721,7 @@
}
}
if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
- r.cancelNotification();
+ cancelForegroudNotificationLocked(r);
r.foregroundId = 0;
r.foregroundNoti = null;
} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
@@ -738,6 +738,27 @@
}
}
+ private void cancelForegroudNotificationLocked(ServiceRecord r) {
+ if (r.foregroundId != 0) {
+ // First check to see if this app has any other active foreground services
+ // with the same notification ID. If so, we shouldn't actually cancel it,
+ // because that would wipe away the notification that still needs to be shown
+ // due the other service.
+ ServiceMap sm = getServiceMap(r.userId);
+ if (sm != null) {
+ for (int i = sm.mServicesByName.size()-1; i >= 0; i--) {
+ ServiceRecord other = sm.mServicesByName.valueAt(i);
+ if (other.foregroundId == r.foregroundId
+ && other.packageName.equals(r.packageName)) {
+ // Found one! Abort the cancel.
+ return;
+ }
+ }
+ }
+ r.cancelNotification();
+ }
+ }
+
private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
boolean anyForeground = false;
for (int i=proc.services.size()-1; i>=0; i--) {
@@ -1556,7 +1577,7 @@
r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
}
- r.cancelNotification();
+ cancelForegroudNotificationLocked(r);
mAm.mHandler.removeCallbacks(r.restarter);
mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
@@ -2018,7 +2039,7 @@
}
}
- r.cancelNotification();
+ cancelForegroudNotificationLocked(r);
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 2bfc402..71c7fd3 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -543,27 +543,25 @@
}
public void cancelNotification() {
- if (foregroundId != 0) {
- // Do asynchronous communication with notification manager to
- // avoid deadlocks.
- final String localPackageName = packageName;
- final int localForegroundId = foregroundId;
- ams.mHandler.post(new Runnable() {
- public void run() {
- INotificationManager inm = NotificationManager.getService();
- if (inm == null) {
- return;
- }
- try {
- inm.cancelNotificationWithTag(localPackageName, null,
- localForegroundId, userId);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error canceling notification for service", e);
- } catch (RemoteException e) {
- }
+ // Do asynchronous communication with notification manager to
+ // avoid deadlocks.
+ final String localPackageName = packageName;
+ final int localForegroundId = foregroundId;
+ ams.mHandler.post(new Runnable() {
+ public void run() {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
}
- });
- }
+ try {
+ inm.cancelNotificationWithTag(localPackageName, null,
+ localForegroundId, userId);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ }
+ });
}
public void stripForegroundServiceFlagFromNotification() {
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
index a58d243..b47e079 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -176,9 +176,21 @@
private void updateRaParams(RaParams params) {
if (mRaDaemon != null) {
+ HashSet<IpPrefix> deprecated = null;
+
+ if (mLastRaParams != null) {
+ deprecated = new HashSet<>();
+
+ for (IpPrefix ipp : mLastRaParams.prefixes) {
+ if (params == null || !params.prefixes.contains(ipp)) {
+ deprecated.add(ipp);
+ }
+ }
+ }
+
// Currently, we send spurious RAs (5) whenever there's any update.
// TODO: Compare params with mLastParams to avoid spurious updates.
- mRaDaemon.buildNewRa(params);
+ mRaDaemon.buildNewRa(params, deprecated);
}
mLastRaParams = params;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 88d6c14..2d71ce9 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -107,6 +107,8 @@
private ParceledListSlice mQueue;
private CharSequence mQueueTitle;
private int mRatingType;
+ private int mRepeatMode;
+ private boolean mShuffleMode;
private long mLastActiveTime;
// End TransportPerformer fields
@@ -620,6 +622,44 @@
}
}
+ private void pushRepeatModeUpdate() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onRepeatModeChanged(mRepeatMode);
+ } catch (DeadObjectException e) {
+ mControllerCallbacks.remove(i);
+ Log.w(TAG, "Removed dead callback in pushRepeatModeUpdate.", e);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushRepeatModeUpdate.", e);
+ }
+ }
+ }
+ }
+
+ private void pushShuffleModeUpdate() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onShuffleModeChanged(mShuffleMode);
+ } catch (DeadObjectException e) {
+ mControllerCallbacks.remove(i);
+ Log.w(TAG, "Removed dead callback in pushShuffleModeUpdate.", e);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushShuffleModeUpdate.", e);
+ }
+ }
+ }
+ }
+
private void pushSessionDestroyed() {
synchronized (mLock) {
// This is the only method that may be (and can only be) called
@@ -828,6 +868,30 @@
}
@Override
+ public void setRepeatMode(int repeatMode) {
+ boolean changed;
+ synchronized (mLock) {
+ changed = mRepeatMode != repeatMode;
+ mRepeatMode = repeatMode;
+ }
+ if (changed) {
+ mHandler.post(MessageHandler.MSG_UPDATE_REPEAT_MODE);
+ }
+ }
+
+ @Override
+ public void setShuffleMode(boolean shuffleMode) {
+ boolean changed;
+ synchronized (mLock) {
+ changed = mShuffleMode != shuffleMode;
+ mShuffleMode = shuffleMode;
+ }
+ if (changed) {
+ mHandler.post(MessageHandler.MSG_UPDATE_SHUFFLE_MODE);
+ }
+ }
+
+ @Override
public void setCurrentVolume(int volume) {
mCurrentVolume = volume;
mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
@@ -1043,6 +1107,22 @@
}
}
+ public void repeatMode(int repeatMode) {
+ try {
+ mCb.onRepeatMode(repeatMode);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in repeatMode.", e);
+ }
+ }
+
+ public void shuffleMode(boolean shuffleMode) {
+ try {
+ mCb.onShuffleMode(shuffleMode);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in shuffleMode.", e);
+ }
+ }
+
public void adjustVolume(int direction) {
try {
mCb.onAdjustVolume(direction);
@@ -1278,13 +1358,25 @@
}
@Override
+ public void repeatMode(int repeatMode) {
+ updateCallingPackage();
+ mSessionCb.repeatMode(repeatMode);
+ }
+
+ @Override
+ public void shuffleMode(boolean shuffleMode) throws RemoteException {
+ updateCallingPackage();
+ mSessionCb.shuffleMode(shuffleMode);
+ }
+
+
+ @Override
public void sendCustomAction(String action, Bundle args)
throws RemoteException {
updateCallingPackage();
mSessionCb.sendCustomAction(action, args);
}
-
@Override
public MediaMetadata getMetadata() {
synchronized (mLock) {
@@ -1322,6 +1414,16 @@
}
@Override
+ public int getRepeatMode() {
+ return mRepeatMode;
+ }
+
+ @Override
+ public boolean getShuffleMode() {
+ return mShuffleMode;
+ }
+
+ @Override
public boolean isTransportControlEnabled() {
return MediaSessionRecord.this.isTransportControlEnabled();
}
@@ -1336,7 +1438,9 @@
private static final int MSG_SEND_EVENT = 6;
private static final int MSG_UPDATE_SESSION_STATE = 7;
private static final int MSG_UPDATE_VOLUME = 8;
- private static final int MSG_DESTROYED = 9;
+ private static final int MSG_UPDATE_REPEAT_MODE = 9;
+ private static final int MSG_UPDATE_SHUFFLE_MODE = 10;
+ private static final int MSG_DESTROYED = 11;
public MessageHandler(Looper looper) {
super(looper);
@@ -1368,6 +1472,12 @@
case MSG_UPDATE_VOLUME:
pushVolumeUpdate();
break;
+ case MSG_UPDATE_REPEAT_MODE:
+ pushRepeatModeUpdate();
+ break;
+ case MSG_UPDATE_SHUFFLE_MODE:
+ pushShuffleModeUpdate();
+ break;
case MSG_DESTROYED:
pushSessionDestroyed();
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b382a3b..f433af8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1036,7 +1036,7 @@
*/
private void notifyOverLimitNL(NetworkTemplate template) {
if (!mOverLimitNotified.contains(template)) {
- mContext.startActivity(buildNetworkOverLimitIntent(template));
+ mContext.startActivity(buildNetworkOverLimitIntent(mContext.getResources(), template));
mOverLimitNotified.add(template);
}
}
@@ -1081,7 +1081,7 @@
builder.setDeleteIntent(PendingIntent.getBroadcast(
mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
- final Intent viewIntent = buildViewDataUsageIntent(policy.template);
+ final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
builder.setContentIntent(PendingIntent.getActivity(
mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
@@ -1117,7 +1117,7 @@
builder.setContentTitle(title);
builder.setContentText(body);
- final Intent intent = buildNetworkOverLimitIntent(policy.template);
+ final Intent intent = buildNetworkOverLimitIntent(res, policy.template);
builder.setContentIntent(PendingIntent.getActivity(
mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
break;
@@ -1152,7 +1152,7 @@
builder.setContentTitle(title);
builder.setContentText(body);
- final Intent intent = buildViewDataUsageIntent(policy.template);
+ final Intent intent = buildViewDataUsageIntent(res, policy.template);
builder.setContentIntent(PendingIntent.getActivity(
mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
break;
@@ -3525,19 +3525,19 @@
return intent;
}
- private static Intent buildNetworkOverLimitIntent(NetworkTemplate template) {
+ private static Intent buildNetworkOverLimitIntent(Resources res, NetworkTemplate template) {
final Intent intent = new Intent();
- intent.setComponent(new ComponentName(
- "com.android.systemui", "com.android.systemui.net.NetworkOverLimitActivity"));
+ intent.setComponent(ComponentName.unflattenFromString(
+ res.getString(R.string.config_networkOverLimitComponent)));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
return intent;
}
- private static Intent buildViewDataUsageIntent(NetworkTemplate template) {
+ private static Intent buildViewDataUsageIntent(Resources res, NetworkTemplate template) {
final Intent intent = new Intent();
- intent.setComponent(new ComponentName(
- "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
+ intent.setComponent(ComponentName.unflattenFromString(
+ res.getString(R.string.config_dataUsageSummaryComponent)));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
return intent;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index cd5bcd4..53e328c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -768,41 +768,46 @@
private void onShortcutChangedInner(@NonNull String packageName,
@UserIdInt int userId) {
- final UserHandle user = UserHandle.of(userId);
+ try {
+ final UserHandle user = UserHandle.of(userId);
- final int n = mListeners.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
- final int launcherUserId = cookie.user.getIdentifier();
+ final int launcherUserId = cookie.user.getIdentifier();
- // Make sure the caller has the permission.
- if (!mShortcutServiceInternal.hasShortcutHostPermission(
- launcherUserId, cookie.packageName)) {
- continue;
+ // Make sure the caller has the permission.
+ if (!mShortcutServiceInternal.hasShortcutHostPermission(
+ launcherUserId, cookie.packageName)) {
+ continue;
+ }
+ // Each launcher has a different set of pinned shortcuts, so we need to do a
+ // query in here.
+ // (As of now, only one launcher has the permission at a time, so it's bit
+ // moot, but we may change the permission model eventually.)
+ final List<ShortcutInfo> list =
+ mShortcutServiceInternal.getShortcuts(launcherUserId,
+ cookie.packageName,
+ /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
+ /* component= */ null,
+ ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
+ | ShortcutQuery.FLAG_GET_ALL_KINDS
+ , userId);
+ try {
+ listener.onShortcutChanged(user, packageName,
+ new ParceledListSlice<>(list));
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
}
- // Each launcher has a different set of pinned shortcuts, so we need to do a
- // query in here.
- // (As of now, only one launcher has the permission at a time, so it's bit
- // moot, but we may change the permission model eventually.)
- final List<ShortcutInfo> list =
- mShortcutServiceInternal.getShortcuts(launcherUserId,
- cookie.packageName,
- /* changedSince= */ 0, packageName, /* shortcutIds=*/ null,
- /* component= */ null,
- ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
- | ShortcutQuery.FLAG_GET_ALL_KINDS
- , userId);
- try {
- listener.onShortcutChanged(user, packageName,
- new ParceledListSlice<>(list));
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
- }
+ mListeners.finishBroadcast();
+ } catch (RuntimeException e) {
+ // When the user is locked we get IllegalState, so just catch all.
+ Log.w(TAG, e.getMessage(), e);
}
- mListeners.finishBroadcast();
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d875f1e9..8d400b5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -77,6 +77,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TypedValue;
@@ -85,7 +86,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -301,6 +301,9 @@
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES;
+ @GuardedBy("mLock")
+ final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
+
// Stats
@VisibleForTesting
interface Stats {
@@ -522,6 +525,8 @@
Slog.d(TAG, "handleUnlockUser: user=" + userId);
}
synchronized (mLock) {
+ mUnlockedUsers.put(userId, true);
+
// Preload the user's shortcuts.
// Also see if the locale has changed.
// Note as of nyc, the locale is per-user, so the locale shouldn't change
@@ -534,8 +539,13 @@
/** lifecycle event */
void handleCleanupUser(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "handleCleanupUser: user=" + userId);
+ }
synchronized (mLock) {
unloadUserLocked(userId);
+
+ mUnlockedUsers.put(userId, false);
}
}
@@ -978,16 +988,20 @@
if (DEBUG) {
Slog.d(TAG, "saveDirtyInfo");
}
- synchronized (mLock) {
- for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
- final int userId = mDirtyUserIds.get(i);
- if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
- saveBaseStateLocked();
- } else {
- saveUserLocked(userId);
+ try {
+ synchronized (mLock) {
+ for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
+ final int userId = mDirtyUserIds.get(i);
+ if (userId == UserHandle.USER_NULL) { // USER_NULL for base state.
+ saveBaseStateLocked();
+ } else {
+ saveUserLocked(userId);
+ }
}
+ mDirtyUserIds.clear();
}
- mDirtyUserIds.clear();
+ } catch (Exception e) {
+ wtf("Exception in saveDirtyInfo", e);
}
}
@@ -1037,20 +1051,14 @@
}
}
- private boolean isUserUnlocked(@UserIdInt int userId) {
- final long token = injectClearCallingIdentity();
- try {
- // Weird: when SystemService.onUnlockUser() is called, the user state is still
- // unlocking, as opposed to unlocked. So we need to accept the "unlocking" state too.
- // We know when the user is unlocking, the CE storage is already unlocked.
- return mUserManager.isUserUnlockingOrUnlocked(userId);
- } finally {
- injectRestoreCallingIdentity(token);
- }
+ // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L".
+ protected boolean isUserUnlockedL(@UserIdInt int userId) {
+ return mUnlockedUsers.get(userId);
}
- void throwIfUserLocked(@UserIdInt int userId) {
- if (!isUserUnlocked(userId)) {
+ // Requires mLock held, but "Locked" prefix would look weired so we jsut say "L".
+ void throwIfUserLockedL(@UserIdInt int userId) {
+ if (!isUserUnlockedL(userId)) {
throw new IllegalStateException("User " + userId + " is locked or not running");
}
}
@@ -1065,9 +1073,8 @@
@GuardedBy("mLock")
@NonNull
ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
- if (!isUserUnlocked(userId)) {
+ if (!isUserUnlockedL(userId)) {
wtf("User still locked");
- return new ShortcutUser(this, userId);
}
ShortcutUser userPackages = mUsers.get(userId);
@@ -1471,22 +1478,21 @@
}
private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
- final long token = injectClearCallingIdentity();
- try {
- if (!mUserManager.isUserRunning(userId)) {
- return;
- }
- } finally {
- injectRestoreCallingIdentity(token);
- }
injectPostToHandler(() -> {
- final ArrayList<ShortcutChangeListener> copy;
- synchronized (mLock) {
- copy = new ArrayList<>(mListeners);
- }
- // Note onShortcutChanged() needs to be called with the system service permissions.
- for (int i = copy.size() - 1; i >= 0; i--) {
- copy.get(i).onShortcutChanged(packageName, userId);
+ try {
+ final ArrayList<ShortcutChangeListener> copy;
+ synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ return;
+ }
+
+ copy = new ArrayList<>(mListeners);
+ }
+ // Note onShortcutChanged() needs to be called with the system service permissions.
+ for (int i = copy.size() - 1; i >= 0; i--) {
+ copy.get(i).onShortcutChanged(packageName, userId);
+ }
+ } catch (Exception ignore) {
}
});
}
@@ -1558,12 +1564,13 @@
public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
@@ -1609,12 +1616,13 @@
public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
@@ -1689,12 +1697,13 @@
public boolean addDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
@@ -1741,9 +1750,10 @@
CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
@@ -1770,9 +1780,10 @@
public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
@@ -1792,9 +1803,10 @@
@UserIdInt int userId) {
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
@@ -1816,9 +1828,10 @@
@Override
public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
ps.deleteAllDynamicShortcuts();
@@ -1832,9 +1845,10 @@
public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
ShortcutInfo::isDynamic);
@@ -1845,9 +1859,10 @@
public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
ShortcutInfo::isManifestShortcut);
@@ -1858,9 +1873,10 @@
public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
ShortcutInfo::isPinned);
@@ -1890,9 +1906,10 @@
@Override
public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
return mMaxUpdatesPerInterval - ps.getApiCallCount();
@@ -1902,9 +1919,10 @@
@Override
public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
return getNextResetTimeLocked();
}
}
@@ -1921,7 +1939,6 @@
@Override
public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
verifyCaller(packageName, userId);
- throwIfUserLocked(userId);
Preconditions.checkNotNull(shortcutId);
@@ -1931,6 +1948,8 @@
}
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
ps.getUser().onCalledByPublisher(packageName);
@@ -1962,6 +1981,11 @@
void resetThrottlingInner(@UserIdInt int userId) {
synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ Log.w(TAG, "User " + userId + " is locked or not running");
+ return;
+ }
+
getUserShortcutsLocked(userId).resetThrottling();
}
scheduleSaveUser(userId);
@@ -1976,25 +2000,23 @@
Slog.i(TAG, "ShortcutManager: throttling counter reset for all users");
}
- void resetPackageThrottling(String packageName, int userId) {
- synchronized (mLock) {
- getPackageShortcutsLocked(packageName, userId)
- .resetRateLimitingForCommandLineNoSaving();
- saveUserLocked(userId);
- }
- }
-
@Override
public void onApplicationActive(String packageName, int userId) {
if (DEBUG) {
Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
}
enforceResetThrottlingPermission();
- if (!isUserUnlocked(userId)) {
- // This is called by system UI, so no need to throw. Just ignore.
- return;
+
+ synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ // This is called by system UI, so no need to throw. Just ignore.
+ return;
+ }
+
+ getPackageShortcutsLocked(packageName, userId)
+ .resetRateLimitingForCommandLineNoSaving();
+ saveUserLocked(userId);
}
- resetPackageThrottling(packageName, userId);
}
// We override this method in unit tests to do a simpler check.
@@ -2011,9 +2033,9 @@
// even when hasShortcutPermission() is overridden.
@VisibleForTesting
boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
- throwIfUserLocked(userId);
-
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
final ShortcutUser user = getUserShortcutsLocked(userId);
// Always trust the in-memory cache.
@@ -2170,9 +2192,6 @@
@Nullable ComponentName componentName,
int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
- if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
- return ret;
- }
final boolean cloneKeyFieldOnly =
((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
@@ -2183,6 +2202,9 @@
}
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2251,11 +2273,10 @@
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
- if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
- return false;
- }
-
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2271,9 +2292,8 @@
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
- if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
- return null;
- }
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
final ShortcutPackage p = getUserShortcutsLocked(userId)
.getPackageShortcutsIfExists(packageName);
@@ -2296,11 +2316,10 @@
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
- if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
- return;
- }
-
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
final ShortcutLauncher launcher =
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
launcher.attemptToRestoreIfNeededAndSave();
@@ -2320,11 +2339,10 @@
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
- if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
- return null;
- }
-
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2354,11 +2372,10 @@
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
- if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
- return 0;
- }
-
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2382,11 +2399,10 @@
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
- if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
- return null;
- }
-
synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
@@ -2418,9 +2434,6 @@
@Override
public boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage) {
- if (!isUserUnlocked(launcherUserId)) {
- return false;
- }
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
}
@@ -2431,8 +2444,12 @@
if (!mBootCompleted.get()) {
return; // Boot not completed, ignore the broadcast.
}
- if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
- handleLocaleChanged();
+ try {
+ if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+ handleLocaleChanged();
+ }
+ } catch (Exception e) {
+ wtf("Exception in mReceiver.onReceive", e);
}
}
};
@@ -2443,11 +2460,13 @@
}
scheduleSaveBaseState();
- final long token = injectClearCallingIdentity();
- try {
- forEachLoadedUserLocked(user -> user.detectLocaleChange());
- } finally {
- injectRestoreCallingIdentity(token);
+ synchronized (mLock) {
+ final long token = injectClearCallingIdentity();
+ try {
+ forEachLoadedUserLocked(user -> user.detectLocaleChange());
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
}
}
@@ -2470,18 +2489,17 @@
// but we still check it in unit tests.
final long token = injectClearCallingIdentity();
try {
-
- if (!isUserUnlocked(userId)) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring package broadcast " + action
- + " for locked/stopped user " + userId);
- }
- return;
- }
-
- // Whenever we get one of those package broadcasts, or get
- // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache.
synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring package broadcast " + action
+ + " for locked/stopped user " + userId);
+ }
+ return;
+ }
+
+ // Whenever we get one of those package broadcasts, or get
+ // ACTION_PREFERRED_ACTIVITY_CHANGED, we purge the default launcher cache.
final ShortcutUser user = getUserShortcutsLocked(userId);
user.clearLauncher();
}
@@ -2521,6 +2539,8 @@
handlePackageDataCleared(packageName, userId);
break;
}
+ } catch (Exception e) {
+ wtf("Exception in mPackageMonitor.onReceive", e);
} finally {
injectRestoreCallingIdentity(token);
}
@@ -3031,9 +3051,14 @@
Slog.d(TAG, "Backing up user " + userId);
}
synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ wtf("Can't backup: user " + userId + " is locked or not running");
+ return null;
+ }
+
final ShortcutUser user = getUserShortcutsLocked(userId);
if (user == null) {
- Slog.w(TAG, "Can't backup: user not found: id=" + userId);
+ wtf("Can't backup: user not found: id=" + userId);
return null;
}
@@ -3058,15 +3083,19 @@
if (DEBUG) {
Slog.d(TAG, "Restoring user " + userId);
}
- final ShortcutUser user;
- final ByteArrayInputStream is = new ByteArrayInputStream(payload);
- try {
- user = loadUserInternal(userId, is, /* fromBackup */ true);
- } catch (XmlPullParserException | IOException e) {
- Slog.w(TAG, "Restoration failed.", e);
- return;
- }
synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ wtf("Can't restore: user " + userId + " is locked or not running");
+ return;
+ }
+ final ShortcutUser user;
+ final ByteArrayInputStream is = new ByteArrayInputStream(payload);
+ try {
+ user = loadUserInternal(userId, is, /* fromBackup */ true);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.w(TAG, "Restoration failed.", e);
+ return;
+ }
mUsers.put(userId, user);
// Then purge all the save images.
@@ -3276,7 +3305,7 @@
private int mUserId = UserHandle.USER_SYSTEM;
- private void parseOptions(boolean takeUser)
+ private void parseOptionsLocked(boolean takeUser)
throws CommandException {
String opt;
while ((opt = getNextOption()) != null) {
@@ -3284,7 +3313,7 @@
case "--user":
if (takeUser) {
mUserId = UserHandle.parseUserArg(getNextArgRequired());
- if (!isUserUnlocked(mUserId)) {
+ if (!isUserUnlockedL(mUserId)) {
throw new CommandException(
"User " + mUserId + " is not running or locked");
}
@@ -3376,11 +3405,13 @@
}
private void handleResetThrottling() throws CommandException {
- parseOptions(/* takeUser =*/ true);
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
- Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
+ Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
- resetThrottlingInner(mUserId);
+ resetThrottlingInner(mUserId);
+ }
}
private void handleResetAllThrottling() {
@@ -3426,34 +3457,42 @@
}
private void handleClearDefaultLauncher() throws CommandException {
- parseOptions(/* takeUser =*/ true);
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
- clearLauncher();
+ clearLauncher();
+ }
}
private void handleGetDefaultLauncher() throws CommandException {
- parseOptions(/* takeUser =*/ true);
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
- clearLauncher();
- showLauncher();
+ clearLauncher();
+ showLauncher();
+ }
}
private void handleUnloadUser() throws CommandException {
- parseOptions(/* takeUser =*/ true);
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
- Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
+ Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
- ShortcutService.this.handleCleanupUser(mUserId);
+ ShortcutService.this.handleCleanupUser(mUserId);
+ }
}
private void handleClearShortcuts() throws CommandException {
- parseOptions(/* takeUser =*/ true);
- final String packageName = getNextArgRequired();
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
+ final String packageName = getNextArgRequired();
- Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
+ Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
- ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
- /* appStillExists = */ true);
+ ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
+ /* appStillExists = */ true);
+ }
}
private void handleVerifyStates() throws CommandException {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e17d243..e489d96 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -27,6 +27,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
@@ -37,6 +38,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -2940,13 +2942,17 @@
if (!TextUtils.isEmpty(demoLauncher)) {
ComponentName componentToEnable = ComponentName.unflattenFromString(demoLauncher);
String demoLauncherPkg = componentToEnable.getPackageName();
- final PackageManager pm = mContext.getPackageManager();
- pm.setComponentEnabledSettingAsUser(componentToEnable,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
- /* userId= */ userId);
- pm.setApplicationEnabledSettingAsUser(demoLauncherPkg,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
- /* userId= */ userId);
+ try {
+ final IPackageManager iPm = AppGlobals.getPackageManager();
+ iPm.setComponentEnabledSetting(componentToEnable,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
+ /* userId= */ userId);
+ iPm.setApplicationEnabledSetting(demoLauncherPkg,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
+ /* userId= */ userId, null);
+ } catch (RemoteException re) {
+ // Internal, shouldn't happen
+ }
}
}
}
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 407d315..1a9d2f2 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -45,7 +45,9 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@@ -109,6 +111,11 @@
private final byte[] mRA = new byte[IPV6_MIN_MTU];
@GuardedBy("mLock")
private int mRaLength;
+ @GuardedBy("mLock")
+ private final HashMap<IpPrefix, Integer> mDeprecatedPrefixes;
+
+ @GuardedBy("mLock")
+ private RaParams mRaParams;
private volatile FileDescriptor mSocket;
private volatile MulticastTransmitter mMulticastTransmitter;
@@ -141,45 +148,29 @@
mIfIndex = ifindex;
mHwAddr = hwaddr;
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
+ mDeprecatedPrefixes = new HashMap<>();
}
- public void buildNewRa(RaParams params) {
+ public void buildNewRa(RaParams params, HashSet<IpPrefix> newlyDeprecated) {
+ if (newlyDeprecated != null) {
+ synchronized (mLock) {
+ for (IpPrefix ipp : newlyDeprecated) {
+ mDeprecatedPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
+ }
+ }
+ }
+
+ // TODO: Send MAX_URGENT_RTR_ADVERTISEMENTS zero router lifetime RAs,
+ // iff. we have already sent an RA.
if (params == null || params.prefixes.isEmpty()) {
// No RA to be served at this time.
clearRa();
return;
}
- if (params.mtu < IPV6_MIN_MTU) {
- params.mtu = IPV6_MIN_MTU;
- }
-
- final ByteBuffer ra = ByteBuffer.wrap(mRA);
- ra.order(ByteOrder.BIG_ENDIAN);
-
synchronized (mLock) {
- try {
- putHeader(ra, params.hasDefaultRoute);
- putSlla(ra, mHwAddr);
- // https://tools.ietf.org/html/rfc5175#section-4 says:
- //
- // "MUST NOT be added to a Router Advertisement message
- // if no flags in the option are set."
- //
- // putExpandedFlagsOption(ra);
- putMtu(ra, params.mtu);
- for (IpPrefix ipp : params.prefixes) {
- putPio(ra, ipp);
- }
- if (params.dnses.size() > 0) {
- putRdnss(ra, params.dnses);
- }
- mRaLength = ra.position();
- } catch (BufferOverflowException e) {
- Log.e(TAG, "Could not construct new RA: " + e);
- mRaLength = 0;
- return;
- }
+ mRaParams = params;
+ assembleRa();
}
maybeNotifyMulticastTransmitter();
@@ -216,6 +207,64 @@
}
}
+ private void assembleRa() {
+ final ByteBuffer ra = ByteBuffer.wrap(mRA);
+ ra.order(ByteOrder.BIG_ENDIAN);
+
+ synchronized (mLock) {
+ try {
+ putHeader(ra, mRaParams.hasDefaultRoute);
+
+ putSlla(ra, mHwAddr);
+
+ // https://tools.ietf.org/html/rfc5175#section-4 says:
+ //
+ // "MUST NOT be added to a Router Advertisement message
+ // if no flags in the option are set."
+ //
+ // putExpandedFlagsOption(ra);
+
+ putMtu(ra, mRaParams.mtu);
+
+ for (IpPrefix ipp : mRaParams.prefixes) {
+ putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
+ mDeprecatedPrefixes.remove(ipp);
+ }
+
+ for (IpPrefix ipp : mDeprecatedPrefixes.keySet()) {
+ putPio(ra, ipp, 0, 0);
+ }
+
+ if (mRaParams.dnses.size() > 0) {
+ putRdnss(ra, mRaParams.dnses);
+ }
+
+ mRaLength = ra.position();
+ } catch (BufferOverflowException e) {
+ Log.e(TAG, "Could not construct new RA: " + e);
+ mRaLength = 0;
+ return;
+ }
+ }
+ }
+
+ private int decrementDeprecatedPrefixes() {
+ int removed = 0;
+
+ synchronized (mLock) {
+ for (Map.Entry<IpPrefix, Integer> kv : mDeprecatedPrefixes.entrySet()) {
+ if (kv.getValue() == 0) {
+ mDeprecatedPrefixes.remove(kv.getKey());
+ removed++;
+ } else {
+ kv.setValue(kv.getValue() - 1);
+ }
+ }
+ }
+
+ return removed;
+ }
+
private void maybeNotifyMulticastTransmitter() {
final MulticastTransmitter m = mMulticastTransmitter;
if (m != null) {
@@ -325,10 +374,11 @@
ra.put(ND_OPTION_MTU)
.put(MTU_NUM_8OCTETS)
.putShort(asShort(0))
- .putInt(mtu);
+ .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
}
- private static void putPio(ByteBuffer ra, IpPrefix ipp) {
+ private static void putPio(ByteBuffer ra, IpPrefix ipp,
+ int validTime, int preferredTime) {
/**
Prefix Information
@@ -359,13 +409,17 @@
final byte ND_OPTION_PIO = 3;
final byte PIO_NUM_8OCTETS = 4;
+ if (validTime < 0) validTime = 0;
+ if (preferredTime < 0) preferredTime = 0;
+ if (preferredTime > validTime) preferredTime = validTime;
+
final byte[] addr = ipp.getAddress().getAddress();
ra.put(ND_OPTION_PIO)
.put(PIO_NUM_8OCTETS)
.put(asByte(prefixLength))
- .put(asByte(0xc0)) // L&A set
- .putInt(DEFAULT_LIFETIME)
- .putInt(DEFAULT_LIFETIME)
+ .put(asByte(0xc0)) /* L & A set */
+ .putInt(validTime)
+ .putInt(preferredTime)
.putInt(0)
.put(addr);
}
@@ -547,6 +601,11 @@
}
maybeSendRA(mAllNodes);
+ if (decrementDeprecatedPrefixes() > 0) {
+ // At least one deprecated PIO has been removed;
+ // reassemble the RA.
+ assembleRa();
+ }
}
}
@@ -560,15 +619,17 @@
}
private int getNextMulticastTransmitDelaySec() {
+ int countDeprecatedPrefixes = 0;
synchronized (mLock) {
if (mRaLength < MIN_RA_HEADER_SIZE) {
// No actual RA to send; just sleep for 1 day.
return DAY_IN_SECONDS;
}
+ countDeprecatedPrefixes = mDeprecatedPrefixes.size();
}
final int urgentPending = mUrgentAnnouncements.getAndDecrement();
- if (urgentPending > 0) {
+ if (urgentPending > 0 || countDeprecatedPrefixes > 0) {
return MIN_DELAY_BETWEEN_RAS_SEC;
}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index ef9739d..a247056 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -42,6 +42,7 @@
import android.test.AndroidTestCase;
import android.test.mock.MockContext;
import android.test.mock.MockPackageManager;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import java.io.File;
@@ -76,7 +77,7 @@
super.tearDown();
}
- public class AccountSorter implements Comparator<Account> {
+ class AccountSorter implements Comparator<Account> {
public int compare(Account object1, Account object2) {
if (object1 == object2) return 0;
if (object1 == null) return 1;
@@ -87,6 +88,7 @@
}
}
+ @SmallTest
public void testCheckAddAccount() throws Exception {
unlockSystemUser();
Account a11 = new Account("account1", "type1");
@@ -128,6 +130,7 @@
assertEquals(a31, accounts[1]);
}
+ @SmallTest
public void testPasswords() throws Exception {
unlockSystemUser();
Account a11 = new Account("account1", "type1");
@@ -144,6 +147,7 @@
assertEquals("p12", mAms.getPassword(a12));
}
+ @SmallTest
public void testUserdata() throws Exception {
unlockSystemUser();
Account a11 = new Account("account1", "type1");
@@ -177,6 +181,7 @@
assertNull(mAms.getUserData(a12, "c"));
}
+ @SmallTest
public void testAuthtokens() throws Exception {
unlockSystemUser();
Account a11 = new Account("account1", "type1");
@@ -211,6 +216,7 @@
assertNull(mAms.peekAuthToken(a12, "att2"));
}
+ @SmallTest
public void testRemovedAccountSync() throws Exception {
unlockSystemUser();
Account a1 = new Account("account1", "type1");
@@ -242,6 +248,7 @@
assertEquals("Only a2 should be returned", a2, accounts[0]);
}
+ @SmallTest
public void testPreNDatabaseMigration() throws Exception {
String preNDatabaseName = mAms.getPreNDatabaseName(UserHandle.USER_SYSTEM);
Context originalContext = ((MyMockContext) getContext()).mTestContext;
@@ -296,10 +303,10 @@
return intent;
}
- static public class MockAccountAuthenticatorCache implements IAccountAuthenticatorCache {
+ static class MockAccountAuthenticatorCache implements IAccountAuthenticatorCache {
private ArrayList<ServiceInfo<AuthenticatorDescription>> mServices;
- public MockAccountAuthenticatorCache() {
+ MockAccountAuthenticatorCache() {
mServices = new ArrayList<>();
AuthenticatorDescription d1 = new AuthenticatorDescription("type1", "p1", 0, 0, 0, 0);
AuthenticatorDescription d2 = new AuthenticatorDescription("type2", "p2", 0, 0, 0, 0);
@@ -339,13 +346,13 @@
}
}
- static public class MyMockContext extends MockContext {
+ static class MyMockContext extends MockContext {
private Context mTestContext;
private AppOpsManager mAppOpsManager;
private UserManager mUserManager;
private PackageManager mPackageManager;
- public MyMockContext(Context testContext) {
+ MyMockContext(Context testContext) {
this.mTestContext = testContext;
this.mAppOpsManager = mock(AppOpsManager.class);
this.mUserManager = mock(UserManager.class);
@@ -411,16 +418,16 @@
}
}
- static public class MyMockPackageManager extends MockPackageManager {
+ static class MyMockPackageManager extends MockPackageManager {
@Override
public int checkSignatures(final int uid1, final int uid2) {
return PackageManager.SIGNATURE_MATCH;
}
}
- static public class MyAccountManagerService extends AccountManagerService {
+ static class MyAccountManagerService extends AccountManagerService {
private Context mRealTestContext;
- public MyAccountManagerService(Context context, PackageManager packageManager,
+ MyAccountManagerService(Context context, PackageManager packageManager,
IAccountAuthenticatorCache authenticatorCache, Context realTestContext) {
super(context, packageManager, authenticatorCache);
this.mRealTestContext = realTestContext;
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 0515a9a..d003e56 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -246,6 +246,20 @@
}
@Override
+ protected boolean isUserUnlockedL(@UserIdInt int userId) {
+ // Note due to a late change, now ShortcutManager doesn't use
+ // UserManager.isUserUnlockingOrUnlocked(). But all unit tests are still using it,
+ // so we convert here.
+
+ final long token = injectClearCallingIdentity();
+ try {
+ return mMockUserManager.isUserUnlockingOrUnlocked(userId);
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
+ }
+
+ @Override
int injectDipToPixel(int dip) {
return dip;
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 4eae4ab..330dbab 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -752,13 +752,6 @@
throw new UnsupportedOperationException();
}
- /** @hide - hidden in superclass */
- @Override
- public void setComponentEnabledSettingAsUser(ComponentName componentName,
- int newState, int flags, int userId) {
- throw new UnsupportedOperationException();
- }
-
@Override
public int getComponentEnabledSetting(ComponentName componentName) {
throw new UnsupportedOperationException();
@@ -769,13 +762,6 @@
throw new UnsupportedOperationException();
}
- /** @hide - hidden in superclass */
- @Override
- public void setApplicationEnabledSettingAsUser(String packageName, int newState, int flags,
- int userId) {
- throw new UnsupportedOperationException();
- }
-
@Override
public int getApplicationEnabledSetting(String packageName) {
throw new UnsupportedOperationException();
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index fe886de..6aa9c0e 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -231,6 +231,7 @@
std::string* outError) {
const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
if (!symbol) {
+ if (outError) *outError = "not found";
return {};
}
diff --git a/tools/aapt2/public_attr_map.py b/tools/aapt2/tools/public_attr_map.py
similarity index 100%
rename from tools/aapt2/public_attr_map.py
rename to tools/aapt2/tools/public_attr_map.py
diff --git a/tools/aapt2/remove-duplicates.py b/tools/aapt2/tools/remove-duplicates.py
similarity index 100%
rename from tools/aapt2/remove-duplicates.py
rename to tools/aapt2/tools/remove-duplicates.py
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 3c0e9bde..e743247 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -434,7 +434,11 @@
std::u16string utf16;
utf16.resize(utf16Length);
- utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(), &*utf16.begin());
+ utf8_to_utf16(
+ reinterpret_cast<const uint8_t*>(utf8.data()),
+ utf8.length(),
+ &*utf16.begin(),
+ (size_t) utf16Length + 1);
return utf16;
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 1d5ac0c..9da65a6 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -638,7 +638,15 @@
}
}
- // not a direct id valid reference? resolve it
+ // not a direct id valid reference. First check if it's an enum (this is a corner case
+ // for attributes that have a reference|enum type), then fallback to resolve
+ // as an ID without prefix.
+ Integer enumValue = resolveEnumAttribute(index);
+ if (enumValue != null) {
+ return enumValue;
+ }
+
+ // Ok, not an enum, resolve as an ID
Integer idValue;
if (resValue.isFramework()) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index cef622b..b3ed9e1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -712,11 +712,6 @@
}
@Override
- public void setComponentEnabledSettingAsUser(ComponentName componentName, int newState,
- int flags, int userId) {
- }
-
- @Override
public int getComponentEnabledSetting(ComponentName componentName) {
return 0;
}
@@ -726,11 +721,6 @@
}
@Override
- public void setApplicationEnabledSettingAsUser(String packageName, int newState, int flags,
- int userId) {
- }
-
- @Override
public int getApplicationEnabledSetting(String packageName) {
return 0;
}