Add CredentialProviderInfo class to get information about credential

providers on the device
Test: manual - built & deployed locally
Bug: 247545196

Change-Id: I86c448fc0adb63f4541ea2f4b46d0b1a5da82453
This commit is contained in:
Reema Bajwa
2022-09-23 07:32:50 +00:00
parent 12639c966f
commit be65ffaea6
4 changed files with 212 additions and 1 deletions

View File

@@ -34,6 +34,7 @@ package android {
field public static final String BIND_COMPANION_DEVICE_SERVICE = "android.permission.BIND_COMPANION_DEVICE_SERVICE";
field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS";
field public static final String BIND_CREDENTIAL_PROVIDER_SERVICE = "android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE";
field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
field public static final String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2022 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 android.service.credentials;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* {@link ServiceInfo} and meta-data about a credential provider.
*
* @hide
*/
public final class CredentialProviderInfo {
private static final String TAG = "CredentialProviderInfo";
@NonNull
private final ServiceInfo mServiceInfo;
@NonNull
private final List<String> mCapabilities;
// TODO: Move the two strings below to CredentialProviderService when ready.
private static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
private static final String SERVICE_INTERFACE =
"android.service.credentials.CredentialProviderService";
/**
* Constructs an information instance of the credential provider.
*
* @param context The context object
* @param serviceComponent The serviceComponent of the provider service
* @param userId The android userId for which the current process is running
* @throws PackageManager.NameNotFoundException If provider service is not found
*/
public CredentialProviderInfo(@NonNull Context context,
@NonNull ComponentName serviceComponent, int userId)
throws PackageManager.NameNotFoundException {
this(context, getServiceInfoOrThrow(serviceComponent, userId));
}
private CredentialProviderInfo(@NonNull Context context, @NonNull ServiceInfo serviceInfo) {
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);
}
mServiceInfo = serviceInfo;
mCapabilities = new ArrayList<>();
populateProviderCapabilities(context);
}
private void populateProviderCapabilities(@NonNull Context context) {
if (mServiceInfo.applicationInfo.metaData == null) {
return;
}
try {
final int resourceId = mServiceInfo.applicationInfo.metaData.getInt(
CAPABILITY_META_DATA_KEY);
String[] capabilities = context.getResources().getStringArray(resourceId);
if (capabilities == null) {
Log.w(TAG, "No capabilities found for provider: " + mServiceInfo.packageName);
return;
}
for (String capability : capabilities) {
if (capability.isEmpty()) {
Log.w(TAG, "Skipping empty capability");
continue;
}
mCapabilities.add(capability);
}
} catch (Resources.NotFoundException e) {
Log.w(TAG, "Exception while populating provider capabilities: " + e.getMessage());
}
}
private static ServiceInfo getServiceInfoOrThrow(@NonNull ComponentName serviceComponent,
int userId) throws PackageManager.NameNotFoundException {
try {
ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(
serviceComponent,
PackageManager.GET_META_DATA,
userId);
if (si != null) {
return si;
}
} catch (RemoteException e) {
Slog.v(TAG, e.getMessage());
}
throw new PackageManager.NameNotFoundException(serviceComponent.toString());
}
/**
* Returns true if the service supports the given {@code credentialType}, false otherwise.
*/
@NonNull
public boolean hasCapability(@NonNull String credentialType) {
return mCapabilities.contains(credentialType);
}
/** Returns the service info. */
@NonNull
public ServiceInfo getServiceInfo() {
return mServiceInfo;
}
/** Returns an immutable list of capabilities this provider service can support. */
@NonNull
public List<String> getCapabilities() {
return Collections.unmodifiableList(mCapabilities);
}
/**
* Returns the valid credential provider services available for the user with the
* given {@code userId}.
*/
public static List<CredentialProviderInfo> getAvailableServices(@NonNull Context context,
@UserIdInt int userId) {
final List<CredentialProviderInfo> services = new ArrayList<>();
final List<ResolveInfo> resolveInfos =
context.getPackageManager().queryIntentServicesAsUser(
new Intent(SERVICE_INTERFACE),
PackageManager.GET_META_DATA,
userId);
for (ResolveInfo resolveInfo : resolveInfos) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
try {
services.add(new CredentialProviderInfo(context, serviceInfo));
} catch (SecurityException e) {
Log.w(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}.
*/
public static List<CredentialProviderInfo> getAvailableServicesForCapability(
Context context, @UserIdInt int userId, 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;
}
}

View File

@@ -4248,6 +4248,13 @@
<permission android:name="android.permission.BIND_AUTOFILL_SERVICE"
android:protectionLevel="signature" />
<!-- Must be required by a CredentialProviderService to ensure that only the
system can bind to it.
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
android:protectionLevel="signature" />
<!-- Alternative version of android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE.
This permission was renamed during the O previews but it was supported on the final O
release, so we need to carry it over.

View File

@@ -17,6 +17,11 @@
package com.android.server.credentials;
import android.annotation.NonNull;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.RemoteException;
import android.util.Log;
import com.android.server.infra.AbstractPerUserSystemService;
@@ -24,7 +29,7 @@ import com.android.server.infra.AbstractPerUserSystemService;
/**
* Per-user implementation of {@link CredentialManagerService}
*/
public class CredentialManagerServiceImpl extends
public final class CredentialManagerServiceImpl extends
AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> {
private static final String TAG = "CredManSysServiceImpl";
@@ -34,6 +39,20 @@ public class CredentialManagerServiceImpl extends
super(master, lock, userId);
}
@Override // from PerUserSystemService
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws PackageManager.NameNotFoundException {
ServiceInfo si;
try {
si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
PackageManager.GET_META_DATA, mUserId);
} catch (RemoteException e) {
throw new PackageManager.NameNotFoundException(
"Could not get service for " + serviceComponent);
}
return si;
}
/**
* Unimplemented getCredentials
*/