diff options
7 files changed, 351 insertions, 0 deletions
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index c7184c0743ce..1468fe5bfca8 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -400,6 +400,9 @@ public class Binder implements IBinder { * } * </pre> * + * <p>The work source will be propagated for future outgoing binder transactions + * executed on this thread. + * * @param workSource The original UID responsible for the binder call. * @return token to restore original work source. * @hide @@ -423,6 +426,9 @@ public class Binder implements IBinder { /** * Clears the work source on this thread. * + * <p>The work source will be propagated for future outgoing binder transactions + * executed on this thread. + * * @return token to restore original work source. * @hide **/ @@ -442,6 +448,9 @@ public class Binder implements IBinder { * Binder.restoreCallingWorkSource(token); * } * </pre> + * + * <p>The work source will be propagated for future outgoing binder transactions + * executed on this thread. * @hide **/ @CriticalNative diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index f60d8d0adcdd..3b650e5ff777 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1316,6 +1316,12 @@ <service android:name="android.os.BinderThreadPriorityService" android:process=":BinderThreadPriorityService" /> + <!-- Used by BinderWorkSourceTest --> + <service android:name="android.os.BinderWorkSourceService" + android:process=":BinderWorkSourceService" /> + <service android:name="android.os.BinderWorkSourceNestedService" + android:process=":BinderWorkSourceNestedService" /> + <!-- Application components used for search manager tests --> <activity android:name="android.app.activity.SearchableActivity" diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java new file mode 100644 index 000000000000..dddeda3874e3 --- /dev/null +++ b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 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.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** +* Service used by {@link BinderWorkSourceTest}. +*/ +public class BinderWorkSourceNestedService extends Service { + private final IBinderWorkSourceNestedService.Stub mBinder = + new IBinderWorkSourceNestedService.Stub() { + + public int[] nestedCallWithWorkSourceToSet(int uidToBlame) { + final int uid = Binder.getCallingWorkSourceUid(); + if (uidToBlame != ThreadLocalWorkSource.UID_NONE) { + Binder.setCallingWorkSourceUid(uidToBlame); + } + final int nestedUid = callGetIncomingWorkSourceUid(); + return new int[] {uid, nestedUid}; + } + + public int[] nestedCall() { + final int uid = Binder.getCallingWorkSourceUid(); + final int nestedUid = callGetIncomingWorkSourceUid(); + return new int[] {uid, nestedUid}; + } + + private int callGetIncomingWorkSourceUid() { + BlockingQueue<IBinderWorkSourceService> blockingQueue = + new LinkedBlockingQueue<>(); + ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + blockingQueue.add(IBinderWorkSourceService.Stub.asInterface(service)); + } + + public void onServiceDisconnected(ComponentName name) { + } + }; + + Context context = getApplicationContext(); + context.bindService( + new Intent(context, BinderWorkSourceService.class), + mConnection, Context.BIND_AUTO_CREATE); + + final IBinderWorkSourceService service; + try { + service = blockingQueue.poll(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (service == null) { + throw new RuntimeException("Gave up waiting for BinderWorkSourceService"); + } + + try { + return service.getIncomingWorkSourceUid(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } finally { + context.unbindService(mConnection); + } + } + }; + + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceService.java b/core/tests/coretests/src/android/os/BinderWorkSourceService.java new file mode 100644 index 000000000000..ac8d7ab98344 --- /dev/null +++ b/core/tests/coretests/src/android/os/BinderWorkSourceService.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 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; + +/** + * Service used by {@link BinderWorkSourceTest}. + */ +public class BinderWorkSourceService extends Service { + private final IBinderWorkSourceService.Stub mBinder = + new IBinderWorkSourceService.Stub() { + public int getIncomingWorkSourceUid() { + return Binder.getCallingWorkSourceUid(); + } + }; + + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java new file mode 100644 index 000000000000..ec178031cc45 --- /dev/null +++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2018 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.assertNotNull; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test whether Binder calls work source is propagated correctly. + */ +@LargeTest +@RunWith(AndroidJUnit4.class) +public class BinderWorkSourceTest { + private static Context sContext; + private static final int UID = 100; + private static final int SECOND_UID = 200; + private static final int UID_NONE = ThreadLocalWorkSource.UID_NONE; + + private IBinderWorkSourceService mService; + private IBinderWorkSourceNestedService mNestedService; + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + mService = IBinderWorkSourceService.Stub.asInterface(service); + } + + public void onServiceDisconnected(ComponentName name) { + mService = null; + } + }; + + private ServiceConnection mNestedConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + mNestedService = IBinderWorkSourceNestedService.Stub.asInterface(service); + } + + public void onServiceDisconnected(ComponentName name) { + mNestedService = null; + } + }; + + @BeforeClass + public static void setUpOnce() throws Exception { + sContext = InstrumentationRegistry.getContext(); + } + + @Before + public void setUp() throws Exception { + sContext.bindService( + new Intent(sContext, BinderWorkSourceService.class), + mConnection, Context.BIND_AUTO_CREATE); + sContext.bindService( + new Intent(sContext, BinderWorkSourceNestedService.class), + mNestedConnection, Context.BIND_AUTO_CREATE); + + final long timeoutMs = System.currentTimeMillis() + 30_000; + while ((mService == null || mNestedService == null) + && System.currentTimeMillis() < timeoutMs) { + Thread.sleep(1_000); + } + assertNotNull("Gave up waiting for BinderWorkSourceService", mService); + assertNotNull("Gave up waiting for BinderWorkSourceNestedService", mNestedService); + } + + @After + public void tearDown() throws Exception { + sContext.unbindService(mConnection); + sContext.unbindService(mNestedConnection); + } + + @Test + public void setWorkSource() throws Exception { + Binder.setCallingWorkSourceUid(UID); + assertEquals(UID, mService.getIncomingWorkSourceUid()); + assertEquals(UID, Binder.getCallingWorkSourceUid()); + } + + @Test + public void clearWorkSource() throws Exception { + Binder.setCallingWorkSourceUid(UID); + Binder.clearCallingWorkSource(); + assertEquals(UID_NONE, mService.getIncomingWorkSourceUid()); + assertEquals(UID_NONE, Binder.getCallingWorkSourceUid()); + } + + @Test + public void setWorkSource_propagatedForMultipleCalls() throws Exception { + Binder.setCallingWorkSourceUid(UID); + assertEquals(UID, mService.getIncomingWorkSourceUid()); + assertEquals(UID, mService.getIncomingWorkSourceUid()); + assertEquals(UID, mService.getIncomingWorkSourceUid()); + assertEquals(UID, Binder.getCallingWorkSourceUid()); + } + + @Test + public void restoreWorkSource() throws Exception { + Binder.setCallingWorkSourceUid(UID); + long token = Binder.clearCallingWorkSource(); + Binder.restoreCallingWorkSource(token); + + assertEquals(UID, mService.getIncomingWorkSourceUid()); + assertEquals(UID, Binder.getCallingWorkSourceUid()); + } + + @Test + public void nestedSetWorkSoucePropagated() throws Exception { + Binder.setCallingWorkSourceUid(UID); + + int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(SECOND_UID); + assertEquals(UID, workSources[0]); + // UID set in ested call. + assertEquals(SECOND_UID, workSources[1]); + // Initial work source restored. + assertEquals(UID, Binder.getCallingWorkSourceUid()); + } + + @Test + public void nestedSetWorkSouceDoesNotEnablePropagation() throws Exception { + int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(UID); + assertEquals(UID_NONE, workSources[0]); + // UID set in ested call. + assertEquals(UID, workSources[1]); + // Initial work source restored. + assertEquals(UID_NONE, Binder.getCallingWorkSourceUid()); + } + + @Test + public void nestedSetWorkSouceNotPropagated() throws Exception { + Binder.setCallingWorkSourceUid(UID); + + int[] workSources = mNestedService.nestedCall(); + assertEquals(UID, workSources[0]); + // No UID propagated. + assertEquals(UID_NONE, workSources[1]); + // Initial work source restored. + assertEquals(UID, Binder.getCallingWorkSourceUid()); + } +} diff --git a/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl new file mode 100644 index 000000000000..365aebb803f6 --- /dev/null +++ b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010 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; + +interface IBinderWorkSourceNestedService { + int[] nestedCallWithWorkSourceToSet(int uidToBlame); + int[] nestedCall(); +} diff --git a/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl new file mode 100644 index 000000000000..05d4e829be0a --- /dev/null +++ b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2010 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; + +interface IBinderWorkSourceService { + int getIncomingWorkSourceUid(); +} |