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