UwbService: Move to packages/modules/Uwb

Also, load the uwb system service jar from uwb apex.

Bug: 194906542
Test: Compiles
Change-Id: Iffc8988c56ba3199c5159563b06120a84a0632dd
This commit is contained in:
Roshan Pius
2021-07-30 11:51:54 -07:00
parent f6e3870461
commit c463a36ace
10 changed files with 3 additions and 847 deletions

View File

@@ -68,7 +68,6 @@ filegroup {
":services.texttospeech-sources",
":services.usage-sources",
":services.usb-sources",
":services.uwb-sources",
":services.voiceinteraction-sources",
":services.wifi-sources",
],
@@ -122,7 +121,6 @@ java_library {
"services.texttospeech",
"services.usage",
"services.usb",
"services.uwb",
"services.voiceinteraction",
"services.wifi",
"service-blobstore",

View File

@@ -384,6 +384,8 @@ 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_APEX_SERVICE_JAR_PATH =
"/apex/com.android.uwb/javalib/service-uwb.jar";
private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";
private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
@@ -2651,7 +2653,7 @@ public final class SystemServer implements Dumpable {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) {
t.traceBegin("UwbService");
mSystemServiceManager.startService(UWB_SERVICE_CLASS);
mSystemServiceManager.startServiceFromJar(UWB_SERVICE_CLASS, UWB_APEX_SERVICE_JAR_PATH);
t.traceEnd();
}

View File

@@ -37,7 +37,6 @@ android_test {
"services.net",
"services.people",
"services.usage",
"services.uwb",
"guava",
"guava-android-testlib",
"androidx.test.core",

View File

@@ -1 +0,0 @@
include platform/frameworks/base:/core/java/android/uwb/OWNERS

View File

@@ -1,365 +0,0 @@
/*
* 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 android.Manifest.permission.UWB_PRIVILEGED;
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.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;
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 {
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;
@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(mUwbInjector.checkUwbRangingPermissionForDataDelivery(any(), any())).thenReturn(true);
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(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
verify(mVendorService).openRanging(
eq(ATTRIBUTION_SOURCE), 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(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
verify(mVendorService).openRanging(
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
// 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(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
verify(mVendorService).openRanging(
eq(ATTRIBUTION_SOURCE), 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(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
verify(mVendorServiceBinder).linkToDeath(mVendorServiceDeathCaptor.capture(), anyInt());
assertThat(mVendorServiceDeathCaptor.getValue()).isNotNull();
verify(mVendorService).openRanging(
eq(ATTRIBUTION_SOURCE), 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()));
}
@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);
}
}

View File

@@ -1,26 +0,0 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
filegroup {
name: "services.uwb-sources",
srcs: ["java/**/*.java"],
path: "java",
visibility: ["//frameworks/base/services"],
}
java_library_static {
name: "services.uwb",
defaults: ["platform_service_defaults"],
srcs: [
":services.uwb-sources",
],
libs: [
"services.core",
],
}

View File

@@ -1 +0,0 @@
include platform/frameworks/base:/core/java/android/uwb/OWNERS

View File

@@ -1,83 +0,0 @@
/*
* 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 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;
/**
* To be used for dependency injection (especially helps mocking static dependencies).
*/
public class UwbInjector {
private static final String TAG = "UwbInjector";
private static final String VENDOR_SERVICE_NAME = "uwb_vendor";
private final Context mContext;
public UwbInjector(@NonNull Context context) {
mContext = context;
}
/**
* @return Returns the vendor service handle.
*/
public IUwbAdapter getVendorService() {
IBinder b = ServiceManager.getService(VENDOR_SERVICE_NAME);
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;
}
}

View File

@@ -1,42 +0,0 @@
/*
* 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 android.content.Context;
import android.util.Log;
import com.android.server.SystemService;
/**
* Uwb System service.
*/
public class UwbService extends SystemService {
private static final String TAG = "UwbService";
private final UwbServiceImpl mImpl;
public UwbService(Context context) {
super(context);
mImpl = new UwbServiceImpl(context, new UwbInjector(context));
}
@Override
public void onStart() {
Log.i(TAG, "Registering " + Context.UWB_SERVICE);
publishBinderService(Context.UWB_SERVICE, mImpl);
}
}

View File

@@ -1,325 +0,0 @@
/*
* 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 android.annotation.NonNull;
import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
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.RangingSession;
import android.uwb.SessionHandle;
import com.android.internal.annotations.GuardedBy;
import java.util.Map;
/**
* Implementation of {@link android.uwb.IUwbAdapter} binder service.
*/
public class UwbServiceImpl extends IUwbAdapter.Stub implements IBinder.DeathRecipient{
private static final String TAG = "UwbServiceImpl";
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.
*
* Access to these callbacks are synchronized.
*/
private class UwbRangingCallbacksWrapper extends IUwbRangingCallbacks.Stub
implements IBinder.DeathRecipient {
private final AttributionSource mAttributionSource;
private final SessionHandle mSessionHandle;
private final IUwbRangingCallbacks mExternalCb;
private boolean mIsValid;
UwbRangingCallbacksWrapper(@NonNull AttributionSource attributionSource,
@NonNull SessionHandle sessionHandle,
@NonNull IUwbRangingCallbacks externalCb) {
mAttributionSource = attributionSource;
mSessionHandle = sessionHandle;
mExternalCb = externalCb;
mIsValid = true;
// 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.", e);
}
}
private void removeClientAndUnlinkToDeath() {
// Remove from the map.
synchronized (mCallbacksMap) {
mCallbacksMap.remove(mSessionHandle);
}
IBinder binder = mExternalCb.asBinder();
binder.unlinkToDeath(this, 0);
mIsValid = false;
}
@Override
public synchronized void onRangingOpened(SessionHandle sessionHandle)
throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingOpened(sessionHandle);
}
@Override
public synchronized void onRangingOpenFailed(SessionHandle sessionHandle,
int reason, PersistableBundle parameters) throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingOpenFailed(sessionHandle, reason, parameters);
}
@Override
public synchronized void onRangingStarted(SessionHandle sessionHandle,
PersistableBundle parameters)
throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingStarted(sessionHandle, parameters);
}
@Override
public synchronized void onRangingStartFailed(SessionHandle sessionHandle,
int reason, PersistableBundle parameters) throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingStartFailed(sessionHandle, reason, parameters);
}
@Override
public synchronized void onRangingReconfigured(SessionHandle sessionHandle,
PersistableBundle parameters)
throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingReconfigured(sessionHandle, parameters);
}
@Override
public synchronized void onRangingReconfigureFailed(SessionHandle sessionHandle,
int reason, PersistableBundle parameters) throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingReconfigureFailed(sessionHandle, reason, parameters);
}
@Override
public synchronized void onRangingStopped(SessionHandle sessionHandle, int reason,
PersistableBundle parameters)
throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingStopped(sessionHandle, reason, parameters);
}
@Override
public synchronized void onRangingStopFailed(SessionHandle sessionHandle, int reason,
PersistableBundle parameters) throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingStopFailed(sessionHandle, reason, parameters);
}
@Override
public synchronized void onRangingClosed(SessionHandle sessionHandle, int reason,
PersistableBundle parameters) throws RemoteException {
if (!mIsValid) return;
mExternalCb.onRangingClosed(sessionHandle, reason, parameters);
removeClientAndUnlinkToDeath();
}
@Override
public synchronized void onRangingResult(SessionHandle sessionHandle,
RangingReport rangingReport)
throws RemoteException {
if (!mIsValid) return;
boolean permissionGranted = Binder.withCleanCallingIdentity(
() -> mUwbInjector.checkUwbRangingPermissionForDataDelivery(
mAttributionSource, "uwb ranging result"));
if (!permissionGranted) {
Log.e(TAG, "Not delivering ranging result because of permission denial"
+ mSessionHandle);
return;
}
mExternalCb.onRangingResult(sessionHandle, rangingReport);
}
@Override
public synchronized void binderDied() {
if (!mIsValid) return;
Log.i(TAG, "Client died: ending session: " + mSessionHandle);
try {
removeClientAndUnlinkToDeath();
stopRanging(mSessionHandle);
closeRanging(mSessionHandle);
} 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 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;
}
UwbServiceImpl(@NonNull Context context, @NonNull UwbInjector uwbInjector) {
mContext = context;
mUwbInjector = uwbInjector;
}
private void enforceUwbPrivilegedPermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.UWB_PRIVILEGED,
"UwbService");
}
@Override
public void registerAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks)
throws RemoteException {
enforceUwbPrivilegedPermission();
getVendorUwbAdapter().registerAdapterStateCallbacks(adapterStateCallbacks);
}
@Override
public void unregisterAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks)
throws RemoteException {
enforceUwbPrivilegedPermission();
getVendorUwbAdapter().unregisterAdapterStateCallbacks(adapterStateCallbacks);
}
@Override
public long getTimestampResolutionNanos() throws RemoteException {
enforceUwbPrivilegedPermission();
return getVendorUwbAdapter().getTimestampResolutionNanos();
}
@Override
public PersistableBundle getSpecificationInfo() throws RemoteException {
enforceUwbPrivilegedPermission();
return getVendorUwbAdapter().getSpecificationInfo();
}
@Override
public void openRanging(AttributionSource attributionSource,
SessionHandle sessionHandle, IUwbRangingCallbacks rangingCallbacks,
PersistableBundle parameters) throws RemoteException {
enforceUwbPrivilegedPermission();
mUwbInjector.enforceUwbRangingPermissionForPreflight(attributionSource);
UwbRangingCallbacksWrapper wrapperCb =
new UwbRangingCallbacksWrapper(attributionSource, sessionHandle, rangingCallbacks);
synchronized (mCallbacksMap) {
mCallbacksMap.put(sessionHandle, wrapperCb);
}
getVendorUwbAdapter().openRanging(attributionSource, sessionHandle, wrapperCb, parameters);
}
@Override
public void startRanging(SessionHandle sessionHandle, PersistableBundle parameters)
throws RemoteException {
enforceUwbPrivilegedPermission();
getVendorUwbAdapter().startRanging(sessionHandle, parameters);
}
@Override
public void reconfigureRanging(SessionHandle sessionHandle, PersistableBundle parameters)
throws RemoteException {
enforceUwbPrivilegedPermission();
getVendorUwbAdapter().reconfigureRanging(sessionHandle, parameters);
}
@Override
public void stopRanging(SessionHandle sessionHandle) throws RemoteException {
enforceUwbPrivilegedPermission();
getVendorUwbAdapter().stopRanging(sessionHandle);
}
@Override
public void closeRanging(SessionHandle sessionHandle) throws RemoteException {
enforceUwbPrivilegedPermission();
getVendorUwbAdapter().closeRanging(sessionHandle);
}
@Override
public synchronized int getAdapterState() throws RemoteException {
return getVendorUwbAdapter().getAdapterState();
}
@Override
public synchronized void setEnabled(boolean enabled) throws RemoteException {
getVendorUwbAdapter().setEnabled(enabled);
}
}