Preserve install initiatior package on uninstall.
Previously when an installer package was uninstalled we removed all reference to it in InstallSource. Now we retain the initiator package name (but not installer or originator). We set a flag to remember it has been uninstalled, and we also store its signatures which would otherwise cease to be available. Update persistence logic. Also minor refactoring of InstallSource#create to separate the normal case from those that need to set every field. This information is not yet exposed in the API, but it will be. Test: atest PackageManagerTests PackageVerifierTest Test: Manually verify XML contents before & after uninstalling an installer. Bug: 134746019 Bug: 146555198 Change-Id: I1452402de554b916e5f79bd386c5857d4860ba12
This commit is contained in:
@@ -18,6 +18,8 @@ package com.android.server.pm;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -29,15 +31,26 @@ final class InstallSource {
|
||||
* An instance of InstallSource representing an absence of knowledge of the source of
|
||||
* a package. Used in preference to null.
|
||||
*/
|
||||
static final InstallSource EMPTY = new InstallSource(null, null, null, false);
|
||||
static final InstallSource EMPTY = new InstallSource(null, null, null, false, false, null);
|
||||
|
||||
/** We also memoize this case because it is common - all un-updated system apps. */
|
||||
private static final InstallSource EMPTY_ORPHANED = new InstallSource(null, null, null, true);
|
||||
private static final InstallSource EMPTY_ORPHANED = new InstallSource(
|
||||
null, null, null, true, false, null);
|
||||
|
||||
/** The package that requested the installation, if known. */
|
||||
/**
|
||||
* The package that requested the installation, if known. May not correspond to a currently
|
||||
* installed package if {@link #isInitiatingPackageUninstalled} is true.
|
||||
*/
|
||||
@Nullable
|
||||
final String initiatingPackageName;
|
||||
|
||||
/**
|
||||
* The signing details of the initiating package, if known. Always null if
|
||||
* {@link #initiatingPackageName} is null.
|
||||
*/
|
||||
@Nullable
|
||||
final PackageSignatures initiatingPackageSignatures;
|
||||
|
||||
/**
|
||||
* The package on behalf of which the initiating package requested the installation, if any.
|
||||
* For example if a downloaded APK is installed via the Package Installer this could be the
|
||||
@@ -57,76 +70,120 @@ final class InstallSource {
|
||||
/** Indicates if the package that was the installerPackageName has been uninstalled. */
|
||||
final boolean isOrphaned;
|
||||
|
||||
/**
|
||||
* Indicates if the package in initiatingPackageName has been uninstalled. Always false if
|
||||
* {@link #initiatingPackageName} is null.
|
||||
*/
|
||||
final boolean isInitiatingPackageUninstalled;
|
||||
|
||||
static InstallSource create(@Nullable String initiatingPackageName,
|
||||
@Nullable String originatingPackageName, @Nullable String installerPackageName) {
|
||||
return create(initiatingPackageName, originatingPackageName, installerPackageName,
|
||||
false, false);
|
||||
}
|
||||
|
||||
static InstallSource create(@Nullable String initiatingPackageName,
|
||||
@Nullable String originatingPackageName, @Nullable String installerPackageName,
|
||||
boolean isOrphaned) {
|
||||
boolean isOrphaned, boolean isInitiatingPackageUninstalled) {
|
||||
return createInternal(
|
||||
intern(initiatingPackageName),
|
||||
intern(originatingPackageName),
|
||||
intern(installerPackageName),
|
||||
isOrphaned);
|
||||
isOrphaned, isInitiatingPackageUninstalled, null);
|
||||
}
|
||||
|
||||
private static InstallSource createInternal(@Nullable String initiatingPackageName,
|
||||
@Nullable String originatingPackageName, @Nullable String installerPackageName,
|
||||
boolean isOrphaned) {
|
||||
boolean isOrphaned, boolean isInitiatingPackageUninstalled,
|
||||
@Nullable PackageSignatures initiatingPackageSignatures) {
|
||||
if (initiatingPackageName == null && originatingPackageName == null
|
||||
&& installerPackageName == null) {
|
||||
&& installerPackageName == null && initiatingPackageSignatures == null
|
||||
&& !isInitiatingPackageUninstalled) {
|
||||
return isOrphaned ? EMPTY_ORPHANED : EMPTY;
|
||||
}
|
||||
return new InstallSource(initiatingPackageName, originatingPackageName,
|
||||
installerPackageName, isOrphaned);
|
||||
installerPackageName, isOrphaned, isInitiatingPackageUninstalled,
|
||||
initiatingPackageSignatures
|
||||
);
|
||||
}
|
||||
|
||||
private InstallSource(@Nullable String initiatingPackageName,
|
||||
@Nullable String originatingPackageName, @Nullable String installerPackageName,
|
||||
boolean isOrphaned) {
|
||||
boolean isOrphaned, boolean isInitiatingPackageUninstalled,
|
||||
@Nullable PackageSignatures initiatingPackageSignatures) {
|
||||
if (initiatingPackageName == null) {
|
||||
Preconditions.checkArgument(initiatingPackageSignatures == null);
|
||||
Preconditions.checkArgument(!isInitiatingPackageUninstalled);
|
||||
}
|
||||
this.initiatingPackageName = initiatingPackageName;
|
||||
this.originatingPackageName = originatingPackageName;
|
||||
this.installerPackageName = installerPackageName;
|
||||
this.isOrphaned = isOrphaned;
|
||||
this.isInitiatingPackageUninstalled = isInitiatingPackageUninstalled;
|
||||
this.initiatingPackageSignatures = initiatingPackageSignatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an InstallSource the same as this one except with the specified installerPackageName.
|
||||
* Return an InstallSource the same as this one except with the specified
|
||||
* {@link #installerPackageName}.
|
||||
*/
|
||||
InstallSource setInstallerPackage(String installerPackageName) {
|
||||
InstallSource setInstallerPackage(@Nullable String installerPackageName) {
|
||||
if (Objects.equals(installerPackageName, this.installerPackageName)) {
|
||||
return this;
|
||||
}
|
||||
return createInternal(initiatingPackageName, originatingPackageName,
|
||||
intern(installerPackageName), isOrphaned);
|
||||
intern(installerPackageName), isOrphaned, isInitiatingPackageUninstalled,
|
||||
initiatingPackageSignatures
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an InstallSource the same as this one except with the specified value for isOrphaned.
|
||||
* Return an InstallSource the same as this one except with the specified value for
|
||||
* {@link #isOrphaned}.
|
||||
*/
|
||||
InstallSource setIsOrphaned(boolean isOrphaned) {
|
||||
if (isOrphaned == this.isOrphaned) {
|
||||
return this;
|
||||
}
|
||||
return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
|
||||
isOrphaned);
|
||||
isOrphaned, isInitiatingPackageUninstalled, initiatingPackageSignatures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an InstallSource the same as this one except it does not refer to the specified
|
||||
* installer package name (which is being uninstalled).
|
||||
* Return an InstallSource the same as this one except with the specified
|
||||
* {@link #initiatingPackageSignatures}.
|
||||
*/
|
||||
InstallSource removeInstallerPackage(String packageName) {
|
||||
InstallSource setInitiatingPackageSignatures(@Nullable PackageSignatures signatures) {
|
||||
if (signatures == initiatingPackageSignatures) {
|
||||
return this;
|
||||
}
|
||||
return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
|
||||
isOrphaned, isInitiatingPackageUninstalled, signatures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an InstallSource the same as this one updated to reflect that the specified installer
|
||||
* package name has been uninstalled.
|
||||
*/
|
||||
InstallSource removeInstallerPackage(@Nullable String packageName) {
|
||||
if (packageName == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean modified = false;
|
||||
String initiatingPackageName = this.initiatingPackageName;
|
||||
boolean isInitiatingPackageUninstalled = this.isInitiatingPackageUninstalled;
|
||||
String originatingPackageName = this.originatingPackageName;
|
||||
String installerPackageName = this.installerPackageName;
|
||||
boolean isOrphaned = this.isOrphaned;
|
||||
|
||||
if (packageName.equals(initiatingPackageName)) {
|
||||
initiatingPackageName = null;
|
||||
modified = true;
|
||||
if (packageName.equals(this.initiatingPackageName)) {
|
||||
if (!isInitiatingPackageUninstalled) {
|
||||
// In this case we deliberately do not clear the package name (and signatures).
|
||||
// We allow an app to retrieve details of its own install initiator even after
|
||||
// it has been uninstalled.
|
||||
isInitiatingPackageUninstalled = true;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
if (packageName.equals(originatingPackageName)) {
|
||||
originatingPackageName = null;
|
||||
@@ -141,8 +198,9 @@ final class InstallSource {
|
||||
if (!modified) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
|
||||
isOrphaned);
|
||||
isOrphaned, isInitiatingPackageUninstalled, initiatingPackageSignatures);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -635,7 +635,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
||||
}
|
||||
}
|
||||
InstallSource installSource = InstallSource.create(installerPackageName,
|
||||
originatingPackageName, requestedInstallerPackageName, false);
|
||||
originatingPackageName, requestedInstallerPackageName);
|
||||
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
|
||||
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
|
||||
installSource, params, createdMillis,
|
||||
|
||||
@@ -1452,7 +1452,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
|
||||
mInstallerUid = uid;
|
||||
mInstallSource = InstallSource.create(packageName, null, packageName, false);
|
||||
mInstallSource = InstallSource.create(packageName, null, packageName);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
onSessionTransferStatus(statusReceiver, packageName,
|
||||
@@ -3115,7 +3115,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
|
||||
InstallSource installSource = InstallSource.create(installInitiatingPackageName,
|
||||
installOriginatingPackageName, installerPackageName, false);
|
||||
installOriginatingPackageName, installerPackageName);
|
||||
return new PackageInstallerSession(callback, context, pm, sessionProvider,
|
||||
installerThread, stagingManager, sessionId, userId, installerUid,
|
||||
installSource, params, createdMillis, stageDir, stageCid, fileInfosArray,
|
||||
|
||||
@@ -15158,10 +15158,10 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
|
||||
|
||||
final String pkgName = pkg.getPackageName();
|
||||
final InstallSource installSource = installArgs.installSource;
|
||||
final String installerPackageName = installSource.installerPackageName;
|
||||
final int[] installedForUsers = res.origUsers;
|
||||
final int installReason = installArgs.installReason;
|
||||
InstallSource installSource = installArgs.installSource;
|
||||
final String installerPackageName = installSource.installerPackageName;
|
||||
|
||||
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getCodePath());
|
||||
synchronized (mLock) {
|
||||
@@ -15207,6 +15207,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
|
||||
}
|
||||
|
||||
if (installSource.initiatingPackageName != null) {
|
||||
final PackageSetting ips = mSettings.mPackages.get(
|
||||
installSource.initiatingPackageName);
|
||||
if (ips != null) {
|
||||
installSource = installSource.setInitiatingPackageSignatures(
|
||||
ips.signatures);
|
||||
}
|
||||
}
|
||||
ps.setInstallSource(installSource);
|
||||
mSettings.addInstallerPackageNames(installSource);
|
||||
|
||||
@@ -19976,19 +19984,25 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
// All installSource strings are interned, so == is ok here
|
||||
if (installSource.initiatingPackageName == installSource.installerPackageName) {
|
||||
// The installer and initiator will often be the same, and when they are
|
||||
// we can skip doing the same check again.
|
||||
initiatingPackageName = installerPackageName;
|
||||
if (installSource.isInitiatingPackageUninstalled) {
|
||||
// TODO(b/146555198) Allow the app itself to see the info
|
||||
// (at least for non-instant apps)
|
||||
initiatingPackageName = null;
|
||||
} else {
|
||||
initiatingPackageName = installSource.initiatingPackageName;
|
||||
final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName);
|
||||
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
|
||||
initiatingPackageName = null;
|
||||
// All installSource strings are interned, so == is ok here
|
||||
if (installSource.initiatingPackageName == installSource.installerPackageName) {
|
||||
// The installer and initiator will often be the same, and when they are
|
||||
// we can skip doing the same check again.
|
||||
initiatingPackageName = installerPackageName;
|
||||
} else {
|
||||
initiatingPackageName = installSource.initiatingPackageName;
|
||||
final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName);
|
||||
if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
|
||||
initiatingPackageName = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
originatingPackageName = installSource.originatingPackageName;
|
||||
if (originatingPackageName != null) {
|
||||
final PackageSetting ps = mSettings.mPackages.get(originatingPackageName);
|
||||
|
||||
@@ -2819,6 +2819,9 @@ public final class Settings {
|
||||
if (installSource.initiatingPackageName != null) {
|
||||
serializer.attribute(null, "installInitiator", installSource.initiatingPackageName);
|
||||
}
|
||||
if (installSource.isInitiatingPackageUninstalled) {
|
||||
serializer.attribute(null, "installInitiatorUninstalled", "true");
|
||||
}
|
||||
if (installSource.originatingPackageName != null) {
|
||||
serializer.attribute(null, "installOriginator", installSource.originatingPackageName);
|
||||
}
|
||||
@@ -2836,6 +2839,11 @@ public final class Settings {
|
||||
|
||||
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
|
||||
|
||||
if (installSource.initiatingPackageSignatures != null) {
|
||||
installSource.initiatingPackageSignatures.writeXml(
|
||||
serializer, "install-initiator-sigs", mPastSignatures);
|
||||
}
|
||||
|
||||
writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissionStates());
|
||||
|
||||
writeSigningKeySetLPr(serializer, pkg.keySetData);
|
||||
@@ -3571,6 +3579,7 @@ public final class Settings {
|
||||
String isOrphaned = null;
|
||||
String installOriginatingPackageName = null;
|
||||
String installInitiatingPackageName = null;
|
||||
String installInitiatorUninstalled = null;
|
||||
String volumeUuid = null;
|
||||
String categoryHintString = null;
|
||||
String updateAvailable = null;
|
||||
@@ -3616,6 +3625,8 @@ public final class Settings {
|
||||
isOrphaned = parser.getAttributeValue(null, "isOrphaned");
|
||||
installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator");
|
||||
installOriginatingPackageName = parser.getAttributeValue(null, "installOriginator");
|
||||
installInitiatorUninstalled = parser.getAttributeValue(null,
|
||||
"installInitiatorUninstalled");
|
||||
volumeUuid = parser.getAttributeValue(null, "volumeUuid");
|
||||
categoryHintString = parser.getAttributeValue(null, "categoryHint");
|
||||
if (categoryHintString != null) {
|
||||
@@ -3772,7 +3783,8 @@ public final class Settings {
|
||||
packageSetting.uidError = "true".equals(uidError);
|
||||
InstallSource installSource = InstallSource.create(
|
||||
installInitiatingPackageName, installOriginatingPackageName,
|
||||
installerPackageName, "true".equals(isOrphaned));
|
||||
installerPackageName, "true".equals(isOrphaned),
|
||||
"true".equals(installInitiatorUninstalled));
|
||||
packageSetting.installSource = installSource;
|
||||
packageSetting.volumeUuid = volumeUuid;
|
||||
packageSetting.categoryHint = categoryHint;
|
||||
@@ -3849,6 +3861,11 @@ public final class Settings {
|
||||
mKeySetRefs.put(id, 1);
|
||||
}
|
||||
packageSetting.keySetData.addDefinedKeySet(id, alias);
|
||||
} else if (tagName.equals("install-initiator-sigs")) {
|
||||
final PackageSignatures signatures = new PackageSignatures();
|
||||
signatures.readXml(parser, mPastSignatures);
|
||||
packageSetting.installSource =
|
||||
packageSetting.installSource.setInitiatingPackageSignatures(signatures);
|
||||
} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
|
||||
readDomainVerificationLPw(parser, packageSetting);
|
||||
} else {
|
||||
|
||||
@@ -156,8 +156,7 @@ public class PackageInstallerSessionTest {
|
||||
if (isMultiPackage) {
|
||||
params.isMultiPackage = true;
|
||||
}
|
||||
InstallSource installSource = InstallSource.create("testInstaller", null, "testInstaller",
|
||||
false);
|
||||
InstallSource installSource = InstallSource.create("testInstaller", null, "testInstaller");
|
||||
return new PackageInstallerSession(
|
||||
/* callback */ null,
|
||||
/* context */null,
|
||||
|
||||
Reference in New Issue
Block a user