diff options
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java | 106 | ||||
| -rw-r--r-- | services/uwb/java/com/android/server/uwb/UwbServiceImpl.java | 134 |
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); } |