Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fee24eaa authored by Reema Bajwa's avatar Reema Bajwa
Browse files

Add system service support to credential manager

Test: Built locally

Change-Id: Idb3e3983ed3194349fdf91dc0d43f40054cb40c9
parent 3af2f318
Loading
Loading
Loading
Loading
+52 −4
Original line number Diff line number Diff line
@@ -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);
            }
+14 −0
Original line number Diff line number Diff line
@@ -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() {
+12 −0
Original line number Diff line number Diff line
@@ -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 -->
    <!-- ========================================= -->
+62 −17
Original line number Diff line number Diff line
@@ -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
                    providerCreateSession -> providerCreateSession
                            .getRemoteCredentialService()
                            .onCreateCredential(
                                    (BeginCreateCredentialRequest)
                                            providerCreateSession.getProviderRequest(),
                                        /* callback= */ providerCreateSession);
                    });
                                    /* callback= */ providerCreateSession));
            return cancelTransport;
        }

+15 −2
Original line number Diff line number Diff line
@@ -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();
    }