• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.Account;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.SyncAdapterType;
23 import android.content.SyncInfo;
24 import android.content.SyncStatusInfo;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ProviderInfo;
27 import android.os.Bundle;
28 import android.os.UserHandle;
29 import android.text.TextUtils;
30 
31 import androidx.annotation.VisibleForTesting;
32 
33 import com.android.car.settings.common.Logger;
34 
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Set;
38 
39 /** Helper that provides utility methods for account syncing. */
40 class AccountSyncHelper {
41     private static final Logger LOG = new Logger(AccountSyncHelper.class);
42 
AccountSyncHelper()43     private AccountSyncHelper() {
44     }
45 
46     /** Returns the visible sync adapters available for an account. */
getVisibleSyncAdaptersForAccount(Context context, Account account, UserHandle userHandle)47     static Set<SyncAdapterType> getVisibleSyncAdaptersForAccount(Context context, Account account,
48             UserHandle userHandle) {
49         Set<SyncAdapterType> syncableAdapters = getSyncableSyncAdaptersForAccount(account,
50                 userHandle);
51 
52         syncableAdapters.removeIf(
53                 (SyncAdapterType syncAdapter) -> !isVisible(context, syncAdapter, userHandle));
54 
55         return syncableAdapters;
56     }
57 
58     /** Returns the syncable sync adapters available for an account. */
getSyncableSyncAdaptersForAccount(Account account, UserHandle userHandle)59     static Set<SyncAdapterType> getSyncableSyncAdaptersForAccount(Account account,
60             UserHandle userHandle) {
61         Set<SyncAdapterType> adapters = new HashSet<>();
62 
63         SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
64                 userHandle.getIdentifier());
65         for (int i = 0; i < syncAdapters.length; i++) {
66             SyncAdapterType syncAdapter = syncAdapters[i];
67             String authority = syncAdapter.authority;
68 
69             // If the sync adapter is not for this account type, don't include it
70             if (!syncAdapter.accountType.equals(account.type)) {
71                 continue;
72             }
73 
74             boolean isSyncable = ContentResolver.getIsSyncableAsUser(account, authority,
75                     userHandle.getIdentifier()) > 0;
76             // If the adapter is not syncable, don't include it
77             if (!isSyncable) {
78                 continue;
79             }
80 
81             adapters.add(syncAdapter);
82         }
83 
84         return adapters;
85     }
86 
87     /**
88      * Requests a sync if it is allowed.
89      *
90      * <p>Derived from
91      * {@link com.android.settings.accounts.AccountSyncSettings#requestOrCancelSync}.
92      *
93      * @return {@code true} if sync was requested, {@code false} otherwise.
94      */
requestSyncIfAllowed(Account account, String authority, int userId)95     static boolean requestSyncIfAllowed(Account account, String authority, int userId) {
96         if (!syncIsAllowed(account, authority, userId)) {
97             return false;
98         }
99 
100         Bundle extras = new Bundle();
101         extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
102         ContentResolver.requestSyncAsUser(account, authority, userId, extras);
103         return true;
104     }
105 
106     /**
107      * Returns the label for a given sync authority.
108      *
109      * @return the title if available, and an empty CharSequence otherwise
110      */
getTitle(Context context, String authority, UserHandle userHandle)111     static CharSequence getTitle(Context context, String authority, UserHandle userHandle) {
112         PackageManager packageManager = context.getPackageManager();
113         ProviderInfo providerInfo = packageManager.resolveContentProviderAsUser(
114                 authority, /* flags= */ 0, userHandle.getIdentifier());
115         if (providerInfo == null) {
116             return "";
117         }
118 
119         return providerInfo.loadLabel(packageManager);
120     }
121 
122     /** Returns whether a sync adapter is currently syncing for the account being shown. */
isSyncing(Account account, List<SyncInfo> currentSyncs, String authority)123     static boolean isSyncing(Account account, List<SyncInfo> currentSyncs, String authority) {
124         for (SyncInfo syncInfo : currentSyncs) {
125             if (syncInfo.account.equals(account) && syncInfo.authority.equals(authority)) {
126                 return true;
127             }
128         }
129         return false;
130     }
131 
132     /** Returns the current sync state based on sync status information. */
getSyncState(SyncStatusInfo status, boolean syncEnabled, boolean activelySyncing)133     static SyncState getSyncState(SyncStatusInfo status, boolean syncEnabled,
134             boolean activelySyncing) {
135         boolean initialSync = status != null && status.initialize;
136         boolean syncIsPending = status != null && status.pending;
137         boolean lastSyncFailed = syncEnabled && status != null && status.lastFailureTime != 0
138                 && status.getLastFailureMesgAsInt(0)
139                 != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
140         if (activelySyncing && !initialSync) {
141             return SyncState.ACTIVE;
142         } else if (syncIsPending && !initialSync) {
143             return SyncState.PENDING;
144         } else if (lastSyncFailed) {
145             return SyncState.FAILED;
146         }
147         return SyncState.NONE;
148     }
149 
150     @VisibleForTesting
syncIsAllowed(Account account, String authority, int userId)151     static boolean syncIsAllowed(Account account, String authority, int userId) {
152         boolean oneTimeSyncMode = !ContentResolver.getMasterSyncAutomaticallyAsUser(userId);
153         boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(account, authority,
154                 userId);
155         return oneTimeSyncMode || syncEnabled;
156     }
157 
isVisible(Context context, SyncAdapterType syncAdapter, UserHandle userHandle)158     private static boolean isVisible(Context context, SyncAdapterType syncAdapter,
159             UserHandle userHandle) {
160         String authority = syncAdapter.authority;
161 
162         if (!syncAdapter.isUserVisible()) {
163             // If the sync adapter is not visible, don't show it
164             return false;
165         }
166 
167         try {
168             context.getPackageManager().getPackageUidAsUser(syncAdapter.getPackageName(),
169                     userHandle.getIdentifier());
170         } catch (PackageManager.NameNotFoundException e) {
171             LOG.e("No uid for package" + syncAdapter.getPackageName(), e);
172             // If we can't get the Uid for the package hosting the sync adapter, don't show it
173             return false;
174         }
175 
176         CharSequence title = getTitle(context, authority, userHandle);
177         if (TextUtils.isEmpty(title)) {
178             return false;
179         }
180 
181         return true;
182     }
183 
184     /** Denotes a sync adapter state. */
185     public enum SyncState {
186         /** The sync adapter is actively syncing. */
187         ACTIVE,
188         /** The sync adapter is waiting to start syncing. */
189         PENDING,
190         /** The sync adapter's last attempt to sync failed. */
191         FAILED,
192         /** Nothing to note about the sync adapter's sync state. */
193         NONE;
194     }
195 }
196