/*
 * Copyright (C) 2016 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.server.pm;

import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.UserHandle;
import android.util.SparseArray;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;

/**
 * Manages package names that need special protection.
 *
 * TODO: This class should persist the information by itself, and also keeps track of device admin
 * packages for all users.  Then PMS.isPackageDeviceAdmin() should use it instead of talking
 * to DPMS.
 */
public class ProtectedPackages {
    @UserIdInt
    @GuardedBy("this")
    private int mDeviceOwnerUserId;

    @Nullable
    @GuardedBy("this")
    private String mDeviceOwnerPackage;

    @Nullable
    @GuardedBy("this")
    private SparseArray<String> mProfileOwnerPackages;

    @Nullable
    @GuardedBy("this")
    private final String mDeviceProvisioningPackage;

    private final Context mContext;

    public ProtectedPackages(Context context) {
        mContext = context;
        mDeviceProvisioningPackage = mContext.getResources().getString(
                R.string.config_deviceProvisioningPackage);
    }

    /**
     * Sets the device/profile owner information.
     */
    public synchronized void setDeviceAndProfileOwnerPackages(
            int deviceOwnerUserId, String deviceOwnerPackage,
            SparseArray<String> profileOwnerPackages) {
        mDeviceOwnerUserId = deviceOwnerUserId;
        mDeviceOwnerPackage =
                (deviceOwnerUserId == UserHandle.USER_NULL) ? null : deviceOwnerPackage;
        mProfileOwnerPackages = (profileOwnerPackages == null) ? null
                : profileOwnerPackages.clone();
    }

    private synchronized boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) {
        if (packageName == null) {
            return false;
        }
        if (mDeviceOwnerPackage != null) {
            if ((mDeviceOwnerUserId == userId)
                    && (packageName.equals(mDeviceOwnerPackage))) {
                return true;
            }
        }
        if (mProfileOwnerPackages != null) {
            if (packageName.equals(mProfileOwnerPackages.get(userId))) {
                return true;
            }
        }
        return false;
    }

    public synchronized String getDeviceOwnerOrProfileOwnerPackage(int userId) {
        if (mDeviceOwnerUserId == userId) {
            return mDeviceOwnerPackage;
        }
        if (mProfileOwnerPackages == null) {
            return null;
        }
        return mProfileOwnerPackages.get(userId);
    }

    /**
     * Returns {@code true} if a given package is protected. Otherwise, returns {@code false}.
     *
     * <p>A protected package means that, apart from the package owner, no system or privileged apps
     * can modify its data or package state.
     */
    private synchronized boolean isProtectedPackage(String packageName) {
        return packageName != null && packageName.equals(mDeviceProvisioningPackage);
    }

    /**
     * Returns {@code true} if a given package's state is protected. Otherwise, returns
     * {@code false}.
     *
     * <p>This is not applicable if the caller is the package owner.
     */
    public boolean isPackageStateProtected(@UserIdInt int userId, String packageName) {
        return hasDeviceOwnerOrProfileOwner(userId, packageName)
                || isProtectedPackage(packageName);
    }

    /**
     * Returns {@code true} if a given package's data is protected. Otherwise, returns
     * {@code false}.
     */
    public boolean isPackageDataProtected(@UserIdInt int userId, String packageName) {
        return hasDeviceOwnerOrProfileOwner(userId, packageName)
                || isProtectedPackage(packageName);
    }
}
