• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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.google.android.iwlan;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.content.SharedPreferences;
22 import android.location.Country;
23 import android.location.CountryDetector;
24 import android.net.ConnectivityManager;
25 import android.net.IpPrefix;
26 import android.net.LinkAddress;
27 import android.net.LinkProperties;
28 import android.net.Network;
29 import android.os.PersistableBundle;
30 import android.os.SystemClock;
31 import android.telephony.CarrierConfigManager;
32 import android.telephony.SubscriptionInfo;
33 import android.telephony.SubscriptionManager;
34 import android.telephony.TelephonyManager;
35 import android.telephony.ims.ImsManager;
36 import android.telephony.ims.ImsMmTelManager;
37 import android.text.TextUtils;
38 import android.util.Log;
39 
40 import java.net.Inet4Address;
41 import java.net.Inet6Address;
42 import java.net.InetAddress;
43 import java.nio.charset.StandardCharsets;
44 import java.util.ArrayList;
45 import java.util.List;
46 
47 public class IwlanHelper {
48 
49     private static final String TAG = IwlanHelper.class.getSimpleName();
50     private static CountryDetector mCountryDetector;
51     private static final String LAST_KNOWN_COUNTRY_CODE_KEY = "last_known_country_code";
52     private static IpPrefix mNat64Prefix = new IpPrefix("64:ff9b::/96");
53 
getNai(Context context, int slotId, byte[] nextReauthId)54     public static String getNai(Context context, int slotId, byte[] nextReauthId) {
55         if (nextReauthId != null) {
56             return new String(nextReauthId, StandardCharsets.UTF_8);
57         }
58 
59         StringBuilder naiBuilder = new StringBuilder();
60         TelephonyManager tm = context.getSystemService(TelephonyManager.class);
61         SubscriptionInfo subInfo = null;
62         tm = tm.createForSubscriptionId(getSubId(context, slotId));
63 
64         try {
65             subInfo = getSubInfo(context, slotId);
66         } catch (IllegalStateException e) {
67             return null;
68         }
69 
70         String mnc = subInfo.getMncString();
71         mnc = (mnc.length() == 2) ? '0' + mnc : mnc;
72 
73         naiBuilder.append('0').append(tm.getSubscriberId()).append('@');
74 
75         return naiBuilder
76                 .append("nai.epc.mnc")
77                 .append(mnc)
78                 .append(".mcc")
79                 .append(subInfo.getMccString())
80                 .append(".3gppnetwork.org")
81                 .toString();
82     }
83 
getSubId(Context context, int slotId)84     public static int getSubId(Context context, int slotId) {
85         int subid = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
86 
87         try {
88             subid = getSubInfo(context, slotId).getSubscriptionId();
89         } catch (IllegalStateException e) {
90             subid = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
91         }
92         return subid;
93     }
94 
getCarrierId(Context context, int slotId)95     public static int getCarrierId(Context context, int slotId) {
96         TelephonyManager tm = context.getSystemService(TelephonyManager.class);
97         tm = tm.createForSubscriptionId(IwlanHelper.getSubId(context, slotId));
98         return tm.getSimCarrierId();
99     }
100 
getSubInfo(Context context, int slotId)101     private static SubscriptionInfo getSubInfo(Context context, int slotId)
102             throws IllegalStateException {
103         SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
104         SubscriptionInfo info = sm.getActiveSubscriptionInfoForSimSlotIndex(slotId);
105 
106         if (info == null) {
107             throw new IllegalStateException("Subscription info is null.");
108         }
109 
110         return info;
111     }
112 
113     // Retrieves all IP addresses for this Network, including stacked IPv4 link addresses.
getAllAddressesForNetwork(Network network, Context context)114     public static List<InetAddress> getAllAddressesForNetwork(Network network, Context context) {
115         ConnectivityManager connectivityManager =
116                 context.getSystemService(ConnectivityManager.class);
117         List<InetAddress> gatewayList = new ArrayList<>();
118         if (network != null) {
119             LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
120             if (linkProperties != null) {
121                 for (LinkAddress linkAddr : linkProperties.getAllLinkAddresses()) {
122                     InetAddress inetAddr = linkAddr.getAddress();
123                     // skip linklocal and loopback addresses
124                     if (!inetAddr.isLoopbackAddress() && !inetAddr.isLinkLocalAddress()) {
125                         gatewayList.add(inetAddr);
126                     }
127                 }
128                 if (linkProperties.getNat64Prefix() != null) {
129                     mNat64Prefix = linkProperties.getNat64Prefix();
130                 }
131             }
132         }
133         return gatewayList;
134     }
135 
136     /**
137      * The method is to check if this IP address is an IPv4-embedded IPv6 address(Pref64::/n).
138      *
139      * @param ipAddress IP address
140      * @return True if it is an IPv4-embedded IPv6 addres, otherwise false.
141      */
isIpv4EmbeddedIpv6Address(@onNull InetAddress ipAddress)142     public static boolean isIpv4EmbeddedIpv6Address(@NonNull InetAddress ipAddress) {
143         return (ipAddress instanceof Inet6Address) && mNat64Prefix.contains(ipAddress);
144     }
145 
hasIpv6Address(List<InetAddress> localAddresses)146     public static boolean hasIpv6Address(List<InetAddress> localAddresses) {
147         if (localAddresses != null) {
148             for (InetAddress address : localAddresses) {
149                 if (address instanceof Inet6Address) {
150                     return true;
151                 }
152             }
153         }
154         return false;
155     }
156 
hasIpv4Address(List<InetAddress> localAddresses)157     public static boolean hasIpv4Address(List<InetAddress> localAddresses) {
158         if (localAddresses != null) {
159             for (InetAddress address : localAddresses) {
160                 if (address instanceof Inet4Address) {
161                     return true;
162                 }
163             }
164         }
165         return false;
166     }
167 
getIpv4Address(List<InetAddress> localAddresses)168     public static InetAddress getIpv4Address(List<InetAddress> localAddresses) {
169         for (InetAddress address : localAddresses) {
170             if (address instanceof Inet4Address) {
171                 return address;
172             }
173         }
174 
175         throw new IllegalStateException("Local address should not be null.");
176     }
177 
getIpv6Address(List<InetAddress> localAddresses)178     public static InetAddress getIpv6Address(List<InetAddress> localAddresses) {
179         for (InetAddress address : localAddresses) {
180             if (address instanceof Inet6Address) {
181                 return address;
182             }
183         }
184 
185         throw new IllegalStateException("Local address should not be null.");
186     }
187 
getConfig(String key, Context context, int slotId)188     public static <T> T getConfig(String key, Context context, int slotId) {
189         CarrierConfigManager carrierConfigManager =
190                 context.getSystemService(CarrierConfigManager.class);
191         if (carrierConfigManager == null) {
192             throw new IllegalStateException("Carrier config manager is null.");
193         }
194 
195         PersistableBundle bundle =
196                 carrierConfigManager.getConfigForSubId(getSubId(context, slotId));
197 
198         if (bundle == null || bundle.get(key) == null) {
199             return getDefaultConfig(key);
200         } else {
201             return (T) bundle.get(key);
202         }
203     }
204 
getDefaultConfig(String key)205     public static <T> T getDefaultConfig(String key) {
206         PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
207         if (bundle == null) {
208             throw new IllegalStateException("Default config is null for: " + key);
209         }
210         return (T) bundle.get(key);
211     }
212 
isDefaultDataSlot(Context context, int slotId)213     public static boolean isDefaultDataSlot(Context context, int slotId) {
214         SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
215         int ddsSlotId = sm.getSlotIndex(sm.getDefaultDataSubscriptionId());
216         if (ddsSlotId != sm.INVALID_SIM_SLOT_INDEX) {
217             if (ddsSlotId == slotId) {
218                 return true;
219             }
220         }
221         return false;
222     }
223 
isCrossSimCallingEnabled(Context context, int slotId)224     public static boolean isCrossSimCallingEnabled(Context context, int slotId) {
225         boolean isCstEnabled = false;
226         int subid = getSubId(context, slotId);
227 
228         if (subid == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
229             // Fail to query subscription id, just return false.
230             return false;
231         }
232 
233         ImsManager imsManager = context.getSystemService(ImsManager.class);
234         if (imsManager != null) {
235             ImsMmTelManager imsMmTelManager = imsManager.getImsMmTelManager(subid);
236             if (imsMmTelManager != null) {
237                 try {
238                     isCstEnabled = imsMmTelManager.isCrossSimCallingEnabled();
239                 } catch (Exception e) {
240                     // Fail to query Cross-SIM calling setting, just return false to avoid an
241                     // exception.
242                 }
243             }
244         }
245         return isCstEnabled;
246     }
247 
startCountryDetector(Context context)248     public static void startCountryDetector(Context context) {
249         mCountryDetector = context.getSystemService(CountryDetector.class);
250         if (mCountryDetector != null) {
251             updateCountryCodeFromCountryDetector(mCountryDetector.detectCountry());
252 
253             mCountryDetector.addCountryListener(
254                     (newCountry) -> {
255                         updateCountryCodeFromCountryDetector(newCountry);
256                     },
257                     null);
258         }
259     }
260 
261     @NonNull
getLastKnownCountryCode(Context context)262     public static String getLastKnownCountryCode(Context context) {
263         final SharedPreferences prefs =
264                 context.getSharedPreferences(LAST_KNOWN_COUNTRY_CODE_KEY, Context.MODE_PRIVATE);
265         return prefs.getString(LAST_KNOWN_COUNTRY_CODE_KEY, "");
266     }
267 
updateCountryCodeWhenNetworkConnected()268     public static void updateCountryCodeWhenNetworkConnected() {
269         if (mCountryDetector != null) {
270             updateCountryCodeFromCountryDetector(mCountryDetector.detectCountry());
271         }
272     }
273 
updateLastKnownCountryCode(String countryCode)274     private static void updateLastKnownCountryCode(String countryCode) {
275         Context context = IwlanDataService.getContext();
276         final SharedPreferences prefs =
277                 context.getSharedPreferences(LAST_KNOWN_COUNTRY_CODE_KEY, Context.MODE_PRIVATE);
278         final SharedPreferences.Editor editor = prefs.edit();
279         editor.putString(LAST_KNOWN_COUNTRY_CODE_KEY, countryCode);
280         editor.commit();
281         Log.d(TAG, "Update the last known country code in sharedPrefs " + countryCode);
282     }
283 
updateCountryCodeFromCountryDetector(Country country)284     private static void updateCountryCodeFromCountryDetector(Country country) {
285         if (country == null) {
286             return;
287         }
288 
289         if (country.getSource() == Country.COUNTRY_SOURCE_NETWORK
290                 || country.getSource() == Country.COUNTRY_SOURCE_LOCATION) {
291             Context context = IwlanDataService.getContext();
292             String newCountryCode = country.getCountryIso();
293             String lastKnownCountryCode = getLastKnownCountryCode(context);
294             if (!TextUtils.isEmpty(newCountryCode)
295                     && (TextUtils.isEmpty(lastKnownCountryCode)
296                             || !lastKnownCountryCode.equalsIgnoreCase(newCountryCode))) {
297                 updateLastKnownCountryCode(newCountryCode);
298             }
299         }
300     }
301 
elapsedRealtime()302     static long elapsedRealtime() {
303         /*Returns milliseconds since boot, including time spent in sleep.*/
304         return SystemClock.elapsedRealtime();
305     }
306 }
307