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