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

Commit aafaaec0 authored by Nikita Ioffe's avatar Nikita Ioffe
Browse files

Add sys config to handle apexes that are allowed to be updated

Unlike staged installer check, we can't check if given APEX package is
allowed to be updated at session creation time, since we don't have
knowledge of the package being installed yet. Instead, the check is
implemented in PackageInstallerSession#handleInstall.

Like staged install check, allowed apex update check has similar
exemptions (adb is allowed to update any APEX,
`adb shell pm --bypass-allowed-apex-update-check` makes next install
session bypass the check).

In order to implement these exemptions, a new
INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK flag that can only be set by
system is added. PackageInstallerSession will skip the APEX update
checks if INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK is set.

Bug: 189274479
Test: atest CtsStagedInstallHostTestCases
Test: atest GtsStagedInstallHostTestCases
Test: atest FrameworksServicesTests:SystemConfigTest
Change-Id: I22921a3ac4d43011b565733d7a7183e5cdb4fe80
parent 4cff586b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ interface IPackageInstaller {

    void bypassNextStagedInstallerCheck(boolean value);

    void bypassNextAllowedApexUpdateCheck(boolean value);

    void setAllowUnlimitedSilentUpdates(String installerPackageName);
    void setSilentUpdatesThrottleTime(long throttleTimeInSeconds);
}
+7 −0
Original line number Diff line number Diff line
@@ -1278,6 +1278,13 @@ public abstract class PackageManager {
     */
    public static final int INSTALL_STAGED = 0x00200000;

    /**
     * Flag parameter for {@link #installPackage} to indicate that check whether given APEX can be
     * updated should be disabled for this install.
     * @hide
     */
    public static final int INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK = 0x00400000;

    /** @hide */
    @IntDef(flag = true, value = {
            DONT_KILL_APP,
+20 −0
Original line number Diff line number Diff line
@@ -240,6 +240,7 @@ public class SystemConfig {

    private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
    private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
    private final ArraySet<String> mAllowedPartnerApexes = new ArraySet<>();

    /**
     * Map of system pre-defined, uniquely named actors; keys are namespace,
@@ -410,6 +411,10 @@ public class SystemConfig {
        return mWhitelistedStagedInstallers;
    }

    public Set<String> getAllowedPartnerApexes() {
        return mAllowedPartnerApexes;
    }

    public ArraySet<String> getAppDataIsolationWhitelistedApps() {
        return mAppDataIsolationWhitelistedApps;
    }
@@ -1212,6 +1217,21 @@ public class SystemConfig {
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "allowed-partner-apex": {
                        // TODO(b/189274479): should this be allowOemPermissions instead?
                        if (allowAppConfigs) {
                            String pkgName = parser.getAttributeValue(null, "package");
                            if (pkgName == null) {
                                Slog.w(TAG, "<" + name + "> without package in " + permFile
                                        + " at " + parser.getPositionDescription());
                            } else {
                                mAllowedPartnerApexes.add(pkgName);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    default: {
                        Slog.w(TAG, "Tag " + name + " is unknown in "
                                + permFile + " at " + parser.getPositionDescription());
+18 −0
Original line number Diff line number Diff line
@@ -157,6 +157,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements

    private volatile boolean mOkToSendBroadcasts = false;
    private volatile boolean mBypassNextStagedInstallerCheck = false;
    private volatile boolean mBypassNextAllowedApexUpdateCheck = false;

    /**
     * File storing persisted {@link #mSessions} metadata.
@@ -651,6 +652,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                throw new IllegalArgumentException(
                    "Non-staged APEX session doesn't support INSTALL_ENABLE_ROLLBACK");
            }
            if (isCalledBySystemOrShell(callingUid) || mBypassNextAllowedApexUpdateCheck) {
                params.installFlags |= PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
            } else {
                // Only specific APEX updates (installed through ADB, or for CTS tests) can disable
                // allowed APEX update check.
                params.installFlags &= ~PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
            }
        }

        if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
@@ -675,6 +683,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
        }

        mBypassNextStagedInstallerCheck = false;
        mBypassNextAllowedApexUpdateCheck = false;

        if (!params.isMultiPackage) {
            // Only system components can circumvent runtime permissions when installing.
            if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
@@ -1107,6 +1117,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
        mBypassNextStagedInstallerCheck = value;
    }

    @Override
    public void bypassNextAllowedApexUpdateCheck(boolean value) {
        if (!isCalledBySystemOrShell(Binder.getCallingUid())) {
            throw new SecurityException("Caller not allowed to bypass allowed apex update check");
        }
        mBypassNextAllowedApexUpdateCheck = value;
    }

    /**
     * Set an installer to allow for the unlimited silent updates.
     */
+26 −0
Original line number Diff line number Diff line
@@ -149,6 +149,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -2319,6 +2320,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            return;
        }


        // Check if APEX update is allowed. We do this check in handleInstall, since this is one of
        // the places that:
        //   * Shared between staged and non-staged APEX update flows.
        //   * Only is called after boot completes.
        // The later is important, since isApexUpdateAllowed check depends on the
        // ModuleInfoProvider, which is only populated after device has booted.
        if (isApexSession()) {
            boolean checkApexUpdateAllowed =
                    (params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK)
                        == 0;
            synchronized (mLock) {
                if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName)) {
                    onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                            "Update of APEX package " + mPackageName + " is not allowed");
                    return;
                }
            }
        }

        if (params.isStaged) {
            // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
            //  though ideally, we just need to send session committed broadcast.
@@ -2803,6 +2824,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        return sessionContains((s) -> !s.isApexSession());
    }

    private boolean isApexUpdateAllowed(String apexPackageName) {
        return mPm.getModuleInfo(apexPackageName, 0) != null
                || SystemConfig.getInstance().getAllowedPartnerApexes().contains(apexPackageName);
    }

    /**
     * Validate apex install.
     * <p>
Loading