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