Annotate BinderProxy objects with their interface
Example: android.os.BinderProxy@1ae2d838 for android.app.job.IJobScheduler
Test: Added fake Binder stub and proxy classes, verified that a class ending with $Stub$Proxy and holding a reference to a BinderProxy object is marked as the BinderProxy interface
Bug:114363695
Change-Id: I19dd24cafbb4c022dda583408f02cc2fae362825
diff --git a/tools/ahat/etc/ahat_api.txt b/tools/ahat/etc/ahat_api.txt
index 7aa994a..c82b314 100644
--- a/tools/ahat/etc/ahat_api.txt
+++ b/tools/ahat/etc/ahat_api.txt
@@ -73,6 +73,7 @@
method public com.android.ahat.heapdump.AhatInstance getAssociatedBitmapInstance();
method public com.android.ahat.heapdump.AhatClassObj getAssociatedClassForOverhead();
method public com.android.ahat.heapdump.AhatInstance getBaseline();
+ method public java.lang.String getBinderProxyInterfaceName();
method public java.lang.String getClassName();
method public com.android.ahat.heapdump.AhatClassObj getClassObj();
method public java.lang.String getDexCacheLocation(int);
diff --git a/tools/ahat/src/main/com/android/ahat/Summarizer.java b/tools/ahat/src/main/com/android/ahat/Summarizer.java
index ab88c04..877ecf4 100644
--- a/tools/ahat/src/main/com/android/ahat/Summarizer.java
+++ b/tools/ahat/src/main/com/android/ahat/Summarizer.java
@@ -112,6 +112,13 @@
formatted.append(" overhead for ");
formatted.append(summarize(cls));
}
+
+ // Annotate BinderProxy with its interface name.
+ String binderInterface = inst.getBinderProxyInterfaceName();
+ if (binderInterface != null) {
+ formatted.appendFormat(" for %s", binderInterface);
+ }
+
return formatted;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
index 0511798..141bdd9 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java
@@ -145,6 +145,21 @@
return null;
}
+ @Override public String getBinderProxyInterfaceName() {
+ if (isInstanceOfClass("android.os.BinderProxy")) {
+ for (AhatInstance inst : getReverseReferences()) {
+ String className = inst.getClassName();
+ if (className.endsWith("$Stub$Proxy")) {
+ Value value = inst.getField("mRemote");
+ if (value != null && value.asAhatInstance() == this) {
+ return className.substring(0, className.lastIndexOf("$Stub$Proxy"));
+ }
+ }
+ }
+ }
+ return null;
+ }
+
@Override public AhatInstance getAssociatedBitmapInstance() {
return getBitmapInfo() == null ? null : this;
}
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
index c85a057..3aae11e 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java
@@ -490,6 +490,17 @@
}
/**
+ * Returns the name of the Binder proxy interface associated with this object. Only applies to
+ * instances of android.os.BinderProxy. If this is an instance of BinderProxy,
+ * returns the fully qualified binder interface name, otherwise returns null.
+ *
+ * @return the name of the binder interface associated with this object
+ */
+ public String getBinderProxyInterfaceName() {
+ return null;
+ }
+
+ /**
* Returns the android.graphics.Bitmap instance associated with this object.
* Instances of android.graphics.Bitmap return themselves. If this is a
* byte[] array containing pixel data for an instance of
diff --git a/tools/ahat/src/test-dump/DumpedStuff.java b/tools/ahat/src/test-dump/DumpedStuff.java
index 804a3a3..50b8878 100644
--- a/tools/ahat/src/test-dump/DumpedStuff.java
+++ b/tools/ahat/src/test-dump/DumpedStuff.java
@@ -124,6 +124,35 @@
}
}
+ private static class IDumpedManager {
+ public static class Stub {
+ public static class Proxy {
+ android.os.IBinder mRemote;
+ Proxy(android.os.IBinder binderProxy) {
+ mRemote = binderProxy;
+ }
+ }
+ }
+ }
+
+ private static class IBinderInterfaceImpostor {
+ public static class Stub {
+ public static class Proxy {
+ android.os.IBinder mFakeRemote = new android.os.BinderProxy();
+ Proxy(android.os.IBinder binderProxy) {
+ mFakeRemote = binderProxy;
+ }
+ }
+ }
+ }
+
+ private static class BinderProxyCarrier {
+ android.os.IBinder mRemote;
+ BinderProxyCarrier(android.os.IBinder binderProxy) {
+ mRemote = binderProxy;
+ }
+ }
+
public String basicString = "hello, world";
public String nonAscii = "Sigma (Ʃ) is not ASCII";
public String embeddedZero = "embedded\0..."; // Non-ASCII for string compression purposes.
@@ -158,6 +187,12 @@
public int[] modifiedArray;
public Object objectAllocatedAtKnownSite;
public Object objectAllocatedAtKnownSubSite;
+ public android.os.IBinder correctBinderProxy = new android.os.BinderProxy();
+ public android.os.IBinder imposedBinderProxy = new android.os.BinderProxy();
+ public android.os.IBinder carriedBinderProxy = new android.os.BinderProxy();
+ Object correctBinderProxyObject = new IDumpedManager.Stub.Proxy(correctBinderProxy);
+ Object impostorBinderProxyObject = new IBinderInterfaceImpostor.Stub.Proxy(imposedBinderProxy);
+ Object carrierBinderProxyObject = new BinderProxyCarrier(carriedBinderProxy);
// Allocate those objects that we need to not be GC'd before taking the heap
// dump.
diff --git a/tools/ahat/src/test-dump/android/os/BinderProxy.java b/tools/ahat/src/test-dump/android/os/BinderProxy.java
new file mode 100644
index 0000000..5f35c61
--- /dev/null
+++ b/tools/ahat/src/test-dump/android/os/BinderProxy.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 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 android.os;
+
+/** Fake android.os.BinderProxy class that does absolutely nothing. */
+public class BinderProxy implements IBinder {}
diff --git a/tools/ahat/src/test-dump/android/os/IBinder.java b/tools/ahat/src/test-dump/android/os/IBinder.java
new file mode 100644
index 0000000..6f01468
--- /dev/null
+++ b/tools/ahat/src/test-dump/android/os/IBinder.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 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 android.os;
+
+/** Fake android.os.IBinder that means nothing. */
+public interface IBinder {}
diff --git a/tools/ahat/src/test/com/android/ahat/InstanceTest.java b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
index 196eb1e..57aa31f 100644
--- a/tools/ahat/src/test/com/android/ahat/InstanceTest.java
+++ b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
@@ -549,4 +549,18 @@
// Other kinds of objects should not have associated classes for overhead.
assertNull(cls.getAssociatedClassForOverhead());
}
+
+ @Test
+ public void binderProxy() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+
+ AhatInstance correctObj = dump.getDumpedAhatInstance("correctBinderProxy");
+ assertEquals("DumpedStuff$IDumpedManager", correctObj.getBinderProxyInterfaceName());
+
+ AhatInstance imposedObj = dump.getDumpedAhatInstance("imposedBinderProxy");
+ assertNull(imposedObj.getBinderProxyInterfaceName());
+
+ AhatInstance carriedObj = dump.getDumpedAhatInstance("carriedBinderProxy");
+ assertNull(carriedObj.getBinderProxyInterfaceName());
+ }
}