Merge "Add permission check in service"
This commit is contained in:
@@ -222,7 +222,7 @@ public final class CredentialManager {
|
||||
* @param callback the callback invoked when the request succeeds or fails
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)
|
||||
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
|
||||
public void listEnabledProviders(
|
||||
@Nullable CancellationSignal cancellationSignal,
|
||||
@CallbackExecutor @NonNull Executor executor,
|
||||
@@ -286,7 +286,7 @@ public final class CredentialManager {
|
||||
*/
|
||||
public static boolean isServiceEnabled(Context context) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
CredentialManager credentialManager =
|
||||
(CredentialManager) context.getSystemService(Context.CREDENTIAL_SERVICE);
|
||||
@@ -298,8 +298,7 @@ public final class CredentialManager {
|
||||
|
||||
private boolean isServiceEnabled() {
|
||||
return DeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER,
|
||||
true);
|
||||
DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_MANAGER, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,22 +312,19 @@ public final class CredentialManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link CredentialDescription} for an actively provisioned {@link Credential}
|
||||
* a CredentialProvider has. This registry will then be used to determine where to
|
||||
* fetch the requested {@link Credential} from. Not all credential types will be supported.
|
||||
* The distinction will be made by the JetPack layer. For the types that are supported,
|
||||
* JetPack will add a new key-value pair into {@link GetCredentialRequest}. These will not
|
||||
* be persistent on the device. The Credential Providers will need to call this API again
|
||||
* upon device reboot.
|
||||
* Registers a {@link CredentialDescription} for an actively provisioned {@link Credential} a
|
||||
* CredentialProvider has. This registry will then be used to determine where to fetch the
|
||||
* requested {@link Credential} from. Not all credential types will be supported. The
|
||||
* distinction will be made by the JetPack layer. For the types that are supported, JetPack will
|
||||
* add a new key-value pair into {@link GetCredentialRequest}. These will not be persistent on
|
||||
* the device. The Credential Providers will need to call this API again upon device reboot.
|
||||
*
|
||||
* @param request the request data
|
||||
*
|
||||
* @throws {@link UnsupportedOperationException} if the feature has not been enabled.
|
||||
* @throws {@link com.android.server.credentials.NonCredentialProviderCallerException}
|
||||
* if the calling package name is not also listed as a Credential Provider.
|
||||
* @throws {@link IllegalArgumentException} if the calling Credential Provider can not handle
|
||||
* one or more of the Credential Types that are sent for registration.
|
||||
*
|
||||
* @throws {@link UnsupportedOperationException} if the feature has not been enabled.
|
||||
* @throws {@link com.android.server.credentials.NonCredentialProviderCallerException} if the
|
||||
* calling package name is not also listed as a Credential Provider.
|
||||
* @throws {@link IllegalArgumentException} if the calling Credential Provider can not handle
|
||||
* one or more of the Credential Types that are sent for registration.
|
||||
*/
|
||||
public void registerCredentialDescription(
|
||||
@NonNull RegisterCredentialDescriptionRequest request) {
|
||||
@@ -346,16 +342,12 @@ public final class CredentialManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unregisters a {@link CredentialDescription} for an actively provisioned {@link Credential}
|
||||
* Unregisters a {@link CredentialDescription} for an actively provisioned {@link Credential}
|
||||
* that has been registered previously.
|
||||
*
|
||||
*
|
||||
* @param request the request data
|
||||
*
|
||||
* @throws {@link UnsupportedOperationException} if the feature has not been enabled.
|
||||
*
|
||||
* @throws {@link UnsupportedOperationException} if the feature has not been enabled.
|
||||
*/
|
||||
public void unregisterCredentialDescription(
|
||||
@NonNull UnregisterCredentialDescriptionRequest request) {
|
||||
@@ -371,7 +363,6 @@ public final class CredentialManager {
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class GetCredentialTransport extends IGetCredentialCallback.Stub {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.credentials;
|
||||
|
||||
import static android.content.Context.CREDENTIAL_SERVICE;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
@@ -81,17 +82,17 @@ public final class CredentialManagerService
|
||||
private static final String TAG = "CredManSysService";
|
||||
private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
|
||||
"enable_credential_description_api";
|
||||
private static final String PERMISSION_DENIED_ERROR = "permission_denied";
|
||||
private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR =
|
||||
"Caller is missing WRITE_SECURE_SETTINGS permission";
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
/**
|
||||
* Cache of system service list per user id.
|
||||
*/
|
||||
/** Cache of system service list per user id. */
|
||||
@GuardedBy("mLock")
|
||||
private final SparseArray<List<CredentialManagerServiceImpl>> mSystemServicesCacheList =
|
||||
new SparseArray<>();
|
||||
|
||||
|
||||
public CredentialManagerService(@NonNull Context context) {
|
||||
super(
|
||||
context,
|
||||
@@ -109,9 +110,11 @@ public final class CredentialManagerService
|
||||
List<CredentialManagerServiceImpl> services = new ArrayList<>();
|
||||
List<CredentialProviderInfo> credentialProviderInfos =
|
||||
CredentialProviderInfo.getAvailableSystemServices(mContext, resolvedUserId);
|
||||
credentialProviderInfos.forEach(info -> {
|
||||
services.add(new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
|
||||
});
|
||||
credentialProviderInfos.forEach(
|
||||
info -> {
|
||||
services.add(
|
||||
new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
|
||||
});
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -174,10 +177,9 @@ public final class CredentialManagerService
|
||||
CredentialManagerServiceImpl serviceToBeRemoved = null;
|
||||
for (CredentialManagerServiceImpl service : services) {
|
||||
if (service != null) {
|
||||
CredentialProviderInfo credentialProviderInfo =
|
||||
service.getCredentialProviderInfo();
|
||||
ComponentName componentName = credentialProviderInfo.getServiceInfo()
|
||||
.getComponentName();
|
||||
CredentialProviderInfo credentialProviderInfo = service.getCredentialProviderInfo();
|
||||
ComponentName componentName =
|
||||
credentialProviderInfo.getServiceInfo().getComponentName();
|
||||
if (packageName.equals(componentName.getPackageName())) {
|
||||
serviceToBeRemoved = service;
|
||||
removeServiceFromMultiModeSettings(componentName.flattenToString(), userId);
|
||||
@@ -193,7 +195,6 @@ public final class CredentialManagerService
|
||||
// TODO("Iterate over system services and remove if needed")
|
||||
}
|
||||
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private List<CredentialManagerServiceImpl> getOrConstructSystemServiceListLock(
|
||||
int resolvedUserId) {
|
||||
@@ -205,6 +206,16 @@ public final class CredentialManagerService
|
||||
return services;
|
||||
}
|
||||
|
||||
private boolean hasWriteSecureSettingsPermission() {
|
||||
final String permission = android.Manifest.permission.WRITE_SECURE_SETTINGS;
|
||||
final boolean result =
|
||||
mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
|
||||
if (!result) {
|
||||
Slog.e(TAG, "Caller does not have WRITE_SECURE_SETTINGS permission.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void runForUser(@NonNull final Consumer<CredentialManagerServiceImpl> c) {
|
||||
final int userId = UserHandle.getCallingUserId();
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
@@ -222,8 +233,7 @@ public final class CredentialManagerService
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private List<CredentialManagerServiceImpl> getAllCredentialProviderServicesLocked(
|
||||
int userId) {
|
||||
private List<CredentialManagerServiceImpl> getAllCredentialProviderServicesLocked(int userId) {
|
||||
List<CredentialManagerServiceImpl> concatenatedServices = new ArrayList<>();
|
||||
List<CredentialManagerServiceImpl> userConfigurableServices =
|
||||
getServiceListForUserLocked(userId);
|
||||
@@ -243,16 +253,18 @@ public final class CredentialManagerService
|
||||
// to be guarded by 'service.mLock', which is the same as mLock.
|
||||
private List<ProviderSession> initiateProviderSessionsWithActiveContainers(
|
||||
GetRequestSession session,
|
||||
List<String> requestOptions, Set<String> activeCredentialContainers) {
|
||||
List<String> requestOptions,
|
||||
Set<String> activeCredentialContainers) {
|
||||
List<ProviderSession> providerSessions = new ArrayList<>();
|
||||
// Invoke all services of a user to initiate a provider session
|
||||
for (String packageName: activeCredentialContainers) {
|
||||
providerSessions.add(ProviderRegistryGetSession.createNewSession(
|
||||
mContext,
|
||||
UserHandle.getCallingUserId(),
|
||||
session,
|
||||
packageName,
|
||||
requestOptions));
|
||||
for (String packageName : activeCredentialContainers) {
|
||||
providerSessions.add(
|
||||
ProviderRegistryGetSession.createNewSession(
|
||||
mContext,
|
||||
UserHandle.getCallingUserId(),
|
||||
session,
|
||||
packageName,
|
||||
requestOptions));
|
||||
}
|
||||
return providerSessions;
|
||||
}
|
||||
@@ -260,16 +272,17 @@ public final class CredentialManagerService
|
||||
@NonNull
|
||||
private Set<String> getFilteredResultFromRegistry(List<CredentialOption> options) {
|
||||
// Session for active/provisioned credential descriptions;
|
||||
CredentialDescriptionRegistry registry = CredentialDescriptionRegistry
|
||||
.forUser(UserHandle.getCallingUserId());
|
||||
CredentialDescriptionRegistry registry =
|
||||
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
|
||||
|
||||
// All requested credential descriptions based on the given request.
|
||||
Set<String> requestedCredentialDescriptions =
|
||||
options.stream().map(
|
||||
getCredentialOption -> getCredentialOption
|
||||
.getCredentialRetrievalData()
|
||||
.getString(CredentialOption
|
||||
.FLATTENED_REQUEST))
|
||||
options.stream()
|
||||
.map(
|
||||
getCredentialOption ->
|
||||
getCredentialOption
|
||||
.getCredentialRetrievalData()
|
||||
.getString(CredentialOption.FLATTENED_REQUEST))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// All requested credential descriptions based on the given request.
|
||||
@@ -306,10 +319,14 @@ public final class CredentialManagerService
|
||||
private CallingAppInfo constructCallingAppInfo(String packageName, int userId) {
|
||||
final PackageInfo packageInfo;
|
||||
try {
|
||||
packageInfo = getContext().getPackageManager().getPackageInfoAsUser(
|
||||
packageName,
|
||||
PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES),
|
||||
userId);
|
||||
packageInfo =
|
||||
getContext()
|
||||
.getPackageManager()
|
||||
.getPackageInfoAsUser(
|
||||
packageName,
|
||||
PackageManager.PackageInfoFlags.of(
|
||||
PackageManager.GET_SIGNING_CERTIFICATES),
|
||||
userId);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.i(TAG, "Issue while retrieving signatureInfo : " + e.getMessage());
|
||||
return new CallingAppInfo(packageName, null);
|
||||
@@ -345,37 +362,50 @@ public final class CredentialManagerService
|
||||
if (isCredentialDescriptionApiEnabled()) {
|
||||
List<CredentialOption> optionsThatRequireActiveCredentials =
|
||||
request.getCredentialOptions().stream()
|
||||
.filter(getCredentialOption ->
|
||||
!TextUtils.isEmpty(getCredentialOption
|
||||
.getCredentialRetrievalData().getString(
|
||||
CredentialOption
|
||||
.FLATTENED_REQUEST, null)))
|
||||
.filter(
|
||||
getCredentialOption ->
|
||||
!TextUtils.isEmpty(
|
||||
getCredentialOption
|
||||
.getCredentialRetrievalData()
|
||||
.getString(
|
||||
CredentialOption
|
||||
.FLATTENED_REQUEST,
|
||||
null)))
|
||||
.toList();
|
||||
|
||||
List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
|
||||
request.getCredentialOptions().stream()
|
||||
.filter(getCredentialOption ->
|
||||
TextUtils.isEmpty(getCredentialOption
|
||||
.getCredentialRetrievalData().getString(
|
||||
CredentialOption
|
||||
.FLATTENED_REQUEST, null)))
|
||||
.filter(
|
||||
getCredentialOption ->
|
||||
TextUtils.isEmpty(
|
||||
getCredentialOption
|
||||
.getCredentialRetrievalData()
|
||||
.getString(
|
||||
CredentialOption
|
||||
.FLATTENED_REQUEST,
|
||||
null)))
|
||||
.toList();
|
||||
|
||||
List<ProviderSession> sessionsWithoutRemoteService =
|
||||
initiateProviderSessionsWithActiveContainers(session,
|
||||
optionsThatRequireActiveCredentials
|
||||
.stream().map(getCredentialOption ->
|
||||
getCredentialOption.getCredentialRetrievalData()
|
||||
.getString(CredentialOption
|
||||
.FLATTENED_REQUEST))
|
||||
initiateProviderSessionsWithActiveContainers(
|
||||
session,
|
||||
optionsThatRequireActiveCredentials.stream()
|
||||
.map(
|
||||
getCredentialOption ->
|
||||
getCredentialOption
|
||||
.getCredentialRetrievalData()
|
||||
.getString(
|
||||
CredentialOption
|
||||
.FLATTENED_REQUEST))
|
||||
.collect(Collectors.toList()),
|
||||
getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));
|
||||
|
||||
List<ProviderSession> sessionsWithRemoteService = initiateProviderSessions(
|
||||
session,
|
||||
optionsThatDoNotRequireActiveCredentials.stream()
|
||||
.map(CredentialOption::getType)
|
||||
.collect(Collectors.toList()));
|
||||
List<ProviderSession> sessionsWithRemoteService =
|
||||
initiateProviderSessions(
|
||||
session,
|
||||
optionsThatDoNotRequireActiveCredentials.stream()
|
||||
.map(CredentialOption::getType)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
Set<ProviderSession> all = new LinkedHashSet<>();
|
||||
all.addAll(sessionsWithRemoteService);
|
||||
@@ -394,7 +424,8 @@ public final class CredentialManagerService
|
||||
|
||||
if (providerSessions.isEmpty()) {
|
||||
try {
|
||||
callback.onError(GetCredentialException.TYPE_NO_CREDENTIAL,
|
||||
callback.onError(
|
||||
GetCredentialException.TYPE_NO_CREDENTIAL,
|
||||
"No credentials available on this device.");
|
||||
} catch (RemoteException e) {
|
||||
Log.i(
|
||||
@@ -437,7 +468,8 @@ public final class CredentialManagerService
|
||||
|
||||
if (providerSessions.isEmpty()) {
|
||||
try {
|
||||
callback.onError(CreateCredentialException.TYPE_NO_CREDENTIAL,
|
||||
callback.onError(
|
||||
CreateCredentialException.TYPE_NO_CREDENTIAL,
|
||||
"No credentials available on this device.");
|
||||
} catch (RemoteException e) {
|
||||
Log.i(
|
||||
@@ -460,6 +492,16 @@ public final class CredentialManagerService
|
||||
Log.i(TAG, "listEnabledProviders");
|
||||
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
|
||||
|
||||
if (!hasWriteSecureSettingsPermission()) {
|
||||
try {
|
||||
callback.onError(
|
||||
PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Issue with invoking response: " + e.getMessage());
|
||||
}
|
||||
return cancelTransport;
|
||||
}
|
||||
|
||||
List<String> enabledProviders = new ArrayList<>();
|
||||
runForUser(
|
||||
(service) -> {
|
||||
@@ -482,6 +524,16 @@ public final class CredentialManagerService
|
||||
List<String> providers, int userId, ISetEnabledProvidersCallback callback) {
|
||||
Log.i(TAG, "setEnabledProviders");
|
||||
|
||||
if (!hasWriteSecureSettingsPermission()) {
|
||||
try {
|
||||
callback.onError(
|
||||
PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Issue with invoking response: " + e.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
userId =
|
||||
ActivityManager.handleIncomingUser(
|
||||
Binder.getCallingPid(),
|
||||
@@ -550,8 +602,7 @@ public final class CredentialManagerService
|
||||
if (providerSessions.isEmpty()) {
|
||||
try {
|
||||
// TODO("Replace with properly defined error type")
|
||||
callback.onError("UNKNOWN", "No crdentials available on this "
|
||||
+ "device");
|
||||
callback.onError("UNKNOWN", "No crdentials available on this " + "device");
|
||||
} catch (RemoteException e) {
|
||||
Log.i(
|
||||
TAG,
|
||||
@@ -569,39 +620,50 @@ public final class CredentialManagerService
|
||||
@Override
|
||||
public void registerCredentialDescription(
|
||||
RegisterCredentialDescriptionRequest request, String callingPackage)
|
||||
throws IllegalArgumentException , NonCredentialProviderCallerException {
|
||||
throws IllegalArgumentException, NonCredentialProviderCallerException {
|
||||
Log.i(TAG, "registerCredentialDescription");
|
||||
|
||||
List<CredentialProviderInfo> services =
|
||||
CredentialProviderInfo.getAvailableServices(mContext,
|
||||
UserHandle.getCallingUserId());
|
||||
CredentialProviderInfo.getAvailableServices(
|
||||
mContext, UserHandle.getCallingUserId());
|
||||
|
||||
List<String> providers = services.stream()
|
||||
.map(credentialProviderInfo
|
||||
-> credentialProviderInfo.getServiceInfo().packageName).toList();
|
||||
List<String> providers =
|
||||
services.stream()
|
||||
.map(
|
||||
credentialProviderInfo ->
|
||||
credentialProviderInfo.getServiceInfo().packageName)
|
||||
.toList();
|
||||
|
||||
if (!providers.contains(callingPackage)) {
|
||||
throw new NonCredentialProviderCallerException(callingPackage);
|
||||
}
|
||||
|
||||
List<CredentialProviderInfo> matchingService = services.stream().filter(
|
||||
credentialProviderInfo ->
|
||||
credentialProviderInfo.getServiceInfo()
|
||||
.packageName.equals(callingPackage)).toList();
|
||||
List<CredentialProviderInfo> matchingService =
|
||||
services.stream()
|
||||
.filter(
|
||||
credentialProviderInfo ->
|
||||
credentialProviderInfo
|
||||
.getServiceInfo()
|
||||
.packageName
|
||||
.equals(callingPackage))
|
||||
.toList();
|
||||
|
||||
CredentialProviderInfo credentialProviderInfo = matchingService.get(0);
|
||||
|
||||
Set<String> supportedTypes = request.getCredentialDescriptions()
|
||||
.stream().map(CredentialDescription::getType).filter(
|
||||
credentialProviderInfo::hasCapability).collect(Collectors.toSet());
|
||||
Set<String> supportedTypes =
|
||||
request.getCredentialDescriptions().stream()
|
||||
.map(CredentialDescription::getType)
|
||||
.filter(credentialProviderInfo::hasCapability)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (supportedTypes.size() != request.getCredentialDescriptions().size()) {
|
||||
throw new IllegalArgumentException("CredentialProvider does not support one or more"
|
||||
+ "of the registered types. Check your XML entry.");
|
||||
throw new IllegalArgumentException(
|
||||
"CredentialProvider does not support one or more"
|
||||
+ "of the registered types. Check your XML entry.");
|
||||
}
|
||||
|
||||
CredentialDescriptionRegistry session = CredentialDescriptionRegistry
|
||||
.forUser(UserHandle.getCallingUserId());
|
||||
CredentialDescriptionRegistry session =
|
||||
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
|
||||
|
||||
session.executeRegisterRequest(request, callingPackage);
|
||||
}
|
||||
@@ -614,19 +676,22 @@ public final class CredentialManagerService
|
||||
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
|
||||
|
||||
List<CredentialProviderInfo> services =
|
||||
CredentialProviderInfo.getAvailableServices(mContext,
|
||||
UserHandle.getCallingUserId());
|
||||
CredentialProviderInfo.getAvailableServices(
|
||||
mContext, UserHandle.getCallingUserId());
|
||||
|
||||
List<String> providers = services.stream()
|
||||
.map(credentialProviderInfo
|
||||
-> credentialProviderInfo.getServiceInfo().packageName).toList();
|
||||
List<String> providers =
|
||||
services.stream()
|
||||
.map(
|
||||
credentialProviderInfo ->
|
||||
credentialProviderInfo.getServiceInfo().packageName)
|
||||
.toList();
|
||||
|
||||
if (!providers.contains(callingPackage)) {
|
||||
throw new NonCredentialProviderCallerException(callingPackage);
|
||||
}
|
||||
|
||||
CredentialDescriptionRegistry session = CredentialDescriptionRegistry
|
||||
.forUser(UserHandle.getCallingUserId());
|
||||
CredentialDescriptionRegistry session =
|
||||
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
|
||||
|
||||
session.executeUnregisterRequest(request, callingPackage);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user