Skip to content
Snippets Groups Projects
Commit c0656a88 authored by Ahmad Khalil's avatar Ahmad Khalil
Browse files

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
parent 1b7e9c03
No related branches found
No related tags found
No related merge requests found
Showing
with 392 additions and 3 deletions
......@@ -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"
}
......@@ -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
......
/*
* 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;
}
}
/*
* 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.");
}
}
......@@ -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}. */
......
......@@ -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>
/*
* 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();
}
}
/*
* 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);
}
}
......@@ -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(
......
/*
* 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);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment