Allow "am" to initiate heap dumps.

This was mostly cloned from the "am profile" implementation.  It's
intended to replace the old "kill -10" approach used by "runhat".

We could really use a native heap dump, so I pass a "managed"
flag through that indicates whether we want to dump the native or
managed heap.  We don't currently have a native heap dump-to-file
function, so it currently just logs a warning.

(android.ddm.DdmHandleNativeHeap.getLeakInfo is a good start -- it
copies /proc/maps and then calls get_malloc_leak_info to get some
goodies.  Needs some formatting to make it human-readable.  I didn't
want to cram all that into this change.)

It would be useful if "am" didn't exit until the heap dump operation
completed, but I'm not sure how to do that.

Bug 2759474.

Change-Id: I46bc98067738d8c72ac0fc10002ca67bb4929271
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1fe85e6..43a08b5 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1294,6 +1294,19 @@
             return true;
         }
 
+        case DUMP_HEAP_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String process = data.readString();
+            boolean managed = data.readInt() != 0;
+            String path = data.readString();
+            ParcelFileDescriptor fd = data.readInt() != 0
+                    ? data.readFileDescriptor() : null;
+            boolean res = dumpHeap(process, managed, path, fd);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
+
         }
         
         return super.onTransact(code, data, reply, flags);
@@ -2874,6 +2887,28 @@
         data.recycle();
         reply.recycle();
     }
-    
+
+    public boolean dumpHeap(String process, boolean managed,
+            String path, ParcelFileDescriptor fd) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(process);
+        data.writeInt(managed ? 1 : 0);
+        data.writeString(path);
+        if (fd != null) {
+            data.writeInt(1);
+            fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        mRemote.transact(DUMP_HEAP_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return res;
+    }
+
     private IBinder mRemote;
 }