Add (un)linkToDeath to generated interfaces.

(un)linkToDeath is now a part of IBase, and can be
called on any generated HIDL interface. The implementation
is a no-op, except in proxy objects, which are by
definition a different process than the interface they point
to.

Since clients are not aware of the transport implementation, we must
wrap the transport-independent callback in a transport-specific
callback object. In case of binder, that object is a
hidl_binder_death_recipient.

The binder proxy object contains a list of registered death
recipients, as well as a mutex to protect access to the list.
The list is required to allow us to map back transport-independent
callbacks to transport-dependent callbacks in unlinkToDeath().

Bug: 31632518
Test: mma, hidl_test
Change-Id: I5083a8789dd706a886a8a09f8c733031a351a36a
diff --git a/Android.bp b/Android.bp
index 8eae5b5..7f32fcd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,7 @@
         "ArrayType.cpp",
         "CompoundType.cpp",
         "ConstantExpression.cpp",
+        "DeathRecipientType.cpp",
         "EnumType.cpp",
         "FQName.cpp",
         "HandleType.cpp",
diff --git a/DeathRecipientType.cpp b/DeathRecipientType.cpp
new file mode 100644
index 0000000..952064c
--- /dev/null
+++ b/DeathRecipientType.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "DeathRecipientType.h"
+
+#include <hidl-util/Formatter.h>
+#include <android-base/logging.h>
+
+namespace android {
+
+DeathRecipientType::DeathRecipientType() {}
+
+void DeathRecipientType::addNamedTypesToSet(std::set<const FQName> &) const {
+    // do nothing
+}
+
+std::string DeathRecipientType::getCppType(StorageMode mode,
+                                   bool specifyNamespaces) const {
+    const std::string base =
+          std::string(specifyNamespaces ? "::android::" : "")
+        + "sp<"
+        + (specifyNamespaces ? "::android::hardware::" : "")
+        + "hidl_death_recipient>";
+
+    switch (mode) {
+        case StorageMode_Stack:
+            return base;
+
+        case StorageMode_Argument:
+            return "const " + base + "&";
+
+        case StorageMode_Result:
+            return "const " + base + "*";
+    }
+}
+
+std::string DeathRecipientType::getJavaType(bool /* forInitializer */) const {
+    return "android.os.IBinder.DeathRecipient";
+}
+
+std::string DeathRecipientType::getVtsType() const {
+    return "TYPE_DEATH_RECIPIENT";
+}
+
+void DeathRecipientType::emitReaderWriter(
+        Formatter& out,
+        const std::string& /* name */,
+        const std::string& /* parcelObj */,
+        bool /* parcelObjIsPointer */,
+        bool /* isReader */,
+        ErrorMode /* mode */) const {
+    out << "LOG_ALWAYS_FATAL(\"DeathRecipient is only supported in passthrough mode\");\n";
+}
+
+bool DeathRecipientType::needsEmbeddedReadWrite() const {
+    return false;
+}
+
+bool DeathRecipientType::resultNeedsDeref() const {
+    return true;
+}
+
+bool DeathRecipientType::isJavaCompatible() const {
+    return true;
+}
+
+void DeathRecipientType::getAlignmentAndSize(size_t *align, size_t *size) const {
+    *align = *size = 0; // this object should only be used in passthrough mode
+}
+
+status_t DeathRecipientType::emitVtsTypeDeclarations(Formatter &out) const {
+    out << "type: " << getVtsType() << "\n";
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/DeathRecipientType.h b/DeathRecipientType.h
new file mode 100644
index 0000000..f0e9b6f
--- /dev/null
+++ b/DeathRecipientType.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef DEATH_RECIPIENT_TYPE_H_
+
+#define DEATH_RECIPIENT_TYPE_H_
+
+#include "Type.h"
+
+namespace android {
+
+struct DeathRecipientType : public Type {
+    DeathRecipientType();
+
+    void addNamedTypesToSet(std::set<const FQName> &set) const override;
+
+    std::string getCppType(
+            StorageMode mode,
+            bool specifyNamespaces) const override;
+
+    std::string getJavaType(bool forInitializer) const override;
+
+    std::string getVtsType() const override;
+
+    void emitReaderWriter(
+            Formatter &out,
+            const std::string &name,
+            const std::string &parcelObj,
+            bool parcelObjIsPointer,
+            bool isReader,
+            ErrorMode mode) const override;
+
+    bool needsEmbeddedReadWrite() const override;
+    bool resultNeedsDeref() const override;
+
+    bool isJavaCompatible() const override;
+
+    void getAlignmentAndSize(size_t *align, size_t *size) const override;
+
+    status_t emitVtsTypeDeclarations(Formatter &out) const override;
+};
+
+}  // namespace android
+
+#endif  // DEATH_RECIPIENT_TYPE_H_
+
diff --git a/Interface.cpp b/Interface.cpp
index 602b221..29afee1 100644
--- a/Interface.cpp
+++ b/Interface.cpp
@@ -17,7 +17,9 @@
 #include "Interface.h"
 
 #include "Annotation.h"
+#include "DeathRecipientType.h"
 #include "Method.h"
+#include "ScalarType.h"
 #include "StringType.h"
 #include "VectorType.h"
 
@@ -46,6 +48,8 @@
     FIRST_HIDL_TRANSACTION  = 0x00f00000,
     HIDL_DESCRIPTOR_CHAIN_TRANSACTION = FIRST_HIDL_TRANSACTION,
     HIDL_SYSPROPS_CHANGED_TRANSACTION,
+    HIDL_LINK_TO_DEATH_TRANSACTION,
+    HIDL_UNLINK_TO_DEATH_TRANSACTION,
     LAST_HIDL_TRANSACTION   = 0x00ffffff,
 };
 
@@ -55,12 +59,132 @@
       mIsJavaCompatibleInProgress(false) {
     mReservedMethods.push_back(createDescriptorChainMethod());
     mReservedMethods.push_back(createSyspropsChangedMethod());
+    mReservedMethods.push_back(createLinkToDeathMethod());
+    mReservedMethods.push_back(createUnlinkToDeathMethod());
 }
 
 std::string Interface::typeName() const {
     return "interface " + localName();
 }
 
+Method *Interface::createLinkToDeathMethod() const {
+    auto *results = new std::vector<TypedVar *> {
+        new TypedVar("success", new ScalarType(ScalarType::KIND_BOOL)) };
+    auto *args = new std::vector<TypedVar *> {
+        new TypedVar("recipient", new DeathRecipientType()),
+        new TypedVar("cookie", new ScalarType(ScalarType::KIND_UINT64)) };
+
+    return new Method("linkToDeath",
+            args,
+            results,
+            false /*oneway */,
+            new std::vector<Annotation *>(),
+            HIDL_LINK_TO_DEATH_TRANSACTION,
+            {
+                {IMPL_HEADER,
+                    [](auto &out) {
+                        out << "(void)cookie;\n"
+                            << "return (recipient != nullptr);\n";
+                    }
+                },
+                {IMPL_PROXY,
+                    [](auto &out) {
+                        out << "::android::hardware::hidl_binder_death_recipient *binder_recipient"
+                            << " = new ::android::hardware::hidl_binder_death_recipient(recipient, cookie, this);\n"
+                            << "std::unique_lock<std::mutex> lock(_hidl_mMutex);\n"
+                            << "_hidl_mDeathRecipients.push_back(binder_recipient);\n"
+                            << "return (remote()->linkToDeath(binder_recipient)"
+                            << " == ::android::OK);\n";
+                    }
+                },
+                {IMPL_STUB,
+                    [](auto &/*out*/) {
+                        // Do nothing
+                    }
+                }
+            }, /*cppImpl*/
+            {
+                {IMPL_HEADER,
+                    [this](auto &out) {
+                        out << "return true;";
+                    }
+                },
+                {IMPL_PROXY,
+                    [this](auto &out) {
+                        // TODO (b/31632518)
+                        out << "return false;";
+                    }
+                },
+                {IMPL_STUB,
+                    [this](auto &/*out*/) {
+                        // Do nothing
+                    }
+                }
+            } /*javaImpl*/
+    );
+}
+
+Method *Interface::createUnlinkToDeathMethod() const {
+    auto *results = new std::vector<TypedVar *> {
+        new TypedVar("success", new ScalarType(ScalarType::KIND_BOOL)) };
+    auto *args = new std::vector<TypedVar *> {
+        new TypedVar("recipient", new DeathRecipientType()) };
+
+    return new Method("unlinkToDeath",
+            args,
+            results,
+            false /*oneway */,
+            new std::vector<Annotation *>(),
+            HIDL_UNLINK_TO_DEATH_TRANSACTION,
+            {
+                {IMPL_HEADER,
+                    [](auto &out) {
+                        out << "return (recipient != nullptr);\n";
+                    }
+                },
+                {IMPL_PROXY,
+                    [](auto &out) {
+                        out << "std::unique_lock<std::mutex> lock(_hidl_mMutex);\n"
+                            << "for (auto it = _hidl_mDeathRecipients.begin();"
+                            << "it != _hidl_mDeathRecipients.end();"
+                            << "++it) {\n";
+                        out.indent([&] {
+                            out.sIf("(*it)->getRecipient() == recipient", [&] {
+                                out << "::android::status_t status = remote()->unlinkToDeath(*it);\n"
+                                    << "_hidl_mDeathRecipients.erase(it);\n"
+                                    << "return status == ::android::OK;\n";
+                                });
+                            });
+                        out << "}\n";
+                        out << "return false;\n";
+                    }
+                },
+                {IMPL_STUB,
+                    [](auto &/*out*/) {
+                        // Do nothing
+                    }
+                }
+            }, /*cppImpl*/
+            {
+                {IMPL_HEADER,
+                    [this](auto &out) {
+                        out << "return true;";
+                    }
+                },
+                {IMPL_PROXY,
+                    [this](auto &out) {
+                        // TODO (b/31632518)
+                        out << "return false;";
+                    }
+                },
+                {IMPL_STUB,
+                    [this](auto &/*out*/) {
+                        // Do nothing
+                    }
+                }
+            } /*javaImpl*/
+    );
+}
 Method *Interface::createSyspropsChangedMethod() const {
     return new Method("notifySyspropsChanged",
             new std::vector<TypedVar *>() /*args */,
@@ -68,13 +192,13 @@
             true /*oneway */,
             new std::vector<Annotation *>(),
             HIDL_SYSPROPS_CHANGED_TRANSACTION,
-            [this](auto &out) { /*cppImpl */
+            { { IMPL_HEADER, [this](auto &out) {
                 out << "::android::report_sysprop_change();\n";
                 out << "return ::android::hardware::Void();";
-            },
-            [this](auto &out) { /* javaImpl */
+            } } }, /*cppImpl */
+            { { IMPL_HEADER, [](auto &out) { /* javaImpl */
                 out << "android.os.SystemProperties.reportSyspropChanged();";
-            }
+            } } } /*javaImpl */
     );
 }
 
@@ -90,7 +214,7 @@
         false /* oneway */,
         new std::vector<Annotation *>(),
         HIDL_DESCRIPTOR_CHAIN_TRANSACTION,
-        [this](auto &out) { /* cppImpl */
+        { { IMPL_HEADER, [this](auto &out) {
             std::vector<const Interface *> chain = typeChain();
             out << "_hidl_cb(";
             out.block([&] {
@@ -100,8 +224,8 @@
             });
             out << ");\n";
             out << "return ::android::hardware::Void();";
-        },
-        [this](auto &out) { /* javaImpl */
+        } } }, /* cppImpl */
+        { { IMPL_HEADER, [this](auto &out) {
             std::vector<const Interface *> chain = typeChain();
             out << "return new java.util.ArrayList<String>(java.util.Arrays.asList(\n";
             out.indent(); out.indent();
@@ -112,7 +236,8 @@
             }
             out << "));";
             out.unindent(); out.unindent();
-        });
+         } } } /* javaImpl */
+    );
 }
 
 
diff --git a/Interface.h b/Interface.h
index a21a036..8542f18 100644
--- a/Interface.h
+++ b/Interface.h
@@ -106,6 +106,8 @@
     mutable bool mIsJavaCompatibleInProgress;
     Method *createDescriptorChainMethod() const;
     Method *createSyspropsChangedMethod() const;
+    Method *createLinkToDeathMethod() const;
+    Method *createUnlinkToDeathMethod() const;
 
     DISALLOW_COPY_AND_ASSIGN(Interface);
 };
diff --git a/Method.cpp b/Method.cpp
index bfd28c9..a085497 100644
--- a/Method.cpp
+++ b/Method.cpp
@@ -71,20 +71,32 @@
     return *mAnnotations;
 }
 
-void Method::cppImpl(Formatter &out) const {
+void Method::cppImpl(MethodImplType type, Formatter &out) const {
     CHECK(mIsHidlReserved);
-    if (mCppImpl) {
-        mCppImpl(out);
+    auto it = mCppImpl.find(type);
+    if (it != mCppImpl.end()) {
+        it->second(out);
     }
 }
 
-void Method::javaImpl(Formatter &out) const {
+void Method::javaImpl(MethodImplType type, Formatter &out) const {
     CHECK(mIsHidlReserved);
-    if (mJavaImpl) {
-        mJavaImpl(out);
+    auto it = mJavaImpl.find(type);
+    if (it != mJavaImpl.end()) {
+        it->second(out);
     }
 }
 
+bool Method::overridesCppImpl(MethodImplType type) const {
+    CHECK(mIsHidlReserved);
+    return mCppImpl.find(type) != mCppImpl.end();
+}
+
+bool Method::overridesJavaImpl(MethodImplType type) const {
+    CHECK(mIsHidlReserved);
+    return mJavaImpl.find(type) != mJavaImpl.end();
+}
+
 void Method::setSerialId(size_t serial) {
     CHECK(!mIsHidlReserved);
     mSerial = serial;
diff --git a/Method.h b/Method.h
index 6981b72..ac681a5 100644
--- a/Method.h
+++ b/Method.h
@@ -21,6 +21,7 @@
 #include <android-base/macros.h>
 #include <functional>
 #include <hidl-util/Formatter.h>
+#include <map>
 #include <set>
 #include <string>
 #include <vector>
@@ -34,7 +35,13 @@
 struct TypedVar;
 struct TypedVarVector;
 
-using MethodImpl = std::function<void(Formatter &)>;
+enum MethodImplType {
+    IMPL_HEADER,
+    IMPL_PROXY,
+    IMPL_STUB
+};
+
+using MethodImpl = std::map<MethodImplType, std::function<void(Formatter &)>>;
 
 struct Method {
     Method(const char *name,
@@ -55,8 +62,10 @@
     const std::vector<TypedVar *> &args() const;
     const std::vector<TypedVar *> &results() const;
     bool isOneway() const { return mOneway; }
-    void cppImpl(Formatter &out) const;
-    void javaImpl(Formatter &out) const;
+    bool overridesCppImpl(MethodImplType type) const;
+    bool overridesJavaImpl(MethodImplType type) const;
+    void cppImpl(MethodImplType type, Formatter &out) const;
+    void javaImpl(MethodImplType type, Formatter &out) const;
     bool isHidlReserved() const { return mIsHidlReserved; }
     const std::vector<Annotation *> &annotations() const;
 
@@ -86,10 +95,10 @@
     std::vector<Annotation *> *mAnnotations;
 
     bool mIsHidlReserved = false;
-    // The following fields has no meaning if mIsHidlReserved is false.
+    // The following fields have no meaning if mIsHidlReserved is false.
     // hard-coded implementation for HIDL reserved methods.
-    MethodImpl mCppImpl = nullptr;
-    MethodImpl mJavaImpl = nullptr;
+    MethodImpl mCppImpl;
+    MethodImpl mJavaImpl;
 
     DISALLOW_COPY_AND_ASSIGN(Method);
 };
diff --git a/generateCpp.cpp b/generateCpp.cpp
index 7ae94b0..04e8f0b 100644
--- a/generateCpp.cpp
+++ b/generateCpp.cpp
@@ -392,7 +392,7 @@
                 }
                 out << " {\n";
                 out.indent();
-                method->cppImpl(out);
+                method->cppImpl(IMPL_HEADER, out);
                 out.unindent();
                 out << "\n}\n";
             } else {
@@ -829,6 +829,8 @@
     out << "#ifndef " << guard << "\n";
     out << "#define " << guard << "\n\n";
 
+    out << "#include <hidl/HidlTransportSupport.h>\n\n";
+
     std::vector<std::string> packageComponents;
     getPackageAndVersionComponents(
             &packageComponents, false /* cpp_compatible */);
@@ -866,6 +868,12 @@
     }
 
     out.unindent();
+    out << "private:\n";
+    out.indent();
+    out << "std::mutex _hidl_mMutex;\n"
+        << "std::vector<::android::sp<::android::hardware::hidl_binder_death_recipient>>"
+        << " _hidl_mDeathRecipients;\n";
+    out.unindent();
     out << "};\n\n";
 
     enterLeaveNamespace(out, false /* enter */);
@@ -1117,6 +1125,13 @@
 
     out.indent();
 
+    if (method->isHidlReserved() && method->overridesCppImpl(IMPL_PROXY)) {
+        method->cppImpl(IMPL_PROXY, out);
+        out.unindent();
+        out << "}\n\n";
+        return OK;
+    }
+
     if (returnsValue && elidedReturn == nullptr) {
         generateCheckNonNull(out, "_hidl_cb");
     }
@@ -1431,6 +1446,12 @@
 
 status_t AST::generateStubSourceForMethod(
         Formatter &out, const Interface *iface, const Method *method) const {
+    if (method->isHidlReserved() && method->overridesCppImpl(IMPL_STUB)) {
+        method->cppImpl(IMPL_STUB, out);
+        out << "break;\n";
+        return OK;
+    }
+
     out << "if (!_hidl_data.enforceInterface(";
 
     out << iface->fqName().cppNamespace()
diff --git a/generateJava.cpp b/generateJava.cpp
index 6faf52d..70ee23e 100644
--- a/generateJava.cpp
+++ b/generateJava.cpp
@@ -315,6 +315,12 @@
         out << ") {\n";
         out.indent();
 
+        if (method->isHidlReserved() && method->overridesJavaImpl(IMPL_PROXY)) {
+            method->javaImpl(IMPL_PROXY, out);
+            out.unindent();
+            out << "}\n";
+            continue;
+        }
         out << "android.os.HwParcel _hidl_request = new android.os.HwParcel();\n";
         out << "_hidl_request.writeInterfaceToken("
             << superInterface->fullJavaName()
@@ -414,9 +420,12 @@
             << resultType
             << " "
             << method->name()
-            << "() {\n";
+            << "("
+            << Method::GetJavaArgSignature(method->args())
+            << ") {\n";
+
         out.indent();
-        method->javaImpl(out);
+        method->javaImpl(IMPL_HEADER, out);
         out.unindent();
         out << "\n}\n\n";
     }
@@ -466,6 +475,13 @@
             << " */:\n{\n";
 
         out.indent();
+        if (method->isHidlReserved() && method->overridesJavaImpl(IMPL_STUB)) {
+            method->javaImpl(IMPL_STUB, out);
+            out.unindent();
+            out << "break;\n";
+            out << "}\n\n";
+            continue;
+        }
 
         out << "_hidl_request.enforceInterface("
             << superInterface->fullJavaName()
diff --git a/hidl-gen_l.ll b/hidl-gen_l.ll
index a65bc9f..e6b4bef 100644
--- a/hidl-gen_l.ll
+++ b/hidl-gen_l.ll
@@ -34,6 +34,7 @@
 #include "ArrayType.h"
 #include "CompoundType.h"
 #include "ConstantExpression.h"
+#include "DeathRecipientType.h"
 #include "EnumType.h"
 #include "HandleType.h"
 #include "MemoryType.h"
@@ -111,6 +112,7 @@
 "float"			{ SCALAR_TYPE(KIND_FLOAT); }
 "double"		{ SCALAR_TYPE(KIND_DOUBLE); }
 
+"death_recipient"	{ yylval->type = new DeathRecipientType; return token::TYPE; }
 "handle"		{ yylval->type = new HandleType; return token::TYPE; }
 "memory"		{ yylval->type = new MemoryType; return token::TYPE; }
 "pointer"		{ yylval->type = new PointerType; return token::TYPE; }
diff --git a/hidl-gen_y.yy b/hidl-gen_y.yy
index 9c0e75b..75843c5 100644
--- a/hidl-gen_y.yy
+++ b/hidl-gen_y.yy
@@ -59,8 +59,8 @@
 
         // Inherited names by interfaces from IInterface / IBinder
         "onAsBinder", "asBinder", "queryLocalInterface", "getInterfaceDescriptor", "isBinderAlive",
-        "pingBinder", "dump", "transact", "linkToDeath", "unlinkToDeath", "checkSubclass",
-        "attachObject", "findObject", "detachObject", "localBinder", "remoteBinder", "mImpl",
+        "pingBinder", "dump", "transact", "checkSubclass", "attachObject", "findObject",
+        "detachObject", "localBinder", "remoteBinder", "mImpl",
     });
     std::string idstr(identifier);
     if (std::find(reserved.begin(), reserved.end(), idstr) != reserved.end()) {
diff --git a/test/main.cpp b/test/main.cpp
index 66b3f34..fb0e773 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -97,14 +97,17 @@
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_death_recipient;
 using ::android::hardware::hidl_memory;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
+using ::android::hidl::base::V1_0::IBase;
 using ::android::hidl::manager::V1_0::IServiceManager;
 using ::android::hidl::manager::V1_0::IServiceNotification;
 using ::android::hidl::memory::V1_0::IAllocator;
 using ::android::hidl::memory::V1_0::IMemory;
 using ::android::sp;
+using ::android::wp;
 using ::android::to_string;
 using ::android::Mutex;
 using ::android::MultiDimensionalToString;
@@ -318,6 +321,7 @@
     sp<IMemoryTest> memoryTest;
     sp<IFetcher> fetcher;
     sp<IFoo> foo;
+    sp<IFoo> dyingFoo;
     sp<IBar> bar;
     sp<IFooCallback> fooCb;
     sp<IGraph> graphInterface;
@@ -358,6 +362,10 @@
         ASSERT_NE(foo, nullptr);
         ASSERT_EQ(foo->isRemote(), gMode == BINDERIZED);
 
+        dyingFoo = IFoo::getService("dyingFoo", gMode == PASSTHROUGH /* getStub */);
+        ASSERT_NE(foo, nullptr);
+        ASSERT_EQ(foo->isRemote(), gMode == BINDERIZED);
+
         bar = IBar::getService("foo", gMode == PASSTHROUGH /* getStub */);
         ASSERT_NE(bar, nullptr);
         ASSERT_EQ(bar->isRemote(), gMode == BINDERIZED);
@@ -379,13 +387,21 @@
         ASSERT_NE(validationPointerInterface, nullptr);
     }
 
+    void killServer(const char *serverName) {
+        for (const auto &pair : mPids) {
+            if (pair.first == serverName) {
+                ::killServer(pair.second, pair.first.c_str());
+            }
+        }
+    }
+
     virtual void TearDown() {
         // clean up by killing server processes.
         ALOGI("Environment tear-down beginning...");
         ALOGI("Killing servers...");
         size_t i = 0;
         for (const auto &pair : mPids) {
-            killServer(pair.second, pair.first.c_str());
+            ::killServer(pair.second, pair.first.c_str());
         }
         ALOGI("Servers all killed.");
         ALOGI("Environment tear-down complete.");
@@ -417,6 +433,7 @@
         addServer<IParent>("parent");
         addServer<IFetcher>("fetcher");
         addServer<IBar>("foo");
+        addServer<IFoo>("dyingFoo");
         addServer<IFooCallback>("foo callback");
         addServer<IGraph>("graph");
         addServer<IPointer>("pointer");
@@ -434,12 +451,23 @@
     sp<IMemoryTest> memoryTest;
     sp<IFetcher> fetcher;
     sp<IFoo> foo;
+    sp<IFoo> dyingFoo;
     sp<IBar> bar;
     sp<IFooCallback> fooCb;
     sp<IGraph> graphInterface;
     sp<IPointer> pointerInterface;
     sp<IPointer> validationPointerInterface;
 
+    void killServer(const char *serverName) {
+        HidlEnvironmentBase *env;
+        if (gMode == BINDERIZED) {
+            env = gBinderizedEnvironment;
+        } else {
+            env = gPassthroughEnvironment;
+        }
+        env->killServer(serverName);
+    }
+
     virtual void SetUp() override {
         ALOGI("Test setup beginning...");
         HidlEnvironmentBase *env;
@@ -453,6 +481,7 @@
         memoryTest = env->memoryTest;
         fetcher = env->fetcher;
         foo = env->foo;
+        dyingFoo = env->dyingFoo;
         bar = env->bar;
         fooCb = env->fooCb;
         graphInterface = env->graphInterface;
@@ -1154,6 +1183,65 @@
     EXPECT_OK(foo->closeHandles());
 }
 
+struct HidlDeathRecipient : hidl_death_recipient {
+    std::mutex mutex;
+    std::condition_variable condition;
+    wp<IBase> who;
+    bool fired = false;
+    uint64_t cookie = 0;
+
+    virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) {
+        std::unique_lock<std::mutex> lock(mutex);
+        fired = true;
+        this->cookie = cookie;
+        this->who = who;
+        condition.notify_one();
+    };
+};
+
+TEST_F(HidlTest, DeathRecipientTest) {
+    // Need a threadpool to receive death calls from the kernel
+    ProcessState::self()->setThreadPoolMaxThreadCount(0);
+    ProcessState::self()->startThreadPool();
+
+    sp<HidlDeathRecipient> recipient = new HidlDeathRecipient();
+    sp<HidlDeathRecipient> recipient2 = new HidlDeathRecipient();
+
+    EXPECT_TRUE(dyingFoo->linkToDeath(recipient, 0x1481));
+    EXPECT_TRUE(dyingFoo->linkToDeath(recipient2, 0x2592));
+    EXPECT_TRUE(dyingFoo->unlinkToDeath(recipient2));
+
+    if (gMode != BINDERIZED) {
+        // Passthrough doesn't fire, nor does it keep state of
+        // registered death recipients (so it won't fail unlinking
+        // the same recipient twice).
+        return;
+    }
+
+    EXPECT_FALSE(dyingFoo->unlinkToDeath(recipient2));
+    killServer("dyingFoo");
+
+    std::unique_lock<std::mutex> lock(recipient->mutex);
+    recipient->condition.wait_for(lock, std::chrono::milliseconds(1000), [&recipient]() {
+            return recipient->fired;
+    });
+    EXPECT_TRUE(recipient->fired);
+    EXPECT_EQ(recipient->cookie, 0x1481u);
+    EXPECT_EQ(recipient->who, dyingFoo);
+    std::unique_lock<std::mutex> lock2(recipient2->mutex);
+    recipient2->condition.wait_for(lock2, std::chrono::milliseconds(1000), [&recipient2]() {
+            return recipient2->fired;
+    });
+    EXPECT_FALSE(recipient2->fired);
+
+    // Verify servicemanager dropped its reference too
+    sp<IFoo> deadFoo = IFoo::getService("dyingFoo", false);
+    if (deadFoo != nullptr) {
+        // Got a passthrough
+        EXPECT_FALSE(deadFoo->isRemote());
+    }
+}
+
 TEST_F(HidlTest, BarThisIsNewTest) {
     // Now the tricky part, get access to the derived interface.
     ALOGI("CLIENT call thisIsNew.");