1 /*
2  * Copyright (C) 2020 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.internal.telephony.d2d;
18 
19 import android.annotation.NonNull;
20 import android.net.Uri;
21 import android.os.Handler;
22 import android.telecom.Log;
23 import android.telephony.ims.ImsCallProfile;
24 import android.telephony.ims.RtpHeaderExtension;
25 import android.telephony.ims.RtpHeaderExtensionType;
26 import android.util.ArraySet;
27 
28 import com.android.internal.telephony.BiMap;
29 
30 import java.util.Objects;
31 import java.util.Optional;
32 import java.util.Set;
33 import java.util.stream.Collectors;
34 
35 /**
36  * Implements a transport protocol which makes use of RTP header extensions to communicate between
37  * two devices.
38  * <p>
39  * Two types of device to device communications are supported,
40  * {@link #DEVICE_STATE_RTP_HEADER_EXTENSION} messages which communicate attributes such as the
41  * device service level and battery, and {@link #CALL_STATE_RTP_HEADER_EXTENSION} which communicates
42  * information pertaining to an ongoing call.
43  * <p>
44  * When the ImsService negotiates the media for a call using SDP it will also potentially negotiate
45  * the supported RTP header extension types which both parties of the call agree upon.  When a call
46  * first begins if the accepted RTP header extension URIs matches the two well defined URIs we
47  * define here, then we can assume the other party supports device to device communication using
48  * RTP header extensions.
49  * <p>
50  * It is, however, possible that network signalling causes a failure to successfully negotiate the
51  * RTP header extension types which both sides agree upon.  In this case we will wait for a short
52  * period of time to see if we receive an RTP header extension which corresponds to one of the
53  * local identifiers we would have expected to negotiation.  If such an RTP header extension is
54  * received in a timely manner, we can assume that the other party is capable of using RTP header
55  * extensions to communicate even though the RTP extension type URIs did not successfully negotiate.
56  * If we do not receive a valid extension in this case, we will consider negotiation for this
57  * transport to have failed.
58  */
59 public class RtpTransport implements TransportProtocol, RtpAdapter.Callback {
60     /**
61      * {@link Uri} identifier for an RTP header extension used to communicate device state during
62      * calls.
63      */
64     public static Uri DEVICE_STATE_RTP_HEADER_EXTENSION =
65             Uri.parse("http://develop.android.com/122020/d2dcomm#device-state");
66 
67     /**
68      * {@link Uri} identifier for an RTP header extension used to communication call state during
69      * calls.
70      */
71     public static Uri CALL_STATE_RTP_HEADER_EXTENSION =
72             Uri.parse("http://develop.android.com/122020/d2dcomm#call-state");
73 
74     /**
75      * Default local identifier for device state RTP header extensions.
76      */
77     public static int DEVICE_STATE_LOCAL_IDENTIFIER = 10;
78 
79     /**
80      * Default local identifier for call state RTP header extensions.
81      */
82     public static int CALL_STATE_LOCAL_IDENTIFIER = 11;
83 
84     /**
85      * {@link RtpHeaderExtensionType} for device state communication.
86      */
87     public static RtpHeaderExtensionType DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE =
88             new RtpHeaderExtensionType(DEVICE_STATE_LOCAL_IDENTIFIER,
89                     DEVICE_STATE_RTP_HEADER_EXTENSION);
90 
91     /**
92      * {@link RtpHeaderExtensionType} for call state communication.
93      */
94     public static RtpHeaderExtensionType CALL_STATE_RTP_HEADER_EXTENSION_TYPE =
95             new RtpHeaderExtensionType(CALL_STATE_LOCAL_IDENTIFIER,
96                     CALL_STATE_RTP_HEADER_EXTENSION);
97 
98 
99     /**
100      * See {@link #generateRtpHeaderExtension(Communicator.Message)} for more information; indicates
101      * the offset of the parameter value in the RTP header extension payload.
102      */
103     public static final int RTP_PARAMETER_BIT_OFFSET = 4;
104 
105     /**
106      * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE} msg.
107      */
108     public static final byte RTP_CALL_STATE_MSG_RADIO_ACCESS_TYPE_BITS = 0b0001;
109 
110     /**
111      * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_AUDIO_CODEC} msg.
112      */
113     public static final byte RTP_CALL_STATE_MSG_CODEC_BITS = 0b0010;
114 
115     /**
116      * RTP header extension bits set for {@link Communicator#MESSAGE_DEVICE_BATTERY_STATE} msg.
117      */
118     public static final byte RTP_DEVICE_STATE_MSG_BATTERY_BITS = 0b0001;
119 
120     /**
121      * RTP header extension bits set for {@link Communicator#MESSAGE_DEVICE_NETWORK_COVERAGE} msg.
122      */
123     public static final byte RTP_DEVICE_STATE_MSG_NETWORK_COVERAGE_BITS = 0b0010;
124 
125     /**
126      * Provides a mapping between the various {@code Communicator#MESSAGE_*} values and their bit
127      * representation in an RTP header extension payload.  Used to translate outgoing message types
128      * to an RTP payload and to interpret incoming RTP payloads and translate them back into message
129      * types.
130      */
131     private static final BiMap<Integer, Byte> CALL_STATE_MSG_TYPE_TO_RTP_BITS = new BiMap<>();
132     private static final BiMap<Integer, Byte> DEVICE_STATE_MSG_TYPE_TO_RTP_BITS = new BiMap<>();
133     static {
CALL_STATE_MSG_TYPE_TO_RTP_BITS.put( Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE, RTP_CALL_STATE_MSG_RADIO_ACCESS_TYPE_BITS)134         CALL_STATE_MSG_TYPE_TO_RTP_BITS.put(
135                 Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
136                 RTP_CALL_STATE_MSG_RADIO_ACCESS_TYPE_BITS);
CALL_STATE_MSG_TYPE_TO_RTP_BITS.put( Communicator.MESSAGE_CALL_AUDIO_CODEC, RTP_CALL_STATE_MSG_CODEC_BITS)137         CALL_STATE_MSG_TYPE_TO_RTP_BITS.put(
138                 Communicator.MESSAGE_CALL_AUDIO_CODEC,
139                 RTP_CALL_STATE_MSG_CODEC_BITS);
DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.put( Communicator.MESSAGE_DEVICE_BATTERY_STATE, RTP_DEVICE_STATE_MSG_BATTERY_BITS)140         DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.put(
141                 Communicator.MESSAGE_DEVICE_BATTERY_STATE,
142                 RTP_DEVICE_STATE_MSG_BATTERY_BITS);
DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.put( Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE, RTP_DEVICE_STATE_MSG_NETWORK_COVERAGE_BITS)143         DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.put(
144                 Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
145                 RTP_DEVICE_STATE_MSG_NETWORK_COVERAGE_BITS);
146     }
147 
148     /**
149      * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE} data
150      * payload.  Corresponds to {@link Communicator#RADIO_ACCESS_TYPE_LTE}.
151      */
152     public static final byte RTP_RAT_VALUE_LTE_BITS  = 0b0001 << RTP_PARAMETER_BIT_OFFSET;
153 
154     /**
155      * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE} data
156      * payload.  Corresponds to {@link Communicator#RADIO_ACCESS_TYPE_IWLAN}.
157      */
158     public static final byte RTP_RAT_VALUE_WLAN_BITS = 0b0010 << RTP_PARAMETER_BIT_OFFSET;
159 
160     /**
161      * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE} data
162      * payload.  Corresponds to {@link Communicator#RADIO_ACCESS_TYPE_NR}.
163      */
164     public static final byte RTP_RAT_VALUE_NR_BITS   = 0b0011 << RTP_PARAMETER_BIT_OFFSET;
165 
166     /**
167      * Provides a mapping between the various {@code Communicator#RADIO_ACCESS_TYPE_*} values and
168      * their bit representation in an RTP header extension payload.
169      */
170     private static final BiMap<Integer, Byte> RAT_VALUE_TO_RTP_BITS = new BiMap<>();
171     static {
RAT_VALUE_TO_RTP_BITS.put( Communicator.RADIO_ACCESS_TYPE_IWLAN, RTP_RAT_VALUE_WLAN_BITS)172         RAT_VALUE_TO_RTP_BITS.put(
173                 Communicator.RADIO_ACCESS_TYPE_IWLAN, RTP_RAT_VALUE_WLAN_BITS);
RAT_VALUE_TO_RTP_BITS.put( Communicator.RADIO_ACCESS_TYPE_LTE, RTP_RAT_VALUE_LTE_BITS)174         RAT_VALUE_TO_RTP_BITS.put(
175                 Communicator.RADIO_ACCESS_TYPE_LTE, RTP_RAT_VALUE_LTE_BITS);
RAT_VALUE_TO_RTP_BITS.put( Communicator.RADIO_ACCESS_TYPE_NR, RTP_RAT_VALUE_NR_BITS)176         RAT_VALUE_TO_RTP_BITS.put(
177                 Communicator.RADIO_ACCESS_TYPE_NR, RTP_RAT_VALUE_NR_BITS);
178     }
179 
180     /**
181      * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_AUDIO_CODEC} data
182      * payload.  Corresponds to {@link Communicator#AUDIO_CODEC_EVS}.
183      */
184     public static final byte RTP_CODEC_VALUE_EVS_BITS    = 0b0001 << RTP_PARAMETER_BIT_OFFSET;
185     public static final byte RTP_CODEC_VALUE_AMR_WB_BITS = 0b0010 << RTP_PARAMETER_BIT_OFFSET;
186     public static final byte RTP_CODEC_VALUE_AMR_NB_BITS = 0b0011 << RTP_PARAMETER_BIT_OFFSET;
187 
188     /**
189      * Provides a mapping between the various {@code Communicator#AUDIO_CODEC_*} values and
190      * their bit representation in an RTP header extension payload.
191      */
192     private static final BiMap<Integer, Byte> CODEC_VALUE_TO_RTP_BITS = new BiMap<>();
193     static {
CODEC_VALUE_TO_RTP_BITS.put( Communicator.AUDIO_CODEC_EVS, RTP_CODEC_VALUE_EVS_BITS)194         CODEC_VALUE_TO_RTP_BITS.put(
195                 Communicator.AUDIO_CODEC_EVS, RTP_CODEC_VALUE_EVS_BITS);
CODEC_VALUE_TO_RTP_BITS.put( Communicator.AUDIO_CODEC_AMR_WB, RTP_CODEC_VALUE_AMR_WB_BITS)196         CODEC_VALUE_TO_RTP_BITS.put(
197                 Communicator.AUDIO_CODEC_AMR_WB, RTP_CODEC_VALUE_AMR_WB_BITS);
CODEC_VALUE_TO_RTP_BITS.put( Communicator.AUDIO_CODEC_AMR_NB, RTP_CODEC_VALUE_AMR_NB_BITS)198         CODEC_VALUE_TO_RTP_BITS.put(
199                 Communicator.AUDIO_CODEC_AMR_NB, RTP_CODEC_VALUE_AMR_NB_BITS);
200     }
201 
202     public static final byte RTP_BATTERY_STATE_LOW_BITS      = 0b0000 << RTP_PARAMETER_BIT_OFFSET;
203     public static final byte RTP_BATTERY_STATE_GOOD_BITS     = 0b0001 << RTP_PARAMETER_BIT_OFFSET;
204     public static final byte RTP_BATTERY_STATE_CHARGING_BITS = 0b0011 << RTP_PARAMETER_BIT_OFFSET;
205 
206     /**
207      * Provides a mapping between the various {@code Communicator#BATTERY_STATE_*} values and
208      * their bit representation in an RTP header extension payload.
209      */
210     private static final BiMap<Integer, Byte> BATTERY_STATE_VALUE_TO_RTP_BITS = new BiMap<>();
211     static {
BATTERY_STATE_VALUE_TO_RTP_BITS.put( Communicator.BATTERY_STATE_LOW, RTP_BATTERY_STATE_LOW_BITS)212         BATTERY_STATE_VALUE_TO_RTP_BITS.put(
213                 Communicator.BATTERY_STATE_LOW, RTP_BATTERY_STATE_LOW_BITS);
BATTERY_STATE_VALUE_TO_RTP_BITS.put( Communicator.BATTERY_STATE_GOOD, RTP_BATTERY_STATE_GOOD_BITS)214         BATTERY_STATE_VALUE_TO_RTP_BITS.put(
215                 Communicator.BATTERY_STATE_GOOD, RTP_BATTERY_STATE_GOOD_BITS);
BATTERY_STATE_VALUE_TO_RTP_BITS.put( Communicator.BATTERY_STATE_CHARGING, RTP_BATTERY_STATE_CHARGING_BITS)216         BATTERY_STATE_VALUE_TO_RTP_BITS.put(
217                 Communicator.BATTERY_STATE_CHARGING, RTP_BATTERY_STATE_CHARGING_BITS);
218     }
219 
220     public static final byte RTP_NETWORK_COVERAGE_POOR_BITS = 0b0000 << RTP_PARAMETER_BIT_OFFSET;
221     public static final byte RTP_NETWORK_COVERAGE_GOOD_BITS = 0b0001 << RTP_PARAMETER_BIT_OFFSET;
222 
223     /**
224      * Provides a mapping between the various {@code Communicator#COVERAGE_*} values and
225      * their bit representation in an RTP header extension payload.
226      */
227     private static final BiMap<Integer, Byte> NETWORK_COVERAGE_VALUE_TO_RTP_BITS = new BiMap<>();
228     static {
NETWORK_COVERAGE_VALUE_TO_RTP_BITS.put( Communicator.COVERAGE_POOR, RTP_NETWORK_COVERAGE_POOR_BITS)229         NETWORK_COVERAGE_VALUE_TO_RTP_BITS.put(
230                 Communicator.COVERAGE_POOR, RTP_NETWORK_COVERAGE_POOR_BITS);
NETWORK_COVERAGE_VALUE_TO_RTP_BITS.put( Communicator.COVERAGE_GOOD, RTP_NETWORK_COVERAGE_GOOD_BITS)231         NETWORK_COVERAGE_VALUE_TO_RTP_BITS.put(
232                 Communicator.COVERAGE_GOOD, RTP_NETWORK_COVERAGE_GOOD_BITS);
233     }
234 
235     /**
236      * Indicates that the transport is not yet ready for use and negotiation has not yet completed.
237      */
238     public static final int PROTOCOL_STATUS_NEGOTIATION_REQUIRED = 1;
239 
240     /**
241      * Indicates that the agreed upon RTP header extension types (see
242      * {@link RtpAdapter#getAcceptedRtpHeaderExtensions()}) did not specify both the
243      * {@link #DEVICE_STATE_RTP_HEADER_EXTENSION} and {@link #CALL_STATE_RTP_HEADER_EXTENSION
244      * RTP header extension types.  We are not going to wait a short time to see if we receive an
245      * incoming RTP packet with one of the local identifiers we'd normally expect associated with
246      * these header extension {@link Uri}s.  If we do receive a valid RTP header extension we will
247      * move to the {@link #PROTOCOL_STATUS_NEGOTIATION_COMPLETE} status.  If we do not receive a
248      * valid RTP header extension we move to {@link #PROTOCOL_STATUS_NEGOTIATION_FAILED} and report
249      * the failure via {@link Callback#onNegotiationFailed(TransportProtocol) }.
250      */
251     public static final int PROTOCOL_STATUS_NEGOTIATION_WAITING_ON_PACKET = 2;
252 
253     /**
254      * Indicates we either agreed upon the required header extensions, or received a valid packet
255      * despite not having agreed upon required header extensions.  The transport is ready to use at
256      * this point.
257      */
258     public static final int PROTOCOL_STATUS_NEGOTIATION_COMPLETE = 3;
259 
260     /**
261      * Indicates protocol negotiation failed.
262      */
263     public static final int PROTOCOL_STATUS_NEGOTIATION_FAILED = 4;
264 
265     /**
266      * Callback which is used to report back to the {@link Communicator} about the status of
267      * protocol negotiation and incoming messages.
268      */
269     private TransportProtocol.Callback mCallback;
270 
271     /**
272      * Adapter which abstracts out the details of sending/receiving RTP header extensions.
273      */
274     private final RtpAdapter mRtpAdapter;
275 
276     /**
277      * Configuration adapter for timeouts related to protocol setup.
278      */
279     private final Timeouts.Adapter mTimeoutsAdapter;
280 
281     /**
282      * Handler for posting future events.
283      */
284     private final Handler mHandler;
285 
286     /**
287      * {@code true} if the carrier supports negotiating the RTP header extensions using SDP.
288      * If {@code true}, we can expected the
289      * {@link ImsCallProfile#getAcceptedRtpHeaderExtensionTypes()} to contain the SDP negotiated RTP
290      * header extensions.  If {@code false} we will assume the protocol is negotiated only after
291      * receiving an RTP header extension of the expected type.
292      */
293     private final boolean mIsSdpNegotiationSupported;
294 
295     /**
296      * Protocol status.
297      */
298     private int mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_REQUIRED;
299 
300     /**
301      * The supported RTP header extension types.
302      */
303     private ArraySet<RtpHeaderExtensionType> mSupportedRtpHeaderExtensionTypes = new ArraySet<>();
304 
305     /**
306      * Initializes the {@link RtpTransport}.
307      * @param rtpAdapter Adapter for abstract send/receive of RTP header extension data.
308      * @param timeoutsAdapter Timeouts adapter for dealing with time based configurations.
309      * @param handler Handler for posting future events.
310      * @param isSdpNegotiationSupported Indicates whether SDP negotiation
311      */
RtpTransport(RtpAdapter rtpAdapter, Timeouts.Adapter timeoutsAdapter, Handler handler, boolean isSdpNegotiationSupported)312     public RtpTransport(RtpAdapter rtpAdapter, Timeouts.Adapter timeoutsAdapter, Handler handler,
313             boolean isSdpNegotiationSupported) {
314         mRtpAdapter = rtpAdapter;
315         mTimeoutsAdapter = timeoutsAdapter;
316         mHandler = handler;
317         mIsSdpNegotiationSupported = isSdpNegotiationSupported;
318     }
319 
320     /**
321      * Sets the Callback for this transport.  Used to report back received messages.
322      * @param callback The callback.
323      */
324     @Override
setCallback(Callback callback)325     public void setCallback(Callback callback) {
326         mCallback = callback;
327     }
328 
329     /**
330      * Begin transport protocol negotiation at the request of the framework.
331      *
332      * If the {@link #DEVICE_STATE_RTP_HEADER_EXTENSION} and
333      * {@link #CALL_STATE_RTP_HEADER_EXTENSION} extension {@link Uri}s are part of the accepted
334      * extension types, we consider the negotiation successful.
335      *
336      * TODO: If they are not in the accepted extensions, we will wait a short period of time to
337      * receive a valid RTP header extension we recognize.
338      */
339     @Override
startNegotiation()340     public void startNegotiation() {
341         Set<RtpHeaderExtensionType> acceptedExtensions =
342                 mRtpAdapter.getAcceptedRtpHeaderExtensions();
343         mSupportedRtpHeaderExtensionTypes.addAll(acceptedExtensions);
344 
345         Log.i(this, "startNegotiation: supportedExtensions=%s", mSupportedRtpHeaderExtensionTypes
346                 .stream()
347                 .map(e -> e.toString())
348                 .collect(Collectors.joining(",")));
349 
350         if (mIsSdpNegotiationSupported) {
351             boolean areExtensionsAvailable = acceptedExtensions.stream().anyMatch(
352                     e -> e.getUri().equals(DEVICE_STATE_RTP_HEADER_EXTENSION))
353                     && acceptedExtensions.stream().anyMatch(
354                     e -> e.getUri().equals(CALL_STATE_RTP_HEADER_EXTENSION));
355 
356             if (areExtensionsAvailable) {
357                 // Headers were negotiated during SDP, so we can assume negotiation is complete and
358                 // signal to the communicator that we can use this transport.
359                 mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_COMPLETE;
360                 Log.i(this, "startNegotiation: header extensions available, negotiation success");
361                 notifyProtocolReady();
362             } else {
363                 // Headers failed to be negotiated during SDP.   Assume protocol is not available.
364                 // TODO: Implement fallback logic where we still try an SDP probe/response.
365                 mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_FAILED;
366                 Log.i(this,
367                         "startNegotiation: header extensions not available; negotiation failed");
368                 notifyProtocolUnavailable();
369             }
370         } else {
371             Log.i(this, "startNegotiation: SDP negotiation not supported; negotiation complete");
372             // TODO: This is temporary; we will need to implement a probe/response in this scenario
373             // if SDP is not supported.  For now we will just assume the protocol is ready.
374             notifyProtocolReady();
375         }
376     }
377 
378     /**
379      * Handles sending messages using the RTP transport.  Generates valid {@link RtpHeaderExtension}
380      * instances for each message to send.
381      * @param messages The messages to send.
382      */
383     @Override
sendMessages(Set<Communicator.Message> messages)384     public void sendMessages(Set<Communicator.Message> messages) {
385         Set<RtpHeaderExtension> toSend = messages.stream().map(m -> generateRtpHeaderExtension(m))
386                 .collect(Collectors.toSet());
387         Log.i(this, "sendMessages: sending=%s", messages);
388         mRtpAdapter.sendRtpHeaderExtensions(toSend);
389     }
390 
391     /**
392      * Forces the protocol status to negotiated; for test purposes.
393      */
394     @Override
forceNegotiated()395     public void forceNegotiated() {
396         // If there is no supported RTP header extensions we need to fake it.
397         if (mSupportedRtpHeaderExtensionTypes == null
398                 || mSupportedRtpHeaderExtensionTypes.isEmpty()) {
399             mSupportedRtpHeaderExtensionTypes.add(DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE);
400             mSupportedRtpHeaderExtensionTypes.add(CALL_STATE_RTP_HEADER_EXTENSION_TYPE);
401         }
402         mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_COMPLETE;
403     }
404 
405     /**
406      * Forces the protocol status to un-negotiated; for test purposes.
407      */
408     @Override
forceNotNegotiated()409     public void forceNotNegotiated() {
410         mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_REQUIRED;
411     }
412 
413     /**
414      * Called by the platform when RTP header extensions are received and need to be translated to
415      * concrete messages.
416      * Results in a callback via
417      * {@link com.android.internal.telephony.d2d.TransportProtocol.Callback#onMessagesReceived(Set)}
418      * to notify when incoming messages are received.
419      * @param extensions The received RTP header extensions.
420      */
421     @Override
onRtpHeaderExtensionsReceived(@onNull Set<RtpHeaderExtension> extensions)422     public void onRtpHeaderExtensionsReceived(@NonNull Set<RtpHeaderExtension> extensions) {
423         Set<Communicator.Message> messages = extensions.stream().map(e -> extractMessage(e))
424                 .filter(Objects::nonNull)
425                 .collect(Collectors.toSet());
426         if (messages.size() == 0) {
427             return;
428         }
429         mCallback.onMessagesReceived(messages);
430     }
431 
432     /**
433      * Given a {@link RtpHeaderExtension} received from the network, parse out an found message.
434      * @param extension The RTP header extension to parse.
435      * @return The message, or {@code null} if no valid message found.
436      */
extractMessage(@onNull RtpHeaderExtension extension)437     private Communicator.Message extractMessage(@NonNull RtpHeaderExtension extension) {
438         // First determine the URI to figure out the general classification of the message.
439         Optional<Uri> foundUri = mSupportedRtpHeaderExtensionTypes.stream()
440                 .filter(et -> et.getLocalIdentifier() == extension.getLocalIdentifier())
441                 .map(et -> et.getUri())
442                 .findFirst();
443         if (!foundUri.isPresent()) {
444             Log.w(this, "extractMessage: localIdentifier=%d not supported.",
445                     extension.getLocalIdentifier());
446             return null;
447         }
448 
449         if (extension.getExtensionData() == null || extension.getExtensionData().length != 1) {
450             Log.w(this, "extractMessage: localIdentifier=%d message with invalid data length.",
451                     extension.getLocalIdentifier());
452             return null;
453         }
454 
455         Uri uri = foundUri.get();
456 
457         // Extract the bits which are the message type.
458         byte messageTypeBits = (byte) (extension.getExtensionData()[0] & 0b1111);
459         byte messageValueBits = (byte) (extension.getExtensionData()[0]
460                 & (0b1111 << RTP_PARAMETER_BIT_OFFSET));
461 
462         int messageType;
463         int messageValue;
464         if (DEVICE_STATE_RTP_HEADER_EXTENSION.equals(uri)) {
465             Integer type = DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.getKey(messageTypeBits);
466             if (type == null) {
467                 Log.w(this, "extractMessage: localIdentifier=%d message with invalid type %s.",
468                         extension.getLocalIdentifier(), Integer.toBinaryString(messageTypeBits));
469                 return null;
470             }
471             messageType = type;
472             switch (messageType) {
473                 case Communicator.MESSAGE_DEVICE_BATTERY_STATE:
474                     Integer val = BATTERY_STATE_VALUE_TO_RTP_BITS.getKey(messageValueBits);
475                     if (val == null) {
476                         Log.w(this, "extractMessage: localIdentifier=%d, battery state msg with "
477                                         + "invalid value=%s",
478                                 extension.getLocalIdentifier(),
479                                 Integer.toBinaryString(messageValueBits));
480                         return null;
481                     }
482                     messageValue = val;
483                     break;
484                 case Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE:
485                     Integer val2 = NETWORK_COVERAGE_VALUE_TO_RTP_BITS.getKey(messageValueBits);
486                     if (val2 == null) {
487                         Log.w(this, "extractMessage: localIdentifier=%d, network coverage msg with "
488                                         + "invalid value=%s",
489                                 extension.getLocalIdentifier(),
490                                 Integer.toBinaryString(messageValueBits));
491                         return null;
492                     }
493                     messageValue = val2;
494                     break;
495                 default:
496                     Log.w(this, "messageType=%s, value=%s; invalid value",
497                             Integer.toBinaryString(messageTypeBits),
498                             Integer.toBinaryString(messageValueBits));
499                     return null;
500             }
501         } else if (CALL_STATE_RTP_HEADER_EXTENSION.equals(uri)) {
502             Integer typeValue = CALL_STATE_MSG_TYPE_TO_RTP_BITS.getKey(messageTypeBits);
503             if (typeValue == null) {
504                 Log.w(this, "extractMessage: localIdentifier=%d, network coverage msg with "
505                                 + "invalid type=%s",
506                         extension.getLocalIdentifier(),
507                         Integer.toBinaryString(messageTypeBits));
508                 return null;
509             }
510             messageType = typeValue;
511             switch (messageType) {
512                 case Communicator.MESSAGE_CALL_AUDIO_CODEC:
513                     Integer val = CODEC_VALUE_TO_RTP_BITS.getKey(messageValueBits);
514                     if (val == null) {
515                         Log.w(this, "extractMessage: localIdentifier=%d, audio codec msg with "
516                                         + "invalid value=%s",
517                                 extension.getLocalIdentifier(),
518                                 Integer.toBinaryString(messageValueBits));
519                         return null;
520                     }
521                     messageValue = val;
522                     break;
523                 case Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE:
524                     Integer val2 = RAT_VALUE_TO_RTP_BITS.getKey(messageValueBits);
525                     if (val2 == null) {
526                         Log.w(this, "extractMessage: localIdentifier=%d, rat type msg with "
527                                         + "invalid value=%s",
528                                 extension.getLocalIdentifier(),
529                                 Integer.toBinaryString(messageValueBits));
530                         return null;
531                     }
532                     messageValue = val2;
533                     break;
534                 default:
535                     Log.w(this, "messageType=%s, value=%s; invalid value",
536                             Integer.toBinaryString(messageTypeBits),
537                             Integer.toBinaryString(messageValueBits));
538                     return null;
539             }
540         } else {
541             Log.w(this, "invalid uri=%s", uri);
542             return null;
543         }
544         Log.i(this, "extractMessage: messageType=%s, value=%s --> message=%d, value=%d",
545                 Integer.toBinaryString(messageTypeBits), Integer.toBinaryString(messageValueBits),
546                 messageType, messageValue);
547         return new Communicator.Message(messageType, messageValue);
548     }
549 
550 
551     /**
552      * Generates an {@link RtpHeaderExtension} based on the specified message.
553      * Per RFC8285, RTP header extensions have the format:
554      * (bit)
555      *  ___byte__ __byte__
556      *  1234 5678 12345678
557      * +----+----+--------+
558      * | ID | len|  data  |
559      * +----+----+--------+
560      * Where ID is the {@link RtpHeaderExtensionType#getLocalIdentifier()}, len indicates the
561      * number of data bytes being sent ({@link RtpHeaderExtension#getExtensionData()}.size()),
562      * and data is the payload of the RTP header extension.
563      * The {@link RtpHeaderExtension} generated here contains the bytes necessary to populate the
564      * data field; the "ID" and "len" fields in the send RTP header extension are populated by the
565      * IMS service.
566      *
567      * This method is responsible for generating the data field in the RTP header extension to be
568      * sent.
569      *
570      * Device to device communication assumes the following format for the single byte payload:
571      *  12345678
572      * +--------+
573      * |PPPPVVVV|
574      * +--------+
575      * Where:
576      *   PPPP - 4 bits reserved for indicating the parameter encoded (e.g. it could be an audio
577      *   codec ({@link Communicator#MESSAGE_CALL_AUDIO_CODEC})).
578      *   VVVV - 4 bits reserved for indicating the value of the parameter encoded (e.g. it could be
579      *   the EVS codec ({@link Communicator#AUDIO_CODEC_EVS})).
580      *
581      * @param message The message to be sent via RTP header extensions.
582      * @return An {@link RtpHeaderExtension} representing the message.
583      */
generateRtpHeaderExtension(Communicator.Message message)584     public RtpHeaderExtension generateRtpHeaderExtension(Communicator.Message message) {
585         byte[] payload = new byte[1];
586         switch (message.getType()) {
587             case Communicator.MESSAGE_CALL_AUDIO_CODEC:
588                 payload[0] |= CALL_STATE_MSG_TYPE_TO_RTP_BITS.getValue(message.getType());
589                 payload[0] |= CODEC_VALUE_TO_RTP_BITS.getValue(message.getValue());
590                 return new RtpHeaderExtension(
591                         getRtpHeaderExtensionIdentifier(CALL_STATE_RTP_HEADER_EXTENSION),
592                         payload);
593             case Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE:
594                 payload[0] |= CALL_STATE_MSG_TYPE_TO_RTP_BITS.getValue(message.getType());
595                 payload[0] |= RAT_VALUE_TO_RTP_BITS.getValue(message.getValue());
596                 return new RtpHeaderExtension(
597                         getRtpHeaderExtensionIdentifier(CALL_STATE_RTP_HEADER_EXTENSION),
598                         payload);
599             case Communicator.MESSAGE_DEVICE_BATTERY_STATE:
600                 payload[0] |= DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.getValue(message.getType());
601                 payload[0] |= BATTERY_STATE_VALUE_TO_RTP_BITS.getValue(message.getValue());
602                 return new RtpHeaderExtension(
603                         getRtpHeaderExtensionIdentifier(DEVICE_STATE_RTP_HEADER_EXTENSION),
604                         payload);
605             case Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE:
606                 payload[0] |= DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.getValue(message.getType());
607                 payload[0] |= NETWORK_COVERAGE_VALUE_TO_RTP_BITS.getValue(message.getValue());
608                 return new RtpHeaderExtension(
609                         getRtpHeaderExtensionIdentifier(DEVICE_STATE_RTP_HEADER_EXTENSION),
610                         payload);
611         }
612         return null;
613     }
614 
615     /**
616      * Given a {@link Uri} identifying an RTP header extension, return the associated local
617      * identifier.
618      * @param requestedUri the requested {@link Uri}.
619      * @return the local identifier.
620      */
getRtpHeaderExtensionIdentifier(Uri requestedUri)621     private int getRtpHeaderExtensionIdentifier(Uri requestedUri) {
622         return mSupportedRtpHeaderExtensionTypes.stream()
623                 .filter(t -> t.getUri().equals(requestedUri))
624                 .findFirst().get().getLocalIdentifier();
625     }
626 
627     /**
628      * Notifies the {@link Communicator} that the RTP-based protocol is available.}
629      */
notifyProtocolReady()630     private void notifyProtocolReady() {
631         if (mCallback != null) {
632             mCallback.onNegotiationSuccess(this);
633         }
634     }
635 
636     /**
637      * Notifies the {@link Communicator} that the RTP-based protocol is unavailable.
638      */
notifyProtocolUnavailable()639     private void notifyProtocolUnavailable() {
640         if (mCallback != null) {
641             mCallback.onNegotiationFailed(this);
642         }
643     }
644 }
645