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.internal.telephony.satellite; 18 19 import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.os.AsyncResult; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.telephony.Rlog; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.satellite.SatelliteDatagram; 31 import android.telephony.satellite.SatelliteManager; 32 33 import com.android.internal.annotations.GuardedBy; 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.telephony.Phone; 36 import com.android.internal.telephony.metrics.SatelliteStats; 37 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats; 38 39 import java.util.LinkedHashMap; 40 import java.util.Map.Entry; 41 import java.util.Set; 42 import java.util.concurrent.atomic.AtomicLong; 43 import java.util.function.Consumer; 44 45 /** 46 * Datagram dispatcher used to send satellite datagrams. 47 */ 48 public class DatagramDispatcher extends Handler { 49 private static final String TAG = "DatagramDispatcher"; 50 51 private static final int CMD_SEND_SATELLITE_DATAGRAM = 1; 52 private static final int EVENT_SEND_SATELLITE_DATAGRAM_DONE = 2; 53 private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3; 54 55 @NonNull private static DatagramDispatcher sInstance; 56 @NonNull private final Context mContext; 57 @NonNull private final DatagramController mDatagramController; 58 @NonNull private final ControllerMetricsStats mControllerMetricsStats; 59 60 private boolean mIsDemoMode = false; 61 private boolean mIsAligned = false; 62 private DatagramDispatcherHandlerRequest mSendSatelliteDatagramRequest = null; 63 64 private static AtomicLong mNextDatagramId = new AtomicLong(0); 65 66 private final Object mLock = new Object(); 67 68 @GuardedBy("mLock") 69 private boolean mSendingDatagramInProgress; 70 71 /** 72 * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending emergency 73 * datagrams. 74 */ 75 @GuardedBy("mLock") 76 private final LinkedHashMap<Long, SendSatelliteDatagramArgument> 77 mPendingEmergencyDatagramsMap = new LinkedHashMap<>(); 78 79 /** 80 * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending non-emergency 81 * datagrams. 82 */ 83 @GuardedBy("mLock") 84 private final LinkedHashMap<Long, SendSatelliteDatagramArgument> 85 mPendingNonEmergencyDatagramsMap = new LinkedHashMap<>(); 86 87 /** 88 * Create the DatagramDispatcher singleton instance. 89 * @param context The Context to use to create the DatagramDispatcher. 90 * @param looper The looper for the handler. 91 * @param datagramController DatagramController which is used to update datagram transfer state. 92 * @return The singleton instance of DatagramDispatcher. 93 */ make(@onNull Context context, @NonNull Looper looper, @NonNull DatagramController datagramController)94 public static DatagramDispatcher make(@NonNull Context context, @NonNull Looper looper, 95 @NonNull DatagramController datagramController) { 96 if (sInstance == null) { 97 sInstance = new DatagramDispatcher(context, looper, datagramController); 98 } 99 return sInstance; 100 } 101 102 /** 103 * Create a DatagramDispatcher to send satellite datagrams. 104 * 105 * @param context The Context for the DatagramDispatcher. 106 * @param looper The looper for the handler. 107 * @param datagramController DatagramController which is used to update datagram transfer state. 108 */ 109 @VisibleForTesting DatagramDispatcher(@onNull Context context, @NonNull Looper looper, @NonNull DatagramController datagramController)110 protected DatagramDispatcher(@NonNull Context context, @NonNull Looper looper, 111 @NonNull DatagramController datagramController) { 112 super(looper); 113 mContext = context; 114 mDatagramController = datagramController; 115 mControllerMetricsStats = ControllerMetricsStats.getInstance(); 116 117 synchronized (mLock) { 118 mSendingDatagramInProgress = false; 119 } 120 } 121 122 private static final class DatagramDispatcherHandlerRequest { 123 /** The argument to use for the request */ 124 public @NonNull Object argument; 125 /** The caller needs to specify the phone to be used for the request */ 126 public @NonNull Phone phone; 127 /** The result of the request that is run on the main thread */ 128 public @Nullable Object result; 129 DatagramDispatcherHandlerRequest(Object argument, Phone phone)130 DatagramDispatcherHandlerRequest(Object argument, Phone phone) { 131 this.argument = argument; 132 this.phone = phone; 133 } 134 } 135 136 private static final class SendSatelliteDatagramArgument { 137 public int subId; 138 public long datagramId; 139 public @SatelliteManager.DatagramType int datagramType; 140 public @NonNull SatelliteDatagram datagram; 141 public boolean needFullScreenPointingUI; 142 public @NonNull Consumer<Integer> callback; 143 public long datagramStartTime; 144 public boolean skipCheckingSatelliteAligned = false; 145 SendSatelliteDatagramArgument(int subId, long datagramId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)146 SendSatelliteDatagramArgument(int subId, long datagramId, 147 @SatelliteManager.DatagramType int datagramType, 148 @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, 149 @NonNull Consumer<Integer> callback) { 150 this.subId = subId; 151 this.datagramId = datagramId; 152 this.datagramType = datagramType; 153 this.datagram = datagram; 154 this.needFullScreenPointingUI = needFullScreenPointingUI; 155 this.callback = callback; 156 } 157 158 /** returns the size of outgoing SMS, rounded by 10 bytes */ getDatagramRoundedSizeBytes()159 public int getDatagramRoundedSizeBytes() { 160 if (datagram.getSatelliteDatagram() != null) { 161 int sizeBytes = datagram.getSatelliteDatagram().length; 162 // rounded by ROUNDING_UNIT 163 return (int) (Math.round((double) sizeBytes / ROUNDING_UNIT) * ROUNDING_UNIT); 164 } else { 165 return 0; 166 } 167 } 168 169 /** sets the start time at datagram is sent out */ setDatagramStartTime()170 public void setDatagramStartTime() { 171 datagramStartTime = 172 datagramStartTime == 0 ? System.currentTimeMillis() : datagramStartTime; 173 } 174 } 175 176 @Override handleMessage(Message msg)177 public void handleMessage(Message msg) { 178 DatagramDispatcherHandlerRequest request; 179 Message onCompleted; 180 AsyncResult ar; 181 182 switch(msg.what) { 183 case CMD_SEND_SATELLITE_DATAGRAM: { 184 logd("CMD_SEND_SATELLITE_DATAGRAM"); 185 request = (DatagramDispatcherHandlerRequest) msg.obj; 186 SendSatelliteDatagramArgument argument = 187 (SendSatelliteDatagramArgument) request.argument; 188 onCompleted = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request); 189 190 if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) { 191 SatelliteModemInterface.getInstance().sendSatelliteDatagram(argument.datagram, 192 argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE, 193 argument.needFullScreenPointingUI, onCompleted); 194 break; 195 } 196 197 Phone phone = request.phone; 198 if (phone != null) { 199 phone.sendSatelliteDatagram(onCompleted, argument.datagram, 200 argument.needFullScreenPointingUI); 201 } else { 202 loge("sendSatelliteDatagram: No phone object"); 203 synchronized (mLock) { 204 // Remove current datagram from pending map 205 if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) { 206 mPendingEmergencyDatagramsMap.remove(argument.datagramId); 207 } else { 208 mPendingNonEmergencyDatagramsMap.remove(argument.datagramId); 209 } 210 211 // Update send status 212 mDatagramController.updateSendStatus(argument.subId, 213 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 214 getPendingDatagramCount(), 215 SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE); 216 mDatagramController.updateSendStatus(argument.subId, 217 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 218 0, SatelliteManager.SATELLITE_ERROR_NONE); 219 220 // report phone == null case 221 reportSendDatagramCompleted(argument, 222 SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE); 223 argument.callback.accept( 224 SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE); 225 226 // Abort sending all the pending datagrams 227 abortSendingPendingDatagrams(argument.subId, 228 SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE); 229 } 230 } 231 break; 232 } 233 234 case EVENT_SEND_SATELLITE_DATAGRAM_DONE: { 235 ar = (AsyncResult) msg.obj; 236 request = (DatagramDispatcherHandlerRequest) ar.userObj; 237 int error = SatelliteServiceUtils.getSatelliteError(ar, "sendSatelliteDatagram"); 238 SendSatelliteDatagramArgument argument = 239 (SendSatelliteDatagramArgument) request.argument; 240 241 synchronized (mLock) { 242 if (mIsDemoMode && (error == SatelliteManager.SATELLITE_ERROR_NONE)) { 243 if (argument.skipCheckingSatelliteAligned) { 244 logd("Satellite was already aligned. No need to check alignment again"); 245 } else if (!mIsAligned) { 246 logd("Satellite is not aligned in demo mode, wait for the alignment."); 247 startSatelliteAlignedTimer(request); 248 break; 249 } 250 } 251 252 logd("EVENT_SEND_SATELLITE_DATAGRAM_DONE error: " + error); 253 // log metrics about the outgoing datagram 254 reportSendDatagramCompleted(argument, error); 255 256 mSendingDatagramInProgress = false; 257 258 // Remove current datagram from pending map. 259 if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) { 260 mPendingEmergencyDatagramsMap.remove(argument.datagramId); 261 } else { 262 mPendingNonEmergencyDatagramsMap.remove(argument.datagramId); 263 } 264 265 if (error == SatelliteManager.SATELLITE_ERROR_NONE) { 266 // Update send status for current datagram 267 mDatagramController.updateSendStatus(argument.subId, 268 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS, 269 getPendingDatagramCount(), error); 270 mControllerMetricsStats.reportOutgoingDatagramSuccessCount( 271 argument.datagramType); 272 273 if (getPendingDatagramCount() > 0) { 274 // Send response for current datagram 275 argument.callback.accept(error); 276 // Send pending datagrams 277 sendPendingDatagrams(); 278 } else { 279 mDatagramController.updateSendStatus(argument.subId, 280 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 281 0, SatelliteManager.SATELLITE_ERROR_NONE); 282 // Send response for current datagram 283 argument.callback.accept(error); 284 } 285 } else { 286 // Update send status 287 mDatagramController.updateSendStatus(argument.subId, 288 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 289 getPendingDatagramCount(), error); 290 mDatagramController.updateSendStatus(argument.subId, 291 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 292 0, SatelliteManager.SATELLITE_ERROR_NONE); 293 // Send response for current datagram 294 // after updating datagram transfer state internally. 295 argument.callback.accept(error); 296 // Abort sending all the pending datagrams 297 mControllerMetricsStats.reportOutgoingDatagramFailCount( 298 argument.datagramType); 299 abortSendingPendingDatagrams(argument.subId, 300 SatelliteManager.SATELLITE_REQUEST_ABORTED); 301 } 302 } 303 break; 304 } 305 306 case EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT: { 307 handleEventSatelliteAlignedTimeout((DatagramDispatcherHandlerRequest) msg.obj); 308 break; 309 } 310 311 default: 312 logw("DatagramDispatcherHandler: unexpected message code: " + msg.what); 313 break; 314 } 315 } 316 317 /** 318 * Send datagram over satellite. 319 * 320 * Gateway encodes SOS message or location sharing message into a datagram and passes it as 321 * input to this method. Datagram received here will be passed down to modem without any 322 * encoding or encryption. 323 * 324 * @param subId The subId of the subscription to send satellite datagrams for. 325 * @param datagramType datagram type indicating whether the datagram is of type 326 * SOS_SMS or LOCATION_SHARING. 327 * @param datagram encoded gateway datagram which is encrypted by the caller. 328 * Datagram will be passed down to modem without any encoding or encryption. 329 * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in 330 * full screen mode. 331 * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request. 332 */ sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)333 public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, 334 @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, 335 @NonNull Consumer<Integer> callback) { 336 Phone phone = SatelliteServiceUtils.getPhone(); 337 338 long datagramId = mNextDatagramId.getAndUpdate( 339 n -> ((n + 1) % DatagramController.MAX_DATAGRAM_ID)); 340 341 SendSatelliteDatagramArgument datagramArgs = 342 new SendSatelliteDatagramArgument(subId, datagramId, datagramType, datagram, 343 needFullScreenPointingUI, callback); 344 345 synchronized (mLock) { 346 // Add datagram to pending datagram map 347 if (datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) { 348 mPendingEmergencyDatagramsMap.put(datagramId, datagramArgs); 349 } else { 350 mPendingNonEmergencyDatagramsMap.put(datagramId, datagramArgs); 351 } 352 353 // Modem can be busy receiving datagrams, so send datagram only when modem is not busy. 354 if (!mSendingDatagramInProgress && mDatagramController.isPollingInIdleState()) { 355 mSendingDatagramInProgress = true; 356 datagramArgs.setDatagramStartTime(); 357 mDatagramController.updateSendStatus(subId, 358 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, 359 getPendingDatagramCount(), SatelliteManager.SATELLITE_ERROR_NONE); 360 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone); 361 } 362 } 363 } 364 retrySendingDatagrams()365 public void retrySendingDatagrams() { 366 synchronized (mLock) { 367 sendPendingDatagrams(); 368 } 369 } 370 371 /** Set demo mode 372 * 373 * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise. 374 */ 375 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setDemoMode(boolean isDemoMode)376 protected void setDemoMode(boolean isDemoMode) { 377 mIsDemoMode = isDemoMode; 378 } 379 380 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) onDeviceAlignedWithSatellite(boolean isAligned)381 protected void onDeviceAlignedWithSatellite(boolean isAligned) { 382 if (mIsDemoMode) { 383 synchronized (mLock) { 384 mIsAligned = isAligned; 385 if (isAligned) handleEventSatelliteAligned(); 386 } 387 } 388 } 389 startSatelliteAlignedTimer(@onNull DatagramDispatcherHandlerRequest request)390 private void startSatelliteAlignedTimer(@NonNull DatagramDispatcherHandlerRequest request) { 391 if (isSatelliteAlignedTimerStarted()) { 392 logd("Satellite aligned timer was already started"); 393 return; 394 } 395 mSendSatelliteDatagramRequest = request; 396 sendMessageDelayed( 397 obtainMessage(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT, request), 398 getSatelliteAlignedTimeoutDuration()); 399 } 400 401 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) getSatelliteAlignedTimeoutDuration()402 protected long getSatelliteAlignedTimeoutDuration() { 403 return mDatagramController.getSatelliteAlignedTimeoutDuration(); 404 } 405 handleEventSatelliteAligned()406 private void handleEventSatelliteAligned() { 407 if (isSatelliteAlignedTimerStarted()) { 408 stopSatelliteAlignedTimer(); 409 410 if (mSendSatelliteDatagramRequest == null) { 411 loge("handleEventSatelliteAligned: mSendSatelliteDatagramRequest is null"); 412 } else { 413 SendSatelliteDatagramArgument argument = 414 (SendSatelliteDatagramArgument) mSendSatelliteDatagramRequest.argument; 415 argument.skipCheckingSatelliteAligned = true; 416 Message message = obtainMessage( 417 EVENT_SEND_SATELLITE_DATAGRAM_DONE, mSendSatelliteDatagramRequest); 418 mSendSatelliteDatagramRequest = null; 419 AsyncResult.forMessage(message, null, null); 420 message.sendToTarget(); 421 } 422 } 423 } 424 handleEventSatelliteAlignedTimeout( @onNull DatagramDispatcherHandlerRequest request)425 private void handleEventSatelliteAlignedTimeout( 426 @NonNull DatagramDispatcherHandlerRequest request) { 427 SatelliteManager.SatelliteException exception = 428 new SatelliteManager.SatelliteException( 429 SatelliteManager.SATELLITE_NOT_REACHABLE); 430 Message message = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request); 431 AsyncResult.forMessage(message, null, exception); 432 message.sendToTarget(); 433 } 434 isSatelliteAlignedTimerStarted()435 private boolean isSatelliteAlignedTimerStarted() { 436 return hasMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT); 437 } 438 stopSatelliteAlignedTimer()439 private void stopSatelliteAlignedTimer() { 440 removeMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT); 441 } 442 443 /** 444 * Send pending satellite datagrams. Emergency datagrams are given priority over 445 * non-emergency datagrams. 446 */ 447 @GuardedBy("mLock") sendPendingDatagrams()448 private void sendPendingDatagrams() { 449 logd("sendPendingDatagrams()"); 450 if (!mDatagramController.isPollingInIdleState()) { 451 // Datagram should be sent to satellite modem when modem is free. 452 logd("sendPendingDatagrams: modem is receiving datagrams"); 453 return; 454 } 455 456 if (getPendingDatagramCount() <= 0) { 457 logd("sendPendingDatagrams: no pending datagrams to send"); 458 return; 459 } 460 461 Phone phone = SatelliteServiceUtils.getPhone(); 462 Set<Entry<Long, SendSatelliteDatagramArgument>> pendingDatagram = null; 463 if (!mSendingDatagramInProgress && !mPendingEmergencyDatagramsMap.isEmpty()) { 464 pendingDatagram = mPendingEmergencyDatagramsMap.entrySet(); 465 } else if (!mSendingDatagramInProgress && !mPendingNonEmergencyDatagramsMap.isEmpty()) { 466 pendingDatagram = mPendingNonEmergencyDatagramsMap.entrySet(); 467 } 468 469 if ((pendingDatagram != null) && pendingDatagram.iterator().hasNext()) { 470 mSendingDatagramInProgress = true; 471 SendSatelliteDatagramArgument datagramArg = 472 pendingDatagram.iterator().next().getValue(); 473 // Sets the trigger time for getting pending datagrams 474 datagramArg.setDatagramStartTime(); 475 mDatagramController.updateSendStatus(datagramArg.subId, 476 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, 477 getPendingDatagramCount(), SatelliteManager.SATELLITE_ERROR_NONE); 478 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArg, phone); 479 } 480 } 481 482 /** 483 * Send error code to all the pending datagrams 484 * 485 * @param pendingDatagramsMap The pending datagrams map to be cleaned up. 486 * @param errorCode error code to be returned. 487 */ 488 @GuardedBy("mLock") sendErrorCodeAndCleanupPendingDatagrams( LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap, @SatelliteManager.SatelliteError int errorCode)489 private void sendErrorCodeAndCleanupPendingDatagrams( 490 LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap, 491 @SatelliteManager.SatelliteError int errorCode) { 492 if (pendingDatagramsMap.size() == 0) { 493 return; 494 } 495 loge("sendErrorCodeAndCleanupPendingDatagrams: cleaning up resources"); 496 497 // Send error code to all the pending datagrams 498 for (Entry<Long, SendSatelliteDatagramArgument> entry : 499 pendingDatagramsMap.entrySet()) { 500 SendSatelliteDatagramArgument argument = entry.getValue(); 501 reportSendDatagramCompleted(argument, errorCode); 502 mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType); 503 argument.callback.accept(errorCode); 504 } 505 506 // Clear pending datagram maps 507 pendingDatagramsMap.clear(); 508 } 509 510 /** 511 * Abort sending all the pending datagrams. 512 * 513 * @param subId The subId of the subscription used to send datagram 514 * @param errorCode The error code that resulted in abort. 515 */ 516 @GuardedBy("mLock") abortSendingPendingDatagrams(int subId, @SatelliteManager.SatelliteError int errorCode)517 private void abortSendingPendingDatagrams(int subId, 518 @SatelliteManager.SatelliteError int errorCode) { 519 logd("abortSendingPendingDatagrams()"); 520 sendErrorCodeAndCleanupPendingDatagrams(mPendingEmergencyDatagramsMap, errorCode); 521 sendErrorCodeAndCleanupPendingDatagrams(mPendingNonEmergencyDatagramsMap, errorCode); 522 } 523 524 /** 525 * Return pending datagram count 526 * @return pending datagram count 527 */ 528 @GuardedBy("mLock") getPendingDatagramCount()529 private int getPendingDatagramCount() { 530 return mPendingEmergencyDatagramsMap.size() + mPendingNonEmergencyDatagramsMap.size(); 531 } 532 533 /** 534 * Posts the specified command to be executed on the main thread and returns immediately. 535 * 536 * @param command command to be executed on the main thread 537 * @param argument additional parameters required to perform of the operation 538 * @param phone phone object used to perform the operation. 539 */ sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone)540 private void sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone) { 541 DatagramDispatcherHandlerRequest request = new DatagramDispatcherHandlerRequest( 542 argument, phone); 543 Message msg = this.obtainMessage(command, request); 544 msg.sendToTarget(); 545 } 546 reportSendDatagramCompleted(@onNull SendSatelliteDatagramArgument argument, @NonNull @SatelliteManager.SatelliteError int resultCode)547 private void reportSendDatagramCompleted(@NonNull SendSatelliteDatagramArgument argument, 548 @NonNull @SatelliteManager.SatelliteError int resultCode) { 549 SatelliteStats.getInstance().onSatelliteOutgoingDatagramMetrics( 550 new SatelliteStats.SatelliteOutgoingDatagramParams.Builder() 551 .setDatagramType(argument.datagramType) 552 .setResultCode(resultCode) 553 .setDatagramSizeBytes(argument.getDatagramRoundedSizeBytes()) 554 .setDatagramTransferTimeMillis( 555 System.currentTimeMillis() - argument.datagramStartTime) 556 .build()); 557 } 558 559 /** 560 * Destroys this DatagramDispatcher. Used for tearing down static resources during testing. 561 */ 562 @VisibleForTesting destroy()563 public void destroy() { 564 sInstance = null; 565 } 566 567 /** 568 * This function is used by {@link DatagramController} to notify {@link DatagramDispatcher} 569 * that satellite modem state has changed. 570 * 571 * @param state Current satellite modem state. 572 */ onSatelliteModemStateChanged(@atelliteManager.SatelliteModemState int state)573 public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) { 574 synchronized (mLock) { 575 if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF 576 || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) { 577 logd("onSatelliteModemStateChanged: cleaning up resources"); 578 cleanUpResources(); 579 } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_IDLE) { 580 sendPendingDatagrams(); 581 } 582 } 583 } 584 585 @GuardedBy("mLock") cleanUpResources()586 private void cleanUpResources() { 587 mSendingDatagramInProgress = false; 588 if (getPendingDatagramCount() > 0) { 589 mDatagramController.updateSendStatus( 590 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 591 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 592 getPendingDatagramCount(), SatelliteManager.SATELLITE_REQUEST_ABORTED); 593 } 594 mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 595 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 596 0, SatelliteManager.SATELLITE_ERROR_NONE); 597 abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 598 SatelliteManager.SATELLITE_REQUEST_ABORTED); 599 600 stopSatelliteAlignedTimer(); 601 mIsDemoMode = false; 602 mSendSatelliteDatagramRequest = null; 603 mIsAligned = false; 604 } 605 logd(@onNull String log)606 private static void logd(@NonNull String log) { 607 Rlog.d(TAG, log); 608 } 609 loge(@onNull String log)610 private static void loge(@NonNull String log) { 611 Rlog.e(TAG, log); 612 } 613 logw(@onNull String log)614 private static void logw(@NonNull String log) { Rlog.w(TAG, log); } 615 } 616