Merge "Refactor origin to request object for the credential manager privilege APIs" into udc-dev

This commit is contained in:
TreeHugger Robot
2023-02-24 03:55:16 +00:00
committed by Android (Google) Code Review
9 changed files with 277 additions and 209 deletions

View File

@@ -13577,17 +13577,26 @@ package android.credentials {
} }
public final class CreateCredentialRequest implements android.os.Parcelable { public final class CreateCredentialRequest implements android.os.Parcelable {
ctor public CreateCredentialRequest(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.Bundle, boolean, boolean);
method public boolean alwaysSendAppInfoToProvider(); method public boolean alwaysSendAppInfoToProvider();
method public int describeContents(); method public int describeContents();
method @NonNull public android.os.Bundle getCandidateQueryData(); method @NonNull public android.os.Bundle getCandidateQueryData();
method @NonNull public android.os.Bundle getCredentialData(); method @NonNull public android.os.Bundle getCredentialData();
method @Nullable public String getOrigin();
method @NonNull public String getType(); method @NonNull public String getType();
method public boolean isSystemProviderRequired(); method public boolean isSystemProviderRequired();
method public void writeToParcel(@NonNull android.os.Parcel, int); method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CreateCredentialRequest> CREATOR; field @NonNull public static final android.os.Parcelable.Creator<android.credentials.CreateCredentialRequest> CREATOR;
} }
public static final class CreateCredentialRequest.Builder {
ctor public CreateCredentialRequest.Builder(@NonNull android.os.Bundle, @NonNull android.os.Bundle);
method @NonNull public android.credentials.CreateCredentialRequest build();
method @NonNull public android.credentials.CreateCredentialRequest.Builder setAlwaysSendAppInfoToProvider(boolean);
method @NonNull public android.credentials.CreateCredentialRequest.Builder setIsSystemProviderRequired(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN) public android.credentials.CreateCredentialRequest.Builder setOrigin(@NonNull String);
method @NonNull public android.credentials.CreateCredentialRequest.Builder setType(@NonNull String);
}
public final class CreateCredentialResponse implements android.os.Parcelable { public final class CreateCredentialResponse implements android.os.Parcelable {
ctor public CreateCredentialResponse(@NonNull android.os.Bundle); ctor public CreateCredentialResponse(@NonNull android.os.Bundle);
method public int describeContents(); method public int describeContents();
@@ -13619,9 +13628,7 @@ package android.credentials {
public final class CredentialManager { 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 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 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 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 boolean isEnabledCredentialProviderService(@NonNull android.content.ComponentName);
method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest); method public void registerCredentialDescription(@NonNull android.credentials.RegisterCredentialDescriptionRequest);
method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest); method public void unregisterCredentialDescription(@NonNull android.credentials.UnregisterCredentialDescriptionRequest);
@@ -13656,6 +13663,7 @@ package android.credentials {
method public int describeContents(); method public int describeContents();
method @NonNull public java.util.List<android.credentials.CredentialOption> getCredentialOptions(); method @NonNull public java.util.List<android.credentials.CredentialOption> getCredentialOptions();
method @NonNull public android.os.Bundle getData(); method @NonNull public android.os.Bundle getData();
method @Nullable public String getOrigin();
method public void writeToParcel(@NonNull android.os.Parcel, int); method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialRequest> CREATOR; field @NonNull public static final android.os.Parcelable.Creator<android.credentials.GetCredentialRequest> CREATOR;
} }
@@ -13666,6 +13674,7 @@ package android.credentials {
method @NonNull public android.credentials.GetCredentialRequest build(); method @NonNull public android.credentials.GetCredentialRequest build();
method @NonNull public android.credentials.GetCredentialRequest.Builder setAlwaysSendAppInfoToProvider(boolean); method @NonNull public android.credentials.GetCredentialRequest.Builder setAlwaysSendAppInfoToProvider(boolean);
method @NonNull public android.credentials.GetCredentialRequest.Builder setCredentialOptions(@NonNull java.util.List<android.credentials.CredentialOption>); method @NonNull public android.credentials.GetCredentialRequest.Builder setCredentialOptions(@NonNull java.util.List<android.credentials.CredentialOption>);
method @NonNull @RequiresPermission(android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN) public android.credentials.GetCredentialRequest.Builder setOrigin(@NonNull String);
} }
public final class GetCredentialResponse implements android.os.Parcelable { public final class GetCredentialResponse implements android.os.Parcelable {
@@ -40563,7 +40572,9 @@ package android.service.credentials {
public final class CallingAppInfo implements android.os.Parcelable { public final class CallingAppInfo implements android.os.Parcelable {
ctor public CallingAppInfo(@NonNull String, @NonNull android.content.pm.SigningInfo); ctor public CallingAppInfo(@NonNull String, @NonNull android.content.pm.SigningInfo);
ctor public CallingAppInfo(@NonNull String, @NonNull android.content.pm.SigningInfo, @Nullable String);
method public int describeContents(); method public int describeContents();
method @Nullable public String getOrigin();
method @NonNull public String getPackageName(); method @NonNull public String getPackageName();
method @NonNull public android.content.pm.SigningInfo getSigningInfo(); method @NonNull public android.content.pm.SigningInfo getSigningInfo();
method public void writeToParcel(@NonNull android.os.Parcel, int); method public void writeToParcel(@NonNull android.os.Parcel, int);

View File

@@ -16,9 +16,14 @@
package android.credentials; package android.credentials;
import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
@@ -68,6 +73,14 @@ public final class CreateCredentialRequest implements Parcelable {
*/ */
private final boolean mIsSystemProviderRequired; private final boolean mIsSystemProviderRequired;
/**
* 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.
*/
@Nullable
private final String mOrigin;
/** /**
* Returns the requested credential type. * Returns the requested credential type.
*/ */
@@ -123,6 +136,14 @@ public final class CreateCredentialRequest implements Parcelable {
return mAlwaysSendAppInfoToProvider; return mAlwaysSendAppInfoToProvider;
} }
/**
* Returns the origin of the calling app if set otherwise returns null.
*/
@Nullable
public String getOrigin() {
return mOrigin;
}
@Override @Override
public void writeToParcel(@NonNull Parcel dest, int flags) { public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mType); dest.writeString8(mType);
@@ -130,6 +151,7 @@ public final class CreateCredentialRequest implements Parcelable {
dest.writeBundle(mCandidateQueryData); dest.writeBundle(mCandidateQueryData);
dest.writeBoolean(mIsSystemProviderRequired); dest.writeBoolean(mIsSystemProviderRequired);
dest.writeBoolean(mAlwaysSendAppInfoToProvider); dest.writeBoolean(mAlwaysSendAppInfoToProvider);
dest.writeString8(mOrigin);
} }
@Override @Override
@@ -146,6 +168,7 @@ public final class CreateCredentialRequest implements Parcelable {
+ ", isSystemProviderRequired=" + mIsSystemProviderRequired + ", isSystemProviderRequired=" + mIsSystemProviderRequired
+ ", alwaysSendAppInfoToProvider=" + ", alwaysSendAppInfoToProvider="
+ mAlwaysSendAppInfoToProvider + mAlwaysSendAppInfoToProvider
+ ", origin=" + mOrigin
+ "}"; + "}";
} }
@@ -165,21 +188,26 @@ public final class CreateCredentialRequest implements Parcelable {
* the query phase, and will only be sent along * the query phase, and will only be sent along
* with the final request, after the user has selected * with the final request, after the user has selected
* an entry on the UI. * an entry on the UI.
* @param origin the origin of the calling app. Callers of this special setter (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.
* *
* @throws IllegalArgumentException If type is empty. * @throws IllegalArgumentException If type is empty.
*/ */
public CreateCredentialRequest( private CreateCredentialRequest(
@NonNull String type, @NonNull String type,
@NonNull Bundle credentialData, @NonNull Bundle credentialData,
@NonNull Bundle candidateQueryData, @NonNull Bundle candidateQueryData,
boolean isSystemProviderRequired, boolean isSystemProviderRequired,
boolean alwaysSendAppInfoToProvider) { boolean alwaysSendAppInfoToProvider,
@NonNull String origin) {
mType = Preconditions.checkStringNotEmpty(type, "type must not be empty"); mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
mCredentialData = requireNonNull(credentialData, "credentialData must not be null"); mCredentialData = requireNonNull(credentialData, "credentialData must not be null");
mCandidateQueryData = requireNonNull(candidateQueryData, mCandidateQueryData = requireNonNull(candidateQueryData,
"candidateQueryData must not be null"); "candidateQueryData must not be null");
mIsSystemProviderRequired = isSystemProviderRequired; mIsSystemProviderRequired = isSystemProviderRequired;
mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider; mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider;
mOrigin = origin;
} }
private CreateCredentialRequest(@NonNull Parcel in) { private CreateCredentialRequest(@NonNull Parcel in) {
@@ -188,6 +216,7 @@ public final class CreateCredentialRequest implements Parcelable {
Bundle candidateQueryData = in.readBundle(); Bundle candidateQueryData = in.readBundle();
boolean isSystemProviderRequired = in.readBoolean(); boolean isSystemProviderRequired = in.readBoolean();
boolean alwaysSendAppInfoToProvider = in.readBoolean(); boolean alwaysSendAppInfoToProvider = in.readBoolean();
mOrigin = in.readString8();
mType = type; mType = type;
AnnotationValidations.validate(NonNull.class, null, mType); AnnotationValidations.validate(NonNull.class, null, mType);
@@ -211,4 +240,104 @@ public final class CreateCredentialRequest implements Parcelable {
return new CreateCredentialRequest(in); return new CreateCredentialRequest(in);
} }
}; };
/** A builder for {@link CreateCredentialRequest}. */
public static final class Builder {
private boolean mAlwaysSendAppInfoToProvider;
@NonNull
private String mType;
@NonNull
private final Bundle mCredentialData;
@NonNull
private final Bundle mCandidateQueryData;
private boolean mIsSystemProviderRequired;
private String mOrigin;
/**
* @param credentialData the full credential creation request data
* @param candidateQueryData the partial request data that will be sent to the provider
* during the initial creation candidate query stage
*/
public Builder(@NonNull Bundle credentialData, @NonNull Bundle candidateQueryData) {
mCredentialData = requireNonNull(credentialData,
"credentialData must not be null");
mCandidateQueryData = requireNonNull(candidateQueryData,
"candidateQueryData must not be null");
}
/**
* Sets a true/false value to determine if the calling app info should be
* removed from the request that is sent to the providers.
*
* Developers must set this to false if they wish to remove the
* {@link android.service.credentials.CallingAppInfo} from the query phases requests that
* providers receive. Note that the calling app info will still be sent in the
* final phase after the user has made a selection on the UI.
*
* If not set, the default value will be true and the calling app info will be
* propagated to the providers in every phase.
*/
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public CreateCredentialRequest.Builder setAlwaysSendAppInfoToProvider(boolean value) {
mAlwaysSendAppInfoToProvider = value;
return this;
}
/**
* Sets the requested credential type.
*/
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public CreateCredentialRequest.Builder setType(@NonNull String type) {
mType = type;
return this;
}
/**
* Sets whether the request must only be fulfilled by a system provider.
* This defaults to false
*/
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public CreateCredentialRequest.Builder setIsSystemProviderRequired(boolean value) {
mIsSystemProviderRequired = value;
return this;
}
/**
* Sets the origin of the calling app. Callers of this special setter (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. The permission check only happens later when this
* instance is passed and processed by the Credential Manager.
*/
@SuppressLint({"MissingGetterMatchingBuilder", "AndroidFrameworkRequiresPermission"})
@RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN)
@NonNull
public CreateCredentialRequest.Builder setOrigin(@NonNull String origin) {
mOrigin = origin;
return this;
}
/**
* Builds a {@link GetCredentialRequest}.
*
* @throws IllegalArgumentException If credentialOptions is empty.
*/
@NonNull
public CreateCredentialRequest build() {
Preconditions.checkStringNotEmpty(
mType,
"type must not be empty");
return new CreateCredentialRequest(mType, mCredentialData, mCandidateQueryData,
mIsSystemProviderRequired, mAlwaysSendAppInfoToProvider, mOrigin);
}
}
} }

View File

@@ -123,6 +123,10 @@ public final class CredentialManager {
* *
* <p>The execution can potentially launch UI flows to collect user consent to using a * <p>The execution can potentially launch UI flows to collect user consent to using a
* credential, display a picker when multiple credentials exist, etc. * credential, display a picker when multiple credentials exist, etc.
* Callers (e.g. browsers) may optionally set origin in {@link GetCredentialRequest} for an
* app different from their own, to be able to get credentials on behalf of that app. They would
* need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN}
* to use this functionality
* *
* @param request the request specifying type(s) of credentials to get from the user * @param request the request specifying type(s) of credentials to get from the user
* @param activity the activity used to launch any UI needed * @param activity the activity used to launch any UI needed
@@ -162,62 +166,15 @@ 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. * 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 * <p>The execution can potentially launch UI flows to collect user consent to creating or
* storing the new credential, etc. * storing the new credential, etc.
* Callers (e.g. browsers) may optionally set origin in {@link CreateCredentialRequest} for an
* app different from their own, to be able to get credentials on behalf of that app. They would
* need additional permission {@link CREDENTIAL_MANAGER_SET_ORIGIN}
* to use this functionality
* *
* @param request the request specifying type(s) of credentials to get from the user * @param request the request specifying type(s) of credentials to get from the user
* @param activity the activity used to launch any UI needed * @param activity the activity used to launch any UI needed
@@ -258,58 +215,6 @@ 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. * Clears the current user credential state from all credential providers.
* *

View File

@@ -16,9 +16,13 @@
package android.credentials; package android.credentials;
import static android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
@@ -48,6 +52,14 @@ public final class GetCredentialRequest implements Parcelable {
@NonNull @NonNull
private final Bundle mData; private final Bundle mData;
/**
* 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.
*/
@Nullable
private String mOrigin;
/** /**
* True/False value to determine if the calling app info should be * True/False value to determine if the calling app info should be
* removed from the request that is sent to the providers. * removed from the request that is sent to the providers.
@@ -75,6 +87,14 @@ public final class GetCredentialRequest implements Parcelable {
return mData; return mData;
} }
/**
* Returns the origin of the calling app if set otherwise returns null.
*/
@Nullable
public String getOrigin() {
return mOrigin;
}
/** /**
* Returns a value to determine if the calling app info should be always * Returns a value to determine if the calling app info should be always
* sent to the provider in every phase (if true), or should be removed * sent to the provider in every phase (if true), or should be removed
@@ -90,6 +110,7 @@ public final class GetCredentialRequest implements Parcelable {
dest.writeTypedList(mCredentialOptions, flags); dest.writeTypedList(mCredentialOptions, flags);
dest.writeBundle(mData); dest.writeBundle(mData);
dest.writeBoolean(mAlwaysSendAppInfoToProvider); dest.writeBoolean(mAlwaysSendAppInfoToProvider);
dest.writeString8(mOrigin);
} }
@Override @Override
@@ -103,11 +124,12 @@ public final class GetCredentialRequest implements Parcelable {
+ ", data=" + mData + ", data=" + mData
+ ", alwaysSendAppInfoToProvider=" + ", alwaysSendAppInfoToProvider="
+ mAlwaysSendAppInfoToProvider + mAlwaysSendAppInfoToProvider
+ ", origin=" + mOrigin
+ "}"; + "}";
} }
private GetCredentialRequest(@NonNull List<CredentialOption> credentialOptions, private GetCredentialRequest(@NonNull List<CredentialOption> credentialOptions,
@NonNull Bundle data, @NonNull boolean alwaysSendAppInfoToProvider) { @NonNull Bundle data, @NonNull boolean alwaysSendAppInfoToProvider, String origin) {
Preconditions.checkCollectionNotEmpty( Preconditions.checkCollectionNotEmpty(
credentialOptions, credentialOptions,
/*valueName=*/ "credentialOptions"); /*valueName=*/ "credentialOptions");
@@ -118,6 +140,7 @@ public final class GetCredentialRequest implements Parcelable {
mData = requireNonNull(data, mData = requireNonNull(data,
"data must not be null"); "data must not be null");
mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider; mAlwaysSendAppInfoToProvider = alwaysSendAppInfoToProvider;
mOrigin = origin;
} }
private GetCredentialRequest(@NonNull Parcel in) { private GetCredentialRequest(@NonNull Parcel in) {
@@ -132,6 +155,7 @@ public final class GetCredentialRequest implements Parcelable {
AnnotationValidations.validate(NonNull.class, null, mData); AnnotationValidations.validate(NonNull.class, null, mData);
mAlwaysSendAppInfoToProvider = in.readBoolean(); mAlwaysSendAppInfoToProvider = in.readBoolean();
mOrigin = in.readString8();
} }
@NonNull public static final Parcelable.Creator<GetCredentialRequest> CREATOR = @NonNull public static final Parcelable.Creator<GetCredentialRequest> CREATOR =
@@ -159,6 +183,8 @@ public final class GetCredentialRequest implements Parcelable {
@NonNull @NonNull
private boolean mAlwaysSendAppInfoToProvider = true; private boolean mAlwaysSendAppInfoToProvider = true;
private String mOrigin;
/** /**
* @param data the top request level data * @param data the top request level data
*/ */
@@ -208,6 +234,20 @@ public final class GetCredentialRequest implements Parcelable {
return this; return this;
} }
/**
* Sets the origin of the calling app. Callers of this special setter (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. The permission check only happens later when this
* instance is passed and processed by the Credential Manager.
*/
@SuppressLint({"MissingGetterMatchingBuilder", "AndroidFrameworkRequiresPermission"})
@RequiresPermission(CREDENTIAL_MANAGER_SET_ORIGIN)
@NonNull
public Builder setOrigin(@NonNull String origin) {
mOrigin = origin;
return this;
}
/** /**
* Builds a {@link GetCredentialRequest}. * Builds a {@link GetCredentialRequest}.
* *
@@ -222,7 +262,7 @@ public final class GetCredentialRequest implements Parcelable {
mCredentialOptions, mCredentialOptions,
/*valueName=*/ "credentialOptions"); /*valueName=*/ "credentialOptions");
return new GetCredentialRequest(mCredentialOptions, mData, return new GetCredentialRequest(mCredentialOptions, mData,
mAlwaysSendAppInfoToProvider); mAlwaysSendAppInfoToProvider, mOrigin);
} }
} }
} }

View File

@@ -41,12 +41,8 @@ interface ICredentialManager {
@nullable ICancellationSignal executeGetCredential(in GetCredentialRequest request, in IGetCredentialCallback callback, String callingPackage); @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 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 clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
@nullable ICancellationSignal listEnabledProviders(in IListEnabledProvidersCallback callback); @nullable ICancellationSignal listEnabledProviders(in IListEnabledProvidersCallback callback);

View File

@@ -17,6 +17,7 @@
package android.service.credentials; package android.service.credentials;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.SigningInfo; import android.content.pm.SigningInfo;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
@@ -32,6 +33,8 @@ import java.util.Objects;
public final class CallingAppInfo implements Parcelable { public final class CallingAppInfo implements Parcelable {
@NonNull private final String mPackageName; @NonNull private final String mPackageName;
@NonNull private final SigningInfo mSigningInfo; @NonNull private final SigningInfo mSigningInfo;
@Nullable
private final String mOrigin;
/** /**
* Constructs a new instance. * Constructs a new instance.
@@ -41,14 +44,31 @@ public final class CallingAppInfo implements Parcelable {
*/ */
public CallingAppInfo(@NonNull String packageName, public CallingAppInfo(@NonNull String packageName,
@NonNull SigningInfo signingInfo) { @NonNull SigningInfo signingInfo) {
this(packageName, signingInfo, /*origin=*/ null);
}
/**
* Constructs a new instance.
*
* @param packageName - the package name of the calling app
* @param signingInfo - the signing info on the calling app
* @param origin - the origin that the calling app wants to use when making request on behalf of
* other
* @throws IllegalArgumentException If {@code packageName} is null or empty.
* @throws NullPointerException If {@code signingInfo} is null.
*/
public CallingAppInfo(@NonNull String packageName,
@NonNull SigningInfo signingInfo, @Nullable String origin) {
mPackageName = Preconditions.checkStringNotEmpty(packageName, "package name" mPackageName = Preconditions.checkStringNotEmpty(packageName, "package name"
+ "must not be null or empty"); + "must not be null or empty");
mSigningInfo = Objects.requireNonNull(signingInfo); mSigningInfo = Objects.requireNonNull(signingInfo);
mOrigin = origin;
} }
private CallingAppInfo(@NonNull Parcel in) { private CallingAppInfo(@NonNull Parcel in) {
mPackageName = in.readString8(); mPackageName = in.readString8();
mSigningInfo = in.readTypedObject(SigningInfo.CREATOR); mSigningInfo = in.readTypedObject(SigningInfo.CREATOR);
mOrigin = in.readString8();
} }
public static final @NonNull Creator<CallingAppInfo> CREATOR = new Creator<CallingAppInfo>() { public static final @NonNull Creator<CallingAppInfo> CREATOR = new Creator<CallingAppInfo>() {
@@ -76,6 +96,22 @@ public final class CallingAppInfo implements Parcelable {
return mSigningInfo; return mSigningInfo;
} }
/**
* Returns the origin of the calling app if set otherwise returns null.
* This value is set only if the origin is different than that of the calling app,
* and should be expected from privileged callers(browsers) only when making request on behalf
* of other applications.
*
* Android system makes sure that only applications that poses the permission
* {@link android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN} can set the origin on
* the incoming {@link android.credentials.GetCredentialRequest} or
* {@link android.credentials.CreateCredentialRequest}.
*/
@Nullable
public String getOrigin() {
return mOrigin;
}
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;
@@ -85,6 +121,7 @@ public final class CallingAppInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) { public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mPackageName); dest.writeString8(mPackageName);
dest.writeTypedObject(mSigningInfo, flags); dest.writeTypedObject(mSigningInfo, flags);
dest.writeString8(mOrigin);
} }
@Override @Override
@@ -97,6 +134,7 @@ public final class CallingAppInfo implements Parcelable {
} else { } else {
builder.append(", mSigningInfo: null"); builder.append(", mSigningInfo: null");
} }
builder.append(",mOrigin: " + mOrigin);
builder.append(" }"); builder.append(" }");
return builder.toString(); return builder.toString();
} }

View File

@@ -101,8 +101,11 @@ public class CredentialManagerTest {
mGetRequest = new GetCredentialRequest.Builder(Bundle.EMPTY).addCredentialOption( mGetRequest = new GetCredentialRequest.Builder(Bundle.EMPTY).addCredentialOption(
new CredentialOption(Credential.TYPE_PASSWORD_CREDENTIAL, Bundle.EMPTY, new CredentialOption(Credential.TYPE_PASSWORD_CREDENTIAL, Bundle.EMPTY,
Bundle.EMPTY, false)).build(); Bundle.EMPTY, false)).build();
mCreateRequest = new CreateCredentialRequest(Credential.TYPE_PASSWORD_CREDENTIAL, mCreateRequest = new CreateCredentialRequest.Builder(Bundle.EMPTY, Bundle.EMPTY)
Bundle.EMPTY, Bundle.EMPTY, false, false); .setType(Credential.TYPE_PASSWORD_CREDENTIAL)
.setIsSystemProviderRequired(false)
.setAlwaysSendAppInfoToProvider(false)
.build();
mClearRequest = new ClearCredentialStateRequest(Bundle.EMPTY); mClearRequest = new ClearCredentialStateRequest(Bundle.EMPTY);
final Slice slice = new Slice.Builder(Uri.parse("foo://bar"), null).addText("some text", final Slice slice = new Slice.Builder(Uri.parse("foo://bar"), null).addText("some text",

View File

@@ -403,13 +403,11 @@ class CredentialManagerRepo(
val credentialData = request.credentialData val credentialData = request.credentialData
return RequestInfo.newCreateRequestInfo( return RequestInfo.newCreateRequestInfo(
Binder(), Binder(),
CreateCredentialRequest( CreateCredentialRequest.Builder(credentialData, Bundle())
"androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL", .setType("androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL")
credentialData, .setIsSystemProviderRequired(false)
/*candidateQueryData=*/ Bundle(), .setAlwaysSendAppInfoToProvider(true)
/*isSystemProviderRequired=*/ false, .build(),
/*alwaysSendAppInfoToProvider=*/ true
),
"com.google.android.youtube" "com.google.android.youtube"
) )
} }
@@ -418,13 +416,11 @@ class CredentialManagerRepo(
val request = CreatePasswordRequest("beckett-bakert@gmail.com", "password123") val request = CreatePasswordRequest("beckett-bakert@gmail.com", "password123")
return RequestInfo.newCreateRequestInfo( return RequestInfo.newCreateRequestInfo(
Binder(), Binder(),
CreateCredentialRequest( CreateCredentialRequest.Builder(request.credentialData, request.candidateQueryData)
TYPE_PASSWORD_CREDENTIAL, .setType(TYPE_PASSWORD_CREDENTIAL)
request.credentialData, .setIsSystemProviderRequired(false)
request.candidateQueryData, .setAlwaysSendAppInfoToProvider(true)
/*isSystemProviderRequired=*/ false, .build(),
/*alwaysSendAppInfoToProvider=*/ true
),
"com.google.android.youtube" "com.google.android.youtube"
) )
} }
@@ -438,13 +434,11 @@ class CredentialManagerRepo(
) )
return RequestInfo.newCreateRequestInfo( return RequestInfo.newCreateRequestInfo(
Binder(), Binder(),
CreateCredentialRequest( CreateCredentialRequest.Builder(data, Bundle())
"other-sign-ins", .setType("other-sign-ins")
data, .setIsSystemProviderRequired(false)
/*candidateQueryData=*/ Bundle(), .setAlwaysSendAppInfoToProvider(true)
/*isSystemProviderRequired=*/ false, .build(),
/*alwaysSendAppInfoToProvider=*/ true
),
"com.google.android.youtube" "com.google.android.youtube"
) )
} }

View File

@@ -360,21 +360,22 @@ public final class CredentialManagerService
int userId, int userId,
@Nullable String origin) { @Nullable String origin) {
final PackageInfo packageInfo; final PackageInfo packageInfo;
String actualPackageName = origin == null ? realPackageName : origin; CallingAppInfo callingAppInfo;
try { try {
packageInfo = packageInfo =
getContext() getContext()
.getPackageManager() .getPackageManager()
.getPackageInfoAsUser( .getPackageInfoAsUser(
actualPackageName, realPackageName,
PackageManager.PackageInfoFlags.of( PackageManager.PackageInfoFlags.of(
PackageManager.GET_SIGNING_CERTIFICATES), PackageManager.GET_SIGNING_CERTIFICATES),
userId); userId);
callingAppInfo = new CallingAppInfo(realPackageName, packageInfo.signingInfo, origin);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
Log.i(TAG, "Issue while retrieving signatureInfo : " + e.getMessage()); Log.i(TAG, "Issue while retrieving signatureInfo : " + e.getMessage());
return new CallingAppInfo(actualPackageName, null); callingAppInfo = new CallingAppInfo(realPackageName, null, origin);
} }
return new CallingAppInfo(actualPackageName, packageInfo.signingInfo); return callingAppInfo;
} }
final class CredentialManagerServiceStub extends ICredentialManager.Stub { final class CredentialManagerServiceStub extends ICredentialManager.Stub {
@@ -386,34 +387,10 @@ public final class CredentialManagerService
Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage); Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport(); ICancellationSignal cancelTransport = CancellationSignal.createTransport();
final int userId = UserHandle.getCallingUserId(); if (request.getOrigin() != null) {
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, 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 // Check privileged permissions
mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null); mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
}
final int userId = UserHandle.getCallingUserId(); final int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid(); final int callingUid = Binder.getCallingUid();
@@ -427,7 +404,7 @@ public final class CredentialManagerService
callingUid, callingUid,
callback, callback,
request, request,
constructCallingAppInfo(callingPackage, userId, origin), constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
CancellationSignal.fromTransport(cancelTransport)); CancellationSignal.fromTransport(cancelTransport));
processGetCredential(request, callback, session); processGetCredential(request, callback, session);
@@ -519,35 +496,10 @@ public final class CredentialManagerService
+ callingPackage); + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport(); ICancellationSignal cancelTransport = CancellationSignal.createTransport();
final int userId = UserHandle.getCallingUserId(); if (request.getOrigin() != null) {
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, 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 // Check privileged permissions
mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null); mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ORIGIN, null);
}
final int userId = UserHandle.getCallingUserId(); final int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid(); final int callingUid = Binder.getCallingUid();
@@ -561,7 +513,7 @@ public final class CredentialManagerService
callingUid, callingUid,
request, request,
callback, callback,
constructCallingAppInfo(callingPackage, userId, origin), constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
CancellationSignal.fromTransport(cancelTransport)); CancellationSignal.fromTransport(cancelTransport));
processCreateCredential(request, callback, session); processCreateCredential(request, callback, session);