1 /* 2 * Copyright (C) 2019 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 com.android.car.settings.accounts; 18 19 import android.accounts.AccountManager; 20 import android.accounts.AuthenticatorDescription; 21 import android.content.Context; 22 import android.content.pm.PackageManager; 23 import android.graphics.drawable.Drawable; 24 import android.os.UserHandle; 25 26 import androidx.annotation.VisibleForTesting; 27 28 import com.android.settingslib.accounts.AuthenticatorHelper; 29 30 import java.util.Collections; 31 import java.util.HashSet; 32 import java.util.List; 33 import java.util.Objects; 34 import java.util.Set; 35 36 /** 37 * Utility that maintains a set of authorized account types. 38 */ 39 public class AccountTypesHelper { 40 /** Callback invoked when the set of authorized account types changes. */ 41 public interface OnChangeListener { 42 /** Called when the set of authorized account types changes. */ onAuthorizedAccountTypesChanged()43 void onAuthorizedAccountTypesChanged(); 44 } 45 46 private final Context mContext; 47 private UserHandle mUserHandle; 48 private AuthenticatorHelper mAuthenticatorHelper; 49 private List<String> mAuthorities; 50 private Set<String> mAccountTypesFilter; 51 private Set<String> mAccountTypesExclusionFilter; 52 private Set<String> mAuthorizedAccountTypes; 53 private OnChangeListener mListener; 54 AccountTypesHelper(Context context)55 public AccountTypesHelper(Context context) { 56 mContext = context; 57 58 // Default to hardcoded Bluetooth account type. 59 mAccountTypesExclusionFilter = new HashSet<>(); 60 mAccountTypesExclusionFilter.add("com.android.bluetooth.pbapsink"); 61 setAccountTypesExclusionFilter(mAccountTypesExclusionFilter); 62 63 mUserHandle = UserHandle.of(UserHandle.myUserId()); 64 mAuthenticatorHelper = new AuthenticatorHelper(mContext, mUserHandle, 65 userHandle -> { 66 // Only force a refresh if accounts have changed for the current user. 67 if (userHandle.equals(mUserHandle)) { 68 updateAuthorizedAccountTypes(false /* isForced */); 69 } 70 }); 71 } 72 73 /** Sets the authorities that the user has. */ setAuthorities(List<String> authorities)74 public void setAuthorities(List<String> authorities) { 75 mAuthorities = authorities; 76 } 77 78 /** Sets the filter for accounts that should be shown. */ setAccountTypesFilter(Set<String> accountTypesFilter)79 public void setAccountTypesFilter(Set<String> accountTypesFilter) { 80 mAccountTypesFilter = accountTypesFilter; 81 } 82 83 /** Sets the filter for accounts that should NOT be shown. */ setAccountTypesExclusionFilter(Set<String> accountTypesExclusionFilter)84 protected void setAccountTypesExclusionFilter(Set<String> accountTypesExclusionFilter) { 85 mAccountTypesExclusionFilter = accountTypesExclusionFilter; 86 } 87 88 /** Sets the callback invoked when the set of authorized account types changes. */ setOnChangeListener(OnChangeListener listener)89 public void setOnChangeListener(OnChangeListener listener) { 90 mListener = listener; 91 } 92 93 /** 94 * Updates the set of authorized account types. 95 * 96 * <p>Derived from 97 * {@link com.android.settings.accounts.ChooseAccountActivity#onAuthDescriptionsUpdated} 98 */ updateAuthorizedAccountTypes(boolean isForced)99 private void updateAuthorizedAccountTypes(boolean isForced) { 100 AccountManager accountManager = AccountManager.get(mContext); 101 AuthenticatorDescription[] authenticatorDescriptions = 102 accountManager.getAuthenticatorTypesAsUser(mUserHandle.getIdentifier()); 103 104 Set<String> authorizedAccountTypes = new HashSet<>(); 105 for (AuthenticatorDescription authenticatorDescription : authenticatorDescriptions) { 106 String accountType = authenticatorDescription.type; 107 108 List<String> accountAuthorities = 109 mAuthenticatorHelper.getAuthoritiesForAccountType(accountType); 110 111 // If there are specific authorities required, we need to check whether they are 112 // included in the account type. 113 boolean authorized = 114 (mAuthorities == null || mAuthorities.isEmpty() || accountAuthorities == null 115 || !Collections.disjoint(accountAuthorities, mAuthorities)); 116 117 // If there is an account type filter, make sure this account type is included. 118 authorized = authorized && (mAccountTypesFilter == null 119 || mAccountTypesFilter.contains(accountType)); 120 121 // Check if the account type is in the exclusion list. 122 authorized = authorized && (mAccountTypesExclusionFilter == null 123 || !mAccountTypesExclusionFilter.contains(accountType)); 124 125 if (authorized) { 126 authorizedAccountTypes.add(accountType); 127 } 128 } 129 130 if (isForced || !Objects.equals(mAuthorizedAccountTypes, authorizedAccountTypes)) { 131 mAuthorizedAccountTypes = authorizedAccountTypes; 132 if (mListener != null) { 133 mListener.onAuthorizedAccountTypesChanged(); 134 } 135 } 136 } 137 138 /** Returns the set of authorized account types, initializing the set first if necessary. */ getAuthorizedAccountTypes()139 public Set<String> getAuthorizedAccountTypes() { 140 if (mAuthorizedAccountTypes == null) { 141 updateAuthorizedAccountTypes(false /* isForced */); 142 } 143 return mAuthorizedAccountTypes; 144 } 145 146 /** Forces an update of the authorized account types. */ forceUpdate()147 public void forceUpdate() { 148 updateAuthorizedAccountTypes(true /* isForced */); 149 } 150 151 /** Starts listening for account updates. */ listenToAccountUpdates()152 public void listenToAccountUpdates() { 153 mAuthenticatorHelper.listenToAccountUpdates(); 154 } 155 156 /** Stops listening for account updates. */ stopListeningToAccountUpdates()157 public void stopListeningToAccountUpdates() { 158 mAuthenticatorHelper.stopListeningToAccountUpdates(); 159 } 160 161 /** 162 * Gets the label associated with a particular account type. Returns {@code null} if none found. 163 * 164 * @param accountType the type of account 165 */ getLabelForType(String accountType)166 public CharSequence getLabelForType(String accountType) { 167 return mAuthenticatorHelper.getLabelForType(mContext, accountType); 168 } 169 170 /** 171 * Gets an icon associated with a particular account type. Returns a default icon if none found. 172 * 173 * @param accountType the type of account 174 * @return a drawable for the icon or a default icon returned by 175 * {@link PackageManager#getDefaultActivityIcon} if one cannot be found. 176 */ getDrawableForType(String accountType)177 public Drawable getDrawableForType(String accountType) { 178 return mAuthenticatorHelper.getDrawableForType(mContext, accountType); 179 } 180 181 /** Used for testing to trigger an account update. */ 182 @VisibleForTesting getAuthenticatorHelper()183 AuthenticatorHelper getAuthenticatorHelper() { 184 return mAuthenticatorHelper; 185 } 186 } 187