1ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/*
2ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Copyright (C) 2009 The Android Open Source Project
3ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
4ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License");
5ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * you may not use this file except in compliance with the License.
6ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * You may obtain a copy of the License at
7ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
8ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *      http://www.apache.org/licenses/LICENSE-2.0
9ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
10ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Unless required by applicable law or agreed to in writing, software
11ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS,
12ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * See the License for the specific language governing permissions and
14ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * limitations under the License.
15ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
16ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
17ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpackage com.android.contacts.common.model;
18ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
19ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.accounts.Account;
20ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.accounts.AccountManager;
21ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.accounts.AuthenticatorDescription;
22ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.accounts.OnAccountsUpdateListener;
23ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.BroadcastReceiver;
24ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.ContentResolver;
25ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.Context;
26ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.Intent;
27ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.IntentFilter;
28ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.SyncAdapterType;
29ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.SyncStatusObserver;
30ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.pm.PackageManager;
31ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.pm.ResolveInfo;
32ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.net.Uri;
33ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.AsyncTask;
34ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Handler;
35ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.HandlerThread;
36ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Looper;
37ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Message;
38ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.SystemClock;
39ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.provider.ContactsContract;
40ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.VisibleForTesting;
41ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.text.TextUtils;
42ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.util.ArrayMap;
43ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.util.Log;
44ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.util.TimingLogger;
45ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.MoreContactUtils;
46ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.list.ContactListFilterController;
47ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.account.AccountType;
48ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.account.AccountTypeWithDataSet;
49ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.account.AccountWithDataSet;
50ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.account.ExchangeAccountType;
51ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.account.ExternalAccountType;
52ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.account.FallbackAccountType;
53ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.account.GoogleAccountType;
54ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.account.SamsungAccountType;
55ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.model.dataitem.DataKind;
56ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.util.Constants;
57ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.ArrayList;
58ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Collection;
59ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Collections;
60ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Comparator;
61ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.HashMap;
62ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.HashSet;
63ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.List;
64ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Map;
65ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Objects;
66ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Set;
67ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.concurrent.CountDownLatch;
68ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.concurrent.atomic.AtomicBoolean;
69ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
70ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/**
71ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Singleton holder for all parsed {@link AccountType} available on the system, typically filled
72ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * through {@link PackageManager} queries.
73ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
74ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpublic abstract class AccountTypeManager {
75ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
76ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  static final String TAG = "AccountTypeManager";
77ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
78ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final Object mInitializationLock = new Object();
79ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static AccountTypeManager mAccountTypeManager;
80ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
81ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
82ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Requests the singleton instance of {@link AccountTypeManager} with data bound from the
83ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * available authenticators. This method can safely be called from the UI thread.
84ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
85ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public static AccountTypeManager getInstance(Context context) {
86ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    synchronized (mInitializationLock) {
87ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (mAccountTypeManager == null) {
88ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        context = context.getApplicationContext();
89ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mAccountTypeManager = new AccountTypeManagerImpl(context);
90ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
91ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
92ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return mAccountTypeManager;
93ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
94ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
95ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
96ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Set the instance of account type manager. This is only for and should only be used by unit
97ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * tests. While having this method is not ideal, it's simpler than the alternative of holding this
98ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * as a service in the ContactsApplication context class.
99ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
100ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param mockManager The mock AccountTypeManager.
101ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
102ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public static void setInstanceForTest(AccountTypeManager mockManager) {
103ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    synchronized (mInitializationLock) {
104ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mAccountTypeManager = mockManager;
105ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
106ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
107ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
108ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
109ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns the list of all accounts (if contactWritableOnly is false) or just the list of contact
110ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * writable accounts (if contactWritableOnly is true).
111ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
112ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // TODO: Consider splitting this into getContactWritableAccounts() and getAllAccounts()
113ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public abstract List<AccountWithDataSet> getAccounts(boolean contactWritableOnly);
114ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
115ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Returns the list of accounts that are group writable. */
116ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public abstract List<AccountWithDataSet> getGroupWritableAccounts();
117ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
118ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public abstract AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet);
119ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
120ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public final AccountType getAccountType(String accountType, String dataSet) {
121ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getAccountType(AccountTypeWithDataSet.get(accountType, dataSet));
122ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
123ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
124ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public final AccountType getAccountTypeForAccount(AccountWithDataSet account) {
125ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (account != null) {
126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return getAccountType(account.getAccountTypeWithDataSet());
127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return getAccountType(null, null);
129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s which
133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     support the "invite" feature and have one or more account.
134ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     <p>This is a filtered down and more "usable" list compared to {@link
135ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     #getAllInvitableAccountTypes}, where usable is defined as: (1) making sure that the app
136ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     that contributed the account type is not disabled (in order to avoid presenting the user
137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     with an option that does nothing), and (2) that there is at least one raw contact with that
138ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     account type in the database (assuming that the user probably doesn't use that account
139ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     type).
140ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     <p>Warning: Don't use on the UI thread because this can scan the database.
141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
142ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public abstract Map<AccountTypeWithDataSet, AccountType> getUsableInvitableAccountTypes();
143ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
144ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
145ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Find the best {@link DataKind} matching the requested {@link AccountType#accountType}, {@link
146ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * AccountType#dataSet}, and {@link DataKind#mimeType}. If no direct match found, we try searching
147ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * {@link FallbackAccountType}.
148ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
149ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DataKind getKindOrFallback(AccountType type, String mimeType) {
150ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return type == null ? null : type.getKindForMimetype(mimeType);
151ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
152ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
153ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
154ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns all registered {@link AccountType}s, including extension ones.
155ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
156ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param contactWritableOnly if true, it only returns ones that support writing contacts.
157ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
158ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public abstract List<AccountType> getAccountTypes(boolean contactWritableOnly);
159ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
160ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
161ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @param contactWritableOnly if true, it only returns ones that support writing contacts.
162ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @return true when this instance contains the given account.
163ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
164ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public boolean contains(AccountWithDataSet account, boolean contactWritableOnly) {
165ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (AccountWithDataSet account_2 : getAccounts(false)) {
166ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (account.equals(account_2)) {
167ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        return true;
168ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
169ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
170ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return false;
171ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
172ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian}
173ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
174ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianclass AccountTypeManagerImpl extends AccountTypeManager
175ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    implements OnAccountsUpdateListener, SyncStatusObserver {
176ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
177ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final Map<AccountTypeWithDataSet, AccountType>
178ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP =
179ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          Collections.unmodifiableMap(new HashMap<AccountTypeWithDataSet, AccountType>());
180ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
181ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
182ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * A sample contact URI used to test whether any activities will respond to an invitable intent
183ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * with the given URI as the intent data. This doesn't need to be specific to a real contact
184ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * because an app that intercepts the intent should probably do so for all types of contact URIs.
185ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
186ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final Uri SAMPLE_CONTACT_URI = ContactsContract.Contacts.getLookupUri(1, "xxx");
187ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
188ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int MESSAGE_LOAD_DATA = 0;
189ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int MESSAGE_PROCESS_BROADCAST_INTENT = 1;
190ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final Comparator<AccountWithDataSet> ACCOUNT_COMPARATOR =
191ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      new Comparator<AccountWithDataSet>() {
192ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        @Override
193ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        public int compare(AccountWithDataSet a, AccountWithDataSet b) {
194ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          if (Objects.equals(a.name, b.name)
195ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              && Objects.equals(a.type, b.type)
196ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              && Objects.equals(a.dataSet, b.dataSet)) {
197ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            return 0;
198ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          } else if (b.name == null || b.type == null) {
199ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            return -1;
200ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          } else if (a.name == null || a.type == null) {
201ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            return 1;
202ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          } else {
203ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            int diff = a.name.compareTo(b.name);
204ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            if (diff != 0) {
205ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              return diff;
206ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
207ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            diff = a.type.compareTo(b.type);
208ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            if (diff != 0) {
209ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              return diff;
210ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
211ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
212ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            // Accounts without data sets get sorted before those that have them.
213ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            if (a.dataSet != null) {
214ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              return b.dataSet == null ? 1 : a.dataSet.compareTo(b.dataSet);
215ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            } else {
216ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              return -1;
217ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
218ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
219ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
220ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      };
221ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final InvitableAccountTypeCache mInvitableAccountTypeCache;
222ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
223ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * The boolean value is equal to true if the {@link InvitableAccountTypeCache} has been
224ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * initialized. False otherwise.
225ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
226ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final AtomicBoolean mInvitablesCacheIsInitialized = new AtomicBoolean(false);
227ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
228ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * The boolean value is equal to true if the {@link FindInvitablesTask} is still executing. False
229ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * otherwise.
230ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
231ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final AtomicBoolean mInvitablesTaskIsRunning = new AtomicBoolean(false);
232ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
233ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
234ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Context mContext;
235ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Runnable mCheckFilterValidityRunnable =
236ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      new Runnable() {
237ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        @Override
238ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        public void run() {
239ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          ContactListFilterController.getInstance(mContext).checkFilterValidity(true);
240ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
241ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      };
242ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private AccountManager mAccountManager;
243ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private AccountType mFallbackAccountType;
244ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private List<AccountWithDataSet> mAccounts = new ArrayList<>();
245ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private List<AccountWithDataSet> mContactWritableAccounts = new ArrayList<>();
246ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private List<AccountWithDataSet> mGroupWritableAccounts = new ArrayList<>();
247ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Map<AccountTypeWithDataSet, AccountType> mAccountTypesWithDataSets = new ArrayMap<>();
248ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes =
249ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP;
250ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private HandlerThread mListenerThread;
251ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Handler mListenerHandler;
252ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private BroadcastReceiver mBroadcastReceiver =
253ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      new BroadcastReceiver() {
254ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
255ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        @Override
256ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        public void onReceive(Context context, Intent intent) {
257ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          Message msg = mListenerHandler.obtainMessage(MESSAGE_PROCESS_BROADCAST_INTENT, intent);
258ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          mListenerHandler.sendMessage(msg);
259ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
260ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      };
261ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /* A latch that ensures that asynchronous initialization completes before data is used */
262ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private volatile CountDownLatch mInitializationLatch = new CountDownLatch(1);
263ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
264ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Internal constructor that only performs initial parsing. */
265ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public AccountTypeManagerImpl(Context context) {
266ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContext = context;
267ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mFallbackAccountType = new FallbackAccountType(context);
268ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
269ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAccountManager = AccountManager.get(mContext);
270ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
271ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mListenerThread = new HandlerThread("AccountChangeListener");
272ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mListenerThread.start();
273ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mListenerHandler =
274ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new Handler(mListenerThread.getLooper()) {
275ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          @Override
276ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          public void handleMessage(Message msg) {
277ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            switch (msg.what) {
278ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              case MESSAGE_LOAD_DATA:
279ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                loadAccountsInBackground();
280ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                break;
281ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              case MESSAGE_PROCESS_BROADCAST_INTENT:
282ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                processBroadcastIntent((Intent) msg.obj);
283ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                break;
284ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
285ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
286ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        };
287ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
288ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mInvitableAccountTypeCache = new InvitableAccountTypeCache();
289ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
290ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Request updates when packages or accounts change
291ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
292ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
293ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
294ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    filter.addDataScheme("package");
295ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContext.registerReceiver(mBroadcastReceiver, filter);
296ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    IntentFilter sdFilter = new IntentFilter();
297ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
298ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
299ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContext.registerReceiver(mBroadcastReceiver, sdFilter);
300ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
301ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Request updates when locale is changed so that the order of each field will
302ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // be able to be changed on the locale change.
303ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
304ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContext.registerReceiver(mBroadcastReceiver, filter);
305ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
306ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mAccountManager.addOnAccountsUpdatedListener(this, mListenerHandler, false);
307ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
308ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this);
309ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
310ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
311ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
312ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
313ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
314ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Find a specific {@link AuthenticatorDescription} in the provided list that matches the given
315ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * account type.
316ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
317ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  protected static AuthenticatorDescription findAuthenticator(
318ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      AuthenticatorDescription[] auths, String accountType) {
319ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (AuthenticatorDescription auth : auths) {
320ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (accountType.equals(auth.type)) {
321ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        return auth;
322ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
323ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
324ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return null;
325ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
326ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
327ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
328ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Return all {@link AccountType}s with at least one account which supports "invite", i.e. its
329ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * {@link AccountType#getInviteContactActivityClassName()} is not empty.
330ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
331ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @VisibleForTesting
332ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  static Map<AccountTypeWithDataSet, AccountType> findAllInvitableAccountTypes(
333ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Context context,
334ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Collection<AccountWithDataSet> accounts,
335ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet) {
336ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Map<AccountTypeWithDataSet, AccountType> result = new ArrayMap<>();
337ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (AccountWithDataSet account : accounts) {
338ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      AccountTypeWithDataSet accountTypeWithDataSet = account.getAccountTypeWithDataSet();
339ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      AccountType type = accountTypesByTypeAndDataSet.get(accountTypeWithDataSet);
340ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (type == null) {
341ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        continue; // just in case
342ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
343ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (result.containsKey(accountTypeWithDataSet)) {
344ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        continue;
345ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
346ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
347ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (Log.isLoggable(TAG, Log.DEBUG)) {
348ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        Log.d(
349ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            TAG,
350ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            "Type "
351ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                + accountTypeWithDataSet
352ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                + " inviteClass="
353ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                + type.getInviteContactActivityClassName());
354ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
355ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (!TextUtils.isEmpty(type.getInviteContactActivityClassName())) {
356ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        result.put(accountTypeWithDataSet, type);
357ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
358ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
359ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return Collections.unmodifiableMap(result);
360ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
361ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
362ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
363ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onStatusChanged(int which) {
364ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
365ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
366ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
367ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void processBroadcastIntent(Intent intent) {
368ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
369ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
370ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
371ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /* This notification will arrive on the background thread */
372ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onAccountsUpdated(Account[] accounts) {
373ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Refresh to catch any changed accounts
374ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    loadAccountsInBackground();
375ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
376ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
377ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
378ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns instantly if accounts and account types have already been loaded. Otherwise waits for
379ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * the background thread to complete the loading.
380ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
381ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  void ensureAccountsLoaded() {
382ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    CountDownLatch latch = mInitializationLatch;
383ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (latch == null) {
384ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return;
385ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
386ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    while (true) {
387ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      try {
388ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        latch.await();
389ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        return;
390ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } catch (InterruptedException e) {
391ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        Thread.currentThread().interrupt();
392ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
393ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
394ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
395ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
396ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
397ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Loads account list and corresponding account types (potentially with data sets). Always called
398ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * on a background thread.
399ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
400ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  protected void loadAccountsInBackground() {
401ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
402ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground start");
403ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
404ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    TimingLogger timings = new TimingLogger(TAG, "loadAccountsInBackground");
405ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final long startTime = SystemClock.currentThreadTimeMillis();
406ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final long startTimeWall = SystemClock.elapsedRealtime();
407ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
408ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Account types, keyed off the account type and data set concatenation.
409ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet = new ArrayMap<>();
410ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
411ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // The same AccountTypes, but keyed off {@link RawContacts#ACCOUNT_TYPE}.  Since there can
412ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // be multiple account types (with different data sets) for the same type of account, each
413ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // type string may have multiple AccountType entries.
414ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Map<String, List<AccountType>> accountTypesByType = new ArrayMap<>();
415ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
416ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final List<AccountWithDataSet> allAccounts = new ArrayList<>();
417ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final List<AccountWithDataSet> contactWritableAccounts = new ArrayList<>();
418ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final List<AccountWithDataSet> groupWritableAccounts = new ArrayList<>();
419ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Set<String> extensionPackages = new HashSet<>();
420ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
421ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final AccountManager am = mAccountManager;
422ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
423ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final SyncAdapterType[] syncs = ContentResolver.getSyncAdapterTypes();
424ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final AuthenticatorDescription[] auths = am.getAuthenticatorTypes();
425ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
426ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // First process sync adapters to find any that provide contact data.
427ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (SyncAdapterType sync : syncs) {
428ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (!ContactsContract.AUTHORITY.equals(sync.authority)) {
429ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // Skip sync adapters that don't provide contact data.
430ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        continue;
431ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
432ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
433ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Look for the formatting details provided by each sync
434ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // adapter, using the authenticator to find general resources.
435ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      final String type = sync.accountType;
436ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      final AuthenticatorDescription auth = findAuthenticator(auths, type);
437ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (auth == null) {
438ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        Log.w(TAG, "No authenticator found for type=" + type + ", ignoring it.");
439ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        continue;
440ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
441ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
442ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      AccountType accountType;
443ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (GoogleAccountType.ACCOUNT_TYPE.equals(type)) {
444ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        accountType = new GoogleAccountType(mContext, auth.packageName);
445ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } else if (ExchangeAccountType.isExchangeType(type)) {
446ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        accountType = new ExchangeAccountType(mContext, auth.packageName, type);
447ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } else if (SamsungAccountType.isSamsungAccountType(mContext, type, auth.packageName)) {
448ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        accountType = new SamsungAccountType(mContext, auth.packageName, type);
449ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } else {
450ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        Log.d(
451ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            TAG, "Registering external account type=" + type + ", packageName=" + auth.packageName);
452ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        accountType = new ExternalAccountType(mContext, auth.packageName, false);
453ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
454ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (!accountType.isInitialized()) {
455ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        if (accountType.isEmbedded()) {
456ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          throw new IllegalStateException(
457ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              "Problem initializing embedded type " + accountType.getClass().getCanonicalName());
458ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        } else {
459ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          // Skip external account types that couldn't be initialized.
460ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          continue;
461ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
462ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
463ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
464ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      accountType.accountType = auth.type;
465ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      accountType.titleRes = auth.labelId;
466ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      accountType.iconRes = auth.iconId;
467ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
468ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
469ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
470ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Check to see if the account type knows of any other non-sync-adapter packages
471ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // that may provide other data sets of contact data.
472ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      extensionPackages.addAll(accountType.getExtensionPackageNames());
473ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
474ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
475ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // If any extension packages were specified, process them as well.
476ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (!extensionPackages.isEmpty()) {
477ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Log.d(TAG, "Registering " + extensionPackages.size() + " extension packages");
478ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      for (String extensionPackage : extensionPackages) {
479ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        ExternalAccountType accountType = new ExternalAccountType(mContext, extensionPackage, true);
480ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        if (!accountType.isInitialized()) {
481ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          // Skip external account types that couldn't be initialized.
482ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          continue;
483ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
484ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        if (!accountType.hasContactsMetadata()) {
485ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          Log.w(
486ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              TAG,
487ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              "Skipping extension package "
488ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  + extensionPackage
489ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  + " because"
490ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  + " it doesn't have the CONTACTS_STRUCTURE metadata");
491ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          continue;
492ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
493ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        if (TextUtils.isEmpty(accountType.accountType)) {
494ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          Log.w(
495ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              TAG,
496ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              "Skipping extension package "
497ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  + extensionPackage
498ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  + " because"
499ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  + " the CONTACTS_STRUCTURE metadata doesn't have the accountType"
500ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                  + " attribute");
501ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          continue;
502ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
503ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        Log.d(
504ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            TAG,
505ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            "Registering extension package account type="
506ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                + accountType.accountType
507ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                + ", dataSet="
508ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                + accountType.dataSet
509ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                + ", packageName="
510ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                + extensionPackage);
511ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
512ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
513ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
514ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
515ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    timings.addSplit("Loaded account types");
516ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
517ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Map in accounts to associate the account names with each account type entry.
518ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Account[] accounts = mAccountManager.getAccounts();
519ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (Account account : accounts) {
520ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      boolean syncable = ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
521ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
522ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (syncable) {
523ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        List<AccountType> accountTypes = accountTypesByType.get(account.type);
524ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        if (accountTypes != null) {
525ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          // Add an account-with-data-set entry for each account type that is
526ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          // authenticated by this account.
527ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          for (AccountType accountType : accountTypes) {
528ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            AccountWithDataSet accountWithDataSet =
529ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                new AccountWithDataSet(account.name, account.type, accountType.dataSet);
530ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            allAccounts.add(accountWithDataSet);
531ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            if (accountType.areContactsWritable()) {
532ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              contactWritableAccounts.add(accountWithDataSet);
533ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
534ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            if (accountType.isGroupMembershipEditable()) {
535ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              groupWritableAccounts.add(accountWithDataSet);
536ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
537ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
538ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
539ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
540ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
541ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
542ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Collections.sort(allAccounts, ACCOUNT_COMPARATOR);
543ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Collections.sort(contactWritableAccounts, ACCOUNT_COMPARATOR);
544ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Collections.sort(groupWritableAccounts, ACCOUNT_COMPARATOR);
545ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
546ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    timings.addSplit("Loaded accounts");
547ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
548ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    synchronized (this) {
549ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mAccountTypesWithDataSets = accountTypesByTypeAndDataSet;
550ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mAccounts = allAccounts;
551ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mContactWritableAccounts = contactWritableAccounts;
552ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mGroupWritableAccounts = groupWritableAccounts;
553ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mInvitableAccountTypes =
554ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          findAllInvitableAccountTypes(mContext, allAccounts, accountTypesByTypeAndDataSet);
555ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
556ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
557ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    timings.dumpToLog();
558ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final long endTimeWall = SystemClock.elapsedRealtime();
559ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final long endTime = SystemClock.currentThreadTimeMillis();
560ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
561ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.i(
562ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        TAG,
563ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        "Loaded meta-data for "
564ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            + mAccountTypesWithDataSets.size()
565ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            + " account types, "
566ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            + mAccounts.size()
567ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            + " accounts in "
568ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            + (endTimeWall - startTimeWall)
569ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            + "ms(wall) "
570ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            + (endTime - startTime)
571ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            + "ms(cpu)");
572ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
573ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mInitializationLatch != null) {
574ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mInitializationLatch.countDown();
575ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mInitializationLatch = null;
576ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
577ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
578ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground finish");
579ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
580ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
581ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Check filter validity since filter may become obsolete after account update. It must be
582ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // done from UI thread.
583ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mMainThreadHandler.post(mCheckFilterValidityRunnable);
584ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
585ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
586ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // Bookkeeping method for tracking the known account types in the given maps.
587ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void addAccountType(
588ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      AccountType accountType,
589ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet,
590ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Map<String, List<AccountType>> accountTypesByType) {
591ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    accountTypesByTypeAndDataSet.put(accountType.getAccountTypeAndDataSet(), accountType);
592ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    List<AccountType> accountsForType = accountTypesByType.get(accountType.accountType);
593ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (accountsForType == null) {
594ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      accountsForType = new ArrayList<>();
595ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
596ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    accountsForType.add(accountType);
597ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    accountTypesByType.put(accountType.accountType, accountsForType);
598ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
599ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
600ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Return list of all known, contact writable {@link AccountWithDataSet}'s. */
601ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
602ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public List<AccountWithDataSet> getAccounts(boolean contactWritableOnly) {
603ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    ensureAccountsLoaded();
604ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return contactWritableOnly ? mContactWritableAccounts : mAccounts;
605ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
606ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
607ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Return the list of all known, group writable {@link AccountWithDataSet}'s. */
608ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public List<AccountWithDataSet> getGroupWritableAccounts() {
609ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    ensureAccountsLoaded();
610ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return mGroupWritableAccounts;
611ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
612ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
613ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
614ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Find the best {@link DataKind} matching the requested {@link AccountType#accountType}, {@link
615ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * AccountType#dataSet}, and {@link DataKind#mimeType}. If no direct match found, we try searching
616ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * {@link FallbackAccountType}.
617ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
618ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
619ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public DataKind getKindOrFallback(AccountType type, String mimeType) {
620ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    ensureAccountsLoaded();
621ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DataKind kind = null;
622ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
623ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Try finding account type and kind matching request
624ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (type != null) {
625ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      kind = type.getKindForMimetype(mimeType);
626ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
627ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
628ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (kind == null) {
629ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Nothing found, so try fallback as last resort
630ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      kind = mFallbackAccountType.getKindForMimetype(mimeType);
631ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
632ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
633ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (kind == null) {
634ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (Log.isLoggable(TAG, Log.DEBUG)) {
635ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        Log.d(TAG, "Unknown type=" + type + ", mime=" + mimeType);
636ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
637ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
638ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
639ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return kind;
640ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
641ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
642ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Return {@link AccountType} for the given account type and data set. */
643ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
644ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
645ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    ensureAccountsLoaded();
646ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    synchronized (this) {
647ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      AccountType type = mAccountTypesWithDataSets.get(accountTypeWithDataSet);
648ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return type != null ? type : mFallbackAccountType;
649ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
650ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
651ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
652ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
653ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s which
654ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     support the "invite" feature and have one or more account. This is an unfiltered list. See
655ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *     {@link #getUsableInvitableAccountTypes()}.
656ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
657ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Map<AccountTypeWithDataSet, AccountType> getAllInvitableAccountTypes() {
658ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    ensureAccountsLoaded();
659ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return mInvitableAccountTypes;
660ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
661ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
662ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
663ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public Map<AccountTypeWithDataSet, AccountType> getUsableInvitableAccountTypes() {
664ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    ensureAccountsLoaded();
665ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Since this method is not thread-safe, it's possible for multiple threads to encounter
666ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // the situation where (1) the cache has not been initialized yet or
667ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // (2) an async task to refresh the account type list in the cache has already been
668ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // started. Hence we use {@link AtomicBoolean}s and return cached values immediately
669ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // while we compute the actual result in the background. We use this approach instead of
670ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // using "synchronized" because computing the account type list involves a DB read, and
671ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // can potentially cause a deadlock situation if this method is called from code which
672ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // holds the DB lock. The trade-off of potentially having an incorrect list of invitable
673ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // account types for a short period of time seems more manageable than enforcing the
674ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // context in which this method is called.
675ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
676ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Computing the list of usable invitable account types is done on the fly as requested.
677ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // If this method has never been called before, then block until the list has been computed.
678ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (!mInvitablesCacheIsInitialized.get()) {
679ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mInvitableAccountTypeCache.setCachedValue(findUsableInvitableAccountTypes(mContext));
680ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mInvitablesCacheIsInitialized.set(true);
681ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
682ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Otherwise, there is a value in the cache. If the value has expired and
683ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // an async task has not already been started by another thread, then kick off a new
684ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // async task to compute the list.
685ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (mInvitableAccountTypeCache.isExpired()
686ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          && mInvitablesTaskIsRunning.compareAndSet(false, true)) {
687ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new FindInvitablesTask().execute();
688ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
689ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
690ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
691ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return mInvitableAccountTypeCache.getCachedValue();
692ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
693ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
694ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
695ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Return all usable {@link AccountType}s that support the "invite" feature from the list of all
696ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * potential invitable account types (retrieved from {@link #getAllInvitableAccountTypes}). A
697ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * usable invitable account type means: (1) there is at least 1 raw contact in the database with
698ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * that account type, and (2) the app contributing the account type is not disabled.
699ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
700ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>Warning: Don't use on the UI thread because this can scan the database.
701ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
702ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Map<AccountTypeWithDataSet, AccountType> findUsableInvitableAccountTypes(
703ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Context context) {
704ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Map<AccountTypeWithDataSet, AccountType> allInvitables = getAllInvitableAccountTypes();
705ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (allInvitables.isEmpty()) {
706ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP;
707ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
708ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
709ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Map<AccountTypeWithDataSet, AccountType> result = new ArrayMap<>();
710ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    result.putAll(allInvitables);
711ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
712ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final PackageManager packageManager = context.getPackageManager();
713ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    for (AccountTypeWithDataSet accountTypeWithDataSet : allInvitables.keySet()) {
714ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      AccountType accountType = allInvitables.get(accountTypeWithDataSet);
715ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
716ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Make sure that account types don't come from apps that are disabled.
717ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Intent invitableIntent = MoreContactUtils.getInvitableIntent(accountType, SAMPLE_CONTACT_URI);
718ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (invitableIntent == null) {
719ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        result.remove(accountTypeWithDataSet);
720ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        continue;
721ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
722ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      ResolveInfo resolveInfo =
723ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          packageManager.resolveActivity(invitableIntent, PackageManager.MATCH_DEFAULT_ONLY);
724ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (resolveInfo == null) {
725ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // If we can't find an activity to start for this intent, then there's no point in
726ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // showing this option to the user.
727ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        result.remove(accountTypeWithDataSet);
728ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        continue;
729ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
730ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
731ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Make sure that there is at least 1 raw contact with this account type. This check
732ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // is non-trivial and should not be done on the UI thread.
733ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (!accountTypeWithDataSet.hasData(context)) {
734ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        result.remove(accountTypeWithDataSet);
735ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
736ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
737ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
738ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return Collections.unmodifiableMap(result);
739ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
740ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
741ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
742ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public List<AccountType> getAccountTypes(boolean contactWritableOnly) {
743ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    ensureAccountsLoaded();
744ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final List<AccountType> accountTypes = new ArrayList<>();
745ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    synchronized (this) {
746ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      for (AccountType type : mAccountTypesWithDataSets.values()) {
747ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        if (!contactWritableOnly || type.areContactsWritable()) {
748ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          accountTypes.add(type);
749ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
750ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
751ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
752ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return accountTypes;
753ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
754ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
755ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
756ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * This cache holds a list of invitable {@link AccountTypeWithDataSet}s, in the form of a {@link
757ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Map<AccountTypeWithDataSet, AccountType>}. Note that the cached value is valid only for {@link
758ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * #TIME_TO_LIVE} milliseconds.
759ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
760ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final class InvitableAccountTypeCache {
761ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
762ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
763ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * The cached {@link #mInvitableAccountTypes} list expires after this number of milliseconds has
764ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * elapsed.
765ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
766ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    private static final long TIME_TO_LIVE = 60000;
767ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
768ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes;
769ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
770ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    private long mTimeLastSet;
771ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
772ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
773ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Returns true if the data in this cache is stale and needs to be refreshed. Returns false
774ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * otherwise.
775ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
776ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public boolean isExpired() {
777ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return SystemClock.elapsedRealtime() - mTimeLastSet > TIME_TO_LIVE;
778ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
779ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
780ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
781ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Returns the cached value. Note that the caller is responsible for checking {@link
782ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * #isExpired()} to ensure that the value is not stale.
783ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
784ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public Map<AccountTypeWithDataSet, AccountType> getCachedValue() {
785ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return mInvitableAccountTypes;
786ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
787ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
788ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void setCachedValue(Map<AccountTypeWithDataSet, AccountType> map) {
789ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mInvitableAccountTypes = map;
790ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mTimeLastSet = SystemClock.elapsedRealtime();
791ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
792ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
793ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
794ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
795ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Background task to find all usable {@link AccountType}s that support the "invite" feature from
796ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * the list of all potential invitable account types. Once the work is completed, the list of
797ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * account types is stored in the {@link AccountTypeManager}'s {@link InvitableAccountTypeCache}.
798ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
799ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private class FindInvitablesTask
800ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      extends AsyncTask<Void, Void, Map<AccountTypeWithDataSet, AccountType>> {
801ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
802ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
803ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    protected Map<AccountTypeWithDataSet, AccountType> doInBackground(Void... params) {
804ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return findUsableInvitableAccountTypes(mContext);
805ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
806ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
807ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
808ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    protected void onPostExecute(Map<AccountTypeWithDataSet, AccountType> accountTypes) {
809ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mInvitableAccountTypeCache.setCachedValue(accountTypes);
810ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mInvitablesTaskIsRunning.set(false);
811ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
812ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
813ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian}
814