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