diff options
| author | 2018-08-31 14:18:04 +0100 | |
|---|---|---|
| committer | 2018-11-05 09:24:58 +0000 | |
| commit | d8bf22e8c6acd0b4b865c471c99d3ba4cb3d23a1 (patch) | |
| tree | 8ebb98f2e60e68e7a497111edb3c36770031aca6 | |
| parent | d0e5bc8db01b6501cdb4c8c602f3b9419834a8f0 (diff) | |
Initial support for installing APEX via adb.
Test: adb install package-signed.apex (succeeds)
adb install package-unsigned.apex (fails)
Change-Id: I3ac7971ce6923511a7d574291fe9002c5d55fa1b
5 files changed, 105 insertions, 10 deletions
diff --git a/Android.bp b/Android.bp index b715b7336d23..ee159f78c32e 100644 --- a/Android.bp +++ b/Android.bp @@ -692,6 +692,7 @@ java_defaults { ], static_libs: [ + "apex_aidl_interface-java", "framework-protos", "mediaplayer2-protos", "android.hidl.base-V1.0-java", diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 1f700f758dc8..67b86c0e6e42 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -855,6 +855,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 51225a77b005..6e450137185b 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -43,6 +43,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; @@ -75,6 +76,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; @@ -838,12 +840,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) { @@ -926,6 +931,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 confirm installation; give installer an intent they can use to involve // user. @@ -1047,6 +1077,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. @@ -1060,7 +1141,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 { ApkLite baseApk = null; mPackageName = null; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index e25cca43e8da..38bd1722e61f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -920,7 +920,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; } @@ -2262,6 +2265,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); } |