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