Merge "Installer connection support for dump_profiles" into nyc-dev
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4bbbdee..11f0eb6 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -482,6 +482,11 @@
     boolean performDexOptMode(String packageName, boolean checkProfiles,
             String targetCompilerFilter, boolean force);
 
+    /**
+     * Ask the package manager to dump profiles associated with a package.
+     */
+    void dumpProfiles(String packageName);
+
     void forceDexOpt(String packageName);
 
     /**
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index a7a3cb5..419c3d8 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -157,9 +157,7 @@
                 sharedLibraries);
     }
 
-    public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
-        final String[] res = execute("merge_profiles", uid, pkgName);
-
+    private boolean safeParseBooleanResult(String[] res) throws InstallerException {
         if ((res == null) || (res.length != 2)) {
             throw new InstallerException("Invalid size result: " + Arrays.toString(res));
         }
@@ -172,6 +170,19 @@
         return Boolean.parseBoolean(res[1]);
     }
 
+    public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
+        final String[] res = execute("merge_profiles", uid, pkgName);
+
+        return safeParseBooleanResult(res);
+    }
+
+    public boolean dumpProfiles(String gid, String packageName, String codePaths)
+            throws InstallerException {
+        final String[] res = execute("dump_profiles", gid, packageName, codePaths);
+
+        return safeParseBooleanResult(res);
+    }
+
     private boolean connect() {
         if (mSocket != null) {
             return true;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 913c824..7b85a4f 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -153,6 +153,11 @@
         return mInstaller.mergeProfiles(uid, pkgName);
     }
 
+    public boolean dumpProfiles(String gid, String packageName, String codePaths)
+            throws InstallerException {
+        return mInstaller.dumpProfiles(gid, packageName, codePaths);
+    }
+
     public void idmap(String targetApkPath, String overlayApkPath, int uid)
             throws InstallerException {
         mInstaller.execute("idmap", targetApkPath, overlayApkPath, uid);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a5cb284..3999d18 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7459,6 +7459,45 @@
     }
 
     @Override
+    public void dumpProfiles(String packageName) {
+        PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+        }
+        /* Only the shell or the app user should be able to dump profiles. */
+        int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SHELL_UID && callingUid != pkg.applicationInfo.uid) {
+            throw new SecurityException("dumpProfiles");
+        }
+
+        synchronized (mInstallLock) {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
+            final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+            try {
+                final File codeFile = new File(pkg.applicationInfo.getCodePath());
+                List<String> allCodePaths = Collections.EMPTY_LIST;
+                if (codeFile != null && codeFile.exists()) {
+                    try {
+                        final PackageLite codePkg = PackageParser.parsePackageLite(codeFile, 0);
+                        allCodePaths = codePkg.getAllCodePaths();
+                    } catch (PackageParserException e) {
+                        // Well, we tried.
+                    }
+                }
+                String gid = Integer.toString(sharedGid);
+                String codePaths = TextUtils.join(";", allCodePaths);
+                mInstaller.dumpProfiles(gid, packageName, codePaths);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Failed to dump profiles", e);
+            }
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    @Override
     public void forceDexOpt(String packageName) {
         enforceSystemOrRoot("forceDexOpt");
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index beff8fc..05f5b8f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -105,6 +105,8 @@
                     return runInstallWrite();
                 case "compile":
                     return runCompile();
+                case "dump-profiles":
+                    return runDumpProfiles();
                 case "list":
                     return runList();
                 case "uninstall":
@@ -387,6 +389,12 @@
         }
     }
 
+    private int runDumpProfiles() throws RemoteException {
+        String packageName = getNextArg();
+        mInterface.dumpProfiles(packageName);
+        return 0;
+    }
+
     private int runList() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         final String type = getNextArg();