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_UNKNOWN; 20 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED; 21 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT; 22 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE; 23 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS; 24 25 import static com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT; 26 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.content.Context; 30 import android.content.res.Resources; 31 import android.os.AsyncResult; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.telephony.DropBoxManagerLoggerBackend; 36 import android.telephony.PersistentLogger; 37 import android.telephony.Rlog; 38 import android.telephony.SubscriptionManager; 39 import android.telephony.satellite.SatelliteDatagram; 40 import android.telephony.satellite.SatelliteManager; 41 42 import com.android.internal.R; 43 import com.android.internal.annotations.GuardedBy; 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.telephony.Phone; 46 import com.android.internal.telephony.flags.FeatureFlags; 47 import com.android.internal.telephony.metrics.SatelliteStats; 48 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats; 49 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats; 50 51 import java.util.LinkedHashMap; 52 import java.util.Map.Entry; 53 import java.util.Set; 54 import java.util.concurrent.TimeUnit; 55 import java.util.concurrent.atomic.AtomicBoolean; 56 import java.util.concurrent.atomic.AtomicLong; 57 import java.util.function.Consumer; 58 59 /** 60 * Datagram dispatcher used to send satellite datagrams. 61 */ 62 public class DatagramDispatcher extends Handler { 63 private static final String TAG = "DatagramDispatcher"; 64 65 private static final int CMD_SEND_SATELLITE_DATAGRAM = 1; 66 private static final int EVENT_SEND_SATELLITE_DATAGRAM_DONE = 2; 67 private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3; 68 private static final int EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT = 4; 69 private static final int EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT = 5; 70 private static final int EVENT_ABORT_SENDING_SATELLITE_DATAGRAMS_DONE = 6; 71 private static final int EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT = 7; 72 private static final Long TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE = TimeUnit.SECONDS.toMillis(10); 73 @NonNull private static DatagramDispatcher sInstance; 74 @NonNull private final Context mContext; 75 @NonNull private final DatagramController mDatagramController; 76 @NonNull private final ControllerMetricsStats mControllerMetricsStats; 77 @NonNull private final SessionMetricsStats mSessionMetricsStats; 78 @NonNull private final FeatureFlags mFeatureFlags; 79 80 private boolean mIsDemoMode = false; 81 private boolean mIsAligned = false; 82 private DatagramDispatcherHandlerRequest mSendSatelliteDatagramRequest = null; 83 84 private static AtomicLong mNextDatagramId = new AtomicLong(0); 85 86 private AtomicBoolean mShouldSendDatagramToModemInDemoMode = null; 87 88 private final Object mLock = new Object(); 89 private long mDemoTimeoutDuration = TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE; 90 91 @GuardedBy("mLock") 92 private boolean mSendingDatagramInProgress; 93 94 /** 95 * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending emergency 96 * datagrams. 97 */ 98 @GuardedBy("mLock") 99 private final LinkedHashMap<Long, SendSatelliteDatagramArgument> 100 mPendingEmergencyDatagramsMap = new LinkedHashMap<>(); 101 102 /** 103 * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending non-emergency 104 * datagrams. 105 */ 106 @GuardedBy("mLock") 107 private final LinkedHashMap<Long, SendSatelliteDatagramArgument> 108 mPendingNonEmergencyDatagramsMap = new LinkedHashMap<>(); 109 110 private long mWaitTimeForDatagramSendingResponse; 111 private long mWaitTimeForDatagramSendingForLastMessageResponse; 112 @SatelliteManager.DatagramType 113 private int mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN; 114 @Nullable private PersistentLogger mPersistentLogger = null; 115 116 /** 117 * Create the DatagramDispatcher singleton instance. 118 * @param context The Context to use to create the DatagramDispatcher. 119 * @param looper The looper for the handler. 120 * @param featureFlags The telephony feature flags. 121 * @param datagramController DatagramController which is used to update datagram transfer state. 122 * @return The singleton instance of DatagramDispatcher. 123 */ make(@onNull Context context, @NonNull Looper looper, @NonNull FeatureFlags featureFlags, @NonNull DatagramController datagramController)124 public static DatagramDispatcher make(@NonNull Context context, @NonNull Looper looper, 125 @NonNull FeatureFlags featureFlags, 126 @NonNull DatagramController datagramController) { 127 if (sInstance == null) { 128 sInstance = new DatagramDispatcher(context, looper, featureFlags, datagramController); 129 } 130 return sInstance; 131 } 132 133 /** 134 * @return The singleton instance of DatagramDispatcher. 135 */ getInstance()136 public static DatagramDispatcher getInstance() { 137 if (sInstance == null) { 138 loge("DatagramDispatcher was not yet initialized."); 139 } 140 return sInstance; 141 } 142 143 /** 144 * Create a DatagramDispatcher to send satellite datagrams. 145 * 146 * @param context The Context for the DatagramDispatcher. 147 * @param looper The looper for the handler. 148 * @param featureFlags The telephony feature flags. 149 * @param datagramController DatagramController which is used to update datagram transfer state. 150 */ 151 @VisibleForTesting DatagramDispatcher(@onNull Context context, @NonNull Looper looper, @NonNull FeatureFlags featureFlags, @NonNull DatagramController datagramController)152 protected DatagramDispatcher(@NonNull Context context, @NonNull Looper looper, 153 @NonNull FeatureFlags featureFlags, 154 @NonNull DatagramController datagramController) { 155 super(looper); 156 mContext = context; 157 mFeatureFlags = featureFlags; 158 mDatagramController = datagramController; 159 mControllerMetricsStats = ControllerMetricsStats.getInstance(); 160 mSessionMetricsStats = SessionMetricsStats.getInstance(); 161 if (isSatellitePersistentLoggingEnabled(context, featureFlags)) { 162 mPersistentLogger = new PersistentLogger( 163 DropBoxManagerLoggerBackend.getInstance(context)); 164 } 165 166 synchronized (mLock) { 167 mSendingDatagramInProgress = false; 168 } 169 mWaitTimeForDatagramSendingResponse = getWaitForDatagramSendingResponseTimeoutMillis(); 170 mWaitTimeForDatagramSendingForLastMessageResponse = 171 getWaitForDatagramSendingResponseForLastMessageTimeoutMillis(); 172 } 173 174 private static final class DatagramDispatcherHandlerRequest { 175 /** The argument to use for the request */ 176 public @NonNull Object argument; 177 /** The caller needs to specify the phone to be used for the request */ 178 public @NonNull Phone phone; 179 /** The result of the request that is run on the main thread */ 180 public @Nullable Object result; 181 DatagramDispatcherHandlerRequest(Object argument, Phone phone)182 DatagramDispatcherHandlerRequest(Object argument, Phone phone) { 183 this.argument = argument; 184 this.phone = phone; 185 } 186 } 187 188 private static final class SendSatelliteDatagramArgument { 189 public int subId; 190 public long datagramId; 191 public @SatelliteManager.DatagramType int datagramType; 192 public @NonNull SatelliteDatagram datagram; 193 public boolean needFullScreenPointingUI; 194 public @NonNull Consumer<Integer> callback; 195 public long datagramStartTime; 196 public boolean skipCheckingSatelliteAligned = false; 197 SendSatelliteDatagramArgument(int subId, long datagramId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)198 SendSatelliteDatagramArgument(int subId, long datagramId, 199 @SatelliteManager.DatagramType int datagramType, 200 @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, 201 @NonNull Consumer<Integer> callback) { 202 this.subId = subId; 203 this.datagramId = datagramId; 204 this.datagramType = datagramType; 205 this.datagram = datagram; 206 this.needFullScreenPointingUI = needFullScreenPointingUI; 207 this.callback = callback; 208 } 209 210 /** returns the size of outgoing SMS, rounded by 10 bytes */ getDatagramRoundedSizeBytes()211 public int getDatagramRoundedSizeBytes() { 212 if (datagram.getSatelliteDatagram() != null) { 213 int sizeBytes = datagram.getSatelliteDatagram().length; 214 // rounded by ROUNDING_UNIT 215 return (int) (Math.round((double) sizeBytes / ROUNDING_UNIT) * ROUNDING_UNIT); 216 } else { 217 return 0; 218 } 219 } 220 221 /** sets the start time at datagram is sent out */ setDatagramStartTime()222 public void setDatagramStartTime() { 223 datagramStartTime = 224 datagramStartTime == 0 ? System.currentTimeMillis() : datagramStartTime; 225 } 226 } 227 228 @Override handleMessage(Message msg)229 public void handleMessage(Message msg) { 230 DatagramDispatcherHandlerRequest request; 231 Message onCompleted; 232 AsyncResult ar; 233 234 switch(msg.what) { 235 case CMD_SEND_SATELLITE_DATAGRAM: { 236 plogd("CMD_SEND_SATELLITE_DATAGRAM mIsDemoMode=" + mIsDemoMode 237 + ", shouldSendDatagramToModemInDemoMode=" 238 + shouldSendDatagramToModemInDemoMode()); 239 request = (DatagramDispatcherHandlerRequest) msg.obj; 240 SendSatelliteDatagramArgument argument = 241 (SendSatelliteDatagramArgument) request.argument; 242 onCompleted = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request); 243 244 synchronized (mLock) { 245 if (mIsDemoMode && !shouldSendDatagramToModemInDemoMode()) { 246 AsyncResult.forMessage(onCompleted, SATELLITE_RESULT_SUCCESS, null); 247 sendMessageDelayed(onCompleted, getDemoTimeoutDuration()); 248 } else { 249 SatelliteModemInterface.getInstance().sendSatelliteDatagram( 250 argument.datagram, 251 SatelliteServiceUtils.isSosMessage(argument.datagramType), 252 argument.needFullScreenPointingUI, onCompleted); 253 startWaitForDatagramSendingResponseTimer(argument); 254 } 255 } 256 break; 257 } 258 case EVENT_SEND_SATELLITE_DATAGRAM_DONE: { 259 ar = (AsyncResult) msg.obj; 260 request = (DatagramDispatcherHandlerRequest) ar.userObj; 261 int error = SatelliteServiceUtils.getSatelliteError(ar, "sendDatagram"); 262 SendSatelliteDatagramArgument argument = 263 (SendSatelliteDatagramArgument) request.argument; 264 265 synchronized (mLock) { 266 if (mIsDemoMode && (error == SatelliteManager.SATELLITE_RESULT_SUCCESS)) { 267 if (argument.skipCheckingSatelliteAligned) { 268 plogd("Satellite was already aligned. " 269 + "No need to check alignment again"); 270 } else if (mDatagramController.waitForAligningToSatellite(mIsAligned)) { 271 plogd("Satellite is not aligned in demo mode, wait for the alignment."); 272 startSatelliteAlignedTimer(request); 273 break; 274 } 275 } 276 plogd("EVENT_SEND_SATELLITE_DATAGRAM_DONE error: " + error 277 + ", mIsDemoMode=" + mIsDemoMode); 278 279 /* 280 * The response should be ignored if either of the following hold 281 * 1) Framework has already received this response from the vendor service. 282 * 2) Framework has timed out to wait for the response from vendor service for 283 * the send request. 284 * 3) All pending send requests have been aborted due to some error. 285 */ 286 if (!shouldProcessEventSendSatelliteDatagramDone(argument)) { 287 plogw("The message " + argument.datagramId + " was already processed"); 288 break; 289 } 290 291 stopWaitForDatagramSendingResponseTimer(); 292 mSendingDatagramInProgress = false; 293 294 // Log metrics about the outgoing datagram 295 reportSendDatagramCompleted(argument, error); 296 // Remove current datagram from pending map. 297 if (SatelliteServiceUtils.isSosMessage(argument.datagramType)) { 298 mPendingEmergencyDatagramsMap.remove(argument.datagramId); 299 } else { 300 mPendingNonEmergencyDatagramsMap.remove(argument.datagramId); 301 } 302 303 if (error == SATELLITE_RESULT_SUCCESS) { 304 // Update send status for current datagram 305 mDatagramController.updateSendStatus(argument.subId, argument.datagramType, 306 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS, 307 getPendingDatagramCount(), error); 308 startWaitForSimulatedPollDatagramsDelayTimer(request); 309 } else { 310 // Update send status 311 mDatagramController.updateSendStatus(argument.subId, argument.datagramType, 312 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 313 getPendingDatagramCount(), error); 314 } 315 316 if (getPendingDatagramCount() > 0) { 317 // Send response for current datagram 318 argument.callback.accept(error); 319 // Send pending datagrams 320 sendPendingDatagrams(); 321 } else { 322 mDatagramController.updateSendStatus(argument.subId, 323 argument.datagramType, 324 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 0, 325 SatelliteManager.SATELLITE_RESULT_SUCCESS); 326 // Send response for current datagram 327 argument.callback.accept(error); 328 } 329 } 330 break; 331 } 332 333 case EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT: 334 handleEventWaitForDatagramSendingResponseTimedOut( 335 (SendSatelliteDatagramArgument) msg.obj); 336 break; 337 338 case EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT: { 339 handleEventSatelliteAlignedTimeout((DatagramDispatcherHandlerRequest) msg.obj); 340 break; 341 } 342 343 case EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT: 344 handleEventDatagramWaitForConnectedStateTimedOut( 345 (SendSatelliteDatagramArgument) msg.obj); 346 break; 347 348 case EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT: 349 request = (DatagramDispatcherHandlerRequest) msg.obj; 350 handleEventWaitForSimulatedPollDatagramsDelayTimedOut( 351 (SendSatelliteDatagramArgument) request.argument); 352 break; 353 354 default: 355 plogw("DatagramDispatcherHandler: unexpected message code: " + msg.what); 356 break; 357 } 358 } 359 360 /** 361 * Send datagram over satellite. 362 * 363 * Gateway encodes SOS message or location sharing message into a datagram and passes it as 364 * input to this method. Datagram received here will be passed down to modem without any 365 * encoding or encryption. 366 * 367 * @param subId The subId of the subscription to send satellite datagrams for. 368 * @param datagramType datagram type indicating whether the datagram is of type 369 * SOS_SMS or LOCATION_SHARING. 370 * @param datagram encoded gateway datagram which is encrypted by the caller. 371 * Datagram will be passed down to modem without any encoding or encryption. 372 * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in 373 * full screen mode. 374 * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request. 375 */ sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)376 public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, 377 @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, 378 @NonNull Consumer<Integer> callback) { 379 Phone phone = SatelliteServiceUtils.getPhone(); 380 381 long datagramId = mNextDatagramId.getAndUpdate( 382 n -> ((n + 1) % DatagramController.MAX_DATAGRAM_ID)); 383 SendSatelliteDatagramArgument datagramArgs = 384 new SendSatelliteDatagramArgument(subId, datagramId, datagramType, datagram, 385 needFullScreenPointingUI, callback); 386 mLastSendRequestDatagramType = datagramType; 387 388 synchronized (mLock) { 389 // Add datagram to pending datagram map 390 if (SatelliteServiceUtils.isSosMessage(datagramType)) { 391 mPendingEmergencyDatagramsMap.put(datagramId, datagramArgs); 392 } else { 393 mPendingNonEmergencyDatagramsMap.put(datagramId, datagramArgs); 394 } 395 396 if (mDatagramController.needsWaitingForSatelliteConnected(datagramType)) { 397 plogd("sendDatagram: wait for satellite connected"); 398 mDatagramController.updateSendStatus(subId, datagramType, 399 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT, 400 getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 401 startDatagramWaitForConnectedStateTimer(datagramArgs); 402 } else if (!mSendingDatagramInProgress && mDatagramController.isPollingInIdleState()) { 403 // Modem can be busy receiving datagrams, so send datagram only when modem is 404 // not busy. 405 mSendingDatagramInProgress = true; 406 datagramArgs.setDatagramStartTime(); 407 mDatagramController.updateSendStatus(subId, datagramType, 408 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, 409 getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 410 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone); 411 } else { 412 plogd("sendDatagram: mSendingDatagramInProgress=" 413 + mSendingDatagramInProgress + ", isPollingInIdleState=" 414 + mDatagramController.isPollingInIdleState()); 415 } 416 } 417 } 418 retrySendingDatagrams()419 public void retrySendingDatagrams() { 420 synchronized (mLock) { 421 sendPendingDatagrams(); 422 } 423 } 424 425 /** Set demo mode 426 * 427 * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise. 428 */ 429 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setDemoMode(boolean isDemoMode)430 protected void setDemoMode(boolean isDemoMode) { 431 mIsDemoMode = isDemoMode; 432 plogd("setDemoMode: mIsDemoMode=" + mIsDemoMode); 433 } 434 435 /** 436 * Set whether the device is aligned with the satellite. 437 */ 438 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setDeviceAlignedWithSatellite(boolean isAligned)439 public void setDeviceAlignedWithSatellite(boolean isAligned) { 440 synchronized (mLock) { 441 mIsAligned = isAligned; 442 plogd("setDeviceAlignedWithSatellite: " + mIsAligned); 443 if (isAligned && mIsDemoMode) handleEventSatelliteAligned(); 444 } 445 } 446 startSatelliteAlignedTimer(@onNull DatagramDispatcherHandlerRequest request)447 private void startSatelliteAlignedTimer(@NonNull DatagramDispatcherHandlerRequest request) { 448 if (isSatelliteAlignedTimerStarted()) { 449 plogd("Satellite aligned timer was already started"); 450 return; 451 } 452 mSendSatelliteDatagramRequest = request; 453 sendMessageDelayed( 454 obtainMessage(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT, request), 455 getSatelliteAlignedTimeoutDuration()); 456 } 457 458 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) getSatelliteAlignedTimeoutDuration()459 protected long getSatelliteAlignedTimeoutDuration() { 460 return mDatagramController.getSatelliteAlignedTimeoutDuration(); 461 } 462 handleEventSatelliteAligned()463 private void handleEventSatelliteAligned() { 464 if (isSatelliteAlignedTimerStarted()) { 465 stopSatelliteAlignedTimer(); 466 467 if (mSendSatelliteDatagramRequest == null) { 468 ploge("handleEventSatelliteAligned: mSendSatelliteDatagramRequest is null"); 469 } else { 470 SendSatelliteDatagramArgument argument = 471 (SendSatelliteDatagramArgument) mSendSatelliteDatagramRequest.argument; 472 argument.skipCheckingSatelliteAligned = true; 473 Message message = obtainMessage( 474 EVENT_SEND_SATELLITE_DATAGRAM_DONE, mSendSatelliteDatagramRequest); 475 mSendSatelliteDatagramRequest = null; 476 AsyncResult.forMessage(message, null, null); 477 message.sendToTarget(); 478 plogd("handleEventSatelliteAligned: EVENT_SEND_SATELLITE_DATAGRAM_DONE"); 479 } 480 } 481 } 482 handleEventSatelliteAlignedTimeout( @onNull DatagramDispatcherHandlerRequest request)483 private void handleEventSatelliteAlignedTimeout( 484 @NonNull DatagramDispatcherHandlerRequest request) { 485 plogd("handleEventSatelliteAlignedTimeout"); 486 mSendSatelliteDatagramRequest = null; 487 SatelliteManager.SatelliteException exception = 488 new SatelliteManager.SatelliteException( 489 SATELLITE_RESULT_NOT_REACHABLE); 490 Message message = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request); 491 AsyncResult.forMessage(message, null, exception); 492 message.sendToTarget(); 493 } 494 isSatelliteAlignedTimerStarted()495 private boolean isSatelliteAlignedTimerStarted() { 496 return hasMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT); 497 } 498 stopSatelliteAlignedTimer()499 private void stopSatelliteAlignedTimer() { 500 removeMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT); 501 } 502 503 /** 504 * Send pending satellite datagrams. Emergency datagrams are given priority over 505 * non-emergency datagrams. 506 */ 507 @GuardedBy("mLock") sendPendingDatagrams()508 private void sendPendingDatagrams() { 509 plogd("sendPendingDatagrams()"); 510 if (!mDatagramController.isPollingInIdleState()) { 511 // Datagram should be sent to satellite modem when modem is free. 512 plogd("sendPendingDatagrams: modem is receiving datagrams"); 513 return; 514 } 515 516 if (getPendingDatagramCount() <= 0) { 517 plogd("sendPendingDatagrams: no pending datagrams to send"); 518 return; 519 } 520 521 Phone phone = SatelliteServiceUtils.getPhone(); 522 Set<Entry<Long, SendSatelliteDatagramArgument>> pendingDatagram = null; 523 if (!mSendingDatagramInProgress && !mPendingEmergencyDatagramsMap.isEmpty()) { 524 pendingDatagram = mPendingEmergencyDatagramsMap.entrySet(); 525 } else if (!mSendingDatagramInProgress && !mPendingNonEmergencyDatagramsMap.isEmpty()) { 526 pendingDatagram = mPendingNonEmergencyDatagramsMap.entrySet(); 527 } 528 529 if ((pendingDatagram != null) && pendingDatagram.iterator().hasNext()) { 530 SendSatelliteDatagramArgument datagramArg = 531 pendingDatagram.iterator().next().getValue(); 532 if (mDatagramController.needsWaitingForSatelliteConnected(datagramArg.datagramType)) { 533 plogd("sendPendingDatagrams: wait for satellite connected"); 534 return; 535 } 536 537 mSendingDatagramInProgress = true; 538 // Sets the trigger time for getting pending datagrams 539 datagramArg.setDatagramStartTime(); 540 mDatagramController.updateSendStatus(datagramArg.subId, datagramArg.datagramType, 541 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING, 542 getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 543 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArg, phone); 544 } 545 } 546 547 /** 548 * Send error code to all the pending datagrams 549 * 550 * @param pendingDatagramsMap The pending datagrams map to be cleaned up. 551 * @param errorCode error code to be returned. 552 */ 553 @GuardedBy("mLock") sendErrorCodeAndCleanupPendingDatagrams( LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap, @SatelliteManager.SatelliteResult int errorCode)554 private void sendErrorCodeAndCleanupPendingDatagrams( 555 LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap, 556 @SatelliteManager.SatelliteResult int errorCode) { 557 if (pendingDatagramsMap.size() == 0) { 558 return; 559 } 560 ploge("sendErrorCodeAndCleanupPendingDatagrams: cleaning up resources"); 561 562 // Send error code to all the pending datagrams 563 for (Entry<Long, SendSatelliteDatagramArgument> entry : 564 pendingDatagramsMap.entrySet()) { 565 SendSatelliteDatagramArgument argument = entry.getValue(); 566 reportSendDatagramCompleted(argument, errorCode); 567 argument.callback.accept(errorCode); 568 } 569 570 // Clear pending datagram maps 571 pendingDatagramsMap.clear(); 572 } 573 574 /** 575 * Abort sending all the pending datagrams. 576 * 577 * @param subId The subId of the subscription used to send datagram 578 * @param errorCode The error code that resulted in abort. 579 */ 580 @GuardedBy("mLock") abortSendingPendingDatagrams(int subId, @SatelliteManager.SatelliteResult int errorCode)581 private void abortSendingPendingDatagrams(int subId, 582 @SatelliteManager.SatelliteResult int errorCode) { 583 plogd("abortSendingPendingDatagrams()"); 584 sendErrorCodeAndCleanupPendingDatagrams(mPendingEmergencyDatagramsMap, errorCode); 585 sendErrorCodeAndCleanupPendingDatagrams(mPendingNonEmergencyDatagramsMap, errorCode); 586 } 587 588 /** 589 * Return pending datagram count 590 * @return pending datagram count 591 */ getPendingDatagramCount()592 public int getPendingDatagramCount() { 593 synchronized (mLock) { 594 return mPendingEmergencyDatagramsMap.size() + mPendingNonEmergencyDatagramsMap.size(); 595 } 596 } 597 598 /** Return pending user messages count */ getPendingUserMessagesCount()599 public int getPendingUserMessagesCount() { 600 synchronized (mLock) { 601 int pendingUserMessagesCount = 0; 602 for (Entry<Long, SendSatelliteDatagramArgument> entry : 603 mPendingNonEmergencyDatagramsMap.entrySet()) { 604 SendSatelliteDatagramArgument argument = entry.getValue(); 605 if (argument.datagramType != SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) { 606 pendingUserMessagesCount += 1; 607 } 608 } 609 pendingUserMessagesCount += mPendingEmergencyDatagramsMap.size(); 610 return pendingUserMessagesCount; 611 } 612 } 613 614 /** 615 * Posts the specified command to be executed on the main thread and returns immediately. 616 * 617 * @param command command to be executed on the main thread 618 * @param argument additional parameters required to perform of the operation 619 * @param phone phone object used to perform the operation. 620 */ sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone)621 private void sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone) { 622 DatagramDispatcherHandlerRequest request = new DatagramDispatcherHandlerRequest( 623 argument, phone); 624 Message msg = this.obtainMessage(command, request); 625 msg.sendToTarget(); 626 } 627 reportSendDatagramCompleted(@onNull SendSatelliteDatagramArgument argument, @NonNull @SatelliteManager.SatelliteResult int resultCode)628 private void reportSendDatagramCompleted(@NonNull SendSatelliteDatagramArgument argument, 629 @NonNull @SatelliteManager.SatelliteResult int resultCode) { 630 SatelliteStats.getInstance().onSatelliteOutgoingDatagramMetrics( 631 new SatelliteStats.SatelliteOutgoingDatagramParams.Builder() 632 .setDatagramType(argument.datagramType) 633 .setResultCode(resultCode) 634 .setDatagramSizeBytes(argument.getDatagramRoundedSizeBytes()) 635 /* In case pending datagram has not been attempted to send to modem 636 interface. transfer time will be 0. */ 637 .setDatagramTransferTimeMillis(argument.datagramStartTime > 0 638 ? (System.currentTimeMillis() - argument.datagramStartTime) : 0) 639 .setIsDemoMode(mIsDemoMode) 640 .build()); 641 if (resultCode == SatelliteManager.SATELLITE_RESULT_SUCCESS) { 642 mControllerMetricsStats.reportOutgoingDatagramSuccessCount(argument.datagramType, 643 mIsDemoMode); 644 mSessionMetricsStats.addCountOfSuccessfulOutgoingDatagram(argument.datagramType); 645 } else { 646 mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType, 647 mIsDemoMode); 648 mSessionMetricsStats.addCountOfFailedOutgoingDatagram(argument.datagramType, 649 resultCode); 650 } 651 } 652 653 /** 654 * Destroys this DatagramDispatcher. Used for tearing down static resources during testing. 655 */ 656 @VisibleForTesting destroy()657 public void destroy() { 658 sInstance = null; 659 } 660 661 /** 662 * This function is used by {@link DatagramController} to notify {@link DatagramDispatcher} 663 * that satellite modem state has changed. 664 * 665 * @param state Current satellite modem state. 666 */ onSatelliteModemStateChanged(@atelliteManager.SatelliteModemState int state)667 public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) { 668 synchronized (mLock) { 669 if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF 670 || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) { 671 plogd("onSatelliteModemStateChanged: cleaning up resources"); 672 cleanUpResources(); 673 } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_IDLE) { 674 sendPendingDatagrams(); 675 } 676 677 if (state == SATELLITE_MODEM_STATE_CONNECTED 678 && isDatagramWaitForConnectedStateTimerStarted()) { 679 stopDatagramWaitForConnectedStateTimer(); 680 sendPendingDatagrams(); 681 } 682 } 683 } 684 685 @GuardedBy("mLock") cleanUpResources()686 private void cleanUpResources() { 687 plogd("cleanUpResources"); 688 mSendingDatagramInProgress = false; 689 if (getPendingDatagramCount() > 0) { 690 mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 691 mLastSendRequestDatagramType, 692 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 693 getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED); 694 } 695 mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 696 mLastSendRequestDatagramType, 697 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 698 0, SatelliteManager.SATELLITE_RESULT_SUCCESS); 699 abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 700 SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED); 701 702 stopSatelliteAlignedTimer(); 703 stopDatagramWaitForConnectedStateTimer(); 704 stopWaitForDatagramSendingResponseTimer(); 705 stopWaitForSimulatedPollDatagramsDelayTimer(); 706 mIsDemoMode = false; 707 mSendSatelliteDatagramRequest = null; 708 mIsAligned = false; 709 mLastSendRequestDatagramType = DATAGRAM_TYPE_UNKNOWN; 710 } 711 startDatagramWaitForConnectedStateTimer( @onNull SendSatelliteDatagramArgument datagramArgs)712 private void startDatagramWaitForConnectedStateTimer( 713 @NonNull SendSatelliteDatagramArgument datagramArgs) { 714 if (isDatagramWaitForConnectedStateTimerStarted()) { 715 plogd("DatagramWaitForConnectedStateTimer is already started"); 716 return; 717 } 718 sendMessageDelayed(obtainMessage( 719 EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT, datagramArgs), 720 mDatagramController.getDatagramWaitTimeForConnectedState( 721 SatelliteServiceUtils.isLastSosMessage(datagramArgs.datagramType))); 722 } 723 stopDatagramWaitForConnectedStateTimer()724 private void stopDatagramWaitForConnectedStateTimer() { 725 removeMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT); 726 } 727 728 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) isDatagramWaitForConnectedStateTimerStarted()729 public boolean isDatagramWaitForConnectedStateTimerStarted() { 730 return hasMessages(EVENT_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMED_OUT); 731 } 732 733 /** 734 * This API is used by CTS tests to override the mWaitTimeForDatagramSendingResponse. 735 */ setWaitTimeForDatagramSendingResponse(boolean reset, long timeoutMillis)736 void setWaitTimeForDatagramSendingResponse(boolean reset, long timeoutMillis) { 737 if (reset) { 738 mWaitTimeForDatagramSendingResponse = getWaitForDatagramSendingResponseTimeoutMillis(); 739 } else { 740 mWaitTimeForDatagramSendingResponse = timeoutMillis; 741 } 742 } 743 startWaitForDatagramSendingResponseTimer( @onNull SendSatelliteDatagramArgument argument)744 private void startWaitForDatagramSendingResponseTimer( 745 @NonNull SendSatelliteDatagramArgument argument) { 746 if (hasMessages(EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT)) { 747 plogd("WaitForDatagramSendingResponseTimer was already started"); 748 return; 749 } 750 long waitTime = SatelliteServiceUtils.isLastSosMessage(argument.datagramType) 751 ? mWaitTimeForDatagramSendingForLastMessageResponse 752 : mWaitTimeForDatagramSendingResponse; 753 logd("startWaitForDatagramSendingResponseTimer: datagramType=" + argument.datagramType 754 + ", waitTime=" + waitTime); 755 sendMessageDelayed(obtainMessage( 756 EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT, argument), waitTime); 757 } 758 stopWaitForDatagramSendingResponseTimer()759 private void stopWaitForDatagramSendingResponseTimer() { 760 removeMessages(EVENT_WAIT_FOR_DATAGRAM_SENDING_RESPONSE_TIMED_OUT); 761 } 762 handleEventDatagramWaitForConnectedStateTimedOut( @onNull SendSatelliteDatagramArgument argument)763 private void handleEventDatagramWaitForConnectedStateTimedOut( 764 @NonNull SendSatelliteDatagramArgument argument) { 765 plogw("Timed out to wait for satellite connected before sending datagrams"); 766 synchronized (mLock) { 767 // Update send status 768 mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 769 argument.datagramType, 770 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 771 getPendingDatagramCount(), 772 SATELLITE_RESULT_NOT_REACHABLE); 773 mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 774 argument.datagramType, 775 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 776 0, SatelliteManager.SATELLITE_RESULT_SUCCESS); 777 abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 778 SATELLITE_RESULT_NOT_REACHABLE); 779 } 780 } 781 shouldSendDatagramToModemInDemoMode()782 private boolean shouldSendDatagramToModemInDemoMode() { 783 if (mShouldSendDatagramToModemInDemoMode != null) { 784 return mShouldSendDatagramToModemInDemoMode.get(); 785 } 786 787 try { 788 mShouldSendDatagramToModemInDemoMode = new AtomicBoolean( 789 mContext.getResources().getBoolean( 790 R.bool.config_send_satellite_datagram_to_modem_in_demo_mode)); 791 return mShouldSendDatagramToModemInDemoMode.get(); 792 793 } catch (Resources.NotFoundException ex) { 794 ploge("shouldSendDatagramToModemInDemoMode: id= " 795 + R.bool.config_send_satellite_datagram_to_modem_in_demo_mode + ", ex=" + ex); 796 return false; 797 } 798 } 799 getWaitForDatagramSendingResponseTimeoutMillis()800 private long getWaitForDatagramSendingResponseTimeoutMillis() { 801 return mContext.getResources().getInteger( 802 R.integer.config_wait_for_datagram_sending_response_timeout_millis); 803 } 804 getWaitForDatagramSendingResponseForLastMessageTimeoutMillis()805 private long getWaitForDatagramSendingResponseForLastMessageTimeoutMillis() { 806 return mContext.getResources().getInteger(R.integer 807 .config_wait_for_datagram_sending_response_for_last_message_timeout_millis); 808 } 809 shouldProcessEventSendSatelliteDatagramDone( @onNull SendSatelliteDatagramArgument argument)810 private boolean shouldProcessEventSendSatelliteDatagramDone( 811 @NonNull SendSatelliteDatagramArgument argument) { 812 synchronized (mLock) { 813 if (SatelliteServiceUtils.isSosMessage(argument.datagramType)) { 814 return mPendingEmergencyDatagramsMap.containsKey(argument.datagramId); 815 } else { 816 return mPendingNonEmergencyDatagramsMap.containsKey(argument.datagramId); 817 } 818 } 819 } 820 handleEventWaitForDatagramSendingResponseTimedOut( @onNull SendSatelliteDatagramArgument argument)821 private void handleEventWaitForDatagramSendingResponseTimedOut( 822 @NonNull SendSatelliteDatagramArgument argument) { 823 synchronized (mLock) { 824 plogw("Timed out to wait for the response of the request to send the datagram " 825 + argument.datagramId); 826 827 // Ask vendor service to abort all datagram-sending requests 828 SatelliteModemInterface.getInstance().abortSendingSatelliteDatagrams( 829 obtainMessage(EVENT_ABORT_SENDING_SATELLITE_DATAGRAMS_DONE, argument)); 830 mSendingDatagramInProgress = false; 831 832 // Update send status 833 mDatagramController.updateSendStatus(argument.subId, argument.datagramType, 834 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED, 835 getPendingDatagramCount(), SATELLITE_RESULT_MODEM_TIMEOUT); 836 mDatagramController.updateSendStatus(argument.subId, argument.datagramType, 837 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 838 0, SatelliteManager.SATELLITE_RESULT_SUCCESS); 839 840 // Send response for current datagram after updating datagram transfer state 841 // internally. 842 argument.callback.accept(SATELLITE_RESULT_MODEM_TIMEOUT); 843 844 // Log metrics about the outgoing datagram 845 reportSendDatagramCompleted(argument, SATELLITE_RESULT_MODEM_TIMEOUT); 846 // Remove current datagram from pending map. 847 if (SatelliteServiceUtils.isSosMessage(argument.datagramType)) { 848 mPendingEmergencyDatagramsMap.remove(argument.datagramId); 849 } else { 850 mPendingNonEmergencyDatagramsMap.remove(argument.datagramId); 851 } 852 853 // Abort sending all the pending datagrams 854 abortSendingPendingDatagrams(argument.subId, SATELLITE_RESULT_MODEM_TIMEOUT); 855 } 856 } 857 858 /** 859 * This API can be used by only CTS to override the cached value for the device overlay config 860 * value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether 861 * outgoing satellite datagrams should be sent to modem in demo mode. 862 * 863 * @param shouldSendToModemInDemoMode Whether send datagram in demo mode should be sent to 864 * satellite modem or not. If it is null, the cache will be cleared. 865 */ 866 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setShouldSendDatagramToModemInDemoMode( @ullable Boolean shouldSendToModemInDemoMode)867 protected void setShouldSendDatagramToModemInDemoMode( 868 @Nullable Boolean shouldSendToModemInDemoMode) { 869 plogd("setShouldSendDatagramToModemInDemoMode(" + (shouldSendToModemInDemoMode == null 870 ? "null" : shouldSendToModemInDemoMode) + ")"); 871 872 if (shouldSendToModemInDemoMode == null) { 873 mShouldSendDatagramToModemInDemoMode = null; 874 } else { 875 if (mShouldSendDatagramToModemInDemoMode == null) { 876 mShouldSendDatagramToModemInDemoMode = new AtomicBoolean( 877 shouldSendToModemInDemoMode); 878 } else { 879 mShouldSendDatagramToModemInDemoMode.set(shouldSendToModemInDemoMode); 880 } 881 } 882 } 883 startWaitForSimulatedPollDatagramsDelayTimer( @onNull DatagramDispatcherHandlerRequest request)884 private void startWaitForSimulatedPollDatagramsDelayTimer( 885 @NonNull DatagramDispatcherHandlerRequest request) { 886 if (mIsDemoMode) { 887 plogd("startWaitForSimulatedPollDatagramsDelayTimer"); 888 sendMessageDelayed( 889 obtainMessage(EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT, request), 890 getDemoTimeoutDuration()); 891 } else { 892 plogd("Should not start WaitForSimulatedPollDatagramsDelayTimer in non-demo mode"); 893 } 894 } 895 stopWaitForSimulatedPollDatagramsDelayTimer()896 private void stopWaitForSimulatedPollDatagramsDelayTimer() { 897 removeMessages(EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT); 898 } 899 handleEventWaitForSimulatedPollDatagramsDelayTimedOut( @onNull SendSatelliteDatagramArgument argument)900 private void handleEventWaitForSimulatedPollDatagramsDelayTimedOut( 901 @NonNull SendSatelliteDatagramArgument argument) { 902 if (mIsDemoMode) { 903 plogd("handleEventWaitForSimulatedPollDatagramsDelayTimedOut"); 904 mDatagramController.pushDemoModeDatagram(argument.datagramType, argument.datagram); 905 Consumer<Integer> internalCallback = new Consumer<Integer>() { 906 @Override 907 public void accept(Integer result) { 908 plogd("pollPendingSatelliteDatagrams result: " + result); 909 } 910 }; 911 mDatagramController.pollPendingSatelliteDatagrams(argument.subId, internalCallback); 912 } else { 913 plogd("Unexpected EVENT_WAIT_FOR_SIMULATED_POLL_DATAGRAMS_DELAY_TIMED_OUT in " 914 + "non-demo mode"); 915 } 916 } 917 getDemoTimeoutDuration()918 long getDemoTimeoutDuration() { 919 return mDemoTimeoutDuration; 920 } 921 922 /** 923 * This API is used by CTS tests to override the mDemoTimeoutDuration. 924 */ setTimeoutDatagramDelayInDemoMode(boolean reset, long timeoutMillis)925 void setTimeoutDatagramDelayInDemoMode(boolean reset, long timeoutMillis) { 926 if (!mIsDemoMode) { 927 return; 928 } 929 if (reset) { 930 mDemoTimeoutDuration = TIMEOUT_DATAGRAM_DELAY_IN_DEMO_MODE; 931 } else { 932 mDemoTimeoutDuration = timeoutMillis; 933 } 934 plogd("setTimeoutDatagramDelayInDemoMode " + mDemoTimeoutDuration + " reset=" + reset); 935 } 936 logd(@onNull String log)937 private static void logd(@NonNull String log) { 938 Rlog.d(TAG, log); 939 } 940 loge(@onNull String log)941 private static void loge(@NonNull String log) { 942 Rlog.e(TAG, log); 943 } 944 logw(@onNull String log)945 private static void logw(@NonNull String log) { Rlog.w(TAG, log); } 946 isSatellitePersistentLoggingEnabled( @onNull Context context, @NonNull FeatureFlags featureFlags)947 private boolean isSatellitePersistentLoggingEnabled( 948 @NonNull Context context, @NonNull FeatureFlags featureFlags) { 949 if (featureFlags.satellitePersistentLogging()) { 950 return true; 951 } 952 try { 953 return context.getResources().getBoolean( 954 R.bool.config_dropboxmanager_persistent_logging_enabled); 955 } catch (RuntimeException e) { 956 return false; 957 } 958 } 959 plogd(@onNull String log)960 private void plogd(@NonNull String log) { 961 Rlog.d(TAG, log); 962 if (mPersistentLogger != null) { 963 mPersistentLogger.debug(TAG, log); 964 } 965 } 966 plogw(@onNull String log)967 private void plogw(@NonNull String log) { 968 Rlog.w(TAG, log); 969 if (mPersistentLogger != null) { 970 mPersistentLogger.warn(TAG, log); 971 } 972 } 973 ploge(@onNull String log)974 private void ploge(@NonNull String log) { 975 Rlog.e(TAG, log); 976 if (mPersistentLogger != null) { 977 mPersistentLogger.error(TAG, log); 978 } 979 } 980 } 981