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.ServiceState.STATE_EMERGENCY_ONLY; 20 import static android.telephony.ServiceState.STATE_IN_SERVICE; 21 22 import static java.util.stream.Collectors.joining; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.content.Context; 27 import android.os.AsyncResult; 28 import android.os.Binder; 29 import android.os.PersistableBundle; 30 import android.telephony.AccessNetworkConstants; 31 import android.telephony.CarrierConfigManager; 32 import android.telephony.CellIdentity; 33 import android.telephony.DropBoxManagerLoggerBackend; 34 import android.telephony.NetworkRegistrationInfo; 35 import android.telephony.PersistentLogger; 36 import android.telephony.ServiceState; 37 import android.telephony.SubscriptionInfo; 38 import android.telephony.SubscriptionManager; 39 import android.telephony.satellite.AntennaPosition; 40 import android.telephony.satellite.EarfcnRange; 41 import android.telephony.satellite.NtnSignalStrength; 42 import android.telephony.satellite.PointingInfo; 43 import android.telephony.satellite.SatelliteCapabilities; 44 import android.telephony.satellite.SatelliteDatagram; 45 import android.telephony.satellite.SatelliteInfo; 46 import android.telephony.satellite.SatelliteManager; 47 import android.telephony.satellite.SatelliteModemEnableRequestAttributes; 48 import android.telephony.satellite.SatelliteSubscriptionInfo; 49 import android.telephony.satellite.SystemSelectionSpecifier; 50 import android.telephony.satellite.stub.NTRadioTechnology; 51 import android.telephony.satellite.stub.SatelliteModemState; 52 import android.telephony.satellite.stub.SatelliteResult; 53 import android.text.TextUtils; 54 import android.util.Log; 55 56 import com.android.internal.R; 57 import com.android.internal.telephony.CommandException; 58 import com.android.internal.telephony.Phone; 59 import com.android.internal.telephony.PhoneFactory; 60 import com.android.internal.telephony.subscription.SubscriptionManagerService; 61 import com.android.internal.telephony.util.TelephonyUtils; 62 63 import java.util.ArrayList; 64 import java.util.Arrays; 65 import java.util.HashMap; 66 import java.util.HashSet; 67 import java.util.List; 68 import java.util.Map; 69 import java.util.Set; 70 import java.util.stream.Collectors; 71 72 /** 73 * Utils class for satellite service <-> framework conversions 74 */ 75 public class SatelliteServiceUtils { 76 private static final String TAG = "SatelliteServiceUtils"; 77 78 /** 79 * Converts a carrier roaming NTN (Non-Terrestrial Network) connect type constant 80 * from {@link CarrierConfigManager} to string. 81 * @param type The carrier roaming NTN connect type constant. 82 * @return A string representation of the connect type, or "Unknown(type)" if not recognized. 83 */ carrierRoamingNtnConnectTypeToString( @arrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_TYPE int type)84 public static String carrierRoamingNtnConnectTypeToString( 85 @CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_TYPE int type) { 86 return switch (type) { 87 case CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC -> "AUTOMATIC"; 88 case CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL -> "MANUAL"; 89 default -> "Unknown(" + type + ")"; 90 }; 91 } 92 93 /** 94 * Convert radio technology from service definition to framework definition. 95 * @param radioTechnology The NTRadioTechnology from the satellite service. 96 * @return The converted NTRadioTechnology for the framework. 97 */ 98 @SatelliteManager.NTRadioTechnology fromSatelliteRadioTechnology(int radioTechnology)99 public static int fromSatelliteRadioTechnology(int radioTechnology) { 100 switch (radioTechnology) { 101 case NTRadioTechnology.NB_IOT_NTN: 102 return SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN; 103 case NTRadioTechnology.NR_NTN: 104 return SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN; 105 case NTRadioTechnology.EMTC_NTN: 106 return SatelliteManager.NT_RADIO_TECHNOLOGY_EMTC_NTN; 107 case NTRadioTechnology.PROPRIETARY: 108 return SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY; 109 default: 110 loge("Received invalid radio technology: " + radioTechnology); 111 return SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN; 112 } 113 } 114 115 /** 116 * Convert satellite error from service definition to framework definition. 117 * @param error The SatelliteError from the satellite service. 118 * @return The converted SatelliteResult for the framework. 119 */ fromSatelliteError(int error)120 @SatelliteManager.SatelliteResult public static int fromSatelliteError(int error) { 121 switch (error) { 122 case SatelliteResult.SATELLITE_RESULT_SUCCESS: 123 return SatelliteManager.SATELLITE_RESULT_SUCCESS; 124 case SatelliteResult.SATELLITE_RESULT_ERROR: 125 return SatelliteManager.SATELLITE_RESULT_ERROR; 126 case SatelliteResult.SATELLITE_RESULT_SERVER_ERROR: 127 return SatelliteManager.SATELLITE_RESULT_SERVER_ERROR; 128 case SatelliteResult.SATELLITE_RESULT_SERVICE_ERROR: 129 return SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR; 130 case SatelliteResult.SATELLITE_RESULT_MODEM_ERROR: 131 return SatelliteManager.SATELLITE_RESULT_MODEM_ERROR; 132 case SatelliteResult.SATELLITE_RESULT_NETWORK_ERROR: 133 return SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR; 134 case SatelliteResult.SATELLITE_RESULT_INVALID_MODEM_STATE: 135 return SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE; 136 case SatelliteResult.SATELLITE_RESULT_INVALID_ARGUMENTS: 137 return SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS; 138 case SatelliteResult.SATELLITE_RESULT_REQUEST_FAILED: 139 return SatelliteManager.SATELLITE_RESULT_REQUEST_FAILED; 140 case SatelliteResult.SATELLITE_RESULT_RADIO_NOT_AVAILABLE: 141 return SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE; 142 case SatelliteResult.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED: 143 return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED; 144 case SatelliteResult.SATELLITE_RESULT_NO_RESOURCES: 145 return SatelliteManager.SATELLITE_RESULT_NO_RESOURCES; 146 case SatelliteResult.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED: 147 return SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED; 148 case SatelliteResult.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS: 149 return SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS; 150 case SatelliteResult.SATELLITE_RESULT_REQUEST_ABORTED: 151 return SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED; 152 case SatelliteResult.SATELLITE_RESULT_ACCESS_BARRED: 153 return SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED; 154 case SatelliteResult.SATELLITE_RESULT_NETWORK_TIMEOUT: 155 return SatelliteManager.SATELLITE_RESULT_NETWORK_TIMEOUT; 156 case SatelliteResult.SATELLITE_RESULT_NOT_REACHABLE: 157 return SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE; 158 case SatelliteResult.SATELLITE_RESULT_NOT_AUTHORIZED: 159 return SatelliteManager.SATELLITE_RESULT_NOT_AUTHORIZED; 160 } 161 loge("Received invalid satellite service error: " + error); 162 return SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR; 163 } 164 165 /** 166 * Convert satellite modem state from service definition to framework definition. 167 * @param modemState The SatelliteModemState from the satellite service. 168 * @return The converted SatelliteModemState for the framework. 169 */ 170 @SatelliteManager.SatelliteModemState fromSatelliteModemState(int modemState)171 public static int fromSatelliteModemState(int modemState) { 172 switch (modemState) { 173 case SatelliteModemState.SATELLITE_MODEM_STATE_IDLE: 174 return SatelliteManager.SATELLITE_MODEM_STATE_IDLE; 175 case SatelliteModemState.SATELLITE_MODEM_STATE_LISTENING: 176 return SatelliteManager.SATELLITE_MODEM_STATE_LISTENING; 177 case SatelliteModemState.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING: 178 return SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING; 179 case SatelliteModemState.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING: 180 return SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING; 181 case SatelliteModemState.SATELLITE_MODEM_STATE_OFF: 182 return SatelliteManager.SATELLITE_MODEM_STATE_OFF; 183 case SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE: 184 return SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE; 185 case SatelliteModemState.SATELLITE_MODEM_STATE_OUT_OF_SERVICE: 186 return SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED; 187 case SatelliteModemState.SATELLITE_MODEM_STATE_IN_SERVICE: 188 return SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED; 189 default: 190 loge("Received invalid modem state: " + modemState); 191 return SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN; 192 } 193 } 194 195 /** 196 * Convert SatelliteCapabilities from service definition to framework definition. 197 * @param capabilities The SatelliteCapabilities from the satellite service. 198 * @return The converted SatelliteCapabilities for the framework. 199 */ fromSatelliteCapabilities( @ullable android.telephony.satellite.stub.SatelliteCapabilities capabilities)200 @Nullable public static SatelliteCapabilities fromSatelliteCapabilities( 201 @Nullable android.telephony.satellite.stub.SatelliteCapabilities capabilities) { 202 if (capabilities == null) return null; 203 int[] radioTechnologies = capabilities.supportedRadioTechnologies == null 204 ? new int[0] : capabilities.supportedRadioTechnologies; 205 206 Map<Integer, AntennaPosition> antennaPositionMap = new HashMap<>(); 207 int[] antennaPositionKeys = capabilities.antennaPositionKeys; 208 AntennaPosition[] antennaPositionValues = capabilities.antennaPositionValues; 209 if (antennaPositionKeys != null && antennaPositionValues != null && 210 antennaPositionKeys.length == antennaPositionValues.length) { 211 for(int i = 0; i < antennaPositionKeys.length; i++) { 212 antennaPositionMap.put(antennaPositionKeys[i], antennaPositionValues[i]); 213 } 214 } 215 216 return new SatelliteCapabilities( 217 Arrays.stream(radioTechnologies) 218 .map(SatelliteServiceUtils::fromSatelliteRadioTechnology) 219 .boxed().collect(Collectors.toSet()), 220 capabilities.isPointingRequired, capabilities.maxBytesPerOutgoingDatagram, 221 antennaPositionMap); 222 } 223 224 /** 225 * Convert PointingInfo from service definition to framework definition. 226 * @param pointingInfo The PointingInfo from the satellite service. 227 * @return The converted PointingInfo for the framework. 228 */ fromPointingInfo( android.telephony.satellite.stub.PointingInfo pointingInfo)229 @Nullable public static PointingInfo fromPointingInfo( 230 android.telephony.satellite.stub.PointingInfo pointingInfo) { 231 if (pointingInfo == null) return null; 232 return new PointingInfo(pointingInfo.satelliteAzimuth, pointingInfo.satelliteElevation); 233 } 234 235 /** 236 * Convert SatelliteDatagram from service definition to framework definition. 237 * @param datagram The SatelliteDatagram from the satellite service. 238 * @return The converted SatelliteDatagram for the framework. 239 */ fromSatelliteDatagram( android.telephony.satellite.stub.SatelliteDatagram datagram)240 @Nullable public static SatelliteDatagram fromSatelliteDatagram( 241 android.telephony.satellite.stub.SatelliteDatagram datagram) { 242 if (datagram == null) return null; 243 byte[] data = datagram.data == null ? new byte[0] : datagram.data; 244 return new SatelliteDatagram(data); 245 } 246 247 /** 248 * Convert non-terrestrial signal strength from service definition to framework definition. 249 * @param ntnSignalStrength The non-terrestrial signal strength from the satellite service. 250 * @return The converted non-terrestrial signal strength for the framework. 251 */ fromNtnSignalStrength( android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength)252 @Nullable public static NtnSignalStrength fromNtnSignalStrength( 253 android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength) { 254 return new NtnSignalStrength(ntnSignalStrength.signalStrengthLevel); 255 } 256 257 /** 258 * Convert SatelliteDatagram from framework definition to service definition. 259 * @param datagram The SatelliteDatagram from the framework. 260 * @return The converted SatelliteDatagram for the satellite service. 261 */ toSatelliteDatagram( @ullable SatelliteDatagram datagram)262 @Nullable public static android.telephony.satellite.stub.SatelliteDatagram toSatelliteDatagram( 263 @Nullable SatelliteDatagram datagram) { 264 android.telephony.satellite.stub.SatelliteDatagram converted = 265 new android.telephony.satellite.stub.SatelliteDatagram(); 266 converted.data = datagram.getSatelliteDatagram(); 267 return converted; 268 } 269 270 /** 271 * Convert SatelliteSubscriptionInfo from framework definition to service definition. 272 * @param info The SatelliteSubscriptionInfo from the framework. 273 * @return The converted SatelliteSubscriptionInfo for the satellite service. 274 */ 275 @NonNull public static android.telephony.satellite.stub toSatelliteSubscriptionInfo( @onNull SatelliteSubscriptionInfo info )276 .SatelliteSubscriptionInfo toSatelliteSubscriptionInfo( 277 @NonNull SatelliteSubscriptionInfo info 278 ) { 279 android.telephony.satellite.stub.SatelliteSubscriptionInfo converted = 280 new android.telephony.satellite.stub.SatelliteSubscriptionInfo(); 281 converted.iccId = info.getIccId(); 282 converted.niddApn = info.getNiddApn(); 283 return converted; 284 } 285 286 /** 287 * Convert SatelliteModemEnableRequestAttributes from framework definition to service definition 288 * @param attributes The SatelliteModemEnableRequestAttributes from the framework. 289 * @return The converted SatelliteModemEnableRequestAttributes for the satellite service. 290 */ 291 @NonNull public static android.telephony.satellite.stub toSatelliteModemEnableRequestAttributes( @onNull SatelliteModemEnableRequestAttributes attributes )292 .SatelliteModemEnableRequestAttributes toSatelliteModemEnableRequestAttributes( 293 @NonNull SatelliteModemEnableRequestAttributes attributes 294 ) { 295 android.telephony.satellite.stub.SatelliteModemEnableRequestAttributes converted = 296 new android.telephony.satellite.stub.SatelliteModemEnableRequestAttributes(); 297 converted.isEnabled = attributes.isEnabled(); 298 converted.isDemoMode = attributes.isForDemoMode(); 299 converted.isEmergencyMode = attributes.isForEmergencyMode(); 300 converted.satelliteSubscriptionInfo = toSatelliteSubscriptionInfo( 301 attributes.getSatelliteSubscriptionInfo()); 302 return converted; 303 } 304 305 /** 306 * Get the {@link SatelliteManager.SatelliteResult} from the provided result. 307 * 308 * @param ar AsyncResult used to determine the error code. 309 * @param caller The satellite request. 310 * 311 * @return The {@link SatelliteManager.SatelliteResult} error code from the request. 312 */ getSatelliteError(@onNull AsyncResult ar, @NonNull String caller)313 @SatelliteManager.SatelliteResult public static int getSatelliteError(@NonNull AsyncResult ar, 314 @NonNull String caller) { 315 int errorCode; 316 if (ar.exception == null) { 317 errorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS; 318 } else { 319 errorCode = SatelliteManager.SATELLITE_RESULT_ERROR; 320 if (ar.exception instanceof SatelliteManager.SatelliteException) { 321 errorCode = ((SatelliteManager.SatelliteException) ar.exception).getErrorCode(); 322 loge(caller + " SatelliteException: " + ar.exception); 323 } else if (ar.exception instanceof CommandException) { 324 errorCode = convertCommandExceptionErrorToSatelliteError( 325 ((CommandException) ar.exception).getCommandError()); 326 loge(caller + " CommandException: " + ar.exception); 327 } else { 328 loge(caller + " unknown exception: " + ar.exception); 329 } 330 } 331 logd(caller + " error: " + errorCode); 332 return errorCode; 333 } 334 convertCommandExceptionErrorToSatelliteError( CommandException.Error commandExceptionError)335 private static int convertCommandExceptionErrorToSatelliteError( 336 CommandException.Error commandExceptionError) { 337 logd("convertCommandExceptionErrorToSatelliteError: commandExceptionError=" 338 + commandExceptionError.toString()); 339 340 switch(commandExceptionError) { 341 case REQUEST_NOT_SUPPORTED: 342 return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED; 343 case RADIO_NOT_AVAILABLE: 344 return SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE; 345 case INTERNAL_ERR: 346 case INVALID_STATE: 347 case INVALID_MODEM_STATE: 348 return SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE; 349 case MODEM_ERR: 350 return SatelliteManager.SATELLITE_RESULT_MODEM_ERROR; 351 default: 352 return SatelliteManager.SATELLITE_RESULT_ERROR; 353 } 354 } 355 356 /** 357 * Get valid subscription id for satellite communication. 358 * 359 * @param subId The subscription id. 360 * @return input subId if the subscription is active else return default subscription id. 361 */ getValidSatelliteSubId(int subId, @NonNull Context context)362 public static int getValidSatelliteSubId(int subId, @NonNull Context context) { 363 final long identity = Binder.clearCallingIdentity(); 364 try { 365 boolean isActive = SubscriptionManagerService.getInstance().isActiveSubId(subId, 366 context.getOpPackageName(), context.getAttributionTag()); 367 368 if (isActive) { 369 return subId; 370 } 371 } finally { 372 Binder.restoreCallingIdentity(identity); 373 } 374 logd("getValidSatelliteSubId: use DEFAULT_SUBSCRIPTION_ID for subId=" + subId); 375 return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; 376 } 377 378 /** 379 * Get the subscription ID which supports OEM based NTN satellite service. 380 * 381 * @return ID of the subscription that supports OEM-based satellite if any, 382 * return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} otherwise. 383 */ getNtnOnlySubscriptionId(@onNull Context context)384 public static int getNtnOnlySubscriptionId(@NonNull Context context) { 385 List<SubscriptionInfo> infoList = 386 SubscriptionManagerService.getInstance().getAllSubInfoList( 387 context.getOpPackageName(), null); 388 389 int subId = infoList.stream() 390 .filter(info -> info.isOnlyNonTerrestrialNetwork()) 391 .mapToInt(SubscriptionInfo::getSubscriptionId) 392 .findFirst() 393 .orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID); 394 logd("getNtnOnlySubscriptionId: subId=" + subId); 395 return subId; 396 } 397 398 /** 399 * Check if the subscription ID is a NTN only subscription ID. 400 * 401 * @return {@code true} if the subscription ID is a NTN only subscription ID, 402 * {@code false} otherwise. 403 */ isNtnOnlySubscriptionId(int subId)404 public static boolean isNtnOnlySubscriptionId(int subId) { 405 SubscriptionManagerService subscriptionManagerService = 406 SubscriptionManagerService.getInstance(); 407 if (subscriptionManagerService == null) { 408 logd("isNtnOnlySubscriptionId: subscriptionManagerService is null"); 409 return false; 410 } 411 412 SubscriptionInfo subInfo = subscriptionManagerService.getSubscriptionInfo(subId); 413 if (subInfo == null) { 414 logd("isNtnOnlySubscriptionId: subInfo is null for subId=" + subId); 415 return false; 416 } 417 418 return subInfo.isOnlyNonTerrestrialNetwork(); 419 } 420 421 /** 422 * Expected format of the input dictionary bundle is: 423 * <ul> 424 * <li>Key: PLMN string.</li> 425 * <li>Value: A string with format "service_1,service_2,..."</li> 426 * </ul> 427 * @return The map of supported services with key: PLMN, value: set of services supported by 428 * the PLMN. 429 */ 430 @NonNull 431 @NetworkRegistrationInfo.ServiceType parseSupportedSatelliteServices( PersistableBundle supportedServicesBundle)432 public static Map<String, Set<Integer>> parseSupportedSatelliteServices( 433 PersistableBundle supportedServicesBundle) { 434 Map<String, Set<Integer>> supportedServicesMap = new HashMap<>(); 435 if (supportedServicesBundle == null || supportedServicesBundle.isEmpty()) { 436 return supportedServicesMap; 437 } 438 439 for (String plmn : supportedServicesBundle.keySet()) { 440 if (TelephonyUtils.isValidPlmn(plmn)) { 441 Set<Integer> supportedServicesSet = new HashSet<>(); 442 for (int serviceType : supportedServicesBundle.getIntArray(plmn)) { 443 if (TelephonyUtils.isValidService(serviceType)) { 444 supportedServicesSet.add(serviceType); 445 } else { 446 loge("parseSupportedSatelliteServices: invalid service type=" + serviceType 447 + " for plmn=" + plmn); 448 } 449 } 450 logd("parseSupportedSatelliteServices: plmn=" + plmn + ", supportedServicesSet=" 451 + supportedServicesSet.stream().map(String::valueOf).collect( 452 joining(","))); 453 supportedServicesMap.put(plmn, supportedServicesSet); 454 } else { 455 loge("parseSupportedSatelliteServices: invalid plmn=" + plmn); 456 } 457 } 458 return supportedServicesMap; 459 } 460 461 /** 462 * Merge two string lists into one such that the result list does not have any duplicate items. 463 */ 464 @NonNull mergeStrLists(List<String> strList1, List<String> strList2)465 public static List<String> mergeStrLists(List<String> strList1, List<String> strList2) { 466 Set<String> mergedStrSet = new HashSet<>(); 467 if (strList1 != null) { 468 mergedStrSet.addAll(strList1); 469 } 470 471 if (strList2 != null) { 472 mergedStrSet.addAll(strList2); 473 } 474 475 return mergedStrSet.stream().toList(); 476 } 477 478 /** 479 * Merge three string lists into one such that the result list does not have any duplicate 480 * items. 481 */ 482 @NonNull mergeStrLists(List<String> strList1, List<String> strList2, List<String> strList3)483 public static List<String> mergeStrLists(List<String> strList1, List<String> strList2, 484 List<String> strList3) { 485 Set<String> mergedStrSet = new HashSet<>(); 486 mergedStrSet.addAll(strList1); 487 mergedStrSet.addAll(strList2); 488 mergedStrSet.addAll(strList3); 489 return mergedStrSet.stream().toList(); 490 } 491 492 /** 493 * Check if the datagramType is the sos message (DATAGRAM_TYPE_SOS_MESSAGE, 494 * DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP, 495 * DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED) or not 496 */ isSosMessage(int datagramType)497 public static boolean isSosMessage(int datagramType) { 498 return datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE 499 || datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP 500 || datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED; 501 } 502 503 /** 504 * Check if the datagramType is the last sos message 505 * (DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP or 506 * DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED) or not 507 */ isLastSosMessage(int datagramType)508 public static boolean isLastSosMessage(int datagramType) { 509 return datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP 510 || datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED; 511 } 512 513 /** 514 * Return phone associated with phoneId 0. 515 * 516 * @return phone associated with phoneId 0 or {@code null} if it doesn't exist. 517 */ getPhone()518 public static @Nullable Phone getPhone() { 519 return PhoneFactory.getPhone(0); 520 } 521 522 /** 523 * Return phone associated with subscription ID. 524 * 525 * @return phone associated with {@code subId} or {@code null} if it doesn't exist. 526 */ getPhone(int subId)527 public static @Nullable Phone getPhone(int subId) { 528 return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); 529 } 530 531 /** Return {@code true} if device has cellular coverage, else return {@code false}. */ isCellularAvailable()532 public static boolean isCellularAvailable() { 533 for (Phone phone : PhoneFactory.getPhones()) { 534 ServiceState serviceState = phone.getServiceState(); 535 if (serviceState != null) { 536 int state = serviceState.getState(); 537 NetworkRegistrationInfo dataNri = serviceState.getNetworkRegistrationInfo( 538 NetworkRegistrationInfo.DOMAIN_PS, 539 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 540 boolean isCellularDataInService = dataNri != null && dataNri.isInService(); 541 logd("isCellularAvailable: phoneId=" + phone.getPhoneId() + " state=" + state 542 + " isEmergencyOnly=" + serviceState.isEmergencyOnly() 543 + " isCellularDataInService=" + isCellularDataInService); 544 545 if ((state == STATE_IN_SERVICE || state == STATE_EMERGENCY_ONLY 546 || serviceState.isEmergencyOnly() 547 || isCellularDataInService) 548 && !isSatellitePlmn(phone.getSubId(), serviceState)) { 549 logd("isCellularAvailable true"); 550 return true; 551 } 552 } 553 } 554 logd("isCellularAvailable false"); 555 return false; 556 } 557 558 /** Check whether device is connected to satellite PLMN */ isSatellitePlmn(int subId, @NonNull ServiceState serviceState)559 public static boolean isSatellitePlmn(int subId, @NonNull ServiceState serviceState) { 560 List<String> satellitePlmnList = new ArrayList<>( 561 SatelliteController.getInstance().getAllPlmnSet()); 562 if (satellitePlmnList.isEmpty()) { 563 logd("isSatellitePlmn: satellitePlmnList is empty"); 564 return false; 565 } 566 567 for (NetworkRegistrationInfo nri : 568 serviceState.getNetworkRegistrationInfoListForTransportType( 569 AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) { 570 String registeredPlmn = nri.getRegisteredPlmn(); 571 String mccmnc = getMccMnc(nri); 572 if (TextUtils.isEmpty(registeredPlmn) && TextUtils.isEmpty(mccmnc)) { 573 logd("isSatellitePlmn: registeredPlmn and cell plmn are empty"); 574 continue; 575 } 576 577 for (String satellitePlmn : satellitePlmnList) { 578 if (TextUtils.equals(satellitePlmn, registeredPlmn) 579 || TextUtils.equals(satellitePlmn, mccmnc)) { 580 logd("isSatellitePlmn: return true, satellitePlmn:" + satellitePlmn 581 + " registeredPlmn:" + registeredPlmn + " mccmnc:" + mccmnc); 582 return true; 583 } 584 } 585 } 586 587 logd("isSatellitePlmn: return false"); 588 return false; 589 } 590 591 /** Get mccmnc string from NetworkRegistrationInfo. */ 592 @Nullable getMccMnc(@onNull NetworkRegistrationInfo nri)593 public static String getMccMnc(@NonNull NetworkRegistrationInfo nri) { 594 CellIdentity cellIdentity = nri.getCellIdentity(); 595 if (cellIdentity == null) { 596 logd("getMccMnc: cellIdentity is null"); 597 return null; 598 } 599 600 String mcc = cellIdentity.getMccString(); 601 String mnc = cellIdentity.getMncString(); 602 if (mcc == null || mnc == null) { 603 logd("getMccMnc: mcc or mnc is null. mcc=" + mcc + " mnc=" + mnc); 604 return null; 605 } 606 607 return mcc + mnc; 608 } 609 610 @NonNull 611 private static android.telephony.satellite.stub convertSystemSelectionSpecifierToHALFormat( @onNull SystemSelectionSpecifier systemSelectionSpecifier)612 .SystemSelectionSpecifier convertSystemSelectionSpecifierToHALFormat( 613 @NonNull SystemSelectionSpecifier systemSelectionSpecifier) { 614 android.telephony.satellite.stub.SystemSelectionSpecifier convertedSpecifier = 615 new android.telephony.satellite.stub.SystemSelectionSpecifier(); 616 617 convertedSpecifier.mMccMnc = systemSelectionSpecifier.getMccMnc(); 618 convertedSpecifier.mBands = systemSelectionSpecifier.getBands(); 619 convertedSpecifier.mEarfcs = systemSelectionSpecifier.getEarfcns(); 620 SatelliteInfo[] satelliteInfos = systemSelectionSpecifier.getSatelliteInfos() 621 .toArray(new SatelliteInfo[0]); 622 android.telephony.satellite.stub.SatelliteInfo[] halSatelliteInfos = 623 new android.telephony.satellite.stub.SatelliteInfo[satelliteInfos.length]; 624 for (int i = 0; i < satelliteInfos.length; i++) { 625 halSatelliteInfos[i] = new android.telephony.satellite.stub.SatelliteInfo(); 626 627 halSatelliteInfos[i].id = new android.telephony.satellite.stub.UUID(); 628 halSatelliteInfos[i].id.mostSigBits = 629 satelliteInfos[i].getSatelliteId().getMostSignificantBits(); 630 halSatelliteInfos[i].id.leastSigBits = 631 satelliteInfos[i].getSatelliteId().getLeastSignificantBits(); 632 633 halSatelliteInfos[i].position = 634 new android.telephony.satellite.stub.SatellitePosition(); 635 halSatelliteInfos[i].position.longitudeDegree = 636 satelliteInfos[i].getSatellitePosition().getLongitudeDegrees(); 637 halSatelliteInfos[i].position.altitudeKm = 638 satelliteInfos[i].getSatellitePosition().getAltitudeKm(); 639 640 halSatelliteInfos[i].bands = satelliteInfos[i].getBands().stream().mapToInt( 641 Integer::intValue).toArray(); 642 643 List<EarfcnRange> earfcnRangeList = satelliteInfos[i].getEarfcnRanges(); 644 halSatelliteInfos[i].earfcnRanges = 645 new android.telephony.satellite.stub.EarfcnRange[earfcnRangeList.size()]; 646 for (int j = 0; j < earfcnRangeList.size(); j++) { 647 halSatelliteInfos[i].earfcnRanges[j] = 648 new android.telephony.satellite.stub.EarfcnRange(); 649 halSatelliteInfos[i].earfcnRanges[j].startEarfcn = earfcnRangeList.get( 650 j).getStartEarfcn(); 651 halSatelliteInfos[i].earfcnRanges[j].endEarfcn = earfcnRangeList.get( 652 j).getEndEarfcn(); 653 } 654 } 655 convertedSpecifier.satelliteInfos = halSatelliteInfos; 656 convertedSpecifier.tagIds = systemSelectionSpecifier.getTagIds(); 657 return convertedSpecifier; 658 } 659 660 /** 661 * Convert SystemSelectionSpecifier from framework definition to service definition 662 * @param systemSelectionSpecifier The SystemSelectionSpecifier from the framework. 663 * @return The converted SystemSelectionSpecifier for the satellite service. 664 */ 665 @NonNull 666 public static List<android.telephony.satellite.stub toSystemSelectionSpecifier( @onNull List<SystemSelectionSpecifier> systemSelectionSpecifier)667 .SystemSelectionSpecifier> toSystemSelectionSpecifier( 668 @NonNull List<SystemSelectionSpecifier> systemSelectionSpecifier) { 669 return systemSelectionSpecifier.stream().map( 670 SatelliteServiceUtils::convertSystemSelectionSpecifierToHALFormat).collect( 671 Collectors.toList()); 672 } 673 674 /** 675 * Expected format of the input dictionary bundle is: 676 * <ul> 677 * <li>Key: Regional satellite config Id string.</li> 678 * <li>Value: Integer arrays of earfcns in the corresponding regions."</li> 679 * </ul> 680 * @return The map of earfcns with key: regional satellite config Id, 681 * value: set of earfcns in the corresponding regions. 682 */ 683 @NonNull parseRegionalSatelliteEarfcns( @ullable PersistableBundle earfcnsBundle)684 public static Map<String, Set<Integer>> parseRegionalSatelliteEarfcns( 685 @Nullable PersistableBundle earfcnsBundle) { 686 Map<String, Set<Integer>> earfcnsMap = new HashMap<>(); 687 if (earfcnsBundle == null || earfcnsBundle.isEmpty()) { 688 logd("parseRegionalSatelliteEarfcns: earfcnsBundle is null or empty"); 689 return earfcnsMap; 690 } 691 692 for (String configId : earfcnsBundle.keySet()) { 693 Set<Integer> earfcnsSet = new HashSet<>(); 694 for (int earfcn : earfcnsBundle.getIntArray(configId)) { 695 earfcnsSet.add(earfcn); 696 } 697 logd("parseRegionalSatelliteEarfcns: configId = " + configId + ", earfcns =" 698 + earfcnsSet.stream().map(String::valueOf).collect(joining(","))); 699 earfcnsMap.put(configId, earfcnsSet); 700 } 701 return earfcnsMap; 702 } 703 704 /** 705 * Returns a persistent logger to persist important log because logcat logs may not be 706 * retained long enough. 707 * 708 * @return a PersistentLogger, return {@code null} if it is not supported or encounters 709 * exception. 710 */ 711 @Nullable getPersistentLogger(@onNull Context context)712 public static PersistentLogger getPersistentLogger(@NonNull Context context) { 713 try { 714 if (context.getResources().getBoolean( 715 R.bool.config_dropboxmanager_persistent_logging_enabled)) { 716 return new PersistentLogger(DropBoxManagerLoggerBackend.getInstance(context)); 717 } 718 } catch (RuntimeException ex) { 719 loge("getPersistentLogger: RuntimeException ex=" + ex); 720 } 721 return null; 722 } 723 724 /** Determines whether the subscription is in carrier roaming NB-IoT NTN or not. */ isNbIotNtn(int subId)725 public static boolean isNbIotNtn(int subId) { 726 Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); 727 if (phone == null) { 728 logd("isNbIotNtn(): phone is null"); 729 return false; 730 } 731 732 SatelliteController satelliteController = SatelliteController.getInstance(); 733 if (satelliteController == null) { 734 logd("isNbIotNtn(): satelliteController is null"); 735 return false; 736 } 737 738 return satelliteController.isInCarrierRoamingNbIotNtn(phone); 739 } 740 logd(@onNull String log)741 private static void logd(@NonNull String log) { 742 Log.d(TAG, log); 743 } 744 loge(@onNull String log)745 private static void loge(@NonNull String log) { 746 Log.e(TAG, log); 747 } 748 logv(@onNull String log)749 private static void logv(@NonNull String log) { 750 Log.v(TAG, log); 751 } 752 } 753