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

Commit 4f3c722c authored by Becca Hughes's avatar Becca Hughes
Browse files

Add framework updates for CredMan policy transparency

Adds a new filter that allows Android Settings to
get the user providers but without filtering
providers that are disabled by device policy. This
will be used to show users which apps are disabled
by policy.

Bug: 318552220
Test: Manual ondevice test
Change-Id: I5b6c2b7474e22ecaf8800c8e6ec67ac2a6254a5d
parent 97f29153
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -74,6 +74,11 @@ public final class CredentialManager {
                PROVIDER_FILTER_ALL_PROVIDERS,
                PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY,
                PROVIDER_FILTER_USER_PROVIDERS_ONLY,
                // By default the returned list of providers will not include any providers that
                // have been hidden by device policy. However, there are some cases where we want
                // them to show up (e.g. settings) so this will return the list of providers with
                // the hidden ones included.
                PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN,
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ProviderFilter {}
@@ -99,6 +104,14 @@ public final class CredentialManager {
     */
    @TestApi public static final int PROVIDER_FILTER_USER_PROVIDERS_ONLY = 2;

    /**
     * Returns user credential providers only. This will include providers that
     * have been disabled by the device policy.
     *
     * @hide
     */
    public static final int PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN = 3;

    private final Context mContext;
    private final ICredentialManager mService;

+16 −5
Original line number Diff line number Diff line
@@ -480,8 +480,12 @@ public final class CredentialProviderInfoFactory {
            Set<ComponentName> primaryServices) {
        requireNonNull(context, "context must not be null");

        // Get the device policy.
        PackagePolicy pp = getDeviceManagerPolicy(context, userId);
        // Get the device policy. If the client has asked for all providers then we
        // should ignore the device policy.
        PackagePolicy pp =
                providerFilter != CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN
                        ? getDeviceManagerPolicy(context, userId)
                        : null;

        // Generate the provider list.
        final boolean disableSystemAppVerificationForTests = false;
@@ -514,8 +518,12 @@ public final class CredentialProviderInfoFactory {
            Set<ComponentName> primaryServices) {
        requireNonNull(context, "context must not be null");

        // Get the device policy.
        PackagePolicy pp = getDeviceManagerPolicy(context, userId);
        // Get the device policy. If the client has asked for all providers then we
        // should ignore the device policy.
        PackagePolicy pp =
                providerFilter != CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN
                        ? getDeviceManagerPolicy(context, userId)
                        : null;

        // Generate the provider list.
        final boolean disableSystemAppVerificationForTests = true;
@@ -593,7 +601,10 @@ public final class CredentialProviderInfoFactory {
            if (cpi.isSystemProvider()) {
                return mProviderFilter == CredentialManager.PROVIDER_FILTER_SYSTEM_PROVIDERS_ONLY;
            } else {
                return mProviderFilter == CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY;
                return mProviderFilter == CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY
                        || mProviderFilter
                                == CredentialManager
                                        .PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN;
            }
        }

+25 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.app.ecm.EnhancedConfirmationManager;
import android.app.admin.PackagePolicy;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
@@ -57,6 +58,7 @@ import androidx.annotation.VisibleForTesting;

import com.android.internal.widget.LockPatternUtils;

import java.util.HashSet;
import java.util.List;

/**
@@ -827,6 +829,29 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
        return RestrictedLockUtils.getProfileOrDeviceOwner(context, UserHandle.of(profileId));
    }

    /**
     * Check if there are restrictions on an application from being a Credential Manager provider.
     *
     * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
     * or {@code null} if the setting is not managed.
     */
    public static @Nullable EnforcedAdmin checkIfApplicationCanBeCredentialManagerProvider(
            @NonNull Context context, @NonNull String packageName) {
        final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
        final PackagePolicy pp = dpm.getCredentialManagerPolicy();

        if (pp == null || pp.isPackageAllowed(packageName, new HashSet<>())) {
            return null;
        }

        EnforcedAdmin admin = RestrictedLockUtilsInternal.getDeviceOwner(context);
        if (admin != null) {
            return admin;
        }
        int profileId = getManagedProfileId(context, UserHandle.USER_SYSTEM);
        return RestrictedLockUtils.getProfileOrDeviceOwner(context, UserHandle.of(profileId));
    }

    /**
     * Static {@link LockPatternUtils} and {@link DevicePolicyManager} wrapper for testing purposes.
     * {@link LockPatternUtils} is an internal API not supported by robolectric.
+168 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 com.android.settingslib;

import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;

import android.content.Context;
import android.os.UserHandle;
import android.util.AttributeSet;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;

import com.android.settingslib.widget.SelectorWithWidgetPreference;

/**
 * Selector with widget preference that can be disabled by a device admin using a user restriction.
 */
public class RestrictedSelectorWithWidgetPreference extends SelectorWithWidgetPreference {
    private RestrictedPreferenceHelper mHelper;

    /**
     * Perform inflation from XML and apply a class-specific base style.
     *
     * @param context The {@link Context} this is associated with, through which it can access the
     *     current theme, resources, {@link SharedPreferences}, etc.
     * @param attrs The attributes of the XML tag that is inflating the preference
     * @param defStyle An attribute in the current theme that contains a reference to a style
     *     resource that supplies default values for the view. Can be 0 to not look for defaults.
     */
    public RestrictedSelectorWithWidgetPreference(
            @NonNull Context context, @NonNull AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mHelper = new RestrictedPreferenceHelper(context, /* preference= */ this, attrs);
    }

    /**
     * Perform inflation from XML and apply a class-specific base style.
     *
     * @param context The {@link Context} this is associated with, through which it can access the
     *     current theme, resources, {@link SharedPreferences}, etc.
     * @param attrs The attributes of the XML tag that is inflating the preference
     */
    public RestrictedSelectorWithWidgetPreference(
            @NonNull Context context, @NonNull AttributeSet attrs) {
        super(context, attrs);
        mHelper = new RestrictedPreferenceHelper(context, /* preference= */ this, attrs);
    }

    /**
     * Constructor to create a preference, which will display with a checkbox style.
     *
     * @param context The {@link Context} this is associated with.
     * @param isCheckbox Whether this preference should display as a checkbox.
     */
    public RestrictedSelectorWithWidgetPreference(@NonNull Context context, boolean isCheckbox) {
        super(context, null);
        mHelper =
                new RestrictedPreferenceHelper(context, /* preference= */ this, /* attrs= */ null);
    }

    /**
     * Constructor to create a preference.
     *
     * @param context The Context this is associated with.
     */
    public RestrictedSelectorWithWidgetPreference(@NonNull Context context) {
        this(context, null);
        mHelper =
                new RestrictedPreferenceHelper(context, /* preference= */ this, /* attrs= */ null);
    }

    @Override
    public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        mHelper.onBindViewHolder(holder);
    }

    @Override
    public void performClick() {
        if (!mHelper.performClick()) {
            super.performClick();
        }
    }

    @Override
    protected void onAttachedToHierarchy(@NonNull PreferenceManager preferenceManager) {
        mHelper.onAttachedToHierarchy();
        super.onAttachedToHierarchy(preferenceManager);
    }

    /**
     * Set the user restriction and disable this preference.
     *
     * @param userRestriction constant from {@link android.os.UserManager}
     */
    public void checkRestrictionAndSetDisabled(@NonNull String userRestriction) {
        mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
    }

    /**
     * Set the user restriction and disable this preference for the given user.
     *
     * @param userRestriction constant from {@link android.os.UserManager}
     * @param userId user to check the restriction for.
     */
    public void checkRestrictionAndSetDisabled(@NonNull String userRestriction, int userId) {
        mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
    }

    /**
     * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
     * package. Marks the preference as disabled if so.
     *
     * @param settingIdentifier The key identifying the setting
     * @param packageName the package to check the settingIdentifier for
     */
    public void checkEcmRestrictionAndSetDisabled(
            @NonNull String settingIdentifier, @NonNull String packageName) {
        mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
    }

    @Override
    public void setEnabled(boolean enabled) {
        if (enabled && isDisabledByAdmin()) {
            mHelper.setDisabledByAdmin(/* admin= */ null);
            return;
        }
        super.setEnabled(enabled);
    }

    /**
     * Check whether this preference is disabled by admin.
     *
     * @return true if this preference is disabled by admin.
     */
    public boolean isDisabledByAdmin() {
        return mHelper.isDisabledByAdmin();
    }

    /**
     * Disable preference based on the enforce admin.
     *
     * @param admin details of the admin who enforced the restriction. If it is {@code null}, then
     *     this preference will be enabled. Otherwise, it will be disabled.
     */
    public void setDisabledByAdmin(@Nullable EnforcedAdmin admin) {
        if (mHelper.setDisabledByAdmin(admin)) {
            notifyChanged();
        }
    }
}