1 /*
2  * Copyright (C) 2016 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 package com.android.contacts.model;
17 
18 import android.accounts.Account;
19 import android.accounts.AccountManager;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.provider.ContactsContract;
23 
24 import com.android.contacts.Experiments;
25 import com.android.contacts.model.account.AccountWithDataSet;
26 import com.android.contacts.model.account.GoogleAccountType;
27 import com.android.contactsbind.ObjectFactory;
28 import com.android.contactsbind.experiments.Flags;
29 
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Set;
34 
35 /**
36  * Attempts to detect accounts for device contacts
37  */
38 public abstract class DeviceLocalAccountLocator {
39 
40     /**
41      * Returns a list of device local accounts
42      */
getDeviceLocalAccounts()43     public abstract List<AccountWithDataSet> getDeviceLocalAccounts();
44 
45     // This works on Nexus and AOSP because the local device account is the null account but most
46     // OEMs have a special account name and type for their device account.
47     public static final DeviceLocalAccountLocator NULL_ONLY = new DeviceLocalAccountLocator() {
48         @Override
49         public List<AccountWithDataSet> getDeviceLocalAccounts() {
50             return Collections.singletonList(AccountWithDataSet.getNullAccount());
51         }
52     };
53 
create(Context context, Set<String> knownAccountTypes)54     public static DeviceLocalAccountLocator create(Context context,
55             Set<String> knownAccountTypes) {
56         if (Flags.getInstance().getBoolean(Experiments.CP2_DEVICE_ACCOUNT_DETECTION_ENABLED)) {
57             return new Cp2DeviceLocalAccountLocator(context.getContentResolver(),
58                     ObjectFactory.getDeviceLocalAccountTypeFactory(context), knownAccountTypes);
59         }
60         return NULL_ONLY;
61     }
62 
create(Context context)63     public static DeviceLocalAccountLocator create(Context context) {
64         final AccountManager accountManager =
65                 (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
66         final Set<String> knownTypes = new HashSet<>();
67         for (Account account : accountManager.getAccounts()) {
68             knownTypes.add(account.type);
69         }
70         if (Flags.getInstance().getBoolean(Experiments.CP2_DEVICE_ACCOUNT_DETECTION_ENABLED)) {
71             return new Cp2DeviceLocalAccountLocator(context.getContentResolver(),
72                     ObjectFactory.getDeviceLocalAccountTypeFactory(context), knownTypes);
73         } else {
74             return new NexusDeviceAccountLocator(context, accountManager);
75         }
76     }
77 
78     /**
79      * On Nexus the "device" account uses "null" values for the account name and type columns
80      *
81      * <p>However, the focus sync adapter migrates contacts from this null account to a Google
82      * account if one exists. Hence, the device account should be returned only when there is no
83      * Google Account added or when there already exists contacts in the null account.
84      * </p>
85      */
86     public static class NexusDeviceAccountLocator extends DeviceLocalAccountLocator {
87         private final Context mContext;
88         private final AccountManager mAccountManager;
89 
90 
NexusDeviceAccountLocator(Context context, AccountManager accountManager)91         public NexusDeviceAccountLocator(Context context, AccountManager accountManager) {
92             mContext = context;
93             mAccountManager = accountManager;
94         }
95 
96         @Override
getDeviceLocalAccounts()97         public List<AccountWithDataSet> getDeviceLocalAccounts() {
98             @SuppressWarnings("MissingPermission")
99             final Account[] accounts = mAccountManager
100                     .getAccountsByType(GoogleAccountType.ACCOUNT_TYPE);
101 
102             if (accounts.length > 0 && !AccountWithDataSet.getNullAccount().hasData(mContext)) {
103                 return Collections.emptyList();
104             } else {
105                 return Collections.singletonList(AccountWithDataSet.getNullAccount());
106             }
107         }
108     }
109 }
110