Merge "Obfuscate account name in account manager logs and dumpsys."
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index f07f5ec..d3b2238 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -20,13 +20,14 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
+
 import com.android.internal.annotations.GuardedBy;
 
 import java.util.Set;
@@ -45,6 +46,7 @@
 
     public final String name;
     public final String type;
+    private String mSafeName;
     @UnsupportedAppUsage
     private final @Nullable String accessId;
 
@@ -135,4 +137,37 @@
     public String toString() {
         return "Account {name=" + name + ", type=" + type + "}";
     }
+
+    /**
+     * Return a string representation of the account that is safe to print
+     * to logs and other places where PII should be avoided.
+     * @hide
+     */
+    public String toSafeString() {
+        if (mSafeName == null) {
+            mSafeName = toSafeName(name, 'x');
+        }
+        return "Account {name=" + mSafeName + ", type=" + type + "}";
+    }
+
+    /**
+     * Given a name, replace all letter or digits with the replacement char.
+     * @param name The input name string.
+     * @param replacement the replacement character.
+     * @return the string after replacement.
+     * @hide
+     */
+    public static String toSafeName(String name, char replacement) {
+        final StringBuilder builder = new StringBuilder(64);
+        final int len = name.length();
+        for (int i = 0; i < len; i++) {
+            final char c = name.charAt(i);
+            if (Character.isLetterOrDigit(c)) {
+                builder.append(replacement);
+            } else {
+                builder.append(c);
+            }
+        }
+        return builder.toString();
+    }
 }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index fd32b5a..a392b51 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1185,8 +1185,8 @@
                         final long accountId = accountEntry.getKey();
                         final Account account = accountEntry.getValue();
                         if (obsoleteAuthType.contains(account.type)) {
-                            Slog.w(TAG, "deleting account " + account.name + " because type "
-                                    + account.type
+                            Slog.w(TAG, "deleting account " + account.toSafeString()
+                                    + " because type " + account.type
                                     + "'s registered authenticator no longer exist.");
                             Map<String, Integer> packagesToVisibility =
                                     getRequestingPackages(account, accounts);
@@ -1326,7 +1326,8 @@
         Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
         List<Account> accountsToRemove = accounts.accountsDb.findCeAccountsNotInDe();
         if (!accountsToRemove.isEmpty()) {
-            Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
+            Slog.i(TAG, accountsToRemove.size()
+                    + " accounts were previously deleted while user "
                     + accounts.userId + " was locked. Removing accounts from CE tables");
             logRecord(accounts, AccountsDb.DEBUG_ACTION_SYNC_DE_CE_ACCOUNTS,
                     AccountsDb.TABLE_ACCOUNTS);
@@ -1641,7 +1642,7 @@
             return;
         }
 
-        Slog.d(TAG, "Copying account " + account.name
+        Slog.d(TAG, "Copying account " + account.toSafeString()
                 + " from user " + userFrom + " to user " + userTo);
         long identityToken = clearCallingIdentity();
         try {
@@ -1777,8 +1778,8 @@
             return false;
         }
         if (!isLocalUnlockedUser(accounts.userId)) {
-            Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
-                    + " is locked. callingUid=" + callingUid);
+            Log.w(TAG, "Account " + account.toSafeString() + " cannot be added - user "
+                    + accounts.userId + " is locked. callingUid=" + callingUid);
             return false;
         }
         synchronized (accounts.dbLock) {
@@ -1786,19 +1787,19 @@
                 accounts.accountsDb.beginTransaction();
                 try {
                     if (accounts.accountsDb.findCeAccountId(account) >= 0) {
-                        Log.w(TAG, "insertAccountIntoDatabase: " + account
+                        Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
                                 + ", skipping since the account already exists");
                         return false;
                     }
                     long accountId = accounts.accountsDb.insertCeAccount(account, password);
                     if (accountId < 0) {
-                        Log.w(TAG, "insertAccountIntoDatabase: " + account
+                        Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
                                 + ", skipping the DB insert failed");
                         return false;
                     }
                     // Insert into DE table
                     if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) {
-                        Log.w(TAG, "insertAccountIntoDatabase: " + account
+                        Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
                                 + ", skipping the DB insert failed");
                         return false;
                     }
@@ -1806,7 +1807,8 @@
                         for (String key : extras.keySet()) {
                             final String value = extras.getString(key);
                             if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) {
-                                Log.w(TAG, "insertAccountIntoDatabase: " + account
+                                Log.w(TAG, "insertAccountIntoDatabase: "
+                                        + account.toSafeString()
                                         + ", skipping since insertExtra failed for key " + key);
                                 return false;
                             }
@@ -2282,7 +2284,8 @@
         boolean isChanged = false;
         boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
         if (!userUnlocked) {
-            Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
+            Slog.i(TAG, "Removing account " + account.toSafeString()
+                    + " while user " + accounts.userId
                     + " is still locked. CE data will be removed later");
         }
         synchronized (accounts.dbLock) {
@@ -2907,7 +2910,7 @@
                 protected String toDebugString(long now) {
                     if (loginOptions != null) loginOptions.keySet();
                     return super.toDebugString(now) + ", getAuthToken"
-                            + ", " + account
+                            + ", " + account.toSafeString()
                             + ", authTokenType " + authTokenType
                             + ", loginOptions " + loginOptions
                             + ", notifyOnAuthFailure " + notifyOnAuthFailure;
@@ -3669,7 +3672,7 @@
                 @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", confirmCredentials"
-                            + ", " + account;
+                            + ", " + account.toSafeString();
                 }
             }.bind();
         } finally {
@@ -3707,7 +3710,7 @@
                 protected String toDebugString(long now) {
                     if (loginOptions != null) loginOptions.keySet();
                     return super.toDebugString(now) + ", updateCredentials"
-                            + ", " + account
+                            + ", " + account.toSafeString()
                             + ", authTokenType " + authTokenType
                             + ", loginOptions " + loginOptions;
                 }
@@ -3771,7 +3774,7 @@
                         loginOptions.keySet();
                     return super.toDebugString(now)
                             + ", startUpdateCredentialsSession"
-                            + ", " + account
+                            + ", " + account.toSafeString()
                             + ", authTokenType " + authTokenType
                             + ", loginOptions " + loginOptions;
                 }
@@ -3812,7 +3815,7 @@
                 @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
-                            + ", " + account;
+                            + ", " + account.toSafeString();
                 }
 
                 @Override
@@ -4369,7 +4372,7 @@
         accounts.accountsDb.deleteSharedAccount(account);
         long accountId = accounts.accountsDb.insertSharedAccount(account);
         if (accountId < 0) {
-            Log.w(TAG, "insertAccountIntoDatabase: " + account
+            Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
                     + ", skipping the DB insert failed");
             return false;
         }
@@ -5208,7 +5211,7 @@
                     Process.SYSTEM_UID, null /* packageName */, false);
             fout.println("Accounts: " + accounts.length);
             for (Account account : accounts) {
-                fout.println("  " + account);
+                fout.println("  " + account.toSafeString());
             }
 
             // Add debug information.
@@ -5613,7 +5616,8 @@
                 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
                     // TODO: Skip this check when running automated tests. Replace this
                     // with a more general solution.
-                    Log.d(TAG, "no credentials permission for usage of " + account + ", "
+                    Log.d(TAG, "no credentials permission for usage of "
+                            + account.toSafeString() + ", "
                             + authTokenType + " by uid " + callerUid
                             + " but ignoring since device is in test harness.");
                     return true;