• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.annotation.Nullable;
21 import android.content.Context;
22 import android.os.ExternalVibration;
23 import android.os.ExternalVibrationScale;
24 import android.os.IBinder;
25 import android.os.VibrationAttributes;
26 import android.os.vibrator.Flags;
27 import android.util.Slog;
28 
29 import com.android.internal.util.FrameworkStatsLog;
30 
31 /**
32  * A vibration session holding a single {@link ExternalVibration} request.
33  */
34 final class ExternalVibrationSession extends Vibration
35         implements VibrationSession, IBinder.DeathRecipient {
36     private static final String TAG = "ExternalVibrationSession";
37 
38     /** Calls into VibratorManager functionality needed for playing an {@link ExternalVibration}. */
39     interface VibratorManagerHooks {
40 
41         /**
42          * Tells the manager that the external vibration is finished and the vibrators can now be
43          * used for another vibration.
44          */
onExternalVibrationReleased(long vibrationId)45         void onExternalVibrationReleased(long vibrationId);
46     }
47 
48     private final long mSessionId = VibrationSession.nextSessionId();
49     private final ExternalVibration mExternalVibration;
50     private final ExternalVibrationScale mScale = new ExternalVibrationScale();
51     private final VibratorManagerHooks mManagerHooks;
52 
ExternalVibrationSession(ExternalVibration externalVibration, VibratorManagerHooks managerHooks)53     ExternalVibrationSession(ExternalVibration externalVibration,
54             VibratorManagerHooks managerHooks) {
55         super(new CallerInfo(
56                 externalVibration.getVibrationAttributes(), externalVibration.getUid(),
57                 // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
58                 // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
59                 Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
60         mExternalVibration = externalVibration;
61         mManagerHooks = managerHooks;
62     }
63 
getScale()64     public ExternalVibrationScale getScale() {
65         return mScale;
66     }
67 
68     @Override
getSessionId()69     public long getSessionId() {
70         return mSessionId;
71     }
72 
73     @Override
getCreateUptimeMillis()74     public long getCreateUptimeMillis() {
75         return stats.getCreateUptimeMillis();
76     }
77 
78     @Override
getCallerInfo()79     public CallerInfo getCallerInfo() {
80         return callerInfo;
81     }
82 
83     @Override
getCallerToken()84     public IBinder getCallerToken() {
85         return mExternalVibration.getToken();
86     }
87 
88     @Override
getDebugInfo()89     public VibrationSession.DebugInfo getDebugInfo() {
90         return new Vibration.DebugInfoImpl(getStatus(), callerInfo,
91                 FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL, stats,
92                 /* playedEffect= */ null, /* originalEffect= */ null, mScale.scaleLevel,
93                 mScale.adaptiveHapticsScale);
94     }
95 
96     @Override
isRepeating()97     public boolean isRepeating() {
98         // We don't currently know if the external vibration is repeating, so we just use a
99         // heuristic based on the usage. Ideally this would be propagated in the ExternalVibration.
100         int usage = mExternalVibration.getVibrationAttributes().getUsage();
101         return usage == VibrationAttributes.USAGE_RINGTONE
102                 || usage == VibrationAttributes.USAGE_ALARM;
103     }
104 
105     @Override
wasEndRequested()106     public boolean wasEndRequested() {
107         // End request is immediate, so just check if vibration has already ended.
108         return hasEnded();
109     }
110 
111     @Override
linkToDeath()112     public boolean linkToDeath() {
113         mExternalVibration.linkToDeath(this);
114         return true;
115     }
116 
117     @Override
unlinkToDeath()118     public void unlinkToDeath() {
119         mExternalVibration.unlinkToDeath(this);
120     }
121 
122     @Override
binderDied()123     public void binderDied() {
124         Slog.d(TAG, "Binder died, cancelling external vibration...");
125         requestEnd(Status.CANCELLED_BINDER_DIED);
126     }
127 
128     @Override
end(EndInfo endInfo)129     void end(EndInfo endInfo) {
130         super.end(endInfo);
131         if (stats.hasStarted()) {
132             // Notify external client that this vibration should stop sending data to the vibrator.
133             mExternalVibration.mute();
134             // External vibration doesn't have feedback from total time the vibrator was playing
135             // with non-zero amplitude, so we use the duration between start and end times of
136             // the vibration as the time the vibrator was ON, since the haptic channels are
137             // open for this duration and can receive vibration waveform data.
138             stats.reportVibratorOn(stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
139             // Notify the manager that external client has released the vibrator control.
140             mManagerHooks.onExternalVibrationReleased(id);
141         }
142     }
143 
144     @Override
requestEnd(@onNull Status status, @Nullable CallerInfo endedBy, boolean immediate)145     public void requestEnd(@NonNull Status status, @Nullable CallerInfo endedBy,
146             boolean immediate) {
147         end(new EndInfo(status, endedBy));
148     }
149 
150     @Override
notifyVibratorCallback(int vibratorId, long vibrationId, long stepId)151     public void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId) {
152         // ignored, external control does not expect callbacks from the vibrator
153     }
154 
155     @Override
notifySyncedVibratorsCallback(long vibrationId)156     public void notifySyncedVibratorsCallback(long vibrationId) {
157         // ignored, external control does not expect callbacks from the vibrator manager for sync
158     }
159 
160     @Override
notifySessionCallback()161     public void notifySessionCallback() {
162         // ignored, external control does not expect callbacks from the vibrator manager for session
163     }
164 
isHoldingSameVibration(ExternalVibration vib)165     boolean isHoldingSameVibration(ExternalVibration vib) {
166         return mExternalVibration.equals(vib);
167     }
168 
muteScale()169     void muteScale() {
170         mScale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
171         if (Flags.hapticsScaleV2Enabled()) {
172             mScale.scaleFactor = 0;
173         }
174     }
175 
scale(VibrationScaler scaler, int usage)176     void scale(VibrationScaler scaler, int usage) {
177         mScale.scaleLevel = scaler.getScaleLevel(usage);
178         if (Flags.hapticsScaleV2Enabled()) {
179             mScale.scaleFactor = scaler.getScaleFactor(usage);
180         }
181         mScale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage);
182         stats.reportAdaptiveScale(mScale.adaptiveHapticsScale);
183     }
184 
185     @Override
toString()186     public String toString() {
187         return "ExternalVibrationSession{"
188                 + "sessionId=" + mSessionId
189                 + ", vibrationId=" + id
190                 + ", callerInfo=" + callerInfo
191                 + ", externalVibration=" + mExternalVibration
192                 + ", scale=" + mScale
193                 + '}';
194     }
195 }
196