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

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

Merge changes from topic "uwb_aosp_service_permission" into sc-dev

* changes:
  UwbService: Enforce UWB_RANGING runtime permission
  UwbService: Enforce UWB_PRIVILEGED permission
parents be20ca46 3ec33a7e
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.uwb;

import android.content.AttributionSource;
import android.os.PersistableBundle;
import android.uwb.IUwbAdapterStateCallbacks;
import android.uwb.IUwbRangingCallbacks;
@@ -77,11 +78,13 @@ interface IUwbAdapter {
   * If the provided sessionHandle is already open for the calling client, then
   * #onRangingOpenFailed must be called and the new session must not be opened.
   *
   * @param attributionSource AttributionSource to use for permission enforcement.
   * @param sessionHandle the session handle to open ranging for
   * @param rangingCallbacks the callbacks used to deliver ranging information
   * @param parameters the configuration to use for ranging
   */
  void openRanging(in SessionHandle sessionHandle,
  void openRanging(in AttributionSource attributionSource,
                   in SessionHandle sessionHandle,
                   in IUwbRangingCallbacks rangingCallbacks,
                   in PersistableBundle parameters);

+1 −2
Original line number Diff line number Diff line
@@ -63,8 +63,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
                    new RangingSession(executor, callbacks, mAdapter, sessionHandle);
            mRangingSessionTable.put(sessionHandle, session);
            try {
                // TODO: Pass in the attributionSource to the service.
                mAdapter.openRanging(sessionHandle, this, params);
                mAdapter.openRanging(attributionSource, sessionHandle, this, params);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
+20 −10
Original line number Diff line number Diff line
@@ -57,7 +57,8 @@ public class RangingManagerTest {
        RangingManager rangingManager = new RangingManager(adapter);
        RangingSession.Callback callback = mock(RangingSession.Callback.class);
        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
        verify(adapter, times(1)).openRanging(any(), eq(rangingManager), eq(PARAMS));
        verify(adapter, times(1)).openRanging(
                eq(ATTRIBUTION_SOURCE), any(), any(), any());
    }

    @Test
@@ -80,11 +81,13 @@ public class RangingManagerTest {

        RangingManager rangingManager = new RangingManager(adapter);
        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(1)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();

        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(2)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();

        rangingManager.onRangingOpened(sessionHandle1);
@@ -106,7 +109,8 @@ public class RangingManagerTest {
                ArgumentCaptor.forClass(SessionHandle.class);

        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(1)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        SessionHandle handle = sessionHandleCaptor.getValue();

        rangingManager.onRangingOpened(handle);
@@ -151,11 +155,13 @@ public class RangingManagerTest {
                ArgumentCaptor.forClass(SessionHandle.class);

        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(1)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();

        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(2)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();

        rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
@@ -178,12 +184,14 @@ public class RangingManagerTest {

        RangingManager rangingManager = new RangingManager(adapter);
        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(1)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();

        rangingManager.onRangingStarted(sessionHandle1, PARAMS);
        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(2)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
        rangingManager.onRangingStarted(sessionHandle2, PARAMS);

@@ -230,7 +238,8 @@ public class RangingManagerTest {
                ArgumentCaptor.forClass(SessionHandle.class);

        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(1)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        SessionHandle handle = sessionHandleCaptor.getValue();

        rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
@@ -238,7 +247,8 @@ public class RangingManagerTest {

        // Open a new session
        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
        verify(adapter, times(2)).openRanging(
                eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
        handle = sessionHandleCaptor.getValue();
        rangingManager.onRangingOpened(handle);

+74 −8
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.uwb;

import static android.Manifest.permission.UWB_PRIVILEGED;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.fail;
@@ -24,11 +26,13 @@ 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.doThrow;
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.AttributionSource;
import android.content.Context;
import android.os.IBinder;
import android.os.PersistableBundle;
@@ -58,6 +62,11 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@Presubmit
public class UwbServiceImplTest {
    private static final int UID = 343453;
    private static final String PACKAGE_NAME = "com.uwb.test";
    private static final AttributionSource ATTRIBUTION_SOURCE =
            new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build();

    @Mock private IUwbAdapter mVendorService;
    @Mock private IBinder mVendorServiceBinder;
    @Mock private Context mContext;
@@ -72,6 +81,7 @@ public class UwbServiceImplTest {
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        when(mUwbInjector.getVendorService()).thenReturn(mVendorService);
        when(mUwbInjector.checkUwbRangingPermissionForDataDelivery(any(), any())).thenReturn(true);
        when(mVendorService.asBinder()).thenReturn(mVendorServiceBinder);
        mUwbServiceImpl = new UwbServiceImpl(mContext, mUwbInjector);
    }
@@ -129,10 +139,11 @@ public class UwbServiceImplTest {
        final IBinder cbBinder = mock(IBinder.class);
        when(cb.asBinder()).thenReturn(cbBinder);

        mUwbServiceImpl.openRanging(sessionHandle, cb, parameters);
        mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);

        verify(mVendorService).openRanging(
                eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters));
                eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
                eq(parameters));
        assertThat(mRangingCbCaptor.getValue()).isNotNull();
    }

@@ -182,10 +193,11 @@ public class UwbServiceImplTest {
        final IBinder cbBinder = mock(IBinder.class);
        when(cb.asBinder()).thenReturn(cbBinder);

        mUwbServiceImpl.openRanging(sessionHandle, cb, parameters);
        mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);

        verify(mVendorService).openRanging(
                eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters));
                eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
                eq(parameters));
        assertThat(mRangingCbCaptor.getValue()).isNotNull();

        // Invoke vendor service callbacks and ensure that the corresponding app callback is
@@ -242,10 +254,11 @@ public class UwbServiceImplTest {
        final IBinder cbBinder = mock(IBinder.class);
        when(cb.asBinder()).thenReturn(cbBinder);

        mUwbServiceImpl.openRanging(sessionHandle, cb, parameters);
        mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);

        verify(mVendorService).openRanging(
                eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters));
                eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
                eq(parameters));
        assertThat(mRangingCbCaptor.getValue()).isNotNull();

        verify(cbBinder).linkToDeath(mClientDeathCaptor.capture(), anyInt());
@@ -275,13 +288,14 @@ public class UwbServiceImplTest {
        final IBinder cbBinder = mock(IBinder.class);
        when(cb.asBinder()).thenReturn(cbBinder);

        mUwbServiceImpl.openRanging(sessionHandle, cb, parameters);
        mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);

        verify(mVendorServiceBinder).linkToDeath(mVendorServiceDeathCaptor.capture(), anyInt());
        assertThat(mVendorServiceDeathCaptor.getValue()).isNotNull();

        verify(mVendorService).openRanging(
                eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters));
                eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
                eq(parameters));
        assertThat(mRangingCbCaptor.getValue()).isNotNull();

        clearInvocations(cb);
@@ -296,4 +310,56 @@ public class UwbServiceImplTest {
                eq(sessionHandle), eq(RangingSession.Callback.REASON_UNKNOWN),
                argThat((p) -> p.isEmpty()));
    }

    @Test
    public void testThrowSecurityExceptionWhenCalledWithoutUwbPrivilegedPermission()
            throws Exception {
        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
                eq(UWB_PRIVILEGED), any());
        final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
        try {
            mUwbServiceImpl.registerAdapterStateCallbacks(cb);
            fail();
        } catch (SecurityException e) { /* pass */ }
    }

    @Test
    public void testThrowSecurityExceptionWhenOpenRangingCalledWithoutUwbRangingPermission()
            throws Exception {
        doThrow(new SecurityException()).when(mUwbInjector).enforceUwbRangingPermissionForPreflight(
                any());

        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);
        try {
            mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
            fail();
        } catch (SecurityException e) { /* pass */ }
    }

    @Test
    public void testOnRangingResultCallbackNotSentWithoutUwbRangingPermission() 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(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);

        verify(mVendorService).openRanging(
                eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
                eq(parameters));
        assertThat(mRangingCbCaptor.getValue()).isNotNull();

        when(mUwbInjector.checkUwbRangingPermissionForDataDelivery(any(), any())).thenReturn(false);

        // Ensure the ranging cb is not delivered to the client.
        final RangingReport rangingReport = new RangingReport.Builder().build();
        mRangingCbCaptor.getValue().onRangingResult(sessionHandle, rangingReport);
        verify(cb, never()).onRangingResult(sessionHandle, rangingReport);
    }
}
+35 −0
Original line number Diff line number Diff line
@@ -16,8 +16,13 @@

package com.android.server.uwb;

import static android.Manifest.permission.UWB_RANGING;
import static android.content.PermissionChecker.PERMISSION_GRANTED;

import android.annotation.NonNull;
import android.content.AttributionSource;
import android.content.Context;
import android.content.PermissionChecker;
import android.os.IBinder;
import android.os.ServiceManager;
import android.uwb.IUwbAdapter;
@@ -45,4 +50,34 @@ public class UwbInjector {
        if (b == null) return null;
        return IUwbAdapter.Stub.asInterface(b);
    }

    /**
     * Throws security exception if the UWB_RANGING permission is not granted for the calling app.
     *
     * <p>Should be used in situations where the app op should not be noted.
     */
    public void enforceUwbRangingPermissionForPreflight(
            @NonNull AttributionSource attributionSource) {
        if (!attributionSource.checkCallingUid()) {
            throw new SecurityException("Invalid attribution source " + attributionSource);
        }
        int permissionCheckResult = PermissionChecker.checkPermissionForPreflight(
                mContext, UWB_RANGING, attributionSource);
        if (permissionCheckResult != PERMISSION_GRANTED) {
            throw new SecurityException("Caller does not hold UWB_RANGING permission");
        }
    }

    /**
     * Returns true if the UWB_RANGING permission is granted for the calling app.
     *
     * <p>Should be used in situations where data will be delivered and hence the app op should
     * be noted.
     */
    public boolean checkUwbRangingPermissionForDataDelivery(
            @NonNull AttributionSource attributionSource, @NonNull String message) {
        int permissionCheckResult = PermissionChecker.checkPermissionForDataDelivery(
                mContext, UWB_RANGING, -1, attributionSource, message);
        return permissionCheckResult == PERMISSION_GRANTED;
    }
}
Loading