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