From 0bfcc6562b8623e41031168f271bacc4abbb2373 Mon Sep 17 00:00:00 2001 From: JW Wang Date: Tue, 10 Jan 2023 14:48:47 +0800 Subject: [PATCH] Add a one-off shell command to disable verification The package verification is provided by Play whose behavior is not controlled by framework. For CTS, such verification is unnecessary because we have dedicated GTS for that. We add a shell command to disable verification when installing packages in CTS to avoid unexpected behavior caused by external entities to improve stableness of tests. There is no reduction in safety: 1. This feature is only available to apps with shell permissions. Such apps can already skip verification by changing a settings value. 2. The command works in a one-off manner. This command will only allow the next created session to bypass verification. This command requires a uid which prevents accidentally granting the ability to other apps. Bug: 264963480 Test: atest CtsStagedInstallHostTestCases Change-Id: I26e60837139007e98e99f44aeb0eb13716d91523 --- .../android/content/pm/IPackageInstaller.aidl | 2 ++ .../server/pm/PackageInstallerService.java | 20 ++++++++++++++- .../server/pm/PackageManagerShellCommand.java | 25 +++++++++++++++++++ .../android/server/pm/VerifyingSession.java | 7 ++++-- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index 115d4b0cb2705..e3016a4253709 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -65,6 +65,8 @@ interface IPackageInstaller { void bypassNextAllowedApexUpdateCheck(boolean value); + void disableVerificationForUid(int uid); + void setAllowUnlimitedSilentUpdates(String installerPackageName); void setSilentUpdatesThrottleTime(long throttleTimeInSeconds); void checkInstallConstraints(String installerPackageName, in List packageNames, diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 239853c857ccf..617561a58351b 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -184,6 +184,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private volatile boolean mOkToSendBroadcasts = false; private volatile boolean mBypassNextStagedInstallerCheck = false; private volatile boolean mBypassNextAllowedApexUpdateCheck = false; + private volatile int mDisableVerificationForUid = INVALID_UID; /** * File storing persisted {@link #mSessions} metadata. @@ -712,7 +713,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements params.installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE; } - if ((params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) { + if (mDisableVerificationForUid != INVALID_UID) { + if (callingUid == mDisableVerificationForUid) { + params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; + } else { + // Clear the flag if current calling uid doesn't match the requested uid. + params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION; + } + // Reset the field as this is a one-off request. + mDisableVerificationForUid = INVALID_UID; + } else if ((params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) { // Only tools under specific conditions (test app installed through ADB, and // verification disabled flag specified) can disable verification. params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION; @@ -1367,6 +1377,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mBypassNextAllowedApexUpdateCheck = value; } + @Override + public void disableVerificationForUid(int uid) { + if (!isCalledBySystemOrShell(Binder.getCallingUid())) { + throw new SecurityException("Operation not allowed for caller"); + } + mDisableVerificationForUid = uid; + } + /** * Set an installer to allow for the unlimited silent updates. */ diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 849cbeba0300f..56fb7d0d1e0cd 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -342,6 +342,8 @@ class PackageManagerShellCommand extends ShellCommand { return runBypassStagedInstallerCheck(); case "bypass-allowed-apex-update-check": return runBypassAllowedApexUpdateCheck(); + case "disable-verification-for-uid": + return runDisableVerificationForUid(); case "set-silent-updates-policy": return runSetSilentUpdatesPolicy(); default: { @@ -514,6 +516,29 @@ class PackageManagerShellCommand extends ShellCommand { } } + private int runDisableVerificationForUid() { + final PrintWriter pw = getOutPrintWriter(); + try { + int uid = Integer.parseInt(getNextArgRequired()); + var amInternal = LocalServices.getService(ActivityManagerInternal.class); + boolean isInstrumented = + amInternal.getInstrumentationSourceUid(uid) != Process.INVALID_UID; + if (isInstrumented) { + mInterface.getPackageInstaller().disableVerificationForUid(uid); + return 0; + } else { + // Only available for testing + pw.println("Error: must specify an instrumented uid"); + return -1; + } + } catch (RemoteException e) { + pw.println("Failure [" + + e.getClass().getName() + " - " + + e.getMessage() + "]"); + return -1; + } + } + private int uninstallSystemUpdates(String packageName) { final PrintWriter pw = getOutPrintWriter(); boolean failedUninstalls = false; diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java index a54f52619f357..b2fd46c172d5c 100644 --- a/services/core/java/com/android/server/pm/VerifyingSession.java +++ b/services/core/java/com/android/server/pm/VerifyingSession.java @@ -680,12 +680,15 @@ final class VerifyingSession { } final int installerUid = mVerificationInfo == null ? -1 : mVerificationInfo.mInstallerUid; + final boolean requestedDisableVerification = + (mInstallFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0; // Check if installing from ADB if ((mInstallFlags & PackageManager.INSTALL_FROM_ADB) != 0) { - boolean requestedDisableVerification = - (mInstallFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0; return isAdbVerificationEnabled(pkgInfoLite, userId, requestedDisableVerification); + } else if (requestedDisableVerification) { + // Skip verification for non-adb installs + return false; } // only when not installed from ADB, skip verification for instant apps when