From c0656a88e620e7813fea498538de89a7fddd0dec Mon Sep 17 00:00:00 2001 From: Ahmad Khalil <khalilahmad@google.com> Date: Sat, 9 Dec 2023 21:05:36 +0000 Subject: [PATCH] Introducing VibratorControlService We're adding part of the framework's implementation for IVibratorControlService, which will allow registering and unregistering a VibrationController. We're also registering the service through the VibratorManagerService. Bug: 305961689 Test: atest FrameworksVibratorServicesTests Change-Id: Ic95c1e5b73106f9c89f02277966586c6c0855e4f --- core/java/android/os/vibrator/flags.aconfig | 7 ++ services/core/Android.bp | 1 + .../vibrator/VibratorControlService.java | 105 ++++++++++++++++++ .../vibrator/VibratorControllerHolder.java | 70 ++++++++++++ .../vibrator/VibratorManagerService.java | 7 ++ services/manifest_services.xml | 5 + .../vibrator/VibratorControlServiceTest.java | 63 +++++++++++ .../VibratorControllerHolderTest.java | 72 ++++++++++++ .../vibrator/VibratorManagerServiceTest.java | 7 +- .../vibrator/FakeVibratorController.java | 58 ++++++++++ 10 files changed, 392 insertions(+), 3 deletions(-) create mode 100644 services/core/java/com/android/server/vibrator/VibratorControlService.java create mode 100644 services/core/java/com/android/server/vibrator/VibratorControllerHolder.java create mode 100644 services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java create mode 100644 services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java create mode 100644 services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index 69d86a6604ad..437668c9a7de 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -44,3 +44,10 @@ flag { description: "Enables the independent keyboard vibration settings feature" bug: "289107579" } + +flag { + namespace: "haptics" + name: "adaptive_haptics_enabled" + description: "Enables the adaptive haptics feature" + bug: "305961689" +} diff --git a/services/core/Android.bp b/services/core/Android.bp index 20a3b9ada85d..b4cf34e00c53 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -154,6 +154,7 @@ java_library_static { static_libs: [ "android.frameworks.location.altitude-V1-java", // AIDL + "android.frameworks.vibrator-V1-java", // AIDL "android.hardware.authsecret-V1.0-java", "android.hardware.authsecret-V1-java", "android.hardware.boot-V1.0-java", // HIDL diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java new file mode 100644 index 000000000000..2eeb903bb551 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 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.vibrator; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.frameworks.vibrator.IVibratorControlService; +import android.frameworks.vibrator.IVibratorController; +import android.frameworks.vibrator.VibrationParam; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import java.util.Objects; + +/** + * Implementation of {@link IVibratorControlService} which allows the registration of + * {@link IVibratorController} to set and receive vibration params. + * + * @hide + */ +public final class VibratorControlService extends IVibratorControlService.Stub { + private static final String TAG = "VibratorControlService"; + + private final VibratorControllerHolder mVibratorControllerHolder; + private final Object mLock; + + public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) { + mVibratorControllerHolder = vibratorControllerHolder; + mLock = lock; + } + + @Override + public void registerVibratorController(IVibratorController controller) + throws RemoteException { + synchronized (mLock) { + mVibratorControllerHolder.setVibratorController(controller); + } + } + + @Override + public void unregisterVibratorController(@NonNull IVibratorController controller) + throws RemoteException { + Objects.requireNonNull(controller); + + synchronized (mLock) { + if (mVibratorControllerHolder.getVibratorController() == null) { + Slog.w(TAG, "Received request to unregister IVibratorController = " + + controller + ", but no controller was previously registered. Request " + + "Ignored."); + return; + } + if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(), + controller.asBinder())) { + Slog.wtf(TAG, "Failed to unregister IVibratorController. The provided " + + "controller doesn't match the registered one. " + this); + return; + } + mVibratorControllerHolder.setVibratorController(null); + } + } + + @Override + public void setVibrationParams( + @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token) + throws RemoteException { + // TODO(b/305939964): Add set vibration implementation. + } + + @Override + public void clearVibrationParams(int types, IVibratorController token) throws RemoteException { + // TODO(b/305939964): Add clear vibration implementation. + } + + @Override + public void onRequestVibrationParamsComplete( + IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) + throws RemoteException { + // TODO(305942827): Cache the vibration params in VibrationScaler + } + + @Override + public int getInterfaceVersion() throws RemoteException { + return this.VERSION; + } + + @Override + public String getInterfaceHash() throws RemoteException { + return this.HASH; + } +} diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java new file mode 100644 index 000000000000..63e69db9480f --- /dev/null +++ b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 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.vibrator; + +import android.annotation.NonNull; +import android.frameworks.vibrator.IVibratorController; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +/** + * Holder class for {@link IVibratorController}. + * + * @hide + */ +public final class VibratorControllerHolder implements IBinder.DeathRecipient { + private static final String TAG = "VibratorControllerHolder"; + + private IVibratorController mVibratorController; + + public IVibratorController getVibratorController() { + return mVibratorController; + } + + /** + * Sets the {@link IVibratorController} in {@link VibratorControllerHolder} to the new + * controller. This will also take care of registering and unregistering death notifications + * for the cached {@link IVibratorController}. + */ + public void setVibratorController(IVibratorController controller) { + try { + if (mVibratorController != null) { + mVibratorController.asBinder().unlinkToDeath(this, 0); + } + mVibratorController = controller; + if (mVibratorController != null) { + mVibratorController.asBinder().linkToDeath(this, 0); + } + } catch (RemoteException e) { + Slog.wtf(TAG, "Failed to set IVibratorController: " + this, e); + } + } + + @Override + public void binderDied(@NonNull IBinder deadBinder) { + if (deadBinder == mVibratorController.asBinder()) { + setVibratorController(null); + } + } + + @Override + public void binderDied() { + // Should not be used as binderDied(IBinder who) is overridden. + Slog.wtf(TAG, "binderDied() called unexpectedly."); + } +} diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 7d4bd3baf613..fc824abd80f5 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -91,6 +91,8 @@ import java.util.function.Function; public class VibratorManagerService extends IVibratorManagerService.Stub { private static final String TAG = "VibratorManagerService"; private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service"; + private static final String VIBRATOR_CONTROL_SERVICE = + "android.frameworks.vibrator.IVibratorControlService/default"; private static final boolean DEBUG = false; private static final VibrationAttributes DEFAULT_ATTRIBUTES = new VibrationAttributes.Builder().build(); @@ -269,6 +271,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED); injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); + if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) { + injector.addService(VIBRATOR_CONTROL_SERVICE, + new VibratorControlService(new VibratorControllerHolder(), mLock)); + } + } /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */ diff --git a/services/manifest_services.xml b/services/manifest_services.xml index 76389154a885..eae159fe9e89 100644 --- a/services/manifest_services.xml +++ b/services/manifest_services.xml @@ -4,4 +4,9 @@ <version>1</version> <fqname>IAltitudeService/default</fqname> </hal> + <hal format="aidl"> + <name>android.frameworks.vibrator</name> + <version>1</version> + <fqname>IVibratorControlService/default</fqname> + </hal> </manifest> diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java new file mode 100644 index 000000000000..49efd1bdd92a --- /dev/null +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java @@ -0,0 +1,63 @@ +/* + * 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.vibrator; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.RemoteException; + +import org.junit.Before; +import org.junit.Test; + +public class VibratorControlServiceTest { + + private VibratorControlService mVibratorControlService; + private final Object mLock = new Object(); + + @Before + public void setUp() throws Exception { + mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock); + } + + @Test + public void testRegisterVibratorController() throws RemoteException { + FakeVibratorController fakeController = new FakeVibratorController(); + mVibratorControlService.registerVibratorController(fakeController); + + assertThat(fakeController.isLinkedToDeath).isTrue(); + } + + @Test + public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest() + throws RemoteException { + FakeVibratorController fakeController = new FakeVibratorController(); + mVibratorControlService.registerVibratorController(fakeController); + mVibratorControlService.unregisterVibratorController(fakeController); + assertThat(fakeController.isLinkedToDeath).isFalse(); + } + + @Test + public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest() + throws RemoteException { + FakeVibratorController fakeController1 = new FakeVibratorController(); + FakeVibratorController fakeController2 = new FakeVibratorController(); + mVibratorControlService.registerVibratorController(fakeController1); + + mVibratorControlService.unregisterVibratorController(fakeController2); + assertThat(fakeController1.isLinkedToDeath).isTrue(); + } +} diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java new file mode 100644 index 000000000000..79abe21a301d --- /dev/null +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java @@ -0,0 +1,72 @@ +/* + * 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.vibrator; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.RemoteException; + +import org.junit.Before; +import org.junit.Test; + +public class VibratorControllerHolderTest { + + private final FakeVibratorController mFakeVibratorController = new FakeVibratorController(); + private VibratorControllerHolder mVibratorControllerHolder; + + @Before + public void setUp() throws Exception { + mVibratorControllerHolder = new VibratorControllerHolder(); + } + + @Test + public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException { + mVibratorControllerHolder.setVibratorController(mFakeVibratorController); + assertThat(mVibratorControllerHolder.getVibratorController()) + .isEqualTo(mFakeVibratorController); + assertThat(mFakeVibratorController.isLinkedToDeath).isTrue(); + } + + @Test + public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath() + throws RemoteException { + mVibratorControllerHolder.setVibratorController(mFakeVibratorController); + mVibratorControllerHolder.setVibratorController(null); + assertThat(mFakeVibratorController.isLinkedToDeath).isFalse(); + assertThat(mVibratorControllerHolder.getVibratorController()).isNull(); + } + + @Test + public void testBinderDied_withValidController_unlinksVibratorControllerToDeath() + throws RemoteException { + mVibratorControllerHolder.setVibratorController(mFakeVibratorController); + mVibratorControllerHolder.binderDied(mFakeVibratorController); + assertThat(mFakeVibratorController.isLinkedToDeath).isFalse(); + assertThat(mVibratorControllerHolder.getVibratorController()).isNull(); + } + + @Test + public void testBinderDied_withInvalidController_ignoresRequest() + throws RemoteException { + mVibratorControllerHolder.setVibratorController(mFakeVibratorController); + FakeVibratorController imposterVibratorController = new FakeVibratorController(); + mVibratorControllerHolder.binderDied(imposterVibratorController); + assertThat(mFakeVibratorController.isLinkedToDeath).isTrue(); + assertThat(mVibratorControllerHolder.getVibratorController()) + .isEqualTo(mFakeVibratorController); + } +} diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 4e9bbe0a28fe..d6b2116e2682 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -307,9 +307,10 @@ public class VibratorManagerServiceTest { @Override void addService(String name, IBinder service) { - Object serviceInstance = service; - mExternalVibratorService = - (VibratorManagerService.ExternalVibratorService) serviceInstance; + if (service instanceof VibratorManagerService.ExternalVibratorService) { + mExternalVibratorService = + (VibratorManagerService.ExternalVibratorService) service; + } } HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider( diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java new file mode 100644 index 000000000000..7e235870cedc --- /dev/null +++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 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.vibrator; + +import android.annotation.NonNull; +import android.frameworks.vibrator.IVibratorController; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * Provides a fake implementation of {@link android.frameworks.vibrator.IVibratorController} for + * testing. + */ +public final class FakeVibratorController extends IVibratorController.Stub { + + public boolean isLinkedToDeath = false; + + @Override + public void requestVibrationParams(int i, long l, IBinder iBinder) throws RemoteException { + + } + + @Override + public int getInterfaceVersion() throws RemoteException { + return 0; + } + + @Override + public String getInterfaceHash() throws RemoteException { + return null; + } + + @Override + public void linkToDeath(@NonNull DeathRecipient recipient, int flags) { + super.linkToDeath(recipient, flags); + isLinkedToDeath = true; + } + + @Override + public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) { + isLinkedToDeath = false; + return super.unlinkToDeath(recipient, flags); + } +} -- GitLab