UwbService: Handle vendor service death
Needs to trigger session close callbacks for all the clients and clear the client map. Bug: 183904955 Test: atest android.uwb.cts.UwbManagerTest Change-Id: Ib6da490ba535e692daba418c3d10abf6c0ad3ab8
This commit is contained in:
@@ -21,6 +21,7 @@ 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.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -58,6 +59,7 @@ import org.mockito.MockitoAnnotations;
|
||||
@Presubmit
|
||||
public class UwbServiceImplTest {
|
||||
@Mock private IUwbAdapter mVendorService;
|
||||
@Mock private IBinder mVendorServiceBinder;
|
||||
@Mock private Context mContext;
|
||||
@Mock private UwbInjector mUwbInjector;
|
||||
@Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor;
|
||||
@@ -70,6 +72,7 @@ public class UwbServiceImplTest {
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mUwbInjector.getVendorService()).thenReturn(mVendorService);
|
||||
when(mVendorService.asBinder()).thenReturn(mVendorServiceBinder);
|
||||
mUwbServiceImpl = new UwbServiceImpl(mContext, mUwbInjector);
|
||||
}
|
||||
|
||||
@@ -123,10 +126,14 @@ public class UwbServiceImplTest {
|
||||
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(sessionHandle, cb, parameters);
|
||||
verify(mVendorService).openRanging(
|
||||
eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters));
|
||||
assertThat(mRangingCbCaptor.getValue()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -259,4 +266,34 @@ public class UwbServiceImplTest {
|
||||
mRangingCbCaptor.getValue().onRangingStarted(sessionHandle, parameters);
|
||||
verify(cb, never()).onRangingStarted(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleVendorServiceDeath() 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(mVendorServiceBinder).linkToDeath(mVendorServiceDeathCaptor.capture(), anyInt());
|
||||
assertThat(mVendorServiceDeathCaptor.getValue()).isNotNull();
|
||||
|
||||
verify(mVendorService).openRanging(
|
||||
eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters));
|
||||
assertThat(mRangingCbCaptor.getValue()).isNotNull();
|
||||
|
||||
clearInvocations(cb);
|
||||
|
||||
// Invoke cb, ensure it reaches the client.
|
||||
mRangingCbCaptor.getValue().onRangingOpened(sessionHandle);
|
||||
verify(cb).onRangingOpened(sessionHandle);
|
||||
|
||||
// Trigger vendor service death and ensure that the client is informed of session end.
|
||||
mVendorServiceDeathCaptor.getValue().binderDied();
|
||||
verify(cb).onRangingClosed(
|
||||
eq(sessionHandle), eq(RangingSession.Callback.REASON_UNKNOWN),
|
||||
argThat((p) -> p.isEmpty()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ 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 com.android.internal.annotations.GuardedBy;
|
||||
@@ -36,7 +37,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Implementation of {@link android.uwb.IUwbAdapter} binder service.
|
||||
*/
|
||||
public class UwbServiceImpl extends IUwbAdapter.Stub {
|
||||
public class UwbServiceImpl extends IUwbAdapter.Stub implements IBinder.DeathRecipient{
|
||||
private static final String TAG = "UwbServiceImpl";
|
||||
|
||||
private final Context mContext;
|
||||
@@ -55,16 +56,20 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
|
||||
/**
|
||||
* Wrapper for callback registered with vendor service. This wrapper is needed for performing
|
||||
* permission check before sending the callback to the external app.
|
||||
*
|
||||
* Access to these callbacks are synchronized.
|
||||
*/
|
||||
private class UwbRangingCallbacksWrapper extends IUwbRangingCallbacks.Stub
|
||||
implements IBinder.DeathRecipient{
|
||||
private final SessionHandle mSessionHandle;
|
||||
private final IUwbRangingCallbacks mExternalCb;
|
||||
private boolean mIsValid;
|
||||
|
||||
UwbRangingCallbacksWrapper(@NonNull SessionHandle sessionHandle,
|
||||
@NonNull IUwbRangingCallbacks externalCb) {
|
||||
mSessionHandle = sessionHandle;
|
||||
mExternalCb = externalCb;
|
||||
mIsValid = true;
|
||||
|
||||
// Link to death for external callback.
|
||||
linkToDeath();
|
||||
@@ -75,7 +80,7 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
|
||||
try {
|
||||
binder.linkToDeath(this, 0);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to link to client death event.");
|
||||
Log.e(TAG, "Unable to link to client death event.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,91 +91,136 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
|
||||
}
|
||||
IBinder binder = mExternalCb.asBinder();
|
||||
binder.unlinkToDeath(this, 0);
|
||||
mIsValid = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRangingOpened(SessionHandle sessionHandle) throws RemoteException {
|
||||
public synchronized void onRangingOpened(SessionHandle sessionHandle)
|
||||
throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingOpened(sessionHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingOpenFailed(SessionHandle sessionHandle,
|
||||
public synchronized void onRangingOpenFailed(SessionHandle sessionHandle,
|
||||
int reason, PersistableBundle parameters) throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingOpenFailed(sessionHandle, reason, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters)
|
||||
public synchronized void onRangingStarted(SessionHandle sessionHandle,
|
||||
PersistableBundle parameters)
|
||||
throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingStarted(sessionHandle, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingStartFailed(SessionHandle sessionHandle,
|
||||
public synchronized void onRangingStartFailed(SessionHandle sessionHandle,
|
||||
int reason, PersistableBundle parameters) throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingStartFailed(sessionHandle, reason, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingReconfigured(SessionHandle sessionHandle, PersistableBundle parameters)
|
||||
public synchronized void onRangingReconfigured(SessionHandle sessionHandle,
|
||||
PersistableBundle parameters)
|
||||
throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingReconfigured(sessionHandle, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingReconfigureFailed(SessionHandle sessionHandle,
|
||||
public synchronized void onRangingReconfigureFailed(SessionHandle sessionHandle,
|
||||
int reason, PersistableBundle parameters) throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingReconfigureFailed(sessionHandle, reason, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingStopped(SessionHandle sessionHandle, int reason,
|
||||
public synchronized void onRangingStopped(SessionHandle sessionHandle, int reason,
|
||||
PersistableBundle parameters)
|
||||
throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingStopped(sessionHandle, reason, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingStopFailed(SessionHandle sessionHandle, int reason,
|
||||
public synchronized void onRangingStopFailed(SessionHandle sessionHandle, int reason,
|
||||
PersistableBundle parameters) throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingStopFailed(sessionHandle, reason, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingClosed(SessionHandle sessionHandle, int reason,
|
||||
public synchronized void onRangingClosed(SessionHandle sessionHandle, int reason,
|
||||
PersistableBundle parameters) throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
mExternalCb.onRangingClosed(sessionHandle, reason, parameters);
|
||||
removeClientAndUnlinkToDeath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRangingResult(SessionHandle sessionHandle, RangingReport rangingReport)
|
||||
public synchronized void onRangingResult(SessionHandle sessionHandle,
|
||||
RangingReport rangingReport)
|
||||
throws RemoteException {
|
||||
if (!mIsValid) return;
|
||||
// TODO: Perform permission checks and noteOp.
|
||||
mExternalCb.onRangingResult(sessionHandle, rangingReport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
public synchronized void binderDied() {
|
||||
if (!mIsValid) return;
|
||||
Log.i(TAG, "Client died: ending session: " + mSessionHandle);
|
||||
try {
|
||||
removeClientAndUnlinkToDeath();
|
||||
stopRanging(mSessionHandle);
|
||||
closeRanging(mSessionHandle);
|
||||
} catch (RemoteException execption) {
|
||||
Log.w(TAG, "Remote exception while handling client death");
|
||||
removeClientAndUnlinkToDeath();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Remote exception while handling client death", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IUwbAdapter getVendorUwbAdapter() throws IllegalStateException {
|
||||
private void linkToVendorServiceDeath() {
|
||||
IBinder binder = mVendorUwbAdapter.asBinder();
|
||||
try {
|
||||
binder.linkToDeath(this, 0);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to link to vendor service death event.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.i(TAG, "Vendor service died: sending session close callbacks");
|
||||
synchronized (mCallbacksMap) {
|
||||
for (Map.Entry<SessionHandle, UwbRangingCallbacksWrapper> e : mCallbacksMap.entrySet()) {
|
||||
try {
|
||||
e.getValue().mExternalCb.onRangingClosed(
|
||||
e.getKey(), RangingSession.Callback.REASON_UNKNOWN,
|
||||
new PersistableBundle());
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Failed to send session close callback " + e.getKey(), ex);
|
||||
}
|
||||
}
|
||||
// Clear all sessions.
|
||||
mCallbacksMap.clear();
|
||||
}
|
||||
mVendorUwbAdapter = null;
|
||||
}
|
||||
|
||||
private synchronized IUwbAdapter getVendorUwbAdapter() throws IllegalStateException {
|
||||
if (mVendorUwbAdapter != null) return mVendorUwbAdapter;
|
||||
mVendorUwbAdapter = mUwbInjector.getVendorService();
|
||||
if (mVendorUwbAdapter == null) {
|
||||
throw new IllegalStateException("No vendor service found!");
|
||||
}
|
||||
Log.i(TAG, "Retrieved vendor service");
|
||||
linkToVendorServiceDeath();
|
||||
return mVendorUwbAdapter;
|
||||
}
|
||||
|
||||
@@ -234,4 +284,5 @@ public class UwbServiceImpl extends IUwbAdapter.Stub {
|
||||
public void closeRanging(SessionHandle sessionHandle) throws RemoteException {
|
||||
getVendorUwbAdapter().closeRanging(sessionHandle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user