Move 'un/install' to cmd
Move the implementation of the install variants and uninstall to the cmd
command. Additionally, make two other important changes: 1) replace calls
to the legacy PackageManager#installPackageAsUser with the PackageInstaller
2) allow streaming package bits for 'pm install'
Change-Id: Ia49dac0ccd6470f9d1c1964bdeb3c0b22b856075
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 031bdbd..eb7c712 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -28,24 +28,13 @@
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.pm.VerificationParams;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -56,26 +45,13 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
-import libcore.io.IoUtils;
-
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.SizedInputStream;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
public final class Pm {
private static final String TAG = "Pm";
@@ -106,7 +82,7 @@
System.exit(exitCode);
}
- public int run(String[] args) throws IOException, RemoteException {
+ public int run(String[] args) throws RemoteException {
boolean validCommand = false;
if (args.length < 1) {
return showUsage();
@@ -142,19 +118,19 @@
}
if ("install-create".equals(op)) {
- return runInstallCreate();
+ return runInstall();
}
if ("install-write".equals(op)) {
- return runInstallWrite();
+ return runInstall();
}
if ("install-commit".equals(op)) {
- return runInstallCommit();
+ return runInstall();
}
if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
- return runInstallAbandon();
+ return runInstall();
}
if ("set-installer".equals(op)) {
@@ -299,6 +275,10 @@
return -1;
}
+ private int runInstall() {
+ return runShellCommand("package", mArgs);
+ }
+
/**
* Execute the list sub-command.
*
@@ -317,6 +297,10 @@
return runShellCommand("package", mArgs);
}
+ private int runUninstall() {
+ return runShellCommand("package", mArgs);
+ }
+
private int runPath() {
int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
@@ -370,49 +354,6 @@
}
}
- /**
- * Converts a failure code into a string by using reflection to find a matching constant
- * in PackageManager.
- */
- private String installFailureToString(LocalPackageInstallObserver obs) {
- final int result = obs.result;
- Field[] fields = PackageManager.class.getFields();
- for (Field f: fields) {
- if (f.getType() == int.class) {
- int modifiers = f.getModifiers();
- // only look at public final static fields.
- if (((modifiers & Modifier.FINAL) != 0) &&
- ((modifiers & Modifier.PUBLIC) != 0) &&
- ((modifiers & Modifier.STATIC) != 0)) {
- String fieldName = f.getName();
- if (fieldName.startsWith("INSTALL_FAILED_") ||
- fieldName.startsWith("INSTALL_PARSE_FAILED_")) {
- // get the int value and compare it to result.
- try {
- if (result == f.getInt(null)) {
- StringBuilder sb = new StringBuilder(64);
- sb.append(fieldName);
- if (obs.extraPermission != null) {
- sb.append(" perm=");
- sb.append(obs.extraPermission);
- }
- if (obs.extraPackage != null) {
- sb.append(" pkg=" + obs.extraPackage);
- }
- return sb.toString();
- }
- } catch (IllegalAccessException e) {
- // this shouldn't happen since we only look for public static fields.
- }
- }
- }
- }
- }
-
- // couldn't find a matching constant? return the value
- return Integer.toString(result);
- }
-
// pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
private int runSetAppLink() {
int userId = UserHandle.USER_SYSTEM;
@@ -602,316 +543,6 @@
}
}
- private int runInstall() {
- int installFlags = 0;
- int userId = UserHandle.USER_ALL;
- String installerPackageName = null;
-
- String opt;
-
- String originatingUriString = null;
- String referrer = null;
- String abi = null;
-
- while ((opt=nextOption()) != null) {
- if (opt.equals("-l")) {
- installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- } else if (opt.equals("-r")) {
- installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- } else if (opt.equals("-i")) {
- installerPackageName = nextOptionData();
- if (installerPackageName == null) {
- System.err.println("Error: no value specified for -i");
- return 1;
- }
- } else if (opt.equals("-t")) {
- installFlags |= PackageManager.INSTALL_ALLOW_TEST;
- } else if (opt.equals("-s")) {
- // Override if -s option is specified.
- installFlags |= PackageManager.INSTALL_EXTERNAL;
- } else if (opt.equals("-f")) {
- // Override if -s option is specified.
- installFlags |= PackageManager.INSTALL_INTERNAL;
- } else if (opt.equals("-d")) {
- installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
- } else if (opt.equals("-g")) {
- installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
- } else if (opt.equals("--originating-uri")) {
- originatingUriString = nextOptionData();
- if (originatingUriString == null) {
- System.err.println("Error: must supply argument for --originating-uri");
- return 1;
- }
- } else if (opt.equals("--referrer")) {
- referrer = nextOptionData();
- if (referrer == null) {
- System.err.println("Error: must supply argument for --referrer");
- return 1;
- }
- } else if (opt.equals("--abi")) {
- abi = checkAbiArgument(nextOptionData());
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- userId = translateUserId(userId, "runInstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- installFlags |= PackageManager.INSTALL_ALL_USERS;
- }
-
- final Uri verificationURI;
- final Uri originatingURI;
- final Uri referrerURI;
-
- if (originatingUriString != null) {
- originatingURI = Uri.parse(originatingUriString);
- } else {
- originatingURI = null;
- }
-
- if (referrer != null) {
- referrerURI = Uri.parse(referrer);
- } else {
- referrerURI = null;
- }
-
- // Populate apkURI, must be present
- final String apkFilePath = nextArg();
- System.err.println("\tpkg: " + apkFilePath);
- if (apkFilePath == null) {
- System.err.println("Error: no package specified");
- return 1;
- }
-
- // Populate verificationURI, optionally present
- final String verificationFilePath = nextArg();
- if (verificationFilePath != null) {
- System.err.println("\tver: " + verificationFilePath);
- verificationURI = Uri.fromFile(new File(verificationFilePath));
- } else {
- verificationURI = null;
- }
-
- LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
- try {
- VerificationParams verificationParams = new VerificationParams(verificationURI,
- originatingURI, referrerURI, VerificationParams.NO_UID, null);
-
- mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
- installerPackageName, verificationParams, abi, userId);
-
- synchronized (obs) {
- while (!obs.finished) {
- try {
- obs.wait();
- } catch (InterruptedException e) {
- }
- }
- if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
- System.out.println("Success");
- return 0;
- } else {
- System.err.println("Failure ["
- + installFailureToString(obs)
- + "]");
- return 1;
- }
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- }
-
- /**
- * @param userId The user id to be translated.
- * @param logContext Optional human readable text to provide some context in error log.
- * @return Translated concrete user id. This will include USER_ALL.
- */
- private int translateUserId(int userId, String logContext) {
- return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, true, true, logContext, "pm command");
- }
-
- private int runInstallCreate() throws RemoteException {
- int userId = UserHandle.USER_ALL;
- String installerPackageName = null;
-
- final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("-l")) {
- params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
- } else if (opt.equals("-r")) {
- params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- } else if (opt.equals("-i")) {
- installerPackageName = nextArg();
- if (installerPackageName == null) {
- throw new IllegalArgumentException("Missing installer package");
- }
- } else if (opt.equals("-t")) {
- params.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
- } else if (opt.equals("-s")) {
- params.installFlags |= PackageManager.INSTALL_EXTERNAL;
- } else if (opt.equals("-f")) {
- params.installFlags |= PackageManager.INSTALL_INTERNAL;
- } else if (opt.equals("-d")) {
- params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
- } else if (opt.equals("-g")) {
- params.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
- } else if (opt.equals("--originating-uri")) {
- params.originatingUri = Uri.parse(nextOptionData());
- } else if (opt.equals("--referrer")) {
- params.referrerUri = Uri.parse(nextOptionData());
- } else if (opt.equals("-p")) {
- params.mode = SessionParams.MODE_INHERIT_EXISTING;
- params.appPackageName = nextOptionData();
- if (params.appPackageName == null) {
- throw new IllegalArgumentException("Missing inherit package name");
- }
- } else if (opt.equals("-S")) {
- params.setSize(Long.parseLong(nextOptionData()));
- } else if (opt.equals("--abi")) {
- params.abiOverride = checkAbiArgument(nextOptionData());
- } else if (opt.equals("--user")) {
- userId = Integer.parseInt(nextOptionData());
- } else if (opt.equals("--install-location")) {
- params.installLocation = Integer.parseInt(nextOptionData());
- } else if (opt.equals("--force-uuid")) {
- params.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
- params.volumeUuid = nextOptionData();
- if ("internal".equals(params.volumeUuid)) {
- params.volumeUuid = null;
- }
- } else {
- throw new IllegalArgumentException("Unknown option " + opt);
- }
- }
-
- userId = translateUserId(userId, "runInstallCreate");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- params.installFlags |= PackageManager.INSTALL_ALL_USERS;
- }
-
- final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
-
- // NOTE: adb depends on parsing this string
- System.out.println("Success: created install session [" + sessionId + "]");
- return 0;
- }
-
- private int runInstallWrite() throws IOException, RemoteException {
- long sizeBytes = -1;
-
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("-S")) {
- sizeBytes = Long.parseLong(nextOptionData());
- } else {
- throw new IllegalArgumentException("Unknown option: " + opt);
- }
- }
-
- final int sessionId = Integer.parseInt(nextArg());
- final String splitName = nextArg();
-
- String path = nextArg();
- if ("-".equals(path)) {
- path = null;
- } else if (path != null) {
- final File file = new File(path);
- if (file.isFile()) {
- sizeBytes = file.length();
- }
- }
-
- final SessionInfo info = mInstaller.getSessionInfo(sessionId);
-
- PackageInstaller.Session session = null;
- InputStream in = null;
- OutputStream out = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
-
- if (path != null) {
- in = new FileInputStream(path);
- } else {
- in = new SizedInputStream(System.in, sizeBytes);
- }
- out = session.openWrite(splitName, 0, sizeBytes);
-
- int total = 0;
- byte[] buffer = new byte[65536];
- int c;
- while ((c = in.read(buffer)) != -1) {
- total += c;
- out.write(buffer, 0, c);
-
- if (info.sizeBytes > 0) {
- final float fraction = ((float) c / (float) info.sizeBytes);
- session.addProgress(fraction);
- }
- }
- session.fsync(out);
-
- System.out.println("Success: streamed " + total + " bytes");
- return 0;
- } finally {
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(session);
- }
- }
-
- private int runInstallCommit() throws RemoteException {
- final int sessionId = Integer.parseInt(nextArg());
-
- PackageInstaller.Session session = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- session.commit(receiver.getIntentSender());
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- System.out.println("Success");
- return 0;
- } else {
- Log.e(TAG, "Failure details: " + result.getExtras());
- System.err.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return 1;
- }
- } finally {
- IoUtils.closeQuietly(session);
- }
- }
-
- private int runInstallAbandon() throws RemoteException {
- final int sessionId = Integer.parseInt(nextArg());
-
- PackageInstaller.Session session = null;
- try {
- session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
- session.abandon();
- System.out.println("Success");
- return 0;
- } finally {
- IoUtils.closeQuietly(session);
- }
- }
-
private int runSetInstaller() throws RemoteException {
final String targetPackage = nextArg();
final String installerPackageName = nextArg();
@@ -1080,80 +711,6 @@
}
}
- private int runUninstall() throws RemoteException {
- int flags = 0;
- int userId = UserHandle.USER_ALL;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-k")) {
- flags |= PackageManager.DELETE_KEEP_DATA;
- } else if (opt.equals("--user")) {
- String param = nextArg();
- if (isNumber(param)) {
- userId = Integer.parseInt(param);
- } else {
- showUsage();
- System.err.println("Error: Invalid user: " + param);
- return 1;
- }
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return 1;
- }
- }
-
- String pkg = nextArg();
- if (pkg == null) {
- System.err.println("Error: no package specified");
- return showUsage();
- }
-
- userId = translateUserId(userId, "runUninstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- flags |= PackageManager.DELETE_ALL_USERS;
- } else {
- PackageInfo info;
- try {
- info = mPm.getPackageInfo(pkg, 0, userId);
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- return 1;
- }
- if (info == null) {
- System.err.println("Failure - not installed for " + userId);
- return 1;
- }
- final boolean isSystem =
- (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- // If we are being asked to delete a system app for just one
- // user set flag so it disables rather than reverting to system
- // version of the app.
- if (isSystem) {
- flags |= PackageManager.DELETE_SYSTEM_APP;
- }
- }
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
- receiver.getIntentSender(), userId);
-
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- System.out.println("Success");
- return 0;
- } else {
- Log.e(TAG, "Failure details: " + result.getExtras());
- System.err.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return 1;
- }
- }
-
static class ClearDataObserver extends IPackageDataObserver.Stub {
boolean finished;
boolean result;
@@ -1499,54 +1056,6 @@
return 1;
}
- private static String checkAbiArgument(String abi) {
- if (TextUtils.isEmpty(abi)) {
- throw new IllegalArgumentException("Missing ABI argument");
- }
-
- if ("-".equals(abi)) {
- return abi;
- }
-
- final String[] supportedAbis = Build.SUPPORTED_ABIS;
- for (String supportedAbi : supportedAbis) {
- if (supportedAbi.equals(abi)) {
- return abi;
- }
- }
-
- throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
- }
-
- private static class LocalIntentReceiver {
- private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
-
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public int send(int code, Intent intent, String resolvedType,
- IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- try {
- mResult.offer(intent, 5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- return 0;
- }
- };
-
- public IntentSender getIntentSender() {
- return new IntentSender((IIntentSender) mLocalSender);
- }
-
- public Intent getResult() {
- try {
- return mResult.take();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
private String nextOption() {
if (mNextArg >= mArgs.length) {
return null;
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index 73c2c80..cad482b 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -19,8 +19,11 @@
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
+import java.io.BufferedInputStream;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.InputStream;
import java.io.PrintWriter;
/**
@@ -43,6 +46,7 @@
private FastPrintWriter mOutPrintWriter;
private FastPrintWriter mErrPrintWriter;
+ private InputStream mInputStream;
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) {
@@ -111,6 +115,13 @@
return mErrPrintWriter;
}
+ public InputStream getInputStream() {
+ if (mInputStream == null) {
+ mInputStream = new BufferedInputStream(new FileInputStream(mIn));
+ }
+ return mInputStream;
+ }
+
/**
* Return the next option on the command line -- that is an argument that
* starts with '-'. If the next argument is not an option, null is returned.
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c259ac2..d7176fd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1,28 +1,68 @@
+/*
+ * Copyright (C) 2015 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 android.app.ActivityManager;
import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.text.TextUtils;
+import com.android.internal.util.SizedInputStream;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.WeakHashMap;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
class PackageManagerShellCommand extends ShellCommand {
final IPackageManager mInterface;
@@ -42,8 +82,21 @@
final PrintWriter pw = getOutPrintWriter();
try {
switch(cmd) {
+ case "install":
+ return runInstall();
+ case "install-abandon":
+ case "install-destroy":
+ return runInstallAbandon();
+ case "install-commit":
+ return runInstallCommit();
+ case "install-create":
+ return runInstallCreate();
+ case "install-write":
+ return runInstallWrite();
case "list":
return runList();
+ case "uninstall":
+ return runUninstall();
default:
return handleDefaultCommands(cmd);
}
@@ -53,6 +106,65 @@
return -1;
}
+ private int runInstall() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final InstallParams params = makeInstallParams();
+ final int sessionId = doCreateSession(params.sessionParams,
+ params.installerPackageName, params.userId);
+
+ final String inPath = getNextArg();
+ if (inPath == null && params.sessionParams.sizeBytes == 0) {
+ pw.println("Error: must either specify a package size or an APK file");
+ return 1;
+ }
+ if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk") != 0) {
+ return 1;
+ }
+ if (doCommitSession(sessionId) != 0) {
+ return 1;
+ }
+ return 0;
+ }
+
+ private int runInstallAbandon() throws RemoteException {
+ final int sessionId = Integer.parseInt(getNextArg());
+ return doAbandonSession(sessionId);
+ }
+
+ private int runInstallCommit() throws RemoteException {
+ final int sessionId = Integer.parseInt(getNextArg());
+ return doCommitSession(sessionId);
+ }
+
+ private int runInstallCreate() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final InstallParams installParams = makeInstallParams();
+ final int sessionId = doCreateSession(installParams.sessionParams,
+ installParams.installerPackageName, installParams.userId);
+
+ // NOTE: adb depends on parsing this string
+ pw.println("Success: created install session [" + sessionId + "]");
+ return 0;
+ }
+
+ private int runInstallWrite() throws RemoteException {
+ long sizeBytes = -1;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("-S")) {
+ sizeBytes = Long.parseLong(getNextArg());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+
+ final int sessionId = Integer.parseInt(getNextArg());
+ final String splitName = getNextArg();
+ final String path = getNextArg();
+ return doWriteSession(sessionId, path, sizeBytes, splitName);
+ }
+
private int runList() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final String type = getNextArg();
@@ -363,6 +475,279 @@
return 0;
}
+ private int runUninstall() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int flags = 0;
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-k":
+ flags |= PackageManager.DELETE_KEEP_DATA;
+ break;
+ case "--user":
+ userId = Integer.parseInt(getNextArg());
+ break;
+ default:
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+
+ String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+
+ userId = translateUserId(userId, "runUninstall");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ flags |= PackageManager.DELETE_ALL_USERS;
+ } else {
+ final PackageInfo info = mInterface.getPackageInfo(packageName, 0, userId);
+ if (info == null) {
+ pw.println("Failure - not installed for " + userId);
+ return 1;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ // If we are being asked to delete a system app for just one
+ // user set flag so it disables rather than reverting to system
+ // version of the app.
+ if (isSystem) {
+ flags |= PackageManager.DELETE_SYSTEM_APP;
+ }
+ }
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ mInterface.getPackageInstaller().uninstall(packageName, null /*callerPackageName*/, flags,
+ receiver.getIntentSender(), userId);
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ pw.println("Success");
+ return 0;
+ } else {
+ pw.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ return 1;
+ }
+ }
+
+ private static class InstallParams {
+ SessionParams sessionParams;
+ String installerPackageName;
+ int userId = UserHandle.USER_ALL;
+ }
+
+ private InstallParams makeInstallParams() {
+ final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ final InstallParams params = new InstallParams();
+ params.sessionParams = sessionParams;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-l":
+ sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
+ break;
+ case "-r":
+ sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ break;
+ case "-i":
+ params.installerPackageName = getNextArg();
+ if (params.installerPackageName == null) {
+ throw new IllegalArgumentException("Missing installer package");
+ }
+ break;
+ case "-t":
+ sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
+ break;
+ case "-s":
+ sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
+ break;
+ case "-f":
+ sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
+ break;
+ case "-d":
+ sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ break;
+ case "-g":
+ sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
+ break;
+ case "--originating-uri":
+ sessionParams.originatingUri = Uri.parse(getNextArg());
+ break;
+ case "--referrer":
+ sessionParams.referrerUri = Uri.parse(getNextArg());
+ break;
+ case "-p":
+ sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
+ sessionParams.appPackageName = getNextArg();
+ if (sessionParams.appPackageName == null) {
+ throw new IllegalArgumentException("Missing inherit package name");
+ }
+ break;
+ case "-S":
+ sessionParams.setSize(Long.parseLong(getNextArg()));
+ break;
+ case "--abi":
+ sessionParams.abiOverride = checkAbiArgument(getNextArg());
+ break;
+ case "--user":
+ params.userId = Integer.parseInt(getNextArg());
+ break;
+ case "--install-location":
+ sessionParams.installLocation = Integer.parseInt(getNextArg());
+ break;
+ case "--force-uuid":
+ sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
+ sessionParams.volumeUuid = getNextArg();
+ if ("internal".equals(sessionParams.volumeUuid)) {
+ sessionParams.volumeUuid = null;
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown option " + opt);
+ }
+ }
+ return params;
+ }
+
+ private static String checkAbiArgument(String abi) {
+ if (TextUtils.isEmpty(abi)) {
+ throw new IllegalArgumentException("Missing ABI argument");
+ }
+
+ if ("-".equals(abi)) {
+ return abi;
+ }
+
+ final String[] supportedAbis = Build.SUPPORTED_ABIS;
+ for (String supportedAbi : supportedAbis) {
+ if (supportedAbi.equals(abi)) {
+ return abi;
+ }
+ }
+
+ throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
+ }
+
+ private int translateUserId(int userId, String logContext) {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, true, true, logContext, "pm command");
+ }
+
+ private int doCreateSession(SessionParams params, String installerPackageName, int userId)
+ throws RemoteException {
+ userId = translateUserId(userId, "runInstallCreate");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ params.installFlags |= PackageManager.INSTALL_ALL_USERS;
+ }
+
+ final int sessionId = mInterface.getPackageInstaller()
+ .createSession(params, installerPackageName, userId);
+ return sessionId;
+ }
+
+ private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName)
+ throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ if ("-".equals(inPath)) {
+ inPath = null;
+ } else if (inPath != null) {
+ final File file = new File(inPath);
+ if (file.isFile()) {
+ sizeBytes = file.length();
+ }
+ }
+
+ final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
+
+ PackageInstaller.Session session = null;
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
+ if (inPath != null) {
+ in = new FileInputStream(inPath);
+ } else {
+ in = new SizedInputStream(getInputStream(), sizeBytes);
+ }
+ out = session.openWrite(splitName, 0, sizeBytes);
+
+ int total = 0;
+ byte[] buffer = new byte[65536];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ total += c;
+ out.write(buffer, 0, c);
+
+ if (info.sizeBytes > 0) {
+ final float fraction = ((float) c / (float) info.sizeBytes);
+ session.addProgress(fraction);
+ }
+ }
+ session.fsync(out);
+
+ pw.println("Success: streamed " + total + " bytes");
+ return 0;
+ } catch (IOException e) {
+ pw.println("Error: failed to write; " + e.getMessage());
+ return 1;
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int doCommitSession(int sessionId) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ session.commit(receiver.getIntentSender());
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ pw.println("Success");
+ } else {
+ pw.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ pw.println("Failure details: " + result.getExtras());
+ }
+ return status;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
+ private int doAbandonSession(int sessionId) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+ session.abandon();
+ pw.println("Success");
+ return 0;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
private void doListPermissions(ArrayList<String> groupList, boolean groups, boolean labels,
boolean summary, int startProtectionLevel, int endProtectionLevel)
throws RemoteException {
@@ -525,5 +910,34 @@
pw.println(" -u: list only the permissions users will see");
pw.println("");
}
+
+ private static class LocalIntentReceiver {
+ private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public int send(int code, Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ try {
+ mResult.offer(intent, 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return 0;
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+
+ public Intent getResult() {
+ try {
+ return mResult.take();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
}