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