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.NetworkRegistrationInfo.FIRST_SERVICE_TYPE; 20 import static android.telephony.NetworkRegistrationInfo.LAST_SERVICE_TYPE; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.os.AsyncResult; 26 import android.os.Binder; 27 import android.os.PersistableBundle; 28 import android.telephony.NetworkRegistrationInfo; 29 import android.telephony.Rlog; 30 import android.telephony.SubscriptionManager; 31 import android.telephony.satellite.AntennaPosition; 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.SatelliteError; 38 import android.telephony.satellite.stub.SatelliteModemState; 39 40 import com.android.internal.telephony.CommandException; 41 import com.android.internal.telephony.Phone; 42 import com.android.internal.telephony.PhoneFactory; 43 import com.android.internal.telephony.RILUtils; 44 import com.android.internal.telephony.subscription.SubscriptionManagerService; 45 46 import java.util.Arrays; 47 import java.util.HashMap; 48 import java.util.HashSet; 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 SatelliteError for the framework. 85 */ fromSatelliteError(int error)86 @SatelliteManager.SatelliteError public static int fromSatelliteError(int error) { 87 switch (error) { 88 case SatelliteError.ERROR_NONE: 89 return SatelliteManager.SATELLITE_ERROR_NONE; 90 case SatelliteError.SATELLITE_ERROR: 91 return SatelliteManager.SATELLITE_ERROR; 92 case SatelliteError.SERVER_ERROR: 93 return SatelliteManager.SATELLITE_SERVER_ERROR; 94 case SatelliteError.SERVICE_ERROR: 95 return SatelliteManager.SATELLITE_SERVICE_ERROR; 96 case SatelliteError.MODEM_ERROR: 97 return SatelliteManager.SATELLITE_MODEM_ERROR; 98 case SatelliteError.NETWORK_ERROR: 99 return SatelliteManager.SATELLITE_NETWORK_ERROR; 100 case SatelliteError.INVALID_TELEPHONY_STATE: 101 return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE; 102 case SatelliteError.INVALID_MODEM_STATE: 103 return SatelliteManager.SATELLITE_INVALID_MODEM_STATE; 104 case SatelliteError.INVALID_ARGUMENTS: 105 return SatelliteManager.SATELLITE_INVALID_ARGUMENTS; 106 case SatelliteError.REQUEST_FAILED: 107 return SatelliteManager.SATELLITE_REQUEST_FAILED; 108 case SatelliteError.RADIO_NOT_AVAILABLE: 109 return SatelliteManager.SATELLITE_RADIO_NOT_AVAILABLE; 110 case SatelliteError.REQUEST_NOT_SUPPORTED: 111 return SatelliteManager.SATELLITE_REQUEST_NOT_SUPPORTED; 112 case SatelliteError.NO_RESOURCES: 113 return SatelliteManager.SATELLITE_NO_RESOURCES; 114 case SatelliteError.SERVICE_NOT_PROVISIONED: 115 return SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED; 116 case SatelliteError.SERVICE_PROVISION_IN_PROGRESS: 117 return SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS; 118 case SatelliteError.REQUEST_ABORTED: 119 return SatelliteManager.SATELLITE_REQUEST_ABORTED; 120 case SatelliteError.SATELLITE_ACCESS_BARRED: 121 return SatelliteManager.SATELLITE_ACCESS_BARRED; 122 case SatelliteError.NETWORK_TIMEOUT: 123 return SatelliteManager.SATELLITE_NETWORK_TIMEOUT; 124 case SatelliteError.SATELLITE_NOT_REACHABLE: 125 return SatelliteManager.SATELLITE_NOT_REACHABLE; 126 case SatelliteError.NOT_AUTHORIZED: 127 return SatelliteManager.SATELLITE_NOT_AUTHORIZED; 128 } 129 loge("Received invalid satellite service error: " + error); 130 return SatelliteManager.SATELLITE_SERVICE_ERROR; 131 } 132 133 /** 134 * Convert satellite modem state from service definition to framework definition. 135 * @param modemState The SatelliteModemState from the satellite service. 136 * @return The converted SatelliteModemState for the framework. 137 */ 138 @SatelliteManager.SatelliteModemState fromSatelliteModemState(int modemState)139 public static int fromSatelliteModemState(int modemState) { 140 switch (modemState) { 141 case SatelliteModemState.SATELLITE_MODEM_STATE_IDLE: 142 return SatelliteManager.SATELLITE_MODEM_STATE_IDLE; 143 case SatelliteModemState.SATELLITE_MODEM_STATE_LISTENING: 144 return SatelliteManager.SATELLITE_MODEM_STATE_LISTENING; 145 case SatelliteModemState.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING: 146 return SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING; 147 case SatelliteModemState.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING: 148 return SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING; 149 case SatelliteModemState.SATELLITE_MODEM_STATE_OFF: 150 return SatelliteManager.SATELLITE_MODEM_STATE_OFF; 151 case SatelliteModemState.SATELLITE_MODEM_STATE_UNAVAILABLE: 152 return SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE; 153 default: 154 loge("Received invalid modem state: " + modemState); 155 return SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN; 156 } 157 } 158 159 /** 160 * Convert SatelliteCapabilities from service definition to framework definition. 161 * @param capabilities The SatelliteCapabilities from the satellite service. 162 * @return The converted SatelliteCapabilities for the framework. 163 */ fromSatelliteCapabilities( @ullable android.telephony.satellite.stub.SatelliteCapabilities capabilities)164 @Nullable public static SatelliteCapabilities fromSatelliteCapabilities( 165 @Nullable android.telephony.satellite.stub.SatelliteCapabilities capabilities) { 166 if (capabilities == null) return null; 167 int[] radioTechnologies = capabilities.supportedRadioTechnologies == null 168 ? new int[0] : capabilities.supportedRadioTechnologies; 169 170 Map<Integer, AntennaPosition> antennaPositionMap = new HashMap<>(); 171 int[] antennaPositionKeys = capabilities.antennaPositionKeys; 172 AntennaPosition[] antennaPositionValues = capabilities.antennaPositionValues; 173 if (antennaPositionKeys != null && antennaPositionValues != null && 174 antennaPositionKeys.length == antennaPositionValues.length) { 175 for(int i = 0; i < antennaPositionKeys.length; i++) { 176 antennaPositionMap.put(antennaPositionKeys[i], antennaPositionValues[i]); 177 } 178 } 179 180 return new SatelliteCapabilities( 181 Arrays.stream(radioTechnologies) 182 .map(SatelliteServiceUtils::fromSatelliteRadioTechnology) 183 .boxed().collect(Collectors.toSet()), 184 capabilities.isPointingRequired, capabilities.maxBytesPerOutgoingDatagram, 185 antennaPositionMap); 186 } 187 188 /** 189 * Convert PointingInfo from service definition to framework definition. 190 * @param pointingInfo The PointingInfo from the satellite service. 191 * @return The converted PointingInfo for the framework. 192 */ fromPointingInfo( android.telephony.satellite.stub.PointingInfo pointingInfo)193 @Nullable public static PointingInfo fromPointingInfo( 194 android.telephony.satellite.stub.PointingInfo pointingInfo) { 195 if (pointingInfo == null) return null; 196 return new PointingInfo(pointingInfo.satelliteAzimuth, pointingInfo.satelliteElevation); 197 } 198 199 /** 200 * Convert SatelliteDatagram from service definition to framework definition. 201 * @param datagram The SatelliteDatagram from the satellite service. 202 * @return The converted SatelliteDatagram for the framework. 203 */ fromSatelliteDatagram( android.telephony.satellite.stub.SatelliteDatagram datagram)204 @Nullable public static SatelliteDatagram fromSatelliteDatagram( 205 android.telephony.satellite.stub.SatelliteDatagram datagram) { 206 if (datagram == null) return null; 207 byte[] data = datagram.data == null ? new byte[0] : datagram.data; 208 return new SatelliteDatagram(data); 209 } 210 211 /** 212 * Convert SatelliteDatagram from framework definition to service definition. 213 * @param datagram The SatelliteDatagram from the framework. 214 * @return The converted SatelliteDatagram for the satellite service. 215 */ toSatelliteDatagram( @ullable SatelliteDatagram datagram)216 @Nullable public static android.telephony.satellite.stub.SatelliteDatagram toSatelliteDatagram( 217 @Nullable SatelliteDatagram datagram) { 218 android.telephony.satellite.stub.SatelliteDatagram converted = 219 new android.telephony.satellite.stub.SatelliteDatagram(); 220 converted.data = datagram.getSatelliteDatagram(); 221 return converted; 222 } 223 224 /** 225 * Get the {@link SatelliteManager.SatelliteError} from the provided result. 226 * 227 * @param ar AsyncResult used to determine the error code. 228 * @param caller The satellite request. 229 * 230 * @return The {@link SatelliteManager.SatelliteError} error code from the request. 231 */ getSatelliteError(@onNull AsyncResult ar, @NonNull String caller)232 @SatelliteManager.SatelliteError public static int getSatelliteError(@NonNull AsyncResult ar, 233 @NonNull String caller) { 234 int errorCode; 235 if (ar.exception == null) { 236 errorCode = SatelliteManager.SATELLITE_ERROR_NONE; 237 } else { 238 errorCode = SatelliteManager.SATELLITE_ERROR; 239 if (ar.exception instanceof CommandException) { 240 CommandException.Error error = ((CommandException) ar.exception).getCommandError(); 241 errorCode = RILUtils.convertToSatelliteError(error); 242 loge(caller + " CommandException: " + ar.exception); 243 } else if (ar.exception instanceof SatelliteManager.SatelliteException) { 244 errorCode = ((SatelliteManager.SatelliteException) ar.exception).getErrorCode(); 245 loge(caller + " SatelliteException: " + ar.exception); 246 } else { 247 loge(caller + " unknown exception: " + ar.exception); 248 } 249 } 250 logd(caller + " error: " + errorCode); 251 return errorCode; 252 } 253 254 /** 255 * Get valid subscription id for satellite communication. 256 * 257 * @param subId The subscription id. 258 * @return input subId if the subscription is active else return default subscription id. 259 */ getValidSatelliteSubId(int subId, @NonNull Context context)260 public static int getValidSatelliteSubId(int subId, @NonNull Context context) { 261 final long identity = Binder.clearCallingIdentity(); 262 try { 263 boolean isActive = SubscriptionManagerService.getInstance().isActiveSubId(subId, 264 context.getOpPackageName(), context.getAttributionTag()); 265 266 if (isActive) { 267 return subId; 268 } 269 } finally { 270 Binder.restoreCallingIdentity(identity); 271 } 272 logd("getValidSatelliteSubId: use DEFAULT_SUBSCRIPTION_ID for subId=" + subId); 273 return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; 274 } 275 276 /** 277 * Expected format of each input string in the array: "PLMN_1:service_1,service_2,..." 278 * 279 * @return The map of supported services with key: PLMN, value: set of services supported by 280 * the PLMN. 281 */ 282 @NonNull 283 @NetworkRegistrationInfo.ServiceType parseSupportedSatelliteServices( String[] supportedSatelliteServicesStrArray)284 public static Map<String, Set<Integer>> parseSupportedSatelliteServices( 285 String[] supportedSatelliteServicesStrArray) { 286 Map<String, Set<Integer>> supportedServicesMap = new HashMap<>(); 287 if (supportedSatelliteServicesStrArray == null 288 || supportedSatelliteServicesStrArray.length == 0) { 289 return supportedServicesMap; 290 } 291 292 for (String supportedServicesPerPlmnStr : supportedSatelliteServicesStrArray) { 293 String[] pairOfPlmnAndsupportedServicesStr = 294 supportedServicesPerPlmnStr.split(":"); 295 if (pairOfPlmnAndsupportedServicesStr != null 296 && (pairOfPlmnAndsupportedServicesStr.length == 1 297 || pairOfPlmnAndsupportedServicesStr.length == 2)) { 298 String plmn = pairOfPlmnAndsupportedServicesStr[0]; 299 Set<Integer> supportedServicesSet = new HashSet<>(); 300 if (pairOfPlmnAndsupportedServicesStr.length == 2) { 301 String[] supportedServicesStrArray = 302 pairOfPlmnAndsupportedServicesStr[1].split(","); 303 for (String service : supportedServicesStrArray) { 304 try { 305 int serviceType = Integer.parseInt(service); 306 if (isServiceTypeValid(serviceType)) { 307 supportedServicesSet.add(serviceType); 308 } else { 309 loge("parseSupportedSatelliteServices: invalid serviceType=" 310 + serviceType); 311 } 312 } catch (NumberFormatException e) { 313 loge("parseSupportedSatelliteServices: supportedServicesPerPlmnStr=" 314 + supportedServicesPerPlmnStr + ", service=" + service 315 + ", e=" + e); 316 } 317 } 318 } 319 supportedServicesMap.put(plmn, supportedServicesSet); 320 } else { 321 loge("parseSupportedSatelliteServices: invalid format input, " 322 + "supportedServicesPerPlmnStr=" + supportedServicesPerPlmnStr); 323 } 324 } 325 return supportedServicesMap; 326 } 327 328 /** 329 * Expected format of the input dictionary bundle is: 330 * <ul> 331 * <li>Key: PLMN string.</li> 332 * <li>Value: A string with format "service_1,service_2,..."</li> 333 * </ul> 334 * @return The map of supported services with key: PLMN, value: set of services supported by 335 * the PLMN. 336 */ 337 @NonNull 338 @NetworkRegistrationInfo.ServiceType parseSupportedSatelliteServices( PersistableBundle supportedServicesBundle)339 public static Map<String, Set<Integer>> parseSupportedSatelliteServices( 340 PersistableBundle supportedServicesBundle) { 341 Map<String, Set<Integer>> supportedServicesMap = new HashMap<>(); 342 if (supportedServicesBundle == null || supportedServicesBundle.isEmpty()) { 343 return supportedServicesMap; 344 } 345 346 for (String plmn : supportedServicesBundle.keySet()) { 347 Set<Integer> supportedServicesSet = new HashSet<>(); 348 for (int serviceType : supportedServicesBundle.getIntArray(plmn)) { 349 if (isServiceTypeValid(serviceType)) { 350 supportedServicesSet.add(serviceType); 351 } else { 352 loge("parseSupportedSatelliteServices: invalid service type=" + serviceType 353 + " for plmn=" + plmn); 354 } 355 } 356 supportedServicesMap.put(plmn, supportedServicesSet); 357 } 358 return supportedServicesMap; 359 } 360 361 /** 362 * For the PLMN that exists in both {@code providerSupportedServices} and 363 * {@code carrierSupportedServices}, the supported services will be the intersection of the two 364 * sets. For the PLMN that is present in {@code providerSupportedServices} but not in 365 * {@code carrierSupportedServices}, the provider supported services will be used. The rest 366 * will not be used. 367 * 368 * @param providerSupportedServices Satellite provider supported satellite services. 369 * @param carrierSupportedServices Carrier supported satellite services. 370 * @return The supported satellite services by the device for the corresponding carrier and the 371 * satellite provider. 372 */ 373 @NonNull 374 @NetworkRegistrationInfo.ServiceType mergeSupportedSatelliteServices( @onNull @etworkRegistrationInfo.ServiceType Map<String, Set<Integer>> providerSupportedServices, @NonNull @NetworkRegistrationInfo.ServiceType Map<String, Set<Integer>> carrierSupportedServices)375 public static Map<String, Set<Integer>> mergeSupportedSatelliteServices( 376 @NonNull @NetworkRegistrationInfo.ServiceType Map<String, Set<Integer>> 377 providerSupportedServices, 378 @NonNull @NetworkRegistrationInfo.ServiceType Map<String, Set<Integer>> 379 carrierSupportedServices) { 380 Map<String, Set<Integer>> supportedServicesMap = new HashMap<>(); 381 for (Map.Entry<String, Set<Integer>> entry : providerSupportedServices.entrySet()) { 382 Set<Integer> supportedServices = new HashSet<>(entry.getValue()); 383 if (carrierSupportedServices.containsKey(entry.getKey())) { 384 supportedServices.retainAll(carrierSupportedServices.get(entry.getKey())); 385 } 386 if (!supportedServices.isEmpty()) { 387 supportedServicesMap.put(entry.getKey(), supportedServices); 388 } 389 } 390 return supportedServicesMap; 391 } 392 isServiceTypeValid(int serviceType)393 private static boolean isServiceTypeValid(int serviceType) { 394 return (serviceType >= FIRST_SERVICE_TYPE && serviceType <= LAST_SERVICE_TYPE); 395 } 396 397 /** 398 * Return phone associated with phoneId 0. 399 * 400 * @return phone associated with phoneId 0 or {@code null} if it doesn't exist. 401 */ getPhone()402 public static @Nullable Phone getPhone() { 403 return PhoneFactory.getPhone(0); 404 } 405 logd(@onNull String log)406 private static void logd(@NonNull String log) { 407 Rlog.d(TAG, log); 408 } 409 loge(@onNull String log)410 private static void loge(@NonNull String log) { 411 Rlog.e(TAG, log); 412 } 413 } 414