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 android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS; 20 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS; 21 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN; 22 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED; 23 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING; 24 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED; 25 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN; 26 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT; 27 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR; 28 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE; 29 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS; 30 31 import static com.android.internal.telephony.SmsDispatchersController.PendingRequest; 32 import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT; 33 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.content.Context; 37 import android.content.res.Resources; 38 import android.os.AsyncResult; 39 import android.os.Handler; 40 import android.os.Looper; 41 import android.os.Message; 42 import android.telephony.PersistentLogger; 43 import android.telephony.satellite.SatelliteDatagram; 44 import android.telephony.satellite.SatelliteManager; 45 import android.telephony.satellite.SatelliteSessionStats; 46 import android.util.Log; 47 48 import com.android.internal.R; 49 import com.android.internal.annotations.GuardedBy; 50 import com.android.internal.annotations.VisibleForTesting; 51 import com.android.internal.os.SomeArgs; 52 import com.android.internal.telephony.Phone; 53 import com.android.internal.telephony.SmsDispatchersController; 54 import com.android.internal.telephony.flags.FeatureFlags; 55 import com.android.internal.telephony.metrics.SatelliteStats; 56 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats; 57 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats; 58 59 import java.util.LinkedHashMap; 60 import java.util.Map.Entry; 61 import java.util.Set; 62 import java.util.concurrent.TimeUnit; 63 import java.util.concurrent.atomic.AtomicBoolean; 64 import java.util.concurrent.atomic.AtomicLong; 65 import java.util.function.Consumer; 66 67 /** 68 * Datagram dispatcher used to send satellite datagrams. 69 */ 70 public class DatagramDispatcher extends Handler { 71 private static final String TAG = "DatagramDispatcher"; 72 73 private static final int CMD_SEND_SATELLITE_DATAGRAM = 1; 74 private static final int EVENT_SEND_SATELLITE_DATAGRAM_DONE = 2; 75 private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3; 76 private static final int EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT = 4; 77 private static final int EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT = 5; 78 private static final int EVENT_ABORT_SENDING_SATELLITE_DATAGRAMS_DONE = 6; 79 private static final int EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT = 7; 80 private static final int CMD_SEND_SMS = 8; 81 private static final int EVENT_SEND_SMS_DONE = 9; 82 private static final int EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT = 10; 83 private static final int CMD_SEND_MT_SMS_POLLING_MESSAGE = 11; 84 85 private static final Long TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE = TimeUnit.SECONDS.toMillis(10); 86 @NonNull private static DatagramDispatcher sInstance; 87 @NonNull private final Context mContext; 88 @NonNull private final DatagramController mDatagramController; 89 @NonNull private final ControllerMetricsStats mControllerMetricsStats; 90 @NonNull private final SessionMetricsStats mSessionMetricsStats; 91 @NonNull private final FeatureFlags mFeatureFlags; 92 93 private boolean mIsDemoMode = false; 94 private boolean mIsAligned = false; 95 private DatagramDispatcherHandlerRequest mSendSatelliteDatagramRequest = null; 96 97 private static AtomicLong mNextDatagramId = new AtomicLong(0); 98 99 private AtomicBoolean mShouldSendDatagramToModemInDemoMode = null; 100 101 private final Object mLock = new Object(); 102 private long mDemoTimeoutDuration = TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE; 103 104 /** {@code true} if already sent an emergency datagram during a session */ 105 @GuardedBy("mLock") 106 private boolean mIsEmergencyCommunicationEstablished = false; 107 108 @GuardedBy("mLock") 109 private boolean mSendingInProgress; 110 111 /** 112 * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending emergency 113 * datagrams. 114 */ 115 @GuardedBy("mLock") 116 private final LinkedHashMap<Long, SendSatelliteDatagramArgument> 117 mPendingEmergencyDatagramsMap = new LinkedHashMap<>(); 118 119 /** 120 * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending non-emergency 121 * datagrams. 122 */ 123 @GuardedBy("mLock") 124 private final LinkedHashMap<Long, SendSatelliteDatagramArgument> 125 mPendingNonEmergencyDatagramsMap = new LinkedHashMap<>(); 126 127 /** 128 * Map key: messageId, value: {@link PendingRequest} which contains all the information to send 129 * carrier roaming nb iot ntn SMS. 130 */ 131 @GuardedBy("mLock") 132 private final LinkedHashMap<Long, PendingRequest> mPendingSmsMap = new LinkedHashMap<>(); 133 134 private long mWaitTimeForDatagramSendingResponse; 135 private long mWaitTimeForDatagramSendingForLastMessageResponse; 136 @SatelliteManager.DatagramType 137 private int mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN; 138 @Nullable private PersistentLogger mPersistentLogger = null; 139 140 @GuardedBy("mLock") 141 private int mModemState = SATELLITE_MODEM_STATE_UNKNOWN; 142 @GuardedBy("mLock") 143 private boolean mHasEnteredConnectedState = false; 144 @GuardedBy("mLock") 145 private boolean mShouldPollMtSms = false; 146 @GuardedBy("mLock") 147 private boolean mIsMtSmsPollingThrottled = false; 148 @GuardedBy("mLock") 149 private int mConnectedStateCounter = 0; 150 private long mSmsTransmissionStartTime = 0; 151 152 /** 153 * Create the DatagramDispatcher singleton instance. 154 * @param context The Context to use to create the DatagramDispatcher. 155 * @param looper The looper for the handler. 156 * @param featureFlags The telephony feature flags. 157 * @param datagramController DatagramController which is used to update datagram transfer state. 158 * @return The singleton instance of DatagramDispatcher. 159 */ make(@onNull Context context, @NonNull Looper looper, @NonNull FeatureFlags featureFlags, @NonNull DatagramController datagramController)160 public static DatagramDispatcher make(@NonNull Context context, @NonNull Looper looper, 161 @NonNull FeatureFlags featureFlags, 162 @NonNull DatagramController datagramController) { 163 if (sInstance == null) { 164 sInstance = new DatagramDispatcher(context, looper, featureFlags, datagramController); 165 } 166 return sInstance; 167 } 168 169 /** 170 * @return The singleton instance of DatagramDispatcher. 171 */ getInstance()172 public static DatagramDispatcher getInstance() { 173 if (sInstance == null) { 174 loge("DatagramDispatcher was not yet initialized."); 175 } 176 return sInstance; 177 } 178 179 /** 180 * Create a DatagramDispatcher to send satellite datagrams. 181 * 182 * @param context The Context for the DatagramDispatcher. 183 * @param looper The looper for the handler. 184 * @param featureFlags The telephony feature flags. 185 * @param datagramController DatagramController which is used to update datagram transfer state. 186 */ 187 @VisibleForTesting DatagramDispatcher(@onNull Context context, @NonNull Looper looper, @NonNull FeatureFlags featureFlags, @NonNull DatagramController datagramController)188 protected DatagramDispatcher(@NonNull Context context, @NonNull Looper looper, 189 @NonNull FeatureFlags featureFlags, 190 @NonNull DatagramController datagramController) { 191 super(looper); 192 mContext = context; 193 mFeatureFlags = featureFlags; 194 mDatagramController = datagramController; 195 mControllerMetricsStats = ControllerMetricsStats.getInstance(); 196 mSessionMetricsStats = SessionMetricsStats.getInstance(); 197 mPersistentLogger = SatelliteServiceUtils.getPersistentLogger(context); 198 199 synchronized (mLock) { 200 mSendingInProgress = false; 201 } 202 mWaitTimeForDatagramSendingResponse = getWaitForDatagramSendingResponseTimeoutMillis(); 203 mWaitTimeForDatagramSendingForLastMessageResponse = 204 getWaitForDatagramSendingResponseForLastMessageTimeoutMillis(); 205 } 206 207 private static final class DatagramDispatcherHandlerRequest { 208 /** The argument to use for the request */ 209 public @NonNull Object argument; 210 /** The caller needs to specify the phone to be used for the request */ 211 public @NonNull Phone phone; 212 /** The result of the request that is run on the main thread */ 213 public @Nullable Object result; 214 DatagramDispatcherHandlerRequest(Object argument, Phone phone)215 DatagramDispatcherHandlerRequest(Object argument, Phone phone) { 216 this.argument = argument; 217 this.phone = phone; 218 } 219 } 220 221 private static final class SendSatelliteDatagramArgument { 222 public int subId; 223 public long datagramId; 224 public @SatelliteManager.DatagramType int datagramType; 225 public @NonNull SatelliteDatagram datagram; 226 public boolean needFullScreenPointingUI; 227 public @NonNull Consumer<Integer> callback; 228 public long datagramStartTime; 229 public boolean skipCheckingSatelliteAligned = false; 230 SendSatelliteDatagramArgument(int subId, long datagramId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)231 SendSatelliteDatagramArgument(int subId, long datagramId, 232 @SatelliteManager.DatagramType int datagramType, 233 @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, 234 @NonNull Consumer<Integer> callback) { 235 this.subId = subId; 236 this.datagramId = datagramId; 237 this.datagramType = datagramType; 238 this.datagram = datagram; 239 this.needFullScreenPointingUI = needFullScreenPointingUI; 240 this.callback = callback; 241 } 242 243 /** returns the size of outgoing SMS, rounded by 10 bytes */ getDatagramRoundedSizeBytes()244 public int getDatagramRoundedSizeBytes() { 245 if (datagram.getSatelliteDatagram() != null) { 246 int sizeBytes = datagram.getSatelliteDatagram().length; 247 // rounded by ROUNDING_UNIT 248 return (int) (Math.round((double) sizeBytes / ROUNDING_UNIT) * ROUNDING_UNIT); 249 } else { 250 return 0; 251 } 252 } 253 254 /** sets the start time at datagram is sent out */ setDatagramStartTime()255 public void setDatagramStartTime() { 256 datagramStartTime = 257 datagramStartTime == 0 ? System.currentTimeMillis() : datagramStartTime; 258 } 259 } 260 261 @Override handleMessage(Message msg)262 public void handleMessage(Message msg) { 263 DatagramDispatcherHandlerRequest request; 264 Message onCompleted; 265 AsyncResult ar; 266 267 switch(msg.what) { 268 case CMD_SEND_SATELLITE_DATAGRAM: { 269 plogd("CMD_SEND_SATELLITE_DATAGRAM mIsDemoMode=" + mIsDemoMode 270 + ", shouldSendDatagramToModemInDemoMode=" 271 + shouldSendDatagramToModemInDemoMode()); 272 request = (DatagramDispatcherHandlerRequest) msg.obj; 273 SendSatelliteDatagramArgument argument = 274 (SendSatelliteDatagramArgument) request.argument; 275 argument.setDatagramStartTime(); 276 onCompleted = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request); 277 278 synchronized (mLock) { 279 if (mIsDemoMode && !shouldSendDatagramToModemInDemoMode()) { 280 AsyncResult.forMessage(onCompleted, SATELLITE_RESULT_SUCCESS, null); 281 sendMessageDelayed(onCompleted, getDemoTimeoutDuration()); 282 } else { 283 SatelliteModemInterface.getInstance().sendSatelliteDatagram( 284 argument.datagram, 285 SatelliteServiceUtils.isSosMessage(argument.datagramType), 286 argument.needFullScreenPointingUI, onCompleted); 287 startWaitForDatagramSendingResponseTimer(argument); 288 } 289 } 290 break; 291 } 292 case EVENT_SEND_SATELLITE_DATAGRAM_DONE: { 293 ar = (AsyncResult) msg.obj; 294 request = (DatagramDispatcherHandlerRequest) ar.userObj; 295 int error = SatelliteServiceUtils.getSatelliteError(ar, "sendDatagram"); 296 SendSatelliteDatagramArgument argument = 297 (SendSatelliteDatagramArgument) request.argument; 298 299 synchronized (mLock) { 300 if (mIsDemoMode && (error == SatelliteManager.SATELLITE_RESULT_SUCCESS)) { 301 if (argument.skipCheckingSatelliteAligned) { 302 plogd("Satellite was already aligned. " 303 + "No need to check alignment again"); 304 } else if (mDatagramController.waitForAligningToSatellite(mIsAligned)) { 305 plogd("Satellite is not aligned in demo mode, wait for the alignment."); 306 startSatelliteAlignedTimer(request); 307 break; 308 } 309 } 310 plogd("EVENT_SEND_SATELLITE_DATAGRAM_DONE error: " + error 311 + ", mIsDemoMode=" + mIsDemoMode); 312 313 /* 314 * The response should be ignored if either of the following hold 315 * 1) Framework has already received this response from the vendor service. 316 * 2) Framework has timed out to wait for the response from vendor service for 317 * the send request. 318 * 3) All pending send requests have been aborted due to some error. 319 */ 320 if (!shouldProcessEventSendSatelliteDatagramDone(argument)) { 321 plogw("The message " + argument.datagramId + " was already processed"); 322 break; 323 } 324 325 stopWaitForDatagramSendingResponseTimer(); 326 mSendingInProgress = false; 327 328 // Log metrics about the outgoing datagram 329 reportSendDatagramCompleted(argument, error); 330 // Remove current datagram from pending map. 331 if (SatelliteServiceUtils.isSosMessage(argument.datagramType)) { 332 mPendingEmergencyDatagramsMap.remove(argument.datagramId); 333 if (error == SATELLITE_RESULT_SUCCESS) { 334 mIsEmergencyCommunicationEstablished = true; 335 } 336 } else { 337 mPendingNonEmergencyDatagramsMap.remove(argument.datagramId); 338 } 339 340 if (error == SATELLITE_RESULT_SUCCESS) { 341 // Update send status for current datagram 342 mDatagramController.updateSendStatus(argument.subId, argument.datagramType, 343 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS, 344 getPendingMessagesCount(), error); 345 startWaitForSimulatedPollDatagramsDelayTimer(request); 346 } else { 347 // Update send status 348 mDatagramController.updateSendStatus(argument.subId, argument.datagramType, 349 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 350 getPendingMessagesCount(), error); 351 } 352 353 if (getPendingMessagesCount() > 0) { 354 // Send response for current datagram 355 argument.callback.accept(error); 356 // Send pending datagrams 357 sendPendingMessages(); 358 } else { 359 mDatagramController.updateSendStatus(argument.subId, 360 argument.datagramType, 361 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0, 362 SatelliteManager.SATELLITE_RESULT_SUCCESS); 363 // Send response for current datagram 364 argument.callback.accept(error); 365 } 366 } 367 break; 368 } 369 370 case EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT: 371 handleEventWaitForDatagramSendingResponseTimedOut( 372 (SendSatelliteDatagramArgument) msg.obj); 373 break; 374 375 case EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT: { 376 handleEventSatelliteAlignedTimeout((DatagramDispatcherHandlerRequest) msg.obj); 377 break; 378 } 379 380 case EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT: 381 handleEventDatagramWaitForConnectedStateTimedOut((int) msg.obj); 382 break; 383 384 case EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT: 385 request = (DatagramDispatcherHandlerRequest) msg.obj; 386 handleEventWaitForSimulatedPollDatagramsDelayTimedOut( 387 (SendSatelliteDatagramArgument) request.argument); 388 break; 389 390 case CMD_SEND_SMS: { 391 PendingRequest pendingRequest = (PendingRequest) msg.obj; 392 Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone(); 393 if (satellitePhone == null) { 394 ploge("CMD_SEND_SMS: satellitePhone is null."); 395 return; 396 } 397 398 SmsDispatchersController smsDispatchersController = 399 satellitePhone.getSmsDispatchersController(); 400 if (smsDispatchersController == null) { 401 ploge("CMD_SEND_SMS: smsDispatchersController is null."); 402 return; 403 } 404 405 mSmsTransmissionStartTime = System.currentTimeMillis(); 406 smsDispatchersController.sendCarrierRoamingNbIotNtnText(pendingRequest); 407 break; 408 } 409 410 case EVENT_SEND_SMS_DONE: { 411 SomeArgs args = (SomeArgs) msg.obj; 412 int subId = (int) args.arg1; 413 long messageId = (long) args.arg2; 414 boolean success = (boolean) args.arg3; 415 try { 416 handleEventSendSmsDone(subId, messageId, success); 417 } finally { 418 args.recycle(); 419 } 420 break; 421 } 422 423 case EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT: { 424 synchronized (mLock) { 425 mIsMtSmsPollingThrottled = false; 426 } 427 if (allowMtSmsPolling()) { 428 sendMessage(obtainMessage(CMD_SEND_MT_SMS_POLLING_MESSAGE)); 429 } 430 break; 431 } 432 433 case CMD_SEND_MT_SMS_POLLING_MESSAGE: { 434 plogd("CMD_SEND_MT_SMS_POLLING_MESSAGE"); 435 handleCmdSendMtSmsPollingMessage(); 436 break; 437 } 438 439 default: 440 plogw("DatagramDispatcherHandler: unexpected message code: " + msg.what); 441 break; 442 } 443 } 444 445 /** 446 * Send datagram over satellite. 447 * 448 * Gateway encodes SOS message or location sharing message into a datagram and passes it as 449 * input to this method. Datagram received here will be passed down to modem without any 450 * encoding or encryption. 451 * 452 * @param subId The subId of the subscription to send satellite datagrams for. 453 * @param datagramType datagram type indicating whether the datagram is of type 454 * SOS_SMS or LOCATION_SHARING. 455 * @param datagram encoded gateway datagram which is encrypted by the caller. 456 * Datagram will be passed down to modem without any encoding or encryption. 457 * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in 458 * full screen mode. 459 * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request. 460 */ sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)461 public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, 462 @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, 463 @NonNull Consumer<Integer> callback) { 464 Phone phone = SatelliteServiceUtils.getPhone(); 465 466 long datagramId = mNextDatagramId.getAndUpdate( 467 n -> ((n + 1) % DatagramController.MAX_DATAGRAM_ID)); 468 SendSatelliteDatagramArgument datagramArgs = 469 new SendSatelliteDatagramArgument(subId, datagramId, datagramType, datagram, 470 needFullScreenPointingUI, callback); 471 mLastSendRequestDatagramType = datagramType; 472 473 synchronized (mLock) { 474 // Add datagram to pending datagram map 475 if (SatelliteServiceUtils.isSosMessage(datagramType)) { 476 mPendingEmergencyDatagramsMap.put(datagramId, datagramArgs); 477 } else { 478 mPendingNonEmergencyDatagramsMap.put(datagramId, datagramArgs); 479 } 480 481 if (mDatagramController.needsWaitingForSatelliteConnected(datagramType)) { 482 plogd("sendDatagram: wait for satellite connected"); 483 mDatagramController.updateSendStatus(subId, datagramType, 484 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, 485 getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 486 startDatagramWaitForConnectedStateTimer(datagramArgs.datagramType); 487 } else if (!mSendingInProgress && mDatagramController.isPollingInIdleState()) { 488 // Modem can be busy receiving datagrams, so send datagram only when modem is 489 // not busy. 490 mSendingInProgress = true; 491 mDatagramController.updateSendStatus(subId, datagramType, 492 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, 493 getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 494 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone); 495 } else { 496 plogd("sendDatagram: mSendingInProgress=" + mSendingInProgress 497 + ", isPollingInIdleState=" + mDatagramController.isPollingInIdleState()); 498 } 499 } 500 } 501 retrySendingDatagrams()502 public void retrySendingDatagrams() { 503 synchronized (mLock) { 504 sendPendingMessages(); 505 } 506 } 507 508 /** Set demo mode 509 * 510 * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise. 511 */ 512 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setDemoMode(boolean isDemoMode)513 protected void setDemoMode(boolean isDemoMode) { 514 mIsDemoMode = isDemoMode; 515 plogd("setDemoMode: mIsDemoMode=" + mIsDemoMode); 516 } 517 518 /** 519 * Set whether the device is aligned with the satellite. 520 */ 521 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setDeviceAlignedWithSatellite(boolean isAligned)522 public void setDeviceAlignedWithSatellite(boolean isAligned) { 523 synchronized (mLock) { 524 mIsAligned = isAligned; 525 plogd("setDeviceAlignedWithSatellite: " + mIsAligned); 526 if (isAligned && mIsDemoMode) handleEventSatelliteAligned(); 527 } 528 if (allowMtSmsPolling()) { 529 sendMessage(obtainMessage(CMD_SEND_MT_SMS_POLLING_MESSAGE)); 530 } 531 } 532 startSatelliteAlignedTimer(@onNull DatagramDispatcherHandlerRequest request)533 private void startSatelliteAlignedTimer(@NonNull DatagramDispatcherHandlerRequest request) { 534 if (isSatelliteAlignedTimerStarted()) { 535 plogd("Satellite aligned timer was already started"); 536 return; 537 } 538 mSendSatelliteDatagramRequest = request; 539 sendMessageDelayed( 540 obtainMessage(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT, request), 541 getSatelliteAlignedTimeoutDuration()); 542 } 543 544 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) getSatelliteAlignedTimeoutDuration()545 protected long getSatelliteAlignedTimeoutDuration() { 546 return mDatagramController.getSatelliteAlignedTimeoutDuration(); 547 } 548 handleEventSatelliteAligned()549 private void handleEventSatelliteAligned() { 550 if (isSatelliteAlignedTimerStarted()) { 551 stopSatelliteAlignedTimer(); 552 553 if (mSendSatelliteDatagramRequest == null) { 554 ploge("handleEventSatelliteAligned: mSendSatelliteDatagramRequest is null"); 555 } else { 556 SendSatelliteDatagramArgument argument = 557 (SendSatelliteDatagramArgument) mSendSatelliteDatagramRequest.argument; 558 argument.skipCheckingSatelliteAligned = true; 559 Message message = obtainMessage( 560 EVENT_SEND_SATELLITE_DATAGRAM_DONE, mSendSatelliteDatagramRequest); 561 mSendSatelliteDatagramRequest = null; 562 AsyncResult.forMessage(message, null, null); 563 message.sendToTarget(); 564 plogd("handleEventSatelliteAligned: EVENT_SEND_SATELLITE_DATAGRAM_DONE"); 565 } 566 } 567 } 568 handleEventSatelliteAlignedTimeout( @onNull DatagramDispatcherHandlerRequest request)569 private void handleEventSatelliteAlignedTimeout( 570 @NonNull DatagramDispatcherHandlerRequest request) { 571 plogd("handleEventSatelliteAlignedTimeout"); 572 mSendSatelliteDatagramRequest = null; 573 SatelliteManager.SatelliteException exception = 574 new SatelliteManager.SatelliteException( 575 SATELLITE_RESULT_NOT_REACHABLE); 576 Message message = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request); 577 AsyncResult.forMessage(message, null, exception); 578 message.sendToTarget(); 579 } 580 isSatelliteAlignedTimerStarted()581 private boolean isSatelliteAlignedTimerStarted() { 582 return hasMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT); 583 } 584 stopSatelliteAlignedTimer()585 private void stopSatelliteAlignedTimer() { 586 removeMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT); 587 } 588 589 @GuardedBy("mLock") sendPendingMessages()590 private void sendPendingMessages() { 591 plogd("sendPendingMessages"); 592 593 // Pending datagrams are prioritized over pending SMS. 594 if (getPendingDatagramCount() > 0) { 595 sendPendingDatagrams(); 596 return; 597 } 598 599 if (mFeatureFlags.carrierRoamingNbIotNtn()) { 600 if (getPendingSmsCount() > 0) { 601 sendPendingSms(); 602 } 603 } 604 } 605 606 /** 607 * Send pending satellite datagrams. Emergency datagrams are given priority over 608 * non-emergency datagrams. 609 */ 610 @GuardedBy("mLock") sendPendingDatagrams()611 private void sendPendingDatagrams() { 612 plogd("sendPendingDatagrams()"); 613 if (!mDatagramController.isPollingInIdleState()) { 614 // Datagram should be sent to satellite modem when modem is free. 615 plogd("sendPendingDatagrams: modem is receiving datagrams"); 616 return; 617 } 618 619 if (getPendingDatagramCount() <= 0) { 620 plogd("sendPendingDatagrams: no pending datagrams to send"); 621 return; 622 } 623 624 Phone phone = SatelliteServiceUtils.getPhone(); 625 Set<Entry<Long, SendSatelliteDatagramArgument>> pendingDatagram = null; 626 if (!mSendingInProgress && !mPendingEmergencyDatagramsMap.isEmpty()) { 627 pendingDatagram = mPendingEmergencyDatagramsMap.entrySet(); 628 } else if (!mSendingInProgress && !mPendingNonEmergencyDatagramsMap.isEmpty()) { 629 pendingDatagram = mPendingNonEmergencyDatagramsMap.entrySet(); 630 } 631 632 if ((pendingDatagram != null) && pendingDatagram.iterator().hasNext()) { 633 SendSatelliteDatagramArgument datagramArg = 634 pendingDatagram.iterator().next().getValue(); 635 if (mDatagramController.needsWaitingForSatelliteConnected(datagramArg.datagramType)) { 636 plogd("sendPendingDatagrams: wait for satellite connected"); 637 mDatagramController.updateSendStatus(datagramArg.subId, 638 datagramArg.datagramType, 639 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, 640 getPendingMessagesCount(), 641 SatelliteManager.SATELLITE_RESULT_SUCCESS); 642 startDatagramWaitForConnectedStateTimer( 643 datagramArg.datagramType); 644 return; 645 } 646 647 mSendingInProgress = true; 648 // Sets the trigger time for getting pending datagrams 649 mDatagramController.updateSendStatus(datagramArg.subId, datagramArg.datagramType, 650 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, 651 getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 652 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArg, phone); 653 } 654 } 655 656 /** 657 * Send error code to all the pending datagrams 658 * 659 * @param pendingDatagramsMap The pending datagrams map to be cleaned up. 660 * @param errorCode error code to be returned. 661 */ 662 @GuardedBy("mLock") sendErrorCodeAndCleanupPendingDatagrams( LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap, @SatelliteManager.SatelliteResult int errorCode)663 private void sendErrorCodeAndCleanupPendingDatagrams( 664 LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap, 665 @SatelliteManager.SatelliteResult int errorCode) { 666 if (pendingDatagramsMap.size() == 0) { 667 return; 668 } 669 ploge("sendErrorCodeAndCleanupPendingDatagrams: cleaning up resources"); 670 671 // Send error code to all the pending datagrams 672 for (Entry<Long, SendSatelliteDatagramArgument> entry : 673 pendingDatagramsMap.entrySet()) { 674 SendSatelliteDatagramArgument argument = entry.getValue(); 675 reportSendDatagramCompleted(argument, errorCode); 676 argument.callback.accept(errorCode); 677 } 678 679 // Clear pending datagram maps 680 pendingDatagramsMap.clear(); 681 } 682 683 /** 684 * Abort sending all the pending datagrams. 685 * 686 * @param subId The subId of the subscription used to send datagram 687 * @param errorCode The error code that resulted in abort. 688 */ 689 @GuardedBy("mLock") abortSendingPendingDatagrams(int subId, @SatelliteManager.SatelliteResult int errorCode)690 private void abortSendingPendingDatagrams(int subId, 691 @SatelliteManager.SatelliteResult int errorCode) { 692 plogd("abortSendingPendingDatagrams()"); 693 sendErrorCodeAndCleanupPendingDatagrams(mPendingEmergencyDatagramsMap, errorCode); 694 sendErrorCodeAndCleanupPendingDatagrams(mPendingNonEmergencyDatagramsMap, errorCode); 695 sendErrorCodeAndCleanupPendingSms(mPendingSmsMap, errorCode); 696 } 697 698 /** 699 * Return pending datagram and SMS count 700 * @return pending messages count 701 */ getPendingMessagesCount()702 public int getPendingMessagesCount() { 703 synchronized (mLock) { 704 if (mFeatureFlags.carrierRoamingNbIotNtn()) { 705 return getPendingDatagramCount() + getPendingSmsCount(); 706 } else { 707 return getPendingDatagramCount(); 708 } 709 } 710 } 711 712 /** 713 * Return pending datagram count 714 * @return pending datagram count 715 */ getPendingDatagramCount()716 public int getPendingDatagramCount() { 717 synchronized (mLock) { 718 return mPendingEmergencyDatagramsMap.size() + mPendingNonEmergencyDatagramsMap.size(); 719 } 720 } 721 722 /** 723 * Return pending SMS count 724 * @return pending SMS count 725 */ getPendingSmsCount()726 public int getPendingSmsCount() { 727 synchronized (mLock) { 728 return mPendingSmsMap.size(); 729 } 730 } 731 732 /** Return pending user messages count */ getPendingUserMessagesCount()733 public int getPendingUserMessagesCount() { 734 synchronized (mLock) { 735 int pendingUserMessagesCount = 0; 736 for (Entry<Long, SendSatelliteDatagramArgument> entry : 737 mPendingNonEmergencyDatagramsMap.entrySet()) { 738 SendSatelliteDatagramArgument argument = entry.getValue(); 739 if (argument.datagramType != SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) { 740 pendingUserMessagesCount += 1; 741 } 742 } 743 pendingUserMessagesCount += mPendingEmergencyDatagramsMap.size(); 744 return pendingUserMessagesCount; 745 } 746 } 747 748 /** 749 * Posts the specified command to be executed on the main thread and returns immediately. 750 * 751 * @param command command to be executed on the main thread 752 * @param argument additional parameters required to perform of the operation 753 * @param phone phone object used to perform the operation. 754 */ sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone)755 private void sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone) { 756 DatagramDispatcherHandlerRequest request = new DatagramDispatcherHandlerRequest( 757 argument, phone); 758 Message msg = this.obtainMessage(command, request); 759 msg.sendToTarget(); 760 } 761 reportSendSmsCompleted(@onNull PendingRequest pendingRequest, @SatelliteManager.SatelliteResult int resultCode)762 private void reportSendSmsCompleted(@NonNull PendingRequest pendingRequest, 763 @SatelliteManager.SatelliteResult int resultCode) { 764 int datagramType = pendingRequest.isMtSmsPolling 765 ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS; 766 if (resultCode == SATELLITE_RESULT_SUCCESS) { 767 long smsTransmissionTime = mSmsTransmissionStartTime > 0 768 ? (System.currentTimeMillis() - mSmsTransmissionStartTime) : 0; 769 mControllerMetricsStats.reportOutgoingDatagramSuccessCount(datagramType, false); 770 mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram( 771 datagramType, smsTransmissionTime); 772 } else { 773 mControllerMetricsStats.reportOutgoingDatagramFailCount(datagramType, false); 774 mSessionMetricsStats.addCountOfFailedOutgoingDatagram( 775 datagramType, resultCode); 776 } 777 } 778 reportSendDatagramCompleted(@onNull SendSatelliteDatagramArgument argument, @NonNull @SatelliteManager.SatelliteResult int resultCode)779 private void reportSendDatagramCompleted(@NonNull SendSatelliteDatagramArgument argument, 780 @NonNull @SatelliteManager.SatelliteResult int resultCode) { 781 long datagramTransmissionTime = argument.datagramStartTime > 0 782 ? (System.currentTimeMillis() - argument.datagramStartTime) : 0; 783 SatelliteStats.getInstance().onSatelliteOutgoingDatagramMetrics( 784 new SatelliteStats.SatelliteOutgoingDatagramParams.Builder() 785 .setDatagramType(argument.datagramType) 786 .setResultCode(resultCode) 787 .setDatagramSizeBytes(argument.getDatagramRoundedSizeBytes()) 788 /* In case pending datagram has not been attempted to send to modem 789 interface. transfer time will be 0. */ 790 .setDatagramTransferTimeMillis(datagramTransmissionTime) 791 .setIsDemoMode(mIsDemoMode) 792 .setCarrierId(SatelliteController.getInstance().getSatelliteCarrierId()) 793 .setIsNtnOnlyCarrier(SatelliteController.getInstance().isNtnOnlyCarrier()) 794 .build()); 795 if (resultCode == SatelliteManager.SATELLITE_RESULT_SUCCESS) { 796 mControllerMetricsStats.reportOutgoingDatagramSuccessCount(argument.datagramType, 797 mIsDemoMode); 798 mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram(argument.datagramType, 799 datagramTransmissionTime); 800 } else { 801 mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType, 802 mIsDemoMode); 803 mSessionMetricsStats.addCountOfFailedOutgoingDatagram(argument.datagramType, 804 resultCode); 805 } 806 } 807 808 /** 809 * Destroys this DatagramDispatcher. Used for tearing down static resources during testing. 810 */ 811 @VisibleForTesting destroy()812 public void destroy() { 813 sInstance = null; 814 } 815 816 /** 817 * This function is used by {@link DatagramController} to notify {@link DatagramDispatcher} 818 * that satellite modem state has changed. 819 * 820 * @param state Current satellite modem state. 821 */ onSatelliteModemStateChanged(@atelliteManager.SatelliteModemState int state)822 public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) { 823 synchronized (mLock) { 824 mModemState = state; 825 if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF 826 || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) { 827 plogd("onSatelliteModemStateChanged: cleaning up resources"); 828 cleanUpResources(); 829 } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_IDLE) { 830 sendPendingMessages(); 831 } 832 833 if (state == SATELLITE_MODEM_STATE_CONNECTED) { 834 mHasEnteredConnectedState = true; 835 836 mConnectedStateCounter++; 837 if (isFirstConnected()) { 838 mShouldPollMtSms = shouldPollMtSms(); 839 } 840 841 if (isDatagramWaitForConnectedStateTimerStarted()) { 842 stopDatagramWaitForConnectedStateTimer(); 843 sendPendingMessages(); 844 } 845 } 846 847 if (state == SATELLITE_MODEM_STATE_NOT_CONNECTED) { 848 if (mHasEnteredConnectedState) { 849 mHasEnteredConnectedState = false; 850 mShouldPollMtSms = shouldPollMtSms(); 851 } 852 } 853 } 854 if (allowMtSmsPolling()) { 855 sendMessage(obtainMessage(CMD_SEND_MT_SMS_POLLING_MESSAGE)); 856 } 857 } 858 859 /** Returns true if this is the first time the satellite modem is connected. */ isFirstConnected()860 private boolean isFirstConnected() { 861 return mConnectedStateCounter == 1; 862 } 863 864 @GuardedBy("mLock") cleanUpResources()865 private void cleanUpResources() { 866 plogd("cleanUpResources"); 867 mSendingInProgress = false; 868 mIsEmergencyCommunicationEstablished = false; 869 870 int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); 871 if (getPendingMessagesCount() > 0) { 872 mDatagramController.updateSendStatus(subId, 873 mLastSendRequestDatagramType, 874 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 875 getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED); 876 } 877 mDatagramController.updateSendStatus(subId, 878 mLastSendRequestDatagramType, 879 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 880 0, SatelliteManager.SATELLITE_RESULT_SUCCESS); 881 abortSendingPendingDatagrams(subId, 882 SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED); 883 884 stopSatelliteAlignedTimer(); 885 stopDatagramWaitForConnectedStateTimer(); 886 stopWaitForDatagramSendingResponseTimer(); 887 stopWaitForSimulatedPollDatagramsDelayTimer(); 888 mIsDemoMode = false; 889 mSendSatelliteDatagramRequest = null; 890 mIsAligned = false; 891 mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN; 892 mModemState = SATELLITE_MODEM_STATE_UNKNOWN; 893 mHasEnteredConnectedState = false; 894 mShouldPollMtSms = false; 895 mConnectedStateCounter = 0; 896 stopMtSmsPollingThrottle(); 897 } 898 899 /** @return {@code true} if already sent an emergency datagram during a session. */ isEmergencyCommunicationEstablished()900 public boolean isEmergencyCommunicationEstablished() { 901 synchronized (mLock) { 902 return mIsEmergencyCommunicationEstablished; 903 } 904 } 905 startDatagramWaitForConnectedStateTimer( @atelliteManager.DatagramType int datagramType)906 private void startDatagramWaitForConnectedStateTimer( 907 @SatelliteManager.DatagramType int datagramType) { 908 if (isDatagramWaitForConnectedStateTimerStarted()) { 909 plogd("DatagramWaitForConnectedStateTimer is already started"); 910 return; 911 } 912 sendMessageDelayed(obtainMessage( 913 EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT, datagramType), 914 mDatagramController.getDatagramWaitTimeForConnectedState( 915 SatelliteServiceUtils.isLastSosMessage(datagramType))); 916 } 917 stopDatagramWaitForConnectedStateTimer()918 private void stopDatagramWaitForConnectedStateTimer() { 919 removeMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT); 920 } 921 922 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) isDatagramWaitForConnectedStateTimerStarted()923 public boolean isDatagramWaitForConnectedStateTimerStarted() { 924 return hasMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT); 925 } 926 927 /** 928 * This API is used by CTS tests to override the mWaitTimeForDatagramSendingResponse. 929 */ setWaitTimeForDatagramSendingResponse(boolean reset, long timeoutMillis)930 void setWaitTimeForDatagramSendingResponse(boolean reset, long timeoutMillis) { 931 if (reset) { 932 mWaitTimeForDatagramSendingResponse = getWaitForDatagramSendingResponseTimeoutMillis(); 933 } else { 934 mWaitTimeForDatagramSendingResponse = timeoutMillis; 935 } 936 } 937 startWaitForDatagramSendingResponseTimer( @onNull SendSatelliteDatagramArgument argument)938 private void startWaitForDatagramSendingResponseTimer( 939 @NonNull SendSatelliteDatagramArgument argument) { 940 if (hasMessages(EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT)) { 941 plogd("WaitForDatagramSendingResponseTimer was already started"); 942 return; 943 } 944 long waitTime = SatelliteServiceUtils.isLastSosMessage(argument.datagramType) 945 ? mWaitTimeForDatagramSendingForLastMessageResponse 946 : mWaitTimeForDatagramSendingResponse; 947 logd("startWaitForDatagramSendingResponseTimer: datagramType=" + argument.datagramType 948 + ", waitTime=" + waitTime); 949 sendMessageDelayed(obtainMessage( 950 EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT, argument), waitTime); 951 } 952 stopWaitForDatagramSendingResponseTimer()953 private void stopWaitForDatagramSendingResponseTimer() { 954 removeMessages(EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT); 955 } 956 handleEventDatagramWaitForConnectedStateTimedOut( @atelliteManager.DatagramType int datagramType)957 private void handleEventDatagramWaitForConnectedStateTimedOut( 958 @SatelliteManager.DatagramType int datagramType) { 959 plogw("Timed out to wait for satellite connected before sending datagrams"); 960 synchronized (mLock) { 961 int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); 962 // Update send status 963 mDatagramController.updateSendStatus(subId, 964 datagramType, 965 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 966 getPendingMessagesCount(), 967 SATELLITE_RESULT_NOT_REACHABLE); 968 mDatagramController.updateSendStatus(subId, 969 datagramType, 970 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 971 0, SatelliteManager.SATELLITE_RESULT_SUCCESS); 972 abortSendingPendingDatagrams(subId, 973 SATELLITE_RESULT_NOT_REACHABLE); 974 } 975 } 976 shouldSendDatagramToModemInDemoMode()977 private boolean shouldSendDatagramToModemInDemoMode() { 978 if (mShouldSendDatagramToModemInDemoMode != null) { 979 return mShouldSendDatagramToModemInDemoMode.get(); 980 } 981 982 try { 983 mShouldSendDatagramToModemInDemoMode = new AtomicBoolean( 984 mContext.getResources().getBoolean( 985 R.bool.config_send_satellite_datagram_to_modem_in_demo_mode)); 986 return mShouldSendDatagramToModemInDemoMode.get(); 987 988 } catch (Resources.NotFoundException ex) { 989 ploge("shouldSendDatagramToModemInDemoMode: id= " 990 + R.bool.config_send_satellite_datagram_to_modem_in_demo_mode + ", ex=" + ex); 991 return false; 992 } 993 } 994 getWaitForDatagramSendingResponseTimeoutMillis()995 private long getWaitForDatagramSendingResponseTimeoutMillis() { 996 return mContext.getResources().getInteger( 997 R.integer.config_wait_for_datagram_sending_response_timeout_millis); 998 } 999 getWaitForDatagramSendingResponseForLastMessageTimeoutMillis()1000 private long getWaitForDatagramSendingResponseForLastMessageTimeoutMillis() { 1001 return mContext.getResources().getInteger(R.integer 1002 .config_wait_for_datagram_sending_response_for_last_message_timeout_millis); 1003 } 1004 shouldProcessEventSendSatelliteDatagramDone( @onNull SendSatelliteDatagramArgument argument)1005 private boolean shouldProcessEventSendSatelliteDatagramDone( 1006 @NonNull SendSatelliteDatagramArgument argument) { 1007 synchronized (mLock) { 1008 if (SatelliteServiceUtils.isSosMessage(argument.datagramType)) { 1009 return mPendingEmergencyDatagramsMap.containsKey(argument.datagramId); 1010 } else { 1011 return mPendingNonEmergencyDatagramsMap.containsKey(argument.datagramId); 1012 } 1013 } 1014 } 1015 handleEventWaitForDatagramSendingResponseTimedOut( @onNull SendSatelliteDatagramArgument argument)1016 private void handleEventWaitForDatagramSendingResponseTimedOut( 1017 @NonNull SendSatelliteDatagramArgument argument) { 1018 synchronized (mLock) { 1019 plogw("Timed out to wait for the response of the request to send the datagram " 1020 + argument.datagramId); 1021 1022 // Ask vendor service to abort all datagram-sending requests 1023 SatelliteModemInterface.getInstance().abortSendingSatelliteDatagrams( 1024 obtainMessage(EVENT_ABORT_SENDING_SATELLITE_DATAGRAMS_DONE, argument)); 1025 mSendingInProgress = false; 1026 1027 // Update send status 1028 mDatagramController.updateSendStatus(argument.subId, argument.datagramType, 1029 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 1030 getPendingMessagesCount(), SATELLITE_RESULT_MODEM_TIMEOUT); 1031 mDatagramController.updateSendStatus(argument.subId, argument.datagramType, 1032 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 1033 0, SatelliteManager.SATELLITE_RESULT_SUCCESS); 1034 1035 // Send response for current datagram after updating datagram transfer state 1036 // internally. 1037 argument.callback.accept(SATELLITE_RESULT_MODEM_TIMEOUT); 1038 1039 // Log metrics about the outgoing datagram 1040 reportSendDatagramCompleted(argument, SATELLITE_RESULT_MODEM_TIMEOUT); 1041 // Remove current datagram from pending map. 1042 if (SatelliteServiceUtils.isSosMessage(argument.datagramType)) { 1043 mPendingEmergencyDatagramsMap.remove(argument.datagramId); 1044 } else { 1045 mPendingNonEmergencyDatagramsMap.remove(argument.datagramId); 1046 } 1047 1048 // Abort sending all the pending datagrams 1049 abortSendingPendingDatagrams(argument.subId, SATELLITE_RESULT_MODEM_TIMEOUT); 1050 } 1051 } 1052 1053 /** 1054 * This API can be used by only CTS to override the cached value for the device overlay config 1055 * value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether 1056 * outgoing satellite datagrams should be sent to modem in demo mode. 1057 * 1058 * @param shouldSendToModemInDemoMode Whether send datagram in demo mode should be sent to 1059 * satellite modem or not. If it is null, the cache will be cleared. 1060 */ 1061 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setShouldSendDatagramToModemInDemoMode( @ullable Boolean shouldSendToModemInDemoMode)1062 protected void setShouldSendDatagramToModemInDemoMode( 1063 @Nullable Boolean shouldSendToModemInDemoMode) { 1064 plogd("setShouldSendDatagramToModemInDemoMode(" + (shouldSendToModemInDemoMode == null 1065 ? "null" : shouldSendToModemInDemoMode) + ")"); 1066 1067 if (shouldSendToModemInDemoMode == null) { 1068 mShouldSendDatagramToModemInDemoMode = null; 1069 } else { 1070 if (mShouldSendDatagramToModemInDemoMode == null) { 1071 mShouldSendDatagramToModemInDemoMode = new AtomicBoolean( 1072 shouldSendToModemInDemoMode); 1073 } else { 1074 mShouldSendDatagramToModemInDemoMode.set(shouldSendToModemInDemoMode); 1075 } 1076 } 1077 } 1078 startWaitForSimulatedPollDatagramsDelayTimer( @onNull DatagramDispatcherHandlerRequest request)1079 private void startWaitForSimulatedPollDatagramsDelayTimer( 1080 @NonNull DatagramDispatcherHandlerRequest request) { 1081 if (mIsDemoMode) { 1082 plogd("startWaitForSimulatedPollDatagramsDelayTimer"); 1083 sendMessageDelayed( 1084 obtainMessage(EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT, request), 1085 getDemoTimeoutDuration()); 1086 } else { 1087 plogd("Should not start WaitForSimulatedPollDatagramsDelayTimer in non-demo mode"); 1088 } 1089 } 1090 stopWaitForSimulatedPollDatagramsDelayTimer()1091 private void stopWaitForSimulatedPollDatagramsDelayTimer() { 1092 removeMessages(EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT); 1093 } 1094 handleEventWaitForSimulatedPollDatagramsDelayTimedOut( @onNull SendSatelliteDatagramArgument argument)1095 private void handleEventWaitForSimulatedPollDatagramsDelayTimedOut( 1096 @NonNull SendSatelliteDatagramArgument argument) { 1097 if (mIsDemoMode) { 1098 plogd("handleEventWaitForSimulatedPollDatagramsDelayTimedOut"); 1099 mDatagramController.pushDemoModeDatagram(argument.datagramType, argument.datagram); 1100 Consumer<Integer> internalCallback = new Consumer<Integer>() { 1101 @Override 1102 public void accept(Integer result) { 1103 plogd("pollPendingSatelliteDatagrams result: " + result); 1104 } 1105 }; 1106 mDatagramController.pollPendingSatelliteDatagrams(argument.subId, internalCallback); 1107 } else { 1108 plogd("Unexpected EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT in " 1109 + "non-demo mode"); 1110 } 1111 } 1112 getDemoTimeoutDuration()1113 long getDemoTimeoutDuration() { 1114 return mDemoTimeoutDuration; 1115 } 1116 1117 /** 1118 * This API is used by CTS tests to override the mDemoTimeoutDuration. 1119 */ setTimeoutDatagramDelayInDemoMode(boolean reset, long timeoutMillis)1120 void setTimeoutDatagramDelayInDemoMode(boolean reset, long timeoutMillis) { 1121 if (!mIsDemoMode) { 1122 return; 1123 } 1124 if (reset) { 1125 mDemoTimeoutDuration = TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE; 1126 } else { 1127 mDemoTimeoutDuration = timeoutMillis; 1128 } 1129 plogd("setTimeoutDatagramDelayInDemoMode " + mDemoTimeoutDuration + " reset=" + reset); 1130 } 1131 1132 /** 1133 * Send carrier roaming nb iot ntn sms. 1134 * 1135 * Store SMS in a pending list until following conditions are met: 1136 * - If messages can be sent only when satellite is connected, then wait until modem state 1137 * becomes {@link SatelliteManager#SATELLITE_MODEM_STATE_CONNECTED} 1138 * - If modem is already sending datagrms/SMS or receiving datagrams, then wait until modem 1139 * becomes IDLE to send current SMS. 1140 * 1141 * @param pendingSms {@link PendingRequest} that contains all the information required to send 1142 * carrier roaming nb iot ntn SMS. 1143 */ sendSms(@onNull PendingRequest pendingSms)1144 public void sendSms(@NonNull PendingRequest pendingSms) { 1145 SatelliteController.getInstance().startPointingUI(); 1146 1147 int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); 1148 long messageId = pendingSms.uniqueMessageId; 1149 plogd("sendSms: subId=" + subId + " messageId:" + messageId); 1150 1151 synchronized (mLock) { 1152 // Add SMS to pending list 1153 mPendingSmsMap.put(messageId, pendingSms); 1154 int datagramType = pendingSms.isMtSmsPolling ? 1155 DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS; 1156 mLastSendRequestDatagramType = datagramType; 1157 1158 if (mDatagramController.needsWaitingForSatelliteConnected(datagramType)) { 1159 plogd("sendSms: wait for satellite connected"); 1160 mDatagramController.updateSendStatus(subId, datagramType, 1161 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, 1162 getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 1163 startDatagramWaitForConnectedStateTimer(datagramType); 1164 } else if (!mSendingInProgress && mDatagramController.isPollingInIdleState()) { 1165 mSendingInProgress = true; 1166 mDatagramController.updateSendStatus(subId, datagramType, 1167 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, 1168 getPendingMessagesCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 1169 1170 sendMessage(obtainMessage(CMD_SEND_SMS, pendingSms)); 1171 } else { 1172 plogd("sendSms: mSendingInProgress=" + mSendingInProgress 1173 + ", isPollingInIdleState=" + mDatagramController.isPollingInIdleState()); 1174 } 1175 } 1176 } 1177 1178 @GuardedBy("mLock") sendPendingSms()1179 private void sendPendingSms() { 1180 plogd("sendPendingSms"); 1181 if (!mDatagramController.isPollingInIdleState()) { 1182 // Datagram or SMS should be sent to satellite modem when modem is free. 1183 plogd("sendPendingSms: modem is receiving datagrams"); 1184 return; 1185 } 1186 1187 int subId = SatelliteController.getInstance().getSelectedSatelliteSubId(); 1188 Set<Entry<Long, PendingRequest>> pendingSms = null; 1189 if (!mSendingInProgress) { 1190 pendingSms = mPendingSmsMap.entrySet(); 1191 } 1192 1193 if (pendingSms != null && pendingSms.iterator().hasNext()) { 1194 PendingRequest pendingRequest = pendingSms.iterator().next().getValue(); 1195 int datagramType = pendingRequest.isMtSmsPolling 1196 ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS; 1197 if (mDatagramController.needsWaitingForSatelliteConnected(DATAGRAM_TYPE_SMS)) { 1198 plogd("sendPendingSms: wait for satellite connected"); 1199 mDatagramController.updateSendStatus(subId, 1200 datagramType, 1201 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, 1202 getPendingMessagesCount(), 1203 SatelliteManager.SATELLITE_RESULT_SUCCESS); 1204 startDatagramWaitForConnectedStateTimer(datagramType); 1205 return; 1206 } 1207 1208 mSendingInProgress = true; 1209 mDatagramController.updateSendStatus(subId, 1210 datagramType, 1211 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, 1212 getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS); 1213 sendMessage(obtainMessage(CMD_SEND_SMS, pendingRequest)); 1214 } else { 1215 plogd("sendPendingSms: mSendingInProgress=" + mSendingInProgress 1216 + " pendingSmsCount=" + getPendingSmsCount()); 1217 } 1218 } 1219 1220 /** 1221 * Sending MO SMS is completed. 1222 * @param subId subscription ID 1223 * @param messageId message ID of MO SMS 1224 * @param success boolean specifying whether MO SMS is successfully sent or not. 1225 */ onSendSmsDone(int subId, long messageId, boolean success)1226 public void onSendSmsDone(int subId, long messageId, boolean success) { 1227 SomeArgs args = SomeArgs.obtain(); 1228 args.arg1 = subId; 1229 args.arg2 = messageId; 1230 args.arg3 = success; 1231 sendMessage(obtainMessage(EVENT_SEND_SMS_DONE, args)); 1232 } 1233 1234 @GuardedBy("mLock") sendErrorCodeAndCleanupPendingSms( LinkedHashMap<Long, PendingRequest> pendingSmsMap, @SatelliteManager.SatelliteResult int errorCode)1235 private void sendErrorCodeAndCleanupPendingSms( 1236 LinkedHashMap<Long, PendingRequest> pendingSmsMap, 1237 @SatelliteManager.SatelliteResult int errorCode) { 1238 if (pendingSmsMap.size() == 0) { 1239 plogd("sendErrorCodeAndCleanupPendingSms: pendingSmsMap is empty."); 1240 return; 1241 } 1242 ploge("sendErrorCodeAndCleanupPendingSms: cleaning up resources. " 1243 + "pendingSmsMap size=" + getPendingSmsCount()); 1244 1245 Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone(); 1246 if (satellitePhone == null) { 1247 ploge("sendErrorCodeAndCleanupPendingSms: satellitePhone is null."); 1248 pendingSmsMap.clear(); 1249 return; 1250 } 1251 1252 SmsDispatchersController smsDispatchersController = 1253 satellitePhone.getSmsDispatchersController(); 1254 if (smsDispatchersController == null) { 1255 ploge("sendErrorCodeAndCleanupPendingSms: smsDispatchersController is null."); 1256 pendingSmsMap.clear(); 1257 return; 1258 } 1259 1260 // Send error code to all the pending text 1261 for (Entry<Long, PendingRequest> entry : pendingSmsMap.entrySet()) { 1262 PendingRequest pendingRequest = entry.getValue(); 1263 smsDispatchersController.onSendCarrierRoamingNbIotNtnTextError( 1264 pendingRequest, errorCode); 1265 reportSendSmsCompleted(pendingRequest, errorCode); 1266 } 1267 1268 // Clear pending text map 1269 pendingSmsMap.clear(); 1270 } 1271 handleEventSendSmsDone(int subId, long messageId, boolean success)1272 private void handleEventSendSmsDone(int subId, long messageId, boolean success) { 1273 synchronized (mLock) { 1274 PendingRequest pendingSms = mPendingSmsMap.remove(messageId); 1275 if (pendingSms == null) { 1276 // Just return, the SMS is not sent by DatagramDispatcher such as Data SMS 1277 plogd("handleEventSendSmsDone there is no pendingSms for messageId=" + messageId); 1278 return; 1279 } 1280 1281 mSendingInProgress = false; 1282 int datagramType = pendingSms.isMtSmsPolling 1283 ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS; 1284 1285 plogd("handleEventSendSmsDone subId=" + subId + " messageId=" + messageId 1286 + " success=" + success + " datagramType=" + datagramType); 1287 1288 if (success) { 1289 // Update send status 1290 mDatagramController.updateSendStatus(subId, datagramType, 1291 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS, 1292 getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS); 1293 reportSendSmsCompleted(pendingSms, SATELLITE_RESULT_SUCCESS); 1294 if (datagramType == DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS) { 1295 startMtSmsPollingThrottle(); 1296 mShouldPollMtSms = false; 1297 } 1298 } else { 1299 // Update send status 1300 mDatagramController.updateSendStatus(subId, datagramType, 1301 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 1302 getPendingMessagesCount(), SATELLITE_RESULT_NETWORK_ERROR); 1303 reportSendSmsCompleted(pendingSms, SATELLITE_RESULT_NETWORK_ERROR); 1304 } 1305 1306 if (getPendingMessagesCount() > 0) { 1307 sendPendingMessages(); 1308 } else { 1309 mDatagramController.updateSendStatus(subId, datagramType, 1310 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0, 1311 SatelliteManager.SATELLITE_RESULT_SUCCESS); 1312 } 1313 } 1314 } 1315 isEnabledMtSmsPolling()1316 private boolean isEnabledMtSmsPolling() { 1317 return mContext.getResources().getBoolean(R.bool.config_enabled_mt_sms_polling); 1318 } 1319 getMtSmsPollingThrottleMillis()1320 private long getMtSmsPollingThrottleMillis() { 1321 return mContext.getResources().getInteger( 1322 R.integer.config_mt_sms_polling_throttle_millis); 1323 } 1324 shouldPollMtSms()1325 private boolean shouldPollMtSms() { 1326 SatelliteController satelliteController = SatelliteController.getInstance(); 1327 Phone satellitePhone = satelliteController.getSatellitePhone(); 1328 return isEnabledMtSmsPolling() 1329 && satelliteController.shouldSendSmsToDatagramDispatcher(satellitePhone); 1330 } 1331 handleCmdSendMtSmsPollingMessage()1332 private void handleCmdSendMtSmsPollingMessage() { 1333 synchronized (mLock) { 1334 if (!mShouldPollMtSms) { 1335 plogd("sendMtSmsPollingMessage: mShouldPollMtSms=" + mShouldPollMtSms); 1336 return; 1337 } 1338 1339 plogd("sendMtSmsPollingMessage"); 1340 if (!allowCheckMessageInNotConnected()) { 1341 mShouldPollMtSms = false; 1342 } 1343 1344 for (Entry<Long, PendingRequest> entry : mPendingSmsMap.entrySet()) { 1345 PendingRequest pendingRequest = entry.getValue(); 1346 if (pendingRequest.isMtSmsPolling) { 1347 plogd("sendMtSmsPollingMessage: mPendingSmsMap already " 1348 + "has the polling message."); 1349 return; 1350 } 1351 } 1352 } 1353 1354 Phone satellitePhone = SatelliteController.getInstance().getSatellitePhone(); 1355 if (satellitePhone == null) { 1356 ploge("sendMtSmsPollingMessage: satellitePhone is null."); 1357 return; 1358 } 1359 1360 SmsDispatchersController smsDispatchersController = 1361 satellitePhone.getSmsDispatchersController(); 1362 if (smsDispatchersController == null) { 1363 ploge("sendMtSmsPollingMessage: smsDispatchersController is null."); 1364 return; 1365 } 1366 1367 smsDispatchersController.sendMtSmsPollingMessage(); 1368 } 1369 1370 @GuardedBy("mLock") startMtSmsPollingThrottle()1371 private void startMtSmsPollingThrottle() { 1372 plogd("startMtSmsPollingThrottle"); 1373 mIsMtSmsPollingThrottled = true; 1374 sendMessageDelayed(obtainMessage(EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT), 1375 getMtSmsPollingThrottleMillis()); 1376 } 1377 1378 @GuardedBy("mLock") stopMtSmsPollingThrottle()1379 private void stopMtSmsPollingThrottle() { 1380 mIsMtSmsPollingThrottled = false; 1381 removeMessages(EVENT_MT_SMS_POLLING_THROTTLE_TIMED_OUT); 1382 } 1383 allowMtSmsPolling()1384 private boolean allowMtSmsPolling() { 1385 SatelliteController satelliteController = SatelliteController.getInstance(); 1386 int subId = satelliteController.getSelectedSatelliteSubId(); 1387 boolean isP2PSmsDisallowed = 1388 satelliteController.isP2PSmsDisallowedOnCarrierRoamingNtn(subId); 1389 if (isP2PSmsDisallowed) { 1390 plogd("allowMtSmsPolling: P2P SMS disallowed, subId = " + subId); 1391 return false; 1392 } 1393 1394 boolean isModemStateConnectedOrTransferring; 1395 boolean isAligned; 1396 boolean isMtSmsPollingThrottled; 1397 synchronized (mLock) { 1398 isMtSmsPollingThrottled = mIsMtSmsPollingThrottled; 1399 isAligned = mIsAligned; 1400 isModemStateConnectedOrTransferring = 1401 mModemState == SATELLITE_MODEM_STATE_CONNECTED 1402 || mModemState == SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING; 1403 } 1404 1405 if (isMtSmsPollingThrottled) { 1406 plogd("allowMtSmsPolling: polling is throttled"); 1407 return false; 1408 } 1409 1410 if (!isAligned) { 1411 plogd("allowMtSmsPolling: not aligned"); 1412 return false; 1413 } 1414 1415 if (!isModemStateConnectedOrTransferring && !allowCheckMessageInNotConnected()) { 1416 plogd("allowMtSmsPolling: not in service and " 1417 + "allow_check_message_in_not_connected is disabled"); 1418 return false; 1419 } 1420 1421 plogd("allowMtSmsPolling: return true"); 1422 return true; 1423 } 1424 allowCheckMessageInNotConnected()1425 private boolean allowCheckMessageInNotConnected() { 1426 return mContext.getResources() 1427 .getBoolean(R.bool.config_satellite_allow_check_message_in_not_connected); 1428 } 1429 logd(@onNull String log)1430 private static void logd(@NonNull String log) { 1431 Log.d(TAG, log); 1432 } 1433 loge(@onNull String log)1434 private static void loge(@NonNull String log) { 1435 Log.e(TAG, log); 1436 } 1437 logw(@onNull String log)1438 private static void logw(@NonNull String log) { Log.w(TAG, log); } 1439 plogd(@onNull String log)1440 private void plogd(@NonNull String log) { 1441 Log.d(TAG, log); 1442 if (mPersistentLogger != null) { 1443 mPersistentLogger.debug(TAG, log); 1444 } 1445 } 1446 plogw(@onNull String log)1447 private void plogw(@NonNull String log) { 1448 Log.w(TAG, log); 1449 if (mPersistentLogger != null) { 1450 mPersistentLogger.warn(TAG, log); 1451 } 1452 } 1453 ploge(@onNull String log)1454 private void ploge(@NonNull String log) { 1455 Log.e(TAG, log); 1456 if (mPersistentLogger != null) { 1457 mPersistentLogger.error(TAG, log); 1458 } 1459 } 1460 updateSessionStatsWithPendingUserMsgCount(SatelliteSessionStats datagramStats)1461 public void updateSessionStatsWithPendingUserMsgCount(SatelliteSessionStats datagramStats) { 1462 synchronized (mLock) { 1463 Log.d("SessionMetricsStats1", 1464 " mPendingEmergencyDatagramsMap size = " 1465 + mPendingEmergencyDatagramsMap.size()); 1466 Log.d("SessionMetricsStats1", " mPendingNonEmergencyDatagramsMap size = " 1467 + mPendingNonEmergencyDatagramsMap.size()); 1468 Log.d("SessionMetricsStats1", " mPendingSmsMap size = " 1469 + mPendingSmsMap.size()); 1470 for (Entry<Long, SendSatelliteDatagramArgument> entry : 1471 mPendingEmergencyDatagramsMap.entrySet()) { 1472 SendSatelliteDatagramArgument argument = entry.getValue(); 1473 Log.d("SessionMetricsStats1", "DataGramType1 = " 1474 + argument.datagramType); 1475 datagramStats.updateCountOfUserMessagesInQueueToBeSent(argument.datagramType); 1476 } 1477 for (Entry<Long, SendSatelliteDatagramArgument> entry : 1478 mPendingNonEmergencyDatagramsMap.entrySet()) { 1479 SendSatelliteDatagramArgument argument = entry.getValue(); 1480 Log.d("SessionMetricsStats1", "DataGramType2 = " 1481 + argument.datagramType); 1482 datagramStats.updateCountOfUserMessagesInQueueToBeSent(argument.datagramType); 1483 } 1484 for (Entry<Long, PendingRequest> entry : mPendingSmsMap.entrySet()) { 1485 PendingRequest pendingRequest = entry.getValue(); 1486 int datagramType = pendingRequest.isMtSmsPolling 1487 ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS; 1488 Log.d("SessionMetricsStats1", "DataGramType3 = " + datagramType); 1489 datagramStats.updateCountOfUserMessagesInQueueToBeSent(datagramType); 1490 } 1491 } 1492 } 1493 } 1494