summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java106
-rw-r--r--services/uwb/java/com/android/server/uwb/UwbServiceImpl.java134
2 files changed, 238 insertions, 2 deletions
diff --git a/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java b/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java
index a8121a634181..867f68415b6b 100644
--- a/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java
@@ -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());
+ }
}
diff --git a/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java b/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java
index 332402c927ca..ce39ff65c5e2 100644
--- a/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java
+++ b/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java
@@ -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);
}