1 /**
2  * Copyright (C) 2022 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 android.telephony.imsmedia;
18 
19 import android.os.Binder;
20 import android.os.IBinder;
21 import android.telephony.CallQuality;
22 import android.telephony.ims.RtpHeaderExtension;
23 import android.telephony.imsmedia.IImsAudioSession;
24 import android.telephony.imsmedia.IImsAudioSessionCallback;
25 
26 import java.util.List;
27 import java.util.concurrent.Executor;
28 
29 /**
30  * Audio session callback APIs
31  *
32  * @hide
33  */
34 public class AudioSessionCallback extends ImsMediaManager.SessionCallback {
35 
36     private final CallbackBinder mCallbackBinder = new CallbackBinder(this);
37 
38     /** @hide */
39     @Override
getBinder()40     public IBinder getBinder() {
41         return mCallbackBinder;
42     }
43 
44     /** @hide */
45     @Override
setExecutor(final Executor executor)46     public void setExecutor(final Executor executor) {
47         mCallbackBinder.setExecutor(executor);
48     }
49 
50     private static class CallbackBinder extends IImsAudioSessionCallback.Stub {
51         private final AudioSessionCallback mLocalCallback;
52         private Executor mExecutor;
53 
CallbackBinder(final AudioSessionCallback localCallback)54         CallbackBinder(final AudioSessionCallback localCallback) {
55             mLocalCallback = localCallback;
56         }
57 
58         @Override
onOpenSessionSuccess(final IImsAudioSession session)59         public void onOpenSessionSuccess(final IImsAudioSession session) {
60             if (mLocalCallback == null) return;
61 
62             final long callingIdentity = Binder.clearCallingIdentity();
63             try {
64                 mExecutor.execute(()
65                         -> mLocalCallback.onOpenSessionSuccess(new ImsAudioSession(session)));
66             } finally {
67                 restoreCallingIdentity(callingIdentity);
68             }
69         }
70 
71         @Override
onOpenSessionFailure(final int error)72         public void onOpenSessionFailure(final int error) {
73             if (mLocalCallback == null) return;
74 
75             final long callingIdentity = Binder.clearCallingIdentity();
76             try {
77                 mExecutor.execute(() -> mLocalCallback.onOpenSessionFailure(error));
78             } finally {
79                 restoreCallingIdentity(callingIdentity);
80             }
81         }
82 
83         @Override
onSessionClosed()84         public void onSessionClosed() {
85             if (mLocalCallback == null) return;
86 
87             final long callingIdentity = Binder.clearCallingIdentity();
88             try {
89                 mExecutor.execute(() -> mLocalCallback.onSessionClosed());
90             } finally {
91                 restoreCallingIdentity(callingIdentity);
92             }
93         }
94 
95         @Override
onModifySessionResponse(final AudioConfig config, final @ImsMediaSession.SessionOperationResult int result)96         public void onModifySessionResponse(final AudioConfig config,
97                 final @ImsMediaSession.SessionOperationResult int result) {
98             if (mLocalCallback == null) return;
99 
100             final long callingIdentity = Binder.clearCallingIdentity();
101             try {
102                 mExecutor.execute(() -> mLocalCallback.onModifySessionResponse(config, result));
103             } finally {
104                 restoreCallingIdentity(callingIdentity);
105             }
106         }
107 
108         @Override
onAddConfigResponse(final AudioConfig config, final @ImsMediaSession.SessionOperationResult int result)109         public void onAddConfigResponse(final AudioConfig config,
110                 final @ImsMediaSession.SessionOperationResult int result) {
111             if (mLocalCallback == null) return;
112 
113             final long callingIdentity = Binder.clearCallingIdentity();
114             try {
115                 mExecutor.execute(() -> mLocalCallback.onAddConfigResponse(config, result));
116             } finally {
117                 restoreCallingIdentity(callingIdentity);
118             }
119         }
120 
121         @Override
onConfirmConfigResponse(final AudioConfig config, final @ImsMediaSession.SessionOperationResult int result)122         public void onConfirmConfigResponse(final AudioConfig config,
123                 final @ImsMediaSession.SessionOperationResult int result) {
124             if (mLocalCallback == null) return;
125 
126             final long callingIdentity = Binder.clearCallingIdentity();
127             try {
128                 mExecutor.execute(() -> mLocalCallback.onConfirmConfigResponse(config, result));
129             } finally {
130                 restoreCallingIdentity(callingIdentity);
131             }
132         }
133 
134         @Override
onFirstMediaPacketReceived(final AudioConfig config)135         public void onFirstMediaPacketReceived(final AudioConfig config) {
136             if (mLocalCallback == null) return;
137 
138             final long callingIdentity = Binder.clearCallingIdentity();
139             try {
140                 mExecutor.execute(() -> mLocalCallback.onFirstMediaPacketReceived(config));
141             } finally {
142                 restoreCallingIdentity(callingIdentity);
143             }
144         }
145 
146         @Override
onHeaderExtensionReceived(final List<RtpHeaderExtension> extensions)147         public void onHeaderExtensionReceived(final List<RtpHeaderExtension> extensions){
148             if (mLocalCallback == null) return;
149 
150             final long callingIdentity = Binder.clearCallingIdentity();
151             try {
152                 mExecutor.execute(() -> mLocalCallback.onHeaderExtensionReceived(extensions));
153             } finally {
154                 restoreCallingIdentity(callingIdentity);
155             }
156         }
157 
158         @Override
notifyMediaQualityStatus(final MediaQualityStatus status)159         public void notifyMediaQualityStatus(final MediaQualityStatus status) {
160             if (mLocalCallback == null) return;
161 
162             final long callingIdentity = Binder.clearCallingIdentity();
163             try {
164                 mExecutor.execute(() -> mLocalCallback.notifyMediaQualityStatus(status));
165             } finally {
166                 restoreCallingIdentity(callingIdentity);
167             }
168         }
169 
170         @Override
onCallQualityChanged(final CallQuality callQuality)171         public void onCallQualityChanged(final CallQuality callQuality) {
172             if (mLocalCallback == null) return;
173 
174             final long callingIdentity = Binder.clearCallingIdentity();
175             try {
176                 mExecutor.execute(() -> mLocalCallback.onCallQualityChanged(callQuality));
177             } finally {
178                 restoreCallingIdentity(callingIdentity);
179             }
180         }
181 
182         @Override
triggerAnbrQuery(final AudioConfig config)183         public void triggerAnbrQuery(final AudioConfig config) {
184             if (mLocalCallback == null) return;
185 
186             final long callingIdentity = Binder.clearCallingIdentity();
187             try {
188                 mExecutor.execute(() -> mLocalCallback.triggerAnbrQuery(config));
189             } finally {
190                 restoreCallingIdentity(callingIdentity);
191             }
192         }
193 
194         @Override
onDtmfReceived(final char dtmfDigit, final int durationMs)195         public void onDtmfReceived(final char dtmfDigit, final int durationMs) {
196             if (mLocalCallback == null) return;
197 
198             final long callingIdentity = Binder.clearCallingIdentity();
199             try {
200                 mExecutor.execute(() -> mLocalCallback.onDtmfReceived(dtmfDigit, durationMs));
201             } finally {
202                 restoreCallingIdentity(callingIdentity);
203             }
204         }
205 
206         @Override
notifyRtpReceptionStats(final RtpReceptionStats stats)207         public void notifyRtpReceptionStats(final RtpReceptionStats stats) {
208             if (mLocalCallback == null) return;
209 
210             final long callingIdentity = Binder.clearCallingIdentity();
211             try {
212                 mExecutor.execute(() -> mLocalCallback.notifyRtpReceptionStats(stats));
213             } finally {
214                 restoreCallingIdentity(callingIdentity);
215             }
216         }
217 
setExecutor(final Executor executor)218         private void setExecutor(final Executor executor) {
219             mExecutor = executor;
220         }
221     }
222 
223     /**
224      * Called when ImsMediaSession#modifySession() API is handled
225      *
226      * @param config The AudioConfig passed in ImsMediaSession#modifySession()
227      * @param result The result of modify session
228      */
onModifySessionResponse(final AudioConfig config, final @ImsMediaSession.SessionOperationResult int result)229     public void onModifySessionResponse(final AudioConfig config,
230             final @ImsMediaSession.SessionOperationResult int result) {
231         // Base Implementation
232     }
233 
234     /**
235      * Called when ImsMediaSession#addConfig() API is handled
236      *
237      * @param config The RTP config passed in
238      *        ImsMediaSession#addConfig()
239      * @param result The result of adding a configuration
240      */
onAddConfigResponse(final AudioConfig config, final @ImsMediaSession.SessionOperationResult int result)241     public void onAddConfigResponse(final AudioConfig config,
242             final @ImsMediaSession.SessionOperationResult int result) {
243         // Base Implementation
244     }
245 
246     /**
247      * Called when ImsMediaSession#confirmConfig() API is handled
248      *
249      * @param config The RTP config passed in
250      *        ImsMediaSession#confirmConfig()
251      * @param result The result of confirm configuration
252      */
onConfirmConfigResponse(final AudioConfig config, final @ImsMediaSession.SessionOperationResult int result)253     public void onConfirmConfigResponse(final AudioConfig config,
254             final @ImsMediaSession.SessionOperationResult int result) {
255         // Base Implementation
256     }
257 
258     /**
259      * Indicates when the first Rtp media packet is received by the UE
260      * during ring back, call hold or early media scenarios. This is
261      * sent only if the packet is received on the active remote
262      * configuration.
263      *
264      * In case of early media scenarios, the implementation shall play
265      * the RTP packets from the most recently added config.
266      *
267      * @param config the remote config where media packet is received
268      */
onFirstMediaPacketReceived(final AudioConfig config)269     public void onFirstMediaPacketReceived(final AudioConfig config) {
270         // Base Implementation
271     }
272 
273     /**
274      * RTP header extension received from the other party
275      *
276      * @param extensions List of received RTP header extensions
277      */
onHeaderExtensionReceived(final List<RtpHeaderExtension> extensions)278     public void onHeaderExtensionReceived(final List<RtpHeaderExtension> extensions){
279         // Base Implementation
280     }
281 
282     /**
283      * Notifies media quality status observed as per thresholds set by
284      * setMediaQualityThreshold() API
285      *
286      * @param status The object of MediaQualityStatus with the rtp and
287      *        the rtcp statistics.
288      */
notifyMediaQualityStatus(final MediaQualityStatus status)289     public void notifyMediaQualityStatus(final MediaQualityStatus status) {
290          // Base Implementation
291     }
292 
293     /**
294      * Notifies when a change to media quality is occurred
295      *
296      * @param callQuality The media quality statistics since last report
297      */
onCallQualityChanged(final CallQuality callQuality)298     public void onCallQualityChanged(final CallQuality callQuality) {
299         // Base Implementation
300     }
301 
302     /**
303      * Notifies when ImsMedia want to query the desired bitrate to NW
304      *
305      * @param config The config containing desired bitrate and direction
306      */
triggerAnbrQuery(final AudioConfig config)307     public void triggerAnbrQuery(final AudioConfig config) {
308         // Base Implementation
309     }
310 
311     /**
312      * Notifies received DTMF digit to play the tone
313      *
314      * @param dtmfDigit single char having one of 12 values: 0-9, *, #
315      * @param durationMs The duration to play the tone in milliseconds unit
316      */
onDtmfReceived(final char dtmfDigit, final int durationMs)317     public void onDtmfReceived(final char dtmfDigit, final int durationMs) {
318         // Base Implementation
319     }
320 
321     /**
322      * Notifies the rtp reception parameters periodically when the requestRtpReceptionStats() is
323      * triggered with the period
324      *
325      * @param stats The rtp reception parameters
326      */
notifyRtpReceptionStats(RtpReceptionStats stats)327     public void notifyRtpReceptionStats(RtpReceptionStats stats) {
328         // Base Implementation
329     }
330 
331 }
332