/*
 * Copyright (C) 2012 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 static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.security.keystore.ArrayUtils;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.LongSparseArray;

import com.android.internal.os.AtomicFile;
import com.android.server.LocalServices;
import com.android.server.pm.permission.PermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageManagerSettingsTests {
    private static final String PACKAGE_NAME_2 = "com.google.app2";
    private static final String PACKAGE_NAME_3 = "com.android.app3";
    private static final String PACKAGE_NAME_1 = "com.google.app1";
    public static final String TAG = "PackageManagerSettingsTests";
    protected final String PREFIX = "android.content.pm";

    /** make sure our initialized KeySetManagerService metadata matches packages.xml */
    @Test
    public void testReadKeySetSettings()
            throws ReflectiveOperationException, IllegalAccessException {
        /* write out files and read */
        writeOldFiles();
        final Context context = InstrumentationRegistry.getContext();
        final Object lock = new Object();
        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
        Settings settings =
                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
        assertThat(settings.readLPw(createFakeUsers()), is(true));
        verifyKeySetMetaData(settings);
    }

    /** read in data, write it out, and read it back in.  Verify same. */
    @Test
    public void testWriteKeySetSettings()
            throws ReflectiveOperationException, IllegalAccessException {
        // write out files and read
        writeOldFiles();
        final Context context = InstrumentationRegistry.getContext();
        final Object lock = new Object();
        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
        Settings settings =
                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
        assertThat(settings.readLPw(createFakeUsers()), is(true));

        // write out, read back in and verify the same
        settings.writeLPr();
        assertThat(settings.readLPw(createFakeUsers()), is(true));
        verifyKeySetMetaData(settings);
    }

    @Test
    public void testSettingsReadOld() {
        // Write the package files and make sure they're parsed properly the first time
        writeOldFiles();
        final Context context = InstrumentationRegistry.getContext();
        final Object lock = new Object();
        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
        Settings settings =
                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
        assertThat(settings.readLPw(createFakeUsers()), is(true));
        assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
        assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue()));

        PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_1);
        assertThat(ps.getEnabled(0), is(COMPONENT_ENABLED_STATE_DEFAULT));
        assertThat(ps.getNotLaunched(0), is(true));

        ps = settings.getPackageLPr(PACKAGE_NAME_2);
        assertThat(ps.getStopped(0), is(false));
        assertThat(ps.getEnabled(0), is(COMPONENT_ENABLED_STATE_DISABLED_USER));
        assertThat(ps.getEnabled(1), is(COMPONENT_ENABLED_STATE_DEFAULT));
    }

    @Test
    public void testNewPackageRestrictionsFile() throws ReflectiveOperationException {
        // Write the package files and make sure they're parsed properly the first time
        writeOldFiles();
        final Context context = InstrumentationRegistry.getContext();
        final Object lock = new Object();
        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
        Settings settings =
                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
        assertThat(settings.readLPw(createFakeUsers()), is(true));
        settings.writeLPr();

        // Create Settings again to make it read from the new files
        settings =
                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
        assertThat(settings.readLPw(createFakeUsers()), is(true));

        PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2);
        assertThat(ps.getEnabled(0), is(COMPONENT_ENABLED_STATE_DISABLED_USER));
        assertThat(ps.getEnabled(1), is(COMPONENT_ENABLED_STATE_DEFAULT));
    }

    @Test
    public void testEnableDisable() {
        // Write the package files and make sure they're parsed properly the first time
        writeOldFiles();
        final Context context = InstrumentationRegistry.getContext();
        final Object lock = new Object();
        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
        Settings settings =
                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
        assertThat(settings.readLPw(createFakeUsers()), is(true));

        // Enable/Disable a package
        PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_1);
        ps.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null);
        ps.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 1, null);
        assertThat(ps.getEnabled(0), is(COMPONENT_ENABLED_STATE_DISABLED));
        assertThat(ps.getEnabled(1), is(COMPONENT_ENABLED_STATE_ENABLED));

        // Enable/Disable a component
        ArraySet<String> components = new ArraySet<String>();
        String component1 = PACKAGE_NAME_1 + "/.Component1";
        components.add(component1);
        ps.setDisabledComponents(components, 0);
        ArraySet<String> componentsDisabled = ps.getDisabledComponents(0);
        assertThat(componentsDisabled.size(), is(1));
        assertThat(componentsDisabled.toArray()[0], is(component1));
        boolean hasEnabled =
                ps.getEnabledComponents(0) != null && ps.getEnabledComponents(1).size() > 0;
        assertThat(hasEnabled, is(false));

        // User 1 should not have any disabled components
        boolean hasDisabled =
                ps.getDisabledComponents(1) != null && ps.getDisabledComponents(1).size() > 0;
        assertThat(hasDisabled, is(false));
        ps.setEnabledComponents(components, 1);
        assertThat(ps.getEnabledComponents(1).size(), is(1));
        hasEnabled = ps.getEnabledComponents(0) != null && ps.getEnabledComponents(0).size() > 0;
        assertThat(hasEnabled, is(false));
    }

    private static final String PACKAGE_NAME = "com.android.bar";
    private static final String REAL_PACKAGE_NAME = "com.android.foo";
    private static final String PARENT_PACKAGE_NAME = "com.android.bar.parent";
    private static final String CHILD_PACKAGE_NAME_01 = "com.android.bar.child01";
    private static final String CHILD_PACKAGE_NAME_02 = "com.android.bar.child02";
    private static final String CHILD_PACKAGE_NAME_03 = "com.android.bar.child03";
    private static final File INITIAL_CODE_PATH =
            new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-1");
    private static final File UPDATED_CODE_PATH =
            new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-2");
    private static final long INITIAL_VERSION_CODE = 10023L;
    private static final long UPDATED_VERSION_CODE = 10025L;

    @Test
    public void testPackageStateCopy01() {
        final List<String> childPackageNames = new ArrayList<>();
        childPackageNames.add(CHILD_PACKAGE_NAME_01);
        childPackageNames.add(CHILD_PACKAGE_NAME_02);
        childPackageNames.add(CHILD_PACKAGE_NAME_03);
        final PackageSetting origPkgSetting01 = new PackageSetting(
                PACKAGE_NAME,
                REAL_PACKAGE_NAME,
                INITIAL_CODE_PATH /*codePath*/,
                INITIAL_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPathString*/,
                "x86_64" /*primaryCpuAbiString*/,
                "x86" /*secondaryCpuAbiString*/,
                null /*cpuAbiOverrideString*/,
                INITIAL_VERSION_CODE,
                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
                PARENT_PACKAGE_NAME,
                childPackageNames,
                0,
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        final PackageSetting testPkgSetting01 = new PackageSetting(origPkgSetting01);
        verifySettingCopy(origPkgSetting01, testPkgSetting01);
    }

    @Test
    public void testPackageStateCopy02() {
        final List<String> childPackageNames = new ArrayList<>();
        childPackageNames.add(CHILD_PACKAGE_NAME_01);
        childPackageNames.add(CHILD_PACKAGE_NAME_02);
        childPackageNames.add(CHILD_PACKAGE_NAME_03);
        final PackageSetting origPkgSetting01 = new PackageSetting(
                PACKAGE_NAME /*pkgName*/,
                REAL_PACKAGE_NAME /*realPkgName*/,
                INITIAL_CODE_PATH /*codePath*/,
                INITIAL_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPathString*/,
                "x86_64" /*primaryCpuAbiString*/,
                "x86" /*secondaryCpuAbiString*/,
                null /*cpuAbiOverrideString*/,
                INITIAL_VERSION_CODE,
                ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
                PARENT_PACKAGE_NAME,
                childPackageNames,
                0,
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        final PackageSetting testPkgSetting01 = new PackageSetting(
                PACKAGE_NAME /*pkgName*/,
                REAL_PACKAGE_NAME /*realPkgName*/,
                UPDATED_CODE_PATH /*codePath*/,
                UPDATED_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPathString*/,
                null /*primaryCpuAbiString*/,
                null /*secondaryCpuAbiString*/,
                null /*cpuAbiOverrideString*/,
                UPDATED_VERSION_CODE,
                0 /*pkgFlags*/,
                0 /*pkgPrivateFlags*/,
                null /*parentPkgName*/,
                null /*childPkgNames*/,
                0,
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        testPkgSetting01.copyFrom(origPkgSetting01);
        verifySettingCopy(origPkgSetting01, testPkgSetting01);
    }

    /** Update package */
    @Test
    public void testUpdatePackageSetting01() throws PackageManagerException {
        final PackageSetting testPkgSetting01 =
                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
        testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
        assertThat(testPkgSetting01.pkgFlags, is(0));
        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
        final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
        Settings.updatePackageSetting(
                testPkgSetting01,
                null /*disabledPkg*/,
                null /*sharedUser*/,
                UPDATED_CODE_PATH /*codePath*/,
                UPDATED_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPath*/,
                "arm64-v8a" /*primaryCpuAbi*/,
                "armeabi" /*secondaryCpuAbi*/,
                0 /*pkgFlags*/,
                0 /*pkgPrivateFlags*/,
                null /*childPkgNames*/,
                UserManagerService.getInstance(),
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
        assertThat(testPkgSetting01.pkgFlags, is(0));
        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
        final PackageUserState userState = testPkgSetting01.readUserState(0);
        final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
        verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/,
                false /*stopped*/, false /*installed*/);
    }

    /** Update package; package now on /system, install for user '0' */
    @Test
    public void testUpdatePackageSetting02() throws PackageManagerException {
        final PackageSetting testPkgSetting01 =
                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
        testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
        assertThat(testPkgSetting01.pkgFlags, is(0));
        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
        final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
        Settings.updatePackageSetting(
                testPkgSetting01,
                null /*disabledPkg*/,
                null /*sharedUser*/,
                UPDATED_CODE_PATH /*codePath*/,
                UPDATED_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPath*/,
                "arm64-v8a" /*primaryCpuAbi*/,
                "armeabi" /*secondaryCpuAbi*/,
                ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
                null /*childPkgNames*/,
                UserManagerService.getInstance(),
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
        assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
        assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
        final PackageUserState userState = testPkgSetting01.readUserState(0);
        final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
        // WARNING: When creating a shallow copy of the PackageSetting we do NOT create
        // new contained objects. For example, this means that changes to the user state
        // in testPkgSetting01 will also change the user state in its copy.
        verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/,
                false /*stopped*/, true /*installed*/);
    }

    /** Update package; changing shared user throws exception */
    @Test
    public void testUpdatePackageSetting03() {
        final Context context = InstrumentationRegistry.getContext();
        final Object lock = new Object();
        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
        final Settings testSettings01 =
                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
        final SharedUserSetting testUserSetting01 = createSharedUserSetting(
                testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
        final PackageSetting testPkgSetting01 =
                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
        try {
            Settings.updatePackageSetting(
                    testPkgSetting01,
                    null /*disabledPkg*/,
                    testUserSetting01 /*sharedUser*/,
                    UPDATED_CODE_PATH /*codePath*/,
                    null /*resourcePath*/,
                    null /*legacyNativeLibraryPath*/,
                    "arm64-v8a" /*primaryCpuAbi*/,
                    "armeabi" /*secondaryCpuAbi*/,
                    0 /*pkgFlags*/,
                    0 /*pkgPrivateFlags*/,
                    null /*childPkgNames*/,
                    UserManagerService.getInstance(),
                    null /*usesStaticLibraries*/,
                    null /*usesStaticLibrariesVersions*/);
            fail("Expected a PackageManagerException");
        } catch (PackageManagerException expected) {
        }
    }

    /** Create a new PackageSetting based on an original package setting */
    @Test
    public void testCreateNewSetting01() {
        final PackageSetting originalPkgSetting01 =
                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
        final PackageSignatures originalSignatures = originalPkgSetting01.signatures;
        final PackageSetting testPkgSetting01 = Settings.createNewSetting(
                REAL_PACKAGE_NAME,
                originalPkgSetting01 /*originalPkg*/,
                null /*disabledPkg*/,
                null /*realPkgName*/,
                null /*sharedUser*/,
                UPDATED_CODE_PATH /*codePath*/,
                UPDATED_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPath*/,
                "arm64-v8a" /*primaryCpuAbi*/,
                "armeabi" /*secondaryCpuAbi*/,
                UPDATED_VERSION_CODE /*versionCode*/,
                ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
                ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
                null /*installUser*/,
                false /*allowInstall*/,
                false /*instantApp*/,
                false /*virtualPreload*/,
                null /*parentPkgName*/,
                null /*childPkgNames*/,
                UserManagerService.getInstance(),
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
        assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
        assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
        assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
        assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
        // signatures object must be different
        assertNotSame(testPkgSetting01.signatures, originalSignatures);
        assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
        final PackageUserState userState = testPkgSetting01.readUserState(0);
        verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
                false /*notLaunched*/, false /*stopped*/, true /*installed*/);
    }

    /** Create a new non-system PackageSetting */
    @Test
    public void testCreateNewSetting02() {
        final PackageSetting testPkgSetting01 = Settings.createNewSetting(
                PACKAGE_NAME,
                null /*originalPkg*/,
                null /*disabledPkg*/,
                null /*realPkgName*/,
                null /*sharedUser*/,
                INITIAL_CODE_PATH /*codePath*/,
                INITIAL_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPath*/,
                "x86_64" /*primaryCpuAbiString*/,
                "x86" /*secondaryCpuAbiString*/,
                INITIAL_VERSION_CODE /*versionCode*/,
                0 /*pkgFlags*/,
                0 /*pkgPrivateFlags*/,
                UserHandle.SYSTEM /*installUser*/,
                true /*allowInstall*/,
                false /*instantApp*/,
                false /*virtualPreload*/,
                null /*parentPkgName*/,
                null /*childPkgNames*/,
                UserManagerService.getInstance(),
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        assertThat(testPkgSetting01.appId, is(0));
        assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
        assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
        assertThat(testPkgSetting01.pkgFlags, is(0));
        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
        assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
        assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
        assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
        assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
        // by default, the package is considered stopped
        final PackageUserState userState = testPkgSetting01.readUserState(0);
        verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
                true /*notLaunched*/, true /*stopped*/, true /*installed*/);
    }

    /** Create PackageSetting for a shared user */
    @Test
    public void testCreateNewSetting03() {
        final Context context = InstrumentationRegistry.getContext();
        final Object lock = new Object();
        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
        final Settings testSettings01 =
                new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
        final SharedUserSetting testUserSetting01 = createSharedUserSetting(
                testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
        final PackageSetting testPkgSetting01 = Settings.createNewSetting(
                PACKAGE_NAME,
                null /*originalPkg*/,
                null /*disabledPkg*/,
                null /*realPkgName*/,
                testUserSetting01 /*sharedUser*/,
                INITIAL_CODE_PATH /*codePath*/,
                INITIAL_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPath*/,
                "x86_64" /*primaryCpuAbiString*/,
                "x86" /*secondaryCpuAbiString*/,
                INITIAL_VERSION_CODE /*versionCode*/,
                0 /*pkgFlags*/,
                0 /*pkgPrivateFlags*/,
                null /*installUser*/,
                false /*allowInstall*/,
                false /*instantApp*/,
                false /*virtualPreload*/,
                null /*parentPkgName*/,
                null /*childPkgNames*/,
                UserManagerService.getInstance(),
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        assertThat(testPkgSetting01.appId, is(10064));
        assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
        assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
        assertThat(testPkgSetting01.pkgFlags, is(0));
        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
        assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
        assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
        assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
        assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
        final PackageUserState userState = testPkgSetting01.readUserState(0);
        verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
                false /*notLaunched*/, false /*stopped*/, true /*installed*/);
    }

    /** Create a new PackageSetting based on a disabled package setting */
    @Test
    public void testCreateNewSetting04() {
        final PackageSetting disabledPkgSetting01 =
                createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
        disabledPkgSetting01.appId = 10064;
        final PackageSignatures disabledSignatures = disabledPkgSetting01.signatures;
        final PackageSetting testPkgSetting01 = Settings.createNewSetting(
                PACKAGE_NAME,
                null /*originalPkg*/,
                disabledPkgSetting01 /*disabledPkg*/,
                null /*realPkgName*/,
                null /*sharedUser*/,
                UPDATED_CODE_PATH /*codePath*/,
                UPDATED_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPath*/,
                "arm64-v8a" /*primaryCpuAbi*/,
                "armeabi" /*secondaryCpuAbi*/,
                UPDATED_VERSION_CODE /*versionCode*/,
                0 /*pkgFlags*/,
                0 /*pkgPrivateFlags*/,
                null /*installUser*/,
                false /*allowInstall*/,
                false /*instantApp*/,
                false /*virtualPreload*/,
                null /*parentPkgName*/,
                null /*childPkgNames*/,
                UserManagerService.getInstance(),
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
        assertThat(testPkgSetting01.appId, is(10064));
        assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
        assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
        assertThat(testPkgSetting01.pkgFlags, is(0));
        assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
        assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
        assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
        assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
        assertNotSame(testPkgSetting01.signatures, disabledSignatures);
        assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
        final PackageUserState userState = testPkgSetting01.readUserState(0);
        verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
                false /*notLaunched*/, false /*stopped*/, true /*installed*/);
    }

    private <T> void assertArrayEquals(T[] a, T[] b) {
        assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
                Arrays.equals(a, b));
    }

    private void assertArrayEquals(int[] a, int[] b) {
        assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
                Arrays.equals(a, b));
    }

    private void assertArrayEquals(long[] a, long[] b) {
        assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
                Arrays.equals(a, b));
    }

    private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
            boolean userStateChanged) {
        verifyUserState(userState, oldUserState, userStateChanged, false /*notLaunched*/,
                false /*stopped*/, true /*installed*/);
    }

    private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
            boolean userStateChanged, boolean notLaunched, boolean stopped, boolean installed) {
        assertThat(userState.enabled, is(0));
        assertThat(userState.hidden, is(false));
        assertThat(userState.installed, is(installed));
        assertThat(userState.notLaunched, is(notLaunched));
        assertThat(userState.stopped, is(stopped));
        assertThat(userState.suspended, is(false));
        if (oldUserState != null) {
            assertThat(userState.equals(oldUserState), is(not(userStateChanged)));
        }
    }

    private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
        assertThat(origPkgSetting, is(not(testPkgSetting)));
        assertThat(origPkgSetting.appId, is(testPkgSetting.appId));
        // different but equal objects
        assertNotSame(origPkgSetting.childPackageNames, testPkgSetting.childPackageNames);
        assertThat(origPkgSetting.childPackageNames, is(testPkgSetting.childPackageNames));
        assertSame(origPkgSetting.codePath, testPkgSetting.codePath);
        assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath));
        assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString);
        assertThat(origPkgSetting.codePathString, is(testPkgSetting.codePathString));
        assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
        assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
        assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
        assertSame(origPkgSetting.installerPackageName, testPkgSetting.installerPackageName);
        assertThat(origPkgSetting.installerPackageName, is(testPkgSetting.installerPackageName));
        assertThat(origPkgSetting.installPermissionsFixed,
                is(testPkgSetting.installPermissionsFixed));
        assertThat(origPkgSetting.installStatus, is(testPkgSetting.installStatus));
        assertThat(origPkgSetting.isOrphaned, is(testPkgSetting.isOrphaned));
        assertSame(origPkgSetting.keySetData, testPkgSetting.keySetData);
        assertThat(origPkgSetting.keySetData, is(testPkgSetting.keySetData));
        assertThat(origPkgSetting.lastUpdateTime, is(testPkgSetting.lastUpdateTime));
        assertSame(origPkgSetting.legacyNativeLibraryPathString,
                testPkgSetting.legacyNativeLibraryPathString);
        assertThat(origPkgSetting.legacyNativeLibraryPathString,
                is(testPkgSetting.legacyNativeLibraryPathString));
        assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
        assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
        assertThat(origPkgSetting.name, is(testPkgSetting.name));
        // oldCodePaths is _not_ copied
        // assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths);
        // assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths)));
        assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName);
        assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName));
        assertSame(origPkgSetting.pkg, testPkgSetting.pkg);
        // No equals() method for this object
        // assertThat(origPkgSetting.pkg, is(testPkgSetting.pkg));
        assertThat(origPkgSetting.pkgFlags, is(testPkgSetting.pkgFlags));
        assertThat(origPkgSetting.pkgPrivateFlags, is(testPkgSetting.pkgPrivateFlags));
        assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString);
        assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString));
        assertThat(origPkgSetting.realName, is(testPkgSetting.realName));
        assertSame(origPkgSetting.resourcePath, testPkgSetting.resourcePath);
        assertThat(origPkgSetting.resourcePath, is(testPkgSetting.resourcePath));
        assertSame(origPkgSetting.resourcePathString, testPkgSetting.resourcePathString);
        assertThat(origPkgSetting.resourcePathString, is(testPkgSetting.resourcePathString));
        assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString);
        assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString));
        assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser);
        assertThat(origPkgSetting.sharedUser, is(testPkgSetting.sharedUser));
        assertSame(origPkgSetting.signatures, testPkgSetting.signatures);
        assertThat(origPkgSetting.signatures, is(testPkgSetting.signatures));
        assertThat(origPkgSetting.timeStamp, is(testPkgSetting.timeStamp));
        assertThat(origPkgSetting.uidError, is(testPkgSetting.uidError));
        assertNotSame(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
        // No equals() method for SparseArray object
        // assertThat(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
        assertSame(origPkgSetting.verificationInfo, testPkgSetting.verificationInfo);
        assertThat(origPkgSetting.verificationInfo, is(testPkgSetting.verificationInfo));
        assertThat(origPkgSetting.versionCode, is(testPkgSetting.versionCode));
        assertSame(origPkgSetting.volumeUuid, testPkgSetting.volumeUuid);
        assertThat(origPkgSetting.volumeUuid, is(testPkgSetting.volumeUuid));
    }

    private SharedUserSetting createSharedUserSetting(Settings settings, String userName,
            int sharedUserId, int pkgFlags, int pkgPrivateFlags) {
        return settings.addSharedUserLPw(
                userName,
                sharedUserId,
                pkgFlags,
                pkgPrivateFlags);
    }
    private PackageSetting createPackageSetting(int sharedUserId, int pkgFlags) {
        return new PackageSetting(
                PACKAGE_NAME,
                REAL_PACKAGE_NAME,
                INITIAL_CODE_PATH /*codePath*/,
                INITIAL_CODE_PATH /*resourcePath*/,
                null /*legacyNativeLibraryPathString*/,
                "x86_64" /*primaryCpuAbiString*/,
                "x86" /*secondaryCpuAbiString*/,
                null /*cpuAbiOverrideString*/,
                INITIAL_VERSION_CODE,
                pkgFlags,
                0 /*privateFlags*/,
                null /*parentPackageName*/,
                null /*childPackageNames*/,
                sharedUserId,
                null /*usesStaticLibraries*/,
                null /*usesStaticLibrariesVersions*/);
    }

    private @NonNull List<UserInfo> createFakeUsers() {
        ArrayList<UserInfo> users = new ArrayList<>();
        users.add(new UserInfo(UserHandle.USER_SYSTEM, "test user", UserInfo.FLAG_INITIALIZED));
        return users;
    }

    private void writeFile(File file, byte[] data) {
        file.mkdirs();
        try {
            AtomicFile aFile = new AtomicFile(file);
            FileOutputStream fos = aFile.startWrite();
            fos.write(data);
            aFile.finishWrite(fos);
        } catch (IOException ioe) {
            Log.e(TAG, "Cannot write file " + file.getPath());
        }
    }

    private void writePackagesXml() {
        writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages.xml"),
                ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
                + "<packages>"
                + "<last-platform-version internal=\"15\" external=\"0\" fingerprint=\"foo\" />"
                + "<permission-trees>"
                + "<item name=\"com.google.android.permtree\" package=\"com.google.android.permpackage\" />"
                + "</permission-trees>"
                + "<permissions>"
                + "<item name=\"android.permission.WRITE_CALL_LOG\" package=\"android\" protection=\"1\" />"
                + "<item name=\"android.permission.ASEC_ACCESS\" package=\"android\" protection=\"2\" />"
                + "<item name=\"android.permission.ACCESS_WIMAX_STATE\" package=\"android\" />"
                + "<item name=\"android.permission.REBOOT\" package=\"android\" protection=\"18\" />"
                + "</permissions>"
                + "<package name=\"com.google.app1\" codePath=\"/system/app/app1.apk\" nativeLibraryPath=\"/data/data/com.google.app1/lib\" flags=\"1\" ft=\"1360e2caa70\" it=\"135f2f80d08\" ut=\"1360e2caa70\" version=\"1109\" sharedUserId=\"11000\">"
                + "<sigs count=\"1\">"
                + "<cert index=\"0\" key=\"" + KeySetStrings.ctsKeySetCertA + "\" />"
                + "</sigs>"
                + "<proper-signing-keyset identifier=\"1\" />"
                + "</package>"
                + "<package name=\"com.google.app2\" codePath=\"/system/app/app2.apk\" nativeLibraryPath=\"/data/data/com.google.app2/lib\" flags=\"1\" ft=\"1360e578718\" it=\"135f2f80d08\" ut=\"1360e578718\" version=\"15\" enabled=\"3\" userId=\"11001\">"
                + "<sigs count=\"1\">"
                + "<cert index=\"0\" />"
                + "</sigs>"
                + "<proper-signing-keyset identifier=\"1\" />"
                + "<defined-keyset alias=\"AB\" identifier=\"4\" />"
                + "</package>"
                + "<package name=\"com.android.app3\" codePath=\"/system/app/app3.apk\" nativeLibraryPath=\"/data/data/com.android.app3/lib\" flags=\"1\" ft=\"1360e577b60\" it=\"135f2f80d08\" ut=\"1360e577b60\" version=\"15\" userId=\"11030\">"
                + "<sigs count=\"1\">"
                + "<cert index=\"1\" key=\"" + KeySetStrings.ctsKeySetCertB + "\" />"
                + "</sigs>"
                + "<proper-signing-keyset identifier=\"2\" />"
                + "<upgrade-keyset identifier=\"3\" />"
                + "<defined-keyset alias=\"C\" identifier=\"3\" />"
                + "</package>"
                + "<shared-user name=\"com.android.shared1\" userId=\"11000\">"
                + "<sigs count=\"1\">"
                + "<cert index=\"1\" />"
                + "</sigs>"
                + "<perms>"
                + "<item name=\"android.permission.REBOOT\" />"
                + "</perms>"
                + "</shared-user>"
                + "<keyset-settings version=\"1\">"
                + "<keys>"
                + "<public-key identifier=\"1\" value=\"" + KeySetStrings.ctsKeySetPublicKeyA + "\" />"
                + "<public-key identifier=\"2\" value=\"" + KeySetStrings.ctsKeySetPublicKeyB + "\" />"
                + "<public-key identifier=\"3\" value=\"" + KeySetStrings.ctsKeySetPublicKeyC + "\" />"
                + "</keys>"
                + "<keysets>"
                + "<keyset identifier=\"1\">"
                + "<key-id identifier=\"1\" />"
                + "</keyset>"
                + "<keyset identifier=\"2\">"
                + "<key-id identifier=\"2\" />"
                + "</keyset>"
                + "<keyset identifier=\"3\">"
                + "<key-id identifier=\"3\" />"
                + "</keyset>"
                + "<keyset identifier=\"4\">"
                + "<key-id identifier=\"1\" />"
                + "<key-id identifier=\"2\" />"
                + "</keyset>"
                + "</keysets>"
                + "<lastIssuedKeyId value=\"3\" />"
                + "<lastIssuedKeySetId value=\"4\" />"
                + "</keyset-settings>"
                + "</packages>").getBytes());
    }

    private void writeStoppedPackagesXml() {
        writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages-stopped.xml"),
                ( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
                + "<stopped-packages>"
                + "<pkg name=\"com.google.app1\" nl=\"1\" />"
                + "<pkg name=\"com.android.app3\" nl=\"1\" />"
                + "</stopped-packages>")
                .getBytes());
    }

    private void writePackagesList() {
        writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages.list"),
                ( "com.google.app1 11000 0 /data/data/com.google.app1 seinfo1"
                + "com.google.app2 11001 0 /data/data/com.google.app2 seinfo2"
                + "com.android.app3 11030 0 /data/data/com.android.app3 seinfo3")
                .getBytes());
    }

    private void deleteSystemFolder() {
        File systemFolder = new File(InstrumentationRegistry.getContext().getFilesDir(), "system");
        deleteFolder(systemFolder);
    }

    private static void deleteFolder(File folder) {
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                deleteFolder(file);
            }
        }
        folder.delete();
    }

    private void writeOldFiles() {
        deleteSystemFolder();
        writePackagesXml();
        writeStoppedPackagesXml();
        writePackagesList();
    }

    @Before
    public void createUserManagerServiceRef() throws ReflectiveOperationException {
        InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
            try {
                // unregister the user manager from the local service
                LocalServices.removeServiceForTest(UserManagerInternal.class);
                new UserManagerService(InstrumentationRegistry.getContext());
            } catch (Exception e) {
                e.printStackTrace();
                fail("Could not create user manager service; " + e);
            }
        });
    }

    private void verifyKeySetMetaData(Settings settings)
            throws ReflectiveOperationException, IllegalAccessException {
        ArrayMap<String, PackageSetting> packages = settings.mPackages;
        KeySetManagerService ksms = settings.mKeySetManagerService;

        /* verify keyset and public key ref counts */
        assertThat(KeySetUtils.getKeySetRefCount(ksms, 1), is(2));
        assertThat(KeySetUtils.getKeySetRefCount(ksms, 2), is(1));
        assertThat(KeySetUtils.getKeySetRefCount(ksms, 3), is(1));
        assertThat(KeySetUtils.getKeySetRefCount(ksms, 4), is(1));
        assertThat(KeySetUtils.getPubKeyRefCount(ksms, 1), is(2));
        assertThat(KeySetUtils.getPubKeyRefCount(ksms, 2), is(2));
        assertThat(KeySetUtils.getPubKeyRefCount(ksms, 3), is(1));

        /* verify public keys properly read */
        PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
        PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
        PublicKey keyC = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
        assertThat(KeySetUtils.getPubKey(ksms, 1), is(keyA));
        assertThat(KeySetUtils.getPubKey(ksms, 2), is(keyB));
        assertThat(KeySetUtils.getPubKey(ksms, 3), is(keyC));

        /* verify mapping is correct (ks -> pub keys) */
        LongSparseArray<ArraySet<Long>> ksMapping = KeySetUtils.getKeySetMapping(ksms);
        ArraySet<Long> mapping = ksMapping.get(1);
        assertThat(mapping.size(), is(1));
        assertThat(mapping.contains(new Long(1)), is(true));
        mapping = ksMapping.get(2);
        assertThat(mapping.size(), is(1));
        assertThat(mapping.contains(new Long(2)), is(true));
        mapping = ksMapping.get(3);
        assertThat(mapping.size(), is(1));
        assertThat(mapping.contains(new Long(3)), is(true));
        mapping = ksMapping.get(4);
        assertThat(mapping.size(), is(2));
        assertThat(mapping.contains(new Long(1)), is(true));
        assertThat(mapping.contains(new Long(2)), is(true));

        /* verify lastIssuedIds are consistent */
        assertThat(KeySetUtils.getLastIssuedKeyId(ksms), is(3L));
        assertThat(KeySetUtils.getLastIssuedKeySetId(ksms), is(4L));

        /* verify packages have been given the appropriate information */
        PackageSetting ps = packages.get("com.google.app1");
        assertThat(ps.keySetData.getProperSigningKeySet(), is(1L));
        ps = packages.get("com.google.app2");
        assertThat(ps.keySetData.getProperSigningKeySet(), is(1L));
        assertThat(ps.keySetData.getAliases().get("AB"), is(4L));
        ps = packages.get("com.android.app3");
        assertThat(ps.keySetData.getProperSigningKeySet(), is(2L));
        assertThat(ps.keySetData.getAliases().get("C"), is(3L));
        assertThat(ps.keySetData.getUpgradeKeySets().length, is(1));
        assertThat(ps.keySetData.getUpgradeKeySets()[0], is(3L));
    }
}
