Support (un)linkToDeath in Java.

Test: mma, hidl_test_java
Bug: 31632518
Change-Id: Ic324deb5b0b17340328a08e736e07b47a7bea59b
diff --git a/DeathRecipientType.cpp b/DeathRecipientType.cpp
index 952064c..5ac58fe 100644
--- a/DeathRecipientType.cpp
+++ b/DeathRecipientType.cpp
@@ -48,7 +48,8 @@
 }
 
 std::string DeathRecipientType::getJavaType(bool /* forInitializer */) const {
-    return "android.os.IBinder.DeathRecipient";
+    // TODO(b/33440494) decouple from hwbinder
+    return "android.os.IHwBinder.DeathRecipient";
 }
 
 std::string DeathRecipientType::getVtsType() const {
diff --git a/Interface.cpp b/Interface.cpp
index 29afee1..1e40bd2 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -97,11 +97,7 @@
                             << " == ::android::OK);\n";
                     }
                 },
-                {IMPL_STUB,
-                    [](auto &/*out*/) {
-                        // Do nothing
-                    }
-                }
+                {IMPL_STUB, nullptr}
             }, /*cppImpl*/
             {
                 {IMPL_HEADER,
@@ -111,15 +107,10 @@
                 },
                 {IMPL_PROXY,
                     [this](auto &out) {
-                        // TODO (b/31632518)
-                        out << "return false;";
+                        out << "return mRemote.linkToDeath(recipient, cookie);\n";
                     }
                 },
-                {IMPL_STUB,
-                    [this](auto &/*out*/) {
-                        // Do nothing
-                    }
-                }
+                {IMPL_STUB, nullptr}
             } /*javaImpl*/
     );
 }
@@ -159,29 +150,20 @@
                         out << "return false;\n";
                     }
                 },
-                {IMPL_STUB,
-                    [](auto &/*out*/) {
-                        // Do nothing
-                    }
-                }
+                {IMPL_STUB, nullptr /* don't generate code */}
             }, /*cppImpl*/
             {
                 {IMPL_HEADER,
                     [this](auto &out) {
-                        out << "return true;";
+                        out << "return true;\n";
                     }
                 },
                 {IMPL_PROXY,
                     [this](auto &out) {
-                        // TODO (b/31632518)
-                        out << "return false;";
+                        out << "return mRemote.unlinkToDeath(recipient);\n";
                     }
                 },
-                {IMPL_STUB,
-                    [this](auto &/*out*/) {
-                        // Do nothing
-                    }
-                }
+                {IMPL_STUB, nullptr /* don't generate code */}
             } /*javaImpl*/
     );
 }
diff --git a/Method.cpp b/Method.cpp
index a085497..e41ebd0 100644
--- a/Method.cpp
+++ b/Method.cpp
@@ -75,7 +75,9 @@
     CHECK(mIsHidlReserved);
     auto it = mCppImpl.find(type);
     if (it != mCppImpl.end()) {
-        it->second(out);
+        if (it->second != nullptr) {
+            it->second(out);
+        }
     }
 }
 
@@ -83,7 +85,9 @@
     CHECK(mIsHidlReserved);
     auto it = mJavaImpl.find(type);
     if (it != mJavaImpl.end()) {
-        it->second(out);
+        if (it->second != nullptr) {
+            it->second(out);
+        }
     }
 }
 
diff --git a/test/java_test/hidl_test_java_native.cpp b/test/java_test/hidl_test_java_native.cpp
index 654947b..f86e43f 100644
--- a/test/java_test/hidl_test_java_native.cpp
+++ b/test/java_test/hidl_test_java_native.cpp
@@ -104,6 +104,7 @@
 
     Return<void> callMeLater(const sp<IBazCallback>& cb) override;
     Return<void> iAmFreeNow() override;
+    Return<void> dieNow() override;
 
     Return<IBaz::SomeEnum> useAnEnum(IBaz::SomeEnum zzz) override;
 
@@ -538,6 +539,11 @@
     return Void();
 }
 
+Return<void> Baz::dieNow() {
+    exit(1);
+    return Void();
+}
+
 Return<IBaz::SomeEnum> Baz::useAnEnum(IBaz::SomeEnum zzz) {
     LOG(INFO) << "useAnEnum " << (int)zzz;
 
diff --git a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
index 0b28781..5252fbf 100644
--- a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
+++ b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
@@ -317,6 +317,41 @@
         return out.toString();
     }
 
+    final class HidlDeathRecipient implements HwBinder.DeathRecipient {
+        final Object mLock = new Object();
+        boolean mCalled = false;
+        long mCookie = 0;
+
+        @Override
+        public void serviceDied(long cookie) {
+            synchronized (mLock) {
+                mCalled = true;
+                mCookie = cookie;
+                mLock.notify();
+            }
+        }
+
+        public boolean cookieMatches(long cookie) {
+            synchronized (mLock) {
+                return mCookie == cookie;
+            }
+        }
+
+        public boolean waitUntilServiceDied(long timeoutMillis) {
+            synchronized(mLock) {
+                while (!mCalled) {
+                    try {
+                        mLock.wait(timeoutMillis);
+                    } catch (InterruptedException e) {
+                        continue; // Spin for another loop
+                    }
+                    break; // got notified or timeout hit
+                }
+                return mCalled;
+            }
+        }
+    };
+
     private void ExpectTrue(boolean x) {
         if (x) {
             return;
@@ -699,6 +734,26 @@
         proxy.callMeLater(new BazCallback());
         System.gc();
         proxy.iAmFreeNow();
+
+        // --- DEATH RECIPIENT TESTING ---
+        // This must always be done last, since it will kill the native server process
+        HidlDeathRecipient recipient1 = new HidlDeathRecipient();
+        HidlDeathRecipient recipient2 = new HidlDeathRecipient();
+
+        final int cookie1 = 0x1481;
+        final int cookie2 = 0x1482;
+        ExpectTrue(proxy.linkToDeath(recipient1, cookie1));
+        ExpectTrue(proxy.linkToDeath(recipient2, cookie2));
+        ExpectTrue(proxy.unlinkToDeath(recipient2));
+        try {
+            proxy.dieNow();
+        } catch (RuntimeException e) {
+            // Expected
+        }
+        ExpectTrue(recipient1.waitUntilServiceDied(2000 /*timeoutMillis*/));
+        ExpectTrue(!recipient2.waitUntilServiceDied(2000 /*timeoutMillis*/));
+        ExpectTrue(recipient1.cookieMatches(cookie1));
+        Log.d(TAG, "OK, exiting");
     }
 
     class Baz extends IBaz.Stub {
@@ -900,6 +955,10 @@
             }
         }
 
+        public void dieNow() {
+            // Not tested in Java
+        }
+
         public byte useAnEnum(byte zzz) {
             Log.d(TAG, "useAnEnum " + zzz);
             return SomeEnum.quux;