diff options
| -rw-r--r-- | core/java/android/os/Binder.java | 12 | ||||
| -rw-r--r-- | core/java/android/os/BinderProxy.java | 4 | ||||
| -rw-r--r-- | core/java/android/os/IBinder.java | 12 | ||||
| -rw-r--r-- | core/jni/android_util_Binder.cpp | 54 | ||||
| -rw-r--r-- | core/tests/coretests/AndroidManifest.xml | 4 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/BinderProxyService.java | 35 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/BinderProxyTest.java | 43 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/BinderTest.java | 14 |
8 files changed, 177 insertions, 1 deletions
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index a90ab8564100..fe7c7c921b67 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -996,6 +996,18 @@ public class Binder implements IBinder { return 0; } + /** @hide */ + @Override + public final native @Nullable IBinder getExtension(); + + /** + * Set the binder extension. + * This should be called immediately when the object is created. + * + * @hide + */ + public final native void setExtension(@Nullable IBinder extension); + /** * Default implementation rewinds the parcels and calls onTransact. On * the remote side, transact calls into the binder to do the IPC. diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index ac70b523bcc1..be307ab4737d 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -455,6 +455,10 @@ public final class BinderProxy implements IBinder { return null; } + /** @hide */ + @Override + public native @Nullable IBinder getExtension() throws RemoteException; + /** * Perform a binder transaction on a proxy. * diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index f5fe9c3334bd..486ec2ab2b88 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -257,6 +257,18 @@ public interface IBinder { @NonNull ResultReceiver resultReceiver) throws RemoteException; /** + * Get the binder extension of this binder interface. + * This allows one to customize an interface without having to modify the original interface. + * + * @return null if don't have binder extension + * @throws RemoteException + * @hide + */ + public default @Nullable IBinder getExtension() throws RemoteException { + throw new IllegalStateException("Method is not implemented"); + } + + /** * Perform a generic operation with the object. * * @param code The action to perform. This should diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index e77c25efb1d4..c269d1cf4295 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -463,6 +463,9 @@ public: if (mVintf) { ::android::internal::Stability::markVintf(b.get()); } + if (mExtension != nullptr) { + b.get()->setExtension(mExtension); + } mBinder = b; ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n", b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount()); @@ -481,6 +484,24 @@ public: mVintf = true; } + sp<IBinder> getExtension() { + AutoMutex _l(mLock); + sp<JavaBBinder> b = mBinder.promote(); + if (b != nullptr) { + return b.get()->getExtension(); + } + return mExtension; + } + + void setExtension(const sp<IBinder>& extension) { + AutoMutex _l(mLock); + mExtension = extension; + sp<JavaBBinder> b = mBinder.promote(); + if (b != nullptr) { + b.get()->setExtension(mExtension); + } + } + private: Mutex mLock; wp<JavaBBinder> mBinder; @@ -489,6 +510,8 @@ private: // is too much binder state here, we can think about making JavaBBinder an // sp here (avoid recreating it) bool mVintf = false; + + sp<IBinder> mExtension; }; // ---------------------------------------------------------------------------- @@ -1037,6 +1060,17 @@ static jobject android_os_Binder_waitForService( return javaObjectForIBinder(env, service); } +static jobject android_os_Binder_getExtension(JNIEnv* env, jobject obj) { + JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject); + return javaObjectForIBinder(env, jbh->getExtension()); +} + +static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject extensionObject) { + JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject); + sp<IBinder> extension = ibinderForJavaObject(env, extensionObject); + jbh->setExtension(extension); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gBinderMethods[] = { @@ -1066,7 +1100,9 @@ static const JNINativeMethod gBinderMethods[] = { { "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder }, { "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer }, { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }, - { "waitForService", "(Ljava/lang/String;)Landroid/os/IBinder;", (void*)android_os_Binder_waitForService } + { "waitForService", "(Ljava/lang/String;)Landroid/os/IBinder;", (void*)android_os_Binder_waitForService }, + { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension }, + { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension }, }; const char* const kBinderPathName = "android/os/Binder"; @@ -1506,6 +1542,21 @@ JNIEXPORT jlong JNICALL android_os_BinderProxy_getNativeFinalizer(JNIEnv*, jclas return (jlong) BinderProxy_destroy; } +static jobject android_os_BinderProxy_getExtension(JNIEnv* env, jobject obj) { + IBinder* binder = getBPNativeData(env, obj)->mObject.get(); + if (binder == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "Native IBinder is null"); + return nullptr; + } + sp<IBinder> extension; + status_t err = binder->getExtension(&extension); + if (err != OK) { + signalExceptionForError(env, obj, err, true /* canThrowRemoteException */); + return nullptr; + } + return javaObjectForIBinder(env, extension); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gBinderProxyMethods[] = { @@ -1517,6 +1568,7 @@ static const JNINativeMethod gBinderProxyMethods[] = { {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath}, {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath}, {"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer}, + {"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension}, }; const char* const kBinderProxyPathName = "android/os/BinderProxy"; diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 23f9f90248df..ee93b397bedf 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1341,6 +1341,10 @@ <service android:name="android.os.BinderWorkSourceNestedService" android:process=":BinderWorkSourceNestedService" /> + <!-- Used by BinderProxyTest --> + <service android:name="android.os.BinderProxyService" + android:process=":BinderProxyService" /> + <!-- Application components used for search manager tests --> <activity android:name="android.app.activity.SearchableActivity" diff --git a/core/tests/coretests/src/android/os/BinderProxyService.java b/core/tests/coretests/src/android/os/BinderProxyService.java new file mode 100644 index 000000000000..bf1fbc555d8b --- /dev/null +++ b/core/tests/coretests/src/android/os/BinderProxyService.java @@ -0,0 +1,35 @@ +/* + * 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 android.app.Service; +import android.content.Intent; + +public class BinderProxyService extends Service { + private final Binder mBinder = new Binder(); + + @Override + public void onCreate() { + super.onCreate(); + mBinder.setExtension(new Binder()); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java index aceda2d0524b..3567d17ea874 100644 --- a/core/tests/coretests/src/android/os/BinderProxyTest.java +++ b/core/tests/coretests/src/android/os/BinderProxyTest.java @@ -17,11 +17,17 @@ package android.os; import android.annotation.Nullable; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.test.AndroidTestCase; import androidx.test.filters.MediumTest; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + public class BinderProxyTest extends AndroidTestCase { private static class CountingListener implements Binder.ProxyTransactListener { int mStartedCount; @@ -86,4 +92,41 @@ public class BinderProxyTest extends AndroidTestCase { // Check it does not throw.. mPowerManager.isInteractive(); } + + private IBinder mRemoteBinder = null; + + @MediumTest + public void testGetExtension() throws Exception { + final CountDownLatch bindLatch = new CountDownLatch(1); + ServiceConnection connection = + new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mRemoteBinder = service; + bindLatch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) {} + }; + try { + mContext.bindService( + new Intent(mContext, BinderProxyService.class), + connection, + Context.BIND_AUTO_CREATE); + if (!bindLatch.await(500, TimeUnit.MILLISECONDS)) { + fail( + "Timed out while binding service: " + + BinderProxyService.class.getSimpleName()); + } + assertTrue(mRemoteBinder instanceof BinderProxy); + assertNotNull(mRemoteBinder); + + IBinder extension = mRemoteBinder.getExtension(); + assertNotNull(extension); + assertTrue(extension.pingBinder()); + } finally { + mContext.unbindService(connection); + } + } } diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java index a354195c75a3..99dbe6445662 100644 --- a/core/tests/coretests/src/android/os/BinderTest.java +++ b/core/tests/coretests/src/android/os/BinderTest.java @@ -52,4 +52,18 @@ public class BinderTest extends TestCase { } catch (IllegalStateException expected) { } } + + @SmallTest + public void testGetExtension() throws Exception { + Binder binder = new Binder(); + assertNull(binder.getExtension()); + + IBinder extension = new Binder(); + binder.setExtension(extension); + assertNotNull(binder.getExtension()); + assertSame(binder.getExtension(), extension); + + binder.setExtension(null); + assertNull(binder.getExtension()); + } } |