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