content: /*
   * 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 static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
  import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
  import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
  import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
  import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
  
  import android.accounts.IAccountManager;
  import android.app.ActivityManager;
  import android.app.ActivityManagerInternal;
  import android.app.role.IRoleManager;
  import android.app.role.RoleManager;
  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.FeatureInfo;
  import android.content.pm.IPackageDataObserver;
  import android.content.pm.IPackageInstaller;
  import android.content.pm.IPackageManager;
  import android.content.pm.InstrumentationInfo;
  import android.content.pm.ModuleInfo;
  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.PackageItemInfo;
  import android.content.pm.PackageManager;
  import android.content.pm.PackageManager.NameNotFoundException;
  import android.content.pm.PackageManagerInternal;
  import android.content.pm.PackageParser;
  import android.content.pm.PackageParser.ApkLite;
  import android.content.pm.PackageParser.PackageLite;
  import android.content.pm.PackageParser.PackageParserException;
  import android.content.pm.ParceledListSlice;
  import android.content.pm.PermissionGroupInfo;
  import android.content.pm.PermissionInfo;
  import android.content.pm.ResolveInfo;
  import android.content.pm.SuspendDialogInfo;
  import android.content.pm.UserInfo;
  import android.content.pm.VersionedPackage;
  import android.content.pm.dex.ArtManager;
  import android.content.pm.dex.DexMetadataHelper;
  import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
  import android.content.res.AssetManager;
  import android.content.res.Resources;
  import android.content.rollback.IRollbackManager;
  import android.content.rollback.PackageRollbackInfo;
  import android.content.rollback.RollbackInfo;
  import android.content.rollback.RollbackManager;
  import android.net.Uri;
  import android.os.Binder;
  import android.os.Build;
  import android.os.Bundle;
  import android.os.IBinder;
  import android.os.IUserManager;
  import android.os.ParcelFileDescriptor;
  import android.os.ParcelFileDescriptor.AutoCloseInputStream;
  import android.os.PersistableBundle;
  import android.os.Process;
  import android.os.RemoteCallback;
  import android.os.RemoteException;
  import android.os.ServiceManager;
  import android.os.ShellCommand;
  import android.os.SystemClock;
  import android.os.SystemProperties;
  import android.os.UserHandle;
  import android.os.UserManager;
  import android.os.storage.StorageManager;
  import android.system.ErrnoException;
  import android.system.Os;
  import android.text.TextUtils;
  import android.text.format.DateUtils;
  import android.util.ArraySet;
  import android.util.PrintWriterPrinter;
  
  import com.android.internal.content.PackageHelper;
  import com.android.internal.util.ArrayUtils;
  import com.android.server.LocalServices;
  import com.android.server.SystemConfig;
  
  import dalvik.system.DexFile;
  
  import libcore.io.IoUtils;
  import libcore.io.Streams;
  
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.net.URISyntaxException;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  import java.util.WeakHashMap;
  import java.util.concurrent.CompletableFuture;
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.LinkedBlockingQueue;
  import java.util.concurrent.TimeUnit;
  
  class PackageManagerShellCommand extends ShellCommand {
      /** Path for streaming APK content */
      private static final String STDIN_PATH = "-";
      /** Path where ART profiles snapshots are dumped for the shell user */
      private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
  
      final IPackageManager mInterface;
      final private WeakHashMap<String, Resources> mResourceCache =
              new WeakHashMap<String, Resources>();
      int mTargetUser;
      boolean mBrief;
      boolean mComponents;
      int mQueryFlags;
  
      PackageManagerShellCommand(PackageManagerService service) {
          mInterface = service;
      }
  
      @Override
      public int onCommand(String cmd) {
          if (cmd == null) {
              return handleDefaultCommands(cmd);
          }
  
          final PrintWriter pw = getOutPrintWriter();
          try {
              switch(cmd) {
                  case "path":
                      return runPath();
                  case "dump":
                      return runDump();
                  case "list":
                      return runList();
                  case "resolve-activity":
                      return runResolveActivity();
                  case "query-activities":
                      return runQueryIntentActivities();
                  case "query-services":
                      return runQueryIntentServices();
                  case "query-receivers":
                      return runQueryIntentReceivers();
                  case "install":
                      return runInstall();
                  case "install-abandon":
                  case "install-destroy":
                      return runInstallAbandon();
                  case "install-commit":
                      return runInstallCommit();
                  case "install-create":
                      return runInstallCreate();
                  case "install-remove":
                      return runInstallRemove();
                  case "install-write":
                      return runInstallWrite();
                  case "install-existing":
                      return runInstallExisting();
                  case "set-install-location":
                      return runSetInstallLocation();
                  case "get-install-location":
                      return runGetInstallLocation();
                  case "install-add-session":
                      return runInstallAddSession();
                  case "move-package":
                      return runMovePackage();
                  case "move-primary-storage":
                      return runMovePrimaryStorage();
                  case "compile":
                      return runCompile();
                  case "reconcile-secondary-dex-files":
                      return runreconcileSecondaryDexFiles();
                  case "force-dex-opt":
                      return runForceDexOpt();
                  case "bg-dexopt-job":
                      return runDexoptJob();
                  case "dump-profiles":
                      return runDumpProfiles();
                  case "snapshot-profile":
                      return runSnapshotProfile();
                  case "uninstall":
                      return runUninstall();
                  case "clear":
                      return runClear();
                  case "enable":
                      return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
                  case "disable":
                      return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
                  case "disable-user":
                      return runSetEnabledSetting(
                              PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
                  case "disable-until-used":
                      return runSetEnabledSetting(
                              PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
                  case "default-state":
                      return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
                  case "hide":
                      return runSetHiddenSetting(true);
                  case "unhide":
                      return runSetHiddenSetting(false);
                  case "suspend":
                      return runSuspend(true);
                  case "unsuspend":
                      return runSuspend(false);
                  case "grant":
                      return runGrantRevokePermission(true);
                  case "revoke":
                      return runGrantRevokePermission(false);
                  case "reset-permissions":
                      return runResetPermissions();
                  case "set-permission-enforced":
                      return runSetPermissionEnforced();
                  case "get-privapp-permissions":
                      return runGetPrivappPermissions();
                  case "get-privapp-deny-permissions":
                      return runGetPrivappDenyPermissions();
                  case "get-oem-permissions":
                      return runGetOemPermissions();
                  case "set-app-link":
                      return runSetAppLink();
                  case "get-app-link":
                      return runGetAppLink();
                  case "trim-caches":
                      return runTrimCaches();
                  case "create-user":
                      return runCreateUser();
                  case "remove-user":
                      return runRemoveUser();
                  case "set-user-restriction":
                      return runSetUserRestriction();
                  case "get-max-users":
                      return runGetMaxUsers();
                  case "get-max-running-users":
                      return runGetMaxRunningUsers();
                  case "set-home-activity":
                      return runSetHomeActivity();
                  case "set-installer":
                      return runSetInstaller();
                  case "get-instantapp-resolver":
                      return runGetInstantAppResolver();
                  case "has-feature":
                      return runHasFeature();
                  case "set-harmful-app-warning":
                      return runSetHarmfulAppWarning();
                  case "get-harmful-app-warning":
                      return runGetHarmfulAppWarning();
                  case "get-stagedsessions":
                      return getStagedSessions();
                  case "uninstall-system-updates":
                      return uninstallSystemUpdates();
                  case "rollback-app":
                      return runRollbackApp();
                  case "get-moduleinfo":
                      return runGetModuleInfo();
                  default: {
                      String nextArg = getNextArg();
                      if (nextArg == null) {
                          if (cmd.equalsIgnoreCase("-l")) {
                              return runListPackages(false);
                          } else if (cmd.equalsIgnoreCase("-lf")) {
                              return runListPackages(true);
                          }
                      } else if (getNextArg() == null) {
                          if (cmd.equalsIgnoreCase("-p")) {
                              return displayPackageFilePath(nextArg, UserHandle.USER_SYSTEM);
                          }
                      }
                      return handleDefaultCommands(cmd);
                  }
              }
          } catch (RemoteException e) {
              pw.println("Remote exception: " + e);
          }
          return -1;
      }
  
      /**
       * Shows module info
       *
       * Usage: get-moduleinfo [--all | --installed] [module-name]
       * Example: get-moduleinfo, get-moduleinfo --all, get-moduleinfo xyz
       */
      private int runGetModuleInfo() {
          final PrintWriter pw = getOutPrintWriter();
          int flags = 0;
  
          String opt;
          while ((opt = getNextOption()) != null) {
              switch (opt) {
                  case "--all":
                      flags |= PackageManager.MATCH_ALL;
                      break;
                  case "--installed":
                      break;
                  default:
                      pw.println("Error: Unknown option: " + opt);
                      return -1;
              }
          }
  
          String moduleName = getNextArg();
          try {
              if (moduleName != null) {
                  ModuleInfo m = mInterface.getModuleInfo(moduleName, flags);
                  pw.println(m.toString() + " packageName: " + m.getPackageName());
  
              } else {
                  List<ModuleInfo> modules = mInterface.getInstalledModules(flags);
                  for (ModuleInfo m: modules) {
                      pw.println(m.toString() + " packageName: " + m.getPackageName());
                  }
              }
          } catch (RemoteException e) {
              pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]");
              return -1;
          }
          return 1;
      }
  
      private int getStagedSessions() {
          final PrintWriter pw = getOutPrintWriter();
          try {
              List<SessionInfo> stagedSessionsList =
                      mInterface.getPackageInstaller().getStagedSessions().getList();
              for (SessionInfo session: stagedSessionsList) {
                  pw.println("appPackageName = " + session.getAppPackageName()
                          + "; sessionId = " + session.getSessionId()
                          + "; isStaged = " + session.isStaged()
                          + "; isStagedSessionReady = " + session.isStagedSessionReady()
                          + "; isStagedSessionApplied = " + session.isStagedSessionApplied()
                          + "; isStagedSessionFailed = " + session.isStagedSessionFailed() + ";");
              }
          } catch (RemoteException e) {
              pw.println("Failure ["
                      + e.getClass().getName() + " - "
                      + e.getMessage() + "]");
              return 0;
          }
          return 1;
      }
  
      private int uninstallSystemUpdates() {
          final PrintWriter pw = getOutPrintWriter();
          List<String> failedUninstalls = new LinkedList<>();
          try {
              final ParceledListSlice<ApplicationInfo> packages =
                      mInterface.getInstalledApplications(
                              PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
              final IPackageInstaller installer = mInterface.getPackageInstaller();
              List<ApplicationInfo> list = packages.getList();
              for (ApplicationInfo info : list) {
                  if (info.isUpdatedSystemApp()) {
                      pw.println("Uninstalling updates to " + info.packageName + "...");
                      final LocalIntentReceiver receiver = new LocalIntentReceiver();
                      installer.uninstall(new VersionedPackage(info.packageName,
                                      info.versionCode), null /*callerPackageName*/, 0 /* flags */,
                              receiver.getIntentSender(), 0);
  
                      final Intent result = receiver.getResult();
                      final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                              PackageInstaller.STATUS_FAILURE);
                      if (status != PackageInstaller.STATUS_SUCCESS) {
                          failedUninstalls.add(info.packageName);
                      }
                  }
              }
          } catch (RemoteException e) {
              pw.println("Failure ["
                      + e.getClass().getName() + " - "
                      + e.getMessage() + "]");
              return 0;
          }
          if (!failedUninstalls.isEmpty()) {
              pw.println("Failure [Couldn't uninstall packages: "
                      + TextUtils.join(", ", failedUninstalls)
                      + "]");
              return 0;
          }
          pw.println("Success");
          return 1;
      }
  
      private int runRollbackApp() {
          final PrintWriter pw = getOutPrintWriter();
  
          final String packageName = getNextArgRequired();
          if (packageName == null) {
              pw.println("Error: package name not specified");
              return 1;
          }
  
          final LocalIntentReceiver receiver = new LocalIntentReceiver();
          try {
              IRollbackManager rm = IRollbackManager.Stub.asInterface(
                      ServiceManager.getService(Context.ROLLBACK_SERVICE));
  
              RollbackInfo rollback = null;
              for (RollbackInfo r : (List<RollbackInfo>) rm.getAvailableRollbacks().getList()) {
                  for (PackageRollbackInfo info : r.getPackages()) {
                      if (packageName.equals(info.getPackageName())) {
                          rollback = r;
                          break;
                      }
                  }
              }
  
              if (rollback == null) {
                  pw.println("No available rollbacks for: " + packageName);
                  return 1;
              }
  
              rm.commitRollback(rollback.getRollbackId(),
                      ParceledListSlice.<VersionedPackage>emptyList(),
                      "com.android.shell", receiver.getIntentSender());
          } catch (RemoteException re) {
              // Cannot happen.
          }
  
          final Intent result = receiver.getResult();
          final int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
                  RollbackManager.STATUS_FAILURE);
          if (status == RollbackManager.STATUS_SUCCESS) {
              pw.println("Success");
              return 0;
          } else {
              pw.println("Failure ["
                      + result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE) + "]");
              return 1;
          }
      }
  
      private void setParamsSize(InstallParams params, String inPath) {
          if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) {
              final ParcelFileDescriptor fd = openFileForSystem(inPath, "r");
              if (fd == null) {
                  getErrPrintWriter().println("Error: Can't open file: " + inPath);
                  throw new IllegalArgumentException("Error: Can't open file: " + inPath);
              }
              try {
                  ApkLite baseApk = PackageParser.parseApkLite(fd.getFileDescriptor(), inPath, 0);
                  PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
                          null, null);
                  params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
                          pkgLite, params.sessionParams.abiOverride, fd.getFileDescriptor()));
              } catch (PackageParserException | IOException e) {
                  getErrPrintWriter().println("Error: Failed to parse APK file: " + inPath);
                  throw new IllegalArgumentException(
                          "Error: Failed to parse APK file: " + inPath, e);
              } finally {
                  try {
                      fd.close();
                  } catch (IOException e) {
                  }
              }
          }
      }
      /**
       * Displays the package file for a package.
       * @param pckg
       */
      private int displayPackageFilePath(String pckg, int userId) throws RemoteException {
          PackageInfo info = mInterface.getPackageInfo(pckg, 0, userId);
          if (info != null && info.applicationInfo != null) {
              final PrintWriter pw = getOutPrintWriter();
              pw.print("package:");
              pw.println(info.applicationInfo.sourceDir);
              if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) {
                  for (String splitSourceDir : info.applicationInfo.splitSourceDirs) {
                      pw.print("package:");
                      pw.println(splitSourceDir);
                  }
              }
              return 0;
          }
          return 1;
      }
  
      private int runPath() throws RemoteException {
          int userId = UserHandle.USER_SYSTEM;
          String option = getNextOption();
          if (option != null && option.equals("--user")) {
              userId = UserHandle.parseUserArg(getNextArgRequired());
          }
  
          String pkg = getNextArgRequired();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified");
              return 1;
          }
          return displayPackageFilePath(pkg, userId);
      }
  
      private int runList() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final String type = getNextArg();
          if (type == null) {
              pw.println("Error: didn't specify type of data to list");
              return -1;
          }
          switch(type) {
              case "features":
                  return runListFeatures();
              case "instrumentation":
                  return runListInstrumentation();
              case "libraries":
                  return runListLibraries();
              case "package":
              case "packages":
                  return runListPackages(false /*showSourceDir*/);
              case "permission-groups":
                  return runListPermissionGroups();
              case "permissions":
                  return runListPermissions();
              case "users":
                  ServiceManager.getService("user").shellCommand(
                          getInFileDescriptor(), getOutFileDescriptor(), getErrFileDescriptor(),
                          new String[] { "list" }, getShellCallback(), adoptResultReceiver());
                  return 0;
          }
          pw.println("Error: unknown list type '" + type + "'");
          return -1;
      }
  
      private int runListFeatures() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final List<FeatureInfo> list = mInterface.getSystemAvailableFeatures().getList();
  
          // sort by name
          Collections.sort(list, new Comparator<FeatureInfo>() {
              public int compare(FeatureInfo o1, FeatureInfo o2) {
                  if (o1.name == o2.name) return 0;
                  if (o1.name == null) return -1;
                  if (o2.name == null) return 1;
                  return o1.name.compareTo(o2.name);
              }
          });
  
          final int count = (list != null) ? list.size() : 0;
          for (int p = 0; p < count; p++) {
              FeatureInfo fi = list.get(p);
              pw.print("feature:");
              if (fi.name != null) {
                  pw.print(fi.name);
                  if (fi.version > 0) {
                      pw.print("=");
                      pw.print(fi.version);
                  }
                  pw.println();
              } else {
                  pw.println("reqGlEsVersion=0x"
                          + Integer.toHexString(fi.reqGlEsVersion));
              }
          }
          return 0;
      }
  
      private int runListInstrumentation() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          boolean showSourceDir = false;
          String targetPackage = null;
  
          try {
              String opt;
              while ((opt = getNextArg()) != null) {
                  switch (opt) {
                      case "-f":
                          showSourceDir = true;
                          break;
                      default:
                          if (opt.charAt(0) != '-') {
                              targetPackage = opt;
                          } else {
                              pw.println("Error: Unknown option: " + opt);
                              return -1;
                          }
                          break;
                  }
              }
          } catch (RuntimeException ex) {
              pw.println("Error: " + ex.toString());
              return -1;
          }
  
          final List<InstrumentationInfo> list =
                  mInterface.queryInstrumentation(targetPackage, 0 /*flags*/).getList();
  
          // sort by target package
          Collections.sort(list, new Comparator<InstrumentationInfo>() {
              public int compare(InstrumentationInfo o1, InstrumentationInfo o2) {
                  return o1.targetPackage.compareTo(o2.targetPackage);
              }
          });
  
          final int count = (list != null) ? list.size() : 0;
          for (int p = 0; p < count; p++) {
              final InstrumentationInfo ii = list.get(p);
              pw.print("instrumentation:");
              if (showSourceDir) {
                  pw.print(ii.sourceDir);
                  pw.print("=");
              }
              final ComponentName cn = new ComponentName(ii.packageName, ii.name);
              pw.print(cn.flattenToShortString());
              pw.print(" (target=");
              pw.print(ii.targetPackage);
              pw.println(")");
          }
          return 0;
      }
  
      private int runListLibraries() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final List<String> list = new ArrayList<String>();
          final String[] rawList = mInterface.getSystemSharedLibraryNames();
          for (int i = 0; i < rawList.length; i++) {
              list.add(rawList[i]);
          }
  
          // sort by name
          Collections.sort(list, new Comparator<String>() {
              public int compare(String o1, String o2) {
                  if (o1 == o2) return 0;
                  if (o1 == null) return -1;
                  if (o2 == null) return 1;
                  return o1.compareTo(o2);
              }
          });
  
          final int count = (list != null) ? list.size() : 0;
          for (int p = 0; p < count; p++) {
              String lib = list.get(p);
              pw.print("library:");
              pw.println(lib);
          }
          return 0;
      }
  
      private int runListPackages(boolean showSourceDir) throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          int getFlags = 0;
          boolean listDisabled = false, listEnabled = false;
          boolean listSystem = false, listThirdParty = false;
          boolean listInstaller = false;
          boolean showUid = false;
          boolean showVersionCode = false;
          boolean listApexOnly = false;
          int uid = -1;
          int userId = UserHandle.USER_SYSTEM;
          try {
              String opt;
              while ((opt = getNextOption()) != null) {
                  switch (opt) {
                      case "-d":
                          listDisabled = true;
                          break;
                      case "-e":
                          listEnabled = true;
                          break;
                      case "-a":
                          getFlags |= PackageManager.MATCH_KNOWN_PACKAGES;
                          getFlags |= PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
                          break;
                      case "-f":
                          showSourceDir = true;
                          break;
                      case "-i":
                          listInstaller = true;
                          break;
                      case "-l":
                          // old compat
                          break;
                      case "-s":
                          listSystem = true;
                          break;
                      case "-U":
                          showUid = true;
                          break;
                      case "-u":
                          getFlags |= PackageManager.MATCH_UNINSTALLED_PACKAGES;
                          break;
                      case "-3":
                          listThirdParty = true;
                          break;
                      case "--show-versioncode":
                          showVersionCode = true;
                          break;
                      case "--apex-only":
                          getFlags |= PackageManager.MATCH_APEX;
                          listApexOnly = true;
                          break;
                      case "--user":
                          userId = UserHandle.parseUserArg(getNextArgRequired());
                          break;
                      case "--uid":
                          showUid = true;
                          uid = Integer.parseInt(getNextArgRequired());
                          break;
                      default:
                          pw.println("Error: Unknown option: " + opt);
                          return -1;
                  }
              }
          } catch (RuntimeException ex) {
              pw.println("Error: " + ex.toString());
              return -1;
          }
  
          final String filter = getNextArg();
  
          @SuppressWarnings("unchecked")
          final ParceledListSlice<PackageInfo> slice =
                  mInterface.getInstalledPackages(getFlags, userId);
          final List<PackageInfo> packages = slice.getList();
  
          final int count = packages.size();
          for (int p = 0; p < count; p++) {
              final PackageInfo info = packages.get(p);
              if (filter != null && !info.packageName.contains(filter)) {
                  continue;
              }
              final boolean isApex = info.isApex;
              if (uid != -1 && !isApex && info.applicationInfo.uid != uid) {
                  continue;
              }
  
              final boolean isSystem = !isApex &&
                      (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
              final boolean isEnabled = !isApex && info.applicationInfo.enabled;
              if ((!listDisabled || !isEnabled) &&
                      (!listEnabled || isEnabled) &&
                      (!listSystem || isSystem) &&
                      (!listThirdParty || !isSystem) &&
                      (!listApexOnly || isApex)) {
                  pw.print("package:");
                  if (showSourceDir && !isApex) {
                      pw.print(info.applicationInfo.sourceDir);
                      pw.print("=");
                  }
                  pw.print(info.packageName);
                  if (showVersionCode) {
                      pw.print(" versionCode:");
                      if (info.applicationInfo != null) {
                          pw.print(info.applicationInfo.longVersionCode);
                      } else {
                          pw.print(info.getLongVersionCode());
                      }
                  }
                  if (listInstaller) {
                      pw.print("  installer=");
                      pw.print(mInterface.getInstallerPackageName(info.packageName));
                  }
                  if (showUid && !isApex) {
                      pw.print(" uid:");
                      pw.print(info.applicationInfo.uid);
                  }
                  pw.println();
              }
          }
          return 0;
      }
  
      private int runListPermissionGroups() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0).getList();
  
          final int count = pgs.size();
          for (int p = 0; p < count ; p++) {
              final PermissionGroupInfo pgi = pgs.get(p);
              pw.print("permission group:");
              pw.println(pgi.name);
          }
          return 0;
      }
  
      private int runListPermissions() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          boolean labels = false;
          boolean groups = false;
          boolean userOnly = false;
          boolean summary = false;
          boolean dangerousOnly = false;
          String opt;
          while ((opt = getNextOption()) != null) {
              switch (opt) {
                  case "-d":
                      dangerousOnly = true;
                      break;
                  case "-f":
                      labels = true;
                      break;
                  case "-g":
                      groups = true;
                      break;
                  case "-s":
                      groups = true;
                      labels = true;
                      summary = true;
                      break;
                  case "-u":
                      userOnly = true;
                      break;
                  default:
                      pw.println("Error: Unknown option: " + opt);
                      return 1;
              }
          }
  
          final ArrayList<String> groupList = new ArrayList<String>();
          if (groups) {
              final List<PermissionGroupInfo> infos =
                      mInterface.getAllPermissionGroups(0 /*flags*/).getList();
              final int count = infos.size();
              for (int i = 0; i < count; i++) {
                  groupList.add(infos.get(i).name);
              }
              groupList.add(null);
          } else {
              final String grp = getNextArg();
              groupList.add(grp);
          }
  
          if (dangerousOnly) {
              pw.println("Dangerous Permissions:");
              pw.println("");
              doListPermissions(groupList, groups, labels, summary,
                      PermissionInfo.PROTECTION_DANGEROUS,
                      PermissionInfo.PROTECTION_DANGEROUS);
              if (userOnly) {
                  pw.println("Normal Permissions:");
                  pw.println("");
                  doListPermissions(groupList, groups, labels, summary,
                          PermissionInfo.PROTECTION_NORMAL,
                          PermissionInfo.PROTECTION_NORMAL);
              }
          } else if (userOnly) {
              pw.println("Dangerous and Normal Permissions:");
              pw.println("");
              doListPermissions(groupList, groups, labels, summary,
                      PermissionInfo.PROTECTION_NORMAL,
                      PermissionInfo.PROTECTION_DANGEROUS);
          } else {
              pw.println("All Permissions:");
              pw.println("");
              doListPermissions(groupList, groups, labels, summary,
                      -10000, 10000);
          }
          return 0;
      }
  
      private Intent parseIntentAndUser() throws URISyntaxException {
          mTargetUser = UserHandle.USER_CURRENT;
          mBrief = false;
          mComponents = false;
          Intent intent = Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
              @Override
              public boolean handleOption(String opt, ShellCommand cmd) {
                  if ("--user".equals(opt)) {
                      mTargetUser = UserHandle.parseUserArg(cmd.getNextArgRequired());
                      return true;
                  } else if ("--brief".equals(opt)) {
                      mBrief = true;
                      return true;
                  } else if ("--components".equals(opt)) {
                      mComponents = true;
                      return true;
                  } else if ("--query-flags".equals(opt)) {
                      mQueryFlags = Integer.decode(cmd.getNextArgRequired());
                      return true;
                  }
                  return false;
              }
          });
          mTargetUser = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                  Binder.getCallingUid(), mTargetUser, false, false, null, null);
          return intent;
      }
  
      private void printResolveInfo(PrintWriterPrinter pr, String prefix, ResolveInfo ri,
              boolean brief, boolean components) {
          if (brief || components) {
              final ComponentName comp;
              if (ri.activityInfo != null) {
                  comp = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
              } else if (ri.serviceInfo != null) {
                  comp = new ComponentName(ri.serviceInfo.packageName, ri.serviceInfo.name);
              } else if (ri.providerInfo != null) {
                  comp = new ComponentName(ri.providerInfo.packageName, ri.providerInfo.name);
              } else {
                  comp = null;
              }
              if (comp != null) {
                  if (!components) {
                      pr.println(prefix + "priority=" + ri.priority
                              + " preferredOrder=" + ri.preferredOrder
                              + " match=0x" + Integer.toHexString(ri.match)
                              + " specificIndex=" + ri.specificIndex
                              + " isDefault=" + ri.isDefault);
                  }
                  pr.println(prefix + comp.flattenToShortString());
                  return;
              }
          }
          ri.dump(pr, prefix);
      }
  
      private int runResolveActivity() {
          Intent intent;
          try {
              intent = parseIntentAndUser();
          } catch (URISyntaxException e) {
              throw new RuntimeException(e.getMessage(), e);
          }
          try {
              ResolveInfo ri = mInterface.resolveIntent(intent, intent.getType(), mQueryFlags,
                      mTargetUser);
              PrintWriter pw = getOutPrintWriter();
              if (ri == null) {
                  pw.println("No activity found");
              } else {
                  PrintWriterPrinter pr = new PrintWriterPrinter(pw);
                  printResolveInfo(pr, "", ri, mBrief, mComponents);
              }
          } catch (RemoteException e) {
              throw new RuntimeException("Failed calling service", e);
          }
          return 0;
      }
  
      private int runQueryIntentActivities() {
          Intent intent;
          try {
              intent = parseIntentAndUser();
          } catch (URISyntaxException e) {
              throw new RuntimeException(e.getMessage(), e);
          }
          try {
              List<ResolveInfo> result = mInterface.queryIntentActivities(intent, intent.getType(),
                      mQueryFlags, mTargetUser).getList();
              PrintWriter pw = getOutPrintWriter();
              if (result == null || result.size() <= 0) {
                  pw.println("No activities found");
              } else {
                  if (!mComponents) {
                      pw.print(result.size()); pw.println(" activities found:");
                      PrintWriterPrinter pr = new PrintWriterPrinter(pw);
                      for (int i = 0; i < result.size(); i++) {
                          pw.print("  Activity #"); pw.print(i); pw.println(":");
                          printResolveInfo(pr, "    ", result.get(i), mBrief, mComponents);
                      }
                  } else {
                      PrintWriterPrinter pr = new PrintWriterPrinter(pw);
                      for (int i = 0; i < result.size(); i++) {
                          printResolveInfo(pr, "", result.get(i), mBrief, mComponents);
                      }
                  }
              }
          } catch (RemoteException e) {
              throw new RuntimeException("Failed calling service", e);
          }
          return 0;
      }
  
      private int runQueryIntentServices() {
          Intent intent;
          try {
              intent = parseIntentAndUser();
          } catch (URISyntaxException e) {
              throw new RuntimeException(e.getMessage(), e);
          }
          try {
              List<ResolveInfo> result = mInterface.queryIntentServices(intent, intent.getType(),
                      mQueryFlags, mTargetUser).getList();
              PrintWriter pw = getOutPrintWriter();
              if (result == null || result.size() <= 0) {
                  pw.println("No services found");
              } else {
                  if (!mComponents) {
                      pw.print(result.size()); pw.println(" services found:");
                      PrintWriterPrinter pr = new PrintWriterPrinter(pw);
                      for (int i = 0; i < result.size(); i++) {
                          pw.print("  Service #"); pw.print(i); pw.println(":");
                          printResolveInfo(pr, "    ", result.get(i), mBrief, mComponents);
                      }
                  } else {
                      PrintWriterPrinter pr = new PrintWriterPrinter(pw);
                      for (int i = 0; i < result.size(); i++) {
                          printResolveInfo(pr, "", result.get(i), mBrief, mComponents);
                      }
                  }
              }
          } catch (RemoteException e) {
              throw new RuntimeException("Failed calling service", e);
          }
          return 0;
      }
  
      private int runQueryIntentReceivers() {
          Intent intent;
          try {
              intent = parseIntentAndUser();
          } catch (URISyntaxException e) {
              throw new RuntimeException(e.getMessage(), e);
          }
          try {
              List<ResolveInfo> result = mInterface.queryIntentReceivers(intent, intent.getType(),
                      mQueryFlags, mTargetUser).getList();
              PrintWriter pw = getOutPrintWriter();
              if (result == null || result.size() <= 0) {
                  pw.println("No receivers found");
              } else {
                  if (!mComponents) {
                      pw.print(result.size()); pw.println(" receivers found:");
                      PrintWriterPrinter pr = new PrintWriterPrinter(pw);
                      for (int i = 0; i < result.size(); i++) {
                          pw.print("  Receiver #"); pw.print(i); pw.println(":");
                          printResolveInfo(pr, "    ", result.get(i), mBrief, mComponents);
                      }
                  } else {
                      PrintWriterPrinter pr = new PrintWriterPrinter(pw);
                      for (int i = 0; i < result.size(); i++) {
                          printResolveInfo(pr, "", result.get(i), mBrief, mComponents);
                      }
                  }
              }
          } catch (RemoteException e) {
              throw new RuntimeException("Failed calling service", e);
          }
          return 0;
      }
  
      private int runInstall() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final InstallParams params = makeInstallParams();
          final String inPath = getNextArg();
  
          setParamsSize(params, inPath);
          final int sessionId = doCreateSession(params.sessionParams,
                  params.installerPackageName, params.userId);
          boolean abandonSession = true;
          try {
              if (inPath == null && params.sessionParams.sizeBytes == -1) {
                  pw.println("Error: must either specify a package size or an APK file");
                  return 1;
              }
              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) {
                  return 1;
              }
              if (doCommitSession(sessionId, false /*logSuccess*/)
                      != PackageInstaller.STATUS_SUCCESS) {
                  return 1;
              }
              abandonSession = false;
              pw.println("Success");
              return 0;
          } finally {
              if (abandonSession) {
                  try {
                      doAbandonSession(sessionId, false /*logSuccess*/);
                  } catch (Exception ignore) {
                  }
              }
          }
      }
  
      private int runInstallAbandon() throws RemoteException {
          final int sessionId = Integer.parseInt(getNextArg());
          return doAbandonSession(sessionId, true /*logSuccess*/);
      }
  
      private int runInstallCommit() throws RemoteException {
          final int sessionId = Integer.parseInt(getNextArg());
          return doCommitSession(sessionId, true /*logSuccess*/);
      }
  
      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 doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
      }
  
      private int runInstallAddSession() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final int parentSessionId = Integer.parseInt(getNextArg());
  
          List<Integer> otherSessionIds = new ArrayList<>();
          String opt;
          while ((opt = getNextArg()) != null) {
              otherSessionIds.add(Integer.parseInt(opt));
          }
          if (otherSessionIds.size() == 0) {
              pw.println("Error: At least two sessions are required.");
              return 1;
          }
          return doInstallAddSession(parentSessionId, ArrayUtils.convertToIntArray(otherSessionIds),
                  true /*logSuccess*/);
      }
  
      private int runInstallRemove() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
  
          final int sessionId = Integer.parseInt(getNextArg());
  
          final String splitName = getNextArg();
          if (splitName == null) {
              pw.println("Error: split name not specified");
              return 1;
          }
          return doRemoveSplit(sessionId, splitName, true /*logSuccess*/);
      }
  
      private int runInstallExisting() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          int userId = UserHandle.USER_SYSTEM;
          int installFlags = PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
          String opt;
          boolean waitTillComplete = false;
          while ((opt = getNextOption()) != null) {
              switch (opt) {
                  case "--user":
                      userId = UserHandle.parseUserArg(getNextArgRequired());
                      break;
                  case "--ephemeral":
                  case "--instant":
                      installFlags |= PackageManager.INSTALL_INSTANT_APP;
                      installFlags &= ~PackageManager.INSTALL_FULL_APP;
                      break;
                  case "--full":
                      installFlags &= ~PackageManager.INSTALL_INSTANT_APP;
                      installFlags |= PackageManager.INSTALL_FULL_APP;
                      break;
                  case "--wait":
                      waitTillComplete = true;
                      break;
                  case "--restrict-permissions":
                      installFlags &= ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
                      break;
                  default:
                      pw.println("Error: Unknown option: " + opt);
                      return 1;
              }
          }
  
          final String packageName = getNextArg();
          if (packageName == null) {
              pw.println("Error: package name not specified");
              return 1;
          }
  
          int installReason = PackageManager.INSTALL_REASON_UNKNOWN;
          try {
              if (waitTillComplete) {
                  final LocalIntentReceiver receiver = new LocalIntentReceiver();
                  final IPackageInstaller installer = mInterface.getPackageInstaller();
                  pw.println("Installing package " + packageName + " for user: " + userId);
                  installer.installExistingPackage(packageName, installFlags, installReason,
                          receiver.getIntentSender(), userId, null);
                  final Intent result = receiver.getResult();
                  final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                          PackageInstaller.STATUS_FAILURE);
                  pw.println("Received intent for package install");
                  return status == PackageInstaller.STATUS_SUCCESS ? 0 : 1;
              }
  
              final int res = mInterface.installExistingPackageAsUser(packageName, userId,
                      installFlags, installReason, null);
              if (res == PackageManager.INSTALL_FAILED_INVALID_URI) {
                  throw new NameNotFoundException("Package " + packageName + " doesn't exist");
              }
              pw.println("Package " + packageName + " installed for user: " + userId);
              return 0;
          } catch (RemoteException | NameNotFoundException e) {
              pw.println(e.toString());
              return 1;
          }
      }
  
      private int runSetInstallLocation() throws RemoteException {
          int loc;
  
          String arg = getNextArg();
          if (arg == null) {
              getErrPrintWriter().println("Error: no install location specified.");
              return 1;
          }
          try {
              loc = Integer.parseInt(arg);
          } catch (NumberFormatException e) {
              getErrPrintWriter().println("Error: install location has to be a number.");
              return 1;
          }
          if (!mInterface.setInstallLocation(loc)) {
              getErrPrintWriter().println("Error: install location has to be a number.");
              return 1;
          }
          return 0;
      }
  
      private int runGetInstallLocation() throws RemoteException {
          int loc = mInterface.getInstallLocation();
          String locStr = "invalid";
          if (loc == PackageHelper.APP_INSTALL_AUTO) {
              locStr = "auto";
          } else if (loc == PackageHelper.APP_INSTALL_INTERNAL) {
              locStr = "internal";
          } else if (loc == PackageHelper.APP_INSTALL_EXTERNAL) {
              locStr = "external";
          }
          getOutPrintWriter().println(loc + "[" + locStr + "]");
          return 0;
      }
  
      public int runMovePackage() throws RemoteException {
          final String packageName = getNextArg();
          if (packageName == null) {
              getErrPrintWriter().println("Error: package name not specified");
              return 1;
          }
          String volumeUuid = getNextArg();
          if ("internal".equals(volumeUuid)) {
              volumeUuid = null;
          }
  
          final int moveId = mInterface.movePackage(packageName, volumeUuid);
  
          int status = mInterface.getMoveStatus(moveId);
          while (!PackageManager.isMoveStatusFinished(status)) {
              SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
              status = mInterface.getMoveStatus(moveId);
          }
  
          if (status == PackageManager.MOVE_SUCCEEDED) {
              getOutPrintWriter().println("Success");
              return 0;
          } else {
              getErrPrintWriter().println("Failure [" + status + "]");
              return 1;
          }
      }
  
      public int runMovePrimaryStorage() throws RemoteException {
          String volumeUuid = getNextArg();
          if ("internal".equals(volumeUuid)) {
              volumeUuid = null;
          }
  
          final int moveId = mInterface.movePrimaryStorage(volumeUuid);
  
          int status = mInterface.getMoveStatus(moveId);
          while (!PackageManager.isMoveStatusFinished(status)) {
              SystemClock.sleep(DateUtils.SECOND_IN_MILLIS);
              status = mInterface.getMoveStatus(moveId);
          }
  
          if (status == PackageManager.MOVE_SUCCEEDED) {
              getOutPrintWriter().println("Success");
              return 0;
          } else {
              getErrPrintWriter().println("Failure [" + status + "]");
              return 1;
          }
      }
  
      private int runCompile() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          boolean checkProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
          boolean forceCompilation = false;
          boolean allPackages = false;
          boolean clearProfileData = false;
          String compilerFilter = null;
          String compilationReason = null;
          String checkProfilesRaw = null;
          boolean secondaryDex = false;
          String split = null;
          boolean compileLayouts = false;
  
          String opt;
          while ((opt = getNextOption()) != null) {
              switch (opt) {
                  case "-a":
                      allPackages = true;
                      break;
                  case "-c":
                      clearProfileData = true;
                      break;
                  case "-f":
                      forceCompilation = true;
                      break;
                  case "-m":
                      compilerFilter = getNextArgRequired();
                      break;
                  case "-r":
                      compilationReason = getNextArgRequired();
                      break;
                  case "--compile-layouts":
                      compileLayouts = true;
                      break;
                  case "--check-prof":
                      checkProfilesRaw = getNextArgRequired();
                      break;
                  case "--reset":
                      forceCompilation = true;
                      clearProfileData = true;
                      compilationReason = "install";
                      break;
                  case "--secondary-dex":
                      secondaryDex = true;
                      break;
                  case "--split":
                      split = getNextArgRequired();
                      break;
                  default:
                      pw.println("Error: Unknown option: " + opt);
                      return 1;
              }
          }
  
          if (checkProfilesRaw != null) {
              if ("true".equals(checkProfilesRaw)) {
                  checkProfiles = true;
              } else if ("false".equals(checkProfilesRaw)) {
                  checkProfiles = false;
              } else {
                  pw.println("Invalid value for \"--check-prof\". Expected \"true\" or \"false\".");
                  return 1;
              }
          }
  
          final boolean compilerFilterGiven = compilerFilter != null;
          final boolean compilationReasonGiven = compilationReason != null;
          // Make sure exactly one of -m, -r, or --compile-layouts is given.
          if ((!compilerFilterGiven && !compilationReasonGiven && !compileLayouts)
              || (!compilerFilterGiven && compilationReasonGiven && compileLayouts)
              || (compilerFilterGiven && !compilationReasonGiven && compileLayouts)
              || (compilerFilterGiven && compilationReasonGiven && !compileLayouts)
              || (compilerFilterGiven && compilationReasonGiven && compileLayouts)) {
              pw.println("Must specify exactly one of compilation filter (\"-m\"), compilation " +
                      "reason (\"-r\"), or compile layouts (\"--compile-layouts\")");
              return 1;
          }
  
          if (allPackages && split != null) {
              pw.println("-a cannot be specified together with --split");
              return 1;
          }
  
          if (secondaryDex && split != null) {
              pw.println("--secondary-dex cannot be specified together with --split");
              return 1;
          }
  
          String targetCompilerFilter = null;
          if (compilerFilterGiven) {
              if (!DexFile.isValidCompilerFilter(compilerFilter)) {
                  pw.println("Error: \"" + compilerFilter +
                          "\" is not a valid compilation filter.");
                  return 1;
              }
              targetCompilerFilter = compilerFilter;
          }
          if (compilationReasonGiven) {
              int reason = -1;
              for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
                  if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals(
                          compilationReason)) {
                      reason = i;
                      break;
                  }
              }
              if (reason == -1) {
                  pw.println("Error: Unknown compilation reason: " + compilationReason);
                  return 1;
              }
              targetCompilerFilter =
                      PackageManagerServiceCompilerMapping.getCompilerFilterForReason(reason);
          }
  
  
          List<String> packageNames = null;
          if (allPackages) {
              packageNames = mInterface.getAllPackages();
          } else {
              String packageName = getNextArg();
              if (packageName == null) {
                  pw.println("Error: package name not specified");
                  return 1;
              }
              packageNames = Collections.singletonList(packageName);
          }
  
          List<String> failedPackages = new ArrayList<>();
          int index = 0;
          for (String packageName : packageNames) {
              if (clearProfileData) {
                  mInterface.clearApplicationProfileData(packageName);
              }
  
              if (allPackages) {
                  pw.println(++index + "/" + packageNames.size() + ": " + packageName);
                  pw.flush();
              }
  
              boolean result = true;
              if (compileLayouts) {
                  PackageManagerInternal internal = LocalServices.getService(
                          PackageManagerInternal.class);
                  result = internal.compileLayouts(packageName);
              } else {
                  result = secondaryDex
                      ? mInterface.performDexOptSecondary(packageName,
                              targetCompilerFilter, forceCompilation)
                      : mInterface.performDexOptMode(packageName,
                              checkProfiles, targetCompilerFilter, forceCompilation,
                              true /* bootComplete */, split);
              }
              if (!result) {
                  failedPackages.add(packageName);
              }
          }
  
          if (failedPackages.isEmpty()) {
              pw.println("Success");
              return 0;
          } else if (failedPackages.size() == 1) {
              pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled");
              return 1;
          } else {
              pw.print("Failure: the following packages could not be compiled: ");
              boolean is_first = true;
              for (String packageName : failedPackages) {
                  if (is_first) {
                      is_first = false;
                  } else {
                      pw.print(", ");
                  }
                  pw.print(packageName);
              }
              pw.println();
              return 1;
          }
      }
  
      private int runreconcileSecondaryDexFiles() throws RemoteException {
          String packageName = getNextArg();
          mInterface.reconcileSecondaryDexFiles(packageName);
          return 0;
      }
  
      public int runForceDexOpt() throws RemoteException {
          mInterface.forceDexOpt(getNextArgRequired());
          return 0;
      }
  
      private int runDexoptJob() throws RemoteException {
          String arg;
          List<String> packageNames = new ArrayList<>();
          while ((arg = getNextArg()) != null) {
              packageNames.add(arg);
          }
          boolean result = mInterface.runBackgroundDexoptJob(packageNames.isEmpty() ? null :
                  packageNames);
          getOutPrintWriter().println(result ? "Success" : "Failure");
          return result ? 0 : -1;
      }
  
      private int runDumpProfiles() throws RemoteException {
          String packageName = getNextArg();
          mInterface.dumpProfiles(packageName);
          return 0;
      }
  
      private int runSnapshotProfile() throws RemoteException {
          PrintWriter pw = getOutPrintWriter();
  
          // Parse the arguments
          final String packageName = getNextArg();
          final boolean isBootImage = "android".equals(packageName);
  
          String codePath = null;
          String opt;
          while ((opt = getNextArg()) != null) {
              switch (opt) {
                  case "--code-path":
                      if (isBootImage) {
                          pw.write("--code-path cannot be used for the boot image.");
                          return -1;
                      }
                      codePath = getNextArg();
                      break;
                  default:
                      pw.write("Unknown arg: " + opt);
                      return -1;
              }
          }
  
          // If no code path was explicitly requested, select the base code path.
          String baseCodePath = null;
          if (!isBootImage) {
              PackageInfo packageInfo = mInterface.getPackageInfo(packageName, /* flags */ 0,
                      /* userId */0);
              if (packageInfo == null) {
                  pw.write("Package not found " + packageName);
                  return -1;
              }
              baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
              if (codePath == null) {
                  codePath = baseCodePath;
              }
          }
  
          // Create the profile snapshot.
          final SnapshotRuntimeProfileCallback callback = new SnapshotRuntimeProfileCallback();
          // The calling package is needed to debug permission access.
          final String callingPackage = (Binder.getCallingUid() == Process.ROOT_UID)
                  ? "root" : "com.android.shell";
          final int profileType = isBootImage
                  ? ArtManager.PROFILE_BOOT_IMAGE : ArtManager.PROFILE_APPS;
          if (!mInterface.getArtManager().isRuntimeProfilingEnabled(profileType, callingPackage)) {
              pw.println("Error: Runtime profiling is not enabled");
              return -1;
          }
          mInterface.getArtManager().snapshotRuntimeProfile(profileType, packageName,
                  codePath, callback, callingPackage);
          if (!callback.waitTillDone()) {
              pw.println("Error: callback not called");
              return callback.mErrCode;
          }
  
          // Copy the snapshot profile to the output profile file.
          try (InputStream inStream = new AutoCloseInputStream(callback.mProfileReadFd)) {
              final String outputFileSuffix = isBootImage || Objects.equals(baseCodePath, codePath)
                      ? "" : ("-" + new File(codePath).getName());
              final String outputProfilePath =
                      ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + packageName + outputFileSuffix + ".prof";
              try (OutputStream outStream = new FileOutputStream(outputProfilePath)) {
                  Streams.copy(inStream, outStream);
              }
              // Give read permissions to the other group.
              Os.chmod(outputProfilePath, /*mode*/ 0644 );
          } catch (IOException | ErrnoException e) {
              pw.println("Error when reading the profile fd: " + e.getMessage());
              e.printStackTrace(pw);
              return -1;
          }
          return 0;
      }
  
      private static class SnapshotRuntimeProfileCallback
              extends ISnapshotRuntimeProfileCallback.Stub {
          private boolean mSuccess = false;
          private int mErrCode = -1;
          private ParcelFileDescriptor mProfileReadFd = null;
          private CountDownLatch mDoneSignal = new CountDownLatch(1);
  
          @Override
          public void onSuccess(ParcelFileDescriptor profileReadFd) {
              mSuccess = true;
              try {
                  // We need to dup the descriptor. We are in the same process as system server
                  // and we will be receiving the same object (which will be closed on the
                  // server side).
                  mProfileReadFd = profileReadFd.dup();
              } catch (IOException e) {
                  e.printStackTrace();
              }
              mDoneSignal.countDown();
          }
  
          @Override
          public void onError(int errCode) {
              mSuccess = false;
              mErrCode = errCode;
              mDoneSignal.countDown();
          }
  
          boolean waitTillDone() {
              boolean done = false;
              try {
                  // The time-out is an arbitrary large value. Since this is a local call the result
                  // will come very fast.
                  done = mDoneSignal.await(10000000, TimeUnit.MILLISECONDS);
              } catch (InterruptedException ignored) {
              }
              return done && mSuccess;
          }
      }
  
      private int runUninstall() throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          int flags = 0;
          int userId = UserHandle.USER_ALL;
          long versionCode = PackageManager.VERSION_CODE_HIGHEST;
  
          String opt;
          while ((opt = getNextOption()) != null) {
              switch (opt) {
                  case "-k":
                      flags |= PackageManager.DELETE_KEEP_DATA;
                      break;
                  case "--user":
                      userId = UserHandle.parseUserArg(getNextArgRequired());
                      break;
                  case "--versionCode":
                      versionCode = Long.parseLong(getNextArgRequired());
                      break;
                  default:
                      pw.println("Error: Unknown option: " + opt);
                      return 1;
              }
          }
  
          final String packageName = getNextArg();
          if (packageName == null) {
              pw.println("Error: package name not specified");
              return 1;
          }
  
          // if a split is specified, just remove it and not the whole package
          final String splitName = getNextArg();
          if (splitName != null) {
              return runRemoveSplit(packageName, splitName);
          }
  
          userId = translateUserId(userId, true /*allowAll*/, "runUninstall");
          final LocalIntentReceiver receiver = new LocalIntentReceiver();
          PackageManagerInternal internal = LocalServices.getService(PackageManagerInternal.class);
  
          if (internal.isApexPackage(packageName)) {
              internal.uninstallApex(packageName, versionCode, userId, receiver.getIntentSender());
          } else {
              if (userId == UserHandle.USER_ALL) {
                  userId = UserHandle.USER_SYSTEM;
                  flags |= PackageManager.DELETE_ALL_USERS;
              } else {
                  final PackageInfo info = mInterface.getPackageInfo(packageName,
                          PackageManager.MATCH_STATIC_SHARED_LIBRARIES, 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;
                  }
              }
  
              mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
                              versionCode), 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 int runRemoveSplit(String packageName, String splitName) throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          final SessionParams sessionParams = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
          sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
          sessionParams.appPackageName = packageName;
          final int sessionId =
                  doCreateSession(sessionParams, null /*installerPackageName*/, UserHandle.USER_ALL);
          boolean abandonSession = true;
          try {
              if (doRemoveSplit(sessionId, splitName, false /*logSuccess*/)
                      != PackageInstaller.STATUS_SUCCESS) {
                  return 1;
              }
              if (doCommitSession(sessionId, false /*logSuccess*/)
                      != PackageInstaller.STATUS_SUCCESS) {
                  return 1;
              }
              abandonSession = false;
              pw.println("Success");
              return 0;
          } finally {
              if (abandonSession) {
                  try {
                      doAbandonSession(sessionId, false /*logSuccess*/);
                  } catch (Exception ignore) {
                  }
              }
          }
      }
  
      static class ClearDataObserver extends IPackageDataObserver.Stub {
          boolean finished;
          boolean result;
  
          @Override
          public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
              synchronized (this) {
                  finished = true;
                  result = succeeded;
                  notifyAll();
              }
          }
      }
  
      private int runClear() throws RemoteException {
          int userId = UserHandle.USER_SYSTEM;
          String option = getNextOption();
          if (option != null && option.equals("--user")) {
              userId = UserHandle.parseUserArg(getNextArgRequired());
          }
  
          String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified");
              return 1;
          }
  
          ClearDataObserver obs = new ClearDataObserver();
          ActivityManager.getService().clearApplicationUserData(pkg, false, obs, userId);
          synchronized (obs) {
              while (!obs.finished) {
                  try {
                      obs.wait();
                  } catch (InterruptedException e) {
                  }
              }
          }
  
          if (obs.result) {
              getOutPrintWriter().println("Success");
              return 0;
          } else {
              getErrPrintWriter().println("Failed");
              return 1;
          }
      }
  
      private static String enabledSettingToString(int state) {
          switch (state) {
              case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
                  return "default";
              case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
                  return "enabled";
              case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
                  return "disabled";
              case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
                  return "disabled-user";
              case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
                  return "disabled-until-used";
          }
          return "unknown";
      }
  
      private int runSetEnabledSetting(int state) throws RemoteException {
          int userId = UserHandle.USER_SYSTEM;
          String option = getNextOption();
          if (option != null && option.equals("--user")) {
              userId = UserHandle.parseUserArg(getNextArgRequired());
          }
  
          String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package or component specified");
              return 1;
          }
          ComponentName cn = ComponentName.unflattenFromString(pkg);
          if (cn == null) {
              mInterface.setApplicationEnabledSetting(pkg, state, 0, userId,
                      "shell:" + android.os.Process.myUid());
              getOutPrintWriter().println("Package " + pkg + " new state: "
                      + enabledSettingToString(
                      mInterface.getApplicationEnabledSetting(pkg, userId)));
              return 0;
          } else {
              mInterface.setComponentEnabledSetting(cn, state, 0, userId);
              getOutPrintWriter().println("Component " + cn.toShortString() + " new state: "
                      + enabledSettingToString(
                      mInterface.getComponentEnabledSetting(cn, userId)));
              return 0;
          }
      }
  
      private int runSetHiddenSetting(boolean state) throws RemoteException {
          int userId = UserHandle.USER_SYSTEM;
          String option = getNextOption();
          if (option != null && option.equals("--user")) {
              userId = UserHandle.parseUserArg(getNextArgRequired());
          }
  
          String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package or component specified");
              return 1;
          }
          mInterface.setApplicationHiddenSettingAsUser(pkg, state, userId);
          getOutPrintWriter().println("Package " + pkg + " new hidden state: "
                  + mInterface.getApplicationHiddenSettingAsUser(pkg, userId));
          return 0;
      }
  
      private int runSuspend(boolean suspendedState) {
          final PrintWriter pw = getOutPrintWriter();
          int userId = UserHandle.USER_SYSTEM;
          String dialogMessage = null;
          final PersistableBundle appExtras = new PersistableBundle();
          final PersistableBundle launcherExtras = new PersistableBundle();
          String opt;
          while ((opt = getNextOption()) != null) {
              switch (opt) {
                  case "--user":
                      userId = UserHandle.parseUserArg(getNextArgRequired());
                      break;
                  case "--dialogMessage":
                      dialogMessage = getNextArgRequired();
                      break;
                  case "--ael":
                  case "--aes":
                  case "--aed":
                  case "--lel":
                  case "--les":
                  case "--led":
                      final String key = getNextArgRequired();
                      final String val = getNextArgRequired();
                      if (!suspendedState) {
                          break;
                      }
                      final PersistableBundle bundleToInsert =
                              opt.startsWith("--a") ? appExtras : launcherExtras;
                      switch (opt.charAt(4)) {
                          case 'l':
                              bundleToInsert.putLong(key, Long.valueOf(val));
                              break;
                          case 'd':
                              bundleToInsert.putDouble(key, Double.valueOf(val));
                              break;
                          case 's':
                              bundleToInsert.putString(key, val);
                              break;
                      }
                      break;
                  default:
                      pw.println("Error: Unknown option: " + opt);
                      return 1;
              }
          }
  
          final String packageName = getNextArg();
          if (packageName == null) {
              pw.println("Error: package name not specified");
              return 1;
          }
          final String callingPackage =
                  (Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell";
  
          final SuspendDialogInfo info;
          if (!TextUtils.isEmpty(dialogMessage)) {
              info = new SuspendDialogInfo.Builder()
                      .setMessage(dialogMessage)
                      .build();
          } else {
              info = null;
          }
          try {
              mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
                      appExtras, launcherExtras, info, callingPackage, userId);
              pw.println("Package " + packageName + " new suspended state: "
                      + mInterface.isPackageSuspendedForUser(packageName, userId));
              return 0;
          } catch (RemoteException | IllegalArgumentException e) {
              pw.println(e.toString());
              return 1;
          }
      }
  
      private int runGrantRevokePermission(boolean grant) throws RemoteException {
          int userId = UserHandle.USER_SYSTEM;
  
          String opt = null;
          while ((opt = getNextOption()) != null) {
              if (opt.equals("--user")) {
                  userId = UserHandle.parseUserArg(getNextArgRequired());
              }
          }
  
          String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified");
              return 1;
          }
          String perm = getNextArg();
          if (perm == null) {
              getErrPrintWriter().println("Error: no permission specified");
              return 1;
          }
  
          if (grant) {
              mInterface.grantRuntimePermission(pkg, perm, userId);
          } else {
              mInterface.revokeRuntimePermission(pkg, perm, userId);
          }
          return 0;
      }
  
      private int runResetPermissions() throws RemoteException {
          mInterface.resetRuntimePermissions();
          return 0;
      }
  
      private int runSetPermissionEnforced() throws RemoteException {
          final String permission = getNextArg();
          if (permission == null) {
              getErrPrintWriter().println("Error: no permission specified");
              return 1;
          }
          final String enforcedRaw = getNextArg();
          if (enforcedRaw == null) {
              getErrPrintWriter().println("Error: no enforcement specified");
              return 1;
          }
          mInterface.setPermissionEnforced(permission, Boolean.parseBoolean(enforcedRaw));
          return 0;
      }
  
      private boolean isVendorApp(String pkg) {
          try {
              final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
              return info != null && info.applicationInfo.isVendor();
          } catch (RemoteException e) {
              return false;
          }
      }
  
      private boolean isProductApp(String pkg) {
          try {
              final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
              return info != null && info.applicationInfo.isProduct();
          } catch (RemoteException e) {
              return false;
          }
      }
  
      private boolean isProductServicesApp(String pkg) {
          try {
              final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
              return info != null && info.applicationInfo.isProductServices();
          } catch (RemoteException e) {
              return false;
          }
      }
  
      private int runGetPrivappPermissions() {
          final String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified.");
              return 1;
          }
  
          ArraySet<String> privAppPermissions = null;
          if (isVendorApp(pkg)) {
              privAppPermissions = SystemConfig.getInstance().getVendorPrivAppPermissions(pkg);
          } else if (isProductApp(pkg)) {
              privAppPermissions = SystemConfig.getInstance().getProductPrivAppPermissions(pkg);
          } else if (isProductServicesApp(pkg)) {
              privAppPermissions = SystemConfig.getInstance()
                      .getProductServicesPrivAppPermissions(pkg);
          } else {
              privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg);
          }
  
          getOutPrintWriter().println(privAppPermissions == null
                  ? "{}" : privAppPermissions.toString());
          return 0;
      }
  
      private int runGetPrivappDenyPermissions() {
          final String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified.");
              return 1;
          }
  
          ArraySet<String> privAppPermissions = null;
          if (isVendorApp(pkg)) {
              privAppPermissions = SystemConfig.getInstance().getVendorPrivAppDenyPermissions(pkg);
          } else if (isProductApp(pkg)) {
              privAppPermissions = SystemConfig.getInstance().getProductPrivAppDenyPermissions(pkg);
          } else if (isProductServicesApp(pkg)) {
              privAppPermissions = SystemConfig.getInstance()
                      .getProductServicesPrivAppDenyPermissions(pkg);
          } else {
              privAppPermissions = SystemConfig.getInstance().getPrivAppDenyPermissions(pkg);
          }
  
          getOutPrintWriter().println(privAppPermissions == null
                  ? "{}" : privAppPermissions.toString());
          return 0;
      }
  
      private int runGetOemPermissions() {
          final String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified.");
              return 1;
          }
          final Map<String, Boolean> oemPermissions = SystemConfig.getInstance()
                  .getOemPermissions(pkg);
          if (oemPermissions == null || oemPermissions.isEmpty()) {
              getOutPrintWriter().println("{}");
          } else {
              oemPermissions.forEach((permission, granted) ->
                      getOutPrintWriter().println(permission + " granted:" + granted)
              );
          }
          return 0;
      }
  
      private String linkStateToString(int state) {
          switch (state) {
              case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED: return "undefined";
              case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask";
              case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always";
              case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never";
              case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask";
          }
          return "Unknown link state: " + state;
      }
  
      // pm set-app-link [--user USER_ID] PACKAGE {always|ask|always-ask|never|undefined}
      private int runSetAppLink() throws RemoteException {
          int userId = UserHandle.USER_SYSTEM;
  
          String opt;
          while ((opt = getNextOption()) != null) {
              if (opt.equals("--user")) {
                  userId = UserHandle.parseUserArg(getNextArgRequired());
              } else {
                  getErrPrintWriter().println("Error: unknown option: " + opt);
                  return 1;
              }
          }
  
          // Package name to act on; required
          final String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified.");
              return 1;
          }
  
          // State to apply; {always|ask|never|undefined}, required
          final String modeString = getNextArg();
          if (modeString == null) {
              getErrPrintWriter().println("Error: no app link state specified.");
              return 1;
          }
  
          final int newMode;
          switch (modeString.toLowerCase()) {
              case "undefined":
                  newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
                  break;
  
              case "always":
                  newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
                  break;
  
              case "ask":
                  newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
                  break;
  
              case "always-ask":
                  newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
                  break;
  
              case "never":
                  newMode = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
                  break;
  
              default:
                  getErrPrintWriter().println("Error: unknown app link state '" + modeString + "'");
                  return 1;
          }
  
          final PackageInfo info = mInterface.getPackageInfo(pkg, 0, userId);
          if (info == null) {
              getErrPrintWriter().println("Error: package " + pkg + " not found.");
              return 1;
          }
  
          if ((info.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
              getErrPrintWriter().println("Error: package " + pkg + " does not handle web links.");
              return 1;
          }
  
          if (!mInterface.updateIntentVerificationStatus(pkg, newMode, userId)) {
              getErrPrintWriter().println("Error: unable to update app link status for " + pkg);
              return 1;
          }
  
          return 0;
      }
  
      // pm get-app-link [--user USER_ID] PACKAGE
      private int runGetAppLink() throws RemoteException {
          int userId = UserHandle.USER_SYSTEM;
  
          String opt;
          while ((opt = getNextOption()) != null) {
              if (opt.equals("--user")) {
                  userId = UserHandle.parseUserArg(getNextArgRequired());
              } else {
                  getErrPrintWriter().println("Error: unknown option: " + opt);
                  return 1;
              }
          }
  
          // Package name to act on; required
          final String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified.");
              return 1;
          }
  
          final PackageInfo info = mInterface.getPackageInfo(pkg, 0, userId);
          if (info == null) {
              getErrPrintWriter().println("Error: package " + pkg + " not found.");
              return 1;
          }
  
          if ((info.applicationInfo.privateFlags
                  & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) == 0) {
              getErrPrintWriter().println("Error: package " + pkg + " does not handle web links.");
              return 1;
          }
  
          getOutPrintWriter().println(linkStateToString(
                  mInterface.getIntentVerificationStatus(pkg, userId)));
  
          return 0;
      }
  
      private int runTrimCaches() throws RemoteException {
          String size = getNextArg();
          if (size == null) {
              getErrPrintWriter().println("Error: no size specified");
              return 1;
          }
          long multiplier = 1;
          int len = size.length();
          char c = size.charAt(len - 1);
          if (c < '0' || c > '9') {
              if (c == 'K' || c == 'k') {
                  multiplier = 1024L;
              } else if (c == 'M' || c == 'm') {
                  multiplier = 1024L*1024L;
              } else if (c == 'G' || c == 'g') {
                  multiplier = 1024L*1024L*1024L;
              } else {
                  getErrPrintWriter().println("Invalid suffix: " + c);
                  return 1;
              }
              size = size.substring(0, len-1);
          }
          long sizeVal;
          try {
              sizeVal = Long.parseLong(size) * multiplier;
          } catch (NumberFormatException e) {
              getErrPrintWriter().println("Error: expected number at: " + size);
              return 1;
          }
          String volumeUuid = getNextArg();
          if ("internal".equals(volumeUuid)) {
              volumeUuid = null;
          }
          ClearDataObserver obs = new ClearDataObserver();
          mInterface.freeStorageAndNotify(volumeUuid, sizeVal,
                  StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED, obs);
          synchronized (obs) {
              while (!obs.finished) {
                  try {
                      obs.wait();
                  } catch (InterruptedException e) {
                  }
              }
          }
          return 0;
      }
  
      private static boolean isNumber(String s) {
          try {
              Integer.parseInt(s);
          } catch (NumberFormatException nfe) {
              return false;
          }
          return true;
      }
  
      public int runCreateUser() throws RemoteException {
          String name;
          int userId = -1;
          int flags = 0;
          String opt;
          boolean preCreateOnly = false;
          while ((opt = getNextOption()) != null) {
              if ("--profileOf".equals(opt)) {
                  userId = UserHandle.parseUserArg(getNextArgRequired());
              } else if ("--managed".equals(opt)) {
                  flags |= UserInfo.FLAG_MANAGED_PROFILE;
              } else if ("--restricted".equals(opt)) {
                  flags |= UserInfo.FLAG_RESTRICTED;
              } else if ("--ephemeral".equals(opt)) {
                  flags |= UserInfo.FLAG_EPHEMERAL;
              } else if ("--guest".equals(opt)) {
                  flags |= UserInfo.FLAG_GUEST;
              } else if ("--demo".equals(opt)) {
                  flags |= UserInfo.FLAG_DEMO;
              } else if ("--pre-create-only".equals(opt)) {
                  preCreateOnly = true;
              } else {
                  getErrPrintWriter().println("Error: unknown option " + opt);
                  return 1;
              }
          }
          String arg = getNextArg();
          if (arg == null && !preCreateOnly) {
              getErrPrintWriter().println("Error: no user name specified.");
              return 1;
          }
          if (arg != null && preCreateOnly) {
              getErrPrintWriter().println("Warning: name is ignored for pre-created users");
          }
  
          name = arg;
          UserInfo info;
          IUserManager um = IUserManager.Stub.asInterface(
                  ServiceManager.getService(Context.USER_SERVICE));
          IAccountManager accm = IAccountManager.Stub.asInterface(
                  ServiceManager.getService(Context.ACCOUNT_SERVICE));
          if ((flags & UserInfo.FLAG_RESTRICTED) != 0) {
              // In non-split user mode, userId can only be SYSTEM
              int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
              info = um.createRestrictedProfile(name, parentUserId);
              accm.addSharedAccountsFromParentUser(parentUserId, userId,
                      (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
          } else if (userId < 0) {
              info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
          } else {
              info = um.createProfileForUser(name, flags, userId, null);
          }
  
          if (info != null) {
              getOutPrintWriter().println("Success: created user id " + info.id);
              return 0;
          } else {
              getErrPrintWriter().println("Error: couldn't create User.");
              return 1;
          }
      }
  
      public int runRemoveUser() throws RemoteException {
          int userId;
          String arg = getNextArg();
          if (arg == null) {
              getErrPrintWriter().println("Error: no user id specified.");
              return 1;
          }
          userId = UserHandle.parseUserArg(arg);
          IUserManager um = IUserManager.Stub.asInterface(
                  ServiceManager.getService(Context.USER_SERVICE));
          if (um.removeUser(userId)) {
              getOutPrintWriter().println("Success: removed user");
              return 0;
          } else {
              getErrPrintWriter().println("Error: couldn't remove user id " + userId);
              return 1;
          }
      }
  
      public int runSetUserRestriction() throws RemoteException {
          int userId = UserHandle.USER_SYSTEM;
          String opt = getNextOption();
          if (opt != null && "--user".equals(opt)) {
              userId = UserHandle.parseUserArg(getNextArgRequired());
          }
  
          String restriction = getNextArg();
          String arg = getNextArg();
          boolean value;
          if ("1".equals(arg)) {
              value = true;
          } else if ("0".equals(arg)) {
              value = false;
          } else {
              getErrPrintWriter().println("Error: valid value not specified");
              return 1;
          }
          IUserManager um = IUserManager.Stub.asInterface(
                  ServiceManager.getService(Context.USER_SERVICE));
          um.setUserRestriction(restriction, value, userId);
          return 0;
      }
  
      public int runGetMaxUsers() {
          getOutPrintWriter().println("Maximum supported users: "
                  + UserManager.getMaxSupportedUsers());
          return 0;
      }
  
      public int runGetMaxRunningUsers() {
          ActivityManagerInternal activityManagerInternal =
                  LocalServices.getService(ActivityManagerInternal.class);
          getOutPrintWriter().println("Maximum supported running users: "
                  + activityManagerInternal.getMaxRunningUsers());
          return 0;
      }
  
      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;
          // Whitelist all permissions by default
          sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
  
          String opt;
          boolean replaceExisting = true;
          while ((opt = getNextOption()) != null) {
              switch (opt) {
                  case "-r": // ignore
                      break;
                  case "-R":
                      replaceExisting = false;
                      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 "-f":
                      sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
                      break;
                  case "-d":
                      sessionParams.installFlags |= PackageManager.INSTALL_REQUEST_DOWNGRADE;
                      break;
                  case "-g":
                      sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
                      break;
                  case "--restrict-permissions":
                      sessionParams.installFlags &=
                              ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
                      break;
                  case "--dont-kill":
                      sessionParams.installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
                      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 "--pkg":
                      sessionParams.appPackageName = getNextArg();
                      if (sessionParams.appPackageName == null) {
                          throw new IllegalArgumentException("Missing package name");
                      }
                      break;
                  case "-S":
                      final long sizeBytes = Long.parseLong(getNextArg());
                      if (sizeBytes <= 0) {
                          throw new IllegalArgumentException("Size must be positive");
                      }
                      sessionParams.setSize(sizeBytes);
                      break;
                  case "--abi":
                      sessionParams.abiOverride = checkAbiArgument(getNextArg());
                      break;
                  case "--ephemeral":
                  case "--instant":
                  case "--instantapp":
                      sessionParams.setInstallAsInstantApp(true /*isInstantApp*/);
                      break;
                  case "--full":
                      sessionParams.setInstallAsInstantApp(false /*isInstantApp*/);
                      break;
                  case "--preload":
                      sessionParams.setInstallAsVirtualPreload();
                      break;
                  case "--user":
                      params.userId = UserHandle.parseUserArg(getNextArgRequired());
                      break;
                  case "--install-location":
                      sessionParams.installLocation = Integer.parseInt(getNextArg());
                      break;
                  case "--install-reason":
                      sessionParams.installReason = 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;
                  case "--force-sdk": // ignore
                      break;
                  case "--apex":
                      sessionParams.setInstallAsApex();
                      sessionParams.setStaged();
                      break;
                  case "--multi-package":
                      sessionParams.setMultiPackage();
                      break;
                  case "--staged":
                      sessionParams.setStaged();
                      break;
                  case "--enable-rollback":
                      if (params.installerPackageName == null) {
                          // com.android.shell has the TEST_MANAGE_ROLLBACKS
                          // permission needed to enable rollback for non-module
                          // packages, which is likely what the user wants when
                          // enabling rollback through the shell command. Set
                          // the installer to com.android.shell if no installer
                          // has been provided so that the user doesn't have to
                          // remember to set it themselves.
                          params.installerPackageName = "com.android.shell";
                      }
                      sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
                      break;
                  default:
                      throw new IllegalArgumentException("Unknown option " + opt);
              }
          }
          if (replaceExisting) {
              sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
          }
          return params;
      }
  
      private int runSetHomeActivity() {
          final PrintWriter pw = getOutPrintWriter();
          int userId = UserHandle.USER_SYSTEM;
          String opt;
          while ((opt = getNextOption()) != null) {
              switch (opt) {
                  case "--user":
                      userId = UserHandle.parseUserArg(getNextArgRequired());
                      break;
                  default:
                      pw.println("Error: Unknown option: " + opt);
                      return 1;
              }
          }
  
          String pkgName;
          String component = getNextArg();
          if (component.indexOf('/') < 0) {
              // No component specified, so assume it's just a package name.
              pkgName = component;
          } else {
              ComponentName componentName =
                      component != null ? ComponentName.unflattenFromString(component) : null;
              if (componentName == null) {
                  pw.println("Error: invalid component name");
                  return 1;
              }
              pkgName = componentName.getPackageName();
          }
  
  
          final CompletableFuture<Boolean> future = new CompletableFuture<>();
          final RemoteCallback callback = new RemoteCallback(res -> future.complete(res != null));
          try {
              IRoleManager roleManager = android.app.role.IRoleManager.Stub.asInterface(
                      ServiceManager.getServiceOrThrow(Context.ROLE_SERVICE));
              roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, pkgName,
                      0, userId, callback);
              boolean success = future.get();
              if (success) {
                  pw.println("Success");
                  return 0;
              } else {
                  pw.println("Error: Failed to set default home.");
                  return 1;
              }
          } catch (Exception e) {
              pw.println(e.toString());
              return 1;
          }
      }
  
      private int runSetInstaller() throws RemoteException {
          final String targetPackage = getNextArg();
          final String installerPackageName = getNextArg();
  
          if (targetPackage == null || installerPackageName == null) {
              getErrPrintWriter().println("Must provide both target and installer package names");
              return 1;
          }
  
          mInterface.setInstallerPackageName(targetPackage, installerPackageName);
          getOutPrintWriter().println("Success");
          return 0;
      }
  
      private int runGetInstantAppResolver() {
          final PrintWriter pw = getOutPrintWriter();
          try {
              final ComponentName instantAppsResolver = mInterface.getInstantAppResolverComponent();
              if (instantAppsResolver == null) {
                  return 1;
              }
              pw.println(instantAppsResolver.flattenToString());
              return 0;
          } catch (Exception e) {
              pw.println(e.toString());
              return 1;
          }
      }
  
      private int runHasFeature() {
          final PrintWriter err = getErrPrintWriter();
          final String featureName = getNextArg();
          if (featureName == null) {
              err.println("Error: expected FEATURE name");
              return 1;
          }
          final String versionString = getNextArg();
          try {
              final int version = (versionString == null) ? 0 : Integer.parseInt(versionString);
              final boolean hasFeature = mInterface.hasSystemFeature(featureName, version);
              getOutPrintWriter().println(hasFeature);
              return hasFeature ? 0 : 1;
          } catch (NumberFormatException e) {
              err.println("Error: illegal version number " + versionString);
              return 1;
          } catch (RemoteException e) {
              err.println(e.toString());
              return 1;
          }
      }
  
      private int runDump() {
          String pkg = getNextArg();
          if (pkg == null) {
              getErrPrintWriter().println("Error: no package specified");
              return 1;
          }
          ActivityManager.dumpPackageStateStatic(getOutFileDescriptor(), pkg);
          return 0;
      }
  
      private int runSetHarmfulAppWarning() throws RemoteException {
          int userId = UserHandle.USER_CURRENT;
  
          String opt;
          while ((opt = getNextOption()) != null) {
              if (opt.equals("--user")) {
                  userId = UserHandle.parseUserArg(getNextArgRequired());
              } else {
                  getErrPrintWriter().println("Error: Unknown option: " + opt);
                  return -1;
              }
          }
  
          userId = translateUserId(userId, false /*allowAll*/, "runSetHarmfulAppWarning");
  
          final String packageName = getNextArgRequired();
          final String warning = getNextArg();
  
          mInterface.setHarmfulAppWarning(packageName, warning, userId);
  
          return 0;
      }
  
      private int runGetHarmfulAppWarning() throws RemoteException {
          int userId = UserHandle.USER_CURRENT;
  
          String opt;
          while ((opt = getNextOption()) != null) {
              if (opt.equals("--user")) {
                  userId = UserHandle.parseUserArg(getNextArgRequired());
              } else {
                  getErrPrintWriter().println("Error: Unknown option: " + opt);
                  return -1;
              }
          }
  
          userId = translateUserId(userId, false /*allowAll*/, "runGetHarmfulAppWarning");
  
          final String packageName = getNextArgRequired();
          final CharSequence warning = mInterface.getHarmfulAppWarning(packageName, userId);
          if (!TextUtils.isEmpty(warning)) {
              getOutPrintWriter().println(warning);
              return 0;
          } else {
              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 int translateUserId(int userId, boolean allowAll, String logContext) {
          return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                  userId, allowAll, true, logContext, "pm command");
      }
  
      private int doCreateSession(SessionParams params, String installerPackageName, int userId)
              throws RemoteException {
          userId = translateUserId(userId, true /*allowAll*/, "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 doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
              boolean logSuccess) throws RemoteException {
          PackageInstaller.Session session = null;
          try {
              final PrintWriter pw = getOutPrintWriter();
              final ParcelFileDescriptor fd;
              if (STDIN_PATH.equals(inPath)) {
                  fd = ParcelFileDescriptor.dup(getInFileDescriptor());
              } else if (inPath != null) {
                  fd = openFileForSystem(inPath, "r");
                  if (fd == null) {
                      return -1;
                  }
                  sizeBytes = fd.getStatSize();
                  if (sizeBytes < 0) {
                      getErrPrintWriter().println("Unable to get size of: " + inPath);
                      return -1;
                  }
              } else {
                  fd = ParcelFileDescriptor.dup(getInFileDescriptor());
              }
              if (sizeBytes <= 0) {
                  getErrPrintWriter().println("Error: must specify a APK size");
                  return 1;
              }
  
              session = new PackageInstaller.Session(
                      mInterface.getPackageInstaller().openSession(sessionId));
              session.write(splitName, 0, sizeBytes, fd);
  
              if (logSuccess) {
                  pw.println("Success: streamed " + sizeBytes + " bytes");
              }
              return 0;
          } catch (IOException e) {
              getErrPrintWriter().println("Error: failed to write; " + e.getMessage());
              return 1;
          } finally {
              IoUtils.closeQuietly(session);
          }
      }
  
      private int doInstallAddSession(int parentId, int[] sessionIds, boolean logSuccess)
              throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          PackageInstaller.Session session = null;
          try {
              session = new PackageInstaller.Session(
                      mInterface.getPackageInstaller().openSession(parentId));
              if (!session.isMultiPackage()) {
                  getErrPrintWriter().println(
                          "Error: parent session ID is not a multi-package session");
                  return 1;
              }
              for (int i = 0; i < sessionIds.length; i++) {
                  session.addChildSessionId(sessionIds[i]);
              }
              if (logSuccess) {
                  pw.println("Success");
              }
              return 0;
          } finally {
              IoUtils.closeQuietly(session);
          }
      }
  
      private int doRemoveSplit(int sessionId, String splitName, boolean logSuccess)
              throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          PackageInstaller.Session session = null;
          try {
              session = new PackageInstaller.Session(
                      mInterface.getPackageInstaller().openSession(sessionId));
              session.removeSplit(splitName);
  
              if (logSuccess) {
                  pw.println("Success");
              }
              return 0;
          } catch (IOException e) {
              pw.println("Error: failed to remove split; " + e.getMessage());
              return 1;
          } finally {
              IoUtils.closeQuietly(session);
          }
      }
  
      private int doCommitSession(int sessionId, boolean logSuccess)
              throws RemoteException {
  
          final PrintWriter pw = getOutPrintWriter();
          PackageInstaller.Session session = null;
          try {
              session = new PackageInstaller.Session(
                      mInterface.getPackageInstaller().openSession(sessionId));
              if (!session.isMultiPackage() && !session.isStaged()) {
                  // Sanity check that all .dm files match an apk.
                  // (The installer does not support standalone .dm files and will not process them.)
                  try {
                      DexMetadataHelper.validateDexPaths(session.getNames());
                  } catch (IllegalStateException | IOException e) {
                      pw.println(
                              "Warning [Could not validate the dex paths: " + e.getMessage() + "]");
                  }
              }
              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) {
                  if (logSuccess) {
                      pw.println("Success");
                  }
              } else {
                  pw.println("Failure ["
                          + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
              }
              return status;
          } finally {
              IoUtils.closeQuietly(session);
          }
      }
  
      private int doAbandonSession(int sessionId, boolean logSuccess) throws RemoteException {
          final PrintWriter pw = getOutPrintWriter();
          PackageInstaller.Session session = null;
          try {
              session = new PackageInstaller.Session(
                      mInterface.getPackageInstaller().openSession(sessionId));
              session.abandon();
              if (logSuccess) {
                  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 {
          final PrintWriter pw = getOutPrintWriter();
          final int groupCount = groupList.size();
          for (int i = 0; i < groupCount; i++) {
              String groupName = groupList.get(i);
              String prefix = "";
              if (groups) {
                  if (i > 0) {
                      pw.println("");
                  }
                  if (groupName != null) {
                      PermissionGroupInfo pgi =
                              mInterface.getPermissionGroupInfo(groupName, 0 /*flags*/);
                      if (summary) {
                          Resources res = getResources(pgi);
                          if (res != null) {
                              pw.print(loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel) + ": ");
                          } else {
                              pw.print(pgi.name + ": ");
  
                          }
                      } else {
                          pw.println((labels ? "+ " : "") + "group:" + pgi.name);
                          if (labels) {
                              pw.println("  package:" + pgi.packageName);
                              Resources res = getResources(pgi);
                              if (res != null) {
                                  pw.println("  label:"
                                          + loadText(pgi, pgi.labelRes, pgi.nonLocalizedLabel));
                                  pw.println("  description:"
                                          + loadText(pgi, pgi.descriptionRes,
                                                  pgi.nonLocalizedDescription));
                              }
                          }
                      }
                  } else {
                      pw.println(((labels && !summary) ? "+ " : "") + "ungrouped:");
                  }
                  prefix = "  ";
              }
              List<PermissionInfo> ps =
                      mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/).getList();
              final int count = ps.size();
              boolean first = true;
              for (int p = 0 ; p < count ; p++) {
                  PermissionInfo pi = ps.get(p);
                  if (groups && groupName == null && pi.group != null) {
                      continue;
                  }
                  final int base = pi.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
                  if (base < startProtectionLevel
                          || base > endProtectionLevel) {
                      continue;
                  }
                  if (summary) {
                      if (first) {
                          first = false;
                      } else {
                          pw.print(", ");
                      }
                      Resources res = getResources(pi);
                      if (res != null) {
                          pw.print(loadText(pi, pi.labelRes,
                                  pi.nonLocalizedLabel));
                      } else {
                          pw.print(pi.name);
                      }
                  } else {
                      pw.println(prefix + (labels ? "+ " : "")
                              + "permission:" + pi.name);
                      if (labels) {
                          pw.println(prefix + "  package:" + pi.packageName);
                          Resources res = getResources(pi);
                          if (res != null) {
                              pw.println(prefix + "  label:"
                                      + loadText(pi, pi.labelRes,
                                              pi.nonLocalizedLabel));
                              pw.println(prefix + "  description:"
                                      + loadText(pi, pi.descriptionRes,
                                              pi.nonLocalizedDescription));
                          }
                          pw.println(prefix + "  protectionLevel:"
                                  + PermissionInfo.protectionToString(pi.protectionLevel));
                      }
                  }
              }
  
              if (summary) {
                  pw.println("");
              }
          }
      }
  
      private String loadText(PackageItemInfo pii, int res, CharSequence nonLocalized)
              throws RemoteException {
          if (nonLocalized != null) {
              return nonLocalized.toString();
          }
          if (res != 0) {
              Resources r = getResources(pii);
              if (r != null) {
                  try {
                      return r.getString(res);
                  } catch (Resources.NotFoundException e) {
                  }
              }
          }
          return null;
      }
  
      private Resources getResources(PackageItemInfo pii) throws RemoteException {
          Resources res = mResourceCache.get(pii.packageName);
          if (res != null) return res;
  
          ApplicationInfo ai = mInterface.getApplicationInfo(pii.packageName, 0, 0);
          AssetManager am = new AssetManager();
          am.addAssetPath(ai.publicSourceDir);
          res = new Resources(am, null, null);
          mResourceCache.put(pii.packageName, res);
          return res;
      }
  
      @Override
      public void onHelp() {
          final PrintWriter pw = getOutPrintWriter();
          pw.println("Package manager (package) commands:");
          pw.println("  help");
          pw.println("    Print this help text.");
          pw.println("");
          pw.println("  path [--user USER_ID] PACKAGE");
          pw.println("    Print the path to the .apk of the given PACKAGE.");
          pw.println("");
          pw.println("  dump PACKAGE");
          pw.println("    Print various system state associated with the given PACKAGE.");
          pw.println("");
          pw.println("  list features");
          pw.println("    Prints all features of the system.");
          pw.println("");
          pw.println("  has-feature FEATURE_NAME [version]");
          pw.println("    Prints true and returns exit status 0 when system has a FEATURE_NAME,");
          pw.println("    otherwise prints false and returns exit status 1");
          pw.println("");
          pw.println("  list instrumentation [-f] [TARGET-PACKAGE]");
          pw.println("    Prints all test packages; optionally only those targeting TARGET-PACKAGE");
          pw.println("    Options:");
          pw.println("      -f: dump the name of the .apk file containing the test package");
          pw.println("");
          pw.println("  list libraries");
          pw.println("    Prints all system libraries.");
          pw.println("");
          pw.println("  list packages [-f] [-d] [-e] [-s] [-3] [-i] [-l] [-u] [-U] ");
          pw.println("      [--show-versioncode] [--apex-only] [--uid UID] [--user USER_ID] [FILTER]");
          pw.println("    Prints all packages; optionally only those whose name contains");
          pw.println("    the text in FILTER.  Options are:");
          pw.println("      -f: see their associated file");
          pw.println("      -a: all known packages (but excluding APEXes)");
          pw.println("      -d: filter to only show disabled packages");
          pw.println("      -e: filter to only show enabled packages");
          pw.println("      -s: filter to only show system packages");
          pw.println("      -3: filter to only show third party packages");
          pw.println("      -i: see the installer for the packages");
          pw.println("      -l: ignored (used for compatibility with older releases)");
          pw.println("      -U: also show the package UID");
          pw.println("      -u: also include uninstalled packages");
          pw.println("      --show-versioncode: also show the version code");
          pw.println("      --apex-only: only show APEX packages");
          pw.println("      --uid UID: filter to only show packages with the given UID");
          pw.println("      --user USER_ID: only list packages belonging to the given user");
          pw.println("");
          pw.println("  list permission-groups");
          pw.println("    Prints all known permission groups.");
          pw.println("");
          pw.println("  list permissions [-g] [-f] [-d] [-u] [GROUP]");
          pw.println("    Prints all known permissions; optionally only those in GROUP.  Options are:");
          pw.println("      -g: organize by group");
          pw.println("      -f: print all information");
          pw.println("      -s: short summary");
          pw.println("      -d: only list dangerous permissions");
          pw.println("      -u: list only the permissions users will see");
          pw.println("");
          pw.println("  resolve-activity [--brief] [--components] [--query-flags FLAGS]");
          pw.println("       [--user USER_ID] INTENT");
          pw.println("    Prints the activity that resolves to the given INTENT.");
          pw.println("");
          pw.println("  query-activities [--brief] [--components] [--query-flags FLAGS]");
          pw.println("       [--user USER_ID] INTENT");
          pw.println("    Prints all activities that can handle the given INTENT.");
          pw.println("");
          pw.println("  query-services [--brief] [--components] [--query-flags FLAGS]");
          pw.println("       [--user USER_ID] INTENT");
          pw.println("    Prints all services that can handle the given INTENT.");
          pw.println("");
          pw.println("  query-receivers [--brief] [--components] [--query-flags FLAGS]");
          pw.println("       [--user USER_ID] INTENT");
          pw.println("    Prints all broadcast receivers that can handle the given INTENT.");
          pw.println("");
          pw.println("  install [-lrtsfdgw] [-i PACKAGE] [--user USER_ID|all|current]");
          pw.println("       [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
          pw.println("       [--install-reason 0/1/2/3/4] [--originating-uri URI]");
          pw.println("       [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
          pw.println("       [--preload] [--instantapp] [--full] [--dont-kill]");
          pw.println("       [--enable-rollback]");
          pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [--apex]");
          pw.println("       [PATH|-]");
          pw.println("    Install an application.  Must provide the apk data to install, either as a");
          pw.println("    file path or '-' to read from stdin.  Options are:");
          pw.println("      -l: forward lock application");
          pw.println("      -R: disallow replacement of existing application");
          pw.println("      -t: allow test packages");
          pw.println("      -i: specify package name of installer owning the app");
          pw.println("      -s: install application on sdcard");
          pw.println("      -f: install application on internal flash");
          pw.println("      -d: allow version code downgrade (debuggable packages only)");
          pw.println("      -p: partial application install (new split on top of existing pkg)");
          pw.println("      -g: grant all runtime permissions");
          pw.println("      -S: size in bytes of package, required for stdin");
          pw.println("      --user: install under the given user.");
          pw.println("      --dont-kill: installing a new feature split, don't kill running app");
          pw.println("      --restrict-permissions: don't whitelist restricted permissions at install");
          pw.println("      --originating-uri: set URI where app was downloaded from");
          pw.println("      --referrer: set URI that instigated the install of the app");
          pw.println("      --pkg: specify expected package name of app being installed");
          pw.println("      --abi: override the default ABI of the platform");
          pw.println("      --instantapp: cause the app to be installed as an ephemeral install app");
          pw.println("      --full: cause the app to be installed as a non-ephemeral full app");
          pw.println("      --install-location: force the install location:");
          pw.println("          0=auto, 1=internal only, 2=prefer external");
          pw.println("      --install-reason: indicates why the app is being installed:");
          pw.println("          0=unknown, 1=admin policy, 2=device restore,");
          pw.println("          3=device setup, 4=user request");
          pw.println("      --force-uuid: force install on to disk volume with given UUID");
          pw.println("      --apex: install an .apex file, not an .apk");
          pw.println("");
          pw.println("  install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
          pw.println("       [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
          pw.println("       [--install-reason 0/1/2/3/4] [--originating-uri URI]");
          pw.println("       [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
          pw.println("       [--preload] [--instantapp] [--full] [--dont-kill]");
          pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
          pw.println("       [--multi-package] [--staged]");
          pw.println("    Like \"install\", but starts an install session.  Use \"install-write\"");
          pw.println("    to push data into the session, and \"install-commit\" to finish.");
          pw.println("");
          pw.println("  install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH|-]");
          pw.println("    Write an apk into the given install session.  If the path is '-', data");
          pw.println("    will be read from stdin.  Options are:");
          pw.println("      -S: size in bytes of package, required for stdin");
          pw.println("");
          pw.println("  install-add-session MULTI_PACKAGE_SESSION_ID CHILD_SESSION_IDs");
          pw.println("    Add one or more session IDs to a multi-package session.");
          pw.println("");
          pw.println("  install-commit SESSION_ID");
          pw.println("    Commit the given active install session, installing the app.");
          pw.println("");
          pw.println("  install-abandon SESSION_ID");
          pw.println("    Delete the given active install session.");
          pw.println("");
          pw.println("  set-install-location LOCATION");
          pw.println("    Changes the default install location.  NOTE this is only intended for debugging;");
          pw.println("    using this can cause applications to break and other undersireable behavior.");
          pw.println("    LOCATION is one of:");
          pw.println("    0 [auto]: Let system decide the best location");
          pw.println("    1 [internal]: Install on internal device storage");
          pw.println("    2 [external]: Install on external media");
          pw.println("");
          pw.println("  get-install-location");
          pw.println("    Returns the current install location: 0, 1 or 2 as per set-install-location.");
          pw.println("");
          pw.println("  move-package PACKAGE [internal|UUID]");
          pw.println("");
          pw.println("  move-primary-storage [internal|UUID]");
          pw.println("");
          pw.println("  pm uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE] PACKAGE [SPLIT]");
          pw.println("    Remove the given package name from the system.  May remove an entire app");
          pw.println("    if no SPLIT name is specified, otherwise will remove only the split of the");
          pw.println("    given app.  Options are:");
          pw.println("      -k: keep the data and cache directories around after package removal.");
          pw.println("      --user: remove the app from the given user.");
          pw.println("      --versionCode: only uninstall if the app has the given version code.");
          pw.println("");
          pw.println("  clear [--user USER_ID] PACKAGE");
          pw.println("    Deletes all data associated with a package.");
          pw.println("");
          pw.println("  enable [--user USER_ID] PACKAGE_OR_COMPONENT");
          pw.println("  disable [--user USER_ID] PACKAGE_OR_COMPONENT");
          pw.println("  disable-user [--user USER_ID] PACKAGE_OR_COMPONENT");
          pw.println("  disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
          pw.println("  default-state [--user USER_ID] PACKAGE_OR_COMPONENT");
          pw.println("    These commands change the enabled state of a given package or");
          pw.println("    component (written as \"package/class\").");
          pw.println("");
          pw.println("  hide [--user USER_ID] PACKAGE_OR_COMPONENT");
          pw.println("  unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
          pw.println("");
          pw.println("  suspend [--user USER_ID] TARGET-PACKAGE");
          pw.println("    Suspends the specified package (as user).");
          pw.println("");
          pw.println("  unsuspend [--user USER_ID] TARGET-PACKAGE");
          pw.println("    Unsuspends the specified package (as user).");
          pw.println("");
          pw.println("  grant [--user USER_ID] PACKAGE PERMISSION");
          pw.println("  revoke [--user USER_ID] PACKAGE PERMISSION");
          pw.println("    These commands either grant or revoke permissions to apps.  The permissions");
          pw.println("    must be declared as used in the app's manifest, be runtime permissions");
          pw.println("    (protection level dangerous), and the app targeting SDK greater than Lollipop MR1.");
          pw.println("");
          pw.println("  reset-permissions");
          pw.println("    Revert all runtime permissions to their default state.");
          pw.println("");
          pw.println("  set-permission-enforced PERMISSION [true|false]");
          pw.println("");
          pw.println("  get-privapp-permissions TARGET-PACKAGE");
          pw.println("    Prints all privileged permissions for a package.");
          pw.println("");
          pw.println("  get-privapp-deny-permissions TARGET-PACKAGE");
          pw.println("    Prints all privileged permissions that are denied for a package.");
          pw.println("");
          pw.println("  get-oem-permissions TARGET-PACKAGE");
          pw.println("    Prints all OEM permissions for a package.");
          pw.println("");
          pw.println("  set-app-link [--user USER_ID] PACKAGE {always|ask|never|undefined}");
          pw.println("  get-app-link [--user USER_ID] PACKAGE");
          pw.println("");
          pw.println("  trim-caches DESIRED_FREE_SPACE [internal|UUID]");
          pw.println("    Trim cache files to reach the given free space.");
          pw.println("");
          pw.println("  list users");
          pw.println("    Lists the current users.");
          pw.println("");
          pw.println("  create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
          pw.println("      [--guest] [--pre-create-only] USER_NAME");
          pw.println("    Create a new user with the given USER_NAME, printing the new user identifier");
          pw.println("    of the user.");
          pw.println("");
          pw.println("  remove-user USER_ID");
          pw.println("    Remove the user with the given USER_IDENTIFIER, deleting all data");
          pw.println("    associated with that user");
          pw.println("");
          pw.println("  set-user-restriction [--user USER_ID] RESTRICTION VALUE");
          pw.println("");
          pw.println("  get-max-users");
          pw.println("");
          pw.println("  get-max-running-users");
          pw.println("");
          pw.println("  compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]");
          pw.println("          [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)");
          pw.println("    Trigger compilation of TARGET-PACKAGE or all packages if \"-a\".  Options are:");
          pw.println("      -a: compile all packages");
          pw.println("      -c: clear profile data before compiling");
          pw.println("      -f: force compilation even if not needed");
          pw.println("      -m: select compilation mode");
          pw.println("          MODE is one of the dex2oat compiler filters:");
          pw.println("            assume-verified");
          pw.println("            extract");
          pw.println("            verify");
          pw.println("            quicken");
          pw.println("            space-profile");
          pw.println("            space");
          pw.println("            speed-profile");
          pw.println("            speed");
          pw.println("            everything");
          pw.println("      -r: select compilation reason");
          pw.println("          REASON is one of:");
          for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
              pw.println("            " + PackageManagerServiceCompilerMapping.REASON_STRINGS[i]);
          }
          pw.println("      --reset: restore package to its post-install state");
          pw.println("      --check-prof (true | false): look at profiles when doing dexopt?");
          pw.println("      --secondary-dex: compile app secondary dex files");
          pw.println("      --split SPLIT: compile only the given split name");
          pw.println("      --compile-layouts: compile layout resources for faster inflation");
          pw.println("");
          pw.println("  force-dex-opt PACKAGE");
          pw.println("    Force immediate execution of dex opt for the given PACKAGE.");
          pw.println("");
          pw.println("  bg-dexopt-job");
          pw.println("    Execute the background optimizations immediately.");
          pw.println("    Note that the command only runs the background optimizer logic. It may");
          pw.println("    overlap with the actual job but the job scheduler will not be able to");
          pw.println("    cancel it. It will also run even if the device is not in the idle");
          pw.println("    maintenance mode.");
          pw.println("");
          pw.println("  reconcile-secondary-dex-files TARGET-PACKAGE");
          pw.println("    Reconciles the package secondary dex files with the generated oat files.");
          pw.println("");
          pw.println("  dump-profiles TARGET-PACKAGE");
          pw.println("    Dumps method/class profile files to");
          pw.println("    " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + "TARGET-PACKAGE.txt");
          pw.println("");
          pw.println("  snapshot-profile TARGET-PACKAGE [--code-path path]");
          pw.println("    Take a snapshot of the package profiles to");
          pw.println("    " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION
                  + "TARGET-PACKAGE[-code-path].prof");
          pw.println("    If TARGET-PACKAGE=android it will take a snapshot of the boot image");
          pw.println("");
          pw.println("  set-home-activity [--user USER_ID] TARGET-COMPONENT");
          pw.println("    Set the default home activity (aka launcher).");
          pw.println("    TARGET-COMPONENT can be a package name (com.package.my) or a full");
          pw.println("    component (com.package.my/component.name). However, only the package name");
          pw.println("    matters: the actual component used will be determined automatically from");
          pw.println("    the package.");
          pw.println("");
          pw.println("  set-installer PACKAGE INSTALLER");
          pw.println("    Set installer package name");
          pw.println("");
          pw.println("  get-instantapp-resolver");
          pw.println("    Return the name of the component that is the current instant app installer.");
          pw.println("");
          pw.println("  set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]");
          pw.println("    Mark the app as harmful with the given warning message.");
          pw.println("");
          pw.println("  get-harmful-app-warning [--user <USER_ID>] <PACKAGE>");
          pw.println("    Return the harmful app warning message for the given app, if present");
          pw.println();
          pw.println("  uninstall-system-updates");
          pw.println("    Remove updates to all system applications and fall back to their /system " +
                  "version.");
          pw.println();
          pw.println("  get-moduleinfo [--all | --installed] [module-name]");
          pw.println("    Displays module info. If module-name is specified only that info is shown");
          pw.println("    By default, without any argument only installed modules are shown.");
          pw.println("      --all: show all module info");
          pw.println("      --installed: show only installed modules");
          pw.println("");
          Intent.printIntentArgsHelp(pw , "");
      }
  
      private static class LocalIntentReceiver {
          private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
  
          private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
              @Override
              public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
                      IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                  try {
                      mResult.offer(intent, 5, TimeUnit.SECONDS);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
              }
          };
  
          public IntentSender getIntentSender() {
              return new IntentSender((IIntentSender) mLocalSender);
          }
  
          public Intent getResult() {
              try {
                  return mResult.take();
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
          }
      }
  }
  