1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "JHwRemoteBinder"
19#include <android-base/logging.h>
20
21#include "android_os_HwRemoteBinder.h"
22
23#include "android_os_HwParcel.h"
24
25#include <JNIHelp.h>
26#include <android_runtime/AndroidRuntime.h>
27#include <hidl/Status.h>
28#include <ScopedUtfChars.h>
29#include <nativehelper/ScopedLocalRef.h>
30
31#include "core_jni_helpers.h"
32
33using android::AndroidRuntime;
34
35#define PACKAGE_PATH    "android/os"
36#define CLASS_NAME      "HwRemoteBinder"
37#define CLASS_PATH      PACKAGE_PATH "/" CLASS_NAME
38
39namespace android {
40
41static struct fields_t {
42    jclass proxy_class;
43    jfieldID contextID;
44    jmethodID constructID;
45    jmethodID sendDeathNotice;
46} gProxyOffsets;
47
48static struct class_offsets_t
49{
50    jmethodID mGetName;
51} gClassOffsets;
52
53static JavaVM* jnienv_to_javavm(JNIEnv* env)
54{
55    JavaVM* vm;
56    return env->GetJavaVM(&vm) >= 0 ? vm : NULL;
57}
58
59static JNIEnv* javavm_to_jnienv(JavaVM* vm)
60{
61    JNIEnv* env;
62    return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
63}
64
65// ----------------------------------------------------------------------------
66class HwBinderDeathRecipient : public hardware::IBinder::DeathRecipient
67{
68public:
69    HwBinderDeathRecipient(JNIEnv* env, jobject object, jlong cookie, const sp<HwBinderDeathRecipientList>& list)
70        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
71          mObjectWeak(NULL), mCookie(cookie), mList(list)
72    {
73        // These objects manage their own lifetimes so are responsible for final bookkeeping.
74        // The list holds a strong reference to this object.
75        list->add(this);
76    }
77
78    void binderDied(const wp<hardware::IBinder>& who)
79    {
80        if (mObject != NULL) {
81            JNIEnv* env = javavm_to_jnienv(mVM);
82
83            env->CallStaticVoidMethod(gProxyOffsets.proxy_class, gProxyOffsets.sendDeathNotice, mObject, mCookie);
84            if (env->ExceptionCheck()) {
85                ALOGE("Uncaught exception returned from death notification.");
86                env->ExceptionClear();
87            }
88
89            // Serialize with our containing HwBinderDeathRecipientList so that we can't
90            // delete the global ref on mObject while the list is being iterated.
91            sp<HwBinderDeathRecipientList> list = mList.promote();
92            if (list != NULL) {
93                AutoMutex _l(list->lock());
94
95                // Demote from strong ref to weak after binderDied() has been delivered,
96                // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
97                mObjectWeak = env->NewWeakGlobalRef(mObject);
98                env->DeleteGlobalRef(mObject);
99                mObject = NULL;
100            }
101        }
102    }
103
104    void clearReference()
105    {
106        sp<HwBinderDeathRecipientList> list = mList.promote();
107        if (list != NULL) {
108            list->remove(this);
109        } else {
110            ALOGE("clearReference() on JDR %p but DRL wp purged", this);
111        }
112    }
113
114    bool matches(jobject obj) {
115        bool result;
116        JNIEnv* env = javavm_to_jnienv(mVM);
117
118        if (mObject != NULL) {
119            result = env->IsSameObject(obj, mObject);
120        } else {
121            jobject me = env->NewLocalRef(mObjectWeak);
122            result = env->IsSameObject(obj, me);
123            env->DeleteLocalRef(me);
124        }
125        return result;
126    }
127
128    void warnIfStillLive() {
129        if (mObject != NULL) {
130            // Okay, something is wrong -- we have a hard reference to a live death
131            // recipient on the VM side, but the list is being torn down.
132            JNIEnv* env = javavm_to_jnienv(mVM);
133            ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
134            ScopedLocalRef<jstring> nameRef(env,
135                    (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
136            ScopedUtfChars nameUtf(env, nameRef.get());
137            if (nameUtf.c_str() != NULL) {
138                ALOGW("BinderProxy is being destroyed but the application did not call "
139                        "unlinkToDeath to unlink all of its death recipients beforehand.  "
140                        "Releasing leaked death recipient: %s", nameUtf.c_str());
141            } else {
142                ALOGW("BinderProxy being destroyed; unable to get DR object name");
143                env->ExceptionClear();
144            }
145        }
146    }
147
148protected:
149    virtual ~HwBinderDeathRecipient()
150    {
151        JNIEnv* env = javavm_to_jnienv(mVM);
152        if (mObject != NULL) {
153            env->DeleteGlobalRef(mObject);
154        } else {
155            env->DeleteWeakGlobalRef(mObjectWeak);
156        }
157    }
158
159private:
160    JavaVM* const mVM;
161    jobject mObject;
162    jweak mObjectWeak; // will be a weak ref to the same VM-side DeathRecipient after binderDied()
163    jlong mCookie;
164    wp<HwBinderDeathRecipientList> mList;
165};
166// ----------------------------------------------------------------------------
167
168HwBinderDeathRecipientList::HwBinderDeathRecipientList() {
169}
170
171HwBinderDeathRecipientList::~HwBinderDeathRecipientList() {
172    AutoMutex _l(mLock);
173
174    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
175        deathRecipient->warnIfStillLive();
176    }
177}
178
179void HwBinderDeathRecipientList::add(const sp<HwBinderDeathRecipient>& recipient) {
180    AutoMutex _l(mLock);
181
182    mList.push_back(recipient);
183}
184
185void HwBinderDeathRecipientList::remove(const sp<HwBinderDeathRecipient>& recipient) {
186    AutoMutex _l(mLock);
187
188    List< sp<HwBinderDeathRecipient> >::iterator iter;
189    for (iter = mList.begin(); iter != mList.end(); iter++) {
190        if (*iter == recipient) {
191            mList.erase(iter);
192            return;
193        }
194    }
195}
196
197sp<HwBinderDeathRecipient> HwBinderDeathRecipientList::find(jobject recipient) {
198    AutoMutex _l(mLock);
199
200    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
201        if (deathRecipient->matches(recipient)) {
202            return deathRecipient;
203        }
204    }
205    return NULL;
206}
207
208Mutex& HwBinderDeathRecipientList::lock() {
209    return mLock;
210}
211
212// static
213void JHwRemoteBinder::InitClass(JNIEnv *env) {
214    jclass clazz = FindClassOrDie(env, CLASS_PATH);
215
216    gProxyOffsets.proxy_class = MakeGlobalRefOrDie(env, clazz);
217    gProxyOffsets.contextID =
218        GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
219    gProxyOffsets.constructID = GetMethodIDOrDie(env, clazz, "<init>", "()V");
220    gProxyOffsets.sendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
221            "(Landroid/os/IHwBinder$DeathRecipient;J)V");
222
223    clazz = FindClassOrDie(env, "java/lang/Class");
224    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
225}
226
227// static
228sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext(
229        JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context) {
230    sp<JHwRemoteBinder> old =
231        (JHwRemoteBinder *)env->GetLongField(thiz, gProxyOffsets.contextID);
232
233    if (context != NULL) {
234        context->incStrong(NULL /* id */);
235    }
236
237    if (old != NULL) {
238        old->decStrong(NULL /* id */);
239    }
240
241    env->SetLongField(thiz, gProxyOffsets.contextID, (long)context.get());
242
243    return old;
244}
245
246// static
247sp<JHwRemoteBinder> JHwRemoteBinder::GetNativeContext(
248        JNIEnv *env, jobject thiz) {
249    return (JHwRemoteBinder *)env->GetLongField(thiz, gProxyOffsets.contextID);
250}
251
252// static
253jobject JHwRemoteBinder::NewObject(
254        JNIEnv *env, const sp<hardware::IBinder> &binder) {
255    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
256
257    // XXX Have to look up the constructor here because otherwise that static
258    // class initializer isn't called and gProxyOffsets.constructID is undefined :(
259
260    jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
261
262    jobject obj = env->NewObject(clazz.get(), constructID);
263    JHwRemoteBinder::GetNativeContext(env, obj)->setBinder(binder);
264
265    return obj;
266}
267
268JHwRemoteBinder::JHwRemoteBinder(
269        JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder)
270    : mBinder(binder) {
271    mDeathRecipientList = new HwBinderDeathRecipientList();
272    jclass clazz = env->GetObjectClass(thiz);
273    CHECK(clazz != NULL);
274
275    mClass = (jclass)env->NewGlobalRef(clazz);
276    mObject = env->NewWeakGlobalRef(thiz);
277}
278
279JHwRemoteBinder::~JHwRemoteBinder() {
280    JNIEnv *env = AndroidRuntime::getJNIEnv();
281
282    env->DeleteWeakGlobalRef(mObject);
283    mObject = NULL;
284
285    env->DeleteGlobalRef(mClass);
286    mClass = NULL;
287}
288
289sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
290    return mBinder;
291}
292
293void JHwRemoteBinder::setBinder(const sp<hardware::IBinder> &binder) {
294    mBinder = binder;
295}
296
297sp<HwBinderDeathRecipientList> JHwRemoteBinder::getDeathRecipientList() const {
298    return mDeathRecipientList;
299}
300
301}  // namespace android
302
303////////////////////////////////////////////////////////////////////////////////
304
305using namespace android;
306
307static void releaseNativeContext(void *nativeContext) {
308    sp<JHwRemoteBinder> binder = (JHwRemoteBinder *)nativeContext;
309
310    if (binder != NULL) {
311        binder->decStrong(NULL /* id */);
312    }
313}
314
315static jlong JHwRemoteBinder_native_init(JNIEnv *env) {
316    JHwRemoteBinder::InitClass(env);
317
318    return reinterpret_cast<jlong>(&releaseNativeContext);
319}
320
321static void JHwRemoteBinder_native_setup_empty(JNIEnv *env, jobject thiz) {
322    sp<JHwRemoteBinder> context =
323        new JHwRemoteBinder(env, thiz, NULL /* service */);
324
325    JHwRemoteBinder::SetNativeContext(env, thiz, context);
326}
327
328static void JHwRemoteBinder_native_transact(
329        JNIEnv *env,
330        jobject thiz,
331        jint code,
332        jobject requestObj,
333        jobject replyObj,
334        jint flags) {
335    sp<hardware::IBinder> binder =
336        JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder();
337
338    if (requestObj == NULL) {
339        jniThrowException(env, "java/lang/NullPointerException", NULL);
340        return;
341    }
342
343    const hardware::Parcel *request =
344        JHwParcel::GetNativeContext(env, requestObj)->getParcel();
345
346    hardware::Parcel *reply =
347        JHwParcel::GetNativeContext(env, replyObj)->getParcel();
348
349    status_t err = binder->transact(code, *request, reply, flags);
350    signalExceptionForError(env, err, true /* canThrowRemoteException */);
351}
352
353static jboolean JHwRemoteBinder_linkToDeath(JNIEnv* env, jobject thiz,
354        jobject recipient, jlong cookie)
355{
356    if (recipient == NULL) {
357        jniThrowNullPointerException(env, NULL);
358        return JNI_FALSE;
359    }
360
361    sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, thiz);
362    sp<hardware::IBinder> binder = context->getBinder();
363
364    if (!binder->localBinder()) {
365        HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get();
366        sp<HwBinderDeathRecipient> jdr = new HwBinderDeathRecipient(env, recipient, cookie, list);
367        status_t err = binder->linkToDeath(jdr, NULL, 0);
368        if (err != NO_ERROR) {
369            // Failure adding the death recipient, so clear its reference
370            // now.
371            jdr->clearReference();
372            return JNI_FALSE;
373        }
374    }
375
376    return JNI_TRUE;
377}
378
379static jboolean JHwRemoteBinder_unlinkToDeath(JNIEnv* env, jobject thiz,
380                                                 jobject recipient)
381{
382    jboolean res = JNI_FALSE;
383    if (recipient == NULL) {
384        jniThrowNullPointerException(env, NULL);
385        return res;
386    }
387
388    sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, thiz);
389    sp<hardware::IBinder> binder = context->getBinder();
390
391    if (!binder->localBinder()) {
392        status_t err = NAME_NOT_FOUND;
393
394        // If we find the matching recipient, proceed to unlink using that
395        HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get();
396        sp<HwBinderDeathRecipient> origJDR = list->find(recipient);
397        if (origJDR != NULL) {
398            wp<hardware::IBinder::DeathRecipient> dr;
399            err = binder->unlinkToDeath(origJDR, NULL, 0, &dr);
400            if (err == NO_ERROR && dr != NULL) {
401                sp<hardware::IBinder::DeathRecipient> sdr = dr.promote();
402                HwBinderDeathRecipient* jdr = static_cast<HwBinderDeathRecipient*>(sdr.get());
403                if (jdr != NULL) {
404                    jdr->clearReference();
405                }
406            }
407        }
408
409        if (err == NO_ERROR || err == DEAD_OBJECT) {
410            res = JNI_TRUE;
411        } else {
412            jniThrowException(env, "java/util/NoSuchElementException",
413                              "Death link does not exist");
414        }
415    }
416
417    return res;
418}
419
420static JNINativeMethod gMethods[] = {
421    { "native_init", "()J", (void *)JHwRemoteBinder_native_init },
422
423    { "native_setup_empty", "()V",
424        (void *)JHwRemoteBinder_native_setup_empty },
425
426    { "transact",
427        "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
428        (void *)JHwRemoteBinder_native_transact },
429
430    {"linkToDeath",
431        "(Landroid/os/IHwBinder$DeathRecipient;J)Z",
432        (void*)JHwRemoteBinder_linkToDeath},
433
434    {"unlinkToDeath",
435        "(Landroid/os/IHwBinder$DeathRecipient;)Z",
436        (void*)JHwRemoteBinder_unlinkToDeath},
437};
438
439namespace android {
440
441int register_android_os_HwRemoteBinder(JNIEnv *env) {
442    return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
443}
444
445}  // namespace android
446
447