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