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

Commit 9bed14ec authored by Roshan Pius's avatar Roshan Pius Committed by Android (Google) Code Review
Browse files

Merge changes from topic "uwb_aosp_service" into sc-dev

* changes:
  UwbService: Handle vendor service death
  UwbService: Create a UwbRanging callback wrapper
  Uwb: Create a new Uwb system service
parents 90e33e72 5681a481
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -24,9 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.AttributionSource;
import android.content.Context;
import android.content.ContextParams;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.PersistableBundle;
@@ -49,7 +47,7 @@ import java.util.concurrent.Executor;
@SystemApi
@SystemService(Context.UWB_SERVICE)
public final class UwbManager {
    private static final String SERVICE_NAME = "uwb";
    private static final String SERVICE_NAME = Context.UWB_SERVICE;

    private final Context mContext;
    private final IUwbAdapter mUwbAdapter;
+2 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ filegroup {
        ":services.texttospeech-sources",
        ":services.usage-sources",
        ":services.usb-sources",
        ":services.uwb-sources",
        ":services.voiceinteraction-sources",
        ":services.wifi-sources",
    ],
@@ -129,6 +130,7 @@ java_library {
        "services.texttospeech",
        "services.usage",
        "services.usb",
        "services.uwb",
        "services.voiceinteraction",
        "services.wifi",
        "service-blobstore",
+7 −0
Original line number Diff line number Diff line
@@ -384,6 +384,7 @@ public final class SystemServer implements Dumpable {
    private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
    private static final String GAME_MANAGER_SERVICE_CLASS =
            "com.android.server.app.GameManagerService$Lifecycle";
    private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";

    private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";

@@ -2637,6 +2638,12 @@ public final class SystemServer implements Dumpable {
        LocalManagerRegistry.addManager(ArtManagerLocal.class, new ArtManagerLocal());
        t.traceEnd();

        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) {
            t.traceBegin("UwbService");
            mSystemServiceManager.startService(UWB_SERVICE_CLASS);
            t.traceEnd();
        }

        t.traceBegin("StartBootPhaseDeviceSpecificServicesReady");
        mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
        t.traceEnd();
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ android_test {
        "services.net",
        "services.people",
        "services.usage",
        "services.uwb",
        "guava",
        "androidx.test.core",
        "androidx.test.ext.truth",
+299 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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.argThat;
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;

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;

/**
 * Tests for {@link UwbServiceImpl}.
 */
@RunWith(AndroidJUnit4.class)
@SmallTest
@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;
    @Captor private ArgumentCaptor<IBinder.DeathRecipient> mClientDeathCaptor;
    @Captor private ArgumentCaptor<IBinder.DeathRecipient> mVendorServiceDeathCaptor;

    private UwbServiceImpl mUwbServiceImpl;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        when(mUwbInjector.getVendorService()).thenReturn(mVendorService);
        when(mVendorService.asBinder()).thenReturn(mVendorServiceBinder);
        mUwbServiceImpl = new UwbServiceImpl(mContext, mUwbInjector);
    }

    @Test
    public void testApiCallThrowsIllegalStateExceptionIfVendorServiceNotFound() throws Exception {
        when(mUwbInjector.getVendorService()).thenReturn(null);

        final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
        try {
            mUwbServiceImpl.registerAdapterStateCallbacks(cb);
            fail();
        } catch (IllegalStateException e) { /* pass */ }
    }

    @Test
    public void testRegisterAdapterStateCallbacks() throws Exception {
        final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
        mUwbServiceImpl.registerAdapterStateCallbacks(cb);

        verify(mVendorService).registerAdapterStateCallbacks(cb);
    }

    @Test
    public void testUnregisterAdapterStateCallbacks() throws Exception {
        final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
        mUwbServiceImpl.unregisterAdapterStateCallbacks(cb);

        verify(mVendorService).unregisterAdapterStateCallbacks(cb);
    }

    @Test
    public void testGetTimestampResolutionNanos() throws Exception {
        final long timestamp = 34L;
        when(mVendorService.getTimestampResolutionNanos()).thenReturn(timestamp);
        assertThat(mUwbServiceImpl.getTimestampResolutionNanos()).isEqualTo(timestamp);

        verify(mVendorService).getTimestampResolutionNanos();
    }

    @Test
    public void testGetSpecificationInfo() throws Exception {
        final PersistableBundle specification = new PersistableBundle();
        when(mVendorService.getSpecificationInfo()).thenReturn(specification);
        assertThat(mUwbServiceImpl.getSpecificationInfo()).isEqualTo(specification);

        verify(mVendorService).getSpecificationInfo();
    }

    @Test
    public void testOpenRanging() 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();
    }

    @Test
    public void testStartRanging() throws Exception {
        final SessionHandle sessionHandle = new SessionHandle(5);
        final PersistableBundle parameters = new PersistableBundle();

        mUwbServiceImpl.startRanging(sessionHandle, parameters);

        verify(mVendorService).startRanging(sessionHandle, parameters);
    }

    @Test
    public void testReconfigureRanging() throws Exception {
        final SessionHandle sessionHandle = new SessionHandle(5);
        final PersistableBundle parameters = new PersistableBundle();

        mUwbServiceImpl.reconfigureRanging(sessionHandle, parameters);

        verify(mVendorService).reconfigureRanging(sessionHandle, parameters);
    }

    @Test
    public void testStopRanging() throws Exception {
        final SessionHandle sessionHandle = new SessionHandle(5);

        mUwbServiceImpl.stopRanging(sessionHandle);

        verify(mVendorService).stopRanging(sessionHandle);
    }

    @Test
    public void testCloseRanging() throws Exception {
        final SessionHandle sessionHandle = new SessionHandle(5);

        mUwbServiceImpl.closeRanging(sessionHandle);

        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());
    }

    @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()));
    }
}
Loading