Merge "Add privileged APIs with origin"

This commit is contained in:
Harsh Lal
2023-02-09 16:45:10 +00:00
committed by Android (Google) Code Review
7 changed files with 264 additions and 65 deletions

View File

@@ -83,6 +83,7 @@ package android {
field public static final String CLEAR_APP_CACHE = "android.permission.CLEAR_APP_CACHE";
field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
field public static final String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
field public static final String CREDENTIAL_MANAGER_SET_ORIGIN = "android.permission.CREDENTIAL_MANAGER_SET_ORIGIN";
field public static final String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES";
field public static final String DELETE_PACKAGES = "android.permission.DELETE_PACKAGES";
field public static final String DELIVER_COMPANION_MESSAGES = "android.permission.DELIVER_COMPANION_MESSAGES";
@@ -13515,7 +13516,9 @@ package android.credentials {
public final class CredentialManager {
method public void clearCredentialState(@NonNull android.credentials.ClearCredentialStateRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.credentials.ClearCredentialStateException>);
method public void createCredential(@NonNull android.credentials.CreateCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN) public void createCredentialWithOrigin(@NonNull android.credentials.CreateCredentialRequest, @Nullable String, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.CreateCredentialResponse,android.credentials.CreateCredentialException>);
method public void getCredential(@NonNull android.credentials.GetCredentialRequest, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
method @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN) public void getCredentialWithOrigin(@NonNull android.credentials.GetCredentialRequest, @Nullable String, @NonNull android.app.Activity, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
method public boolean isEnabledCredentialProviderService(@NonNull android.content.ComponentName);
method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest);
method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest);

View File

@@ -247,6 +247,8 @@ package android {
field public static final String PERFORM_IMS_SINGLE_REGISTRATION = "android.permission.PERFORM_IMS_SINGLE_REGISTRATION";
field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
field public static final String POWER_SAVER = "android.permission.POWER_SAVER";
field public static final String PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE = "android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE";
field public static final String PROVIDE_HYBRID_CREDENTIAL_SERVICE = "android.permission.PROVIDE_HYBRID_CREDENTIAL_SERVICE";
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final String PROVISION_DEMO_DEVICE = "android.permission.PROVISION_DEMO_DEVICE";

View File

@@ -16,6 +16,8 @@
package android.credentials;
import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
import static java.util.Objects.requireNonNull;
import android.annotation.CallbackExecutor;
@@ -123,6 +125,57 @@ public final class CredentialManager {
}
}
/**
* Launches the necessary flows to retrieve an app credential from the user, for the given
* origin.
*
* <p>The execution can potentially launch UI flows to collect user consent to using a
* credential, display a picker when multiple credentials exist, etc.
*
* @param request the request specifying type(s) of credentials to get from the user
* @param origin the origin of the calling app. Callers of this special API (e.g. browsers)
* can set this origin for an app different from their own, to be able to get credentials
* on behalf of that app.
* @param activity the activity used to launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this {@link Executor}
* @param callback the callback invoked when the request succeeds or fails
*/
@RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN)
public void getCredentialWithOrigin(
@NonNull GetCredentialRequest request,
@Nullable String origin,
@NonNull Activity activity,
@Nullable CancellationSignal cancellationSignal,
@CallbackExecutor @NonNull Executor executor,
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
requireNonNull(request, "request must not be null");
requireNonNull(activity, "activity must not be null");
requireNonNull(executor, "executor must not be null");
requireNonNull(callback, "callback must not be null");
if (cancellationSignal != null && cancellationSignal.isCanceled()) {
Log.w(TAG, "getCredential already canceled");
return;
}
ICancellationSignal cancelRemote = null;
try {
cancelRemote =
mService.executeGetCredentialWithOrigin(
request,
new GetCredentialTransport(activity, executor, callback),
mContext.getOpPackageName(),
origin);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
if (cancellationSignal != null && cancelRemote != null) {
cancellationSignal.setRemote(cancelRemote);
}
}
/**
* Launches the necessary flows to register an app credential for the user.
*
@@ -168,6 +221,58 @@ public final class CredentialManager {
}
}
/**
* Launches the necessary flows to register an app credential for the user.
*
* <p>The execution can potentially launch UI flows to collect user consent to creating or
* storing the new credential, etc.
*
* @param request the request specifying type(s) of credentials to get from the user, for the
* given origin
* @param origin the origin of the calling app. Callers of this special API (e.g. browsers)
* can set this origin for an app different from their own, to be able to get credentials
* on behalf of that app.
* @param activity the activity used to launch any UI needed
* @param cancellationSignal an optional signal that allows for cancelling this call
* @param executor the callback will take place on this {@link Executor}
* @param callback the callback invoked when the request succeeds or fails
*/
@RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN)
public void createCredentialWithOrigin(
@NonNull CreateCredentialRequest request,
@Nullable String origin,
@NonNull Activity activity,
@Nullable CancellationSignal cancellationSignal,
@CallbackExecutor @NonNull Executor executor,
@NonNull
OutcomeReceiver<CreateCredentialResponse, CreateCredentialException> callback) {
requireNonNull(request, "request must not be null");
requireNonNull(activity, "activity must not be null");
requireNonNull(executor, "executor must not be null");
requireNonNull(callback, "callback must not be null");
if (cancellationSignal != null && cancellationSignal.isCanceled()) {
Log.w(TAG, "createCredential already canceled");
return;
}
ICancellationSignal cancelRemote = null;
try {
cancelRemote =
mService.executeCreateCredentialWithOrigin(
request,
new CreateCredentialTransport(activity, executor, callback),
mContext.getOpPackageName(),
origin);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
if (cancellationSignal != null && cancelRemote != null) {
cancellationSignal.setRemote(cancelRemote);
}
}
/**
* Clears the current user credential state from all credential providers.
*

View File

@@ -40,8 +40,12 @@ interface ICredentialManager {
@nullable ICancellationSignal executeGetCredential(in GetCredentialRequest request, in IGetCredentialCallback callback, String callingPackage);
@nullable ICancellationSignal executeGetCredentialWithOrigin(in GetCredentialRequest request, in IGetCredentialCallback callback, String callingPackage, String origin);
@nullable ICancellationSignal executeCreateCredential(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage);
@nullable ICancellationSignal executeCreateCredentialWithOrigin(in CreateCredentialRequest request, in ICreateCredentialCallback callback, String callingPackage, String origin);
@nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
@nullable ICancellationSignal listEnabledProviders(in IListEnabledProvidersCallback callback);

View File

@@ -175,7 +175,8 @@ public final class CredentialProviderInfo {
serviceInfo.packageName,
PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
if (appInfo != null
&& context.checkPermission(Manifest.permission.SYSTEM_CREDENTIAL_PROVIDER,
&& context.checkPermission(
Manifest.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE,
/*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) {
services.add(new CredentialProviderInfo(context, serviceInfo,
/*isSystemProvider=*/true));

View File

@@ -4036,14 +4036,19 @@
<!-- Allows a system application to be registered with credential manager without
having to be enabled by the user.
@hide -->
<permission android:name="android.permission.SYSTEM_CREDENTIAL_PROVIDER"
@hide @SystemApi -->
<permission android:name="android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE"
android:protectionLevel="signature|privileged" />
<!-- Allows a browser to invoke credential manager APIs on behalf of another RP.
<p>Protection level: normal -->
<permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN"
android:protectionLevel="normal" />
<!-- Allows an application to be able to store and retrieve credentials from a remote
device.
@hide -->
<permission android:name="android.permission.HYBRID_CREDENTIAL_PROVIDER"
@hide @SystemApi -->
<permission android:name="android.permission.PROVIDE_HYBRID_CREDENTIAL_SERVICE"
android:protectionLevel="signature|privileged" />
<!-- ========================================= -->

View File

@@ -16,10 +16,12 @@
package com.android.server.credentials;
import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
import static android.content.Context.CREDENTIAL_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -316,22 +318,26 @@ public final class CredentialManagerService
CredentialDescriptionRegistry.clearUserSession(user.getUserIdentifier());
}
private CallingAppInfo constructCallingAppInfo(String packageName, int userId) {
private CallingAppInfo constructCallingAppInfo(
String realPackageName,
int userId,
@Nullable String origin) {
final PackageInfo packageInfo;
String actualPackageName = origin == null ? realPackageName : origin;
try {
packageInfo =
getContext()
.getPackageManager()
.getPackageInfoAsUser(
packageName,
PackageManager.PackageInfoFlags.of(
PackageManager.GET_SIGNING_CERTIFICATES),
userId);
getContext()
.getPackageManager()
.getPackageInfoAsUser(
actualPackageName,
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);
return new CallingAppInfo(actualPackageName, null);
}
return new CallingAppInfo(packageName, packageInfo.signingInfo);
return new CallingAppInfo(actualPackageName, packageInfo.signingInfo);
}
final class CredentialManagerServiceStub extends ICredentialManager.Stub {
@@ -355,59 +361,97 @@ public final class CredentialManagerService
callingUid,
callback,
request,
constructCallingAppInfo(callingPackage, userId),
constructCallingAppInfo(callingPackage, userId, null),
CancellationSignal.fromTransport(cancelTransport));
processGetCredential(request, callback, session);
return cancelTransport;
}
public ICancellationSignal executeGetCredentialWithOrigin(
GetCredentialRequest request,
IGetCredentialCallback callback,
final String callingPackage,
final String origin) {
Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
// Check privileged permissions
mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
final int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid();
enforceCallingPackage(callingPackage, callingUid);
// New request session, scoped for this request only.
final GetRequestSession session =
new GetRequestSession(
getContext(),
userId,
callingUid,
callback,
request,
constructCallingAppInfo(callingPackage, userId, origin),
CancellationSignal.fromTransport(cancelTransport));
processGetCredential(request, callback, session);
return cancelTransport;
}
private void processGetCredential(
GetCredentialRequest request,
IGetCredentialCallback callback,
GetRequestSession session) {
List<ProviderSession> providerSessions;
// TODO(b/268143699): temporarily disable the flag due to bug.
if (false) {
List<CredentialOption> optionsThatRequireActiveCredentials =
request.getCredentialOptions().stream()
.filter(
getCredentialOption ->
!TextUtils.isEmpty(
getCredentialOption
.getCredentialRetrievalData()
.getString(
CredentialOption
.FLATTENED_REQUEST,
null)))
.toList();
.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)))
.toList();
.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))
.collect(Collectors.toList()),
getFilteredResultFromRegistry(optionsThatRequireActiveCredentials));
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()));
session,
optionsThatDoNotRequireActiveCredentials.stream()
.map(CredentialOption::getType)
.collect(Collectors.toList()));
Set<ProviderSession> all = new LinkedHashSet<>();
all.addAll(sessionsWithRemoteService);
@@ -417,11 +461,11 @@ public final class CredentialManagerService
} else {
// Initiate all provider sessions
providerSessions =
initiateProviderSessions(
session,
request.getCredentialOptions().stream()
.map(CredentialOption::getType)
.collect(Collectors.toList()));
initiateProviderSessions(
session,
request.getCredentialOptions().stream()
.map(CredentialOption::getType)
.collect(Collectors.toList()));
}
if (providerSessions.isEmpty()) {
@@ -433,13 +477,11 @@ public final class CredentialManagerService
Log.i(
TAG,
"Issue invoking onError on IGetCredentialCallback "
+ "callback: "
+ e.getMessage());
+ "callback: "
+ e.getMessage());
}
}
providerSessions.forEach(ProviderSession::invokeSession);
return cancelTransport;
}
@Override
@@ -462,9 +504,47 @@ public final class CredentialManagerService
callingUid,
request,
callback,
constructCallingAppInfo(callingPackage, userId),
constructCallingAppInfo(callingPackage, userId, null),
CancellationSignal.fromTransport(cancelTransport));
processCreateCredential(request, callback, session);
return cancelTransport;
}
public ICancellationSignal executeCreateCredentialWithOrigin(
CreateCredentialRequest request,
ICreateCredentialCallback callback,
String callingPackage,
String origin) {
Log.i(TAG, "starting executeCreateCredential with callingPackage: " + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
// Check privileged permissions
mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
final int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid();
enforceCallingPackage(callingPackage, callingUid);
// New request session, scoped for this request only.
final CreateRequestSession session =
new CreateRequestSession(
getContext(),
userId,
callingUid,
request,
callback,
constructCallingAppInfo(callingPackage, userId, origin),
CancellationSignal.fromTransport(cancelTransport));
processCreateCredential(request, callback, session);
return cancelTransport;
}
private void processCreateCredential(
CreateCredentialRequest request,
ICreateCredentialCallback callback,
CreateRequestSession session) {
// Initiate all provider sessions
List<ProviderSession> providerSessions =
initiateProviderSessions(session, List.of(request.getType()));
@@ -478,14 +558,13 @@ public final class CredentialManagerService
Log.i(
TAG,
"Issue invoking onError on ICreateCredentialCallback "
+ "callback: "
+ e.getMessage());
+ "callback: "
+ e.getMessage());
}
}
// Iterate over all provider sessions and invoke the request
providerSessions.forEach(ProviderSession::invokeSession);
return cancelTransport;
}
@SuppressWarnings("GuardedBy") // ErrorProne requires listEnabledProviders
@@ -630,7 +709,7 @@ public final class CredentialManagerService
callingUid,
callback,
request,
constructCallingAppInfo(callingPackage, userId),
constructCallingAppInfo(callingPackage, userId, null),
CancellationSignal.fromTransport(cancelTransport));
// Initiate all provider sessions