Merge "Add system service support to credential manager"
This commit is contained in:
@@ -24,6 +24,7 @@ import android.app.AppGlobals;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
@@ -58,6 +59,7 @@ public final class CredentialProviderInfo {
|
||||
private final Drawable mIcon;
|
||||
@Nullable
|
||||
private final CharSequence mLabel;
|
||||
private final boolean mIsSystemProvider;
|
||||
|
||||
/**
|
||||
* Constructs an information instance of the credential provider.
|
||||
@@ -65,13 +67,14 @@ public final class CredentialProviderInfo {
|
||||
* @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
|
||||
* @param isSystemProvider whether this provider is a system provider
|
||||
* @throws PackageManager.NameNotFoundException If provider service is not found
|
||||
* @throws SecurityException If provider does not require the relevant permission
|
||||
*/
|
||||
public CredentialProviderInfo(@NonNull Context context,
|
||||
@NonNull ComponentName serviceComponent, int userId)
|
||||
@NonNull ComponentName serviceComponent, int userId, boolean isSystemProvider)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
this(context, getServiceInfoOrThrow(serviceComponent, userId));
|
||||
this(context, getServiceInfoOrThrow(serviceComponent, userId), isSystemProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,8 +82,11 @@ public final class CredentialProviderInfo {
|
||||
* @param context the context object
|
||||
* @param serviceInfo the service info for the provider app. This must be retrieved from the
|
||||
* {@code PackageManager}
|
||||
* @param isSystemProvider whether the provider is a system app or not
|
||||
*/
|
||||
public CredentialProviderInfo(@NonNull Context context, @NonNull ServiceInfo serviceInfo) {
|
||||
public CredentialProviderInfo(@NonNull Context context,
|
||||
@NonNull ServiceInfo serviceInfo,
|
||||
boolean isSystemProvider) {
|
||||
if (!Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE.equals(serviceInfo.permission)) {
|
||||
Log.i(TAG, "Credential Provider Service from : " + serviceInfo.packageName
|
||||
+ "does not require permission"
|
||||
@@ -95,6 +101,7 @@ public final class CredentialProviderInfo {
|
||||
mLabel = mServiceInfo.loadSafeLabel(
|
||||
mContext.getPackageManager(), 0 /* do not ellipsize */,
|
||||
TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
|
||||
mIsSystemProvider = isSystemProvider;
|
||||
Log.i(TAG, "mLabel is : " + mLabel + ", for: " + mServiceInfo.getComponentName()
|
||||
.flattenToString());
|
||||
populateProviderCapabilities(context, serviceInfo);
|
||||
@@ -146,6 +153,42 @@ public final class CredentialProviderInfo {
|
||||
throw new PackageManager.NameNotFoundException(serviceComponent.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the valid credential provider services available for the user with the
|
||||
* given {@code userId}.
|
||||
*/
|
||||
@NonNull
|
||||
public static List<CredentialProviderInfo> getAvailableSystemServices(
|
||||
@NonNull Context context,
|
||||
@UserIdInt int userId) {
|
||||
final List<CredentialProviderInfo> services = new ArrayList<>();
|
||||
|
||||
final List<ResolveInfo> resolveInfos =
|
||||
context.getPackageManager().queryIntentServicesAsUser(
|
||||
new Intent(CredentialProviderService.SYSTEM_SERVICE_INTERFACE),
|
||||
PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
|
||||
userId);
|
||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
try {
|
||||
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
|
||||
serviceInfo.packageName,
|
||||
PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
|
||||
if (appInfo != null
|
||||
&& context.checkPermission(Manifest.permission.SYSTEM_CREDENTIAL_PROVIDER,
|
||||
/*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) {
|
||||
services.add(new CredentialProviderInfo(context, serviceInfo,
|
||||
/*isSystemProvider=*/true));
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Log.i(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.i(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
}
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the service supports the given {@code credentialType}, false otherwise.
|
||||
*/
|
||||
@@ -160,6 +203,10 @@ public final class CredentialProviderInfo {
|
||||
return mServiceInfo;
|
||||
}
|
||||
|
||||
public boolean isSystemProvider() {
|
||||
return mIsSystemProvider;
|
||||
}
|
||||
|
||||
/** Returns the service icon. */
|
||||
@Nullable
|
||||
public Drawable getServiceIcon() {
|
||||
@@ -195,7 +242,8 @@ public final class CredentialProviderInfo {
|
||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
|
||||
try {
|
||||
services.add(new CredentialProviderInfo(context, serviceInfo));
|
||||
services.add(new CredentialProviderInfo(context,
|
||||
serviceInfo, false));
|
||||
} catch (SecurityException e) {
|
||||
Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e);
|
||||
}
|
||||
|
||||
@@ -140,6 +140,20 @@ public abstract class CredentialProviderService extends Service {
|
||||
public static final String SERVICE_INTERFACE =
|
||||
"android.service.credentials.CredentialProviderService";
|
||||
|
||||
/**
|
||||
* The {@link Intent} that must be declared as handled by a system credential provider
|
||||
* service.
|
||||
*
|
||||
* <p>The service must also require the
|
||||
* {android.Manifest.permission#BIND_CREDENTIAL_PROVIDER_SERVICE} permission
|
||||
* so that only the system can bind to it.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
|
||||
public static final String SYSTEM_SERVICE_INTERFACE =
|
||||
"android.service.credentials.system.CredentialProviderService";
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onCreate() {
|
||||
|
||||
@@ -3993,6 +3993,18 @@
|
||||
<permission android:name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- 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"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- Allows an application to be able to store and retrieve credentials from a remote
|
||||
device.
|
||||
@hide -->
|
||||
<permission android:name="android.permission.HYBRID_CREDENTIAL_PROVIDER"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- ========================================= -->
|
||||
<!-- Permissions for special development tools -->
|
||||
<!-- ========================================= -->
|
||||
|
||||
@@ -50,6 +50,7 @@ import android.service.credentials.CredentialProviderInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.infra.AbstractMasterSystemService;
|
||||
@@ -73,6 +74,16 @@ public final class CredentialManagerService
|
||||
|
||||
private static final String TAG = "CredManSysService";
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
@@ -80,6 +91,20 @@ public final class CredentialManagerService
|
||||
context, Settings.Secure.CREDENTIAL_SERVICE, /* isMultipleMode= */ true),
|
||||
null,
|
||||
PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@GuardedBy("mLock")
|
||||
private List<CredentialManagerServiceImpl> constructSystemServiceListLocked(
|
||||
int resolvedUserId) {
|
||||
List<CredentialManagerServiceImpl> services = new ArrayList<>();
|
||||
List<CredentialProviderInfo> credentialProviderInfos =
|
||||
CredentialProviderInfo.getAvailableSystemServices(mContext, resolvedUserId);
|
||||
credentialProviderInfos.forEach(info -> {
|
||||
services.add(new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
|
||||
});
|
||||
return services;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,8 +130,10 @@ public final class CredentialManagerService
|
||||
}
|
||||
|
||||
@Override // from AbstractMasterSystemService
|
||||
@GuardedBy("mLock")
|
||||
protected List<CredentialManagerServiceImpl> newServiceListLocked(
|
||||
int resolvedUserId, boolean disabled, String[] serviceNames) {
|
||||
getOrConstructSystemServiceListLock(resolvedUserId);
|
||||
if (serviceNames == null || serviceNames.length == 0) {
|
||||
Slog.i(TAG, "serviceNames sent in newServiceListLocked is null, or empty");
|
||||
return new ArrayList<>();
|
||||
@@ -155,13 +182,24 @@ public final class CredentialManagerService
|
||||
// TODO("Iterate over system services and remove if needed")
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private List<CredentialManagerServiceImpl> getOrConstructSystemServiceListLock(
|
||||
int resolvedUserId) {
|
||||
List<CredentialManagerServiceImpl> services = mSystemServicesCacheList.get(resolvedUserId);
|
||||
if (services == null || services.size() == 0) {
|
||||
services = constructSystemServiceListLocked(resolvedUserId);
|
||||
mSystemServicesCacheList.put(resolvedUserId, services);
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
private void runForUser(@NonNull final Consumer<CredentialManagerServiceImpl> c) {
|
||||
final int userId = UserHandle.getCallingUserId();
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
final List<CredentialManagerServiceImpl> services =
|
||||
getServiceListForUserLocked(userId);
|
||||
getAllCredentialProviderServicesLocked(userId);
|
||||
for (CredentialManagerServiceImpl s : services) {
|
||||
c.accept(s);
|
||||
}
|
||||
@@ -171,6 +209,19 @@ public final class CredentialManagerService
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private List<CredentialManagerServiceImpl> getAllCredentialProviderServicesLocked(
|
||||
int userId) {
|
||||
List<CredentialManagerServiceImpl> concatenatedServices = new ArrayList<>();
|
||||
List<CredentialManagerServiceImpl> userConfigurableServices =
|
||||
getServiceListForUserLocked(userId);
|
||||
if (userConfigurableServices != null && !userConfigurableServices.isEmpty()) {
|
||||
concatenatedServices.addAll(userConfigurableServices);
|
||||
}
|
||||
concatenatedServices.addAll(getOrConstructSystemServiceListLock(userId));
|
||||
return concatenatedServices;
|
||||
}
|
||||
|
||||
@SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
|
||||
// to be guarded by 'service.mLock', which is the same as mLock.
|
||||
private List<ProviderSession> initiateProviderSessions(
|
||||
@@ -248,14 +299,10 @@ public final class CredentialManagerService
|
||||
|
||||
// Iterate over all provider sessions and invoke the request
|
||||
providerSessions.forEach(
|
||||
providerGetSession -> {
|
||||
providerGetSession
|
||||
.getRemoteCredentialService()
|
||||
.onBeginGetCredential(
|
||||
(BeginGetCredentialRequest)
|
||||
providerGetSession.getProviderRequest(),
|
||||
/* callback= */ providerGetSession);
|
||||
});
|
||||
providerGetSession -> providerGetSession
|
||||
.getRemoteCredentialService().onBeginGetCredential(
|
||||
(BeginGetCredentialRequest) providerGetSession.getProviderRequest(),
|
||||
/*callback=*/providerGetSession));
|
||||
return cancelTransport;
|
||||
}
|
||||
|
||||
@@ -297,14 +344,12 @@ public final class CredentialManagerService
|
||||
|
||||
// Iterate over all provider sessions and invoke the request
|
||||
providerSessions.forEach(
|
||||
providerCreateSession -> {
|
||||
providerCreateSession
|
||||
.getRemoteCredentialService()
|
||||
.onCreateCredential(
|
||||
(BeginCreateCredentialRequest)
|
||||
providerCreateSession.getProviderRequest(),
|
||||
/* callback= */ providerCreateSession);
|
||||
});
|
||||
providerCreateSession -> providerCreateSession
|
||||
.getRemoteCredentialService()
|
||||
.onCreateCredential(
|
||||
(BeginCreateCredentialRequest)
|
||||
providerCreateSession.getProviderRequest(),
|
||||
/* callback= */ providerCreateSession));
|
||||
return cancelTransport;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,18 @@ public final class CredentialManagerServiceImpl extends
|
||||
return mInfo.getServiceInfo().getComponentName();
|
||||
}
|
||||
|
||||
@Override // from PerUserSystemService
|
||||
CredentialManagerServiceImpl(
|
||||
@NonNull CredentialManagerService master,
|
||||
@NonNull Object lock, int userId, CredentialProviderInfo providerInfo) {
|
||||
super(master, lock, userId);
|
||||
Log.i(TAG, "in CredentialManagerServiceImpl constructed with system constructor: "
|
||||
+ providerInfo.isSystemProvider()
|
||||
+ " , " + providerInfo.getServiceInfo() == null ? "" :
|
||||
providerInfo.getServiceInfo().getComponentName().flattenToString());
|
||||
mInfo = providerInfo;
|
||||
}
|
||||
|
||||
@Override // from PerUserSystemService when a new setting based service is to be created
|
||||
@GuardedBy("mLock")
|
||||
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
@@ -71,7 +82,9 @@ public final class CredentialManagerServiceImpl extends
|
||||
Log.i(TAG, "newServiceInfoLocked with null mInfo , "
|
||||
+ serviceComponent.getPackageName());
|
||||
}
|
||||
mInfo = new CredentialProviderInfo(getContext(), serviceComponent, mUserId);
|
||||
mInfo = new CredentialProviderInfo(
|
||||
getContext(), serviceComponent,
|
||||
mUserId, /*isSystemProvider=*/false);
|
||||
return mInfo.getServiceInfo();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user