diff options
| author | 2022-12-21 02:17:39 +0000 | |
|---|---|---|
| committer | 2023-01-06 23:10:16 +0000 | |
| commit | fee24eaa743bc2ededaafb470d1d77133e431767 (patch) | |
| tree | 44d84e758365d14019789ed2bf6145831b6e044e | |
| parent | 3af2f3184506c340b36858192f9fe54a83915254 (diff) | |
Add system service support to credential manager
Test: Built locally
Change-Id: Idb3e3983ed3194349fdf91dc0d43f40054cb40c9
5 files changed, 155 insertions, 23 deletions
diff --git a/core/java/android/service/credentials/CredentialProviderInfo.java b/core/java/android/service/credentials/CredentialProviderInfo.java index f89ad8e6e429..6a10a6ac891d 100644 --- a/core/java/android/service/credentials/CredentialProviderInfo.java +++ b/core/java/android/service/credentials/CredentialProviderInfo.java @@ -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); @@ -147,6 +154,42 @@ public final class CredentialProviderInfo { } /** + * 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. */ @NonNull @@ -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); } diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java index 41d20f26b381..a70659552543 100644 --- a/core/java/android/service/credentials/CredentialProviderService.java +++ b/core/java/android/service/credentials/CredentialProviderService.java @@ -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() { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index bfa530143380..6953a467f54b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3906,6 +3906,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 --> <!-- ========================================= --> diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 25e205b459d9..a1ae0263ba1b 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -48,6 +48,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; @@ -71,6 +72,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, @@ -78,6 +89,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 @@ -103,8 +128,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<>(); @@ -153,13 +180,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); } @@ -169,6 +207,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( @@ -231,14 +282,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; } @@ -279,14 +326,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; } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java index 0fd1f1929cae..546c48fe05f4 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java @@ -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(); } |