• 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 com.android.internal.telephony.satellite.DatagramController.ROUNDING_UNIT;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.Context;
24 import android.os.AsyncResult;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.telephony.Rlog;
29 import android.telephony.SubscriptionManager;
30 import android.telephony.satellite.SatelliteDatagram;
31 import android.telephony.satellite.SatelliteManager;
32 
33 import com.android.internal.annotations.GuardedBy;
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.internal.telephony.Phone;
36 import com.android.internal.telephony.metrics.SatelliteStats;
37 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
38 
39 import java.util.LinkedHashMap;
40 import java.util.Map.Entry;
41 import java.util.Set;
42 import java.util.concurrent.atomic.AtomicLong;
43 import java.util.function.Consumer;
44 
45 /**
46  * Datagram dispatcher used to send satellite datagrams.
47  */
48 public class DatagramDispatcher extends Handler {
49     private static final String TAG = "DatagramDispatcher";
50 
51     private static final int CMD_SEND_SATELLITE_DATAGRAM = 1;
52     private static final int EVENT_SEND_SATELLITE_DATAGRAM_DONE = 2;
53     private static final int EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT = 3;
54 
55     @NonNull private static DatagramDispatcher sInstance;
56     @NonNull private final Context mContext;
57     @NonNull private final DatagramController mDatagramController;
58     @NonNull private final ControllerMetricsStats mControllerMetricsStats;
59 
60     private boolean mIsDemoMode = false;
61     private boolean mIsAligned = false;
62     private DatagramDispatcherHandlerRequest mSendSatelliteDatagramRequest = null;
63 
64     private static AtomicLong mNextDatagramId = new AtomicLong(0);
65 
66     private final Object mLock = new Object();
67 
68     @GuardedBy("mLock")
69     private boolean mSendingDatagramInProgress;
70 
71     /**
72      * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending emergency
73      * datagrams.
74      */
75     @GuardedBy("mLock")
76     private final LinkedHashMap<Long, SendSatelliteDatagramArgument>
77             mPendingEmergencyDatagramsMap = new LinkedHashMap<>();
78 
79     /**
80      * Map key: datagramId, value: SendSatelliteDatagramArgument to retry sending non-emergency
81      * datagrams.
82      */
83     @GuardedBy("mLock")
84     private final LinkedHashMap<Long, SendSatelliteDatagramArgument>
85             mPendingNonEmergencyDatagramsMap = new LinkedHashMap<>();
86 
87     /**
88      * Create the DatagramDispatcher singleton instance.
89      * @param context The Context to use to create the DatagramDispatcher.
90      * @param looper The looper for the handler.
91      * @param datagramController DatagramController which is used to update datagram transfer state.
92      * @return The singleton instance of DatagramDispatcher.
93      */
make(@onNull Context context, @NonNull Looper looper, @NonNull DatagramController datagramController)94     public static DatagramDispatcher make(@NonNull Context context, @NonNull Looper looper,
95             @NonNull DatagramController datagramController) {
96         if (sInstance == null) {
97             sInstance = new DatagramDispatcher(context, looper, datagramController);
98         }
99         return sInstance;
100     }
101 
102     /**
103      * Create a DatagramDispatcher to send satellite datagrams.
104      *
105      * @param context The Context for the DatagramDispatcher.
106      * @param looper The looper for the handler.
107      * @param datagramController DatagramController which is used to update datagram transfer state.
108      */
109     @VisibleForTesting
DatagramDispatcher(@onNull Context context, @NonNull Looper looper, @NonNull DatagramController datagramController)110     protected DatagramDispatcher(@NonNull Context context, @NonNull Looper looper,
111             @NonNull DatagramController datagramController) {
112         super(looper);
113         mContext = context;
114         mDatagramController = datagramController;
115         mControllerMetricsStats = ControllerMetricsStats.getInstance();
116 
117         synchronized (mLock) {
118             mSendingDatagramInProgress = false;
119         }
120     }
121 
122     private static final class DatagramDispatcherHandlerRequest {
123         /** The argument to use for the request */
124         public @NonNull Object argument;
125         /** The caller needs to specify the phone to be used for the request */
126         public @NonNull Phone phone;
127         /** The result of the request that is run on the main thread */
128         public @Nullable Object result;
129 
DatagramDispatcherHandlerRequest(Object argument, Phone phone)130         DatagramDispatcherHandlerRequest(Object argument, Phone phone) {
131             this.argument = argument;
132             this.phone = phone;
133         }
134     }
135 
136     private static final class SendSatelliteDatagramArgument {
137         public int subId;
138         public long datagramId;
139         public @SatelliteManager.DatagramType int datagramType;
140         public @NonNull SatelliteDatagram datagram;
141         public boolean needFullScreenPointingUI;
142         public @NonNull Consumer<Integer> callback;
143         public long datagramStartTime;
144         public boolean skipCheckingSatelliteAligned = false;
145 
SendSatelliteDatagramArgument(int subId, long datagramId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)146         SendSatelliteDatagramArgument(int subId, long datagramId,
147                 @SatelliteManager.DatagramType int datagramType,
148                 @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
149                 @NonNull Consumer<Integer> callback) {
150             this.subId = subId;
151             this.datagramId = datagramId;
152             this.datagramType = datagramType;
153             this.datagram = datagram;
154             this.needFullScreenPointingUI = needFullScreenPointingUI;
155             this.callback = callback;
156         }
157 
158         /** returns the size of outgoing SMS, rounded by 10 bytes */
getDatagramRoundedSizeBytes()159         public int getDatagramRoundedSizeBytes() {
160             if (datagram.getSatelliteDatagram() != null) {
161                 int sizeBytes = datagram.getSatelliteDatagram().length;
162                 // rounded by ROUNDING_UNIT
163                 return (int) (Math.round((double) sizeBytes / ROUNDING_UNIT) * ROUNDING_UNIT);
164             } else {
165                 return 0;
166             }
167         }
168 
169         /** sets the start time at datagram is sent out */
setDatagramStartTime()170         public void setDatagramStartTime() {
171             datagramStartTime =
172                     datagramStartTime == 0 ? System.currentTimeMillis() : datagramStartTime;
173         }
174     }
175 
176     @Override
handleMessage(Message msg)177     public void handleMessage(Message msg) {
178         DatagramDispatcherHandlerRequest request;
179         Message onCompleted;
180         AsyncResult ar;
181 
182         switch(msg.what) {
183             case CMD_SEND_SATELLITE_DATAGRAM: {
184                 logd("CMD_SEND_SATELLITE_DATAGRAM");
185                 request = (DatagramDispatcherHandlerRequest) msg.obj;
186                 SendSatelliteDatagramArgument argument =
187                         (SendSatelliteDatagramArgument) request.argument;
188                 onCompleted = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request);
189 
190                 if (SatelliteModemInterface.getInstance().isSatelliteServiceSupported()) {
191                     SatelliteModemInterface.getInstance().sendSatelliteDatagram(argument.datagram,
192                             argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
193                             argument.needFullScreenPointingUI, onCompleted);
194                     break;
195                 }
196 
197                 Phone phone = request.phone;
198                 if (phone != null) {
199                     phone.sendSatelliteDatagram(onCompleted, argument.datagram,
200                             argument.needFullScreenPointingUI);
201                 } else {
202                     loge("sendSatelliteDatagram: No phone object");
203                     synchronized (mLock) {
204                         // Remove current datagram from pending map
205                         if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
206                             mPendingEmergencyDatagramsMap.remove(argument.datagramId);
207                         } else {
208                             mPendingNonEmergencyDatagramsMap.remove(argument.datagramId);
209                         }
210 
211                         // Update send status
212                         mDatagramController.updateSendStatus(argument.subId,
213                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
214                                 getPendingDatagramCount(),
215                                 SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
216                         mDatagramController.updateSendStatus(argument.subId,
217                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
218                                 0, SatelliteManager.SATELLITE_ERROR_NONE);
219 
220                         // report phone == null case
221                         reportSendDatagramCompleted(argument,
222                                 SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
223                         argument.callback.accept(
224                                 SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
225 
226                         // Abort sending all the pending datagrams
227                         abortSendingPendingDatagrams(argument.subId,
228                                 SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
229                     }
230                 }
231                 break;
232             }
233 
234             case EVENT_SEND_SATELLITE_DATAGRAM_DONE: {
235                 ar = (AsyncResult) msg.obj;
236                 request = (DatagramDispatcherHandlerRequest) ar.userObj;
237                 int error = SatelliteServiceUtils.getSatelliteError(ar, "sendSatelliteDatagram");
238                 SendSatelliteDatagramArgument argument =
239                         (SendSatelliteDatagramArgument) request.argument;
240 
241                 synchronized (mLock) {
242                     if (mIsDemoMode && (error == SatelliteManager.SATELLITE_ERROR_NONE)) {
243                         if (argument.skipCheckingSatelliteAligned) {
244                             logd("Satellite was already aligned. No need to check alignment again");
245                         } else if (!mIsAligned) {
246                             logd("Satellite is not aligned in demo mode, wait for the alignment.");
247                             startSatelliteAlignedTimer(request);
248                             break;
249                         }
250                     }
251 
252                     logd("EVENT_SEND_SATELLITE_DATAGRAM_DONE error: " + error);
253                     // log metrics about the outgoing datagram
254                     reportSendDatagramCompleted(argument, error);
255 
256                     mSendingDatagramInProgress = false;
257 
258                     // Remove current datagram from pending map.
259                     if (argument.datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
260                         mPendingEmergencyDatagramsMap.remove(argument.datagramId);
261                     } else {
262                         mPendingNonEmergencyDatagramsMap.remove(argument.datagramId);
263                     }
264 
265                     if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
266                         // Update send status for current datagram
267                         mDatagramController.updateSendStatus(argument.subId,
268                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
269                                 getPendingDatagramCount(), error);
270                         mControllerMetricsStats.reportOutgoingDatagramSuccessCount(
271                                 argument.datagramType);
272 
273                         if (getPendingDatagramCount() > 0) {
274                             // Send response for current datagram
275                             argument.callback.accept(error);
276                             // Send pending datagrams
277                             sendPendingDatagrams();
278                         } else {
279                             mDatagramController.updateSendStatus(argument.subId,
280                                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
281                                     0, SatelliteManager.SATELLITE_ERROR_NONE);
282                             // Send response for current datagram
283                             argument.callback.accept(error);
284                         }
285                     } else {
286                         // Update send status
287                         mDatagramController.updateSendStatus(argument.subId,
288                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
289                                 getPendingDatagramCount(), error);
290                         mDatagramController.updateSendStatus(argument.subId,
291                                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
292                                 0, SatelliteManager.SATELLITE_ERROR_NONE);
293                         // Send response for current datagram
294                         // after updating datagram transfer state internally.
295                         argument.callback.accept(error);
296                         // Abort sending all the pending datagrams
297                         mControllerMetricsStats.reportOutgoingDatagramFailCount(
298                                 argument.datagramType);
299                         abortSendingPendingDatagrams(argument.subId,
300                                 SatelliteManager.SATELLITE_REQUEST_ABORTED);
301                     }
302                 }
303                 break;
304             }
305 
306             case EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT: {
307                 handleEventSatelliteAlignedTimeout((DatagramDispatcherHandlerRequest) msg.obj);
308                 break;
309             }
310 
311             default:
312                 logw("DatagramDispatcherHandler: unexpected message code: " + msg.what);
313                 break;
314         }
315     }
316 
317     /**
318      * Send datagram over satellite.
319      *
320      * Gateway encodes SOS message or location sharing message into a datagram and passes it as
321      * input to this method. Datagram received here will be passed down to modem without any
322      * encoding or encryption.
323      *
324      * @param subId The subId of the subscription to send satellite datagrams for.
325      * @param datagramType datagram type indicating whether the datagram is of type
326      *                     SOS_SMS or LOCATION_SHARING.
327      * @param datagram encoded gateway datagram which is encrypted by the caller.
328      *                 Datagram will be passed down to modem without any encoding or encryption.
329      * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
330      *                                 full screen mode.
331      * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
332      */
sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)333     public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
334             @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
335             @NonNull Consumer<Integer> callback) {
336         Phone phone = SatelliteServiceUtils.getPhone();
337 
338         long datagramId = mNextDatagramId.getAndUpdate(
339                 n -> ((n + 1) % DatagramController.MAX_DATAGRAM_ID));
340 
341         SendSatelliteDatagramArgument datagramArgs =
342                 new SendSatelliteDatagramArgument(subId, datagramId, datagramType, datagram,
343                         needFullScreenPointingUI, callback);
344 
345         synchronized (mLock) {
346             // Add datagram to pending datagram map
347             if (datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
348                 mPendingEmergencyDatagramsMap.put(datagramId, datagramArgs);
349             } else {
350                 mPendingNonEmergencyDatagramsMap.put(datagramId, datagramArgs);
351             }
352 
353             // Modem can be busy receiving datagrams, so send datagram only when modem is not busy.
354             if (!mSendingDatagramInProgress && mDatagramController.isPollingInIdleState()) {
355                 mSendingDatagramInProgress = true;
356                 datagramArgs.setDatagramStartTime();
357                 mDatagramController.updateSendStatus(subId,
358                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
359                         getPendingDatagramCount(), SatelliteManager.SATELLITE_ERROR_NONE);
360                 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone);
361             }
362         }
363     }
364 
retrySendingDatagrams()365     public void retrySendingDatagrams() {
366         synchronized (mLock) {
367             sendPendingDatagrams();
368         }
369     }
370 
371     /** Set demo mode
372      *
373      * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise.
374      */
375     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setDemoMode(boolean isDemoMode)376     protected void setDemoMode(boolean isDemoMode) {
377         mIsDemoMode = isDemoMode;
378     }
379 
380     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
onDeviceAlignedWithSatellite(boolean isAligned)381     protected void onDeviceAlignedWithSatellite(boolean isAligned) {
382         if (mIsDemoMode) {
383             synchronized (mLock) {
384                 mIsAligned = isAligned;
385                 if (isAligned) handleEventSatelliteAligned();
386             }
387         }
388     }
389 
startSatelliteAlignedTimer(@onNull DatagramDispatcherHandlerRequest request)390     private void startSatelliteAlignedTimer(@NonNull DatagramDispatcherHandlerRequest request) {
391         if (isSatelliteAlignedTimerStarted()) {
392             logd("Satellite aligned timer was already started");
393             return;
394         }
395         mSendSatelliteDatagramRequest = request;
396         sendMessageDelayed(
397                 obtainMessage(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT, request),
398                 getSatelliteAlignedTimeoutDuration());
399     }
400 
401     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
getSatelliteAlignedTimeoutDuration()402     protected long getSatelliteAlignedTimeoutDuration() {
403         return mDatagramController.getSatelliteAlignedTimeoutDuration();
404     }
405 
handleEventSatelliteAligned()406     private void handleEventSatelliteAligned() {
407         if (isSatelliteAlignedTimerStarted()) {
408             stopSatelliteAlignedTimer();
409 
410             if (mSendSatelliteDatagramRequest == null) {
411                 loge("handleEventSatelliteAligned: mSendSatelliteDatagramRequest is null");
412             } else {
413                 SendSatelliteDatagramArgument argument =
414                         (SendSatelliteDatagramArgument) mSendSatelliteDatagramRequest.argument;
415                 argument.skipCheckingSatelliteAligned = true;
416                 Message message = obtainMessage(
417                         EVENT_SEND_SATELLITE_DATAGRAM_DONE, mSendSatelliteDatagramRequest);
418                 mSendSatelliteDatagramRequest = null;
419                 AsyncResult.forMessage(message, null, null);
420                 message.sendToTarget();
421             }
422         }
423     }
424 
handleEventSatelliteAlignedTimeout( @onNull DatagramDispatcherHandlerRequest request)425     private void handleEventSatelliteAlignedTimeout(
426             @NonNull DatagramDispatcherHandlerRequest request) {
427         SatelliteManager.SatelliteException exception =
428                 new SatelliteManager.SatelliteException(
429                         SatelliteManager.SATELLITE_NOT_REACHABLE);
430         Message message = obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request);
431         AsyncResult.forMessage(message, null, exception);
432         message.sendToTarget();
433     }
434 
isSatelliteAlignedTimerStarted()435     private boolean isSatelliteAlignedTimerStarted() {
436         return hasMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT);
437     }
438 
stopSatelliteAlignedTimer()439     private void stopSatelliteAlignedTimer() {
440         removeMessages(EVENT_WAIT_FOR_DEVICE_ALIGNMENT_IN_DEMO_MODE_TIMED_OUT);
441     }
442 
443     /**
444      * Send pending satellite datagrams. Emergency datagrams are given priority over
445      * non-emergency datagrams.
446      */
447     @GuardedBy("mLock")
sendPendingDatagrams()448     private void sendPendingDatagrams() {
449         logd("sendPendingDatagrams()");
450         if (!mDatagramController.isPollingInIdleState()) {
451             // Datagram should be sent to satellite modem when modem is free.
452             logd("sendPendingDatagrams: modem is receiving datagrams");
453             return;
454         }
455 
456         if (getPendingDatagramCount() <= 0) {
457             logd("sendPendingDatagrams: no pending datagrams to send");
458             return;
459         }
460 
461         Phone phone = SatelliteServiceUtils.getPhone();
462         Set<Entry<Long, SendSatelliteDatagramArgument>> pendingDatagram = null;
463         if (!mSendingDatagramInProgress && !mPendingEmergencyDatagramsMap.isEmpty()) {
464             pendingDatagram = mPendingEmergencyDatagramsMap.entrySet();
465         } else if (!mSendingDatagramInProgress && !mPendingNonEmergencyDatagramsMap.isEmpty()) {
466             pendingDatagram = mPendingNonEmergencyDatagramsMap.entrySet();
467         }
468 
469         if ((pendingDatagram != null) && pendingDatagram.iterator().hasNext()) {
470             mSendingDatagramInProgress = true;
471             SendSatelliteDatagramArgument datagramArg =
472                     pendingDatagram.iterator().next().getValue();
473             // Sets the trigger time for getting pending datagrams
474             datagramArg.setDatagramStartTime();
475             mDatagramController.updateSendStatus(datagramArg.subId,
476                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
477                     getPendingDatagramCount(), SatelliteManager.SATELLITE_ERROR_NONE);
478             sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArg, phone);
479         }
480     }
481 
482     /**
483      * Send error code to all the pending datagrams
484      *
485      * @param pendingDatagramsMap The pending datagrams map to be cleaned up.
486      * @param errorCode error code to be returned.
487      */
488     @GuardedBy("mLock")
sendErrorCodeAndCleanupPendingDatagrams( LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap, @SatelliteManager.SatelliteError int errorCode)489     private void sendErrorCodeAndCleanupPendingDatagrams(
490             LinkedHashMap<Long, SendSatelliteDatagramArgument> pendingDatagramsMap,
491             @SatelliteManager.SatelliteError int errorCode) {
492         if (pendingDatagramsMap.size() == 0) {
493             return;
494         }
495         loge("sendErrorCodeAndCleanupPendingDatagrams: cleaning up resources");
496 
497         // Send error code to all the pending datagrams
498         for (Entry<Long, SendSatelliteDatagramArgument> entry :
499                 pendingDatagramsMap.entrySet()) {
500             SendSatelliteDatagramArgument argument = entry.getValue();
501             reportSendDatagramCompleted(argument, errorCode);
502             mControllerMetricsStats.reportOutgoingDatagramFailCount(argument.datagramType);
503             argument.callback.accept(errorCode);
504         }
505 
506         // Clear pending datagram maps
507         pendingDatagramsMap.clear();
508     }
509 
510     /**
511      * Abort sending all the pending datagrams.
512      *
513      * @param subId The subId of the subscription used to send datagram
514      * @param errorCode The error code that resulted in abort.
515      */
516     @GuardedBy("mLock")
abortSendingPendingDatagrams(int subId, @SatelliteManager.SatelliteError int errorCode)517     private void abortSendingPendingDatagrams(int subId,
518             @SatelliteManager.SatelliteError int errorCode) {
519         logd("abortSendingPendingDatagrams()");
520         sendErrorCodeAndCleanupPendingDatagrams(mPendingEmergencyDatagramsMap, errorCode);
521         sendErrorCodeAndCleanupPendingDatagrams(mPendingNonEmergencyDatagramsMap, errorCode);
522     }
523 
524     /**
525      * Return pending datagram count
526      * @return pending datagram count
527      */
528     @GuardedBy("mLock")
getPendingDatagramCount()529     private int getPendingDatagramCount() {
530         return mPendingEmergencyDatagramsMap.size() + mPendingNonEmergencyDatagramsMap.size();
531     }
532 
533     /**
534      * Posts the specified command to be executed on the main thread and returns immediately.
535      *
536      * @param command command to be executed on the main thread
537      * @param argument additional parameters required to perform of the operation
538      * @param phone phone object used to perform the operation.
539      */
sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone)540     private void sendRequestAsync(int command, @NonNull Object argument, @Nullable Phone phone) {
541         DatagramDispatcherHandlerRequest request = new DatagramDispatcherHandlerRequest(
542                 argument, phone);
543         Message msg = this.obtainMessage(command, request);
544         msg.sendToTarget();
545     }
546 
reportSendDatagramCompleted(@onNull SendSatelliteDatagramArgument argument, @NonNull @SatelliteManager.SatelliteError int resultCode)547     private void reportSendDatagramCompleted(@NonNull SendSatelliteDatagramArgument argument,
548             @NonNull @SatelliteManager.SatelliteError int resultCode) {
549         SatelliteStats.getInstance().onSatelliteOutgoingDatagramMetrics(
550                 new SatelliteStats.SatelliteOutgoingDatagramParams.Builder()
551                         .setDatagramType(argument.datagramType)
552                         .setResultCode(resultCode)
553                         .setDatagramSizeBytes(argument.getDatagramRoundedSizeBytes())
554                         .setDatagramTransferTimeMillis(
555                                 System.currentTimeMillis() - argument.datagramStartTime)
556                         .build());
557     }
558 
559     /**
560      * Destroys this DatagramDispatcher. Used for tearing down static resources during testing.
561      */
562     @VisibleForTesting
destroy()563     public void destroy() {
564         sInstance = null;
565     }
566 
567     /**
568      * This function is used by {@link DatagramController} to notify {@link DatagramDispatcher}
569      * that satellite modem state has changed.
570      *
571      * @param state Current satellite modem state.
572      */
onSatelliteModemStateChanged(@atelliteManager.SatelliteModemState int state)573     public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) {
574         synchronized (mLock) {
575             if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF
576                     || state == SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE) {
577                 logd("onSatelliteModemStateChanged: cleaning up resources");
578                 cleanUpResources();
579             } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_IDLE) {
580                 sendPendingDatagrams();
581             }
582         }
583     }
584 
585     @GuardedBy("mLock")
cleanUpResources()586     private void cleanUpResources() {
587         mSendingDatagramInProgress = false;
588         if (getPendingDatagramCount() > 0) {
589             mDatagramController.updateSendStatus(
590                     SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
591                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED,
592                     getPendingDatagramCount(), SatelliteManager.SATELLITE_REQUEST_ABORTED);
593         }
594         mDatagramController.updateSendStatus(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
595                 SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
596                 0, SatelliteManager.SATELLITE_ERROR_NONE);
597         abortSendingPendingDatagrams(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
598                 SatelliteManager.SATELLITE_REQUEST_ABORTED);
599 
600         stopSatelliteAlignedTimer();
601         mIsDemoMode = false;
602         mSendSatelliteDatagramRequest = null;
603         mIsAligned = false;
604     }
605 
logd(@onNull String log)606     private static void logd(@NonNull String log) {
607         Rlog.d(TAG, log);
608     }
609 
loge(@onNull String log)610     private static void loge(@NonNull String log) {
611         Rlog.e(TAG, log);
612     }
613 
logw(@onNull String log)614     private static void logw(@NonNull String log) { Rlog.w(TAG, log); }
615 }
616