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