From e186f9981afa1723007601ff82e2023c38d7b818 Mon Sep 17 00:00:00 2001 From: Dario Freni Date: Fri, 31 Aug 2018 14:18:04 +0100 Subject: Initial support for installing APEX via adb. Test: adb install package-signed.apex (succeeds) adb install package-unsigned.apex (fails) Merged-In: I3ac7971ce6923511a7d574291fe9002c5d55fa1b Change-Id: I53bd15286b62a2a4ae6c2c5746b2ed039bc6ff77 --- Android.bp | 1 + core/java/android/content/pm/PackageManager.java | 8 ++ .../android/server/pm/PackageInstallerService.java | 5 +- .../android/server/pm/PackageInstallerSession.java | 93 ++++++++++++++++++++-- .../server/pm/PackageManagerShellCommand.java | 8 +- 5 files changed, 105 insertions(+), 10 deletions(-) diff --git a/Android.bp b/Android.bp index fe4fdb2a502e..2b5c513c3bb5 100644 --- a/Android.bp +++ b/Android.bp @@ -675,6 +675,7 @@ java_defaults { ], static_libs: [ + "apex_aidl_interface-java", "framework-protos", "android.hidl.base-V1.0-java", "android.hardware.cas-V1.0-java", diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 2597f1d53937..02c35e601753 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -846,6 +846,14 @@ public abstract class PackageManager { */ public static final int INSTALL_VIRTUAL_PRELOAD = 0x00010000; + /** + * Flag parameter for {@link #installPackage} to indicate that this package + * is an APEX package + * + * @hide + */ + public static final int INSTALL_APEX = 0x00020000; + /** @hide */ @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = { DONT_KILL_APP diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 0b32d1a5d69b..6ccd0406bfcc 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -58,7 +58,6 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SELinux; -import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; @@ -646,8 +645,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } try { - Os.mkdir(stageDir.getAbsolutePath(), 0755); - Os.chmod(stageDir.getAbsolutePath(), 0755); + Os.mkdir(stageDir.getAbsolutePath(), 0775); + Os.chmod(stageDir.getAbsolutePath(), 0775); } catch (ErrnoException e) { // This purposefully throws if directory already exists throw new IOException("Failed to prepare session dir: " + stageDir, e); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 81a836749a7f..17daeed1a5c1 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -42,6 +42,7 @@ import static com.android.server.pm.PackageInstallerService.prepareStageDir; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.apex.IApexService; import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.content.Context; @@ -73,6 +74,7 @@ import android.os.ParcelableException; import android.os.Process; import android.os.RemoteException; import android.os.RevocableFileDescriptor; +import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; @@ -858,12 +860,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { resolveStageDirLocked(); mSealed = true; - - // Verify that stage looks sane with respect to existing application. - // This currently only ensures packageName, versionCode, and certificate - // consistency. try { - validateInstallLocked(pkgInfo); + if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { + validateApexInstallLocked(pkgInfo); + } else { + // Verify that stage looks sane with respect to existing application. + // This currently only ensures packageName, versionCode, and certificate + // consistency. + validateApkInstallLocked(pkgInfo); + } } catch (PackageManagerException e) { throw e; } catch (Throwable e) { @@ -942,6 +947,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Preconditions.checkNotNull(mSigningDetails); Preconditions.checkNotNull(mResolvedBaseFile); + if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { + commitApexLocked(); + } else { + commitApkLocked(); + } + } + + @GuardedBy("mLock") + private void commitApexLocked() throws PackageManagerException { + try { + IApexService apex = IApexService.Stub.asInterface( + ServiceManager.getService("apexservice")); + apex.installPackage(mResolvedBaseFile.toString()); + } catch (Throwable e) { + // Convert all exceptions into package manager exceptions as only those are handled + // in the code above + throw new PackageManagerException(e); + } finally { + destroyInternal(); + dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "APEX installed", null); + } + } + + @GuardedBy("mLock") + private void commitApkLocked() throws PackageManagerException { if (needToAskForPermissionsLocked()) { // User needs to accept permissions; give installer an intent they // can use to involve user. @@ -1063,6 +1093,57 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { (params.installFlags & PackageManager.DONT_KILL_APP) != 0; } + @GuardedBy("mLock") + private void validateApexInstallLocked(@Nullable PackageInfo pkgInfo) + throws PackageManagerException { + mResolvedStagedFiles.clear(); + mResolvedInheritedFiles.clear(); + + try { + resolveStageDirLocked(); + } catch (IOException e) { + throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, + "Failed to resolve stage location", e); + } + + final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter); + if (ArrayUtils.isEmpty(addedFiles)) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); + } + + if (addedFiles.length > 1) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Only one APEX file at a time might be installed"); + } + File addedFile = addedFiles[0]; + final ApkLite apk; + try { + apk = PackageParser.parseApkLite( + addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES); + } catch (PackageParserException e) { + throw PackageManagerException.from(e); + } + + mPackageName = apk.packageName; + mVersionCode = apk.getLongVersionCode(); + mSigningDetails = apk.signingDetails; + mResolvedBaseFile = addedFile; + + assertApkConsistentLocked(String.valueOf(addedFile), apk); + + if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { + try { + // STOPSHIP: For APEX we should also implement proper APK Signature verification. + mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts( + pkgInfo.applicationInfo.sourceDir, + PackageParser.SigningDetails.SignatureSchemeVersion.JAR); + } catch (PackageParserException e) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Couldn't obtain signatures from base APK"); + } + } + } + /** * Validate install by confirming that all application packages are have * consistent package name, version code, and signing certificates. @@ -1076,7 +1157,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * {@link PackageManagerService}. */ @GuardedBy("mLock") - private void validateInstallLocked(@Nullable PackageInfo pkgInfo) + private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo) throws PackageManagerException { mPackageName = null; mVersionCode = -1; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 96bf44ac6d5a..580126f5c330 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -915,7 +915,10 @@ class PackageManagerShellCommand extends ShellCommand { pw.println("Error: must either specify a package size or an APK file"); return 1; } - if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", + final boolean isApex = + (params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0; + String splitName = "base." + (isApex ? "apex" : "apk"); + if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } @@ -2229,6 +2232,9 @@ class PackageManagerShellCommand extends ShellCommand { case "--force-sdk": sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK; break; + case "--apex": + sessionParams.installFlags |= PackageManager.INSTALL_APEX; + break; default: throw new IllegalArgumentException("Unknown option " + opt); } -- cgit v1.2.3-59-g8ed1b From 364aa07576fd43bf3403f85944cb5093ac431d05 Mon Sep 17 00:00:00 2001 From: Dario Freni Date: Wed, 7 Nov 2018 12:17:04 +0000 Subject: Rename APEX installPackage method to stagePackage. Test: adb install apex.test.apex (succeeds) Bug: 112669193 Merged-In: I8dc23d9d621d6a9fd5ee983eb6edbbe5ed13ad9a Change-Id: I8dc23d9d621d6a9fd5ee983eb6edbbe5ed13ad9a --- services/core/java/com/android/server/pm/PackageInstallerSession.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 17daeed1a5c1..57deb3f18969 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -959,7 +959,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { IApexService apex = IApexService.Stub.asInterface( ServiceManager.getService("apexservice")); - apex.installPackage(mResolvedBaseFile.toString()); + apex.stagePackage(mResolvedBaseFile.toString()); } catch (Throwable e) { // Convert all exceptions into package manager exceptions as only those are handled // in the code above -- cgit v1.2.3-59-g8ed1b From e8129a31bbe63c88ad4d817485718f42cf486244 Mon Sep 17 00:00:00 2001 From: Dario Freni Date: Thu, 18 Oct 2018 11:49:50 +0100 Subject: Add MATCH_APEX flag to getInstalledPackages. If set, PackageManager will query apexservice and ask for activated packages. Test: wrote a small app to test the new query. Bug: 117589375 Merged-In: I498bd97896f3eab65c88e9684874a30713be585e Change-Id: I498bd97896f3eab65c88e9684874a30713be585e --- api/current.txt | 2 ++ core/java/android/content/pm/PackageInfo.java | 18 +++++++++++++++++- core/java/android/content/pm/PackageManager.java | 12 ++++++++++++ .../android/server/pm/PackageManagerService.java | 22 +++++++++++++++++++++- 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/api/current.txt b/api/current.txt index b9615e0c2af3..31a5443a4e84 100755 --- a/api/current.txt +++ b/api/current.txt @@ -11012,6 +11012,7 @@ package android.content.pm { field public int[] gids; field public int installLocation; field public android.content.pm.InstrumentationInfo[] instrumentation; + field public boolean isApex; field public long lastUpdateTime; field public java.lang.String packageName; field public android.content.pm.PermissionInfo[] permissions; @@ -11390,6 +11391,7 @@ package android.content.pm { field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0 field public static final int INSTALL_REASON_USER = 4; // 0x4 field public static final int MATCH_ALL = 131072; // 0x20000 + field public static final int MATCH_APEX = 1073741824; // 0x40000000 field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000 field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000 field public static final int MATCH_DIRECT_BOOT_UNAWARE = 262144; // 0x40000 diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index d9d177760243..89e8608eec08 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -18,6 +18,7 @@ package android.content.pm; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; +import android.apex.ApexInfo; import android.os.Parcel; import android.os.Parcelable; @@ -390,6 +391,11 @@ public class PackageInfo implements Parcelable { @Nullable public String compileSdkVersionCodename; + /** + * Whether the package is an APEX package. + */ + public boolean isApex; + public PackageInfo() { } @@ -472,6 +478,7 @@ public class PackageInfo implements Parcelable { } else { dest.writeInt(0); } + dest.writeBoolean(isApex); } public static final Parcelable.Creator CREATOR @@ -533,7 +540,7 @@ public class PackageInfo implements Parcelable { if (hasSigningInfo != 0) { signingInfo = SigningInfo.CREATOR.createFromParcel(source); } - + isApex = source.readBoolean(); // The component lists were flattened with the redundant ApplicationInfo // instances omitted. Distribute the canonical one here as appropriate. if (applicationInfo != null) { @@ -544,6 +551,15 @@ public class PackageInfo implements Parcelable { } } + /** + * @hide + */ + public PackageInfo(ApexInfo apexInfo) { + packageName = apexInfo.packageName; + setLongVersionCode(apexInfo.versionCode); + isApex = true; + } + private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) { if (components != null) { for (ComponentInfo ci : components) { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 02c35e601753..1394c358868c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -145,6 +145,7 @@ public abstract class PackageManager { MATCH_FACTORY_ONLY, MATCH_DEBUG_TRIAGED_MISSING, MATCH_INSTANT, + MATCH_APEX, GET_DISABLED_COMPONENTS, GET_DISABLED_UNTIL_USED_COMPONENTS, GET_UNINSTALLED_PACKAGES, @@ -530,6 +531,17 @@ public abstract class PackageManager { */ public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000; + /** + * {@link PackageInfo} flag: include APEX packages that are currently + * installed. In APEX terminology, this corresponds to packages that are + * currently active, i.e. mounted and available to other processes of the OS. + * In particular, this flag alone will not match APEX files that are staged + * for activation at next reboot. + * TODO(b/119767311): include uninstalled/inactive APEX if + * MATCH_UNINSTALLED_PACKAGES is set. + */ + public static final int MATCH_APEX = 0x40000000; + /** * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when * resolving an intent that matches the {@code CrossProfileIntentFilter}, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 47723daf95b0..52b6ffcd3d19 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -65,6 +65,7 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.content.pm.PackageManager.MATCH_APEX; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; @@ -123,6 +124,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.apex.ApexInfo; +import android.apex.IApexService; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; @@ -7912,6 +7915,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; + final boolean listApex = (flags & MATCH_APEX) != 0; + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); @@ -7950,7 +7955,22 @@ public class PackageManagerService extends IPackageManager.Stub } } } - + if (listApex) { + final IApexService apex = IApexService.Stub.asInterface( + ServiceManager.getService("apexservice")); + if (apex != null) { + try { + final ApexInfo[] activePkgs = apex.getActivePackages(); + for (ApexInfo apexInfo : activePkgs) { + list.add(new PackageInfo(apexInfo)); + } + } catch (RemoteException e) { + Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString()); + } + } else { + Log.e(TAG, "Unable to connect to apexservice for querying packages."); + } + } return new ParceledListSlice<>(list); } } -- cgit v1.2.3-59-g8ed1b