• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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