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 @android.ravenwood.annotation.RavenwoodKeepWholeClass 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 @NonNull String name; 50 public final @NonNull 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(@onNull String name, @NonNull String type)69 public Account(@NonNull String name, @NonNull 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(@onNull String name, @NonNull String type, String accessId)83 public Account(@NonNull String name, @NonNull 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 this.name = name; 91 this.type = type; 92 this.accessId = accessId; 93 } 94 Account(Parcel in)95 public Account(Parcel in) { 96 this.name = in.readString(); 97 this.type = in.readString(); 98 if (TextUtils.isEmpty(name)) { 99 throw new android.os.BadParcelableException("the name must not be empty: " + name); 100 } 101 if (TextUtils.isEmpty(type)) { 102 throw new android.os.BadParcelableException("the type must not be empty: " + type); 103 } 104 this.accessId = in.readString(); 105 if (accessId != null) { 106 synchronized (sAccessedAccounts) { 107 if (sAccessedAccounts.add(this)) { 108 onAccountAccessed(accessId); 109 } 110 } 111 } 112 } 113 114 @android.ravenwood.annotation.RavenwoodReplace onAccountAccessed(String accessId)115 private static void onAccountAccessed(String accessId) { 116 try { 117 IAccountManager accountManager = IAccountManager.Stub.asInterface( 118 ServiceManager.getService(Context.ACCOUNT_SERVICE)); 119 accountManager.onAccountAccessed(accessId); 120 } catch (RemoteException e) { 121 Log.e(TAG, "Error noting account access", e); 122 } 123 } 124 onAccountAccessed$ravenwood(String accessId)125 private static void onAccountAccessed$ravenwood(String accessId) { 126 // No AccountManager to communicate with; ignored 127 } 128 129 /** @hide */ getAccessId()130 public String getAccessId() { 131 return accessId; 132 } 133 describeContents()134 public int describeContents() { 135 return 0; 136 } 137 writeToParcel(Parcel dest, int flags)138 public void writeToParcel(Parcel dest, int flags) { 139 dest.writeString(name); 140 dest.writeString(type); 141 dest.writeString(accessId); 142 } 143 144 public static final @android.annotation.NonNull Creator<Account> CREATOR = new Creator<Account>() { 145 public Account createFromParcel(Parcel source) { 146 return new Account(source); 147 } 148 149 public Account[] newArray(int size) { 150 return new Account[size]; 151 } 152 }; 153 toString()154 public String toString() { 155 return "Account {name=" + name + ", type=" + type + "}"; 156 } 157 158 /** 159 * Return a string representation of the account that is safe to print 160 * to logs and other places where PII should be avoided. 161 * @hide 162 */ toSafeString()163 public String toSafeString() { 164 if (mSafeName == null) { 165 mSafeName = toSafeName(name, 'x'); 166 } 167 return "Account {name=" + mSafeName + ", type=" + type + "}"; 168 } 169 170 /** 171 * Given a name, replace all letter or digits with the replacement char. 172 * @param name The input name string. 173 * @param replacement the replacement character. 174 * @return the string after replacement. 175 * @hide 176 */ toSafeName(String name, char replacement)177 public static String toSafeName(String name, char replacement) { 178 final StringBuilder builder = new StringBuilder(64); 179 final int len = name.length(); 180 for (int i = 0; i < len; i++) { 181 final char c = name.charAt(i); 182 if (Character.isLetterOrDigit(c)) { 183 builder.append(replacement); 184 } else { 185 builder.append(c); 186 } 187 } 188 return builder.toString(); 189 } 190 } 191