1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.vibrator; 18 19 import android.annotation.NonNull; 20 import android.os.CombinedVibration; 21 import android.os.VibrationEffect; 22 import android.os.VibratorInfo; 23 import android.os.vibrator.VibrationEffectSegment; 24 import android.util.Slog; 25 import android.util.SparseArray; 26 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.List; 30 31 /** 32 * Adapts a {@link CombinedVibration} to a device by transforming each {@link VibrationEffect} to 33 * the available device vibrator capabilities defined by {@link VibratorInfo}. 34 */ 35 final class DeviceAdapter implements CombinedVibration.VibratorAdapter { 36 private static final String TAG = "DeviceAdapter"; 37 38 /** 39 * The VibratorController.getInfo might trigger HAL method calls, so just keep a reference to 40 * the system controllers until the adaptor is triggered by the VibrationThread. 41 */ 42 private final SparseArray<VibratorController> mAvailableVibrators; 43 private final int[] mAvailableVibratorIds; 44 45 /** 46 * The actual adapters that can replace VibrationEffectSegment entries from a list based on the 47 * VibratorInfo. They can be applied in a chain to a mutable list before a new VibrationEffect 48 * instance is created with the final segment list. 49 */ 50 private final List<VibrationSegmentsAdapter> mSegmentAdapters; 51 DeviceAdapter(VibrationSettings settings, SparseArray<VibratorController> vibrators)52 DeviceAdapter(VibrationSettings settings, SparseArray<VibratorController> vibrators) { 53 mSegmentAdapters = Arrays.asList( 54 // TODO(b/167947076): add filter that removes unsupported primitives 55 // TODO(b/167947076): add filter that replaces unsupported prebaked with fallback 56 // Convert segments based on device capabilities 57 new RampToStepAdapter(settings.getRampStepDuration()), 58 new StepToRampAdapter(), 59 // Add extra ramp down segments as needed 60 new RampDownAdapter(settings.getRampDownDuration(), settings.getRampStepDuration()), 61 // Split segments based on their duration and device supported limits 62 new SplitSegmentsAdapter(), 63 // Clip amplitudes and frequencies of final segments based on device bandwidth curve 64 new ClippingAmplitudeAndFrequencyAdapter() 65 ); 66 mAvailableVibrators = vibrators; 67 mAvailableVibratorIds = new int[vibrators.size()]; 68 for (int i = 0; i < vibrators.size(); i++) { 69 mAvailableVibratorIds[i] = vibrators.keyAt(i); 70 } 71 } 72 getAvailableVibrators()73 SparseArray<VibratorController> getAvailableVibrators() { 74 return mAvailableVibrators; 75 } 76 77 @Override getAvailableVibratorIds()78 public int[] getAvailableVibratorIds() { 79 return mAvailableVibratorIds; 80 } 81 82 @NonNull 83 @Override adaptToVibrator(int vibratorId, @NonNull VibrationEffect effect)84 public VibrationEffect adaptToVibrator(int vibratorId, @NonNull VibrationEffect effect) { 85 if (!(effect instanceof VibrationEffect.Composed)) { 86 // Segments adapters can only apply to Composed effects. 87 Slog.wtf(TAG, "Error adapting unsupported vibration effect: " + effect); 88 return effect; 89 } 90 91 VibratorController controller = mAvailableVibrators.get(vibratorId); 92 if (controller == null) { 93 // Effect mapped to nonexistent vibrator, skip adapter. 94 return effect; 95 } 96 97 VibratorInfo info = controller.getVibratorInfo(); 98 VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; 99 List<VibrationEffectSegment> newSegments = new ArrayList<>(composed.getSegments()); 100 int newRepeatIndex = composed.getRepeatIndex(); 101 102 int adapterCount = mSegmentAdapters.size(); 103 for (int i = 0; i < adapterCount; i++) { 104 newRepeatIndex = 105 mSegmentAdapters.get(i).adaptToVibrator(info, newSegments, newRepeatIndex); 106 } 107 108 return new VibrationEffect.Composed(newSegments, newRepeatIndex); 109 } 110 } 111