diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl new file mode 100644 index 0000000000000..e821e3194d214 --- /dev/null +++ b/core/java/android/os/IVibratorManagerService.aidl @@ -0,0 +1,24 @@ +/** + * 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 android.os; + +import android.os.VibrationAttributes; + +/** {@hide} */ +interface IVibratorManagerService { + int[] getVibratorIds(); +} diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 40c291f14b675..91eb2a5026382 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -3,6 +3,7 @@ per-file ExternalVibration.aidl = michaelwr@google.com per-file ExternalVibration.java = michaelwr@google.com per-file IExternalVibrationController.aidl = michaelwr@google.com per-file IExternalVibratorService.aidl = michaelwr@google.com +per-file IVibratorManagerService.aidl = michaelwr@google.com per-file IVibratorService.aidl = michaelwr@google.com per-file NullVibrator.java = michaelwr@google.com per-file SystemVibrator.java = michaelwr@google.com diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 8099f8f37bc7c..e67b9d8ccd91f 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -2,8 +2,7 @@ per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, junyulai@google.com, lorenzo@google.com, reminv@google.com, satk@google.com # Vibrator / Threads -per-file VibratorService.java, DisplayThread.java = michaelwr@google.com -per-file VibratorService.java, DisplayThread.java = ogunwale@google.com +per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com # Zram writeback per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java new file mode 100644 index 0000000000000..2f35da79420e8 --- /dev/null +++ b/services/core/java/com/android/server/VibratorManagerService.java @@ -0,0 +1,154 @@ +/* + * 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; + +import android.content.Context; +import android.os.IBinder; +import android.os.IVibratorManagerService; +import android.os.ResultReceiver; +import android.os.ShellCallback; +import android.os.ShellCommand; + +import com.android.internal.annotations.VisibleForTesting; + +import libcore.util.NativeAllocationRegistry; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; + +/** System implementation of {@link IVibratorManagerService}. */ +public class VibratorManagerService extends IVibratorManagerService.Stub { + private static final String TAG = "VibratorManagerService"; + private static final boolean DEBUG = false; + + private final Context mContext; + private final NativeWrapper mNativeWrapper; + private final int[] mVibratorIds; + + static native long nativeInit(); + + static native long nativeGetFinalizer(); + + static native int[] nativeGetVibratorIds(long nativeServicePtr); + + VibratorManagerService(Context context) { + this(context, new Injector()); + } + + @VisibleForTesting + VibratorManagerService(Context context, Injector injector) { + mContext = context; + mNativeWrapper = injector.getNativeWrapper(); + + mNativeWrapper.init(); + + int[] vibratorIds = mNativeWrapper.getVibratorIds(); + mVibratorIds = vibratorIds == null ? new int[0] : vibratorIds; + } + + @Override // Binder call + public int[] getVibratorIds() { + return Arrays.copyOf(mVibratorIds, mVibratorIds.length); + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback cb, ResultReceiver resultReceiver) { + new VibratorManagerShellCommand(this).exec(this, in, out, err, args, cb, resultReceiver); + } + + /** Point of injection for test dependencies */ + @VisibleForTesting + static class Injector { + + NativeWrapper getNativeWrapper() { + return new NativeWrapper(); + } + } + + /** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */ + @VisibleForTesting + public static class NativeWrapper { + + private long mNativeServicePtr = 0; + + /** Returns native pointer to newly created controller and connects with HAL service. */ + public void init() { + mNativeServicePtr = VibratorManagerService.nativeInit(); + long finalizerPtr = VibratorManagerService.nativeGetFinalizer(); + + if (finalizerPtr != 0) { + NativeAllocationRegistry registry = + NativeAllocationRegistry.createMalloced( + VibratorManagerService.class.getClassLoader(), finalizerPtr); + registry.registerNativeAllocation(this, mNativeServicePtr); + } + } + + /** Returns vibrator ids. */ + public int[] getVibratorIds() { + return VibratorManagerService.nativeGetVibratorIds(mNativeServicePtr); + } + } + + /** Provides limited functionality from {@link VibratorManagerService} as shell commands. */ + private final class VibratorManagerShellCommand extends ShellCommand { + + private final IBinder mToken; + + private VibratorManagerShellCommand(IBinder token) { + mToken = token; + } + + @Override + public int onCommand(String cmd) { + if ("list".equals(cmd)) { + return runListVibrators(); + } + return handleDefaultCommands(cmd); + } + + private int runListVibrators() { + try (PrintWriter pw = getOutPrintWriter();) { + if (mVibratorIds.length == 0) { + pw.println("No vibrator found"); + } else { + for (int id : mVibratorIds) { + pw.println(id); + } + } + pw.println(""); + return 0; + } + } + + @Override + public void onHelp() { + try (PrintWriter pw = getOutPrintWriter();) { + pw.println("Vibrator Manager commands:"); + pw.println(" help"); + pw.println(" Prints this help text."); + pw.println(""); + pw.println(" list"); + pw.println(" Prints the id of device vibrators. This do not include any "); + pw.println(" connected input device."); + pw.println(""); + } + } + } +} diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 687af107590af..cb6f616f50785 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -385,15 +385,7 @@ public class VibratorService extends IVibratorService.Stub mNativeWrapper = injector.getNativeWrapper(); mH = injector.createHandler(Looper.myLooper()); - long nativeServicePtr = mNativeWrapper.vibratorInit(this::onVibrationComplete); - long finalizerPtr = mNativeWrapper.vibratorGetFinalizer(); - - if (finalizerPtr != 0) { - NativeAllocationRegistry registry = - NativeAllocationRegistry.createMalloced( - VibratorService.class.getClassLoader(), finalizerPtr); - registry.registerNativeAllocation(this, nativeServicePtr); - } + mNativeWrapper.vibratorInit(this::onVibrationComplete); // Reset the hardware to a default state, in case this is a runtime // restart instead of a fresh boot. @@ -1746,18 +1738,17 @@ public class VibratorService extends IVibratorService.Stub return VibratorService.vibratorExists(mNativeServicePtr); } - /** - * Returns native pointer to newly created controller and initializes connection to vibrator - * HAL service. - */ - public long vibratorInit(OnCompleteListener listener) { + /** Initializes connection to vibrator HAL service. */ + public void vibratorInit(OnCompleteListener listener) { mNativeServicePtr = VibratorService.vibratorInit(listener); - return mNativeServicePtr; - } + long finalizerPtr = VibratorService.vibratorGetFinalizer(); - /** Returns pointer to native finalizer function to be called by GC. */ - public long vibratorGetFinalizer() { - return VibratorService.vibratorGetFinalizer(); + if (finalizerPtr != 0) { + NativeAllocationRegistry registry = + NativeAllocationRegistry.createMalloced( + VibratorService.class.getClassLoader(), finalizerPtr); + registry.registerNativeAllocation(this, mNativeServicePtr); + } } /** Turns vibrator on for given time. */ diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index d84f9d1a7dad5..a1a98c7f40959 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -53,6 +53,7 @@ cc_library_static { "com_android_server_UsbDescriptorParser.cpp", "com_android_server_UsbMidiDevice.cpp", "com_android_server_UsbHostManager.cpp", + "com_android_server_VibratorManagerService.cpp", "com_android_server_VibratorService.cpp", "com_android_server_PersistentDataBlockService.cpp", "com_android_server_am_CachedAppOptimizer.cpp", diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index 8646a53f3390c..4c017f5513a2a 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -2,6 +2,7 @@ per-file com_android_server_lights_LightsService.cpp = michaelwr@google.com, santoscordon@google.com # Haptics +per-file com_android_server_VibratorManagerService.cpp = michaelwr@google.com per-file com_android_server_VibratorService.cpp = michaelwr@google.com # Input diff --git a/services/core/jni/com_android_server_VibratorManagerService.cpp b/services/core/jni/com_android_server_VibratorManagerService.cpp new file mode 100644 index 0000000000000..dae9cefdd1f40 --- /dev/null +++ b/services/core/jni/com_android_server_VibratorManagerService.cpp @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#define LOG_TAG "VibratorManagerService" + +#include +#include "android_runtime/AndroidRuntime.h" +#include "core_jni_helpers.h" +#include "jni.h" + +#include +#include + +#include + +namespace android { + +class NativeVibratorManagerService { +public: + NativeVibratorManagerService() : mHal(std::make_unique()) {} + ~NativeVibratorManagerService() = default; + + vibrator::ManagerHalWrapper* hal() const { return mHal.get(); } + +private: + const std::unique_ptr mHal; +}; + +static void destroyNativeService(void* ptr) { + NativeVibratorManagerService* service = reinterpret_cast(ptr); + if (service) { + delete service; + } +} + +static jlong nativeInit(JNIEnv* /* env */, jclass /* clazz */) { + std::unique_ptr service = + std::make_unique(); + return reinterpret_cast(service.release()); +} + +static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { + return static_cast(reinterpret_cast(&destroyNativeService)); +} + +static jintArray nativeGetVibratorIds(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { + NativeVibratorManagerService* service = + reinterpret_cast(servicePtr); + if (service == nullptr) { + ALOGE("nativeGetVibratorIds failed because native service was not initialized"); + return nullptr; + } + auto result = service->hal()->getVibratorIds(); + if (!result.isOk()) { + return nullptr; + } + std::vector vibratorIds = result.value(); + jintArray ids = env->NewIntArray(vibratorIds.size()); + env->SetIntArrayRegion(ids, 0, vibratorIds.size(), reinterpret_cast(vibratorIds.data())); + return ids; +} + +static const JNINativeMethod method_table[] = { + {"nativeInit", "()J", (void*)nativeInit}, + {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer}, + {"nativeGetVibratorIds", "(J)[I", (void*)nativeGetVibratorIds}, +}; + +int register_android_server_VibratorManagerService(JNIEnv* env) { + return jniRegisterNativeMethods(env, "com/android/server/VibratorManagerService", method_table, + NELEM(method_table)); +} + +}; // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 5df1adafed5ed..0ffa5c39bacc3 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -37,6 +37,7 @@ int register_android_server_UsbDeviceManager(JNIEnv* env); int register_android_server_UsbMidiDevice(JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_vr_VrManagerService(JNIEnv* env); +int register_android_server_VibratorManagerService(JNIEnv* env); int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env); int register_android_server_location_GnssLocationProvider(JNIEnv* env); int register_android_server_connectivity_Vpn(JNIEnv* env); @@ -90,6 +91,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_UsbAlsaJackDetector(env); register_android_server_UsbHostManager(env); register_android_server_vr_VrManagerService(env); + register_android_server_VibratorManagerService(env); register_android_server_VibratorService(vm, env); register_android_server_SystemServer(env); register_android_server_location_GnssLocationProvider(env); diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java new file mode 100644 index 0000000000000..8a22a2f5d92fc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java @@ -0,0 +1,78 @@ +/* + * 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; + +import static org.junit.Assert.assertArrayEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** + * Tests for {@link VibratorManagerService}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:VibratorManagerServiceTest + */ +@Presubmit +public class VibratorManagerServiceTest { + + @Rule public MockitoRule rule = MockitoJUnit.rule(); + + @Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock; + + @Before + public void setUp() throws Exception { + } + + private VibratorManagerService createService() { + return new VibratorManagerService(InstrumentationRegistry.getContext(), + new VibratorManagerService.Injector() { + @Override + VibratorManagerService.NativeWrapper getNativeWrapper() { + return mNativeWrapperMock; + } + }); + } + + @Test + public void createService_initializesNativeService() { + createService(); + verify(mNativeWrapperMock).init(); + } + + @Test + public void getVibratorIds_withNullResultFromNative_returnsEmptyArray() { + when(mNativeWrapperMock.getVibratorIds()).thenReturn(null); + assertArrayEquals(new int[0], createService().getVibratorIds()); + } + + @Test + public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() { + when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{ 1, 2 }); + assertArrayEquals(new int[]{ 1, 2 }, createService().getVibratorIds()); + } +}