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

Commit 624eb18e authored by Lais Andrade's avatar Lais Andrade
Browse files

Introduce predefined effect fallback to DeviceAdapter

Move predefined vibration effect fallback logic out of the
VibrationThread into the DeviceAdapter, since now the supported effects
are known beforehand via IVibrator AIDL APIs.

Fix tests to check fallback behavior still works with feature flag,
playing the expected fallback effect right away.

Bug: 423560263
Flag: android.os.vibrator.remove_hidl_support
Test: FrameworksVibratorServicesTests
Change-Id: If31bb772db4453a8bfede2cc09e93785c90ad8d3
parent 6df5bbe7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -371,7 +371,7 @@ public class VibratorInfo implements Parcelable {
     * supported or not.
     */
    @Vibrator.VibrationEffectSupport
    public int isEffectSupported(@VibrationEffect.EffectType int effectId) {
    public int isEffectSupported(int effectId) {
        if (mSupportedEffects == null) {
            return Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
        }
+5 −2
Original line number Diff line number Diff line
@@ -55,7 +55,8 @@ final class DeviceAdapter implements CombinedVibration.VibratorAdapter {

    DeviceAdapter(VibrationSettings settings, SparseArray<VibratorController> vibrators) {
        mSegmentAdapters = Arrays.asList(
                // TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
                // Replace unsupported prebaked effects with fallback
                new PrebakedFallbackAdapter(settings.getFallbackEffects()),
                // Updates primitive delays to hardware supported pauses
                new PrimitiveDelayAdapter(),
                // Convert segments based on device capabilities
@@ -76,7 +77,9 @@ final class DeviceAdapter implements CombinedVibration.VibratorAdapter {
                // Validate Pwle segments base on the vibrators frequency range
                new PwleSegmentsValidator(),
                // Validate primitive segments based on device support
                new PrimitiveSegmentsValidator()
                new PrimitiveSegmentsValidator(),
                // Validate prebaked segments based on device support
                new PrebakedSegmentsValidator()
        );
        mAvailableVibrators = vibrators;
        mAvailableVibratorIds = new int[vibrators.size()];
+4 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import java.util.function.IntFunction;
 */
final class HalVibration extends Vibration {

    // TODO(b/409002423): remove this map once remove_hidl_support flag removed
    public final SparseArray<VibrationEffect> mFallbacks = new SparseArray<>();

    /** A {@link CountDownLatch} to enable waiting for completion. */
@@ -91,6 +92,7 @@ final class HalVibration extends Vibration {
     * Add a fallback {@link VibrationEffect} to be played for each predefined effect id, which
     * might be necessary for replacement in realtime.
     */
    // TODO(b/409002423): remove this method once remove_hidl_support flag removed
    public void fillFallbacks(IntFunction<VibrationEffect> fallbackProvider) {
        fillFallbacksForEffect(mEffectToPlay, fallbackProvider);
    }
@@ -179,6 +181,7 @@ final class HalVibration extends Vibration {
                && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT);
    }

    // TODO(b/409002423): remove this method once remove_hidl_support flag removed
    private void fillFallbacksForEffect(CombinedVibration effect,
            IntFunction<VibrationEffect> fallbackProvider) {
        if (effect instanceof CombinedVibration.Mono) {
@@ -198,6 +201,7 @@ final class HalVibration extends Vibration {
        }
    }

    // TODO(b/409002423): remove this method once remove_hidl_support flag removed
    private void fillFallbacksForEffect(VibrationEffect effect,
            IntFunction<VibrationEffect> fallbackProvider) {
        if (!(effect instanceof VibrationEffect.Composed composed)) {
+18 −14
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.vibrator;
import android.annotation.NonNull;
import android.os.Trace;
import android.os.VibrationEffect;
import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.Slog;
@@ -62,19 +63,21 @@ final class PerformPrebakedVibratorStep extends AbstractComposedVibratorStep {
                        + controller.getVibratorInfo().getId());
            }

            VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
            int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
            long vibratorOnResult = controller.on(prebaked, getVibration().id, stepId);
            handleVibratorOnResult(vibratorOnResult);
            getVibration().stats.reportPerformEffect(vibratorOnResult, prebaked);

            if (!Flags.removeHidlSupport()) {
                VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
                if (vibratorOnResult == 0 && prebaked.shouldFallback()
                        && (fallback instanceof VibrationEffect.Composed)) {
                    if (VibrationThread.DEBUG) {
                        Slog.d(VibrationThread.TAG, "Playing fallback for effect "
                                + VibrationEffect.effectIdToString(prebaked.getEffectId()));
                    }
                AbstractVibratorStep fallbackStep = conductor.nextVibrateStep(startTime, controller,
                    AbstractVibratorStep fallbackStep = conductor.nextVibrateStep(startTime,
                            controller,
                            replaceCurrentSegment((VibrationEffect.Composed) fallback),
                            segmentIndex, mPendingVibratorOffDeadline);
                    List<Step> fallbackResult = fallbackStep.play();
@@ -83,6 +86,7 @@ final class PerformPrebakedVibratorStep extends AbstractComposedVibratorStep {
                    handleVibratorOnResult(fallbackStep.getVibratorOnDuration());
                    return fallbackResult;
                }
            }

            return vibratorOnNextSteps(/* segmentsPlayed= */ 1);
        } finally {
+66 −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 android.os.Vibrator.VIBRATION_EFFECT_SUPPORT_YES;

import android.os.VibrationEffect;
import android.os.VibratorInfo;
import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.SparseArray;

import java.util.List;

/** Adapter that replaces unsupported {@link PrebakedSegment} with fallback. */
final class PrebakedFallbackAdapter implements VibrationSegmentsAdapter {
    private final SparseArray<VibrationEffect> mFallbackEffects;

    PrebakedFallbackAdapter(SparseArray<VibrationEffect> fallbackEffects) {
        mFallbackEffects = fallbackEffects;
    }

    @Override
    public int adaptToVibrator(VibratorInfo info, List<VibrationEffectSegment> segments,
            int repeatIndex) {
        if (!Flags.removeHidlSupport()) {
            return repeatIndex;
        }
        for (int i = 0; i < segments.size(); i++) {
            if (!(segments.get(i) instanceof PrebakedSegment prebaked)) {
                continue;
            }
            int effectId = prebaked.getEffectId();
            if (info.isEffectSupported(effectId) == VIBRATION_EFFECT_SUPPORT_YES
                    || !prebaked.shouldFallback()) {
                continue;
            }
            if (!(mFallbackEffects.get(effectId) instanceof VibrationEffect.Composed fallback)) {
                continue;
            }
            segments.remove(i);
            segments.addAll(i, fallback.getSegments());
            int segmentsAdded = fallback.getSegments().size() - 1;
            if (repeatIndex > i) {
                repeatIndex += segmentsAdded;
            }
            i += segmentsAdded;
        }
        return repeatIndex;
    }
}
Loading