summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/PackageInstaller.java6
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java13
-rw-r--r--services/core/java/com/android/server/pm/InstallArgs.java8
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java17
-rw-r--r--services/core/java/com/android/server/pm/InstallingSession.java13
-rw-r--r--services/core/java/com/android/server/pm/MovePackageHelper.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java15
10 files changed, 78 insertions, 25 deletions
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 4b883cd4c17b..082ede2641ea 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2369,6 +2369,8 @@ public class PackageInstaller {
public int requireUserAction = USER_ACTION_UNSPECIFIED;
/** {@hide} */
public boolean applicationEnabledSettingPersistent = false;
+ /** {@hide} */
+ public int developmentInstallFlags = 0;
private final ArrayMap<String, Integer> mPermissionStates;
@@ -2418,6 +2420,7 @@ public class PackageInstaller {
requireUserAction = source.readInt();
packageSource = source.readInt();
applicationEnabledSettingPersistent = source.readBoolean();
+ developmentInstallFlags = source.readInt();
}
/** {@hide} */
@@ -2449,6 +2452,7 @@ public class PackageInstaller {
ret.requireUserAction = requireUserAction;
ret.packageSource = packageSource;
ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
+ ret.developmentInstallFlags = developmentInstallFlags;
return ret;
}
@@ -3113,6 +3117,7 @@ public class PackageInstaller {
pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
pw.printPair("applicationEnabledSettingPersistent",
applicationEnabledSettingPersistent);
+ pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
pw.println();
}
@@ -3154,6 +3159,7 @@ public class PackageInstaller {
dest.writeInt(requireUserAction);
dest.writeInt(packageSource);
dest.writeBoolean(applicationEnabledSettingPersistent);
+ dest.writeInt(developmentInstallFlags);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 12b366e3dbaa..25c73ee3f603 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1454,6 +1454,16 @@ public abstract class PackageManager {
public @interface InstallFlags {}
/**
+ * Install flags that can only be used in development workflows (e.g. {@code adb install}).
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "INSTALL_DEVELOPMENT_" }, value = {
+ INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DevelopmentInstallFlags {}
+
+ /**
* Flag parameter for {@link #installPackage} to indicate that you want to
* replace an already installed package, if one exists.
*
@@ -1663,6 +1673,14 @@ public abstract class PackageManager {
*/
public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
+ /**
+ * 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_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE = 1;
+
/** @hide */
@IntDef(flag = true, value = {
DONT_KILL_APP,
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 5e62b56c7bcd..42be07b3e3ba 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -363,9 +363,13 @@ public abstract class ApexManager {
/**
* Performs a non-staged install of the given {@code apexFile}.
*
+ * If {@code force} is {@code true}, then update is forced even for APEXes that do not support
+ * non-staged update. This feature is only available on debuggable builds to improve development
+ * velocity of the teams that have their code packaged in an APEX.
+ *
* @return {@code ApeInfo} about the newly installed APEX package.
*/
- abstract ApexInfo installPackage(File apexFile) throws PackageManagerException;
+ abstract ApexInfo installPackage(File apexFile, boolean force) throws PackageManagerException;
/**
* Get a list of apex system services implemented in an apex.
@@ -910,10 +914,11 @@ public abstract class ApexManager {
}
@Override
- ApexInfo installPackage(File apexFile)
+ ApexInfo installPackage(File apexFile, boolean force)
throws PackageManagerException {
try {
- return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath());
+ return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath(),
+ force);
} catch (RemoteException e) {
throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"apexservice not available");
@@ -1170,7 +1175,7 @@ public abstract class ApexManager {
}
@Override
- ApexInfo installPackage(File apexFile) {
+ ApexInfo installPackage(File apexFile, boolean force) {
throw new UnsupportedOperationException("APEX updates are not supported");
}
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index 6de7f07b78bb..dd96a2b84a97 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -43,6 +43,7 @@ final class InstallArgs {
final IPackageInstallObserver2 mObserver;
// Always refers to PackageManager flags only
final int mInstallFlags;
+ final int mDevelopmentInstallFlags;
@NonNull
final InstallSource mInstallSource;
final String mVolumeUuid;
@@ -69,8 +70,8 @@ final class InstallArgs {
@Nullable String[] mInstructionSets;
InstallArgs(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
- int installFlags, InstallSource installSource, String volumeUuid,
- UserHandle user, String[] instructionSets, String abiOverride,
+ int installFlags, int developmentInstallFlags, InstallSource installSource,
+ String volumeUuid, UserHandle user, String[] instructionSets, String abiOverride,
@NonNull ArrayMap<String, Integer> permissionStates,
List<String> allowlistedRestrictedPermissions,
int autoRevokePermissionsMode, String traceMethod, int traceCookie,
@@ -80,6 +81,7 @@ final class InstallArgs {
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mInstallFlags = installFlags;
+ mDevelopmentInstallFlags = developmentInstallFlags;
mObserver = observer;
mInstallSource = Preconditions.checkNotNull(installSource);
mVolumeUuid = volumeUuid;
@@ -105,7 +107,7 @@ final class InstallArgs {
* when cleaning up old installs, or used as a move source.
*/
InstallArgs(String codePath, String[] instructionSets) {
- this(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY, null, null,
+ this(OriginInfo.fromNothing(), null, null, 0, 0, InstallSource.EMPTY, null, null,
instructionSets, null, new ArrayMap<>(), null, MODE_DEFAULT, null, 0,
SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN,
PackageManager.INSTALL_SCENARIO_DEFAULT, false, DataLoaderType.NONE,
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 34648740d54c..6fc14e814525 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -134,12 +134,13 @@ final class InstallRequest {
InstallRequest(InstallingSession params) {
mUserId = params.getUser().getIdentifier();
mInstallArgs = new InstallArgs(params.mOriginInfo, params.mMoveInfo, params.mObserver,
- params.mInstallFlags, params.mInstallSource, params.mVolumeUuid,
- params.getUser(), null /*instructionSets*/, params.mPackageAbiOverride,
- params.mPermissionStates, params.mAllowlistedRestrictedPermissions,
- params.mAutoRevokePermissionsMode, params.mTraceMethod, params.mTraceCookie,
- params.mSigningDetails, params.mInstallReason, params.mInstallScenario,
- params.mForceQueryableOverride, params.mDataLoaderType, params.mPackageSource,
+ params.mInstallFlags, params.mDevelopmentInstallFlags, params.mInstallSource,
+ params.mVolumeUuid, params.getUser(), null /*instructionSets*/,
+ params.mPackageAbiOverride, params.mPermissionStates,
+ params.mAllowlistedRestrictedPermissions, params.mAutoRevokePermissionsMode,
+ params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
+ params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
+ params.mDataLoaderType, params.mPackageSource,
params.mApplicationEnabledSettingPersistent);
mPackageMetrics = new PackageMetrics(this);
mIsInstallInherit = params.mIsInherit;
@@ -286,6 +287,10 @@ final class InstallRequest {
return mInstallArgs == null ? 0 : mInstallArgs.mInstallFlags;
}
+ public int getDevelopmentInstallFlags() {
+ return mInstallArgs == null ? 0 : mInstallArgs.mDevelopmentInstallFlags;
+ }
+
public int getInstallReason() {
return mInstallArgs == null ? INSTALL_REASON_UNKNOWN : mInstallArgs.mInstallReason;
}
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index 35862db3d3db..30a23bfd0641 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -68,6 +68,7 @@ class InstallingSession {
final MoveInfo mMoveInfo;
final IPackageInstallObserver2 mObserver;
int mInstallFlags;
+ int mDevelopmentInstallFlags;
@NonNull
final InstallSource mInstallSource;
final String mVolumeUuid;
@@ -102,8 +103,8 @@ class InstallingSession {
// For move install
InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
- int installFlags, InstallSource installSource, String volumeUuid,
- UserHandle user, String packageAbiOverride, int packageSource,
+ int installFlags, int developmentInstallFlags, InstallSource installSource,
+ String volumeUuid, UserHandle user, String packageAbiOverride, int packageSource,
PackageLite packageLite, PackageManagerService pm) {
mPm = pm;
mUser = user;
@@ -113,6 +114,7 @@ class InstallingSession {
mMoveInfo = moveInfo;
mObserver = observer;
mInstallFlags = installFlags;
+ mDevelopmentInstallFlags = developmentInstallFlags;
mInstallSource = Preconditions.checkNotNull(installSource);
mVolumeUuid = volumeUuid;
mPackageAbiOverride = packageAbiOverride;
@@ -149,6 +151,7 @@ class InstallingSession {
mInstallScenario = sessionParams.installScenario;
mObserver = observer;
mInstallFlags = sessionParams.installFlags;
+ mDevelopmentInstallFlags = sessionParams.developmentInstallFlags;
mInstallSource = installSource;
mVolumeUuid = sessionParams.volumeUuid;
mPackageAbiOverride = sessionParams.abiOverride;
@@ -592,6 +595,10 @@ class InstallingSession {
"Only a non-staged install of a single APEX is supported");
}
InstallRequest request = requests.get(0);
+ boolean force =
+ (request.getDevelopmentInstallFlags()
+ & PackageManager.INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE)
+ != 0;
try {
// Should directory scanning logic be moved to ApexManager for better test coverage?
final File dir = request.getOriginInfo().mResolvedFile;
@@ -608,7 +615,7 @@ class InstallingSession {
PackageManagerException.INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE);
}
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
- ApexInfo apexInfo = mPm.mApexManager.installPackage(apexes[0]);
+ ApexInfo apexInfo = mPm.mApexManager.installPackage(apexes[0], force);
// APEX has been handled successfully by apexd. Let's continue the install flow
// so it will be scanned and registered with the system.
// TODO(b/225756739): Improve atomicity of rebootless APEX install.
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index f55d1eb21d87..adee143691b1 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -305,8 +305,9 @@ public final class MovePackageHelper {
new File(origin.mResolvedPath), /* flags */ 0);
final PackageLite lite = ret.isSuccess() ? ret.getResult() : null;
final InstallingSession installingSession = new InstallingSession(origin, move,
- installObserver, installFlags, installSource, volumeUuid, user, packageAbiOverride,
- PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, lite, mPm);
+ installObserver, installFlags, /* developmentInstallFlags= */ 0, installSource,
+ volumeUuid, user, packageAbiOverride, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED,
+ lite, mPm);
installingSession.movePackage();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a9115371413c..178719f3c4ec 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -709,6 +709,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
!= PackageManager.PERMISSION_GRANTED) {
params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
}
+
+ // developmentInstallFlags can ony be set by shell or root.
+ params.developmentInstallFlags = 0;
}
String originatingPackageName = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a096f330689c..2a7ebfb75dde 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3380,6 +3380,8 @@ class PackageManagerShellCommand extends ShellCommand {
}
if (forceNonStaged) {
sessionParams.isStaged = false;
+ sessionParams.developmentInstallFlags |=
+ PackageManager.INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE;
} else if (staged) {
sessionParams.setStaged();
}
@@ -4279,7 +4281,8 @@ class PackageManagerShellCommand extends ShellCommand {
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(" session, which may complete without requiring a reboot");
+ pw.println(" session, which may complete without requiring a reboot. This will");
+ pw.println(" force a rebootless update even for APEXes that don't support it");
pw.println(" --staged-ready-timeout: By default, staged sessions wait "
+ DEFAULT_STAGED_READY_TIMEOUT_MS);
pw.println(" milliseconds for pre-reboot verification to complete when");
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 5b0e2f3800c3..3a73dd9eb101 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -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;
@@ -368,10 +369,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");
- newApexInfo = mApexManager.installPackage(installedApex);
+ newApexInfo = mApexManager.installPackage(installedApex, /* force= */ false);
var newPkg = mockParsePackage(mPackageParser2, newApexInfo);
assertThat(newPkg.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath());
@@ -398,10 +400,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");
- newApexInfo = mApexManager.installPackage(installedApex);
+ newApexInfo = mApexManager.installPackage(installedApex, /* force= */ false);
var newPkg = mockParsePackage(mPackageParser2, newApexInfo);
assertThat(newPkg.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath());
@@ -416,13 +419,13 @@ public class ApexManagerTest {
@Test
public void testInstallPackageBinderCallFails() throws Exception {
- 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));
+ () -> mApexManager.installPackage(installedApex, /* force= */ false));
}
@Test