1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.accounts; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.content.Context; 23 import android.os.Build; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.os.RemoteException; 27 import android.os.ServiceManager; 28 import android.text.TextUtils; 29 import android.util.ArraySet; 30 import android.util.Log; 31 32 import com.android.internal.annotations.GuardedBy; 33 34 import java.util.Objects; 35 import java.util.Set; 36 37 /** 38 * Value type that represents an Account in the {@link AccountManager}. This object is 39 * {@link Parcelable} and also overrides {@link #equals} and {@link #hashCode}, making it 40 * suitable for use as the key of a {@link java.util.Map} 41 */ 42 public class Account implements Parcelable { 43 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 44 private static final String TAG = "Account"; 45 46 @GuardedBy("sAccessedAccounts") 47 private static final Set<Account> sAccessedAccounts = new ArraySet<>(); 48 49 public final String name; 50 public final String type; 51 private String mSafeName; 52 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 53 private final @Nullable String accessId; 54 equals(@ullable Object o)55 public boolean equals(@Nullable Object o) { 56 if (o == this) return true; 57 if (!(o instanceof Account)) return false; 58 final Account other = (Account)o; 59 return name.equals(other.name) && type.equals(other.type); 60 } 61 hashCode()62 public int hashCode() { 63 int result = 17; 64 result = 31 * result + name.hashCode(); 65 result = 31 * result + type.hashCode(); 66 return result; 67 } 68 Account(String name, String type)69 public Account(String name, String type) { 70 this(name, type, null); 71 } 72 73 /** 74 * @hide 75 */ Account(@onNull Account other, @NonNull String accessId)76 public Account(@NonNull Account other, @NonNull String accessId) { 77 this(other.name, other.type, accessId); 78 } 79 80 /** 81 * @hide 82 */ Account(String name, String type, String accessId)83 public Account(String name, String type, String accessId) { 84 if (TextUtils.isEmpty(name)) { 85 throw new IllegalArgumentException("the name must not be empty: " + name); 86 } 87 if (TextUtils.isEmpty(type)) { 88 throw new IllegalArgumentException("the type must not be empty: " + type); 89 } 90 if (name.length() > 200) { 91 throw new IllegalArgumentException("account name is longer than 200 characters"); 92 } 93 if (type.length() > 200) { 94 throw new IllegalArgumentException("account type is longer than 200 characters"); 95 } 96 this.name = name; 97 this.type = type; 98 this.accessId = accessId; 99 } 100 Account(Parcel in)101 public Account(Parcel in) { 102 this.name = in.readString(); 103 this.type = in.readString(); 104 if (TextUtils.isEmpty(name)) { 105 throw new android.os.BadParcelableException("the name must not be empty: " + name); 106 } 107 if (TextUtils.isEmpty(type)) { 108 throw new android.os.BadParcelableException("the type must not be empty: " + type); 109 } 110 this.accessId = in.readString(); 111 if (accessId != null) { 112 synchronized (sAccessedAccounts) { 113 if (sAccessedAccounts.add(this)) { 114 try { 115 IAccountManager accountManager = IAccountManager.Stub.asInterface( 116 ServiceManager.getService(Context.ACCOUNT_SERVICE)); 117 accountManager.onAccountAccessed(accessId); 118 } catch (RemoteException e) { 119 Log.e(TAG, "Error noting account access", e); 120 } 121 } 122 } 123 } 124 } 125 126 /** @hide */ getAccessId()127 public String getAccessId() { 128 return accessId; 129 } 130 describeContents()131 public int describeContents() { 132 return 0; 133 } 134 writeToParcel(Parcel dest, int flags)135 public void writeToParcel(Parcel dest, int flags) { 136 dest.writeString(name); 137 dest.writeString(type); 138 dest.writeString(accessId); 139 } 140 141 public static final @android.annotation.NonNull Creator<Account> CREATOR = new Creator<Account>() { 142 public Account createFromParcel(Parcel source) { 143 return new Account(source); 144 } 145 146 public Account[] newArray(int size) { 147 return new Account[size]; 148 } 149 }; 150 toString()151 public String toString() { 152 return "Account {name=" + name + ", type=" + type + "}"; 153 } 154 155 /** 156 * Return a string representation of the account that is safe to print 157 * to logs and other places where PII should be avoided. 158 * @hide 159 */ toSafeString()160 public String toSafeString() { 161 if (mSafeName == null) { 162 mSafeName = toSafeName(name, 'x'); 163 } 164 return "Account {name=" + mSafeName + ", type=" + type + "}"; 165 } 166 167 /** 168 * Given a name, replace all letter or digits with the replacement char. 169 * @param name The input name string. 170 * @param replacement the replacement character. 171 * @return the string after replacement. 172 * @hide 173 */ toSafeName(String name, char replacement)174 public static String toSafeName(String name, char replacement) { 175 final StringBuilder builder = new StringBuilder(64); 176 final int len = name.length(); 177 for (int i = 0; i < len; i++) { 178 final char c = name.charAt(i); 179 if (Character.isLetterOrDigit(c)) { 180 builder.append(replacement); 181 } else { 182 builder.append(c); 183 } 184 } 185 return builder.toString(); 186 } 187 } 188