Merge "Update owner under multi-user scenarios" into udc-dev

This commit is contained in:
Jackal Guo
2023-02-20 01:07:59 +00:00
committed by Android (Google) Code Review
8 changed files with 116 additions and 58 deletions

View File

@@ -581,7 +581,7 @@ public class PackageInstaller {
/**
* 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 @@ public class PackageInstaller {
* 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}

View File

@@ -1607,6 +1607,13 @@ public abstract class PackageManager {
*/
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,

View File

@@ -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] -->

View File

@@ -144,18 +144,11 @@ public class PackageInstallerActivity extends AlertActivity {
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);

View File

@@ -65,6 +65,7 @@ import android.annotation.NonNull;
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 @@ public class ComputerEngine implements Computer {
&& 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 @@ public class ComputerEngine implements Computer {
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;
}
}

View File

@@ -102,6 +102,7 @@ import android.app.AppOpsManager;
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.provider.DeviceConfig;
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 @@ final class InstallPackageHelper {
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 @@ final class InstallPackageHelper {
}
// 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 @@ final class InstallPackageHelper {
}
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 =

View File

@@ -926,6 +926,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
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;

View File

@@ -868,15 +868,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
: 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
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;