Merge "Update owner under multi-user scenarios" into udc-dev
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 2b4ea70..7766896 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -581,7 +581,7 @@
/**
* Indicate the user intervention is required because the update ownership enforcement is
- * enabled, and remind the update owner will retain.
+ * enabled, and remind the update owner is a different package.
*
* @see PackageInstaller.SessionParams#setRequestUpdateOwnership
* @see InstallSourceInfo#getUpdateOwnerPackageName
@@ -2978,8 +2978,7 @@
* permission. Default to {@code false}.
*
* The update ownership enforcement can only be enabled on initial installation. Set
- * this to {@code true} on package update indicates the installer package wants to be
- * the update owner if the update ownership enforcement has enabled.
+ * this to {@code true} on package update is a no-op.
*
* Note: To enable the update ownership enforcement, the installer must have the
* {@link android.Manifest.permission#ENFORCE_UPDATE_OWNERSHIP ENFORCE_UPDATE_OWNERSHIP}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 23cce6a..db05b95 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1607,6 +1607,13 @@
*/
public static final int INSTALL_REQUEST_UPDATE_OWNERSHIP = 1 << 25;
+ /**
+ * Flag parameter for {@link PackageInstaller.SessionParams} to indicate that this
+ * session is from a managed user or profile.
+ * @hide
+ */
+ public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
+
/** @hide */
@IntDef(flag = true, value = {
DONT_KILL_APP,
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index ab680b0..6669e35 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -38,9 +38,6 @@
<!-- Message for updating an existing app [CHAR LIMIT=NONE] -->
<string name="install_confirm_question_update">Do you want to update this app?</string>
<!-- TODO(b/244413073) Revise the description after getting UX input and UXR on this. -->
- <!-- Message for updating an existing app when updating owner changed [DO NOT TRANSLATE][CHAR LIMIT=NONE] -->
- <string name="install_confirm_question_update_owner_changed">Updates to this app are currently managed by <xliff:g id="existing_update_owner">%1$s</xliff:g>.\n\nBy updating, you\'ll get future updates from <xliff:g id="new_update_owner">%2$s</xliff:g> instead.</string>
- <!-- TODO(b/244413073) Revise the description after getting UX input and UXR on this. -->
<!-- Message for updating an existing app with update owner reminder [DO NOT TRANSLATE][CHAR LIMIT=NONE] -->
<string name="install_confirm_question_update_owner_reminder">Updates to this app are currently managed by <xliff:g id="existing_update_owner">%1$s</xliff:g>.\n\nDo you want to install this update from <xliff:g id="new_update_owner">%2$s</xliff:g>?</string>
<!-- [CHAR LIMIT=100] -->
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index fd41f5f..d41cfbc2 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -144,18 +144,11 @@
final CharSequence existingUpdateOwnerLabel = getExistingUpdateOwnerLabel();
final CharSequence requestedUpdateOwnerLabel = getApplicationLabel(mCallingPackage);
- if (!TextUtils.isEmpty(existingUpdateOwnerLabel)) {
- if (mPendingUserActionReason == PackageInstaller.REASON_OWNERSHIP_CHANGED) {
- viewToEnable.setText(
- getString(R.string.install_confirm_question_update_owner_changed,
- existingUpdateOwnerLabel, requestedUpdateOwnerLabel));
- } else if (mPendingUserActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP
- || mPendingUserActionReason
- == PackageInstaller.REASON_CONFIRM_PACKAGE_CHANGE) {
- viewToEnable.setText(
- getString(R.string.install_confirm_question_update_owner_reminder,
- existingUpdateOwnerLabel, requestedUpdateOwnerLabel));
- }
+ if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
+ && mPendingUserActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP) {
+ viewToEnable.setText(
+ getString(R.string.install_confirm_question_update_owner_reminder,
+ existingUpdateOwnerLabel, requestedUpdateOwnerLabel));
}
mOk.setText(R.string.update);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 58a609e..5984360 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -65,6 +65,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -2190,6 +2191,11 @@
&& UserHandle.getAppId(uid) == pkg.getUid();
}
+ private boolean isCallerFromManagedUserOrProfile(@UserIdInt int userId) {
+ final var dpmi = mInjector.getLocalService(DevicePolicyManagerInternal.class);
+ return dpmi != null && dpmi.isUserOrganizationManaged(userId);
+ }
+
public final boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
return true;
@@ -5002,8 +5008,15 @@
updateOwnerPackageName = installSource.mUpdateOwnerPackageName;
if (updateOwnerPackageName != null) {
final PackageStateInternal ps = mSettings.getPackage(updateOwnerPackageName);
+ final boolean isCallerSystemOrUpdateOwner = callingUid == Process.SYSTEM_UID
+ || isCallerSameApp(updateOwnerPackageName, callingUid);
+ // Except for package visibility filtering, we also hide update owner if the installer
+ // is in the managed user or profile. As we don't enforce the update ownership for the
+ // managed user and profile, knowing there's an update owner is meaningless in that
+ // user unless the installer is the owner.
if (ps == null
- || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
+ || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)
+ || (!isCallerSystemOrUpdateOwner && isCallerFromManagedUserOrProfile(userId))) {
updateOwnerPackageName = null;
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 4b2be2f..a868470 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -102,6 +102,7 @@
import android.app.ApplicationExitInfo;
import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.IBackupManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -144,6 +145,7 @@
import android.stats.storage.StorageEnums;
import android.system.ErrnoException;
import android.system.Os;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
@@ -317,10 +319,16 @@
InstallSource installSource = request.getInstallSource();
final boolean isApex = (scanFlags & SCAN_AS_APEX) != 0;
+ final boolean pkgAlreadyExists = oldPkgSetting != null;
+ final boolean isAllowUpdateOwnership = parsedPackage.isAllowUpdateOwnership();
+ final String oldUpdateOwner =
+ pkgAlreadyExists ? oldPkgSetting.getInstallSource().mUpdateOwnerPackageName : null;
final String updateOwnerFromSysconfig = isApex || !pkgSetting.isSystem() ? null
: mPm.mInjector.getSystemConfig().getSystemAppUpdateOwnerPackageName(
parsedPackage.getPackageName());
- // For new install (standard install), the installSource isn't null.
+ final boolean isUpdateOwnershipEnabled = oldUpdateOwner != null;
+
+ // For standard install (install via session), the installSource isn't null.
if (installSource != null) {
// If this is part of a standard install, set the initiating package name, else rely on
// previous device state.
@@ -334,39 +342,68 @@
}
// Handle the update ownership enforcement for APK
- if (updateOwnerFromSysconfig != null) {
- // For system app, we always use the update owner from sysconfig if presented.
- installSource = installSource.setUpdateOwnerPackageName(updateOwnerFromSysconfig);
- } else if (!parsedPackage.isAllowUpdateOwnership()) {
+ if (!isAllowUpdateOwnership) {
// If the app wants to opt-out of the update ownership enforcement via manifest,
// it overrides the installer's use of #setRequestUpdateOwnership.
installSource = installSource.setUpdateOwnerPackageName(null);
} else if (!isApex) {
- final boolean isUpdate = oldPkgSetting != null;
- final String oldUpdateOwner =
- isUpdate ? oldPkgSetting.getInstallSource().mUpdateOwnerPackageName : null;
- final boolean isUpdateOwnershipEnabled = oldUpdateOwner != null;
+ // User installer UID as "current" userId if present; otherwise, use the userId
+ // from InstallRequest.
+ final int userId = installSource.mInstallerPackageUid != Process.INVALID_UID
+ ? UserHandle.getUserId(installSource.mInstallerPackageUid)
+ : request.getUserId();
+ // Whether the parsedPackage is installed on the userId
+ // If the oldPkgSetting doesn't exist, this package isn't installed for all users.
+ final boolean isUpdate = pkgAlreadyExists && (userId >= UserHandle.USER_SYSTEM
+ // If userID >= 0, we could check via oldPkgSetting.getInstalled(userId).
+ ? oldPkgSetting.getInstalled(userId)
+ // When userId is -1 (USER_ALL) and it's not installed for any user,
+ // treat it as not installed.
+ : oldPkgSetting.getNotInstalledUserIds().length
+ <= (UserManager.isHeadlessSystemUserMode() ? 1 : 0));
final boolean isRequestUpdateOwnership = (request.getInstallFlags()
& PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP) != 0;
+ final boolean isSameUpdateOwner =
+ TextUtils.equals(oldUpdateOwner, installSource.mInstallerPackageName);
- // Here we assign the update owner for the package, and the rules are:
- // -. If the installer doesn't request update ownership on initial installation,
- // keep the update owner as null.
- // -. If the installer doesn't want to be the owner to provide the subsequent
- // update (doesn't request to be the update owner), e.g., non-store installer
- // (file manager), ADB, or DO/PO, we should not update the owner.
- // -. Else, the installer requests update ownership on initial installation or
- // update, we use installSource.mUpdateOwnerPackageName as the update owner.
- if (!isRequestUpdateOwnership || (isUpdate && !isUpdateOwnershipEnabled)) {
- installSource = installSource.setUpdateOwnerPackageName(oldUpdateOwner);
+ // Here we handle the update owner for the package, and the rules are:
+ // -. Only enabling update ownership enforcement on initial installation if the
+ // installer has requested.
+ // -. Once the installer changes and users agree to proceed, clear the update
+ // owner (package state in other users are taken into account as well).
+ if (!isUpdate) {
+ if (!isRequestUpdateOwnership) {
+ installSource = installSource.setUpdateOwnerPackageName(null);
+ } else if ((!isUpdateOwnershipEnabled && pkgAlreadyExists)
+ || (isUpdateOwnershipEnabled && !isSameUpdateOwner)) {
+ installSource = installSource.setUpdateOwnerPackageName(null);
+ }
+ } else if (!isSameUpdateOwner || !isUpdateOwnershipEnabled) {
+ installSource = installSource.setUpdateOwnerPackageName(null);
}
}
pkgSetting.setInstallSource(installSource);
- // non-standard install (addForInit and install existing packages), installSource is null.
- } else if (updateOwnerFromSysconfig != null) {
- // For system app, we always use the update owner from sysconfig if presented.
- pkgSetting.setUpdateOwnerPackage(updateOwnerFromSysconfig);
+ // For non-standard install (addForInit), installSource is null.
+ } else if (pkgSetting.isSystem()) {
+ // We still honor the manifest attr if the system app wants to opt-out of it.
+ if (!isAllowUpdateOwnership) {
+ pkgSetting.setUpdateOwnerPackage(null);
+ } else {
+ final boolean isSameUpdateOwner = isUpdateOwnershipEnabled
+ && TextUtils.equals(oldUpdateOwner, updateOwnerFromSysconfig);
+
+ // Here we handle the update owner for the system package, and the rules are:
+ // -. We use the update owner from sysconfig as the initial value.
+ // -. Once an app becomes to system app later via OTA, only retains the update
+ // owner if it's consistence with sysconfig.
+ // -. Clear the update owner when update owner changes from sysconfig.
+ if (!pkgAlreadyExists || isSameUpdateOwner) {
+ pkgSetting.setUpdateOwnerPackage(updateOwnerFromSysconfig);
+ } else {
+ pkgSetting.setUpdateOwnerPackage(null);
+ }
+ }
}
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
@@ -641,6 +678,18 @@
}
if (installed) {
+ final String updateOwner = pkgSetting.getInstallSource().mUpdateOwnerPackageName;
+ final var dpmi = mInjector.getLocalService(DevicePolicyManagerInternal.class);
+ final boolean isFromManagedUserOrProfile =
+ dpmi != null && dpmi.isUserOrganizationManaged(userId);
+ // Here we handle the update owner when install existing package, and the rules are:
+ // -. Retain the update owner when enable a system app in managed user or profile.
+ // -. Retain the update owner if the installer is the same.
+ // -. Clear the update owner when update owner changes.
+ if (!preLockSnapshot.isCallerSameApp(updateOwner, callingUid)
+ && (!pkgSetting.isSystem() || !isFromManagedUserOrProfile)) {
+ pkgSetting.setUpdateOwnerPackage(null);
+ }
if (pkgSetting.getPkg() != null) {
final PermissionManagerServiceInternal.PackageInstalledParams.Builder
permissionParamsBuilder =
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 77ffa16..adc0b0b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -926,6 +926,11 @@
requestedInstallerPackageName = null;
}
+ final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
+ if (dpmi != null && dpmi.isUserOrganizationManaged(userId)) {
+ params.installFlags |= PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE;
+ }
+
if (isApex || mContext.checkCallingOrSelfPermission(
Manifest.permission.ENFORCE_UPDATE_OWNERSHIP) == PackageManager.PERMISSION_DENIED) {
params.installFlags &= ~PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index fb6ae8b..fa535c3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -868,15 +868,13 @@
private static final int USER_ACTION_NOT_NEEDED = 0;
private static final int USER_ACTION_REQUIRED = 1;
private static final int USER_ACTION_PENDING_APK_PARSING = 2;
- private static final int USER_ACTION_REQUIRED_UPDATE_OWNER_CHANGED = 3;
- private static final int USER_ACTION_REQUIRED_UPDATE_OWNER_RETAINED = 4;
+ private static final int USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER = 3;
@IntDef({
USER_ACTION_NOT_NEEDED,
USER_ACTION_REQUIRED,
USER_ACTION_PENDING_APK_PARSING,
- USER_ACTION_REQUIRED_UPDATE_OWNER_CHANGED,
- USER_ACTION_REQUIRED_UPDATE_OWNER_RETAINED
+ USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER,
})
@interface UserActionRequirement {}
@@ -937,7 +935,7 @@
: null;
final boolean isInstallerOfRecord = isUpdate
&& Objects.equals(existingInstallerPackageName, getInstallerPackageName());
- final boolean isUpdateOwner = Objects.equals(existingUpdateOwnerPackageName,
+ final boolean isUpdateOwner = TextUtils.equals(existingUpdateOwnerPackageName,
getInstallerPackageName());
final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
final boolean isPermissionGranted = isInstallPermissionGranted
@@ -947,6 +945,8 @@
final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
+ final boolean isFromManagedUserOrProfile =
+ (params.installFlags & PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE) != 0;
final boolean isUpdateOwnershipEnforcementEnabled =
mPm.isUpdateOwnershipEnforcementAvailable()
&& existingUpdateOwnerPackageName != null;
@@ -963,12 +963,10 @@
if (isUpdateOwnershipEnforcementEnabled
&& !isApexSession()
&& !isUpdateOwner
- && !isInstallerShell) {
- final boolean isRequestUpdateOwner =
- (params.installFlags & PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP) != 0;
-
- return isRequestUpdateOwner ? USER_ACTION_REQUIRED_UPDATE_OWNER_CHANGED
- : USER_ACTION_REQUIRED_UPDATE_OWNER_RETAINED;
+ && !isInstallerShell
+ // We don't enforce the update ownership for the managed user and profile.
+ && !isFromManagedUserOrProfile) {
+ return USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER;
}
if (isPermissionGranted) {
@@ -2361,8 +2359,7 @@
userActionRequirement = session.computeUserActionRequirement();
session.updateUserActionRequirement(userActionRequirement);
if (userActionRequirement == USER_ACTION_REQUIRED
- || userActionRequirement == USER_ACTION_REQUIRED_UPDATE_OWNER_CHANGED
- || userActionRequirement == USER_ACTION_REQUIRED_UPDATE_OWNER_RETAINED) {
+ || userActionRequirement == USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER) {
session.sendPendingUserActionIntent(target);
return true;
}
@@ -2425,9 +2422,7 @@
private static @UserActionReason int userActionRequirementToReason(
@UserActionRequirement int requirement) {
switch (requirement) {
- case USER_ACTION_REQUIRED_UPDATE_OWNER_CHANGED:
- return PackageInstaller.REASON_OWNERSHIP_CHANGED;
- case USER_ACTION_REQUIRED_UPDATE_OWNER_RETAINED:
+ case USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER:
return PackageInstaller.REASON_REMIND_OWNERSHIP;
default:
return PackageInstaller.REASON_CONFIRM_PACKAGE_CHANGE;