Merge changes from topic "apex_adb"
* changes: Add MATCH_APEX flag to getInstalledPackages. Rename APEX installPackage method to stagePackage. Initial support for installing APEX via adb.
This commit is contained in:
@@ -675,6 +675,7 @@ java_defaults {
|
|||||||
],
|
],
|
||||||
|
|
||||||
static_libs: [
|
static_libs: [
|
||||||
|
"apex_aidl_interface-java",
|
||||||
"framework-protos",
|
"framework-protos",
|
||||||
"android.hidl.base-V1.0-java",
|
"android.hidl.base-V1.0-java",
|
||||||
"android.hardware.cas-V1.0-java",
|
"android.hardware.cas-V1.0-java",
|
||||||
|
|||||||
@@ -11012,6 +11012,7 @@ package android.content.pm {
|
|||||||
field public int[] gids;
|
field public int[] gids;
|
||||||
field public int installLocation;
|
field public int installLocation;
|
||||||
field public android.content.pm.InstrumentationInfo[] instrumentation;
|
field public android.content.pm.InstrumentationInfo[] instrumentation;
|
||||||
|
field public boolean isApex;
|
||||||
field public long lastUpdateTime;
|
field public long lastUpdateTime;
|
||||||
field public java.lang.String packageName;
|
field public java.lang.String packageName;
|
||||||
field public android.content.pm.PermissionInfo[] permissions;
|
field public android.content.pm.PermissionInfo[] permissions;
|
||||||
@@ -11390,6 +11391,7 @@ package android.content.pm {
|
|||||||
field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
|
field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
|
||||||
field public static final int INSTALL_REASON_USER = 4; // 0x4
|
field public static final int INSTALL_REASON_USER = 4; // 0x4
|
||||||
field public static final int MATCH_ALL = 131072; // 0x20000
|
field public static final int MATCH_ALL = 131072; // 0x20000
|
||||||
|
field public static final int MATCH_APEX = 1073741824; // 0x40000000
|
||||||
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
|
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
|
||||||
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
|
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
|
||||||
field public static final int MATCH_DIRECT_BOOT_UNAWARE = 262144; // 0x40000
|
field public static final int MATCH_DIRECT_BOOT_UNAWARE = 262144; // 0x40000
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package android.content.pm;
|
|||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.UnsupportedAppUsage;
|
import android.annotation.UnsupportedAppUsage;
|
||||||
|
import android.apex.ApexInfo;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
@@ -390,6 +391,11 @@ public class PackageInfo implements Parcelable {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public String compileSdkVersionCodename;
|
public String compileSdkVersionCodename;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the package is an APEX package.
|
||||||
|
*/
|
||||||
|
public boolean isApex;
|
||||||
|
|
||||||
public PackageInfo() {
|
public PackageInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,6 +478,7 @@ public class PackageInfo implements Parcelable {
|
|||||||
} else {
|
} else {
|
||||||
dest.writeInt(0);
|
dest.writeInt(0);
|
||||||
}
|
}
|
||||||
|
dest.writeBoolean(isApex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<PackageInfo> CREATOR
|
public static final Parcelable.Creator<PackageInfo> CREATOR
|
||||||
@@ -533,7 +540,7 @@ public class PackageInfo implements Parcelable {
|
|||||||
if (hasSigningInfo != 0) {
|
if (hasSigningInfo != 0) {
|
||||||
signingInfo = SigningInfo.CREATOR.createFromParcel(source);
|
signingInfo = SigningInfo.CREATOR.createFromParcel(source);
|
||||||
}
|
}
|
||||||
|
isApex = source.readBoolean();
|
||||||
// The component lists were flattened with the redundant ApplicationInfo
|
// The component lists were flattened with the redundant ApplicationInfo
|
||||||
// instances omitted. Distribute the canonical one here as appropriate.
|
// instances omitted. Distribute the canonical one here as appropriate.
|
||||||
if (applicationInfo != null) {
|
if (applicationInfo != null) {
|
||||||
@@ -544,6 +551,15 @@ public class PackageInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public PackageInfo(ApexInfo apexInfo) {
|
||||||
|
packageName = apexInfo.packageName;
|
||||||
|
setLongVersionCode(apexInfo.versionCode);
|
||||||
|
isApex = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
|
private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
|
||||||
if (components != null) {
|
if (components != null) {
|
||||||
for (ComponentInfo ci : components) {
|
for (ComponentInfo ci : components) {
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ public abstract class PackageManager {
|
|||||||
MATCH_FACTORY_ONLY,
|
MATCH_FACTORY_ONLY,
|
||||||
MATCH_DEBUG_TRIAGED_MISSING,
|
MATCH_DEBUG_TRIAGED_MISSING,
|
||||||
MATCH_INSTANT,
|
MATCH_INSTANT,
|
||||||
|
MATCH_APEX,
|
||||||
GET_DISABLED_COMPONENTS,
|
GET_DISABLED_COMPONENTS,
|
||||||
GET_DISABLED_UNTIL_USED_COMPONENTS,
|
GET_DISABLED_UNTIL_USED_COMPONENTS,
|
||||||
GET_UNINSTALLED_PACKAGES,
|
GET_UNINSTALLED_PACKAGES,
|
||||||
@@ -530,6 +531,17 @@ public abstract class PackageManager {
|
|||||||
*/
|
*/
|
||||||
public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000;
|
public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link PackageInfo} flag: include APEX packages that are currently
|
||||||
|
* installed. In APEX terminology, this corresponds to packages that are
|
||||||
|
* currently active, i.e. mounted and available to other processes of the OS.
|
||||||
|
* In particular, this flag alone will not match APEX files that are staged
|
||||||
|
* for activation at next reboot.
|
||||||
|
* TODO(b/119767311): include uninstalled/inactive APEX if
|
||||||
|
* MATCH_UNINSTALLED_PACKAGES is set.
|
||||||
|
*/
|
||||||
|
public static final int MATCH_APEX = 0x40000000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
|
* Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
|
||||||
* resolving an intent that matches the {@code CrossProfileIntentFilter},
|
* resolving an intent that matches the {@code CrossProfileIntentFilter},
|
||||||
@@ -846,6 +858,14 @@ public abstract class PackageManager {
|
|||||||
*/
|
*/
|
||||||
public static final int INSTALL_VIRTUAL_PRELOAD = 0x00010000;
|
public static final int INSTALL_VIRTUAL_PRELOAD = 0x00010000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag parameter for {@link #installPackage} to indicate that this package
|
||||||
|
* is an APEX package
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final int INSTALL_APEX = 0x00020000;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
|
@IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
|
||||||
DONT_KILL_APP
|
DONT_KILL_APP
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ import android.os.Process;
|
|||||||
import android.os.RemoteCallbackList;
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SELinux;
|
import android.os.SELinux;
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.os.storage.StorageManager;
|
import android.os.storage.StorageManager;
|
||||||
@@ -646,8 +645,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Os.mkdir(stageDir.getAbsolutePath(), 0755);
|
Os.mkdir(stageDir.getAbsolutePath(), 0775);
|
||||||
Os.chmod(stageDir.getAbsolutePath(), 0755);
|
Os.chmod(stageDir.getAbsolutePath(), 0775);
|
||||||
} catch (ErrnoException e) {
|
} catch (ErrnoException e) {
|
||||||
// This purposefully throws if directory already exists
|
// This purposefully throws if directory already exists
|
||||||
throw new IOException("Failed to prepare session dir: " + stageDir, e);
|
throw new IOException("Failed to prepare session dir: " + stageDir, e);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import static com.android.server.pm.PackageInstallerService.prepareStageDir;
|
|||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.apex.IApexService;
|
||||||
import android.app.admin.DeviceAdminInfo;
|
import android.app.admin.DeviceAdminInfo;
|
||||||
import android.app.admin.DevicePolicyManagerInternal;
|
import android.app.admin.DevicePolicyManagerInternal;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -73,6 +74,7 @@ import android.os.ParcelableException;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.RevocableFileDescriptor;
|
import android.os.RevocableFileDescriptor;
|
||||||
|
import android.os.ServiceManager;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.storage.StorageManager;
|
import android.os.storage.StorageManager;
|
||||||
@@ -858,12 +860,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
resolveStageDirLocked();
|
resolveStageDirLocked();
|
||||||
|
|
||||||
mSealed = true;
|
mSealed = true;
|
||||||
|
|
||||||
// Verify that stage looks sane with respect to existing application.
|
|
||||||
// This currently only ensures packageName, versionCode, and certificate
|
|
||||||
// consistency.
|
|
||||||
try {
|
try {
|
||||||
validateInstallLocked(pkgInfo);
|
if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
|
||||||
|
validateApexInstallLocked(pkgInfo);
|
||||||
|
} else {
|
||||||
|
// Verify that stage looks sane with respect to existing application.
|
||||||
|
// This currently only ensures packageName, versionCode, and certificate
|
||||||
|
// consistency.
|
||||||
|
validateApkInstallLocked(pkgInfo);
|
||||||
|
}
|
||||||
} catch (PackageManagerException e) {
|
} catch (PackageManagerException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@@ -942,6 +947,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
Preconditions.checkNotNull(mSigningDetails);
|
Preconditions.checkNotNull(mSigningDetails);
|
||||||
Preconditions.checkNotNull(mResolvedBaseFile);
|
Preconditions.checkNotNull(mResolvedBaseFile);
|
||||||
|
|
||||||
|
if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
|
||||||
|
commitApexLocked();
|
||||||
|
} else {
|
||||||
|
commitApkLocked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private void commitApexLocked() throws PackageManagerException {
|
||||||
|
try {
|
||||||
|
IApexService apex = IApexService.Stub.asInterface(
|
||||||
|
ServiceManager.getService("apexservice"));
|
||||||
|
apex.stagePackage(mResolvedBaseFile.toString());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Convert all exceptions into package manager exceptions as only those are handled
|
||||||
|
// in the code above
|
||||||
|
throw new PackageManagerException(e);
|
||||||
|
} finally {
|
||||||
|
destroyInternal();
|
||||||
|
dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "APEX installed", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private void commitApkLocked() throws PackageManagerException {
|
||||||
if (needToAskForPermissionsLocked()) {
|
if (needToAskForPermissionsLocked()) {
|
||||||
// User needs to accept permissions; give installer an intent they
|
// User needs to accept permissions; give installer an intent they
|
||||||
// can use to involve user.
|
// can use to involve user.
|
||||||
@@ -1063,6 +1093,57 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
(params.installFlags & PackageManager.DONT_KILL_APP) != 0;
|
(params.installFlags & PackageManager.DONT_KILL_APP) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private void validateApexInstallLocked(@Nullable PackageInfo pkgInfo)
|
||||||
|
throws PackageManagerException {
|
||||||
|
mResolvedStagedFiles.clear();
|
||||||
|
mResolvedInheritedFiles.clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
resolveStageDirLocked();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
|
||||||
|
"Failed to resolve stage location", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
|
||||||
|
if (ArrayUtils.isEmpty(addedFiles)) {
|
||||||
|
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addedFiles.length > 1) {
|
||||||
|
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
|
||||||
|
"Only one APEX file at a time might be installed");
|
||||||
|
}
|
||||||
|
File addedFile = addedFiles[0];
|
||||||
|
final ApkLite apk;
|
||||||
|
try {
|
||||||
|
apk = PackageParser.parseApkLite(
|
||||||
|
addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
throw PackageManagerException.from(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
mPackageName = apk.packageName;
|
||||||
|
mVersionCode = apk.getLongVersionCode();
|
||||||
|
mSigningDetails = apk.signingDetails;
|
||||||
|
mResolvedBaseFile = addedFile;
|
||||||
|
|
||||||
|
assertApkConsistentLocked(String.valueOf(addedFile), apk);
|
||||||
|
|
||||||
|
if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
|
||||||
|
try {
|
||||||
|
// STOPSHIP: For APEX we should also implement proper APK Signature verification.
|
||||||
|
mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
|
||||||
|
pkgInfo.applicationInfo.sourceDir,
|
||||||
|
PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
|
||||||
|
} catch (PackageParserException e) {
|
||||||
|
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
|
||||||
|
"Couldn't obtain signatures from base APK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate install by confirming that all application packages are have
|
* Validate install by confirming that all application packages are have
|
||||||
* consistent package name, version code, and signing certificates.
|
* consistent package name, version code, and signing certificates.
|
||||||
@@ -1076,7 +1157,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
* {@link PackageManagerService}.
|
* {@link PackageManagerService}.
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
|
private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
|
||||||
throws PackageManagerException {
|
throws PackageManagerException {
|
||||||
mPackageName = null;
|
mPackageName = null;
|
||||||
mVersionCode = -1;
|
mVersionCode = -1;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO
|
|||||||
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
|
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
|
||||||
import static android.content.pm.PackageManager.MATCH_ALL;
|
import static android.content.pm.PackageManager.MATCH_ALL;
|
||||||
import static android.content.pm.PackageManager.MATCH_ANY_USER;
|
import static android.content.pm.PackageManager.MATCH_ANY_USER;
|
||||||
|
import static android.content.pm.PackageManager.MATCH_APEX;
|
||||||
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
|
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
|
||||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
|
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
|
||||||
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
|
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
|
||||||
@@ -123,6 +124,8 @@ import android.annotation.IntDef;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
|
import android.apex.ApexInfo;
|
||||||
|
import android.apex.IApexService;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.ActivityManagerInternal;
|
import android.app.ActivityManagerInternal;
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
@@ -7912,6 +7915,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
|
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
|
||||||
flags = updateFlagsForPackage(flags, userId, null);
|
flags = updateFlagsForPackage(flags, userId, null);
|
||||||
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
|
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
|
||||||
|
final boolean listApex = (flags & MATCH_APEX) != 0;
|
||||||
|
|
||||||
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
|
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
|
||||||
false /* requireFullPermission */, false /* checkShell */,
|
false /* requireFullPermission */, false /* checkShell */,
|
||||||
"get installed packages");
|
"get installed packages");
|
||||||
@@ -7950,7 +7955,22 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (listApex) {
|
||||||
|
final IApexService apex = IApexService.Stub.asInterface(
|
||||||
|
ServiceManager.getService("apexservice"));
|
||||||
|
if (apex != null) {
|
||||||
|
try {
|
||||||
|
final ApexInfo[] activePkgs = apex.getActivePackages();
|
||||||
|
for (ApexInfo apexInfo : activePkgs) {
|
||||||
|
list.add(new PackageInfo(apexInfo));
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Unable to retrieve packages from apexservice: " + e.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Unable to connect to apexservice for querying packages.");
|
||||||
|
}
|
||||||
|
}
|
||||||
return new ParceledListSlice<>(list);
|
return new ParceledListSlice<>(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -915,7 +915,10 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
pw.println("Error: must either specify a package size or an APK file");
|
pw.println("Error: must either specify a package size or an APK file");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
|
final boolean isApex =
|
||||||
|
(params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
|
||||||
|
String splitName = "base." + (isApex ? "apex" : "apk");
|
||||||
|
if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
|
||||||
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
|
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -2229,6 +2232,9 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
case "--force-sdk":
|
case "--force-sdk":
|
||||||
sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK;
|
sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK;
|
||||||
break;
|
break;
|
||||||
|
case "--apex":
|
||||||
|
sessionParams.installFlags |= PackageManager.INSTALL_APEX;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown option " + opt);
|
throw new IllegalArgumentException("Unknown option " + opt);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user