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

Commit 33eca2dd authored by Reema Bajwa's avatar Reema Bajwa Committed by Android (Google) Code Review
Browse files

Merge "Add system service support to credential manager"

parents 125d1c47 fee24eaa
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
@@ -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 -->
    <!-- ========================================= -->
+62 −17
Original line number Diff line number Diff line
@@ -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
                    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();
    }