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
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -37,6 +37,7 @@ android_test {
|
||||
"services.net",
|
||||
"services.people",
|
||||
"services.usage",
|
||||
"services.uwb",
|
||||
"guava",
|
||||
"androidx.test.core",
|
||||
"androidx.test.ext.truth",
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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()));
|
||||
}
|
||||
}
|
||||
26
services/uwb/Android.bp
Normal file
26
services/uwb/Android.bp
Normal file
@@ -0,0 +1,26 @@
|
||||
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",
|
||||
],
|
||||
}
|
||||
48
services/uwb/java/com/android/server/uwb/UwbInjector.java
Normal file
48
services/uwb/java/com/android/server/uwb/UwbInjector.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.Context;
|
||||
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);
|
||||
}
|
||||
}
|
||||
42
services/uwb/java/com/android/server/uwb/UwbService.java
Normal file
42
services/uwb/java/com/android/server/uwb/UwbService.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
288
services/uwb/java/com/android/server/uwb/UwbServiceImpl.java
Normal file
288
services/uwb/java/com/android/server/uwb/UwbServiceImpl.java
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* 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.Context;
|
||||
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 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();
|
||||
}
|
||||
|
||||
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;
|
||||
// TODO: Perform permission checks and noteOp.
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks)
|
||||
throws RemoteException {
|
||||
getVendorUwbAdapter().registerAdapterStateCallbacks(adapterStateCallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks)
|
||||
throws RemoteException {
|
||||
getVendorUwbAdapter().unregisterAdapterStateCallbacks(adapterStateCallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestampResolutionNanos() throws RemoteException {
|
||||
return getVendorUwbAdapter().getTimestampResolutionNanos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistableBundle getSpecificationInfo() throws RemoteException {
|
||||
return getVendorUwbAdapter().getSpecificationInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openRanging(SessionHandle sessionHandle, IUwbRangingCallbacks rangingCallbacks,
|
||||
PersistableBundle parameters) throws RemoteException {
|
||||
UwbRangingCallbacksWrapper wrapperCb =
|
||||
new UwbRangingCallbacksWrapper(sessionHandle, rangingCallbacks);
|
||||
synchronized (mCallbacksMap) {
|
||||
mCallbacksMap.put(sessionHandle, wrapperCb);
|
||||
}
|
||||
getVendorUwbAdapter().openRanging(sessionHandle, wrapperCb, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startRanging(SessionHandle sessionHandle, PersistableBundle parameters)
|
||||
throws RemoteException {
|
||||
// TODO: Perform permission checks.
|
||||
getVendorUwbAdapter().startRanging(sessionHandle, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconfigureRanging(SessionHandle sessionHandle, PersistableBundle parameters)
|
||||
throws RemoteException {
|
||||
getVendorUwbAdapter().reconfigureRanging(sessionHandle, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRanging(SessionHandle sessionHandle) throws RemoteException {
|
||||
getVendorUwbAdapter().stopRanging(sessionHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeRanging(SessionHandle sessionHandle) throws RemoteException {
|
||||
getVendorUwbAdapter().closeRanging(sessionHandle);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user