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

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

Merge "Add CredentialProviderInfo class to get information about credential...

Merge "Add CredentialProviderInfo class to get information about credential providers on the device Test: manual - built & deployed locally Bug: 247545196"
parents b593125e be65ffae
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ package android {
    field public static final String BIND_COMPANION_DEVICE_SERVICE = "android.permission.BIND_COMPANION_DEVICE_SERVICE";
    field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
    field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS";
    field public static final String BIND_CREDENTIAL_PROVIDER_SERVICE = "android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE";
    field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
    field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
    field public static final String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
+184 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.credentials;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * {@link ServiceInfo} and meta-data about a credential provider.
 *
 * @hide
 */
public final class CredentialProviderInfo {
    private static final String TAG = "CredentialProviderInfo";

    @NonNull
    private final ServiceInfo mServiceInfo;
    @NonNull
    private final List<String> mCapabilities;

    // TODO: Move the two strings below to CredentialProviderService when ready.
    private static final String CAPABILITY_META_DATA_KEY = "android.credentials.capabilities";
    private static final String SERVICE_INTERFACE =
            "android.service.credentials.CredentialProviderService";


    /**
     * Constructs an information instance of the credential provider.
     *
     * @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
     * @throws PackageManager.NameNotFoundException If provider service is not found
     */
    public CredentialProviderInfo(@NonNull Context context,
            @NonNull ComponentName serviceComponent, int userId)
            throws PackageManager.NameNotFoundException {
        this(context, getServiceInfoOrThrow(serviceComponent, userId));
    }

    private CredentialProviderInfo(@NonNull Context context, @NonNull ServiceInfo serviceInfo) {
        if (!Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE.equals(serviceInfo.permission)) {
            Log.i(TAG, "Credential Provider Service from : " + serviceInfo.packageName
                    + "does not require permission"
                    + Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE);
            throw new SecurityException("Service does not require the expected permission : "
                    + Manifest.permission.BIND_CREDENTIAL_PROVIDER_SERVICE);
        }
        mServiceInfo = serviceInfo;
        mCapabilities = new ArrayList<>();
        populateProviderCapabilities(context);
    }

    private void populateProviderCapabilities(@NonNull Context context) {
        if (mServiceInfo.applicationInfo.metaData == null) {
            return;
        }
        try {
            final int resourceId = mServiceInfo.applicationInfo.metaData.getInt(
                    CAPABILITY_META_DATA_KEY);
            String[] capabilities = context.getResources().getStringArray(resourceId);
            if (capabilities == null) {
                Log.w(TAG, "No capabilities found for provider: " + mServiceInfo.packageName);
                return;
            }
            for (String capability : capabilities) {
                if (capability.isEmpty()) {
                    Log.w(TAG, "Skipping empty capability");
                    continue;
                }
                mCapabilities.add(capability);
            }
        } catch (Resources.NotFoundException e) {
            Log.w(TAG, "Exception while populating provider capabilities: " + e.getMessage());
        }
    }

    private static ServiceInfo getServiceInfoOrThrow(@NonNull ComponentName serviceComponent,
            int userId) throws PackageManager.NameNotFoundException {
        try {
            ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(
                    serviceComponent,
                    PackageManager.GET_META_DATA,
                    userId);
            if (si != null) {
                return si;
            }
        } catch (RemoteException e) {
            Slog.v(TAG, e.getMessage());
        }
        throw new PackageManager.NameNotFoundException(serviceComponent.toString());
    }

    /**
     * Returns true if the service supports the given {@code credentialType}, false otherwise.
     */
    @NonNull
    public boolean hasCapability(@NonNull String credentialType) {
        return mCapabilities.contains(credentialType);
    }

    /** Returns the service info. */
    @NonNull
    public ServiceInfo getServiceInfo() {
        return mServiceInfo;
    }

    /** Returns an immutable list of capabilities this provider service can support. */
    @NonNull
    public List<String> getCapabilities() {
        return Collections.unmodifiableList(mCapabilities);
    }

    /**
     * Returns the valid credential provider services available for the user with the
     * given {@code userId}.
     */
    public static List<CredentialProviderInfo> getAvailableServices(@NonNull Context context,
            @UserIdInt int userId) {
        final List<CredentialProviderInfo> services = new ArrayList<>();

        final List<ResolveInfo> resolveInfos =
                context.getPackageManager().queryIntentServicesAsUser(
                        new Intent(SERVICE_INTERFACE),
                        PackageManager.GET_META_DATA,
                        userId);
        for (ResolveInfo resolveInfo : resolveInfos) {
            final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
            try {
                services.add(new CredentialProviderInfo(context, serviceInfo));
            } catch (SecurityException e) {
                Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e);
            }
        }
        return services;
    }

    /**
     * Returns the valid credential provider services available for the user, that can
     * support the given {@code credentialType}.
     */
    public static List<CredentialProviderInfo> getAvailableServicesForCapability(
            Context context, @UserIdInt int userId, String credentialType) {
        List<CredentialProviderInfo> servicesForCapability = new ArrayList<>();
        final List<CredentialProviderInfo> services = getAvailableServices(context, userId);

        for (CredentialProviderInfo service : services) {
            if (service.hasCapability(credentialType)) {
                servicesForCapability.add(service);
            }
        }
        return servicesForCapability;
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -4260,6 +4260,13 @@
    <permission android:name="android.permission.BIND_AUTOFILL_SERVICE"
        android:protectionLevel="signature" />

    <!-- Must be required by a CredentialProviderService to ensure that only the
         system can bind to it.
         <p>Protection level: signature
    -->
    <permission android:name="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
                android:protectionLevel="signature" />

   <!-- Alternative version of android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE.
        This permission was renamed during the O previews but it was supported on the final O
        release, so we need to carry it over.
+20 −1
Original line number Diff line number Diff line
@@ -17,6 +17,11 @@
package com.android.server.credentials;

import android.annotation.NonNull;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.RemoteException;
import android.util.Log;

import com.android.server.infra.AbstractPerUserSystemService;
@@ -24,7 +29,7 @@ import com.android.server.infra.AbstractPerUserSystemService;
/**
 * Per-user implementation of {@link CredentialManagerService}
 */
public class CredentialManagerServiceImpl extends
public final class CredentialManagerServiceImpl extends
        AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> {
    private static final String TAG = "CredManSysServiceImpl";

@@ -34,6 +39,20 @@ public class CredentialManagerServiceImpl extends
        super(master, lock, userId);
    }

    @Override // from PerUserSystemService
    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
            throws PackageManager.NameNotFoundException {
        ServiceInfo si;
        try {
            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
                    PackageManager.GET_META_DATA, mUserId);
        } catch (RemoteException e) {
            throw new PackageManager.NameNotFoundException(
                    "Could not get service for " + serviceComponent);
        }
        return si;
    }

    /**
     * Unimplemented getCredentials
     */