• 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 android.annotation.NonNull;
20 import android.content.Context;
21 import android.os.Build;
22 import android.os.Looper;
23 import android.os.SystemProperties;
24 import android.telephony.Rlog;
25 import android.telephony.satellite.ISatelliteDatagramCallback;
26 import android.telephony.satellite.SatelliteDatagram;
27 import android.telephony.satellite.SatelliteManager;
28 
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.internal.annotations.VisibleForTesting;
31 
32 import java.util.concurrent.TimeUnit;
33 import java.util.function.Consumer;
34 
35 /**
36  * Datagram controller used for sending and receiving satellite datagrams.
37  */
38 public class DatagramController {
39     private static final String TAG = "DatagramController";
40 
41     @NonNull private static DatagramController sInstance;
42     @NonNull private final Context mContext;
43     @NonNull private final PointingAppController mPointingAppController;
44     @NonNull private final DatagramDispatcher mDatagramDispatcher;
45     @NonNull private final DatagramReceiver mDatagramReceiver;
46     public static final long MAX_DATAGRAM_ID = (long) Math.pow(2, 16);
47     public static final int ROUNDING_UNIT = 10;
48     public static final long SATELLITE_ALIGN_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
49     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
50     private static final boolean DEBUG = !"user".equals(Build.TYPE);
51 
52     /** Variables used to update onSendDatagramStateChanged(). */
53     private final Object mLock = new Object();
54     @GuardedBy("mLock")
55     private int mSendSubId;
56     @GuardedBy("mLock")
57     private @SatelliteManager.SatelliteDatagramTransferState int mSendDatagramTransferState =
58             SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
59     @GuardedBy("mLock")
60     private int mSendPendingCount = 0;
61     @GuardedBy("mLock")
62     private int mSendErrorCode = SatelliteManager.SATELLITE_ERROR_NONE;
63     /** Variables used to update onReceiveDatagramStateChanged(). */
64     @GuardedBy("mLock")
65     private int mReceiveSubId;
66     @GuardedBy("mLock")
67     private @SatelliteManager.SatelliteDatagramTransferState int mReceiveDatagramTransferState =
68             SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
69     @GuardedBy("mLock")
70     private int mReceivePendingCount = 0;
71     @GuardedBy("mLock")
72     private int mReceiveErrorCode = SatelliteManager.SATELLITE_ERROR_NONE;
73 
74     private SatelliteDatagram mDemoModeDatagram;
75     private boolean mIsDemoMode = false;
76     private long mAlignTimeoutDuration = SATELLITE_ALIGN_TIMEOUT;
77 
78     /**
79      * @return The singleton instance of DatagramController.
80      */
getInstance()81     public static DatagramController getInstance() {
82         if (sInstance == null) {
83             loge("DatagramController was not yet initialized.");
84         }
85         return sInstance;
86     }
87 
88     /**
89      * Create the DatagramController singleton instance.
90      * @param context The Context to use to create the DatagramController.
91      * @param looper The looper for the handler.
92      * @param pointingAppController PointingAppController is used to update
93      *                              PointingApp about datagram transfer state changes.
94      * @return The singleton instance of DatagramController.
95      */
make(@onNull Context context, @NonNull Looper looper, @NonNull PointingAppController pointingAppController)96     public static DatagramController make(@NonNull Context context, @NonNull Looper looper,
97             @NonNull PointingAppController pointingAppController) {
98         if (sInstance == null) {
99             sInstance = new DatagramController(context, looper, pointingAppController);
100         }
101         return sInstance;
102     }
103 
104     /**
105      * Create a DatagramController to send and receive satellite datagrams.
106      *
107      * @param context The Context for the DatagramController.
108      * @param looper The looper for the handler
109      * @param pointingAppController PointingAppController is used to update PointingApp
110      *                              about datagram transfer state changes.
111      */
112     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
DatagramController(@onNull Context context, @NonNull Looper looper, @NonNull PointingAppController pointingAppController)113     protected DatagramController(@NonNull Context context, @NonNull Looper  looper,
114             @NonNull PointingAppController pointingAppController) {
115         mContext = context;
116         mPointingAppController = pointingAppController;
117 
118         // Create the DatagramDispatcher singleton,
119         // which is used to send satellite datagrams.
120         mDatagramDispatcher = DatagramDispatcher.make(mContext, looper, this);
121 
122         // Create the DatagramReceiver singleton,
123         // which is used to receive satellite datagrams.
124         mDatagramReceiver = DatagramReceiver.make(mContext, looper, this);
125     }
126 
127     /**
128      * Register to receive incoming datagrams over satellite.
129      *
130      * @param subId The subId of the subscription to register for incoming satellite datagrams.
131      * @param callback The callback to handle incoming datagrams over satellite.
132      *
133      * @return The {@link SatelliteManager.SatelliteError} result of the operation.
134      */
registerForSatelliteDatagram(int subId, @NonNull ISatelliteDatagramCallback callback)135     @SatelliteManager.SatelliteError public int registerForSatelliteDatagram(int subId,
136             @NonNull ISatelliteDatagramCallback callback) {
137         return mDatagramReceiver.registerForSatelliteDatagram(subId, callback);
138     }
139 
140     /**
141      * Unregister to stop receiving incoming datagrams over satellite.
142      * If callback was not registered before, the request will be ignored.
143      *
144      * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
145      * @param callback The callback that was passed to
146      *                 {@link #registerForSatelliteDatagram(int, ISatelliteDatagramCallback)}.
147      */
unregisterForSatelliteDatagram(int subId, @NonNull ISatelliteDatagramCallback callback)148     public void unregisterForSatelliteDatagram(int subId,
149             @NonNull ISatelliteDatagramCallback callback) {
150         mDatagramReceiver.unregisterForSatelliteDatagram(subId, callback);
151     }
152 
153     /**
154      * Poll pending satellite datagrams over satellite.
155      *
156      * This method requests modem to check if there are any pending datagrams to be received over
157      * satellite. If there are any incoming datagrams, they will be received via
158      * {@link android.telephony.satellite.SatelliteDatagramCallback#onSatelliteDatagramReceived(
159      * long, SatelliteDatagram, int, Consumer)}
160      *
161      * @param subId The subId of the subscription used for receiving datagrams.
162      * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
163      */
pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback)164     public void pollPendingSatelliteDatagrams(int subId, @NonNull Consumer<Integer> callback) {
165         mDatagramReceiver.pollPendingSatelliteDatagrams(subId, callback);
166     }
167 
168     /**
169      * Send datagram over satellite.
170      *
171      * Gateway encodes SOS message or location sharing message into a datagram and passes it as
172      * input to this method. Datagram received here will be passed down to modem without any
173      * encoding or encryption.
174      *
175      * When demo mode is on, save the sent datagram and this datagram will be used as a received
176      * datagram.
177      *
178      * @param subId The subId of the subscription to send satellite datagrams for.
179      * @param datagramType datagram type indicating whether the datagram is of type
180      *                     SOS_SMS or LOCATION_SHARING.
181      * @param datagram encoded gateway datagram which is encrypted by the caller.
182      *                 Datagram will be passed down to modem without any encoding or encryption.
183      * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
184      *                                 full screen mode.
185      * @param callback The callback to get {@link SatelliteManager.SatelliteError} of the request.
186      */
sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType, @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI, @NonNull Consumer<Integer> callback)187     public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
188             @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
189             @NonNull Consumer<Integer> callback) {
190         setDemoModeDatagram(datagramType, datagram);
191         mDatagramDispatcher.sendSatelliteDatagram(subId, datagramType, datagram,
192                 needFullScreenPointingUI, callback);
193     }
194 
195     /**
196      * Update send status to {@link PointingAppController}.
197      *
198      * @param subId The subId of the subscription to send satellite datagrams for
199      * @param datagramTransferState The new send datagram transfer state.
200      * @param sendPendingCount number of datagrams that are currently being sent
201      * @param errorCode If datagram transfer failed, the reason for failure.
202      */
updateSendStatus(int subId, @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState, int sendPendingCount, int errorCode)203     public void updateSendStatus(int subId,
204             @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
205             int sendPendingCount, int errorCode) {
206         synchronized (mLock) {
207             logd("updateSendStatus"
208                     + " subId: " + subId
209                     + " datagramTransferState: " + datagramTransferState
210                     + " sendPendingCount: " + sendPendingCount + " errorCode: " + errorCode);
211 
212             mSendSubId = subId;
213             mSendDatagramTransferState = datagramTransferState;
214             mSendPendingCount = sendPendingCount;
215             mSendErrorCode = errorCode;
216 
217             notifyDatagramTransferStateChangedToSessionController();
218             mPointingAppController.updateSendDatagramTransferState(mSendSubId,
219                     mSendDatagramTransferState, mSendPendingCount, mSendErrorCode);
220         }
221     }
222 
223     /**
224      * Update receive status to {@link PointingAppController}.
225      *
226      * @param subId The subId of the subscription used to receive datagrams
227      * @param datagramTransferState The new receive datagram transfer state.
228      * @param receivePendingCount The number of datagrams that are currently pending to be received.
229      * @param errorCode If datagram transfer failed, the reason for failure.
230      */
updateReceiveStatus(int subId, @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState, int receivePendingCount, int errorCode)231     public void updateReceiveStatus(int subId,
232             @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
233             int receivePendingCount, int errorCode) {
234         synchronized (mLock) {
235             logd("updateReceiveStatus"
236                     + " subId: " + subId
237                     + " datagramTransferState: " + datagramTransferState
238                     + " receivePendingCount: " + receivePendingCount + " errorCode: " + errorCode);
239 
240             mReceiveSubId = subId;
241             mReceiveDatagramTransferState = datagramTransferState;
242             mReceivePendingCount = receivePendingCount;
243             mReceiveErrorCode = errorCode;
244 
245             notifyDatagramTransferStateChangedToSessionController();
246             mPointingAppController.updateReceiveDatagramTransferState(mReceiveSubId,
247                     mReceiveDatagramTransferState, mReceivePendingCount, mReceiveErrorCode);
248         }
249 
250         if (isPollingInIdleState()) {
251             mDatagramDispatcher.retrySendingDatagrams();
252         }
253     }
254 
255     /**
256      * Return receive pending datagram count
257      * @return receive pending datagram count.
258      */
getReceivePendingCount()259     public int getReceivePendingCount() {
260         return mReceivePendingCount;
261     }
262 
263     /**
264      * This function is used by {@link SatelliteController} to notify {@link DatagramController}
265      * that satellite modem state has changed.
266      *
267      * @param state Current satellite modem state.
268      */
onSatelliteModemStateChanged(@atelliteManager.SatelliteModemState int state)269     public void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state) {
270         mDatagramDispatcher.onSatelliteModemStateChanged(state);
271         mDatagramReceiver.onSatelliteModemStateChanged(state);
272     }
273 
onDeviceAlignedWithSatellite(boolean isAligned)274     void onDeviceAlignedWithSatellite(boolean isAligned) {
275         mDatagramDispatcher.onDeviceAlignedWithSatellite(isAligned);
276         mDatagramReceiver.onDeviceAlignedWithSatellite(isAligned);
277     }
278 
279     @VisibleForTesting
isReceivingDatagrams()280     public boolean isReceivingDatagrams() {
281         synchronized (mLock) {
282             return (mReceiveDatagramTransferState
283                     == SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING);
284         }
285     }
286 
isSendingInIdleState()287     public boolean isSendingInIdleState() {
288         synchronized (mLock) {
289             return mSendDatagramTransferState ==
290                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
291         }
292     }
293 
isPollingInIdleState()294     public boolean isPollingInIdleState() {
295         synchronized (mLock) {
296             return mReceiveDatagramTransferState ==
297                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
298         }
299     }
300 
301     /**
302      * Set variables for {@link DatagramDispatcher} and {@link DatagramReceiver} to run demo mode
303      * @param isDemoMode {@code true} means demo mode is on, {@code false} otherwise.
304      */
305     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setDemoMode(boolean isDemoMode)306     public void setDemoMode(boolean isDemoMode) {
307         mIsDemoMode = isDemoMode;
308         mDatagramDispatcher.setDemoMode(isDemoMode);
309         mDatagramReceiver.setDemoMode(isDemoMode);
310 
311         if (!isDemoMode) {
312             mDemoModeDatagram = null;
313         }
314     }
315 
316     /** Get the last sent datagram for demo mode */
317     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
getDemoModeDatagram()318     public SatelliteDatagram getDemoModeDatagram() {
319         return mDemoModeDatagram;
320     }
321 
322     /**
323      * Set last sent datagram for demo mode
324      * @param datagramType datagram type, only DATAGRAM_TYPE_SOS_MESSAGE will be saved
325      * @param datagram datagram The last datagram saved when sendSatelliteDatagramForDemo is called
326      */
327     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
setDemoModeDatagram(@atelliteManager.DatagramType int datagramType, SatelliteDatagram datagram)328     protected void setDemoModeDatagram(@SatelliteManager.DatagramType int datagramType,
329             SatelliteDatagram datagram) {
330         if (mIsDemoMode &&  datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE) {
331             mDemoModeDatagram = datagram;
332         }
333     }
334 
getSatelliteAlignedTimeoutDuration()335     long getSatelliteAlignedTimeoutDuration() {
336         return mAlignTimeoutDuration;
337     }
338 
339     /**
340      * This API can be used by only CTS to update the timeout duration in milliseconds whether
341      * the device is aligned with the satellite for demo mode
342      *
343      * @param timeoutMillis The timeout duration in millisecond.
344      * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
345      */
setSatelliteDeviceAlignedTimeoutDuration(long timeoutMillis)346     boolean setSatelliteDeviceAlignedTimeoutDuration(long timeoutMillis) {
347         if (!isMockModemAllowed()) {
348             loge("Updating align timeout duration is not allowed");
349             return false;
350         }
351 
352         logd("setSatelliteDeviceAlignedTimeoutDuration: timeoutMillis=" + timeoutMillis);
353         mAlignTimeoutDuration = timeoutMillis;
354         return true;
355     }
356 
isMockModemAllowed()357     private boolean isMockModemAllowed() {
358         return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false));
359     }
360 
notifyDatagramTransferStateChangedToSessionController()361     private void notifyDatagramTransferStateChangedToSessionController() {
362         SatelliteSessionController sessionController = SatelliteSessionController.getInstance();
363         if (sessionController == null) {
364             loge("notifyDatagramTransferStateChangeToSessionController: SatelliteSessionController"
365                     + " is not initialized yet");
366         } else {
367             sessionController.onDatagramTransferStateChanged(
368                     mSendDatagramTransferState, mReceiveDatagramTransferState);
369         }
370     }
371 
logd(@onNull String log)372     private static void logd(@NonNull String log) {
373         Rlog.d(TAG, log);
374     }
375 
loge(@onNull String log)376     private static void loge(@NonNull String log) {
377         Rlog.e(TAG, log);
378     }
379 }
380