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

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

Merge "Introduce HalNativeHandler to vibrator service" into main

parents e9e39de9 b7eb4fe1
Loading
Loading
Loading
Loading
+51 −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 android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.vibrator.IVibrationSession;
import android.hardware.vibrator.IVibratorManager;

/** Handles interactions with vibrator HAL services through native. */
interface HalNativeHandler {

    /** Initializes the callback instance for future interactions. */
    void init(@NonNull HalVibratorManager.Callbacks managerCallback,
            @NonNull HalVibrator.Callbacks vibratorCallback);

    /**
     * Call {@link IVibratorManager#triggerSynced} using given vibration id for callbacks from HAL.
     *
     * <p>This should only be called if HAL has {@link IVibratorManager#CAP_TRIGGER_CALLBACK}. The
     * HAL might fail the request otherwise.
     *
     * @return true if successful, false otherwise.
     */
    boolean triggerSyncedWithCallback(long vibrationId);

    /**
     * Call {@link IVibratorManager#startSession} using given session id for callbacks from HAL.
     *
     * <p>This should only be called if HAL has {@link IVibratorManager#CAP_START_SESSIONS}. The
     * HAL might fail the request otherwise.
     *
     * @return the session binder token if successful, null otherwise.
     */
    @Nullable
    IVibrationSession startSessionWithCallback(long sessionId, int[] vibratorIds);
}
+6 −0
Original line number Diff line number Diff line
@@ -27,10 +27,16 @@ import android.os.vibrator.PwlePoint;
import android.os.vibrator.RampSegment;
import android.util.IndentingPrintWriter;

import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.UsedByNative;

/** Handles interactions with a single vibrator HAL. */
interface HalVibrator {

    /** Callbacks from the vibrator HAL. */
    @UsedByNative(
            description = "Called from JNI in jni/VibratorManagerService.cpp",
            kind = KeepItemKind.CLASS_AND_MEMBERS)
    interface Callbacks {
        /** Callback triggered when a vibration step is complete. */
        void onVibrationStepComplete(int vibratorId, long vibrationId, long stepId);
+6 −0
Original line number Diff line number Diff line
@@ -20,10 +20,16 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.IndentingPrintWriter;

import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.UsedByNative;

/** Handles interactions with a vibrator manager HAL. */
interface HalVibratorManager {

    /** Callbacks from the vibrator manager HAL. */
    @UsedByNative(
            description = "Called from JNI in jni/VibratorManagerService.cpp",
            kind = KeepItemKind.CLASS_AND_MEMBERS)
    interface Callbacks {
        /** Callback triggered when synced vibration is complete. */
        void onSyncedVibrationComplete(long vibrationId);
+68 −4
Original line number Diff line number Diff line
@@ -88,6 +88,8 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.vibrator.VibrationSession.CallerInfo;
import com.android.server.vibrator.VibrationSession.DebugInfo;
import com.android.server.vibrator.VibrationSession.Status;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.UsedByNative;

import libcore.util.NativeAllocationRegistry;

@@ -108,6 +110,9 @@ import java.util.function.Function;
import java.util.function.Predicate;

/** System implementation of {@link IVibratorManagerService}. */
@UsedByNative(
        description = "Called from JNI in jni/VibratorManagerService.cpp",
        kind = KeepItemKind.CLASS_AND_MEMBERS)
public class VibratorManagerService extends IVibratorManagerService.Stub {
    private static final String TAG = "VibratorManagerService";
    private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
@@ -233,10 +238,22 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                }
            };

    // TODO(b/409002423): remove the native methods once remove_hidl_support flag removed
    static native long nativeInit(HalVibratorManager.Callbacks callback);
    /** Create native objects and keep weak references to global callbacks. */
    private static native long nativeNewInit(HalVibratorManager.Callbacks managerCallbacks,
            HalVibrator.Callbacks vibratorCallbacks);

    /** Return pointer to function to destroy native objects created by {@link #nativeInit}. */
    private static native long nativeGetFinalizer();

    /** Calls {@link IVibratorManager#triggerSynced} with callback. */
    private static native boolean nativeTriggerSyncedWithCallback(long nativePtr, long vibrationId);

    /** Calls {@link IVibratorManager#startSession} with callback. */
    private static native IBinder nativeStartSessionWithCallback(long nativePtr, long sessionId,
            int[] vibratorIds);

    static native long nativeGetFinalizer();
    // TODO(b/409002423): remove native methods below once remove_hidl_support flag removed
    static native long nativeInit(HalVibratorManager.Callbacks callback);

    static native long nativeGetCapabilities(long nativeServicePtr);

@@ -1741,7 +1758,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        }

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

        HalVibratorManager createNativeHalVibratorManager() {
@@ -2309,6 +2326,53 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        }
    }

    /** {@link HalNativeHandler} based on {@link VibratorManagerService} native methods. */
    private static class NativeHandler implements HalNativeHandler {

        @SuppressWarnings("unused") // Used from native as a weak global reference
        private HalVibratorManager.Callbacks mManagerCallbacks;
        @SuppressWarnings("unused") // Used from native as a weak global reference
        private HalVibrator.Callbacks mVibratorCallbacks;

        /**
         * Keep pointer to native resources allocated by {@link #nativeInit}, to be used on each
         * native method call and cleared when this instance is garbage collected.
         */
        private long mNativePtr;

        @Override
        public void init(@NonNull HalVibratorManager.Callbacks managerCallback,
                @NonNull HalVibrator.Callbacks vibratorCallbacks) {
            mManagerCallbacks = managerCallback; // Used from native as a weak global reference
            mVibratorCallbacks = vibratorCallbacks; // Used from native as a weak global reference
            mNativePtr = nativeNewInit(managerCallback, vibratorCallbacks);
            long finalizerPtr = nativeGetFinalizer();

            if (finalizerPtr != 0) {
                NativeAllocationRegistry registry =
                        NativeAllocationRegistry.createMalloced(
                                VibratorManagerService.class.getClassLoader(), finalizerPtr);
                registry.registerNativeAllocation(this, mNativePtr);
            }
        }

        @Override
        public boolean triggerSyncedWithCallback(long vibrationId) {
            return nativeTriggerSyncedWithCallback(mNativePtr, vibrationId);
        }

        @Nullable
        @Override
        public android.hardware.vibrator.IVibrationSession startSessionWithCallback(
                long sessionId, int[] vibratorIds) {
            IBinder token = nativeStartSessionWithCallback(mNativePtr, sessionId, vibratorIds);
            if (token == null) {
                return null;
            }
            return android.hardware.vibrator.IVibrationSession.Stub.asInterface(token);
        }
    }

    /** Keep records of vibrations played and provide debug information for this service. */
    private static final class VibratorManagerRecords {
        private final VibrationRecords mAggregatedVibrationHistory;
+77 −92
Original line number Diff line number Diff line
@@ -20,9 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
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;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.IBinder;
@@ -37,7 +35,6 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.vibrator.VintfUtils.VintfSupplier;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -50,14 +47,14 @@ class VintfHalVibratorManager {
    private static final int DEFAULT_VIBRATOR_ID = 0;

    /** Create {@link HalVibratorManager} based on declared services on device. */
    static HalVibratorManager createHalVibratorManager() {
    static HalVibratorManager createHalVibratorManager(HalNativeHandler nativeHandler) {
        // TODO(b/422944962): Replace this with Vintf HalVibrator
        IntFunction<HalVibrator> vibratorFactory = VibratorController::new;

        if (ServiceManager.isDeclared(IVibratorManager.DESCRIPTOR + "/default")) {
            Slog.v(TAG, "Loading default IVibratorManager service.");
            return new DefaultHalVibratorManager(
                    new DefaultVibratorManagerSupplier(), vibratorFactory);
            return new DefaultHalVibratorManager(new DefaultVibratorManagerSupplier(),
                    nativeHandler, vibratorFactory);
        }
        if (ServiceManager.isDeclared(IVibrator.DESCRIPTOR + "/default")) {
            Slog.v(TAG, "Loading default IVibrator service.");
@@ -88,10 +85,34 @@ class VintfHalVibratorManager {
    static final class DefaultHalVibratorManager implements HalVibratorManager {
        private static final String TAG = "DefaultHalVibratorManager";

        /** Wrapper for native callbacks to keep track of ongoing vibration sessions. */
        private final class CallbacksWrapper implements Callbacks {
            private final Callbacks mDelegate;

            CallbacksWrapper(Callbacks delegate) {
                mDelegate = delegate;
            }

            @Override
            public void onSyncedVibrationComplete(long vibrationId) {
                mDelegate.onSyncedVibrationComplete(vibrationId);
            }

            @Override
            public void onVibrationSessionComplete(long sessionId) {
                removeSession(sessionId);
                mDelegate.onVibrationSessionComplete(sessionId);
            }
        }

        private final Object mLock = new Object();
        @GuardedBy("mLock")
        private final LongSparseArray<IVibrationSession> mOngoingSessions = new LongSparseArray<>();
        @GuardedBy("mLock")
        private final LongSparseArray<IBinder.DeathRecipient> mSessionDeathRecipients =
                new LongSparseArray<>();
        private final VintfSupplier<IVibratorManager> mHalSupplier;
        private final HalNativeHandler mNativeHandler;
        private final IntFunction<HalVibrator> mVibratorFactory;
        private final SparseArray<HalVibrator> mVibrators = new SparseArray<>();

@@ -101,14 +122,16 @@ class VintfHalVibratorManager {
        private volatile int[] mVibratorIds = new int[0];

        DefaultHalVibratorManager(VintfSupplier<IVibratorManager> supplier,
                IntFunction<HalVibrator> vibratorFactory) {
                HalNativeHandler nativeHandler, IntFunction<HalVibrator> vibratorFactory) {
            mHalSupplier = supplier;
            mNativeHandler = nativeHandler;
            mVibratorFactory = vibratorFactory;
        }

        @Override
        public void init(@NonNull Callbacks cb, @NonNull HalVibrator.Callbacks vibratorCb) {
            mCallbacks = cb;
            mCallbacks = new CallbacksWrapper(cb);
            mNativeHandler.init(mCallbacks, vibratorCb);

            // Load vibrator hardware info. The vibrator ids and manager capabilities are loaded
            // once and assumed unchanged for the lifecycle of this service. Each vibrator can still
@@ -177,12 +200,14 @@ class VintfHalVibratorManager {
                Slog.w(TAG, "No capability to synchronize vibrations, ignoring trigger request.");
                return false;
            }
            final IVibratorCallback callback =
                    hasCapability(IVibratorManager.CAP_TRIGGER_CALLBACK)
                            ? new SyncedVibrationCallback(this, vibrationId)
                            : null;
            if (hasCapability(IVibratorManager.CAP_TRIGGER_CALLBACK)) {
                // Delegate trigger with callback to native, to avoid creating a new callback
                // instance for each call, overloading the GC.
                return mNativeHandler.triggerSyncedWithCallback(vibrationId);
            }
            // Trigger callback not supported, avoid unnecessary JNI round trip.
            return VintfUtils.runNoThrow(mHalSupplier,
                    hal -> hal.triggerSynced(callback),
                    hal -> hal.triggerSynced(null),
                    e -> Slog.e(TAG, "Error triggering synced vibration " + vibrationId, e));
        }

@@ -203,19 +228,33 @@ class VintfHalVibratorManager {
                Slog.w(TAG, "No capability to start sessions, ignoring start session request.");
                return false;
            }
            final IVibratorCallback callback = new SessionCallback(this, sessionId);
            VibrationSessionConfig config = new VibrationSessionConfig();
            Optional<IVibrationSession> session = VintfUtils.getNoThrow(mHalSupplier,
                    hal -> hal.startSession(vibratorIds, config, callback),
                    e -> Slog.e(TAG, "Error starting vibration session " + sessionId
                            + " on vibrators " + Arrays.toString(vibratorIds), e));
            if (session.isPresent()) {
            // Delegate start session with callback to native, to avoid creating a new callback
            // instance for each call, overloading the GC.
            IVibrationSession session = mNativeHandler.startSessionWithCallback(
                    sessionId, vibratorIds);
            if (session == null) {
                Slog.e(TAG, "Error starting session " + sessionId
                        + " for vibrators " + Arrays.toString(vibratorIds));
                return false;
            }
            // Use same callback from death recipient to remove session and notify client.
            IBinder.DeathRecipient deathRecipient =
                    () -> mCallbacks.onVibrationSessionComplete(sessionId);
            try {
                IBinder sessionToken = session.asBinder();
                Binder.allowBlocking(sessionToken); // Required to trigger close/abort methods.
                sessionToken.linkToDeath(deathRecipient, 0);
            } catch (RemoteException e) {
                Slog.e(TAG, "Unable to register DeathRecipient for session " + sessionId, e);
                deathRecipient = null;
            }
            synchronized (mLock) {
                    mOngoingSessions.put(sessionId, session.get());
                mOngoingSessions.put(sessionId, session);
                if (deathRecipient != null) {
                    mSessionDeathRecipients.put(sessionId, deathRecipient);
                }
                return true;
            }
            return false;
            return true;
        }

        @Override
@@ -229,7 +268,8 @@ class VintfHalVibratorManager {
                session = mOngoingSessions.get(sessionId);
            }
            if (session == null) {
                Slog.w(TAG, "No session with id " + sessionId + " to end, ignoring request.");
                Slog.w(TAG, "Error ending session " + sessionId + " with abort=" + shouldAbort
                        + ", session not found");
                return false;
            }
            try {
@@ -289,8 +329,20 @@ class VintfHalVibratorManager {
        }

        private void removeSession(long sessionId) {
            IVibrationSession session;
            IBinder.DeathRecipient deathRecipient;
            synchronized (mLock) {
                session = mOngoingSessions.get(sessionId);
                mOngoingSessions.remove(sessionId);
                deathRecipient = mSessionDeathRecipients.get(sessionId);
                mSessionDeathRecipients.remove(sessionId);
            }
            if (session != null && deathRecipient != null) {
                try {
                    session.asBinder().unlinkToDeath(deathRecipient, 0);
                } catch (Exception e) {
                    Slog.e(TAG, "Unable to remove DeathRecipient for session " + sessionId, e);
                }
            }
        }

@@ -325,73 +377,6 @@ class VintfHalVibratorManager {
            }
            return names.toArray(new String[names.size()]);
        }

        /** Provides {@link IVibratorCallback} without references to local instances. */
        private static final class SyncedVibrationCallback extends IVibratorCallback.Stub {
            private final WeakReference<DefaultHalVibratorManager> mManagerRef;
            private final long mVibrationId;

            SyncedVibrationCallback(DefaultHalVibratorManager manager, long vibrationId) {
                mManagerRef = new WeakReference<>(manager);
                mVibrationId = vibrationId;
            }

            @Override
            public void onComplete() {
                DefaultHalVibratorManager manager = mManagerRef.get();
                if (manager == null) {
                    return;
                }
                Callbacks callbacks = manager.mCallbacks;
                if (callbacks != null) {
                    callbacks.onSyncedVibrationComplete(mVibrationId);
                }
            }

            @Override
            public int getInterfaceVersion() {
                return IVibratorCallback.VERSION;
            }

            @Override
            public String getInterfaceHash() {
                return IVibratorCallback.HASH;
            }
        }

        /** Provides {@link IVibratorCallback} without references to local instances. */
        private static final class SessionCallback extends IVibratorCallback.Stub {
            private final WeakReference<DefaultHalVibratorManager> mManagerRef;
            private final long mSessionId;

            SessionCallback(DefaultHalVibratorManager manager, long sessionId) {
                mManagerRef = new WeakReference<>(manager);
                mSessionId = sessionId;
            }

            @Override
            public void onComplete() {
                DefaultHalVibratorManager manager = mManagerRef.get();
                if (manager == null) {
                    return;
                }
                manager.removeSession(mSessionId);
                Callbacks callbacks = manager.mCallbacks;
                if (callbacks != null) {
                    callbacks.onVibrationSessionComplete(mSessionId);
                }
            }

            @Override
            public int getInterfaceVersion() {
                return IVibratorCallback.VERSION;
            }

            @Override
            public String getInterfaceHash() {
                return IVibratorCallback.HASH;
            }
        }
    }

    /** Legacy implementation for devices without a declared {@link IVibratorManager} service. */
Loading