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_KEEP_ALIVE; 21 import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_UNKNOWN; 22 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE; 23 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED; 24 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING; 25 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_IDLE; 26 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED; 27 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF; 28 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS; 29 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.content.Context; 33 import android.content.res.Resources; 34 import android.os.Build; 35 import android.os.Looper; 36 import android.os.SystemProperties; 37 import android.telephony.PersistentLogger; 38 import android.telephony.satellite.ISatelliteDatagramCallback; 39 import android.telephony.satellite.SatelliteDatagram; 40 import android.telephony.satellite.SatelliteManager; 41 import android.util.Log; 42 43 import com.android.internal.R; 44 import com.android.internal.annotations.GuardedBy; 45 import com.android.internal.annotations.VisibleForTesting; 46 import com.android.internal.telephony.flags.FeatureFlags; 47 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.concurrent.TimeUnit; 51 import java.util.function.Consumer; 52 53 /** 54 * Datagram controller used for sending and receiving satellite datagrams. 55 */ 56 public class DatagramController { 57 private static final String TAG = "DatagramController"; 58 59 @NonNull private static DatagramController sInstance; 60 @NonNull private final Context mContext; 61 @NonNull private final FeatureFlags mFeatureFlags; 62 @NonNull private final PointingAppController mPointingAppController; 63 @NonNull private final DatagramDispatcher mDatagramDispatcher; 64 @NonNull private final DatagramReceiver mDatagramReceiver; 65 66 public static final long MAX_DATAGRAM_ID = (long) Math.pow(2, 16); 67 public static final int ROUNDING_UNIT = 10; 68 public static final long SATELLITE_ALIGN_TIMEOUT = TimeUnit.SECONDS.toMillis(30); 69 /** This type is used by CTS to override the satellite align timeout */ 70 public static final int TIMEOUT_TYPE_ALIGN = 1; 71 /** This type is used by CTS to override the time to wait for connected state */ 72 public static final int TIMEOUT_TYPE_DATAGRAM_WAIT_FOR_CONNECTED_STATE = 2; 73 /** This type is used by CTS to override the time to wait for response of the send request */ 74 public static final int TIMEOUT_TYPE_WAIT_FOR_DATAGRAM_SENDING_RESPONSE = 3; 75 /** This type is used by CTS to override the time to datagram delay in demo mode */ 76 public static final int TIMEOUT_TYPE_DATAGRAM_DELAY_IN_DEMO_MODE = 4; 77 /** This type is used by CTS to override wait for device alignment in demo datagram boolean */ 78 public static final int BOOLEAN_TYPE_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_DATAGRAM = 1; 79 private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem"; 80 private static final boolean DEBUG = !"user".equals(Build.TYPE); 81 82 /** Variables used to update onSendDatagramStateChanged(). */ 83 private final Object mLock = new Object(); 84 @GuardedBy("mLock") 85 private int mSendSubId; 86 @GuardedBy("mLock") 87 private @SatelliteManager.DatagramType int mDatagramType = DATAGRAM_TYPE_UNKNOWN; 88 @GuardedBy("mLock") 89 private @SatelliteManager.SatelliteDatagramTransferState int mSendDatagramTransferState = 90 SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE; 91 @GuardedBy("mLock") 92 private int mSendPendingCount = 0; 93 @GuardedBy("mLock") 94 private int mSendErrorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS; 95 /** Variables used to update onReceiveDatagramStateChanged(). */ 96 @GuardedBy("mLock") 97 private int mReceiveSubId; 98 @GuardedBy("mLock") 99 private @SatelliteManager.SatelliteDatagramTransferState int mReceiveDatagramTransferState = 100 SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE; 101 @GuardedBy("mLock") 102 private int mReceivePendingCount = 0; 103 @GuardedBy("mLock") 104 private int mReceiveErrorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS; 105 @GuardedBy("mLock") 106 private final List<SatelliteDatagram> mDemoModeDatagramList; 107 private boolean mIsDemoMode = false; 108 private long mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT; 109 private long mDatagramWaitTimeForConnectedState; 110 private long mModemImageSwitchingDuration; 111 private boolean mWaitForDeviceAlignmentInDemoDatagram; 112 private long mDatagramWaitTimeForConnectedStateForLastMessage; 113 @GuardedBy("mLock") 114 @SatelliteManager.SatelliteModemState 115 private int mSatelltieModemState = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN; 116 @Nullable 117 private PersistentLogger mPersistentLogger = null; 118 119 /** 120 * @return The singleton instance of DatagramController. 121 */ getInstance()122 public static DatagramController getInstance() { 123 if (sInstance == null) { 124 loge("DatagramController was not yet initialized."); 125 } 126 return sInstance; 127 } 128 129 /** 130 * Create the DatagramController singleton instance. 131 * @param context The Context to use to create the DatagramController. 132 * @param looper The looper for the handler. 133 * @param featureFlags The telephony feature flags. 134 * @param pointingAppController PointingAppController is used to update 135 * PointingApp about datagram transfer state changes. 136 * @return The singleton instance of DatagramController. 137 */ make(@onNull Context context, @NonNull Looper looper, @NonNull FeatureFlags featureFlags, @NonNull PointingAppController pointingAppController)138 public static DatagramController make(@NonNull Context context, @NonNull Looper looper, 139 @NonNull FeatureFlags featureFlags, 140 @NonNull PointingAppController pointingAppController) { 141 if (sInstance == null) { 142 sInstance = new DatagramController( 143 context, looper, featureFlags, pointingAppController); 144 } 145 return sInstance; 146 } 147 148 /** 149 * Create a DatagramController to send and receive satellite datagrams. 150 * 151 * @param context The Context for the DatagramController. 152 * @param looper The looper for the handler 153 * @param featureFlags The telephony feature flags. 154 * @param pointingAppController PointingAppController is used to update PointingApp 155 * about datagram transfer state changes. 156 */ 157 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) DatagramController(@onNull Context context, @NonNull Looper looper, @NonNull FeatureFlags featureFlags, @NonNull PointingAppController pointingAppController)158 public DatagramController(@NonNull Context context, @NonNull Looper looper, 159 @NonNull FeatureFlags featureFlags, 160 @NonNull PointingAppController pointingAppController) { 161 mContext = context; 162 mFeatureFlags = featureFlags; 163 mPointingAppController = pointingAppController; 164 165 // Create the DatagramDispatcher singleton, 166 // which is used to send satellite datagrams. 167 mDatagramDispatcher = DatagramDispatcher.make( 168 mContext, looper, mFeatureFlags, this); 169 170 // Create the DatagramReceiver singleton, 171 // which is used to receive satellite datagrams. 172 mDatagramReceiver = DatagramReceiver.make( 173 mContext, looper, mFeatureFlags, this); 174 175 mDatagramWaitTimeForConnectedState = getDatagramWaitForConnectedStateTimeoutMillis(); 176 mModemImageSwitchingDuration = getSatelliteModemImageSwitchingDurationMillis(); 177 mWaitForDeviceAlignmentInDemoDatagram = 178 getWaitForDeviceAlignmentInDemoDatagramFromResources(); 179 mDatagramWaitTimeForConnectedStateForLastMessage = 180 getDatagramWaitForConnectedStateForLastMessageTimeoutMillis(); 181 mDemoModeDatagramList = new ArrayList<>(); 182 mPersistentLogger = SatelliteServiceUtils.getPersistentLogger(context); 183 } 184 185 /** 186 * Register to receive incoming datagrams over satellite. 187 * 188 * @param subId The subId of the subscription to register for incoming satellite datagrams. 189 * @param callback The callback to handle incoming datagrams over satellite. 190 * 191 * @return The {@link SatelliteManager.SatelliteResult} result of the operation. 192 */ registerForSatelliteDatagram(int subId, @NonNull ISatelliteDatagramCallback callback)193 @SatelliteManager.SatelliteResult public int registerForSatelliteDatagram(int subId, 194 @NonNull ISatelliteDatagramCallback callback) { 195 return mDatagramReceiver.registerForSatelliteDatagram(subId, callback); 196 } 197 198 /** 199 * Unregister to stop receiving incoming datagrams over satellite. 200 * If callback was not registered before, the request will be ignored. 201 * 202 * @param subId The subId of the subscription to unregister for incoming satellite datagrams. 203 * @param callback The callback that was passed to 204 * {@link #registerForSatelliteDatagram(int, ISatelliteDatagramCallback)}. 205 */ unregisterForSatelliteDatagram(int subId, @NonNull ISatelliteDatagramCallback callback)206 public void unregisterForSatelliteDatagram(int subId, 207 @NonNull ISatelliteDatagramCallback callback) { 208 mDatagramReceiver.unregisterForSatelliteDatagram(subId, callback); 209 } 210 211 /** 212 * Poll pending satellite datagrams over satellite. 213 * 214 * This method requests modem to check if there are any pending datagrams to be received over 215 * satellite. If there are any incoming datagrams, they will be received via 216 * {@link android.telephony.satellite.SatelliteDatagramCallback#onSatelliteDatagramReceived( 217 * long, SatelliteDatagram, int, Consumer)} 218 * 219 * @param subId The subId of the subscription used for receiving datagrams. 220 * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request. 221 */ pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback)222 public void pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback) { 223 plogd("pollPendingSatelliteDatagrams"); 224 mDatagramReceiver.pollPendingSatelliteDatagrams(subId, callback); 225 } 226 227 /** 228 * Send datagram over satellite. 229 * 230 * Gateway encodes SOS message or location sharing message into a datagram and passes it as 231 * input to this method. Datagram received here will be passed down to modem without any 232 * encoding or encryption. 233 * 234 * When demo mode is on, save the sent datagram and this datagram will be used as a received 235 * datagram. 236 * 237 * @param subId The subId of the subscription to send satellite datagrams for. 238 * @param datagramType datagram type indicating whether the datagram is of type 239 * SOS_SMS or LOCATION_SHARING. 240 * @param datagram encoded gateway datagram which is encrypted by the caller. 241 * Datagram will be passed down to modem without any encoding or encryption. 242 * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in 243 * full screen mode. 244 * @param callback The callback to get {@link SatelliteManager.SatelliteResult} of the request. 245 */ sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)246 public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, 247 @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, 248 @NonNull Consumer<Integer> callback) { 249 mDatagramDispatcher.sendSatelliteDatagram(subId, datagramType, datagram, 250 needFullScreenPointingUI, callback); 251 mPointingAppController.onSendDatagramRequested(subId, datagramType); 252 } 253 254 /** 255 * Update send status to {@link PointingAppController}. 256 * 257 * @param subId The subId of the subscription to send satellite datagrams for 258 * @param datagramTransferState The new send datagram transfer state. 259 * @param sendPendingCount number of datagrams that are currently being sent 260 * @param errorCode If datagram transfer failed, the reason for failure. 261 */ updateSendStatus(int subId, @SatelliteManager.DatagramType int datagramType, @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState, int sendPendingCount, int errorCode)262 public void updateSendStatus(int subId, @SatelliteManager.DatagramType int datagramType, 263 @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState, 264 int sendPendingCount, int errorCode) { 265 synchronized (mLock) { 266 plogd("updateSendStatus" 267 + " subId: " + subId 268 + " datagramType: " + datagramType 269 + " datagramTransferState: " + datagramTransferState 270 + " sendPendingCount: " + sendPendingCount + " errorCode: " + errorCode); 271 if (shouldSuppressDatagramTransferStateUpdate(datagramType)) { 272 plogd("Ignore the request to update send status"); 273 return; 274 } 275 276 mSendSubId = subId; 277 mDatagramType = datagramType; 278 mSendDatagramTransferState = datagramTransferState; 279 mSendPendingCount = sendPendingCount; 280 mSendErrorCode = errorCode; 281 notifyDatagramTransferStateChangedToSessionController(mDatagramType); 282 mPointingAppController.updateSendDatagramTransferState(mSendSubId, mDatagramType, 283 mSendDatagramTransferState, mSendPendingCount, mSendErrorCode); 284 retryPollPendingDatagramsInDemoMode(); 285 } 286 } 287 shouldSuppressDatagramTransferStateUpdate( @atelliteManager.DatagramType int datagramType)288 private boolean shouldSuppressDatagramTransferStateUpdate( 289 @SatelliteManager.DatagramType int datagramType) { 290 synchronized (mLock) { 291 if (!SatelliteController.getInstance().isSatelliteAttachRequired()) { 292 return false; 293 } 294 if (datagramType == DATAGRAM_TYPE_KEEP_ALIVE 295 && mSatelltieModemState == SATELLITE_MODEM_STATE_NOT_CONNECTED) { 296 return true; 297 } 298 return false; 299 } 300 } 301 302 /** 303 * Update receive status to {@link PointingAppController}. 304 * 305 * @param subId The subId of the subscription used to receive datagrams 306 * @param datagramTransferState The new receive datagram transfer state. 307 * @param receivePendingCount The number of datagrams that are currently pending to be received. 308 * @param errorCode If datagram transfer failed, the reason for failure. 309 */ updateReceiveStatus(int subId, @SatelliteManager.DatagramType int datagramType, @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState, int receivePendingCount, int errorCode)310 public void updateReceiveStatus(int subId, @SatelliteManager.DatagramType int datagramType, 311 @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState, 312 int receivePendingCount, int errorCode) { 313 synchronized (mLock) { 314 plogd("updateReceiveStatus" 315 + " subId: " + subId 316 + " datagramType: " + datagramType 317 + " datagramTransferState: " + datagramTransferState 318 + " receivePendingCount: " + receivePendingCount + " errorCode: " + errorCode); 319 320 mReceiveSubId = subId; 321 mDatagramType = datagramType; 322 mReceiveDatagramTransferState = datagramTransferState; 323 mReceivePendingCount = receivePendingCount; 324 mReceiveErrorCode = errorCode; 325 326 notifyDatagramTransferStateChangedToSessionController(mDatagramType); 327 mPointingAppController.updateReceiveDatagramTransferState(mReceiveSubId, 328 mReceiveDatagramTransferState, mReceivePendingCount, mReceiveErrorCode); 329 retryPollPendingDatagramsInDemoMode(); 330 } 331 332 if (isPollingInIdleState()) { 333 mDatagramDispatcher.retrySendingDatagrams(); 334 } 335 } 336 337 /** 338 * Return receive pending datagram count 339 * @return receive pending datagram count. 340 */ getReceivePendingCount()341 public int getReceivePendingCount() { 342 return mReceivePendingCount; 343 } 344 345 346 /** @return {@code true} if already sent an emergency datagram during a session. */ isEmergencyCommunicationEstablished()347 public boolean isEmergencyCommunicationEstablished() { 348 return mDatagramDispatcher.isEmergencyCommunicationEstablished(); 349 } 350 351 /** 352 * This function is used by {@link SatelliteController} to notify {@link DatagramController} 353 * that satellite modem state has changed. 354 * 355 * @param state Current satellite modem state. 356 */ onSatelliteModemStateChanged(@atelliteManager.SatelliteModemState int state)357 public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) { 358 synchronized (mLock) { 359 mSatelltieModemState = state; 360 } 361 mDatagramDispatcher.onSatelliteModemStateChanged(state); 362 mDatagramReceiver.onSatelliteModemStateChanged(state); 363 } 364 365 /** 366 * Notify SMS received. 367 * 368 * @param subId The subId of the subscription used to receive SMS 369 */ onSmsReceived(int subId)370 public void onSmsReceived(int subId) { 371 // To keep exist notification flow, need to call with each state. 372 updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS, 373 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING, 374 getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 375 updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS, 376 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS, 377 getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 378 updateReceiveStatus(subId, SatelliteManager.DATAGRAM_TYPE_SMS, 379 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE, 380 getReceivePendingCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS); 381 } 382 383 /** 384 * Set whether the device is aligned with the satellite. 385 */ 386 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setDeviceAlignedWithSatellite(boolean isAligned)387 public void setDeviceAlignedWithSatellite(boolean isAligned) { 388 mDatagramDispatcher.setDeviceAlignedWithSatellite(isAligned); 389 mDatagramReceiver.setDeviceAlignedWithSatellite(isAligned); 390 if (isAligned) { 391 retryPollPendingDatagramsInDemoMode(); 392 } 393 } 394 395 @VisibleForTesting isReceivingDatagrams()396 public boolean isReceivingDatagrams() { 397 synchronized (mLock) { 398 return (mReceiveDatagramTransferState 399 == SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING); 400 } 401 } 402 403 /** 404 * Check if Telephony needs to wait for the modem satellite connected to a satellite network 405 * before transferring datagrams via satellite. 406 */ 407 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) needsWaitingForSatelliteConnected( @atelliteManager.DatagramType int datagramType)408 public boolean needsWaitingForSatelliteConnected( 409 @SatelliteManager.DatagramType int datagramType) { 410 synchronized (mLock) { 411 if (!SatelliteController.getInstance().isSatelliteAttachRequired()) { 412 return false; 413 } 414 if (datagramType == DATAGRAM_TYPE_KEEP_ALIVE 415 && mSatelltieModemState == SATELLITE_MODEM_STATE_NOT_CONNECTED) { 416 return false; 417 } 418 boolean allowCheckMessageInNotConnected = 419 mContext.getResources().getBoolean( 420 R.bool.config_satellite_allow_check_message_in_not_connected); 421 if (datagramType == DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS 422 && mSatelltieModemState == SATELLITE_MODEM_STATE_NOT_CONNECTED 423 && allowCheckMessageInNotConnected 424 && mFeatureFlags.carrierRoamingNbIotNtn()) { 425 return false; 426 } 427 if (mSatelltieModemState != SATELLITE_MODEM_STATE_CONNECTED 428 && mSatelltieModemState != SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING) { 429 return true; 430 } 431 return false; 432 } 433 } 434 isSendingInIdleState()435 public boolean isSendingInIdleState() { 436 synchronized (mLock) { 437 return (mSendDatagramTransferState 438 == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); 439 } 440 } 441 isPollingInIdleState()442 public boolean isPollingInIdleState() { 443 synchronized (mLock) { 444 return (mReceiveDatagramTransferState 445 == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE); 446 } 447 } 448 449 /** 450 * Set variables for {@link DatagramDispatcher} and {@link DatagramReceiver} to run demo mode 451 * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise. 452 */ 453 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setDemoMode(boolean isDemoMode)454 public void setDemoMode(boolean isDemoMode) { 455 mIsDemoMode = isDemoMode; 456 mDatagramDispatcher.setDemoMode(isDemoMode); 457 mDatagramReceiver.setDemoMode(isDemoMode); 458 459 if (!isDemoMode) { 460 synchronized (mLock) { 461 mDemoModeDatagramList.clear(); 462 } 463 setDeviceAlignedWithSatellite(false); 464 } 465 plogd("setDemoMode: mIsDemoMode=" + mIsDemoMode); 466 } 467 468 /** Get the last sent datagram for demo mode */ 469 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) popDemoModeDatagram()470 public SatelliteDatagram popDemoModeDatagram() { 471 if (!mIsDemoMode) { 472 return null; 473 } 474 475 synchronized (mLock) { 476 plogd("popDemoModeDatagram"); 477 return mDemoModeDatagramList.size() > 0 ? mDemoModeDatagramList.remove(0) : null; 478 } 479 } 480 481 /** 482 * Set last sent datagram for demo mode 483 * @param datagramType datagram type, DATAGRAM_TYPE_SOS_MESSAGE, 484 * DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP, 485 * DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED will be saved 486 * @param datagram datagram The last datagram saved when sendSatelliteDatagramForDemo is called 487 */ 488 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) pushDemoModeDatagram(@atelliteManager.DatagramType int datagramType, SatelliteDatagram datagram)489 public void pushDemoModeDatagram(@SatelliteManager.DatagramType int datagramType, 490 SatelliteDatagram datagram) { 491 if (mIsDemoMode && SatelliteServiceUtils.isSosMessage(datagramType)) { 492 synchronized (mLock) { 493 mDemoModeDatagramList.add(datagram); 494 plogd("pushDemoModeDatagram size=" + mDemoModeDatagramList.size()); 495 } 496 } 497 } 498 getSatelliteAlignedTimeoutDuration()499 long getSatelliteAlignedTimeoutDuration() { 500 return mAlignTimeoutDuration; 501 } 502 503 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) getDatagramWaitTimeForConnectedState(boolean isLastSosMessage)504 public long getDatagramWaitTimeForConnectedState(boolean isLastSosMessage) { 505 synchronized (mLock) { 506 long timeout = isLastSosMessage ? mDatagramWaitTimeForConnectedStateForLastMessage : 507 mDatagramWaitTimeForConnectedState; 508 logd("getDatagramWaitTimeForConnectedState: isLastSosMessage=" + isLastSosMessage 509 + ", timeout=" + timeout + ", modemState=" + mSatelltieModemState); 510 if (mSatelltieModemState == SATELLITE_MODEM_STATE_OFF 511 || mSatelltieModemState == SATELLITE_MODEM_STATE_IDLE) { 512 return (timeout + mModemImageSwitchingDuration); 513 } 514 return timeout; 515 } 516 } 517 518 /** 519 * This API can be used by only CTS to timeout durations used by DatagramController module. 520 * 521 * @param timeoutMillis The timeout duration in millisecond. 522 * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise. 523 */ setDatagramControllerTimeoutDuration( boolean reset, int timeoutType, long timeoutMillis)524 boolean setDatagramControllerTimeoutDuration( 525 boolean reset, int timeoutType, long timeoutMillis) { 526 if (!isMockModemAllowed()) { 527 ploge("Updating timeout duration is not allowed"); 528 return false; 529 } 530 531 plogd("setDatagramControllerTimeoutDuration: timeoutMillis=" + timeoutMillis 532 + ", reset=" + reset + ", timeoutType=" + timeoutType); 533 if (timeoutType == TIMEOUT_TYPE_ALIGN) { 534 if (reset) { 535 mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT; 536 } else { 537 mAlignTimeoutDuration = timeoutMillis; 538 } 539 } else if (timeoutType == TIMEOUT_TYPE_DATAGRAM_WAIT_FOR_CONNECTED_STATE) { 540 if (reset) { 541 mDatagramWaitTimeForConnectedState = 542 getDatagramWaitForConnectedStateTimeoutMillis(); 543 mModemImageSwitchingDuration = getSatelliteModemImageSwitchingDurationMillis(); 544 } else { 545 mDatagramWaitTimeForConnectedState = timeoutMillis; 546 mModemImageSwitchingDuration = 0; 547 } 548 } else if (timeoutType == TIMEOUT_TYPE_WAIT_FOR_DATAGRAM_SENDING_RESPONSE) { 549 mDatagramDispatcher.setWaitTimeForDatagramSendingResponse(reset, timeoutMillis); 550 } else if (timeoutType == TIMEOUT_TYPE_DATAGRAM_DELAY_IN_DEMO_MODE) { 551 mDatagramDispatcher.setTimeoutDatagramDelayInDemoMode(reset, timeoutMillis); 552 } else { 553 ploge("Invalid timeout type " + timeoutType); 554 return false; 555 } 556 return true; 557 } 558 559 /** 560 * This API can be used by only CTS to override the boolean configs used by the 561 * DatagramController module. 562 * 563 * @param enable Whether to enable or disable boolean config. 564 * @return {@code true} if the boolean config is set successfully, {@code false} otherwise. 565 */ setDatagramControllerBooleanConfig( boolean reset, int booleanType, boolean enable)566 boolean setDatagramControllerBooleanConfig( 567 boolean reset, int booleanType, boolean enable) { 568 if (!isMockModemAllowed()) { 569 loge("Updating boolean config is not allowed"); 570 return false; 571 } 572 573 logd("setDatagramControllerTimeoutDuration: booleanType=" + booleanType 574 + ", reset=" + reset + ", enable=" + enable); 575 if (booleanType == BOOLEAN_TYPE_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_DATAGRAM) { 576 if (reset) { 577 mWaitForDeviceAlignmentInDemoDatagram = 578 getWaitForDeviceAlignmentInDemoDatagramFromResources(); 579 } else { 580 mWaitForDeviceAlignmentInDemoDatagram = enable; 581 } 582 } else { 583 loge("Invalid boolean type " + booleanType); 584 return false; 585 } 586 return true; 587 } 588 isMockModemAllowed()589 private boolean isMockModemAllowed() { 590 return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false)); 591 } 592 notifyDatagramTransferStateChangedToSessionController(int datagramType)593 private void notifyDatagramTransferStateChangedToSessionController(int datagramType) { 594 SatelliteSessionController sessionController = SatelliteSessionController.getInstance(); 595 if (sessionController == null) { 596 ploge("notifyDatagramTransferStateChangeToSessionController: SatelliteSessionController" 597 + " is not initialized yet"); 598 } else { 599 synchronized (mLock) { 600 sessionController.onDatagramTransferStateChanged( 601 mSendDatagramTransferState, mReceiveDatagramTransferState, datagramType); 602 } 603 } 604 } 605 getDatagramWaitForConnectedStateTimeoutMillis()606 private long getDatagramWaitForConnectedStateTimeoutMillis() { 607 return mContext.getResources().getInteger( 608 R.integer.config_datagram_wait_for_connected_state_timeout_millis); 609 } 610 getSatelliteModemImageSwitchingDurationMillis()611 private long getSatelliteModemImageSwitchingDurationMillis() { 612 return mContext.getResources().getInteger( 613 R.integer.config_satellite_modem_image_switching_duration_millis); 614 } 615 getDatagramWaitForConnectedStateForLastMessageTimeoutMillis()616 private long getDatagramWaitForConnectedStateForLastMessageTimeoutMillis() { 617 return mContext.getResources().getInteger( 618 R.integer.config_datagram_wait_for_connected_state_for_last_message_timeout_millis); 619 } 620 621 /** 622 * This API can be used by only CTS to override the cached value for the device overlay config 623 * value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether 624 * outgoing satellite datagrams should be sent to modem in demo mode. 625 * 626 * @param shouldSendToModemInDemoMode Whether send datagram in demo mode should be sent to 627 * satellite modem or not. 628 */ setShouldSendDatagramToModemInDemoMode(boolean shouldSendToModemInDemoMode)629 void setShouldSendDatagramToModemInDemoMode(boolean shouldSendToModemInDemoMode) { 630 mDatagramDispatcher.setShouldSendDatagramToModemInDemoMode(shouldSendToModemInDemoMode); 631 } 632 retryPollPendingDatagramsInDemoMode()633 private void retryPollPendingDatagramsInDemoMode() { 634 synchronized (mLock) { 635 if (mIsDemoMode && isSendingInIdleState() && isPollingInIdleState() 636 && !mDemoModeDatagramList.isEmpty()) { 637 Consumer<Integer> internalCallback = new Consumer<Integer>() { 638 @Override 639 public void accept(Integer result) { 640 if (result != SATELLITE_RESULT_SUCCESS) { 641 plogd("retryPollPendingDatagramsInDemoMode result: " + result); 642 } 643 } 644 }; 645 pollPendingSatelliteDatagrams( 646 SatelliteController.getInstance().getSelectedSatelliteSubId(), 647 internalCallback); 648 } 649 } 650 } 651 652 /** 653 * Get whether to wait for device alignment with satellite before sending datagrams. 654 * 655 * @param isAligned if the device is aligned with satellite or not 656 * @return {@code true} if device is not aligned to satellite, 657 * and it is required to wait for alignment else {@code false} 658 */ 659 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) waitForAligningToSatellite(boolean isAligned)660 public boolean waitForAligningToSatellite(boolean isAligned) { 661 if (isAligned) { 662 return false; 663 } 664 665 return getWaitForDeviceAlignmentInDemoDatagram(); 666 } 667 getWaitForDeviceAlignmentInDemoDatagram()668 private boolean getWaitForDeviceAlignmentInDemoDatagram() { 669 return mWaitForDeviceAlignmentInDemoDatagram; 670 } 671 getWaitForDeviceAlignmentInDemoDatagramFromResources()672 private boolean getWaitForDeviceAlignmentInDemoDatagramFromResources() { 673 boolean waitForDeviceAlignmentInDemoDatagram = false; 674 try { 675 waitForDeviceAlignmentInDemoDatagram = mContext.getResources().getBoolean( 676 R.bool.config_wait_for_device_alignment_in_demo_datagram); 677 } catch (Resources.NotFoundException ex) { 678 loge("getWaitForDeviceAlignmentInDemoDatagram: ex=" + ex); 679 } 680 681 return waitForDeviceAlignmentInDemoDatagram; 682 } 683 logd(@onNull String log)684 private static void logd(@NonNull String log) { 685 Log.d(TAG, log); 686 } 687 loge(@onNull String log)688 private static void loge(@NonNull String log) { 689 Log.e(TAG, log); 690 } 691 plogd(@onNull String log)692 private void plogd(@NonNull String log) { 693 Log.d(TAG, log); 694 if (mPersistentLogger != null) { 695 mPersistentLogger.debug(TAG, log); 696 } 697 } 698 ploge(@onNull String log)699 private void ploge(@NonNull String log) { 700 Log.e(TAG, log); 701 if (mPersistentLogger != null) { 702 mPersistentLogger.error(TAG, log); 703 } 704 } 705 } 706