diff options
-rw-r--r-- | api/current.txt | 3 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageInfo.java | 31 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageInfoLite.java | 16 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 50 | ||||
-rw-r--r-- | core/java/android/net/VpnService.java | 6 | ||||
-rw-r--r-- | core/res/res/values/attrs_manifest.xml | 14 | ||||
-rw-r--r-- | core/res/res/values/public.xml | 1 | ||||
-rw-r--r-- | packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java | 3 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageInstallerSession.java | 39 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 48 |
10 files changed, 175 insertions, 36 deletions
diff --git a/api/current.txt b/api/current.txt index 490b309..7508a8d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1017,6 +1017,7 @@ package android { field public static final int restrictionType = 16843923; // 0x1010493 field public static final int resumeWhilePausing = 16843954; // 0x10104b2 field public static final int reversible = 16843851; // 0x101044b + field public static final int revisionCode = 16843989; // 0x10104d5 field public static final int right = 16843183; // 0x10101af field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093 field public static final int ringtoneType = 16843257; // 0x10101f9 @@ -8545,6 +8546,7 @@ package android.content.pm { field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1 field public android.content.pm.ActivityInfo[] activities; field public android.content.pm.ApplicationInfo applicationInfo; + field public int baseRevisionCode; field public android.content.pm.ConfigurationInfo[] configPreferences; field public android.content.pm.FeatureGroupInfo[] featureGroups; field public long firstInstallTime; @@ -8564,6 +8566,7 @@ package android.content.pm { field public int sharedUserLabel; field public android.content.pm.Signature[] signatures; field public java.lang.String[] splitNames; + field public int[] splitRevisionCodes; field public int versionCode; field public java.lang.String versionName; } diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index b66bd01..9223269 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -41,14 +41,30 @@ public class PackageInfo implements Parcelable { * attribute. */ public int versionCode; - + /** * The version name of this package, as specified by the <manifest> * tag's {@link android.R.styleable#AndroidManifest_versionName versionName} * attribute. */ public String versionName; - + + /** + * The revision number of the base APK for this package, as specified by the + * <manifest> tag's + * {@link android.R.styleable#AndroidManifest_revisionCode revisionCode} + * attribute. + */ + public int baseRevisionCode; + + /** + * The revision number of any split APKs for this package, as specified by + * the <manifest> tag's + * {@link android.R.styleable#AndroidManifest_revisionCode revisionCode} + * attribute. Indexes are a 1:1 mapping against {@link #splitNames}. + */ + public int[] splitRevisionCodes; + /** * The shared user ID name of this package, as specified by the <manifest> * tag's {@link android.R.styleable#AndroidManifest_sharedUserId sharedUserId} @@ -257,21 +273,26 @@ public class PackageInfo implements Parcelable { public PackageInfo() { } + @Override public String toString() { return "PackageInfo{" + Integer.toHexString(System.identityHashCode(this)) + " " + packageName + "}"; } + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeString(packageName); dest.writeStringArray(splitNames); dest.writeInt(versionCode); dest.writeString(versionName); + dest.writeInt(baseRevisionCode); + dest.writeIntArray(splitRevisionCodes); dest.writeString(sharedUserId); dest.writeInt(sharedUserLabel); if (applicationInfo != null) { @@ -305,10 +326,12 @@ public class PackageInfo implements Parcelable { public static final Parcelable.Creator<PackageInfo> CREATOR = new Parcelable.Creator<PackageInfo>() { + @Override public PackageInfo createFromParcel(Parcel source) { return new PackageInfo(source); } + @Override public PackageInfo[] newArray(int size) { return new PackageInfo[size]; } @@ -316,9 +339,11 @@ public class PackageInfo implements Parcelable { private PackageInfo(Parcel source) { packageName = source.readString(); - splitNames = source.readStringArray(); + splitNames = source.createStringArray(); versionCode = source.readInt(); versionName = source.readString(); + baseRevisionCode = source.readInt(); + splitRevisionCodes = source.createIntArray(); sharedUserId = source.readString(); sharedUserLabel = source.readInt(); int hasApp = source.readInt(); diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java index e336c5f..1efe082 100644 --- a/core/java/android/content/pm/PackageInfoLite.java +++ b/core/java/android/content/pm/PackageInfoLite.java @@ -19,6 +19,8 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.content.PackageHelper; + /** * Basic information about a package as specified in its manifest. * Utility class used in PackageManager methods @@ -31,11 +33,19 @@ public class PackageInfoLite implements Parcelable { */ public String packageName; + /** Names of any split APKs, ordered by parsed splitName */ + public String[] splitNames; + /** * The android:versionCode of the package. */ public int versionCode; + /** Revision code of base APK */ + public int baseRevisionCode; + /** Revision codes of any split APKs, ordered by parsed splitName */ + public int[] splitRevisionCodes; + /** * The android:multiArch flag from the package manifest. If set, * we will extract all native libraries for the given app, not just those @@ -70,7 +80,10 @@ public class PackageInfoLite implements Parcelable { public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeString(packageName); + dest.writeStringArray(splitNames); dest.writeInt(versionCode); + dest.writeInt(baseRevisionCode); + dest.writeIntArray(splitRevisionCodes); dest.writeInt(recommendedInstallLocation); dest.writeInt(installLocation); dest.writeInt(multiArch ? 1 : 0); @@ -96,7 +109,10 @@ public class PackageInfoLite implements Parcelable { private PackageInfoLite(Parcel source) { packageName = source.readString(); + splitNames = source.createStringArray(); versionCode = source.readInt(); + baseRevisionCode = source.readInt(); + splitRevisionCodes = source.createIntArray(); recommendedInstallLocation = source.readInt(); installLocation = source.readInt(); multiArch = (source.readInt() != 0); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 8515520..82da7c5 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -261,11 +261,16 @@ public class PackageParser { /** Paths of any split APKs, ordered by parsed splitName */ public final String[] splitCodePaths; + /** Revision code of base APK */ + public final int baseRevisionCode; + /** Revision codes of any split APKs, ordered by parsed splitName */ + public final int[] splitRevisionCodes; + public final boolean coreApp; public final boolean multiArch; public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, - String[] splitCodePaths) { + String[] splitCodePaths, int[] splitRevisionCodes) { this.packageName = baseApk.packageName; this.versionCode = baseApk.versionCode; this.installLocation = baseApk.installLocation; @@ -274,6 +279,8 @@ public class PackageParser { this.codePath = codePath; this.baseCodePath = baseApk.codePath; this.splitCodePaths = splitCodePaths; + this.baseRevisionCode = baseApk.revisionCode; + this.splitRevisionCodes = splitRevisionCodes; this.coreApp = baseApk.coreApp; this.multiArch = baseApk.multiArch; } @@ -296,6 +303,7 @@ public class PackageParser { public final String packageName; public final String splitName; public final int versionCode; + public final int revisionCode; public final int installLocation; public final VerifierInfo[] verifiers; public final Signature[] signatures; @@ -303,12 +311,13 @@ public class PackageParser { public final boolean multiArch; public ApkLite(String codePath, String packageName, String splitName, int versionCode, - int installLocation, List<VerifierInfo> verifiers, Signature[] signatures, - boolean coreApp, boolean multiArch) { + int revisionCode, int installLocation, List<VerifierInfo> verifiers, + Signature[] signatures, boolean coreApp, boolean multiArch) { this.codePath = codePath; this.packageName = packageName; this.splitName = splitName; this.versionCode = versionCode; + this.revisionCode = revisionCode; this.installLocation = installLocation; this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]); this.signatures = signatures; @@ -409,6 +418,8 @@ public class PackageParser { pi.packageName = p.packageName; pi.splitNames = p.splitNames; pi.versionCode = p.mVersionCode; + pi.baseRevisionCode = p.baseRevisionCode; + pi.splitRevisionCodes = p.splitRevisionCodes; pi.versionName = p.mVersionName; pi.sharedUserId = p.mSharedUserId; pi.sharedUserLabel = p.mSharedUserLabel; @@ -647,7 +658,7 @@ public class PackageParser { throws PackageParserException { final ApkLite baseApk = parseApkLite(packageFile, flags); final String packagePath = packageFile.getAbsolutePath(); - return new PackageLite(packagePath, baseApk, null, null); + return new PackageLite(packagePath, baseApk, null, null, null); } private static PackageLite parseClusterPackageLite(File packageDir, int flags) @@ -704,20 +715,24 @@ public class PackageParser { String[] splitNames = null; String[] splitCodePaths = null; + int[] splitRevisionCodes = null; if (size > 0) { splitNames = new String[size]; splitCodePaths = new String[size]; + splitRevisionCodes = new int[size]; splitNames = apks.keySet().toArray(splitNames); Arrays.sort(splitNames, sSplitNameComparator); for (int i = 0; i < size; i++) { splitCodePaths[i] = apks.get(splitNames[i]).codePath; + splitRevisionCodes[i] = apks.get(splitNames[i]).revisionCode; } } final String codePath = packageDir.getAbsolutePath(); - return new PackageLite(codePath, baseApk, splitNames, splitCodePaths); + return new PackageLite(codePath, baseApk, splitNames, splitCodePaths, + splitRevisionCodes); } /** @@ -782,6 +797,7 @@ public class PackageParser { final int num = lite.splitNames.length; pkg.splitNames = lite.splitNames; pkg.splitCodePaths = lite.splitCodePaths; + pkg.splitRevisionCodes = lite.splitRevisionCodes; pkg.splitFlags = new int[num]; for (int i = 0; i < num; i++) { @@ -1249,25 +1265,21 @@ public class PackageParser { int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; int versionCode = 0; + int revisionCode = 0; boolean coreApp = false; boolean multiArch = false; - int numFound = 0; for (int i = 0; i < attrs.getAttributeCount(); i++) { - String attr = attrs.getAttributeName(i); + final String attr = attrs.getAttributeName(i); if (attr.equals("installLocation")) { installLocation = attrs.getAttributeIntValue(i, PARSE_DEFAULT_INSTALL_LOCATION); - numFound++; } else if (attr.equals("versionCode")) { versionCode = attrs.getAttributeIntValue(i, 0); - numFound++; + } else if (attr.equals("revisionCode")) { + revisionCode = attrs.getAttributeIntValue(i, 0); } else if (attr.equals("coreApp")) { coreApp = attrs.getAttributeBooleanValue(i, false); - numFound++; - } - if (numFound >= 3) { - break; } } @@ -1301,7 +1313,7 @@ public class PackageParser { } return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode, - installLocation, verifiers, signatures, coreApp, multiArch); + revisionCode, installLocation, verifiers, signatures, coreApp, multiArch); } /** @@ -1359,6 +1371,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifest); pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_versionCode, 0); + pkg.baseRevisionCode = sa.getInteger( + com.android.internal.R.styleable.AndroidManifest_revisionCode, 0); pkg.mVersionName = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifest_versionName, 0); if (pkg.mVersionName != null) { @@ -4168,6 +4182,7 @@ public class PackageParser { public final static class Package { public String packageName; + /** Names of any split APKs, ordered by parsed splitName */ public String[] splitNames; @@ -4185,6 +4200,11 @@ public class PackageParser { /** Paths of any split APKs, ordered by parsed splitName */ public String[] splitCodePaths; + /** Revision code of base APK */ + public int baseRevisionCode; + /** Revision codes of any split APKs, ordered by parsed splitName */ + public int[] splitRevisionCodes; + /** Flags of any split APKs; ordered by parsed splitName */ public int[] splitFlags; @@ -4222,7 +4242,7 @@ public class PackageParser { // The version code declared for this package. public int mVersionCode; - + // The version name declared for this package. public String mVersionName; diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index ad54912..78b9c18 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -296,9 +296,9 @@ public class VpnService extends Service { * * This method only needs to be called if the VPN has explicitly bound its underlying * communications channels — such as the socket(s) passed to {@link #protect(int)} — - * to a {@code Network} using APIs such as {@link Network#bindSocket} or {@link - * Network#bindDatagramSocket}. The VPN should call this method every time the set of {@code - * Network}s it is using changes. + * to a {@code Network} using APIs such as {@link Network#bindSocket(Socket)} or + * {@link Network#bindSocket(DatagramSocket)}. The VPN should call this method every time + * the set of {@code Network}s it is using changes. * * {@code networks} is one of the following: * <ul> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 10c2518..0c3fb9a 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -260,9 +260,18 @@ released, or define it however else you want, as long as each successive version has a higher number. This is not a version number generally shown to the user, that is usually supplied - with {@link android.R.attr#versionName}. --> + with {@link android.R.attr#versionName}. When an app is delivered + as multiple split APKs, each APK must have the exact same versionCode. --> <attr name="versionCode" format="integer" /> - + + <!-- Internal revision code. This number is the number used to determine + whether one APK is more recent than another: it has no other meaning + than that higher numbers are more recent. This value is only meaningful + when the two {@link android.R.attr#versionCode} values are already + identical. When an app is delivered as multiple split APKs, each + APK may have a different revisionCode value. --> + <attr name="revisionCode" format="integer" /> + <!-- The text shown to the user to indicate the version they have. This is used for no other purpose than display to the user; the actual significant version number is given by {@link android.R.attr#versionCode}. --> @@ -1025,6 +1034,7 @@ <declare-styleable name="AndroidManifest"> <attr name="versionCode" /> <attr name="versionName" /> + <attr name="revisionCode" /> <attr name="sharedUserId" /> <attr name="sharedUserLabel" /> <attr name="installLocation" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index def5444..2bb9aa8 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2593,6 +2593,7 @@ <public type="attr" name="accessibilityTraversalAfter" id="0x010104d2" /> <public type="attr" name="dialogPreferredPadding" id="0x010104d3" /> <public type="attr" name="searchHintIcon" id="0x010104d4" /> + <public type="attr" name="revisionCode" /> <public type="style" name="Theme.DeviceDefault.Dialog.Alert" /> <public type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" /> diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 1f28324..cc1e01a 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -180,7 +180,10 @@ public class DefaultContainerService extends IntentService { } ret.packageName = pkg.packageName; + ret.splitNames = pkg.splitNames; ret.versionCode = pkg.versionCode; + ret.baseRevisionCode = pkg.baseRevisionCode; + ret.splitRevisionCodes = pkg.splitRevisionCodes; ret.installLocation = pkg.installLocation; ret.verifiers = pkg.verifiers; ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context, diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index fb0e357..cc1b3ad 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -497,12 +497,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // haven't been overridden. if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { try { - if (stageCid != null) { + final List<File> fromFiles = mResolvedInheritedFiles; + final File toDir = resolveStageDir(); + + if (isLinkPossible(fromFiles, toDir)) { + linkFiles(fromFiles, toDir); + } else { // TODO: this should delegate to DCS so the system process // avoids holding open FDs into containers. - copyFiles(mResolvedInheritedFiles, resolveStageDir()); - } else { - linkFiles(mResolvedInheritedFiles, resolveStageDir()); + copyFiles(fromFiles, toDir); } } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, @@ -721,7 +724,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // This is kind of hacky; we're creating a half-parsed package that is // straddled between the inherited and staged APKs. final PackageLite pkg = new PackageLite(null, baseApk, null, - splitPaths.toArray(new String[splitPaths.size()])); + splitPaths.toArray(new String[splitPaths.size()]), null); final boolean isForwardLocked = (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; @@ -733,6 +736,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + /** + * Determine if creating hard links between source and destination is + * possible. That is, do they all live on the same underlying device. + */ + private boolean isLinkPossible(List<File> fromFiles, File toDir) { + try { + final StructStat toStat = Os.stat(toDir.getAbsolutePath()); + for (File fromFile : fromFiles) { + final StructStat fromStat = Os.stat(fromFile.getAbsolutePath()); + if (fromStat.st_dev != toStat.st_dev) { + return false; + } + } + } catch (ErrnoException e) { + Slog.w(TAG, "Failed to detect if linking possible: " + e); + return false; + } + return true; + } + private static void linkFiles(List<File> fromFiles, File toDir) throws IOException { for (File fromFile : fromFiles) { final File toFile = new File(toDir, fromFile.getName()); @@ -760,7 +783,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (!FileUtils.copyFile(fromFile, tmpFile)) { throw new IOException("Failed to copy " + fromFile + " to " + tmpFile); } - + try { + Os.chmod(tmpFile.getAbsolutePath(), 0644); + } catch (ErrnoException e) { + throw new IOException("Failed to chmod " + tmpFile); + } final File toFile = new File(toDir, fromFile.getName()); if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile); if (!tmpFile.renameTo(toFile)) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b891d0c..32e9dae 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -41,6 +41,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY; import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED; +import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.PackageParser.isApkFile; @@ -4275,7 +4276,7 @@ public class PackageManagerService extends IPackageManager.Stub { // version of the new path against what we have stored to determine // what to do. if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath); - if (pkg.mVersionCode < ps.versionCode) { + if (pkg.mVersionCode <= ps.versionCode) { // The system package has been updated and the code path does not match // Ignore entry. Skip it. Slog.i(TAG, "Package " + ps.name + " at " + scanFile @@ -4366,7 +4367,7 @@ public class PackageManagerService extends IPackageManager.Stub { * already installed version, hide it. It will be scanned later * and re-added like an update. */ - if (pkg.mVersionCode < ps.versionCode) { + if (pkg.mVersionCode <= ps.versionCode) { shouldHideSystemApp = true; logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile + " but new version " + pkg.mVersionCode + " better than installed " @@ -8857,11 +8858,10 @@ public class PackageManagerService extends IPackageManager.Stub { if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { // Check for downgrading. if ((installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) { - if (pkgLite.versionCode < pkg.mVersionCode) { - Slog.w(TAG, "Can't install update of " + packageName - + " update version " + pkgLite.versionCode - + " is older than installed version " - + pkg.mVersionCode); + try { + checkDowngrade(pkg, pkgLite); + } catch (PackageManagerException e) { + Slog.w(TAG, "Downgrade detected: " + e.getMessage()); return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE; } } @@ -13539,4 +13539,38 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + + /** + * Check and throw if the given before/after packages would be considered a + * downgrade. + */ + private static void checkDowngrade(PackageParser.Package before, PackageInfoLite after) + throws PackageManagerException { + if (after.versionCode < before.mVersionCode) { + throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, + "Update version code " + after.versionCode + " is older than current " + + before.mVersionCode); + } else if (after.versionCode == before.mVersionCode) { + if (after.baseRevisionCode < before.baseRevisionCode) { + throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, + "Update base revision code " + after.baseRevisionCode + + " is older than current " + before.baseRevisionCode); + } + + if (!ArrayUtils.isEmpty(after.splitNames)) { + for (int i = 0; i < after.splitNames.length; i++) { + final String splitName = after.splitNames[i]; + final int j = ArrayUtils.indexOf(before.splitNames, splitName); + if (j != -1) { + if (after.splitRevisionCodes[i] < before.splitRevisionCodes[j]) { + throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE, + "Update split " + splitName + " revision code " + + after.splitRevisionCodes[i] + " is older than current " + + before.splitRevisionCodes[j]); + } + } + } + } + } + } } |