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.Set; 35 36 /** 37 * Value type that represents an Account in the {@link AccountManager}. This object is 38 * {@link Parcelable} and also overrides {@link #equals} and {@link #hashCode}, making it 39 * suitable for use as the key of a {@link java.util.Map} 40 */ 41 public class Account implements Parcelable { 42 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 43 private static final String TAG = "Account"; 44 45 @GuardedBy("sAccessedAccounts") 46 private static final Set<Account> sAccessedAccounts = new ArraySet<>(); 47 48 public final String name; 49 public final String type; 50 private String mSafeName; 51 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 52 private final @Nullable String accessId; 53 equals(@ullable Object o)54 public boolean equals(@Nullable Object o) { 55 if (o == this) return true; 56 if (!(o instanceof Account)) return false; 57 final Account other = (Account)o; 58 return name.equals(other.name) && type.equals(other.type); 59 } 60 hashCode()61 public int hashCode() { 62 int result = 17; 63 result = 31 * result + name.hashCode(); 64 result = 31 * result + type.hashCode(); 65 return result; 66 } 67 Account(String name, String type)68 public Account(String name, String type) { 69 this(name, type, null); 70 } 71 72 /** 73 * @hide 74 */ Account(@onNull Account other, @NonNull String accessId)75 public Account(@NonNull Account other, @NonNull String accessId) { 76 this(other.name, other.type, accessId); 77 } 78 79 /** 80 * @hide 81 */ Account(String name, String type, String accessId)82 public Account(String name, String type, String accessId) { 83 if (TextUtils.isEmpty(name)) { 84 throw new IllegalArgumentException("the name must not be empty: " + name); 85 } 86 if (TextUtils.isEmpty(type)) { 87 throw new IllegalArgumentException("the type must not be empty: " + type); 88 } 89 this.name = name; 90 this.type = type; 91 this.accessId = accessId; 92 } 93 Account(Parcel in)94 public Account(Parcel in) { 95 this.name = in.readString(); 96 this.type = in.readString(); 97 if (TextUtils.isEmpty(name)) { 98 throw new android.os.BadParcelableException("the name must not be empty: " + name); 99 } 100 if (TextUtils.isEmpty(type)) { 101 throw new android.os.BadParcelableException("the type must not be empty: " + type); 102 } 103 this.accessId = in.readString(); 104 if (accessId != null) { 105 synchronized (sAccessedAccounts) { 106 if (sAccessedAccounts.add(this)) { 107 try { 108 IAccountManager accountManager = IAccountManager.Stub.asInterface( 109 ServiceManager.getService(Context.ACCOUNT_SERVICE)); 110 accountManager.onAccountAccessed(accessId); 111 } catch (RemoteException e) { 112 Log.e(TAG, "Error noting account access", e); 113 } 114 } 115 } 116 } 117 } 118 119 /** @hide */ getAccessId()120 public String getAccessId() { 121 return accessId; 122 } 123 describeContents()124 public int describeContents() { 125 return 0; 126 } 127 writeToParcel(Parcel dest, int flags)128 public void writeToParcel(Parcel dest, int flags) { 129 dest.writeString(name); 130 dest.writeString(type); 131 dest.writeString(accessId); 132 } 133 134 public static final @android.annotation.NonNull Creator<Account> CREATOR = new Creator<Account>() { 135 public Account createFromParcel(Parcel source) { 136 return new Account(source); 137 } 138 139 public Account[] newArray(int size) { 140 return new Account[size]; 141 } 142 }; 143 toString()144 public String toString() { 145 return "Account {name=" + name + ", type=" + type + "}"; 146 } 147 148 /** 149 * Return a string representation of the account that is safe to print 150 * to logs and other places where PII should be avoided. 151 * @hide 152 */ toSafeString()153 public String toSafeString() { 154 if (mSafeName == null) { 155 mSafeName = toSafeName(name, 'x'); 156 } 157 return "Account {name=" + mSafeName + ", type=" + type + "}"; 158 } 159 160 /** 161 * Given a name, replace all letter or digits with the replacement char. 162 * @param name The input name string. 163 * @param replacement the replacement character. 164 * @return the string after replacement. 165 * @hide 166 */ toSafeName(String name, char replacement)167 public static String toSafeName(String name, char replacement) { 168 final StringBuilder builder = new StringBuilder(64); 169 final int len = name.length(); 170 for (int i = 0; i < len; i++) { 171 final char c = name.charAt(i); 172 if (Character.isLetterOrDigit(c)) { 173 builder.append(replacement); 174 } else { 175 builder.append(c); 176 } 177 } 178 return builder.toString(); 179 } 180 } 181