libbinder_ndk: ABBinder objects are tagged.

This allows us to always cast an object directly to the underlying
object rather than temporarily putting it behind a proxy. This also
simplifies the AIBinder_associateClass API greatly.

As a side-effect, this also fixes reading null parcelables from
binder buffers due to a previously missing check.

Bug: 111445392
Test: runtests.sh
Change-Id: I0c9a75132f9da35a2500b1e83f218b180b2dda36
diff --git a/libs/binder/ndk/AIBinder.cpp b/libs/binder/ndk/AIBinder.cpp
index d0ce98d..fe07914 100644
--- a/libs/binder/ndk/AIBinder.cpp
+++ b/libs/binder/ndk/AIBinder.cpp
@@ -28,14 +28,29 @@
 using ::android::String16;
 using ::android::wp;
 
+namespace ABBinderTag {
+
+static const void* kId = "ABBinder";
+static void* kValue = static_cast<void*>(new bool{true});
+void cleanId(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */};
+
+static void attach(const sp<IBinder>& binder) {
+    binder->attachObject(kId, kValue, nullptr /*cookie*/, cleanId);
+}
+static bool has(const sp<IBinder>& binder) {
+    return binder != nullptr && binder->findObject(kId) == kValue;
+}
+
+} // namespace ABBinderTag
+
 AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {}
 AIBinder::~AIBinder() {}
 
-sp<AIBinder> AIBinder::associateClass(const AIBinder_Class* clazz) {
+bool AIBinder::associateClass(const AIBinder_Class* clazz) {
     using ::android::String8;
 
-    if (clazz == nullptr) return nullptr;
-    if (mClazz == clazz) return this;
+    if (clazz == nullptr) return false;
+    if (mClazz == clazz) return true;
 
     String8 newDescriptor(clazz->getInterfaceDescriptor());
 
@@ -45,42 +60,31 @@
             LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
                        << "' match during associateClass, but they are different class objects. "
                           "Class descriptor collision?";
-            return nullptr;
+        } else {
+            LOG(ERROR) << __func__
+                       << ": Class cannot be associated on object which already has a class. "
+                          "Trying to associate to '"
+                       << newDescriptor.c_str() << "' but already set to '"
+                       << currentDescriptor.c_str() << "'.";
         }
 
-        LOG(ERROR) << __func__
-                   << ": Class cannot be associated on object which already has a class. Trying to "
-                      "associate to '"
-                   << newDescriptor.c_str() << "' but already set to '" << currentDescriptor.c_str()
-                   << "'.";
-        return nullptr;
+        // always a failure because we know mClazz != clazz
+        return false;
     }
 
+    CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor
+
     String8 descriptor(getBinder()->getInterfaceDescriptor());
     if (descriptor != newDescriptor) {
         LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str()
                    << "' but descriptor is actually '" << descriptor.c_str() << "'.";
-        return nullptr;
+        return false;
     }
 
-    // The descriptor matches, so if it is local, this is guaranteed to be the libbinder_ndk class.
-    // An error here can occur if there is a conflict between descriptors (two unrelated classes
-    // define the same descriptor), but this should never happen.
-
-    // if this is a local ABBinder, mClazz should be non-null
-    CHECK(asABBinder() == nullptr);
-    CHECK(asABpBinder() != nullptr);
-
-    if (!isRemote()) {
-        // ABpBinder but proxy to a local object. Therefore that local object must be an ABBinder.
-        ABBinder* binder = static_cast<ABBinder*>(getBinder().get());
-        return binder;
-    }
-
-    // This is a remote object
+    // if this is a local object, it's not one known to libbinder_ndk
     mClazz = clazz;
 
-    return this;
+    return true;
 }
 
 ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
@@ -111,12 +115,22 @@
     }
 }
 
-ABpBinder::ABpBinder(::android::sp<::android::IBinder> binder)
+ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder)
       : AIBinder(nullptr /*clazz*/), BpRefBase(binder) {
     CHECK(binder != nullptr);
 }
 ABpBinder::~ABpBinder() {}
 
+sp<AIBinder> ABpBinder::fromBinder(const ::android::sp<::android::IBinder>& binder) {
+    if (binder == nullptr) {
+        return nullptr;
+    }
+    if (ABBinderTag::has(binder)) {
+        return static_cast<ABBinder*>(binder.get());
+    }
+    return new ABpBinder(binder);
+}
+
 struct AIBinder_Weak {
     wp<AIBinder> binder;
 };
@@ -162,9 +176,11 @@
 
     void* userData = clazz->onCreate(args);
 
-    AIBinder* ret = new ABBinder(clazz, userData);
-    AIBinder_incStrong(ret);
-    return ret;
+    sp<AIBinder> ret = new ABBinder(clazz, userData);
+    ABBinderTag::attach(ret->getBinder());
+
+    AIBinder_incStrong(ret.get());
+    return ret.get();
 }
 
 bool AIBinder_isRemote(AIBinder* binder) {
@@ -200,23 +216,12 @@
     return binder->getStrongCount();
 }
 
-void AIBinder_associateClass(AIBinder** binder, const AIBinder_Class* clazz) {
-    if (binder == nullptr || *binder == nullptr) {
-        return;
+bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) {
+    if (binder == nullptr) {
+        return false;
     }
 
-    sp<AIBinder> result = (*binder)->associateClass(clazz);
-
-    // This function takes one refcount of 'binder' and delivers one refcount of 'result' to the
-    // callee. First we give the callee their refcount and then take it away from binder. This is
-    // done in this order in order to handle the case that the result and the binder are the same
-    // object.
-    if (result != nullptr) {
-        AIBinder_incStrong(result.get());
-    }
-    AIBinder_decStrong(*binder);
-
-    *binder = result.get(); // Maybe no-op
+    return binder->associateClass(clazz);
 }
 
 const AIBinder_Class* AIBinder_getClass(AIBinder* binder) {
diff --git a/libs/binder/ndk/AIBinder_internal.h b/libs/binder/ndk/AIBinder_internal.h
index d44b937..ed3b37b 100644
--- a/libs/binder/ndk/AIBinder_internal.h
+++ b/libs/binder/ndk/AIBinder_internal.h
@@ -35,24 +35,14 @@
     AIBinder(const AIBinder_Class* clazz);
     virtual ~AIBinder();
 
-    // This returns an AIBinder object with this class associated. If the class is already
-    // associated, 'this' will be returned. If there is a local AIBinder implementation, that will
-    // be returned. If this is a remote object, the class will be associated and this will be ready
-    // to be used for transactions.
-    ::android::sp<AIBinder> associateClass(const AIBinder_Class* clazz);
+    bool associateClass(const AIBinder_Class* clazz);
     const AIBinder_Class* getClass() const { return mClazz; }
 
-    // This does not create the binder if it does not exist in the process.
     virtual ::android::sp<::android::IBinder> getBinder() = 0;
     virtual ABBinder* asABBinder() { return nullptr; }
     virtual ABpBinder* asABpBinder() { return nullptr; }
 
-    bool isRemote() {
-        auto binder = getBinder();
-        // if the binder is nullptr, then it is a local object which hasn't been sent out of process
-        // yet.
-        return binder != nullptr && binder->remoteBinder() != nullptr;
-    }
+    bool isRemote() { return getBinder()->remoteBinder() != nullptr; }
 
 private:
     // AIBinder instance is instance of this class for a local object. In order to transact on a
@@ -63,7 +53,6 @@
 
 // This is a local AIBinder object with a known class.
 struct ABBinder : public AIBinder, public ::android::BBinder {
-    ABBinder(const AIBinder_Class* clazz, void* userData);
     virtual ~ABBinder();
 
     void* getUserData() { return mUserData; }
@@ -76,6 +65,11 @@
                                ::android::Parcel* reply, binder_flags_t flags) override;
 
 private:
+    ABBinder(const AIBinder_Class* clazz, void* userData);
+
+    // only thing that should create an ABBinder
+    friend AIBinder* AIBinder_new(const AIBinder_Class*, void*);
+
     // Can contain implementation if this is a local binder. This can still be nullptr for a local
     // binder. If it is nullptr, the implication is the implementation state is entirely external to
     // this object and the functionality provided in the AIBinder_Class is sufficient.
@@ -85,11 +79,15 @@
 // This binder object may be remote or local (even though it is 'Bp'). It is not yet associated with
 // a class.
 struct ABpBinder : public AIBinder, public ::android::BpRefBase {
-    ABpBinder(::android::sp<::android::IBinder> binder);
+    static ::android::sp<AIBinder> fromBinder(const ::android::sp<::android::IBinder>& binder);
+
     virtual ~ABpBinder();
 
     ::android::sp<::android::IBinder> getBinder() override { return remote(); }
     ABpBinder* asABpBinder() override { return this; }
+
+private:
+    ABpBinder(const ::android::sp<::android::IBinder>& binder);
 };
 
 struct AIBinder_Class {
diff --git a/libs/binder/ndk/AParcel.cpp b/libs/binder/ndk/AParcel.cpp
index b63b138..f39e732 100644
--- a/libs/binder/ndk/AParcel.cpp
+++ b/libs/binder/ndk/AParcel.cpp
@@ -34,8 +34,9 @@
     if (status != EX_NONE) {
         return status;
     }
-    *binder = new ABpBinder(readBinder);
-    AIBinder_incStrong(*binder);
+    sp<AIBinder> ret = ABpBinder::fromBinder(readBinder);
+    AIBinder_incStrong(ret.get());
+    *binder = ret.get();
     return status;
 }
 binder_status_t AParcel_readNullableStrongBinder(const AParcel* parcel, AIBinder** binder) {
@@ -44,8 +45,9 @@
     if (status != EX_NONE) {
         return status;
     }
-    *binder = new ABpBinder(readBinder);
-    AIBinder_incStrong(*binder);
+    sp<AIBinder> ret = ABpBinder::fromBinder(readBinder);
+    AIBinder_incStrong(ret.get());
+    *binder = ret.get();
     return status;
 }
 
diff --git a/libs/binder/ndk/AServiceManager.cpp b/libs/binder/ndk/AServiceManager.cpp
index f61b914..3979945 100644
--- a/libs/binder/ndk/AServiceManager.cpp
+++ b/libs/binder/ndk/AServiceManager.cpp
@@ -41,7 +41,7 @@
     sp<IServiceManager> sm = defaultServiceManager();
     sp<IBinder> binder = sm->getService(String16(instance));
 
-    AIBinder* ret = new ABpBinder(binder);
-    AIBinder_incStrong(ret);
-    return ret;
+    sp<AIBinder> ret = ABpBinder::fromBinder(binder);
+    AIBinder_incStrong(ret.get());
+    return ret.get();
 }
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 23136a2..fb82ab6 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -167,11 +167,10 @@
  * However, if an object is just intended to be passed through to another process or used as a
  * handle this need not be called.
  *
- * The binder parameter may or may not be updated. If it is updated, the ownership of the original
- * object is transferred to the new object. If the class association fails, ownership of the binder
- * is lost, and it is set to nullptr.
+ * This returns true if the class association succeeds. If it fails, no change is made to the
+ * binder object.
  */
-void AIBinder_associateClass(AIBinder** binder, const AIBinder_Class* clazz);
+bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz);
 
 /*
  * Returns the class that this binder was constructed with or associated with.
diff --git a/libs/binder/ndk/test/iface.cpp b/libs/binder/ndk/test/iface.cpp
index eed09f0..1317a74 100644
--- a/libs/binder/ndk/test/iface.cpp
+++ b/libs/binder/ndk/test/iface.cpp
@@ -118,12 +118,15 @@
 
 sp<IFoo> IFoo::getService(const char* instance) {
     AIBinder* binder = AServiceManager_getService(instance); // maybe nullptr
-    AIBinder_associateClass(&binder, IFoo::kClass);
-
     if (binder == nullptr) {
         return nullptr;
     }
 
+    if (!AIBinder_associateClass(binder, IFoo::kClass)) {
+        AIBinder_decStrong(binder);
+        return nullptr;
+    }
+
     if (AIBinder_isRemote(binder)) {
         sp<IFoo> ret = new BpFoo(binder); // takes ownership of binder
         return ret;