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