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

Commit 5770e468 authored by Nikita Ioffe's avatar Nikita Ioffe
Browse files

Add an install flag to force non-staged APEX update

This is a development only feature to speed up development workflow for
teams that have their code package in an APEX.

Bug: 290750901
Test: m
Test: atest ApexManagerTest
Test: see other change in the topic
Merged-In: Ic0abb9e97d529910805af50208baf42b9b28b171
Change-Id: Ib452dcdbdd30991f60205f94be25389379153183
parent f85f3a29
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -1561,6 +1561,14 @@ public abstract class PackageManager {
     */
    public static final int INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK = 0x01000000;

    /**
     * Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
     * a development-only feature and should not be used on end user devices.
     *
     * @hide
     */
    public static final int INSTALL_FORCE_NON_STAGED_APEX_UPDATE = 0x02000000;

    /** @hide */
    @IntDef(flag = true, value = {
            DONT_KILL_APP,
+4 −4
Original line number Diff line number Diff line
@@ -424,7 +424,7 @@ public abstract class ApexManager {
    /**
     * Performs a non-staged install of the given {@code apexFile}.
     */
    abstract void installPackage(File apexFile, PackageParser2 packageParser)
    abstract void installPackage(File apexFile, PackageParser2 packageParser, boolean force)
            throws PackageManagerException;

    /**
@@ -1136,7 +1136,7 @@ public abstract class ApexManager {
        }

        @Override
        void installPackage(File apexFile, PackageParser2 packageParser)
        void installPackage(File apexFile, PackageParser2 packageParser, boolean force)
                throws PackageManagerException {
            try {
                final int flags = PackageManager.GET_META_DATA
@@ -1159,7 +1159,7 @@ public abstract class ApexManager {
                }
                checkApexSignature(existingApexPkg, newApexPkg);
                ApexInfo apexInfo = waitForApexService().installAndActivatePackage(
                        apexFile.getAbsolutePath());
                        apexFile.getAbsolutePath(), force);
                final ParsedPackage parsedPackage2 = packageParser.parsePackage(
                        new File(apexInfo.modulePath), flags, /* useCaches= */ false);
                final PackageInfo finalApexPkg = PackageInfoWithoutStateUtils.generate(
@@ -1505,7 +1505,7 @@ public abstract class ApexManager {
        }

        @Override
        void installPackage(File apexFile, PackageParser2 packageParser) {
        void installPackage(File apexFile, PackageParser2 packageParser, boolean force) {
            throw new UnsupportedOperationException("APEX updates are not supported");
        }

+3 −1
Original line number Diff line number Diff line
@@ -872,8 +872,10 @@ final class InstallPackageHelper {
                        "Expected exactly one .apex file under " + dir.getAbsolutePath()
                                + " got: " + apexes.length);
            }
            boolean force = (request.mArgs.mInstallFlags
                    & PackageManager.INSTALL_FORCE_NON_STAGED_APEX_UPDATE) != 0;
            try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
                mApexManager.installPackage(apexes[0], packageParser);
                mApexManager.installPackage(apexes[0], packageParser, force);
            }
        } catch (PackageManagerException e) {
            request.mInstallResult.setError("APEX installation failed", e);
+1 −0
Original line number Diff line number Diff line
@@ -3208,6 +3208,7 @@ class PackageManagerShellCommand extends ShellCommand {
        }
        if (forceNonStaged) {
            sessionParams.isStaged = false;
            sessionParams.installFlags |= PackageManager.INSTALL_FORCE_NON_STAGED_APEX_UPDATE;
        } else if (staged) {
            sessionParams.setStaged();
        }
+15 −9
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
@@ -362,7 +363,7 @@ public class ApexManagerTest {

        File apex = extractResource("test.apex_rebootless_v1", "test.rebootless_apex_v1.apex");
        PackageManagerException e = expectThrows(PackageManagerException.class,
                () -> mApexManager.installPackage(apex, mPackageParser2));
                () -> mApexManager.installPackage(apex, mPackageParser2, /* force= */ false));
        assertThat(e).hasMessageThat().contains("It is forbidden to install new APEX packages");
    }

@@ -378,10 +379,11 @@ public class ApexManagerTest {
        File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
        ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
                /* isFactory= */ false, finalApex);
        when(mApexService.installAndActivatePackage(anyString())).thenReturn(newApexInfo);
        when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenReturn(
                newApexInfo);

        File installedApex = extractResource("installed", "test.rebootless_apex_v2.apex");
        mApexManager.installPackage(installedApex, mPackageParser2);
        mApexManager.installPackage(installedApex, mPackageParser2, /* force= */ false);

        PackageInfo newInfo = mApexManager.getPackageInfo("test.apex.rebootless",
                ApexManager.MATCH_ACTIVE_PACKAGE);
@@ -416,10 +418,11 @@ public class ApexManagerTest {
        File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
        ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
                /* isFactory= */ false, finalApex);
        when(mApexService.installAndActivatePackage(anyString())).thenReturn(newApexInfo);
        when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenReturn(
                newApexInfo);

        File installedApex = extractResource("installed", "test.rebootless_apex_v2.apex");
        mApexManager.installPackage(installedApex, mPackageParser2);
        mApexManager.installPackage(installedApex, mPackageParser2, /* force= */ false);

        PackageInfo newInfo = mApexManager.getPackageInfo("test.apex.rebootless",
                ApexManager.MATCH_ACTIVE_PACKAGE);
@@ -447,13 +450,14 @@ public class ApexManagerTest {
        mApexManager.scanApexPackagesTraced(mPackageParser2,
                ParallelPackageParser.makeExecutorService());

        when(mApexService.installAndActivatePackage(anyString())).thenThrow(
        when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenThrow(
                new RuntimeException("install failed :("));

        File installedApex = extractResource("test.apex_rebootless_v1",
                "test.rebootless_apex_v1.apex");
        assertThrows(PackageManagerException.class,
                () -> mApexManager.installPackage(installedApex, mPackageParser2));
                () -> mApexManager.installPackage(installedApex, mPackageParser2, /* force= */
                        false));
    }

    @Test
@@ -468,7 +472,8 @@ public class ApexManagerTest {
        File installedApex = extractResource("shim_different_certificate",
                "com.android.apex.cts.shim.v2_different_certificate.apex");
        PackageManagerException e = expectThrows(PackageManagerException.class,
                () -> mApexManager.installPackage(installedApex, mPackageParser2));
                () -> mApexManager.installPackage(installedApex, mPackageParser2, /* force= */
                        false));
        assertThat(e).hasMessageThat().contains("APK container signature of ");
        assertThat(e).hasMessageThat().contains(
                "is not compatible with currently installed on device");
@@ -486,7 +491,8 @@ public class ApexManagerTest {
        File installedApex = extractResource("shim_unsigned_apk_container",
                "com.android.apex.cts.shim.v2_unsigned_apk_container.apex");
        PackageManagerException e = expectThrows(PackageManagerException.class,
                () -> mApexManager.installPackage(installedApex, mPackageParser2));
                () -> mApexManager.installPackage(installedApex, mPackageParser2, /* force= */
                        false));
        assertThat(e).hasMessageThat().contains("Failed to collect certificates from ");
    }