summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt3
-rw-r--r--core/java/android/content/pm/PackageInfo.java31
-rw-r--r--core/java/android/content/pm/PackageInfoLite.java16
-rw-r--r--core/java/android/content/pm/PackageParser.java50
-rw-r--r--core/java/android/net/VpnService.java6
-rw-r--r--core/res/res/values/attrs_manifest.xml14
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java39
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java48
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 &mdash; such as the socket(s) passed to {@link #protect(int)} &mdash;
- * 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]);
+ }
+ }
+ }
+ }
+ }
+ }
}