Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5681a481 authored by Roshan Pius's avatar Roshan Pius
Browse files

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
parent e4fd3ed6
Loading
Loading
Loading
Loading
+38 −1
Original line number Diff line number Diff line
@@ -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()));
    }
}
+68 −17
Original line number Diff line number Diff line
@@ -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 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 IUwbAdapter getVendorUwbAdapter() throws IllegalStateException {
    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);
    }

}