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 java.util.stream.Collectors.joining; 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.Binder; 26 import android.os.PersistableBundle; 27 import android.telephony.NetworkRegistrationInfo; 28 import android.telephony.Rlog; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.satellite.AntennaPosition; 31 import android.telephony.satellite.NtnSignalStrength; 32 import android.telephony.satellite.PointingInfo; 33 import android.telephony.satellite.SatelliteCapabilities; 34 import android.telephony.satellite.SatelliteDatagram; 35 import android.telephony.satellite.SatelliteManager; 36 import android.telephony.satellite.stub.NTRadioTechnology; 37 import android.telephony.satellite.stub.SatelliteModemState; 38 import android.telephony.satellite.stub.SatelliteResult; 39 40 import com.android.internal.telephony.Phone; 41 import com.android.internal.telephony.PhoneFactory; 42 import com.android.internal.telephony.subscription.SubscriptionManagerService; 43 import com.android.internal.telephony.util.TelephonyUtils; 44 45 import java.util.Arrays; 46 import java.util.HashMap; 47 import java.util.HashSet; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.Set; 51 import java.util.stream.Collectors; 52 53 /** 54 * Utils class for satellite service <-> framework conversions 55 */ 56 public class SatelliteServiceUtils { 57 private static final String TAG = "SatelliteServiceUtils"; 58 59 /** 60 * Convert radio technology from service definition to framework definition. 61 * @param radioTechnology The NTRadioTechnology from the satellite service. 62 * @return The converted NTRadioTechnology for the framework. 63 */ 64 @SatelliteManager.NTRadioTechnology fromSatelliteRadioTechnology(int radioTechnology)65 public static int fromSatelliteRadioTechnology(int radioTechnology) { 66 switch (radioTechnology) { 67 case NTRadioTechnology.NB_IOT_NTN: 68 return SatelliteManager.NT_RADIO_TECHNOLOGY_NB_IOT_NTN; 69 case NTRadioTechnology.NR_NTN: 70 return SatelliteManager.NT_RADIO_TECHNOLOGY_NR_NTN; 71 case NTRadioTechnology.EMTC_NTN: 72 return SatelliteManager.NT_RADIO_TECHNOLOGY_EMTC_NTN; 73 case NTRadioTechnology.PROPRIETARY: 74 return SatelliteManager.NT_RADIO_TECHNOLOGY_PROPRIETARY; 75 default: 76 loge("Received invalid radio technology: " + radioTechnology); 77 return SatelliteManager.NT_RADIO_TECHNOLOGY_UNKNOWN; 78 } 79 } 80 81 /** 82 * Convert satellite error from service definition to framework definition. 83 * @param error The SatelliteError from the satellite service. 84 * @return The converted SatelliteResult for the framework. 85 */ fromSatelliteError(int error)86 @SatelliteManager.SatelliteResult public static int fromSatelliteError(int error) { 87 switch (error) { 88 case SatelliteResult.SATELLITE_RESULT_SUCCESS: 89 return SatelliteManager.SATELLITE_RESULT_SUCCESS; 90 case SatelliteResult.SATELLITE_RESULT_ERROR: 91 return SatelliteManager.SATELLITE_RESULT_ERROR; 92 case SatelliteResult.SATELLITE_RESULT_SERVER_ERROR: 93 return SatelliteManager.SATELLITE_RESULT_SERVER_ERROR; 94 case SatelliteResult.SATELLITE_RESULT_SERVICE_ERROR: 95 return SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR; 96 case SatelliteResult.SATELLITE_RESULT_MODEM_ERROR: 97 return SatelliteManager.SATELLITE_RESULT_MODEM_ERROR; 98 case SatelliteResult.SATELLITE_RESULT_NETWORK_ERROR: 99 return SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR; 100 case SatelliteResult.SATELLITE_RESULT_INVALID_MODEM_STATE: 101 return SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE; 102 case SatelliteResult.SATELLITE_RESULT_INVALID_ARGUMENTS: 103 return SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS; 104 case SatelliteResult.SATELLITE_RESULT_REQUEST_FAILED: 105 return SatelliteManager.SATELLITE_RESULT_REQUEST_FAILED; 106 case SatelliteResult.SATELLITE_RESULT_RADIO_NOT_AVAILABLE: 107 return SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE; 108 case SatelliteResult.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED: 109 return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED; 110 case SatelliteResult.SATELLITE_RESULT_NO_RESOURCES: 111 return SatelliteManager.SATELLITE_RESULT_NO_RESOURCES; 112 case SatelliteResult.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED: 113 return SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED; 114 case SatelliteResult.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS: 115 return SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS; 116 case SatelliteResult.SATELLITE_RESULT_REQUEST_ABORTED: 117 return SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED; 118 case SatelliteResult.SATELLITE_RESULT_ACCESS_BARRED: 119 return SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED; 120 case SatelliteResult.SATELLITE_RESULT_NETWORK_TIMEOUT: 121 return SatelliteManager.SATELLITE_RESULT_NETWORK_TIMEOUT; 122 case SatelliteResult.SATELLITE_RESULT_NOT_REACHABLE: 123 return SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE; 124 case SatelliteResult.SATELLITE_RESULT_NOT_AUTHORIZED: 125 return SatelliteManager.SATELLITE_RESULT_NOT_AUTHORIZED; 126 } 127 loge("Received invalid satellite service error: " + error); 128 return SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR; 129 } 130 131 /** 132 * Convert satellite modem state from service definition to framework definition. 133 * @param modemState The SatelliteModemState from the satellite service. 134 * @return The converted SatelliteModemState for the framework. 135 */ 136 @SatelliteManager.SatelliteModemState fromSatelliteModemState(int modemState)137 public static int fromSatelliteModemState(int modemState) { 138 switch (modemState) { 139 case SatelliteModemState.SATELLITE_MODEM_STATE_IDLE: 140 return SatelliteManager.SATELLITE_MODEM_STATE_IDLE; 141 case SatelliteModemState.SATELLITE_MODEM_STATE_LISTENING: 142 return SatelliteManager.SATELLITE_MODEM_STATE_LISTENING; 143 case SatelliteModemState.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING: 144 return SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING; 145 case SatelliteModemState.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING: 146 return SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING; 147 case SatelliteModemState.SATELLITE_MODEM_STATE_OFF: 148 return SatelliteManager.SATELLITE_MODEM_STATE_OFF; 149 case SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE: 150 return SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE; 151 case SatelliteModemState.SATELLITE_MODEM_STATE_NOT_CONNECTED: 152 return SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED; 153 case SatelliteModemState.SATELLITE_MODEM_STATE_CONNECTED: 154 return SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED; 155 default: 156 loge("Received invalid modem state: " + modemState); 157 return SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN; 158 } 159 } 160 161 /** 162 * Convert SatelliteCapabilities from service definition to framework definition. 163 * @param capabilities The SatelliteCapabilities from the satellite service. 164 * @return The converted SatelliteCapabilities for the framework. 165 */ fromSatelliteCapabilities( @ullable android.telephony.satellite.stub.SatelliteCapabilities capabilities)166 @Nullable public static SatelliteCapabilities fromSatelliteCapabilities( 167 @Nullable android.telephony.satellite.stub.SatelliteCapabilities capabilities) { 168 if (capabilities == null) return null; 169 int[] radioTechnologies = capabilities.supportedRadioTechnologies == null 170 ? new int[0] : capabilities.supportedRadioTechnologies; 171 172 Map<Integer, AntennaPosition> antennaPositionMap = new HashMap<>(); 173 int[] antennaPositionKeys = capabilities.antennaPositionKeys; 174 AntennaPosition[] antennaPositionValues = capabilities.antennaPositionValues; 175 if (antennaPositionKeys != null && antennaPositionValues != null && 176 antennaPositionKeys.length == antennaPositionValues.length) { 177 for(int i = 0; i < antennaPositionKeys.length; i++) { 178 antennaPositionMap.put(antennaPositionKeys[i], antennaPositionValues[i]); 179 } 180 } 181 182 return new SatelliteCapabilities( 183 Arrays.stream(radioTechnologies) 184 .map(SatelliteServiceUtils::fromSatelliteRadioTechnology) 185 .boxed().collect(Collectors.toSet()), 186 capabilities.isPointingRequired, capabilities.maxBytesPerOutgoingDatagram, 187 antennaPositionMap); 188 } 189 190 /** 191 * Convert PointingInfo from service definition to framework definition. 192 * @param pointingInfo The PointingInfo from the satellite service. 193 * @return The converted PointingInfo for the framework. 194 */ fromPointingInfo( android.telephony.satellite.stub.PointingInfo pointingInfo)195 @Nullable public static PointingInfo fromPointingInfo( 196 android.telephony.satellite.stub.PointingInfo pointingInfo) { 197 if (pointingInfo == null) return null; 198 return new PointingInfo(pointingInfo.satelliteAzimuth, pointingInfo.satelliteElevation); 199 } 200 201 /** 202 * Convert SatelliteDatagram from service definition to framework definition. 203 * @param datagram The SatelliteDatagram from the satellite service. 204 * @return The converted SatelliteDatagram for the framework. 205 */ fromSatelliteDatagram( android.telephony.satellite.stub.SatelliteDatagram datagram)206 @Nullable public static SatelliteDatagram fromSatelliteDatagram( 207 android.telephony.satellite.stub.SatelliteDatagram datagram) { 208 if (datagram == null) return null; 209 byte[] data = datagram.data == null ? new byte[0] : datagram.data; 210 return new SatelliteDatagram(data); 211 } 212 213 /** 214 * Convert non-terrestrial signal strength from service definition to framework definition. 215 * @param ntnSignalStrength The non-terrestrial signal strength from the satellite service. 216 * @return The converted non-terrestrial signal strength for the framework. 217 */ fromNtnSignalStrength( android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength)218 @Nullable public static NtnSignalStrength fromNtnSignalStrength( 219 android.telephony.satellite.stub.NtnSignalStrength ntnSignalStrength) { 220 return new NtnSignalStrength(ntnSignalStrength.signalStrengthLevel); 221 } 222 223 /** 224 * Convert SatelliteDatagram from framework definition to service definition. 225 * @param datagram The SatelliteDatagram from the framework. 226 * @return The converted SatelliteDatagram for the satellite service. 227 */ toSatelliteDatagram( @ullable SatelliteDatagram datagram)228 @Nullable public static android.telephony.satellite.stub.SatelliteDatagram toSatelliteDatagram( 229 @Nullable SatelliteDatagram datagram) { 230 android.telephony.satellite.stub.SatelliteDatagram converted = 231 new android.telephony.satellite.stub.SatelliteDatagram(); 232 converted.data = datagram.getSatelliteDatagram(); 233 return converted; 234 } 235 236 /** 237 * Get the {@link SatelliteManager.SatelliteResult} from the provided result. 238 * 239 * @param ar AsyncResult used to determine the error code. 240 * @param caller The satellite request. 241 * 242 * @return The {@link SatelliteManager.SatelliteResult} error code from the request. 243 */ getSatelliteError(@onNull AsyncResult ar, @NonNull String caller)244 @SatelliteManager.SatelliteResult public static int getSatelliteError(@NonNull AsyncResult ar, 245 @NonNull String caller) { 246 int errorCode; 247 if (ar.exception == null) { 248 errorCode = SatelliteManager.SATELLITE_RESULT_SUCCESS; 249 } else { 250 errorCode = SatelliteManager.SATELLITE_RESULT_ERROR; 251 if (ar.exception instanceof SatelliteManager.SatelliteException) { 252 errorCode = ((SatelliteManager.SatelliteException) ar.exception).getErrorCode(); 253 loge(caller + " SatelliteException: " + ar.exception); 254 } else { 255 loge(caller + " unknown exception: " + ar.exception); 256 } 257 } 258 logd(caller + " error: " + errorCode); 259 return errorCode; 260 } 261 262 /** 263 * Get valid subscription id for satellite communication. 264 * 265 * @param subId The subscription id. 266 * @return input subId if the subscription is active else return default subscription id. 267 */ getValidSatelliteSubId(int subId, @NonNull Context context)268 public static int getValidSatelliteSubId(int subId, @NonNull Context context) { 269 final long identity = Binder.clearCallingIdentity(); 270 try { 271 boolean isActive = SubscriptionManagerService.getInstance().isActiveSubId(subId, 272 context.getOpPackageName(), context.getAttributionTag()); 273 274 if (isActive) { 275 return subId; 276 } 277 } finally { 278 Binder.restoreCallingIdentity(identity); 279 } 280 logd("getValidSatelliteSubId: use DEFAULT_SUBSCRIPTION_ID for subId=" + subId); 281 return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; 282 } 283 284 /** 285 * Expected format of the input dictionary bundle is: 286 * <ul> 287 * <li>Key: PLMN string.</li> 288 * <li>Value: A string with format "service_1,service_2,..."</li> 289 * </ul> 290 * @return The map of supported services with key: PLMN, value: set of services supported by 291 * the PLMN. 292 */ 293 @NonNull 294 @NetworkRegistrationInfo.ServiceType parseSupportedSatelliteServices( PersistableBundle supportedServicesBundle)295 public static Map<String, Set<Integer>> parseSupportedSatelliteServices( 296 PersistableBundle supportedServicesBundle) { 297 Map<String, Set<Integer>> supportedServicesMap = new HashMap<>(); 298 if (supportedServicesBundle == null || supportedServicesBundle.isEmpty()) { 299 return supportedServicesMap; 300 } 301 302 for (String plmn : supportedServicesBundle.keySet()) { 303 if (TelephonyUtils.isValidPlmn(plmn)) { 304 Set<Integer> supportedServicesSet = new HashSet<>(); 305 for (int serviceType : supportedServicesBundle.getIntArray(plmn)) { 306 if (TelephonyUtils.isValidService(serviceType)) { 307 supportedServicesSet.add(serviceType); 308 } else { 309 loge("parseSupportedSatelliteServices: invalid service type=" + serviceType 310 + " for plmn=" + plmn); 311 } 312 } 313 logd("parseSupportedSatelliteServices: plmn=" + plmn + ", supportedServicesSet=" 314 + supportedServicesSet.stream().map(String::valueOf).collect( 315 joining(","))); 316 supportedServicesMap.put(plmn, supportedServicesSet); 317 } else { 318 loge("parseSupportedSatelliteServices: invalid plmn=" + plmn); 319 } 320 } 321 return supportedServicesMap; 322 } 323 324 /** 325 * Merge two string lists into one such that the result list does not have any duplicate items. 326 */ 327 @NonNull mergeStrLists(List<String> strList1, List<String> strList2)328 public static List<String> mergeStrLists(List<String> strList1, List<String> strList2) { 329 Set<String> mergedStrSet = new HashSet<>(); 330 mergedStrSet.addAll(strList1); 331 mergedStrSet.addAll(strList2); 332 return mergedStrSet.stream().toList(); 333 } 334 335 /** 336 * Merge three string lists into one such that the result list does not have any duplicate 337 * items. 338 */ 339 @NonNull mergeStrLists(List<String> strList1, List<String> strList2, List<String> strList3)340 public static List<String> mergeStrLists(List<String> strList1, List<String> strList2, 341 List<String> strList3) { 342 Set<String> mergedStrSet = new HashSet<>(); 343 mergedStrSet.addAll(strList1); 344 mergedStrSet.addAll(strList2); 345 mergedStrSet.addAll(strList3); 346 return mergedStrSet.stream().toList(); 347 } 348 349 /** 350 * Check if the datagramType is the sos message (DATAGRAM_TYPE_SOS_MESSAGE, 351 * DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP, 352 * DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED) or not 353 */ isSosMessage(int datagramType)354 public static boolean isSosMessage(int datagramType) { 355 return datagramType == SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE 356 || datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP 357 || datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED; 358 } 359 360 /** 361 * Check if the datagramType is the last sos message 362 * (DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP or 363 * DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED) or not 364 */ isLastSosMessage(int datagramType)365 public static boolean isLastSosMessage(int datagramType) { 366 return datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP 367 || datagramType == SatelliteManager.DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED; 368 } 369 370 /** 371 * Return phone associated with phoneId 0. 372 * 373 * @return phone associated with phoneId 0 or {@code null} if it doesn't exist. 374 */ getPhone()375 public static @Nullable Phone getPhone() { 376 return PhoneFactory.getPhone(0); 377 } 378 379 /** 380 * Return phone associated with subscription ID. 381 * 382 * @return phone associated with {@code subId} or {@code null} if it doesn't exist. 383 */ getPhone(int subId)384 public static @Nullable Phone getPhone(int subId) { 385 return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId)); 386 } 387 logd(@onNull String log)388 private static void logd(@NonNull String log) { 389 Rlog.d(TAG, log); 390 } 391 loge(@onNull String log)392 private static void loge(@NonNull String log) { 393 Rlog.e(TAG, log); 394 } 395 } 396