Merge "Enforce device policy in credential manager (framework)"
This commit is contained in:
committed by
Android (Google) Code Review
commit
eef487fa75
@@ -21,6 +21,7 @@ import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
@@ -30,6 +31,7 @@ import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ICancellationSignal;
|
||||
import android.os.OutcomeReceiver;
|
||||
@@ -37,6 +39,8 @@ import android.os.RemoteException;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -54,6 +58,39 @@ import java.util.concurrent.Executor;
|
||||
public final class CredentialManager {
|
||||
private static final String TAG = "CredentialManager";
|
||||
|
||||
/** @hide */
|
||||
@IntDef(
|
||||
flag = true,
|
||||
prefix = {"PROVIDER_FILTER_"},
|
||||
value = {
|
||||
PROVIDER_FILTER_ALL_PROVIDERS,
|
||||
PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY,
|
||||
PROVIDER_FILTER_USER_PROVIDERS_ONLY,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ProviderFilter {}
|
||||
|
||||
/**
|
||||
* Returns both system and user credential providers.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int PROVIDER_FILTER_ALL_PROVIDERS = 0;
|
||||
|
||||
/**
|
||||
* Returns system credential providers only.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY = 1;
|
||||
|
||||
/**
|
||||
* Returns user credential providers only.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2;
|
||||
|
||||
private final Context mContext;
|
||||
private final ICredentialManager mService;
|
||||
|
||||
@@ -404,6 +441,42 @@ public final class CredentialManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of ServiceInfo for all discovered credential providers on this device.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
@RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
|
||||
public List<ServiceInfo> getCredentialProviderServicesForTesting(
|
||||
@ProviderFilter int providerFilter) {
|
||||
try {
|
||||
return mService.getCredentialProviderServices(
|
||||
mContext.getUserId(),
|
||||
/* disableSystemAppVerificationForTests= */ true,
|
||||
providerFilter);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of ServiceInfo for all discovered credential providers on this device.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
@RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
|
||||
public List<ServiceInfo> getCredentialProviderServices(
|
||||
int userId, @ProviderFilter int providerFilter) {
|
||||
try {
|
||||
return mService.getCredentialProviderServices(
|
||||
userId, /* disableSystemAppVerificationForTests= */ false, providerFilter);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the service is enabled.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.credentials;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.credentials.ClearCredentialStateRequest;
|
||||
import android.credentials.CreateCredentialRequest;
|
||||
import android.credentials.GetCredentialRequest;
|
||||
@@ -57,5 +58,7 @@ interface ICredentialManager {
|
||||
void unregisterCredentialDescription(in UnregisterCredentialDescriptionRequest request, String callingPackage);
|
||||
|
||||
boolean isEnabledCredentialProviderService(in ComponentName componentName, String callingPackage);
|
||||
|
||||
List<ServiceInfo> getCredentialProviderServices(in int userId, in boolean disableSystemAppVerificationForTests, in int providerFilter);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.PackagePolicy;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -29,6 +31,7 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.credentials.CredentialManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
@@ -38,7 +41,10 @@ import android.util.Slog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link ServiceInfo} and meta-data about a credential provider.
|
||||
@@ -74,39 +80,124 @@ public final class CredentialProviderInfo {
|
||||
public CredentialProviderInfo(@NonNull Context context,
|
||||
@NonNull ComponentName serviceComponent, int userId, boolean isSystemProvider)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
this(context, getServiceInfoOrThrow(serviceComponent, userId), isSystemProvider);
|
||||
this(
|
||||
context,
|
||||
getServiceInfoOrThrow(serviceComponent, userId),
|
||||
isSystemProvider,
|
||||
/* disableSystemAppVerificationForTests= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an information instance of the credential provider.
|
||||
*
|
||||
* @param context the context object
|
||||
* @param serviceInfo the service info for the provider app. This must be retrieved from the
|
||||
* {@code PackageManager}
|
||||
* @param isSystemProvider whether the provider is a system app or not
|
||||
* {@code PackageManager}
|
||||
* @param isSystemProvider whether the provider app is a system provider
|
||||
* @param disableSystemAppVerificationForTests whether to disable system app permission
|
||||
* verification so that tests can install system providers
|
||||
* @throws SecurityException If provider does not require the relevant permission
|
||||
*/
|
||||
public CredentialProviderInfo(@NonNull Context context,
|
||||
public CredentialProviderInfo(
|
||||
@NonNull Context context,
|
||||
@NonNull ServiceInfo serviceInfo,
|
||||
boolean isSystemProvider) {
|
||||
if (!Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE.equals(serviceInfo.permission)) {
|
||||
Log.i(TAG, "Credential Provider Service from : " + serviceInfo.packageName
|
||||
+ "does not require permission"
|
||||
+ Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE);
|
||||
throw new SecurityException("Service does not require the expected permission : "
|
||||
+ Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE);
|
||||
boolean isSystemProvider,
|
||||
boolean disableSystemAppVerificationForTests) {
|
||||
verifyProviderPermission(serviceInfo);
|
||||
if (isSystemProvider) {
|
||||
if (!isValidSystemProvider(
|
||||
context, serviceInfo, disableSystemAppVerificationForTests)) {
|
||||
Slog.e(TAG, "Provider is not a valid system provider: " + serviceInfo);
|
||||
throw new SecurityException(
|
||||
"Provider is not a valid system provider: " + serviceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
mIsSystemProvider = isSystemProvider;
|
||||
mContext = context;
|
||||
mServiceInfo = serviceInfo;
|
||||
mCapabilities = new ArrayList<>();
|
||||
mIcon = mServiceInfo.loadIcon(mContext.getPackageManager());
|
||||
mLabel = mServiceInfo.loadSafeLabel(
|
||||
mContext.getPackageManager(), 0 /* do not ellipsize */,
|
||||
TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
|
||||
mIsSystemProvider = isSystemProvider;
|
||||
Log.i(TAG, "mLabel is : " + mLabel + ", for: " + mServiceInfo.getComponentName()
|
||||
.flattenToString());
|
||||
mLabel =
|
||||
mServiceInfo.loadSafeLabel(
|
||||
mContext.getPackageManager(),
|
||||
0 /* do not ellipsize */,
|
||||
TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
|
||||
Log.i(
|
||||
TAG,
|
||||
"mLabel is : "
|
||||
+ mLabel
|
||||
+ ", for: "
|
||||
+ mServiceInfo.getComponentName().flattenToString());
|
||||
populateProviderCapabilities(context, serviceInfo);
|
||||
}
|
||||
|
||||
private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException {
|
||||
final String permission = Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE;
|
||||
if (permission.equals(serviceInfo.permission)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Slog.e(
|
||||
TAG,
|
||||
"Credential Provider Service from : "
|
||||
+ serviceInfo.packageName
|
||||
+ "does not require permission"
|
||||
+ permission);
|
||||
throw new SecurityException(
|
||||
"Service does not require the expected permission : " + permission);
|
||||
}
|
||||
|
||||
private static boolean isSystemProviderWithValidPermission(
|
||||
ServiceInfo serviceInfo, Context context) {
|
||||
final String permission = Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE;
|
||||
try {
|
||||
ApplicationInfo appInfo =
|
||||
context.getPackageManager()
|
||||
.getApplicationInfo(
|
||||
serviceInfo.packageName,
|
||||
PackageManager.ApplicationInfoFlags.of(
|
||||
PackageManager.MATCH_SYSTEM_ONLY));
|
||||
if (appInfo != null
|
||||
&& context.checkPermission(permission, /* pid= */ -1, appInfo.uid)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
Slog.i(TAG, "SYS permission granted for: " + serviceInfo.packageName);
|
||||
return true;
|
||||
} else {
|
||||
Slog.i(TAG, "SYS permission failed for: " + serviceInfo.packageName);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isValidSystemProvider(
|
||||
Context context,
|
||||
ServiceInfo serviceInfo,
|
||||
boolean disableSystemAppVerificationForTests) {
|
||||
boolean isValidSystemTestProvider =
|
||||
isTestSystemProvider(serviceInfo, disableSystemAppVerificationForTests);
|
||||
if (isValidSystemTestProvider) {
|
||||
return true;
|
||||
}
|
||||
return isSystemProviderWithValidPermission(serviceInfo, context);
|
||||
}
|
||||
|
||||
private static boolean isTestSystemProvider(
|
||||
ServiceInfo serviceInfo, boolean disableSystemAppVerificationForTests) {
|
||||
if (!disableSystemAppVerificationForTests) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bundle metadata = serviceInfo.metaData;
|
||||
if (metadata == null) {
|
||||
Slog.e(TAG, "metadata is null: " + serviceInfo);
|
||||
return false;
|
||||
}
|
||||
return metadata.getBoolean(CredentialProviderService.TEST_SYSTEM_PROVIDER_META_DATA_KEY);
|
||||
}
|
||||
|
||||
private void populateProviderCapabilities(@NonNull Context context, ServiceInfo serviceInfo) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
@@ -133,7 +224,9 @@ public final class CredentialProviderInfo {
|
||||
mCapabilities.add(capability);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.i(TAG, e.getMessage());
|
||||
Slog.e(TAG, e.getMessage());
|
||||
} catch (Resources.NotFoundException e) {
|
||||
Slog.e(TAG, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,40 +247,80 @@ public final class CredentialProviderInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid credential provider services available for the user with the
|
||||
* given {@code userId}.
|
||||
* Returns the valid credential provider services available for the user with the given {@code
|
||||
* userId}.
|
||||
*/
|
||||
@NonNull
|
||||
private static List<ServiceInfo> getAvailableSystemServiceInfos(
|
||||
@NonNull Context context,
|
||||
@UserIdInt int userId,
|
||||
boolean disableSystemAppVerificationForTests) {
|
||||
final List<ServiceInfo> services = new ArrayList<>();
|
||||
final List<ResolveInfo> resolveInfos = new ArrayList<>();
|
||||
|
||||
resolveInfos.addAll(
|
||||
context.getPackageManager()
|
||||
.queryIntentServicesAsUser(
|
||||
new Intent(CredentialProviderService.SYSTEM_SERVICE_INTERFACE),
|
||||
PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
|
||||
userId));
|
||||
|
||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
try {
|
||||
PackageManager.ApplicationInfoFlags appInfoFlags =
|
||||
disableSystemAppVerificationForTests
|
||||
? PackageManager.ApplicationInfoFlags.of(0)
|
||||
: PackageManager.ApplicationInfoFlags.of(
|
||||
PackageManager.MATCH_SYSTEM_ONLY);
|
||||
ApplicationInfo appInfo =
|
||||
context.getPackageManager()
|
||||
.getApplicationInfo(serviceInfo.packageName, appInfoFlags);
|
||||
|
||||
if (appInfo == null || serviceInfo == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
services.add(serviceInfo);
|
||||
} catch (SecurityException e) {
|
||||
Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
}
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid credential provider services available for the user with the given {@code
|
||||
* userId}.
|
||||
*/
|
||||
@NonNull
|
||||
public static List<CredentialProviderInfo> getAvailableSystemServices(
|
||||
@NonNull Context context,
|
||||
@UserIdInt int userId) {
|
||||
final List<CredentialProviderInfo> services = new ArrayList<>();
|
||||
|
||||
final List<ResolveInfo> resolveInfos =
|
||||
context.getPackageManager().queryIntentServicesAsUser(
|
||||
new Intent(CredentialProviderService.SYSTEM_SERVICE_INTERFACE),
|
||||
PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
|
||||
userId);
|
||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
@UserIdInt int userId,
|
||||
boolean disableSystemAppVerificationForTests) {
|
||||
final List<CredentialProviderInfo> providerInfos = new ArrayList<>();
|
||||
for (ServiceInfo si :
|
||||
getAvailableSystemServiceInfos(
|
||||
context, userId, disableSystemAppVerificationForTests)) {
|
||||
try {
|
||||
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
|
||||
serviceInfo.packageName,
|
||||
PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
|
||||
if (appInfo != null
|
||||
&& context.checkPermission(
|
||||
Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE,
|
||||
/*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) {
|
||||
services.add(new CredentialProviderInfo(context, serviceInfo,
|
||||
/*isSystemProvider=*/true));
|
||||
CredentialProviderInfo cpi =
|
||||
new CredentialProviderInfo(
|
||||
context,
|
||||
si,
|
||||
/* isSystemProvider= */ true,
|
||||
disableSystemAppVerificationForTests);
|
||||
if (cpi.isSystemProvider()) {
|
||||
providerInfos.add(cpi);
|
||||
} else {
|
||||
Slog.e(TAG, "Non system provider was in system provider list.");
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Log.i(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.i(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
Slog.e(TAG, "Failed to create CredentialProviderInfo: " + e);
|
||||
}
|
||||
}
|
||||
return services;
|
||||
return providerInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,47 +359,153 @@ public final class CredentialProviderInfo {
|
||||
return Collections.unmodifiableList(mCapabilities);
|
||||
}
|
||||
|
||||
private static @Nullable PackagePolicy getDeviceManagerPolicy(@NonNull Context context) {
|
||||
try {
|
||||
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
|
||||
return dpm.getCredentialManagerPolicy();
|
||||
} catch (SecurityException e) {
|
||||
// If the current user is not enrolled in DPM then this can throw a security error.
|
||||
Log.e(TAG, "Failed to get device policy: " + e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid credential provider services available for the user with the
|
||||
* given {@code userId}.
|
||||
* Returns the valid credential provider services available for the user with the given {@code
|
||||
* userId}.
|
||||
*/
|
||||
@NonNull
|
||||
public static List<CredentialProviderInfo> getAvailableServices(@NonNull Context context,
|
||||
@UserIdInt int userId) {
|
||||
final List<CredentialProviderInfo> services = new ArrayList<>();
|
||||
public static List<CredentialProviderInfo> getCredentialProviderServices(
|
||||
@NonNull Context context,
|
||||
int userId,
|
||||
boolean disableSystemAppVerificationForTests,
|
||||
int providerFilter) {
|
||||
// Get the device policy.
|
||||
PackagePolicy pp = getDeviceManagerPolicy(context);
|
||||
|
||||
// Generate the provider list.
|
||||
ProviderGenerator generator =
|
||||
new ProviderGenerator(
|
||||
context, pp, disableSystemAppVerificationForTests, providerFilter);
|
||||
generator.addUserProviders(
|
||||
getUserProviders(context, userId, disableSystemAppVerificationForTests));
|
||||
generator.addSystemProviders(
|
||||
getAvailableSystemServices(context, userId, disableSystemAppVerificationForTests));
|
||||
return generator.getProviders();
|
||||
}
|
||||
|
||||
private static class ProviderGenerator {
|
||||
private final Context mContext;
|
||||
private final PackagePolicy mPp;
|
||||
private final boolean mDisableSystemAppVerificationForTests;
|
||||
private final Map<String, CredentialProviderInfo> mServices = new HashMap();
|
||||
private final int mProviderFilter;
|
||||
|
||||
ProviderGenerator(
|
||||
Context context,
|
||||
PackagePolicy pp,
|
||||
boolean disableSystemAppVerificationForTests,
|
||||
int providerFilter) {
|
||||
this.mContext = context;
|
||||
this.mPp = pp;
|
||||
this.mDisableSystemAppVerificationForTests = disableSystemAppVerificationForTests;
|
||||
this.mProviderFilter = providerFilter;
|
||||
}
|
||||
|
||||
private boolean isPackageAllowed(boolean isSystemProvider, String packageName) {
|
||||
if (mPp == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isSystemProvider) {
|
||||
return mPp.getPolicyType() == PackagePolicy.PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM;
|
||||
}
|
||||
|
||||
return mPp.isPackageAllowed(packageName, new HashSet<>());
|
||||
}
|
||||
|
||||
public List<CredentialProviderInfo> getProviders() {
|
||||
return new ArrayList<>(mServices.values());
|
||||
}
|
||||
|
||||
public void addUserProviders(List<CredentialProviderInfo> providers) {
|
||||
for (CredentialProviderInfo cpi : providers) {
|
||||
if (!cpi.isSystemProvider()) {
|
||||
addProvider(cpi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addSystemProviders(List<CredentialProviderInfo> providers) {
|
||||
for (CredentialProviderInfo cpi : providers) {
|
||||
if (cpi.isSystemProvider()) {
|
||||
addProvider(cpi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProviderAllowedWithFilter(CredentialProviderInfo cpi) {
|
||||
if (mProviderFilter == CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cpi.isSystemProvider()) {
|
||||
return mProviderFilter == CredentialManager.PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY;
|
||||
} else {
|
||||
return mProviderFilter == CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
private void addProvider(CredentialProviderInfo cpi) {
|
||||
final String componentNameString =
|
||||
cpi.getServiceInfo().getComponentName().flattenToString();
|
||||
if (!isProviderAllowedWithFilter(cpi)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPackageAllowed(cpi.isSystemProvider(), cpi.getServiceInfo().packageName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mServices.put(componentNameString, cpi);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid credential provider services available for the user with the given {@code
|
||||
* userId}.
|
||||
*/
|
||||
@NonNull
|
||||
private static List<CredentialProviderInfo> getUserProviders(
|
||||
@NonNull Context context,
|
||||
@UserIdInt int userId,
|
||||
boolean disableSystemAppVerificationForTests) {
|
||||
final List<CredentialProviderInfo> services = new ArrayList<>();
|
||||
final List<ResolveInfo> resolveInfos =
|
||||
context.getPackageManager().queryIntentServicesAsUser(
|
||||
new Intent(CredentialProviderService.SERVICE_INTERFACE),
|
||||
PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
|
||||
userId);
|
||||
context.getPackageManager()
|
||||
.queryIntentServicesAsUser(
|
||||
new Intent(CredentialProviderService.SERVICE_INTERFACE),
|
||||
PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
|
||||
userId);
|
||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
try {
|
||||
services.add(new CredentialProviderInfo(context,
|
||||
serviceInfo, false));
|
||||
CredentialProviderInfo cpi =
|
||||
new CredentialProviderInfo(
|
||||
context,
|
||||
serviceInfo,
|
||||
/* isSystemProvider= */ false,
|
||||
disableSystemAppVerificationForTests);
|
||||
if (!cpi.isSystemProvider()) {
|
||||
services.add(cpi);
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
}
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid credential provider services available for the user, that can
|
||||
* support the given {@code credentialType}.
|
||||
*/
|
||||
@NonNull
|
||||
public static List<CredentialProviderInfo> getAvailableServicesForCapability(
|
||||
@NonNull Context context, @UserIdInt int userId, @NonNull String credentialType) {
|
||||
List<CredentialProviderInfo> servicesForCapability = new ArrayList<>();
|
||||
final List<CredentialProviderInfo> services = getAvailableServices(context, userId);
|
||||
|
||||
for (CredentialProviderInfo service : services) {
|
||||
if (service.hasCapability(credentialType)) {
|
||||
servicesForCapability.add(service);
|
||||
}
|
||||
}
|
||||
return servicesForCapability;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +157,10 @@ public abstract class CredentialProviderService extends Service {
|
||||
|
||||
public static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
|
||||
|
||||
/** @hide */
|
||||
public static final String TEST_SYSTEM_PROVIDER_META_DATA_KEY =
|
||||
"android.credentials.testsystemprovider";
|
||||
|
||||
private Handler mHandler;
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,10 +28,12 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.credentials.ClearCredentialStateRequest;
|
||||
import android.credentials.CreateCredentialException;
|
||||
import android.credentials.CreateCredentialRequest;
|
||||
import android.credentials.CredentialDescription;
|
||||
import android.credentials.CredentialManager;
|
||||
import android.credentials.CredentialOption;
|
||||
import android.credentials.GetCredentialException;
|
||||
import android.credentials.GetCredentialRequest;
|
||||
@@ -110,9 +112,12 @@ public final class CredentialManagerService
|
||||
private List<CredentialManagerServiceImpl> constructSystemServiceListLocked(
|
||||
int resolvedUserId) {
|
||||
List<CredentialManagerServiceImpl> services = new ArrayList<>();
|
||||
List<CredentialProviderInfo> credentialProviderInfos =
|
||||
CredentialProviderInfo.getAvailableSystemServices(mContext, resolvedUserId);
|
||||
credentialProviderInfos.forEach(
|
||||
List<CredentialProviderInfo> serviceInfos =
|
||||
CredentialProviderInfo.getAvailableSystemServices(
|
||||
mContext,
|
||||
resolvedUserId,
|
||||
/* disableSystemAppVerificationForTests= */ false);
|
||||
serviceInfos.forEach(
|
||||
info -> {
|
||||
services.add(
|
||||
new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
|
||||
@@ -209,11 +214,20 @@ public final class CredentialManagerService
|
||||
}
|
||||
|
||||
private boolean hasWriteSecureSettingsPermission() {
|
||||
final String permission = android.Manifest.permission.WRITE_SECURE_SETTINGS;
|
||||
return hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
|
||||
}
|
||||
|
||||
private void verifyPermission(String permission) throws SecurityException {
|
||||
if (!hasPermission(permission)) {
|
||||
throw new SecurityException("Caller is missing permission: " + permission);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPermission(String permission) {
|
||||
final boolean result =
|
||||
mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
|
||||
if (!result) {
|
||||
Slog.e(TAG, "Caller does not have WRITE_SECURE_SETTINGS permission.");
|
||||
Slog.e(TAG, "Caller does not have permission: " + permission);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -224,7 +238,7 @@ public final class CredentialManagerService
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
final List<CredentialManagerServiceImpl> services =
|
||||
getAllCredentialProviderServicesLocked(userId);
|
||||
getCredentialProviderServicesLocked(userId);
|
||||
for (CredentialManagerServiceImpl s : services) {
|
||||
c.accept(s);
|
||||
}
|
||||
@@ -235,7 +249,7 @@ public final class CredentialManagerService
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private List<CredentialManagerServiceImpl> getAllCredentialProviderServicesLocked(int userId) {
|
||||
private List<CredentialManagerServiceImpl> getCredentialProviderServicesLocked(int userId) {
|
||||
List<CredentialManagerServiceImpl> concatenatedServices = new ArrayList<>();
|
||||
List<CredentialManagerServiceImpl> userConfigurableServices =
|
||||
getServiceListForUserLocked(userId);
|
||||
@@ -693,6 +707,23 @@ public final class CredentialManagerService
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServiceInfo> getCredentialProviderServices(
|
||||
int userId, boolean disableSystemAppVerificationForTests, int providerFilter) {
|
||||
Log.i(TAG, "getCredentialProviderServices");
|
||||
verifyPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS);
|
||||
|
||||
List<ServiceInfo> services = new ArrayList<>();
|
||||
List<CredentialProviderInfo> providers =
|
||||
CredentialProviderInfo.getCredentialProviderServices(
|
||||
mContext, userId, disableSystemAppVerificationForTests, providerFilter);
|
||||
for (CredentialProviderInfo p : providers) {
|
||||
services.add(p.getServiceInfo());
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICancellationSignal clearCredentialState(
|
||||
ClearCredentialStateRequest request,
|
||||
@@ -748,8 +779,7 @@ public final class CredentialManagerService
|
||||
enforceCallingPackage(callingPackage, Binder.getCallingUid());
|
||||
|
||||
List<CredentialProviderInfo> services =
|
||||
CredentialProviderInfo.getAvailableServices(
|
||||
mContext, UserHandle.getCallingUserId());
|
||||
getServicesForCredentialDescription(UserHandle.getCallingUserId());
|
||||
|
||||
List<String> providers =
|
||||
services.stream()
|
||||
@@ -801,8 +831,7 @@ public final class CredentialManagerService
|
||||
enforceCallingPackage(callingPackage, Binder.getCallingUid());
|
||||
|
||||
List<CredentialProviderInfo> services =
|
||||
CredentialProviderInfo.getAvailableServices(
|
||||
mContext, UserHandle.getCallingUserId());
|
||||
getServicesForCredentialDescription(UserHandle.getCallingUserId());
|
||||
|
||||
List<String> providers =
|
||||
services.stream()
|
||||
@@ -820,6 +849,14 @@ public final class CredentialManagerService
|
||||
|
||||
session.executeUnregisterRequest(request, callingPackage);
|
||||
}
|
||||
|
||||
private List<CredentialProviderInfo> getServicesForCredentialDescription(int userId) {
|
||||
return CredentialProviderInfo.getCredentialProviderServices(
|
||||
mContext,
|
||||
userId,
|
||||
/* disableSystemAppVerificationForTests= */ false,
|
||||
CredentialManager.PROVIDER_FILTER_ALL_PROVIDERS);
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceCallingPackage(String callingPackage, int callingUid) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.credentials.CredentialManager;
|
||||
import android.credentials.ui.DisabledProviderData;
|
||||
import android.credentials.ui.IntentFactory;
|
||||
import android.credentials.ui.ProviderData;
|
||||
@@ -116,13 +117,17 @@ public class CredentialManagerUi {
|
||||
Set<String> enabledProviders = providerDataList.stream()
|
||||
.map(ProviderData::getProviderFlattenedComponentName)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
// TODO("Filter out non user configurable providers")
|
||||
Set<String> allProviders =
|
||||
CredentialProviderInfo.getAvailableServices(mContext, mUserId).stream()
|
||||
.map(CredentialProviderInfo::getServiceInfo)
|
||||
.map(ServiceInfo::getComponentName)
|
||||
.map(ComponentName::flattenToString)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
CredentialProviderInfo.getCredentialProviderServices(
|
||||
mContext,
|
||||
mUserId,
|
||||
/* disableSystemAppVerificationForTests= */ false,
|
||||
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)
|
||||
.stream()
|
||||
.map(CredentialProviderInfo::getServiceInfo)
|
||||
.map(ServiceInfo::getComponentName)
|
||||
.map(ComponentName::flattenToString)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
for (String provider: allProviders) {
|
||||
if (!enabledProviders.contains(provider)) {
|
||||
|
||||
Reference in New Issue
Block a user