From ec059d839de6d061085dcfd85ce6565dcf4d5b69 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Tue, 3 Nov 2015 17:08:55 -0800 Subject: [PATCH] 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 --- cmds/pm/src/com/android/commands/pm/Pm.java | 517 +-------------------- core/java/android/os/ShellCommand.java | 11 + .../server/pm/PackageManagerShellCommand.java | 414 +++++++++++++++++ 3 files changed, 438 insertions(+), 504 deletions(-) diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 031bdbd1f222..eb7c71295617 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.ActivityManagerNative; 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.ServiceManager; 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 @@ public final class Pm { 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 @@ public final class Pm { } 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 @@ public final class Pm { return -1; } + private int runInstall() { + return runShellCommand("package", mArgs); + } + /** * Execute the list sub-command. * @@ -317,6 +297,10 @@ public final class Pm { 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 @@ public final class Pm { } } - /** - * 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 @@ public final class Pm { } } - 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 @@ public final class Pm { } } - 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 @@ public final class Pm { 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 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 73c2c804bdf1..cad482b6bab9 100644 --- a/core/java/android/os/ShellCommand.java +++ b/core/java/android/os/ShellCommand.java @@ -19,8 +19,11 @@ package android.os; 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 @@ public abstract class ShellCommand { 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 @@ public abstract class ShellCommand { 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 c259ac2b2fec..d7176fd0776b 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 groupList, boolean groups, boolean labels, boolean summary, int startProtectionLevel, int endProtectionLevel) throws RemoteException { @@ -525,5 +910,34 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" -u: list only the permissions users will see"); pw.println(""); } + + private static class LocalIntentReceiver { + private final SynchronousQueue 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); + } + } + } } -- 2.11.0