/*
|
* Copyright (C) 2011 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package com.android.server.pm;
|
|
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
|
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
|
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
|
|
import android.content.pm.ApplicationInfo;
|
import android.content.pm.IntentFilterVerificationInfo;
|
import android.content.pm.PackageManager;
|
import android.content.pm.PackageParser;
|
import android.content.pm.PackageUserState;
|
import android.content.pm.Signature;
|
import android.content.pm.SuspendDialogInfo;
|
import android.os.PersistableBundle;
|
import android.service.pm.PackageProto;
|
import android.util.ArraySet;
|
import android.util.SparseArray;
|
import android.util.proto.ProtoOutputStream;
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import java.io.File;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.List;
|
import java.util.Set;
|
|
/**
|
* Settings base class for pending and resolved classes.
|
*/
|
public abstract class PackageSettingBase extends SettingBase {
|
|
private static final int[] EMPTY_INT_ARRAY = new int[0];
|
|
public final String name;
|
final String realName;
|
|
String parentPackageName;
|
List<String> childPackageNames;
|
|
/**
|
* Path where this package was found on disk. For monolithic packages
|
* this is path to single base APK file; for cluster packages this is
|
* path to the cluster directory.
|
*/
|
File codePath;
|
String codePathString;
|
File resourcePath;
|
String resourcePathString;
|
|
String[] usesStaticLibraries;
|
long[] usesStaticLibrariesVersions;
|
|
/**
|
* The path under which native libraries have been unpacked. This path is
|
* always derived at runtime, and is only stored here for cleanup when a
|
* package is uninstalled.
|
*/
|
@Deprecated
|
String legacyNativeLibraryPathString;
|
|
/**
|
* The primary CPU abi for this package.
|
*/
|
String primaryCpuAbiString;
|
|
/**
|
* The secondary CPU abi for this package.
|
*/
|
String secondaryCpuAbiString;
|
|
/**
|
* The install time CPU override, if any. This value is written at install time
|
* and doesn't change during the life of an install. If non-null,
|
* {@code primaryCpuAbiString} will contain the same value.
|
*/
|
String cpuAbiOverrideString;
|
|
long timeStamp;
|
long firstInstallTime;
|
long lastUpdateTime;
|
long versionCode;
|
|
boolean uidError;
|
|
PackageSignatures signatures;
|
|
boolean installPermissionsFixed;
|
|
PackageKeySetData keySetData = new PackageKeySetData();
|
|
static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
|
|
// Whether this package is currently stopped, thus can not be
|
// started until explicitly launched by the user.
|
private final SparseArray<PackageUserState> mUserState = new SparseArray<>();
|
|
/**
|
* Non-persisted value. During an "upgrade without restart", we need the set
|
* of all previous code paths so we can surgically add the new APKs to the
|
* active classloader. If at any point an application is upgraded with a
|
* restart, this field will be cleared since the classloader would be created
|
* using the full set of code paths when the package's process is started.
|
*/
|
Set<String> mOldCodePaths;
|
|
/** Package name of the app that installed this package */
|
String installerPackageName;
|
/** Indicates if the package that installed this app has been uninstalled */
|
boolean isOrphaned;
|
/** UUID of {@link VolumeInfo} hosting this app */
|
String volumeUuid;
|
/** The category of this app, as hinted by the installer */
|
int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
|
/** Whether or not an update is available. Ostensibly only for instant apps. */
|
boolean updateAvailable;
|
|
IntentFilterVerificationInfo verificationInfo;
|
|
PackageSettingBase(String name, String realName, File codePath, File resourcePath,
|
String legacyNativeLibraryPathString, String primaryCpuAbiString,
|
String secondaryCpuAbiString, String cpuAbiOverrideString,
|
long pVersionCode, int pkgFlags, int pkgPrivateFlags,
|
String parentPackageName, List<String> childPackageNames,
|
String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
|
super(pkgFlags, pkgPrivateFlags);
|
this.name = name;
|
this.realName = realName;
|
this.parentPackageName = parentPackageName;
|
this.childPackageNames = (childPackageNames != null)
|
? new ArrayList<>(childPackageNames) : null;
|
this.usesStaticLibraries = usesStaticLibraries;
|
this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
|
init(codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString,
|
secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode);
|
}
|
|
/**
|
* New instance of PackageSetting with one-level-deep cloning.
|
* <p>
|
* IMPORTANT: With a shallow copy, we do NOT create new contained objects.
|
* This means, for example, changes to the user state of the original PackageSetting
|
* will also change the user state in its copy.
|
*/
|
PackageSettingBase(PackageSettingBase base, String realName) {
|
super(base);
|
name = base.name;
|
this.realName = realName;
|
doCopy(base);
|
}
|
|
void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
|
String primaryCpuAbiString, String secondaryCpuAbiString,
|
String cpuAbiOverrideString, long pVersionCode) {
|
this.codePath = codePath;
|
this.codePathString = codePath.toString();
|
this.resourcePath = resourcePath;
|
this.resourcePathString = resourcePath.toString();
|
this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
|
this.primaryCpuAbiString = primaryCpuAbiString;
|
this.secondaryCpuAbiString = secondaryCpuAbiString;
|
this.cpuAbiOverrideString = cpuAbiOverrideString;
|
this.versionCode = pVersionCode;
|
this.signatures = new PackageSignatures();
|
}
|
|
public void setInstallerPackageName(String packageName) {
|
installerPackageName = packageName;
|
}
|
|
public String getInstallerPackageName() {
|
return installerPackageName;
|
}
|
|
public void setVolumeUuid(String volumeUuid) {
|
this.volumeUuid = volumeUuid;
|
}
|
|
public String getVolumeUuid() {
|
return volumeUuid;
|
}
|
|
public void setTimeStamp(long newStamp) {
|
timeStamp = newStamp;
|
}
|
|
public void setUpdateAvailable(boolean updateAvailable) {
|
this.updateAvailable = updateAvailable;
|
}
|
|
public boolean isUpdateAvailable() {
|
return updateAvailable;
|
}
|
|
public boolean isSharedUser() {
|
return false;
|
}
|
|
public Signature[] getSignatures() {
|
return signatures.mSigningDetails.signatures;
|
}
|
|
public PackageParser.SigningDetails getSigningDetails() {
|
return signatures.mSigningDetails;
|
}
|
|
/**
|
* Makes a shallow copy of the given package settings.
|
*
|
* NOTE: For some fields [such as keySetData, signatures, mUserState, verificationInfo, etc...],
|
* the original object is copied and a new one is not created.
|
*/
|
public void copyFrom(PackageSettingBase orig) {
|
super.copyFrom(orig);
|
doCopy(orig);
|
}
|
|
private void doCopy(PackageSettingBase orig) {
|
childPackageNames = (orig.childPackageNames != null)
|
? new ArrayList<>(orig.childPackageNames) : null;
|
codePath = orig.codePath;
|
codePathString = orig.codePathString;
|
cpuAbiOverrideString = orig.cpuAbiOverrideString;
|
firstInstallTime = orig.firstInstallTime;
|
installPermissionsFixed = orig.installPermissionsFixed;
|
installerPackageName = orig.installerPackageName;
|
isOrphaned = orig.isOrphaned;
|
keySetData = orig.keySetData;
|
lastUpdateTime = orig.lastUpdateTime;
|
legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
|
// Intentionally skip mOldCodePaths; it's not relevant for copies
|
parentPackageName = orig.parentPackageName;
|
primaryCpuAbiString = orig.primaryCpuAbiString;
|
resourcePath = orig.resourcePath;
|
resourcePathString = orig.resourcePathString;
|
secondaryCpuAbiString = orig.secondaryCpuAbiString;
|
signatures = orig.signatures;
|
timeStamp = orig.timeStamp;
|
uidError = orig.uidError;
|
mUserState.clear();
|
for (int i = 0; i < orig.mUserState.size(); i++) {
|
mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i));
|
}
|
verificationInfo = orig.verificationInfo;
|
versionCode = orig.versionCode;
|
volumeUuid = orig.volumeUuid;
|
categoryHint = orig.categoryHint;
|
usesStaticLibraries = orig.usesStaticLibraries != null
|
? Arrays.copyOf(orig.usesStaticLibraries,
|
orig.usesStaticLibraries.length) : null;
|
usesStaticLibrariesVersions = orig.usesStaticLibrariesVersions != null
|
? Arrays.copyOf(orig.usesStaticLibrariesVersions,
|
orig.usesStaticLibrariesVersions.length) : null;
|
updateAvailable = orig.updateAvailable;
|
}
|
|
private PackageUserState modifyUserState(int userId) {
|
PackageUserState state = mUserState.get(userId);
|
if (state == null) {
|
state = new PackageUserState();
|
mUserState.put(userId, state);
|
}
|
return state;
|
}
|
|
public PackageUserState readUserState(int userId) {
|
PackageUserState state = mUserState.get(userId);
|
if (state == null) {
|
return DEFAULT_USER_STATE;
|
}
|
state.categoryHint = categoryHint;
|
return state;
|
}
|
|
void setEnabled(int state, int userId, String callingPackage) {
|
PackageUserState st = modifyUserState(userId);
|
st.enabled = state;
|
st.lastDisableAppCaller = callingPackage;
|
}
|
|
int getEnabled(int userId) {
|
return readUserState(userId).enabled;
|
}
|
|
String getLastDisabledAppCaller(int userId) {
|
return readUserState(userId).lastDisableAppCaller;
|
}
|
|
void setInstalled(boolean inst, int userId) {
|
modifyUserState(userId).installed = inst;
|
}
|
|
boolean getInstalled(int userId) {
|
return readUserState(userId).installed;
|
}
|
|
int getInstallReason(int userId) {
|
return readUserState(userId).installReason;
|
}
|
|
void setInstallReason(int installReason, int userId) {
|
modifyUserState(userId).installReason = installReason;
|
}
|
|
void setOverlayPaths(List<String> overlayPaths, int userId) {
|
modifyUserState(userId).overlayPaths = overlayPaths == null ? null :
|
overlayPaths.toArray(new String[overlayPaths.size()]);
|
}
|
|
String[] getOverlayPaths(int userId) {
|
return readUserState(userId).overlayPaths;
|
}
|
|
/** Only use for testing. Do NOT use in production code. */
|
@VisibleForTesting
|
SparseArray<PackageUserState> getUserState() {
|
return mUserState;
|
}
|
|
boolean isAnyInstalled(int[] users) {
|
for (int user: users) {
|
if (readUserState(user).installed) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
int[] queryInstalledUsers(int[] users, boolean installed) {
|
int num = 0;
|
for (int user : users) {
|
if (getInstalled(user) == installed) {
|
num++;
|
}
|
}
|
int[] res = new int[num];
|
num = 0;
|
for (int user : users) {
|
if (getInstalled(user) == installed) {
|
res[num] = user;
|
num++;
|
}
|
}
|
return res;
|
}
|
|
long getCeDataInode(int userId) {
|
return readUserState(userId).ceDataInode;
|
}
|
|
void setCeDataInode(long ceDataInode, int userId) {
|
modifyUserState(userId).ceDataInode = ceDataInode;
|
}
|
|
boolean getStopped(int userId) {
|
return readUserState(userId).stopped;
|
}
|
|
void setStopped(boolean stop, int userId) {
|
modifyUserState(userId).stopped = stop;
|
}
|
|
boolean getNotLaunched(int userId) {
|
return readUserState(userId).notLaunched;
|
}
|
|
void setNotLaunched(boolean stop, int userId) {
|
modifyUserState(userId).notLaunched = stop;
|
}
|
|
boolean getHidden(int userId) {
|
return readUserState(userId).hidden;
|
}
|
|
void setHidden(boolean hidden, int userId) {
|
modifyUserState(userId).hidden = hidden;
|
}
|
|
int getDistractionFlags(int userId) {
|
return readUserState(userId).distractionFlags;
|
}
|
|
void setDistractionFlags(int distractionFlags, int userId) {
|
modifyUserState(userId).distractionFlags = distractionFlags;
|
}
|
|
boolean getSuspended(int userId) {
|
return readUserState(userId).suspended;
|
}
|
|
void setSuspended(boolean suspended, String suspendingPackage, SuspendDialogInfo dialogInfo,
|
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
|
final PackageUserState existingUserState = modifyUserState(userId);
|
existingUserState.suspended = suspended;
|
existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
|
existingUserState.dialogInfo = suspended ? dialogInfo : null;
|
existingUserState.suspendedAppExtras = suspended ? appExtras : null;
|
existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
|
}
|
|
public boolean getInstantApp(int userId) {
|
return readUserState(userId).instantApp;
|
}
|
|
void setInstantApp(boolean instantApp, int userId) {
|
modifyUserState(userId).instantApp = instantApp;
|
}
|
|
boolean getVirtulalPreload(int userId) {
|
return readUserState(userId).virtualPreload;
|
}
|
|
void setVirtualPreload(boolean virtualPreload, int userId) {
|
modifyUserState(userId).virtualPreload = virtualPreload;
|
}
|
|
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
|
boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
|
String suspendingPackage,
|
SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras,
|
PersistableBundle suspendedLauncherExtras, boolean instantApp,
|
boolean virtualPreload, String lastDisableAppCaller,
|
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
|
int domainVerifState, int linkGeneration, int installReason,
|
String harmfulAppWarning) {
|
PackageUserState state = modifyUserState(userId);
|
state.ceDataInode = ceDataInode;
|
state.enabled = enabled;
|
state.installed = installed;
|
state.stopped = stopped;
|
state.notLaunched = notLaunched;
|
state.hidden = hidden;
|
state.distractionFlags = distractionFlags;
|
state.suspended = suspended;
|
state.suspendingPackage = suspendingPackage;
|
state.dialogInfo = dialogInfo;
|
state.suspendedAppExtras = suspendedAppExtras;
|
state.suspendedLauncherExtras = suspendedLauncherExtras;
|
state.lastDisableAppCaller = lastDisableAppCaller;
|
state.enabledComponents = enabledComponents;
|
state.disabledComponents = disabledComponents;
|
state.domainVerificationStatus = domainVerifState;
|
state.appLinkGeneration = linkGeneration;
|
state.installReason = installReason;
|
state.instantApp = instantApp;
|
state.virtualPreload = virtualPreload;
|
state.harmfulAppWarning = harmfulAppWarning;
|
}
|
|
ArraySet<String> getEnabledComponents(int userId) {
|
return readUserState(userId).enabledComponents;
|
}
|
|
ArraySet<String> getDisabledComponents(int userId) {
|
return readUserState(userId).disabledComponents;
|
}
|
|
void setEnabledComponents(ArraySet<String> components, int userId) {
|
modifyUserState(userId).enabledComponents = components;
|
}
|
|
void setDisabledComponents(ArraySet<String> components, int userId) {
|
modifyUserState(userId).disabledComponents = components;
|
}
|
|
void setEnabledComponentsCopy(ArraySet<String> components, int userId) {
|
modifyUserState(userId).enabledComponents = components != null
|
? new ArraySet<String>(components) : null;
|
}
|
|
void setDisabledComponentsCopy(ArraySet<String> components, int userId) {
|
modifyUserState(userId).disabledComponents = components != null
|
? new ArraySet<String>(components) : null;
|
}
|
|
PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
|
PackageUserState state = modifyUserState(userId);
|
if (disabled && state.disabledComponents == null) {
|
state.disabledComponents = new ArraySet<String>(1);
|
}
|
if (enabled && state.enabledComponents == null) {
|
state.enabledComponents = new ArraySet<String>(1);
|
}
|
return state;
|
}
|
|
void addDisabledComponent(String componentClassName, int userId) {
|
modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
|
}
|
|
void addEnabledComponent(String componentClassName, int userId) {
|
modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
|
}
|
|
boolean enableComponentLPw(String componentClassName, int userId) {
|
PackageUserState state = modifyUserStateComponents(userId, false, true);
|
boolean changed = state.disabledComponents != null
|
? state.disabledComponents.remove(componentClassName) : false;
|
changed |= state.enabledComponents.add(componentClassName);
|
return changed;
|
}
|
|
boolean disableComponentLPw(String componentClassName, int userId) {
|
PackageUserState state = modifyUserStateComponents(userId, true, false);
|
boolean changed = state.enabledComponents != null
|
? state.enabledComponents.remove(componentClassName) : false;
|
changed |= state.disabledComponents.add(componentClassName);
|
return changed;
|
}
|
|
boolean restoreComponentLPw(String componentClassName, int userId) {
|
PackageUserState state = modifyUserStateComponents(userId, true, true);
|
boolean changed = state.disabledComponents != null
|
? state.disabledComponents.remove(componentClassName) : false;
|
changed |= state.enabledComponents != null
|
? state.enabledComponents.remove(componentClassName) : false;
|
return changed;
|
}
|
|
int getCurrentEnabledStateLPr(String componentName, int userId) {
|
PackageUserState state = readUserState(userId);
|
if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) {
|
return COMPONENT_ENABLED_STATE_ENABLED;
|
} else if (state.disabledComponents != null
|
&& state.disabledComponents.contains(componentName)) {
|
return COMPONENT_ENABLED_STATE_DISABLED;
|
} else {
|
return COMPONENT_ENABLED_STATE_DEFAULT;
|
}
|
}
|
|
void removeUser(int userId) {
|
mUserState.delete(userId);
|
}
|
|
public int[] getNotInstalledUserIds() {
|
int count = 0;
|
int userStateCount = mUserState.size();
|
for (int i = 0; i < userStateCount; i++) {
|
if (!mUserState.valueAt(i).installed) {
|
count++;
|
}
|
}
|
if (count == 0) return EMPTY_INT_ARRAY;
|
int[] excludedUserIds = new int[count];
|
int idx = 0;
|
for (int i = 0; i < userStateCount; i++) {
|
if (!mUserState.valueAt(i).installed) {
|
excludedUserIds[idx++] = mUserState.keyAt(i);
|
}
|
}
|
return excludedUserIds;
|
}
|
|
IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
|
return verificationInfo;
|
}
|
|
void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
|
verificationInfo = info;
|
}
|
|
// Returns a packed value as a long:
|
//
|
// high 'int'-sized word: link status: undefined/ask/never/always.
|
// low 'int'-sized word: relative priority among 'always' results.
|
long getDomainVerificationStatusForUser(int userId) {
|
PackageUserState state = readUserState(userId);
|
long result = (long) state.appLinkGeneration;
|
result |= ((long) state.domainVerificationStatus) << 32;
|
return result;
|
}
|
|
void setDomainVerificationStatusForUser(final int status, int generation, int userId) {
|
PackageUserState state = modifyUserState(userId);
|
state.domainVerificationStatus = status;
|
if (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
|
state.appLinkGeneration = generation;
|
}
|
}
|
|
void clearDomainVerificationStatusForUser(int userId) {
|
modifyUserState(userId).domainVerificationStatus =
|
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
|
}
|
|
protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
|
int count = mUserState.size();
|
for (int i = 0; i < count; i++) {
|
final long userToken = proto.start(fieldId);
|
final int userId = mUserState.keyAt(i);
|
final PackageUserState state = mUserState.valueAt(i);
|
proto.write(PackageProto.UserInfoProto.ID, userId);
|
final int installType;
|
if (state.instantApp) {
|
installType = PackageProto.UserInfoProto.INSTANT_APP_INSTALL;
|
} else if (state.installed) {
|
installType = PackageProto.UserInfoProto.FULL_APP_INSTALL;
|
} else {
|
installType = PackageProto.UserInfoProto.NOT_INSTALLED_FOR_USER;
|
}
|
proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
|
proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
|
proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags);
|
proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
|
if (state.suspended) {
|
proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage);
|
}
|
proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped);
|
proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched);
|
proto.write(PackageProto.UserInfoProto.ENABLED_STATE, state.enabled);
|
proto.write(
|
PackageProto.UserInfoProto.LAST_DISABLED_APP_CALLER,
|
state.lastDisableAppCaller);
|
proto.end(userToken);
|
}
|
}
|
|
void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
|
PackageUserState userState = modifyUserState(userId);
|
userState.harmfulAppWarning = harmfulAppWarning;
|
}
|
|
String getHarmfulAppWarning(int userId) {
|
PackageUserState userState = readUserState(userId);
|
return userState.harmfulAppWarning;
|
}
|
|
protected PackageSettingBase updateFrom(PackageSettingBase other) {
|
super.copyFrom(other);
|
this.parentPackageName = other.parentPackageName;
|
this.childPackageNames = other.childPackageNames;
|
this.codePath = other.codePath;
|
this.codePathString = other.codePathString;
|
this.resourcePath = other.resourcePath;
|
this.resourcePathString = other.resourcePathString;
|
this.usesStaticLibraries = other.usesStaticLibraries;
|
this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
|
this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
|
this.primaryCpuAbiString = other.primaryCpuAbiString;
|
this.secondaryCpuAbiString = other.secondaryCpuAbiString;
|
this.cpuAbiOverrideString = other.cpuAbiOverrideString;
|
this.timeStamp = other.timeStamp;
|
this.firstInstallTime = other.firstInstallTime;
|
this.lastUpdateTime = other.lastUpdateTime;
|
this.versionCode = other.versionCode;
|
this.uidError = other.uidError;
|
this.signatures = other.signatures;
|
this.installPermissionsFixed = other.installPermissionsFixed;
|
this.keySetData = other.keySetData;
|
this.installerPackageName = other.installerPackageName;
|
this.isOrphaned = other.isOrphaned;
|
this.volumeUuid = other.volumeUuid;
|
this.categoryHint = other.categoryHint;
|
this.updateAvailable = other.updateAvailable;
|
this.verificationInfo = other.verificationInfo;
|
|
if (mOldCodePaths != null) {
|
if (other.mOldCodePaths != null) {
|
mOldCodePaths.clear();
|
mOldCodePaths.addAll(other.mOldCodePaths);
|
} else {
|
mOldCodePaths = null;
|
}
|
}
|
mUserState.clear();
|
for (int i = 0; i < other.mUserState.size(); i++) {
|
mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i));
|
}
|
return this;
|
}
|
}
|