TestAppAuthenticatorUtils.java

/*
 * Copyright 2021 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 androidx.security.app.authenticator;

import android.content.Context;
import android.content.pm.PackageManager;

import androidx.collection.ArrayMap;
import androidx.collection.ArraySet;

import java.util.Map;
import java.util.Set;

/**
 * An extension of the {@link AppAuthenticatorUtils} used by the {@link AppAuthenticator} that
 * can be injected into the {@code AppAuthenticator} to configure it to behave as required by the
 * test.
 *
 * <p>This test class supports changing the UID of a specified package and treating the package
 * as not being installed to allowing testing of error path scenarios.
 */
class TestAppAuthenticatorUtils extends AppAuthenticatorUtils {
    private Map<String, Integer> mPackageUids;
    private Set<String> mNotInstalledPackages;

    /**
     * Constructor; instances should be configured through the {@link Builder}.
     */
    TestAppAuthenticatorUtils(Context context, Map<String, Integer> packageUids,
            Set<String> exceptionPackages) {
        super(context);
        mPackageUids = packageUids;
        mNotInstalledPackages = exceptionPackages;
    }

    /**
     * Builder for a new {@link TestAppAuthenticatorUtils} that allows this test class to be
     * configured as required for the test.
     */
    static class Builder {
        private Context mContext;
        private Map<String, Integer> mPackageUids;
        private Set<String> mNotInstalledPackages;

        /**
         * Creates a new {@code Builder} with the specified {@link Context}.
         *
         * @param context the {@code Context} within which to create the new Builder
         */
        Builder(Context context) {
            mContext = context;
            mPackageUids = new ArrayMap<>();
            mNotInstalledPackages = new ArraySet<>();
        }

        /**
         * Sets the {@code uid} to be returned when the specified {@code packageName} is queried.
         *
         * @param packageName the name of the package to be configured
         * @param uid         the uid to return for the specified package
         * @return this instance of the {@code Builder}
         */
        Builder setUidForPackage(String packageName, int uid) {
            mPackageUids.put(packageName, uid);
            return this;
        }

        /**
         * Treats the provided {@code packageName} as not being installed; this will result in a
         * {@link PackageManager.NameNotFoundException} being thrown when this package is queried.
         *
         * @param packageName the name of the package to be treated as not installed
         * @return this instance of the {@code Builder}
         */
        Builder setPackageNotInstalled(String packageName) {
            mNotInstalledPackages.add(packageName);
            return this;
        }

        /**
         * Builds an extension of the {@link AppAuthenticatorUtils} that can be injected to satisfy
         * test requirements.
         *
         * @return a new {@link TestAppAuthenticatorUtils} that will respond to queries as
         * configured.
         */
        TestAppAuthenticatorUtils build() {
            return new TestAppAuthenticatorUtils(mContext, mPackageUids, mNotInstalledPackages);
        }
    }

    /**
     * Returns the UID configured for the specified {@code packageName}, or the calling UID if
     * the UID of the package has not been configured.
     *
     * @param packageName the name of the package to be queried
     * @return the UID of the specified package
     * @throws PackageManager.NameNotFoundException if this class has been configured to treat
     *                                              the specified package as not installed
     */
    @Override
    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
        if (mNotInstalledPackages.contains(packageName)) {
            throw new PackageManager.NameNotFoundException("Test configured to throw exception "
                    + "for package " + packageName);
        }
        if (mPackageUids.containsKey(packageName)) {
            return mPackageUids.get(packageName);
        }
        return getCallingUid();
    }
}