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:
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user