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

Commit 19a969e5 authored by Pedro Loureiro's avatar Pedro Loureiro Committed by Android (Google) Code Review
Browse files

Merge "Parse new xml attributes used for updatable shared libraries"

parents e971f8d1 70da334f
Loading
Loading
Loading
Loading
+80 −7
Original line number Original line Diff line number Diff line
@@ -108,17 +108,74 @@ public class SystemConfig {
        public final String name;
        public final String name;
        public final String filename;
        public final String filename;
        public final String[] dependencies;
        public final String[] dependencies;

        /**
         * SDK version this library was added to the BOOTCLASSPATH.
         *
         * <p>At the SDK level specified in this field and higher, the apps' uses-library tags for
         * this library will be ignored, since the library is always available on BOOTCLASSPATH.
         *
         * <p>0 means not specified.
         */
        public final int onBootclasspathSince;

        /**
         * SDK version this library was removed from the BOOTCLASSPATH.
         *
         * <p>At the SDK level specified in this field and higher, this library needs to be
         * explicitly added by apps. For compatibility reasons, when an app
         * targets an SDK less than the value of this attribute, this library is automatically
         * added.
         *
         * <p>0 means not specified.
         */
        public final int onBootclasspathBefore;

        /**
         * Declares whether this library can be safely ignored from <uses-library> tags.
         *
         * <p> This can happen if the library initially had to be explicitly depended-on using that
         * tag but has since been moved to the BOOTCLASSPATH which means now is always available
         * and the tag is no longer required.
         */
        public final boolean canBeSafelyIgnored;

        public final boolean isNative;
        public final boolean isNative;


        SharedLibraryEntry(String name, String filename, String[] dependencies) {

            this(name, filename, dependencies, false /* isNative */);
        @VisibleForTesting
        public SharedLibraryEntry(String name, String filename, String[] dependencies,
                boolean isNative) {
            this(name, filename, dependencies, 0 /* onBootclasspathSince */,
                    0 /* onBootclasspathBefore */, isNative);
        }

        @VisibleForTesting
        public SharedLibraryEntry(String name, String filename, String[] dependencies,
                int onBootclasspathSince, int onBootclassPathBefore) {
            this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore,
                    false /* isNative */);
        }
        }


        SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) {
        SharedLibraryEntry(String name, String filename, String[] dependencies,
                int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) {
            this.name = name;
            this.name = name;
            this.filename = filename;
            this.filename = filename;
            this.dependencies = dependencies;
            this.dependencies = dependencies;
            this.onBootclasspathSince = onBootclasspathSince;
            this.onBootclasspathBefore = onBootclasspathBefore;
            this.isNative = isNative;
            this.isNative = isNative;

            canBeSafelyIgnored = this.onBootclasspathSince != 0
                    && isSdkAtLeast(this.onBootclasspathSince);
        }

        private static boolean isSdkAtLeast(int level) {
            if ("REL".equals(Build.VERSION.CODENAME)) {
                return Build.VERSION.SDK_INT >= level;
            }
            return level == Build.VERSION_CODES.CUR_DEVELOPMENT
                    || Build.VERSION.SDK_INT >= level;
        }
        }
    }
    }


@@ -771,11 +828,17 @@ public class SystemConfig {
                            XmlUtils.skipCurrentTag(parser);
                            XmlUtils.skipCurrentTag(parser);
                        }
                        }
                    } break;
                    } break;
                    case "updatable-library":
                        // "updatable-library" is meant to behave exactly like "library"
                    case "library": {
                    case "library": {
                        if (allowLibs) {
                        if (allowLibs) {
                            String lname = parser.getAttributeValue(null, "name");
                            String lname = parser.getAttributeValue(null, "name");
                            String lfile = parser.getAttributeValue(null, "file");
                            String lfile = parser.getAttributeValue(null, "file");
                            String ldependency = parser.getAttributeValue(null, "dependency");
                            String ldependency = parser.getAttributeValue(null, "dependency");
                            int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk",
                                    0);
                            int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk",
                                    0);
                            if (lname == null) {
                            if (lname == null) {
                                Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
                                Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
                                        + parser.getPositionDescription());
                                        + parser.getPositionDescription());
@@ -783,11 +846,21 @@ public class SystemConfig {
                                Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
                                Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
                                        + parser.getPositionDescription());
                                        + parser.getPositionDescription());
                            } else {
                            } else {
                                //Log.i(TAG, "Got library " + lname + " in " + lfile);
                                boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT;
                                boolean allowedMaxSdk =
                                        maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT;
                                if (allowedMinSdk && allowedMaxSdk) {
                                    int bcpSince = XmlUtils.readIntAttribute(parser,
                                            "on-bootclasspath-since", 0);
                                    int bcpBefore = XmlUtils.readIntAttribute(parser,
                                            "on-bootclasspath-before", 0);
                                    SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
                                    SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
                                        ldependency == null ? new String[0] : ldependency.split(":"));
                                            ldependency == null
                                                    ? new String[0] : ldependency.split(":"),
                                            bcpSince, bcpBefore);
                                    mSharedLibraries.put(lname, entry);
                                    mSharedLibraries.put(lname, entry);
                                }
                                }
                            }
                        } else {
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        }
+67 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 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 com.android.server.pm.parsing.library;

import android.util.ArrayMap;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.ParsedPackage;

/**
 * Updates packages to add or remove dependencies on shared libraries as per attributes
 * in the library declaration
 *
 * @hide
 */
@VisibleForTesting
public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater {

    /**
     * ArrayMap like the one you find in {@link SystemConfig}. The keys are the library names.
     */
    private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries;

    public ApexSharedLibraryUpdater(
            ArrayMap<String, SystemConfig.SharedLibraryEntry> sharedLibraries) {
        mSharedLibraries = sharedLibraries;
    }

    @Override
    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
        final int builtInLibCount = mSharedLibraries.size();
        for (int i = 0; i < builtInLibCount; i++) {
            updateSharedLibraryForPackage(mSharedLibraries.valueAt(i), parsedPackage);
        }
    }

    private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry,
            ParsedPackage parsedPackage) {
        if (entry.onBootclasspathBefore != 0
                && parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) {
            // this package targets an API where this library was in the BCP, so add
            // the library transparently in case the package is using it
            prefixRequiredLibrary(parsedPackage, entry.name);
        }

        if (entry.canBeSafelyIgnored) {
            // the library is now present in the BCP and always available; we don't need to add
            // it a second time
            removeLibrary(parsedPackage, entry.name);
        }
    }
}
+11 −0
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import android.content.pm.parsing.ParsingPackage;
import android.util.Log;
import android.util.Log;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.parsing.pkg.ParsedPackage;


import java.util.ArrayList;
import java.util.ArrayList;
@@ -63,6 +64,11 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {


        boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters);
        boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters);


        // ApexSharedLibraryUpdater should be the last one, to allow modifications introduced by
        // mainline after dessert release.
        packageUpdaters.add(new ApexSharedLibraryUpdater(
                SystemConfig.getInstance().getSharedLibraries()));

        PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
        PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
                .toArray(new PackageSharedLibraryUpdater[0]);
                .toArray(new PackageSharedLibraryUpdater[0]);
        INSTANCE = new PackageBackwardCompatibility(
        INSTANCE = new PackageBackwardCompatibility(
@@ -106,6 +112,11 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {


    private final PackageSharedLibraryUpdater[] mPackageUpdaters;
    private final PackageSharedLibraryUpdater[] mPackageUpdaters;


    @VisibleForTesting
    PackageSharedLibraryUpdater[] getPackageUpdaters() {
        return mPackageUpdaters;
    }

    private PackageBackwardCompatibility(
    private PackageBackwardCompatibility(
            boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
            boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
        this.mBootClassPathContainsATB = bootClassPathContainsATB;
        this.mBootClassPathContainsATB = bootClassPathContainsATB;
+281 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 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 com.android.server.pm.parsing.library;

import android.os.Build;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;

import androidx.test.filters.SmallTest;

import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;

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


/**
 * Test for {@link ApexSharedLibraryUpdater}
 */
@Presubmit
@SmallTest
@RunWith(JUnit4.class)
public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest {

    private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries =
            new ArrayMap<>(8);

    @Before
    public void setUp() throws Exception {
        installSharedLibraries();
    }

    private void installSharedLibraries() throws Exception {
        mSharedLibraries.clear();
        insertLibrary("foo", 0, 0);
        insertLibrary("fooBcpSince30", 30, 0);
        insertLibrary("fooBcpBefore30", 0, 30);
        insertLibrary("fooFromFuture", Build.VERSION.SDK_INT + 2, 0);
    }

    private void insertLibrary(String libraryName, int onBootclasspathSince,
            int onBootclasspathBefore) {
        mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry(
                libraryName,
                "foo.jar",
                new String[0] /* dependencies */,
                onBootclasspathSince,
                onBootclasspathBefore
                )
        );
    }

    @Test
    public void testRegularAppOnRPlus() {
        // platform Q should have changes (tested below)

        // these should have no changes
        checkNoChanges(Build.VERSION_CODES.R);
        checkNoChanges(Build.VERSION_CODES.S);
        checkNoChanges(Build.VERSION_CODES.TIRAMISU);
        checkNoChanges(Build.VERSION_CODES.CUR_DEVELOPMENT);
    }

    private void checkNoChanges(int targetSdkVersion) {
        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(targetSdkVersion)
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(targetSdkVersion)
                .hideAsParsed())
                .hideAsFinal();

        checkBackwardsCompatibility(before, after);
    }

    @Test
    public void testBcpSince30Applied() {
        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .addUsesLibrary("fooBcpSince30")
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .hideAsParsed())
                .hideAsFinal();

        // note: target sdk is not what matters in this logic. It's the system SDK
        // should be removed because on 30+ (R+) it is implicit

        checkBackwardsCompatibility(before, after);
    }

    @Test
    public void testBcpSince11kNotAppliedWithoutLibrary() {
        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .hideAsParsed())
                .hideAsFinal();

        // note: target sdk is not what matters in this logic. It's the system SDK
        // nothing should change because the implicit from is only from a future platform release
        checkBackwardsCompatibility(before, after);
    }

    @Test
    public void testBcpSince11kNotAppliedWithLibrary() {
        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .addUsesLibrary("fooFromFuture")
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .addUsesLibrary("fooFromFuture")
                .hideAsParsed())
                .hideAsFinal();

        // note: target sdk is not what matters in this logic. It's the system SDK
        // nothing should change because the implicit from is only from a future platform release
        checkBackwardsCompatibility(before, after);
    }

    @Test
    public void testBcpBefore30NotApplied() {
        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .hideAsParsed())
                .hideAsFinal();

        // should not be affected because it is still in the BCP in 30 / R
        checkBackwardsCompatibility(before, after);
    }

    @Test
    public void testBcpBefore30Applied() {
        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.Q)
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.Q)
                .addUsesLibrary("fooBcpBefore30")
                .hideAsParsed())
                .hideAsFinal();

        // should be present because this was in BCP in 29 / Q
        checkBackwardsCompatibility(before, after);
    }

    /**
     * Test a library that was first removed from the BCP [to a mainline module] and later was
     * moved back to the BCP via a mainline module update. All of this happening before the current
     * SDK.
     */
    @Test
    public void testBcpRemovedThenAddedPast() {
        insertLibrary("fooBcpRemovedThenAdded", 30, 28);

        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.N)
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.N)
                .addUsesLibrary("fooBcpBefore30")
                .hideAsParsed())
                .hideAsFinal();

        // the library is now in the BOOTCLASSPATH (for the second time) so it doesn't need to be
        // listed
        checkBackwardsCompatibility(before, after);
    }

    /**
     * Test a library that was first removed from the BCP [to a mainline module] and later was
     * moved back to the BCP via a mainline module update. The first part happening before the
     * current SDK and the second part after.
     */
    @Test
    public void testBcpRemovedThenAddedMiddle_targetQ() {
        insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);

        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.Q)
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.Q)
                .addUsesLibrary("fooBcpRemovedThenAdded")
                .addUsesLibrary("fooBcpBefore30")
                .hideAsParsed())
                .hideAsFinal();

        // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
        // Because the app targets Q / 29 (when this library was in the BCP) then we need to add it
        checkBackwardsCompatibility(before, after);
    }

    /**
     * Test a library that was first removed from the BCP [to a mainline module] and later was
     * moved back to the BCP via a mainline module update. The first part happening before the
     * current SDK and the second part after.
     */
    @Test
    public void testBcpRemovedThenAddedMiddle_targetR() {
        insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);

        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .hideAsParsed())
                .hideAsFinal();

        // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
        // Because the app targets R/30 (when this library was removed from the BCP) then we don't
        //need to add it
        checkBackwardsCompatibility(before, after);
    }

    /**
     * Test a library that was first removed from the BCP [to a mainline module] and later was
     * moved back to the BCP via a mainline module update. The first part happening before the
     * current SDK and the second part after.
     */
    @Test
    public void testBcpRemovedThenAddedMiddle_targetR_usingLib() {
        insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);

        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .addUsesLibrary("fooBcpRemovedThenAdded")
                .hideAsParsed());

        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
                .setTargetSdkVersion(Build.VERSION_CODES.R)
                .addUsesLibrary("fooBcpRemovedThenAdded")
                .hideAsParsed())
                .hideAsFinal();

        // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
        // Because the app wants to use the library, it needs to be present
        checkBackwardsCompatibility(before, after);
    }

    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
        checkBackwardsCompatibility(before, after,
                () -> new ApexSharedLibraryUpdater(mSharedLibraries));
    }
}
+18 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,8 @@ import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_T
import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;


import static com.google.common.truth.Truth.assertThat;

import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackage;
import android.os.Build;
import android.os.Build;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.Presubmit;
@@ -182,6 +184,22 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate
        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
    }
    }


    /**
     * Ensures that ApexSharedLibraryUpdater is the last updater in the list of package updaters
     * used by PackageBackwardCompatibility.
     *
     * This is required so mainline can add and remove libraries installed by the platform updaters.
     */
    @Test
    public void testApexPackageUpdaterOrdering() {
        PackageBackwardCompatibility instance =
                (PackageBackwardCompatibility) PackageBackwardCompatibility.getInstance();
        PackageSharedLibraryUpdater[] updaterArray = instance.getPackageUpdaters();

        PackageSharedLibraryUpdater lastUpdater = updaterArray[updaterArray.length - 1];
        assertThat(lastUpdater).isInstanceOf(ApexSharedLibraryUpdater.class);
    }

    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
        checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
        checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
    }
    }
Loading