Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2df1ac5b authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Introduce manager implementation for IVibrator" into main

parents e7722f3f 1a6d4442
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -270,7 +270,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler);

        HalListener halListener = new HalListener(this);
        mVibratorManager = injector.createHalVibratorManager();
        mVibratorManager = Flags.removeHidlSupport()
                ? injector.createHalVibratorManager()
                : injector.createNativeHalVibratorManager();
        mVibratorManager.init(halListener);

        int recentDumpSizeLimit = mContext.getResources().getInteger(
@@ -1741,6 +1743,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        }

        HalVibratorManager createHalVibratorManager() {
            return VintfHalVibratorManager.createHalVibratorManager();
        }

        HalVibratorManager createNativeHalVibratorManager() {
            return new NativeHalVibratorManager(new NativeWrapper());
        }

+86 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.vibrator;

import android.hardware.vibrator.IVibrationSession;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibratorCallback;
import android.hardware.vibrator.IVibratorManager;
import android.hardware.vibrator.VibrationSessionConfig;
@@ -44,6 +45,23 @@ import java.util.Optional;

/** Implementations for {@link HalVibratorManager} backed by VINTF objects. */
class VintfHalVibratorManager {
    private static final String TAG = "VintfHalVibratorManager";
    private static final int DEFAULT_VIBRATOR_ID = 0;

    /** Create {@link HalVibratorManager} based on declared services on device. */
    static HalVibratorManager createHalVibratorManager() {
        if (ServiceManager.isDeclared(IVibratorManager.DESCRIPTOR + "/default")) {
            Slog.v(TAG, "Loading default IVibratorManager service.");
            return new DefaultHalVibratorManager(new DefaultVibratorManagerSupplier());
        }
        if (ServiceManager.isDeclared(IVibrator.DESCRIPTOR + "/default")) {
            Slog.v(TAG, "Loading default IVibrator service.");
            return new LegacyHalVibratorManager(new int[] { DEFAULT_VIBRATOR_ID });
        }
        Slog.v(TAG, "No default services declared for IVibratorManager or IVibrator."
                + " Vibrator manager service will proceed without vibrator hardware.");
        return new LegacyHalVibratorManager(new int[0]);
    }

    /** {@link VintfSupplier} for default {@link IVibratorManager} service. */
    static final class DefaultVibratorManagerSupplier extends VintfSupplier<IVibratorManager> {
@@ -343,4 +361,72 @@ class VintfHalVibratorManager {
            }
        }
    }

    /** Legacy implementation for devices without a declared {@link IVibratorManager} service. */
    static final class LegacyHalVibratorManager implements HalVibratorManager {
        private final int[] mVibratorIds;

        LegacyHalVibratorManager(@NonNull int[] vibratorIds) {
            mVibratorIds = vibratorIds;
        }

        @Override
        public void init(@NonNull Callbacks callbacks) {
        }

        @Override
        public void onSystemReady() {
        }

        @Override
        public long getCapabilities() {
            return 0;
        }

        @NonNull
        @Override
        public int[] getVibratorIds() {
            return mVibratorIds;
        }

        @Override
        public boolean prepareSynced(@NonNull int[] vibratorIds) {
            return false;
        }

        @Override
        public boolean triggerSynced(long vibrationId) {
            return false;
        }

        @Override
        public boolean cancelSynced() {
            return false;
        }

        @Override
        public boolean startSession(long sessionId, @NonNull int[] vibratorIds) {
            return false;
        }

        @Override
        public boolean endSession(long sessionId, boolean shouldAbort) {
            return false;
        }

        @Override
        public void dump(IndentingPrintWriter pw) {
            pw.println("Legacy HAL VibratorManager:");
            pw.increaseIndent();
            pw.println("vibratorIds = " + Arrays.toString(mVibratorIds));
            pw.decreaseIndent();
        }

        @Override
        public String toString() {
            return "LegacyHalVibratorManager{"
                    + ", mVibratorIds=" + Arrays.toString(mVibratorIds)
                    + '}';
        }
    }
}
+48 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.server.vibrator.VintfHalVibratorManager.LegacyHalVibratorManager;

import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

/** Test class for {@link LegacyHalVibratorManager}. */
public class LegacyHalVibratorManagerTest {
    @Rule public MockitoRule rule = MockitoJUnit.rule();

    @Mock HalVibratorManager.Callbacks mHalCallbackMock;

    @Test
    public void init_returnsAllFalseExceptVibratorIds() {
        HalVibratorManager manager = new LegacyHalVibratorManager(new int[] { 1, 2 });
        manager.init(mHalCallbackMock);

        assertThat(manager.getVibratorIds()).asList().containsExactly(1, 2).inOrder();
        assertThat(manager.getCapabilities()).isEqualTo(0);
        assertThat(manager.prepareSynced(new int[] { 1 })).isFalse();
        assertThat(manager.triggerSynced(1)).isFalse();
        assertThat(manager.cancelSynced()).isFalse();
        assertThat(manager.startSession(1, new int[] { 2 })).isFalse();
        assertThat(manager.endSession(1, false)).isFalse();
    }
}
+76 −5
Original line number Diff line number Diff line
@@ -226,6 +226,7 @@ public class VibratorManagerServiceTest {
    private VibrationConfig mVibrationConfig;
    private InputManagerGlobal.TestSession mInputManagerGlobalSession;
    private InputManager mInputManager;
    private boolean mUseLegacyHalManager = false;

    @Before
    public void setUp() throws Exception {
@@ -346,6 +347,14 @@ public class VibratorManagerServiceTest {

                    @Override
                    HalVibratorManager createHalVibratorManager() {
                        if (mUseLegacyHalManager) {
                            return mHalVibratorManagerHelper.newLegacyVibratorManager();
                        }
                        return mHalVibratorManagerHelper.newDefaultVibratorManager();
                    }

                    @Override
                    HalVibratorManager createNativeHalVibratorManager() {
                        return mHalVibratorManagerHelper.newNativeHalVibratorManager();
                    }

@@ -411,6 +420,7 @@ public class VibratorManagerServiceTest {
    @Test
    @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
    public void createService_resetsVibratorManager() {
        mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_START_SESSIONS);
        mockVibrators(1, 2);
        createService();

@@ -467,13 +477,27 @@ public class VibratorManagerServiceTest {
    }

    @Test
    public void getVibratorIds_withNullResultFromNative_returnsEmptyArray() {
    @EnableFlags({Flags.FLAG_REMOVE_HIDL_SUPPORT, Flags.FLAG_VENDOR_VIBRATION_EFFECTS})
    public void createService_withLegacyManager_initializesEmptyManager() {
        mUseLegacyHalManager = true;
        mockVibrators(1, 2);

        createService();
        assertThat(mHalVibratorManagerHelper.getConnectCount()).isEqualTo(0);
        assertThat(mHalVibratorManagerHelper.getCancelSyncedCount()).isEqualTo(0);
        assertThat(mHalVibratorManagerHelper.getClearSessionsCount()).isEqualTo(0);
        assertThat(mVibratorHelpers.get(1).isInitialized()).isTrue();
        assertThat(mVibratorHelpers.get(2).isInitialized()).isTrue();
    }

    @Test
    public void getVibratorIds_withNullResultFromHal_returnsEmptyArray() {
        mHalVibratorManagerHelper.setVibratorIds(null);
        assertArrayEquals(new int[0], createSystemReadyService().getVibratorIds());
    }

    @Test
    public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() {
    public void getVibratorIds_withNonEmptyResultFromHal_returnsSameArray() {
        mockVibrators(2, 1);
        assertArrayEquals(new int[]{2, 1}, createSystemReadyService().getVibratorIds());
    }
@@ -1496,11 +1520,11 @@ public class VibratorManagerServiceTest {
    }

    @Test
    public void vibrate_withNativeCallbackTriggered_finishesVibration() throws Exception {
    public void vibrate_withHalCallbackTriggered_finishesVibration() throws Exception {
        mockVibrators(1);
        mVibratorHelpers.get(1).setSupportedEffects(EFFECT_CLICK);
        VibratorManagerService service = createSystemReadyService();
        // The native callback will be dispatched manually in this test.
        // The HAL callback will be dispatched manually in this test.
        mTestLooper.stopAutoDispatchAndIgnoreExceptions();

        vibrate(service, VibrationEffect.get(EFFECT_CLICK), ALARM_ATTRS);
@@ -1512,7 +1536,7 @@ public class VibratorManagerServiceTest {
        mTestLooper.moveTimeForward(50);
        mTestLooper.dispatchAll();

        // VibrationThread needs some time to react to native callbacks and stop the vibrator.
        // VibrationThread needs some time to react to HAL callbacks and stop the vibrator.
        assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
    }

@@ -1597,6 +1621,23 @@ public class VibratorManagerServiceTest {
        assertThat(mHalVibratorManagerHelper.getTriggerSyncedCount()).isEqualTo(0);
    }

    @Test
    @EnableFlags(Flags.FLAG_REMOVE_HIDL_SUPPORT)
    public void vibrate_withLegacyHal_skipPrepareAndTrigger() throws Exception {
        // Capabilities ignored by legacy HAL service, as it's backed by single IVibrator.
        mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON);
        mockVibrators(1, 2);
        mUseLegacyHalManager = true;
        VibratorManagerService service = createSystemReadyService();

        CombinedVibration effect =
                CombinedVibration.createParallel(VibrationEffect.createOneShot(10, 100));
        vibrateAndWaitUntilFinished(service, effect, ALARM_ATTRS);

        assertThat(mHalVibratorManagerHelper.getPrepareSyncedCount()).isEqualTo(0);
        assertThat(mHalVibratorManagerHelper.getTriggerSyncedCount()).isEqualTo(0);
    }

    @Test
    public void vibrate_withMultipleVibratorsPrepareFailed_skipTriggerAndCancel() throws Exception {
        mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_ON);
@@ -3009,6 +3050,36 @@ public class VibratorManagerServiceTest {
                .onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_UNSUPPORTED));
    }

    @Test
    @EnableFlags({Flags.FLAG_REMOVE_HIDL_SUPPORT, Flags.FLAG_VENDOR_VIBRATION_EFFECTS})
    public void startVibrationSession_withLegacyHal_doesNotStart() throws Exception {
        // Capabilities ignored by legacy HAL service, as it's backed by single IVibrator.
        mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
        int vibratorId = 1;
        mockVibrators(vibratorId);
        mUseLegacyHalManager = true;
        VibratorManagerService service = createSystemReadyService();

        IVibrationSessionCallback callback = mockSessionCallbacks();
        VendorVibrationSession session = startSession(service, RINGTONE_ATTRS,
                callback, vibratorId);

        // Make sure all messages are processed before asserting on the session callbacks.
        stopAutoDispatcherAndDispatchAll();

        assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
        assertThat(mHalVibratorManagerHelper.getStartSessionCount()).isEqualTo(0);
        verify(mVibratorFrameworkStatsLoggerMock, never())
                .logVibrationVendorSessionStarted(anyInt());
        verify(mVibratorFrameworkStatsLoggerMock, never())
                .logVibrationVendorSessionVibrations(anyInt(), anyInt());
        verify(mVibratorFrameworkStatsLoggerMock, never())
                .logVibrationVendorSessionInterrupted(anyInt());
        verify(callback, never()).onFinishing();
        verify(callback)
                .onFinished(eq(android.os.vibrator.VendorVibrationSession.STATUS_UNSUPPORTED));
    }

    @Test
    @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
    public void startVibrationSession_thenFinish_returnsSuccessAfterCallback() throws Exception {
+6 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.os.Looper;
import android.os.RemoteException;

import com.android.server.vibrator.VintfHalVibratorManager.DefaultHalVibratorManager;
import com.android.server.vibrator.VintfHalVibratorManager.LegacyHalVibratorManager;

import java.util.Arrays;
import java.util.List;
@@ -75,6 +76,11 @@ public class HalVibratorManagerHelper {
        return new DefaultHalVibratorManager(new FakeVibratorManagerSupplier(fakeManager));
    }

    /** Create new {@link LegacyHalVibratorManager} for testing. */
    public LegacyHalVibratorManager newLegacyVibratorManager() {
        return new LegacyHalVibratorManager(mVibratorIds);
    }

    public void setCapabilities(long capabilities) {
        mCapabilities = capabilities;
    }