1 /*
2  * Copyright (C) 2024 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.annotation.Nullable;
21 import android.content.Context;
22 import android.util.ArraySet;
23 import android.util.Log;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.internal.telephony.satellite.nano.SatelliteConfigData;
27 
28 import java.io.ByteArrayInputStream;
29 import java.io.File;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.StandardCopyOption;
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41 
42 /**
43  * SatelliteConfig is utility class for satellite.
44  * It is obtained through the getConfig() at the SatelliteConfigParser.
45  */
46 public class SatelliteConfig {
47 
48     private static final String TAG = "SatelliteConfig";
49     private static final String SATELLITE_DIR_NAME = "satellite";
50     private static final String S2_CELL_FILE_NAME = "s2_cell_file";
51     private int mVersion;
52     private Map<Integer, Map<String, Set<Integer>>> mSupportedServicesPerCarrier;
53     private List<String> mSatelliteRegionCountryCodes;
54     private Boolean mIsSatelliteRegionAllowed;
55     private File mSatS2File;
56     private SatelliteConfigData.SatelliteConfigProto mConfigData;
57 
SatelliteConfig(SatelliteConfigData.SatelliteConfigProto configData)58     public SatelliteConfig(SatelliteConfigData.SatelliteConfigProto configData) {
59         mConfigData = configData;
60         mVersion = mConfigData.version;
61         mSupportedServicesPerCarrier = getCarrierSupportedSatelliteServices();
62         mSatelliteRegionCountryCodes = List.of(
63                 mConfigData.deviceSatelliteRegion.countryCodes);
64         mIsSatelliteRegionAllowed = mConfigData.deviceSatelliteRegion.isAllowed;
65         mSatS2File = null;
66 
67         Log.d(TAG, "mVersion:" + mVersion + " | "
68                 + "mSupportedServicesPerCarrier:" + mSupportedServicesPerCarrier + " | "
69                 + "mSatelliteRegionCountryCodes:"
70                 + String.join(",", mSatelliteRegionCountryCodes) + " | "
71                 + "mIsSatelliteRegionAllowed:" + mIsSatelliteRegionAllowed + " | "
72                 + " | s2CellFile size:" + mConfigData.deviceSatelliteRegion.s2CellFile.length);
73     }
74 
75     /**
76      * @return a Map data with carrier_id, plmns and allowed_services.
77      */
getCarrierSupportedSatelliteServices()78     private Map<Integer, Map<String, Set<Integer>>> getCarrierSupportedSatelliteServices() {
79         SatelliteConfigData.CarrierSupportedSatelliteServicesProto[] satelliteServices =
80                 mConfigData.carrierSupportedSatelliteServices;
81         Map<Integer, Map<String, Set<Integer>>> carrierToServicesMap = new HashMap<>();
82         for (SatelliteConfigData.CarrierSupportedSatelliteServicesProto carrierProto :
83                 satelliteServices) {
84             SatelliteConfigData.SatelliteProviderCapabilityProto[] satelliteCapabilities =
85                     carrierProto.supportedSatelliteProviderCapabilities;
86             Map<String, Set<Integer>> satelliteCapabilityMap = new HashMap<>();
87             for (SatelliteConfigData.SatelliteProviderCapabilityProto capabilityProto :
88                     satelliteCapabilities) {
89                 String carrierPlmn = capabilityProto.carrierPlmn;
90                 Set<Integer> allowedServices = new HashSet<>();
91                 for (int service : capabilityProto.allowedServices) {
92                     allowedServices.add(service);
93                 }
94                 satelliteCapabilityMap.put(carrierPlmn, allowedServices);
95             }
96             carrierToServicesMap.put(carrierProto.carrierId, satelliteCapabilityMap);
97         }
98         return carrierToServicesMap;
99     }
100 
101     /**
102      * Get satellite plmns for carrier
103      *
104      * @param carrierId the carrier identifier.
105      * @return Plmns corresponding to carrier identifier.
106      */
107     @NonNull
getAllSatellitePlmnsForCarrier(int carrierId)108     public List<String> getAllSatellitePlmnsForCarrier(int carrierId) {
109         if (mSupportedServicesPerCarrier != null) {
110             Map<String, Set<Integer>> satelliteCapabilitiesMap = mSupportedServicesPerCarrier.get(
111                     carrierId);
112             if (satelliteCapabilitiesMap != null) {
113                 return new ArrayList<>(satelliteCapabilitiesMap.keySet());
114             }
115         }
116         Log.d(TAG, "getAllSatellitePlmnsForCarrier : mConfigData is null or no config data");
117         return new ArrayList<>();
118     }
119 
120     /**
121      * Get supported satellite services of all providers for a carrier.
122      * The format of the return value - Key: PLMN, Value: Set of supported satellite services.
123      *
124      * @param carrierId the carrier identifier.
125      * @return all supported satellite services for a carrier
126      */
127     @NonNull
getSupportedSatelliteServices(int carrierId)128     public Map<String, Set<Integer>> getSupportedSatelliteServices(int carrierId) {
129         if (mSupportedServicesPerCarrier != null) {
130             Map<String, Set<Integer>> satelliteCapaMap =
131                     mSupportedServicesPerCarrier.get(carrierId);
132             if (satelliteCapaMap != null) {
133                 return satelliteCapaMap;
134             } else {
135                 Log.d(TAG, "No supported services found for carrier=" + carrierId);
136             }
137         } else {
138             Log.d(TAG, "mSupportedServicesPerCarrier is null");
139         }
140         return new HashMap<>();
141     }
142 
143     /**
144      * Get carrier identifier set for the satellite
145      *
146      * @return carrier identifier set from the config data.
147      */
148     @NonNull
getAllSatelliteCarrierIds()149     public Set<Integer> getAllSatelliteCarrierIds() {
150         if (mSupportedServicesPerCarrier != null) {
151             return new ArraySet<>(mSupportedServicesPerCarrier.keySet());
152         }
153         return new ArraySet<>();
154     }
155 
156     /**
157      * @return satellite region country codes
158      */
159     @NonNull
getDeviceSatelliteCountryCodes()160     public List<String> getDeviceSatelliteCountryCodes() {
161         if (mSatelliteRegionCountryCodes != null) {
162             return mSatelliteRegionCountryCodes;
163         }
164         Log.d(TAG, "getDeviceSatelliteCountryCodes : mConfigData is null or no config data");
165         return new ArrayList<>();
166     }
167 
168     /**
169      * @return satellite access allow value, if there is no config data then it returns null.
170      */
171     @Nullable
isSatelliteDataForAllowedRegion()172     public Boolean isSatelliteDataForAllowedRegion() {
173         if (mIsSatelliteRegionAllowed == null) {
174             Log.d(TAG, "getIsSatelliteRegionAllowed : mConfigData is null or no config data");
175         }
176         return mIsSatelliteRegionAllowed;
177     }
178 
179 
180     /**
181      * @param context the Context
182      * @return satellite s2_cell_file path
183      */
184     @Nullable
getSatelliteS2CellFile(@ullable Context context)185     public File getSatelliteS2CellFile(@Nullable Context context) {
186         if (context == null) {
187             Log.d(TAG, "getSatelliteS2CellFile : context is null");
188             return null;
189         }
190 
191         if (isFileExist(mSatS2File)) {
192             Log.d(TAG, "File mSatS2File is already exist");
193             return mSatS2File;
194         }
195 
196         if (mConfigData != null && mConfigData.deviceSatelliteRegion != null) {
197             mSatS2File = copySatS2FileToPhoneDirectory(
198                     context, mConfigData.deviceSatelliteRegion.s2CellFile);
199             return mSatS2File;
200         }
201         Log.d(TAG, "getSatelliteS2CellFile: "
202                 + "mConfigData is null or mConfigData.deviceSatelliteRegion is null");
203         return null;
204     }
205 
206     /**
207      * @param context       the Context
208      * @param byteArrayFile byte array type of protobuffer config data
209      * @return the satellite_cell_file path
210      */
211     @Nullable
212     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
copySatS2FileToPhoneDirectory(@ullable Context context, @Nullable byte[] byteArrayFile)213     public File copySatS2FileToPhoneDirectory(@Nullable Context context,
214             @Nullable byte[] byteArrayFile) {
215 
216         if (context == null || byteArrayFile == null) {
217             Log.d(TAG, "copySatS2FileToPhoneDirectory : context or byteArrayFile are null");
218             return null;
219         }
220 
221         File satS2FileDir = context.getDir(SATELLITE_DIR_NAME, Context.MODE_PRIVATE);
222         if (!satS2FileDir.exists()) {
223             satS2FileDir.mkdirs();
224         }
225 
226         Path targetSatS2FilePath = satS2FileDir.toPath().resolve(S2_CELL_FILE_NAME);
227         try {
228             InputStream inputStream = new ByteArrayInputStream(byteArrayFile);
229             if (inputStream == null) {
230                 Log.d(TAG, "copySatS2FileToPhoneDirectory: Resource=" + S2_CELL_FILE_NAME
231                         + " not found");
232             } else {
233                 Files.copy(inputStream, targetSatS2FilePath, StandardCopyOption.REPLACE_EXISTING);
234             }
235         } catch (IOException ex) {
236             Log.e(TAG, "copySatS2FileToPhoneDirectory: ex=" + ex);
237         }
238         return targetSatS2FilePath.toFile();
239     }
240 
241     /**
242      * @return {@code true} if the SatS2File is already existed and {@code false} otherwise.
243      */
244     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
isFileExist(@ullable File file)245     public boolean isFileExist(@Nullable File file) {
246         if (file == null) {
247             Log.d(TAG, "isFileExist : file is null");
248             return false;
249         }
250         return file.exists();
251     }
252 }
253