diff options
author | Todd Kennedy <toddke@google.com> | 2015-11-09 15:36:43 +0000 |
---|---|---|
committer | Todd Kennedy <toddke@google.com> | 2015-11-09 15:36:43 +0000 |
commit | 18bc3305e43a040018add48c97cfbc7b3d9ecf7c (patch) | |
tree | b197aa8db1111391d607eea953a1c397d66ae8ee /cmds/pm | |
parent | ec059d839de6d061085dcfd85ce6565dcf4d5b69 (diff) |
Revert "Move 'un/install' to cmd"
This reverts commit ec059d839de6d061085dcfd85ce6565dcf4d5b69.
Change-Id: Ieaa1373e96fb4cc20aa41c3159518bd9e86c572b
Diffstat (limited to 'cmds/pm')
-rw-r--r-- | cmds/pm/src/com/android/commands/pm/Pm.java | 517 |
1 files changed, 504 insertions, 13 deletions
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index eb7c71295617..031bdbd1f222 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -28,13 +28,24 @@ 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; @@ -45,13 +56,26 @@ 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"; @@ -82,7 +106,7 @@ public final class Pm { System.exit(exitCode); } - public int run(String[] args) throws RemoteException { + public int run(String[] args) throws IOException, RemoteException { boolean validCommand = false; if (args.length < 1) { return showUsage(); @@ -118,19 +142,19 @@ public final class Pm { } if ("install-create".equals(op)) { - return runInstall(); + return runInstallCreate(); } if ("install-write".equals(op)) { - return runInstall(); + return runInstallWrite(); } if ("install-commit".equals(op)) { - return runInstall(); + return runInstallCommit(); } if ("install-abandon".equals(op) || "install-destroy".equals(op)) { - return runInstall(); + return runInstallAbandon(); } if ("set-installer".equals(op)) { @@ -275,10 +299,6 @@ public final class Pm { return -1; } - private int runInstall() { - return runShellCommand("package", mArgs); - } - /** * Execute the list sub-command. * @@ -297,10 +317,6 @@ 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(); @@ -354,6 +370,49 @@ 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; @@ -543,6 +602,316 @@ 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(); @@ -711,6 +1080,80 @@ 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; @@ -1056,6 +1499,54 @@ 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<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; |