Merge changes from topics "forced-non-staged-apex-update", "non-staged-flag" into main
* changes: Add an install flag to force non-staged APEX update Add --non-staged flag
This commit is contained in:
@@ -1561,6 +1561,14 @@ public abstract class PackageManager {
|
|||||||
*/
|
*/
|
||||||
public static final int INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK = 0x01000000;
|
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 */
|
/** @hide */
|
||||||
@IntDef(flag = true, value = {
|
@IntDef(flag = true, value = {
|
||||||
DONT_KILL_APP,
|
DONT_KILL_APP,
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ public abstract class ApexManager {
|
|||||||
/**
|
/**
|
||||||
* Performs a non-staged install of the given {@code apexFile}.
|
* 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;
|
throws PackageManagerException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1136,7 +1136,7 @@ public abstract class ApexManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void installPackage(File apexFile, PackageParser2 packageParser)
|
void installPackage(File apexFile, PackageParser2 packageParser, boolean force)
|
||||||
throws PackageManagerException {
|
throws PackageManagerException {
|
||||||
try {
|
try {
|
||||||
final int flags = PackageManager.GET_META_DATA
|
final int flags = PackageManager.GET_META_DATA
|
||||||
@@ -1159,7 +1159,7 @@ public abstract class ApexManager {
|
|||||||
}
|
}
|
||||||
checkApexSignature(existingApexPkg, newApexPkg);
|
checkApexSignature(existingApexPkg, newApexPkg);
|
||||||
ApexInfo apexInfo = waitForApexService().installAndActivatePackage(
|
ApexInfo apexInfo = waitForApexService().installAndActivatePackage(
|
||||||
apexFile.getAbsolutePath());
|
apexFile.getAbsolutePath(), force);
|
||||||
final ParsedPackage parsedPackage2 = packageParser.parsePackage(
|
final ParsedPackage parsedPackage2 = packageParser.parsePackage(
|
||||||
new File(apexInfo.modulePath), flags, /* useCaches= */ false);
|
new File(apexInfo.modulePath), flags, /* useCaches= */ false);
|
||||||
final PackageInfo finalApexPkg = PackageInfoWithoutStateUtils.generate(
|
final PackageInfo finalApexPkg = PackageInfoWithoutStateUtils.generate(
|
||||||
@@ -1505,7 +1505,7 @@ public abstract class ApexManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void installPackage(File apexFile, PackageParser2 packageParser) {
|
void installPackage(File apexFile, PackageParser2 packageParser, boolean force) {
|
||||||
throw new UnsupportedOperationException("APEX updates are not supported");
|
throw new UnsupportedOperationException("APEX updates are not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -872,8 +872,10 @@ final class InstallPackageHelper {
|
|||||||
"Expected exactly one .apex file under " + dir.getAbsolutePath()
|
"Expected exactly one .apex file under " + dir.getAbsolutePath()
|
||||||
+ " got: " + apexes.length);
|
+ " got: " + apexes.length);
|
||||||
}
|
}
|
||||||
|
boolean force = (request.mArgs.mInstallFlags
|
||||||
|
& PackageManager.INSTALL_FORCE_NON_STAGED_APEX_UPDATE) != 0;
|
||||||
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
|
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
|
||||||
mApexManager.installPackage(apexes[0], packageParser);
|
mApexManager.installPackage(apexes[0], packageParser, force);
|
||||||
}
|
}
|
||||||
} catch (PackageManagerException e) {
|
} catch (PackageManagerException e) {
|
||||||
request.mInstallResult.setError("APEX installation failed", e);
|
request.mInstallResult.setError("APEX installation failed", e);
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ import android.os.ServiceManager;
|
|||||||
import android.os.ServiceSpecificException;
|
import android.os.ServiceSpecificException;
|
||||||
import android.os.ShellCommand;
|
import android.os.ShellCommand;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.SystemProperties;
|
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
@@ -3053,6 +3052,13 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
// Set package source to other by default
|
// Set package source to other by default
|
||||||
sessionParams.setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER);
|
sessionParams.setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER);
|
||||||
|
|
||||||
|
// Encodes one of the states:
|
||||||
|
// 1. Install request explicitly specified --staged, then value will be true.
|
||||||
|
// 2. Install request explicitly specified --non-staged, then value will be false.
|
||||||
|
// 3. Install request did not specify either --staged or --non-staged, then for APEX
|
||||||
|
// installs the value will be true, and for apk installs it will be false.
|
||||||
|
Boolean staged = null;
|
||||||
|
|
||||||
String opt;
|
String opt;
|
||||||
boolean replaceExisting = true;
|
boolean replaceExisting = true;
|
||||||
boolean forceNonStaged = false;
|
boolean forceNonStaged = false;
|
||||||
@@ -3151,7 +3157,6 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
break;
|
break;
|
||||||
case "--apex":
|
case "--apex":
|
||||||
sessionParams.setInstallAsApex();
|
sessionParams.setInstallAsApex();
|
||||||
sessionParams.setStaged();
|
|
||||||
break;
|
break;
|
||||||
case "--force-non-staged":
|
case "--force-non-staged":
|
||||||
forceNonStaged = true;
|
forceNonStaged = true;
|
||||||
@@ -3160,7 +3165,10 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
sessionParams.setMultiPackage();
|
sessionParams.setMultiPackage();
|
||||||
break;
|
break;
|
||||||
case "--staged":
|
case "--staged":
|
||||||
sessionParams.setStaged();
|
staged = true;
|
||||||
|
break;
|
||||||
|
case "--non-staged":
|
||||||
|
staged = false;
|
||||||
break;
|
break;
|
||||||
case "--force-queryable":
|
case "--force-queryable":
|
||||||
sessionParams.setForceQueryable();
|
sessionParams.setForceQueryable();
|
||||||
@@ -3192,11 +3200,17 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
throw new IllegalArgumentException("Unknown option " + opt);
|
throw new IllegalArgumentException("Unknown option " + opt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (staged == null) {
|
||||||
|
staged = (sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
|
||||||
|
}
|
||||||
if (replaceExisting) {
|
if (replaceExisting) {
|
||||||
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
|
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
|
||||||
}
|
}
|
||||||
if (forceNonStaged) {
|
if (forceNonStaged) {
|
||||||
sessionParams.isStaged = false;
|
sessionParams.isStaged = false;
|
||||||
|
sessionParams.installFlags |= PackageManager.INSTALL_FORCE_NON_STAGED_APEX_UPDATE;
|
||||||
|
} else if (staged) {
|
||||||
|
sessionParams.setStaged();
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
@@ -3978,7 +3992,8 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
|
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
|
||||||
pw.println(" [--enable-rollback]");
|
pw.println(" [--enable-rollback]");
|
||||||
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
|
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
|
||||||
pw.println(" [--apex] [--force-non-staged] [--staged-ready-timeout TIMEOUT]");
|
pw.println(" [--apex] [--non-staged] [--force-non-staged]");
|
||||||
|
pw.println(" [--staged-ready-timeout TIMEOUT]");
|
||||||
pw.println(" [PATH [SPLIT...]|-]");
|
pw.println(" [PATH [SPLIT...]|-]");
|
||||||
pw.println(" Install an application. Must provide the apk data to install, either as");
|
pw.println(" Install an application. Must provide the apk data to install, either as");
|
||||||
pw.println(" file path(s) or '-' to read from stdin. Options are:");
|
pw.println(" file path(s) or '-' to read from stdin. Options are:");
|
||||||
@@ -4006,6 +4021,9 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
pw.println(" 3=device setup, 4=user request");
|
pw.println(" 3=device setup, 4=user request");
|
||||||
pw.println(" --force-uuid: force install on to disk volume with given UUID");
|
pw.println(" --force-uuid: force install on to disk volume with given UUID");
|
||||||
pw.println(" --apex: install an .apex file, not an .apk");
|
pw.println(" --apex: install an .apex file, not an .apk");
|
||||||
|
pw.println(" --non-staged: explicitly set this installation to be non-staged.");
|
||||||
|
pw.println(" This flag is only useful for APEX installs that are implicitly");
|
||||||
|
pw.println(" assumed to be staged.");
|
||||||
pw.println(" --force-non-staged: force the installation to run under a non-staged");
|
pw.println(" --force-non-staged: force the installation to run under a non-staged");
|
||||||
pw.println(" session, which may complete without requiring a reboot");
|
pw.println(" session, which may complete without requiring a reboot");
|
||||||
pw.println(" --staged-ready-timeout: By default, staged sessions wait "
|
pw.println(" --staged-ready-timeout: By default, staged sessions wait "
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.server.pm;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
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");
|
File apex = extractResource("test.apex_rebootless_v1", "test.rebootless_apex_v1.apex");
|
||||||
PackageManagerException e = expectThrows(PackageManagerException.class,
|
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");
|
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");
|
File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
|
||||||
ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
|
ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
|
||||||
/* isFactory= */ false, finalApex);
|
/* 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");
|
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",
|
PackageInfo newInfo = mApexManager.getPackageInfo("test.apex.rebootless",
|
||||||
ApexManager.MATCH_ACTIVE_PACKAGE);
|
ApexManager.MATCH_ACTIVE_PACKAGE);
|
||||||
@@ -416,10 +418,11 @@ public class ApexManagerTest {
|
|||||||
File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
|
File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
|
||||||
ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
|
ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
|
||||||
/* isFactory= */ false, finalApex);
|
/* 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");
|
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",
|
PackageInfo newInfo = mApexManager.getPackageInfo("test.apex.rebootless",
|
||||||
ApexManager.MATCH_ACTIVE_PACKAGE);
|
ApexManager.MATCH_ACTIVE_PACKAGE);
|
||||||
@@ -447,13 +450,14 @@ public class ApexManagerTest {
|
|||||||
mApexManager.scanApexPackagesTraced(mPackageParser2,
|
mApexManager.scanApexPackagesTraced(mPackageParser2,
|
||||||
ParallelPackageParser.makeExecutorService());
|
ParallelPackageParser.makeExecutorService());
|
||||||
|
|
||||||
when(mApexService.installAndActivatePackage(anyString())).thenThrow(
|
when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenThrow(
|
||||||
new RuntimeException("install failed :("));
|
new RuntimeException("install failed :("));
|
||||||
|
|
||||||
File installedApex = extractResource("test.apex_rebootless_v1",
|
File installedApex = extractResource("test.apex_rebootless_v1",
|
||||||
"test.rebootless_apex_v1.apex");
|
"test.rebootless_apex_v1.apex");
|
||||||
assertThrows(PackageManagerException.class,
|
assertThrows(PackageManagerException.class,
|
||||||
() -> mApexManager.installPackage(installedApex, mPackageParser2));
|
() -> mApexManager.installPackage(installedApex, mPackageParser2, /* force= */
|
||||||
|
false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -468,7 +472,8 @@ public class ApexManagerTest {
|
|||||||
File installedApex = extractResource("shim_different_certificate",
|
File installedApex = extractResource("shim_different_certificate",
|
||||||
"com.android.apex.cts.shim.v2_different_certificate.apex");
|
"com.android.apex.cts.shim.v2_different_certificate.apex");
|
||||||
PackageManagerException e = expectThrows(PackageManagerException.class,
|
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("APK container signature of ");
|
||||||
assertThat(e).hasMessageThat().contains(
|
assertThat(e).hasMessageThat().contains(
|
||||||
"is not compatible with currently installed on device");
|
"is not compatible with currently installed on device");
|
||||||
@@ -486,7 +491,8 @@ public class ApexManagerTest {
|
|||||||
File installedApex = extractResource("shim_unsigned_apk_container",
|
File installedApex = extractResource("shim_unsigned_apk_container",
|
||||||
"com.android.apex.cts.shim.v2_unsigned_apk_container.apex");
|
"com.android.apex.cts.shim.v2_unsigned_apk_container.apex");
|
||||||
PackageManagerException e = expectThrows(PackageManagerException.class,
|
PackageManagerException e = expectThrows(PackageManagerException.class,
|
||||||
() -> mApexManager.installPackage(installedApex, mPackageParser2));
|
() -> mApexManager.installPackage(installedApex, mPackageParser2, /* force= */
|
||||||
|
false));
|
||||||
assertThat(e).hasMessageThat().contains("Failed to collect certificates from ");
|
assertThat(e).hasMessageThat().contains("Failed to collect certificates from ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user