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