blob: cbc7ca5d9e3c78cd378e09571f168563104141ca [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.settings.accounts;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
import android.content.SyncStatusInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
import com.android.car.settings.common.Logger;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Helper that provides utility methods for account syncing. */
class AccountSyncHelper {
private static final Logger LOG = new Logger(AccountSyncHelper.class);
private AccountSyncHelper() {
}
/** Returns the visible sync adapters available for an account. */
static Set<SyncAdapterType> getVisibleSyncAdaptersForAccount(Context context, Account account,
UserHandle userHandle) {
Set<SyncAdapterType> syncableAdapters = getSyncableSyncAdaptersForAccount(account,
userHandle);
syncableAdapters.removeIf(
(SyncAdapterType syncAdapter) -> !isVisible(context, syncAdapter, userHandle));
return syncableAdapters;
}
/** Returns the syncable sync adapters available for an account. */
static Set<SyncAdapterType> getSyncableSyncAdaptersForAccount(Account account,
UserHandle userHandle) {
Set<SyncAdapterType> adapters = new HashSet<>();
SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
userHandle.getIdentifier());
for (int i = 0; i < syncAdapters.length; i++) {
SyncAdapterType syncAdapter = syncAdapters[i];
String authority = syncAdapter.authority;
// If the sync adapter is not for this account type, don't include it
if (!syncAdapter.accountType.equals(account.type)) {
continue;
}
boolean isSyncable = ContentResolver.getIsSyncableAsUser(account, authority,
userHandle.getIdentifier()) > 0;
// If the adapter is not syncable, don't include it
if (!isSyncable) {
continue;
}
adapters.add(syncAdapter);
}
return adapters;
}
/**
* Requests a sync if it is allowed.
*
* <p>Derived from
* {@link com.android.settings.accounts.AccountSyncSettings#requestOrCancelSync}.
*
* @return {@code true} if sync was requested, {@code false} otherwise.
*/
static boolean requestSyncIfAllowed(Account account, String authority, int userId) {
if (!syncIsAllowed(account, authority, userId)) {
return false;
}
Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
ContentResolver.requestSyncAsUser(account, authority, userId, extras);
return true;
}
/**
* Returns the label for a given sync authority.
*
* @return the title if available, and an empty CharSequence otherwise
*/
static CharSequence getTitle(Context context, String authority, UserHandle userHandle) {
PackageManager packageManager = context.getPackageManager();
ProviderInfo providerInfo = packageManager.resolveContentProviderAsUser(
authority, /* flags= */ 0, userHandle.getIdentifier());
if (providerInfo == null) {
return "";
}
return providerInfo.loadLabel(packageManager);
}
/** Returns whether a sync adapter is currently syncing for the account being shown. */
static boolean isSyncing(Account account, List<SyncInfo> currentSyncs, String authority) {
for (SyncInfo syncInfo : currentSyncs) {
if (syncInfo.account.equals(account) && syncInfo.authority.equals(authority)) {
return true;
}
}
return false;
}
/** Returns the current sync state based on sync status information. */
static SyncState getSyncState(SyncStatusInfo status, boolean syncEnabled,
boolean activelySyncing) {
boolean initialSync = status != null && status.initialize;
boolean syncIsPending = status != null && status.pending;
boolean lastSyncFailed = syncEnabled && status != null && status.lastFailureTime != 0
&& status.getLastFailureMesgAsInt(0)
!= ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
if (activelySyncing && !initialSync) {
return SyncState.ACTIVE;
} else if (syncIsPending && !initialSync) {
return SyncState.PENDING;
} else if (lastSyncFailed) {
return SyncState.FAILED;
}
return SyncState.NONE;
}
@VisibleForTesting
static boolean syncIsAllowed(Account account, String authority, int userId) {
boolean oneTimeSyncMode = !ContentResolver.getMasterSyncAutomaticallyAsUser(userId);
boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(account, authority,
userId);
return oneTimeSyncMode || syncEnabled;
}
private static boolean isVisible(Context context, SyncAdapterType syncAdapter,
UserHandle userHandle) {
String authority = syncAdapter.authority;
if (!syncAdapter.isUserVisible()) {
// If the sync adapter is not visible, don't show it
return false;
}
try {
context.getPackageManager().getPackageUidAsUser(syncAdapter.getPackageName(),
userHandle.getIdentifier());
} catch (PackageManager.NameNotFoundException e) {
LOG.e("No uid for package" + syncAdapter.getPackageName(), e);
// If we can't get the Uid for the package hosting the sync adapter, don't show it
return false;
}
CharSequence title = getTitle(context, authority, userHandle);
if (TextUtils.isEmpty(title)) {
return false;
}
return true;
}
/** Denotes a sync adapter state. */
public enum SyncState {
/** The sync adapter is actively syncing. */
ACTIVE,
/** The sync adapter is waiting to start syncing. */
PENDING,
/** The sync adapter's last attempt to sync failed. */
FAILED,
/** Nothing to note about the sync adapter's sync state. */
NONE;
}
}