UwbService: Create a UwbRanging callback wrapper

This wrapper will be used for performing permission checks before
sending the results to the corresponding app.

Bug: 183904955
Test: atest android.uwb.cts.UwbManagerTest
Change-Id: I562951d9f28f1a55391f723445c30fa5ff8aea47
This commit is contained in:
Roshan Pius
2021-03-31 18:08:33 -07:00
parent 27e1a87a48
commit e4fd3ed60b
2 changed files with 238 additions and 2 deletions

View File

@@ -19,17 +19,25 @@ package com.android.server.uwb;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
import android.test.suitebuilder.annotation.SmallTest;
import android.uwb.IUwbAdapter;
import android.uwb.IUwbAdapterStateCallbacks;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.RangingReport;
import android.uwb.RangingSession;
import android.uwb.SessionHandle;
import androidx.test.runner.AndroidJUnit4;
@@ -37,6 +45,8 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -50,6 +60,9 @@ public class UwbServiceImplTest {
@Mock private IUwbAdapter mVendorService;
@Mock private Context mContext;
@Mock private UwbInjector mUwbInjector;
@Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor;
@Captor private ArgumentCaptor<IBinder.DeathRecipient> mClientDeathCaptor;
@Captor private ArgumentCaptor<IBinder.DeathRecipient> mVendorServiceDeathCaptor;
private UwbServiceImpl mUwbServiceImpl;
@@ -153,4 +166,97 @@ public class UwbServiceImplTest {
verify(mVendorService).closeRanging(sessionHandle);
}
@Test
public void testRangingCallbacks() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
final PersistableBundle parameters = new PersistableBundle();
final IBinder cbBinder = mock(IBinder.class);
when(cb.asBinder()).thenReturn(cbBinder);
mUwbServiceImpl.openRanging(sessionHandle, cb, parameters);
verify(mVendorService).openRanging(
eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters));
assertThat(mRangingCbCaptor.getValue()).isNotNull();
// Invoke vendor service callbacks and ensure that the corresponding app callback is
// invoked.
mRangingCbCaptor.getValue().onRangingOpened(sessionHandle);
verify(cb).onRangingOpened(sessionHandle);
mRangingCbCaptor.getValue().onRangingOpenFailed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
verify(cb).onRangingOpenFailed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
mRangingCbCaptor.getValue().onRangingStarted(sessionHandle, parameters);
verify(cb).onRangingStarted(sessionHandle, parameters);
mRangingCbCaptor.getValue().onRangingStartFailed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
verify(cb).onRangingStartFailed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
mRangingCbCaptor.getValue().onRangingReconfigured(sessionHandle, parameters);
verify(cb).onRangingReconfigured(sessionHandle, parameters);
mRangingCbCaptor.getValue().onRangingReconfigureFailed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
verify(cb).onRangingReconfigureFailed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
mRangingCbCaptor.getValue().onRangingStopped(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
verify(cb).onRangingStopped(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
mRangingCbCaptor.getValue().onRangingStopFailed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
verify(cb).onRangingStopFailed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
final RangingReport rangingReport = new RangingReport.Builder().build();
mRangingCbCaptor.getValue().onRangingResult(sessionHandle, rangingReport);
verify(cb).onRangingResult(sessionHandle, rangingReport);
mRangingCbCaptor.getValue().onRangingClosed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
verify(cb).onRangingClosed(
sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
}
@Test
public void testHandleClientDeath() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
final PersistableBundle parameters = new PersistableBundle();
final IBinder cbBinder = mock(IBinder.class);
when(cb.asBinder()).thenReturn(cbBinder);
mUwbServiceImpl.openRanging(sessionHandle, cb, parameters);
verify(mVendorService).openRanging(
eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters));
assertThat(mRangingCbCaptor.getValue()).isNotNull();
verify(cbBinder).linkToDeath(mClientDeathCaptor.capture(), anyInt());
assertThat(mClientDeathCaptor.getValue()).isNotNull();
clearInvocations(cb);
// Invoke cb, ensure it reaches the client.
mRangingCbCaptor.getValue().onRangingOpened(sessionHandle);
verify(cb).onRangingOpened(sessionHandle);
// Trigger client death and ensure the session is stopped.
mClientDeathCaptor.getValue().binderDied();
verify(mVendorService).stopRanging(sessionHandle);
verify(mVendorService).closeRanging(sessionHandle);
// Invoke cb, it should be ignored.
mRangingCbCaptor.getValue().onRangingStarted(sessionHandle, parameters);
verify(cb, never()).onRangingStarted(any(), any());
}
}

View File

@@ -18,14 +18,21 @@ package com.android.server.uwb;
import android.annotation.NonNull;
import android.content.Context;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.uwb.IUwbAdapter;
import android.uwb.IUwbAdapterStateCallbacks;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.RangingReport;
import android.uwb.SessionHandle;
import com.android.internal.annotations.GuardedBy;
import java.util.Map;
/**
* Implementation of {@link android.uwb.IUwbAdapter} binder service.
*/
@@ -34,12 +41,129 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
private final Context mContext;
private final UwbInjector mUwbInjector;
/**
* Map for storing the callbacks wrapper for each session.
*/
@GuardedBy("mCallbacksMap")
private final Map<SessionHandle, UwbRangingCallbacksWrapper> mCallbacksMap = new ArrayMap<>();
/**
* Used for caching the vendor implementation of {@link IUwbAdapter} interface.
*/
private IUwbAdapter mVendorUwbAdapter;
/**
* Wrapper for callback registered with vendor service. This wrapper is needed for performing
* permission check before sending the callback to the external app.
*/
private class UwbRangingCallbacksWrapper extends IUwbRangingCallbacks.Stub
implements IBinder.DeathRecipient{
private final SessionHandle mSessionHandle;
private final IUwbRangingCallbacks mExternalCb;
UwbRangingCallbacksWrapper(@NonNull SessionHandle sessionHandle,
@NonNull IUwbRangingCallbacks externalCb) {
mSessionHandle = sessionHandle;
mExternalCb = externalCb;
// Link to death for external callback.
linkToDeath();
}
private void linkToDeath() {
IBinder binder = mExternalCb.asBinder();
try {
binder.linkToDeath(this, 0);
} catch (RemoteException e) {
Log.e(TAG, "Unable to link to client death event.");
}
}
private void removeClientAndUnlinkToDeath() {
// Remove from the map.
synchronized (mCallbacksMap) {
mCallbacksMap.remove(mSessionHandle);
}
IBinder binder = mExternalCb.asBinder();
binder.unlinkToDeath(this, 0);
}
@Override
public void onRangingOpened(SessionHandle sessionHandle) throws RemoteException {
mExternalCb.onRangingOpened(sessionHandle);
}
@Override
public void onRangingOpenFailed(SessionHandle sessionHandle,
int reason, PersistableBundle parameters) throws RemoteException {
mExternalCb.onRangingOpenFailed(sessionHandle, reason, parameters);
}
@Override
public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters)
throws RemoteException {
mExternalCb.onRangingStarted(sessionHandle, parameters);
}
@Override
public void onRangingStartFailed(SessionHandle sessionHandle,
int reason, PersistableBundle parameters) throws RemoteException {
mExternalCb.onRangingStartFailed(sessionHandle, reason, parameters);
}
@Override
public void onRangingReconfigured(SessionHandle sessionHandle, PersistableBundle parameters)
throws RemoteException {
mExternalCb.onRangingReconfigured(sessionHandle, parameters);
}
@Override
public void onRangingReconfigureFailed(SessionHandle sessionHandle,
int reason, PersistableBundle parameters) throws RemoteException {
mExternalCb.onRangingReconfigureFailed(sessionHandle, reason, parameters);
}
@Override
public void onRangingStopped(SessionHandle sessionHandle, int reason,
PersistableBundle parameters)
throws RemoteException {
mExternalCb.onRangingStopped(sessionHandle, reason, parameters);
}
@Override
public void onRangingStopFailed(SessionHandle sessionHandle, int reason,
PersistableBundle parameters) throws RemoteException {
mExternalCb.onRangingStopFailed(sessionHandle, reason, parameters);
}
@Override
public void onRangingClosed(SessionHandle sessionHandle, int reason,
PersistableBundle parameters) throws RemoteException {
mExternalCb.onRangingClosed(sessionHandle, reason, parameters);
removeClientAndUnlinkToDeath();
}
@Override
public void onRangingResult(SessionHandle sessionHandle, RangingReport rangingReport)
throws RemoteException {
// TODO: Perform permission checks and noteOp.
mExternalCb.onRangingResult(sessionHandle, rangingReport);
}
@Override
public void binderDied() {
Log.i(TAG, "Client died: ending session: " + mSessionHandle);
try {
stopRanging(mSessionHandle);
closeRanging(mSessionHandle);
} catch (RemoteException execption) {
Log.w(TAG, "Remote exception while handling client death");
removeClientAndUnlinkToDeath();
}
}
}
private IUwbAdapter getVendorUwbAdapter() throws IllegalStateException {
if (mVendorUwbAdapter != null) return mVendorUwbAdapter;
mVendorUwbAdapter = mUwbInjector.getVendorService();
@@ -50,7 +174,7 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
return mVendorUwbAdapter;
}
public UwbServiceImpl(@NonNull Context context, @NonNull UwbInjector uwbInjector) {
UwbServiceImpl(@NonNull Context context, @NonNull UwbInjector uwbInjector) {
mContext = context;
mUwbInjector = uwbInjector;
}
@@ -80,12 +204,18 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
@Override
public void openRanging(SessionHandle sessionHandle, IUwbRangingCallbacks rangingCallbacks,
PersistableBundle parameters) throws RemoteException {
getVendorUwbAdapter().openRanging(sessionHandle, rangingCallbacks, parameters);
UwbRangingCallbacksWrapper wrapperCb =
new UwbRangingCallbacksWrapper(sessionHandle, rangingCallbacks);
synchronized (mCallbacksMap) {
mCallbacksMap.put(sessionHandle, wrapperCb);
}
getVendorUwbAdapter().openRanging(sessionHandle, wrapperCb, parameters);
}
@Override
public void startRanging(SessionHandle sessionHandle, PersistableBundle parameters)
throws RemoteException {
// TODO: Perform permission checks.
getVendorUwbAdapter().startRanging(sessionHandle, parameters);
}