Supplying the IBinder to binderDied

Plugging the binder instance that dies. This helps a single
DeathRecipient to listen for multiple binder deaths.

Test: atest FrameworksCoreTests:BinderDeathRecipientTest

Bug: 135633339
Change-Id: I976ee2a138877830885ee1cbbd2b313ecb87aeb3
This commit is contained in:
Suprabh Shukla
2019-12-23 15:12:20 -08:00
parent 9001d13c50
commit b8f20004f1
10 changed files with 283 additions and 8 deletions

View File

@@ -631,10 +631,12 @@ public final class BinderProxy implements IBinder {
}
}
private static void sendDeathNotice(DeathRecipient recipient) {
if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
private static void sendDeathNotice(DeathRecipient recipient, IBinder binderProxy) {
if (false) {
Log.v("JavaBinder", "sendDeathNotice to " + recipient + " for " + binderProxy);
}
try {
recipient.binderDied();
recipient.binderDied(binderProxy);
} catch (RuntimeException exc) {
Log.w("BinderNative", "Uncaught exception from death notification",
exc);

View File

@@ -285,6 +285,13 @@ public interface IBinder {
*/
public interface DeathRecipient {
public void binderDied();
/**
* @hide
*/
default void binderDied(IBinder who) {
binderDied();
}
}
/**

View File

@@ -537,9 +537,9 @@ public:
LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
if (mObject != NULL) {
JNIEnv* env = javavm_to_jnienv(mVM);
jobject jBinderProxy = javaObjectForIBinder(env, who.promote());
env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
gBinderProxyOffsets.mSendDeathNotice, mObject);
gBinderProxyOffsets.mSendDeathNotice, mObject, jBinderProxy);
if (env->ExceptionCheck()) {
jthrowable excep = env->ExceptionOccurred();
report_exception(env, excep,
@@ -1532,8 +1532,9 @@ static int int_register_android_os_BinderProxy(JNIEnv* env)
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
"(JJ)Landroid/os/BinderProxy;");
gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;)V");
gBinderProxyOffsets.mSendDeathNotice =
GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
clazz = FindClassOrDie(env, "java/lang/Class");

View File

@@ -8,6 +8,7 @@ android_test {
"EnabledTestApp/src/**/*.java",
"BinderProxyCountingTestApp/src/**/*.java",
"BinderProxyCountingTestService/src/**/*.java",
"BinderDeathRecipientHelperApp/src/**/*.java",
"aidl/**/I*.aidl",
],
@@ -59,7 +60,11 @@ android_test {
resource_dirs: ["res"],
resource_zips: [":FrameworksCoreTests_apks_as_resources"],
data: [":BstatsTestApp"],
data: [
":BstatsTestApp",
":BinderDeathRecipientHelperApp1",
":BinderDeathRecipientHelperApp2",
],
}
// Rules to copy all the test apks to the intermediate raw resource directory

View File

@@ -95,6 +95,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.KILL_UID" />
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />

View File

@@ -21,6 +21,8 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksCoreTests.apk" />
<option name="test-file-name" value="BstatsTestApp.apk" />
<option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" />
<option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" />
</target_preparer>
<option name="test-tag" value="FrameworksCoreTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >

View File

@@ -0,0 +1,19 @@
android_test_helper_app {
name: "BinderDeathRecipientHelperApp1",
srcs: ["**/*.java"],
sdk_version: "current",
}
android_test_helper_app {
name: "BinderDeathRecipientHelperApp2",
srcs: ["**/*.java"],
sdk_version: "current",
aaptflags: [
"--rename-manifest-package com.android.frameworks.coretests.bdr_helper_app2",
],
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.coretests.bdr_helper_app1">
<application>
<receiver android:name="com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver"
android:exported="true"/>
</application>
</manifest>

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2020 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 com.android.frameworks.coretests.bdr_helper_app;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.util.Log;
/**
* Receiver used to hand off a binder owned by this process to
* {@link android.os.BinderDeathRecipientTest}.
*/
public class TestCommsReceiver extends BroadcastReceiver {
private static final String TAG = TestCommsReceiver.class.getSimpleName();
private static final String PACKAGE_NAME = "com.android.frameworks.coretests.bdr_helper_app";
public static final String ACTION_GET_BINDER = PACKAGE_NAME + ".action.GET_BINDER";
public static final String EXTRA_KEY_BINDER = PACKAGE_NAME + ".EXTRA_BINDER";
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case ACTION_GET_BINDER:
final Bundle resultExtras = new Bundle();
resultExtras.putBinder(EXTRA_KEY_BINDER, new Binder());
setResult(Activity.RESULT_OK, null, resultExtras);
break;
default:
Log.e(TAG, "Unknown action " + intent.getAction());
break;
}
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2020 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Set;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* Tests functionality of {@link android.os.IBinder.DeathRecipient} callbacks.
*/
@RunWith(AndroidJUnit4.class)
public class BinderDeathRecipientTest {
private static final String TAG = BinderDeathRecipientTest.class.getSimpleName();
private static final String TEST_PACKAGE_NAME_1 =
"com.android.frameworks.coretests.bdr_helper_app1";
private static final String TEST_PACKAGE_NAME_2 =
"com.android.frameworks.coretests.bdr_helper_app2";
private Context mContext;
private Handler mHandler;
private ActivityManager mActivityManager;
private Set<Pair<IBinder, IBinder.DeathRecipient>> mLinkedDeathRecipients = new ArraySet<>();
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
mActivityManager = mContext.getSystemService(ActivityManager.class);
mHandler = new Handler(Looper.getMainLooper());
}
private IBinder getNewRemoteBinder(String testPackage) throws InterruptedException {
final CountDownLatch resultLatch = new CountDownLatch(1);
final AtomicInteger resultCode = new AtomicInteger(Activity.RESULT_CANCELED);
final AtomicReference<Bundle> resultExtras = new AtomicReference<>();
final Intent intent = new Intent(TestCommsReceiver.ACTION_GET_BINDER)
.setClassName(testPackage, TestCommsReceiver.class.getName());
mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
resultCode.set(getResultCode());
resultExtras.set(getResultExtras(true));
resultLatch.countDown();
}
}, mHandler, Activity.RESULT_CANCELED, null, null);
assertTrue("Request for binder timed out", resultLatch.await(5, TimeUnit.SECONDS));
assertEquals(Activity.RESULT_OK, resultCode.get());
return resultExtras.get().getBinder(TestCommsReceiver.EXTRA_KEY_BINDER);
}
@Test
public void binderDied_noArgs() throws Exception {
final IBinder testAppBinder = getNewRemoteBinder(TEST_PACKAGE_NAME_1);
final CountDownLatch deathNotificationLatch = new CountDownLatch(1);
final IBinder.DeathRecipient simpleDeathRecipient =
() -> deathNotificationLatch.countDown();
testAppBinder.linkToDeath(simpleDeathRecipient, 0);
mLinkedDeathRecipients.add(Pair.create(testAppBinder, simpleDeathRecipient));
mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_1);
assertTrue("Death notification did not arrive",
deathNotificationLatch.await(10, TimeUnit.SECONDS));
}
@Test
public void binderDied_iBinderArg() throws Exception {
final IBinder testApp1Binder = getNewRemoteBinder(TEST_PACKAGE_NAME_1);
final IBinder testApp2Binder = getNewRemoteBinder(TEST_PACKAGE_NAME_2);
final CyclicBarrier barrier = new CyclicBarrier(2);
final AtomicReference<IBinder> binderThatDied = new AtomicReference<>();
final IBinder.DeathRecipient sameDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.e(TAG, "Should not have been called!");
}
@Override
public void binderDied(IBinder who) {
binderThatDied.set(who);
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
Log.e(TAG, "Unexpected exception while waiting on CyclicBarrier", e);
}
}
};
testApp1Binder.linkToDeath(sameDeathRecipient, 0);
mLinkedDeathRecipients.add(Pair.create(testApp1Binder, sameDeathRecipient));
testApp2Binder.linkToDeath(sameDeathRecipient, 0);
mLinkedDeathRecipients.add(Pair.create(testApp2Binder, sameDeathRecipient));
mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_1);
try {
barrier.await(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
fail("Timed out while waiting for 1st death notification: " + e.getMessage());
}
assertEquals("Different binder received", testApp1Binder, binderThatDied.get());
barrier.reset();
mActivityManager.forceStopPackage(TEST_PACKAGE_NAME_2);
try {
barrier.await(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
fail("Timed out while waiting for 2nd death notification: " + e.getMessage());
}
assertEquals("Different binder received", testApp2Binder, binderThatDied.get());
}
@After
public void tearDown() {
for (Pair<IBinder, IBinder.DeathRecipient> linkedPair : mLinkedDeathRecipients) {
linkedPair.first.unlinkToDeath(linkedPair.second, 0);
}
}
}