Merge "Introduce revision codes for split APKs." into lmp-mr1-dev

This commit is contained in:
Jeff Sharkey
2014-11-24 20:16:12 +00:00
committed by Android (Google) Code Review
10 changed files with 175 additions and 36 deletions

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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,

View File

@@ -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)) {

View File

@@ -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]);
}
}
}
}
}
}
}