1/* 2 * Copyright (C) 2017 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 17package com.android.server.wifi; 18 19import android.annotation.NonNull; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.database.ContentObserver; 25import android.net.Uri; 26import android.net.wifi.EAPConstants; 27import android.net.wifi.WifiEnterpriseConfig; 28import android.os.Handler; 29import android.os.Looper; 30import android.os.PersistableBundle; 31import android.telephony.CarrierConfigManager; 32import android.telephony.ImsiEncryptionInfo; 33import android.telephony.SubscriptionInfo; 34import android.telephony.SubscriptionManager; 35import android.telephony.TelephonyManager; 36import android.util.Base64; 37import android.util.Log; 38 39import java.util.HashMap; 40import java.util.List; 41import java.util.Map; 42 43/** 44 * Class for maintaining/caching carrier Wi-Fi network configurations. 45 */ 46public class CarrierNetworkConfig { 47 private static final String TAG = "CarrierNetworkConfig"; 48 49 private static final String NETWORK_CONFIG_SEPARATOR = ","; 50 private static final int ENCODED_SSID_INDEX = 0; 51 private static final int EAP_TYPE_INDEX = 1; 52 private static final int CONFIG_ELEMENT_SIZE = 2; 53 private static final Uri CONTENT_URI = Uri.parse("content://carrier_information/carrier"); 54 55 private final Map<String, NetworkInfo> mCarrierNetworkMap; 56 private boolean mIsCarrierImsiEncryptionInfoAvailable = false; 57 58 public CarrierNetworkConfig(@NonNull Context context, @NonNull Looper looper, 59 @NonNull FrameworkFacade framework) { 60 mCarrierNetworkMap = new HashMap<>(); 61 updateNetworkConfig(context); 62 63 // Monitor for carrier config changes. 64 IntentFilter filter = new IntentFilter(); 65 filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 66 context.registerReceiver(new BroadcastReceiver() { 67 @Override 68 public void onReceive(Context context, Intent intent) { 69 updateNetworkConfig(context); 70 } 71 }, filter); 72 73 framework.registerContentObserver(context, CONTENT_URI, false, 74 new ContentObserver(new Handler(looper)) { 75 @Override 76 public void onChange(boolean selfChange) { 77 updateNetworkConfig(context); 78 } 79 }); 80 } 81 82 /** 83 * @return true if the given SSID is associated with a carrier network 84 */ 85 public boolean isCarrierNetwork(String ssid) { 86 return mCarrierNetworkMap.containsKey(ssid); 87 } 88 89 /** 90 * @return the EAP type associated with a carrier AP, or -1 if the specified AP 91 * is not associated with a carrier network 92 */ 93 public int getNetworkEapType(String ssid) { 94 NetworkInfo info = mCarrierNetworkMap.get(ssid); 95 return info == null ? -1 : info.mEapType; 96 } 97 98 /** 99 * @return the name of carrier associated with a carrier AP, or null if the specified AP 100 * is not associated with a carrier network. 101 */ 102 public String getCarrierName(String ssid) { 103 NetworkInfo info = mCarrierNetworkMap.get(ssid); 104 return info == null ? null : info.mCarrierName; 105 } 106 107 /** 108 * @return True if carrier IMSI encryption info is available, False otherwise. 109 */ 110 public boolean isCarrierEncryptionInfoAvailable() { 111 return mIsCarrierImsiEncryptionInfoAvailable; 112 } 113 114 /** 115 * Verify whether carrier IMSI encryption info is available. 116 * 117 * @param context Current application context 118 * 119 * @return True if carrier IMSI encryption info is available, False otherwise. 120 */ 121 private boolean verifyCarrierImsiEncryptionInfoIsAvailable(Context context) { 122 TelephonyManager telephonyManager = 123 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 124 if (telephonyManager == null) { 125 return false; 126 } 127 try { 128 ImsiEncryptionInfo imsiEncryptionInfo = telephonyManager 129 .getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN); 130 if (imsiEncryptionInfo == null) { 131 return false; 132 } 133 } catch (RuntimeException e) { 134 Log.e(TAG, "Failed to get imsi encryption info: " + e.getMessage()); 135 return false; 136 } 137 138 return true; 139 } 140 141 /** 142 * Utility class for storing carrier network information. 143 */ 144 private static class NetworkInfo { 145 final int mEapType; 146 final String mCarrierName; 147 148 NetworkInfo(int eapType, String carrierName) { 149 mEapType = eapType; 150 mCarrierName = carrierName; 151 } 152 } 153 154 /** 155 * Update the carrier network map based on the current carrier configuration of the active 156 * subscriptions. 157 * 158 * @param context Current application context 159 */ 160 private void updateNetworkConfig(Context context) { 161 mIsCarrierImsiEncryptionInfoAvailable = verifyCarrierImsiEncryptionInfoIsAvailable(context); 162 163 // Reset network map. 164 mCarrierNetworkMap.clear(); 165 166 CarrierConfigManager carrierConfigManager = 167 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 168 if (carrierConfigManager == null) { 169 return; 170 } 171 172 SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService( 173 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 174 if (subscriptionManager == null) { 175 return; 176 } 177 List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList(); 178 if (subInfoList == null) { 179 return; 180 } 181 182 // Process the carrier config for each active subscription. 183 for (SubscriptionInfo subInfo : subInfoList) { 184 processNetworkConfig( 185 carrierConfigManager.getConfigForSubId(subInfo.getSubscriptionId()), 186 subInfo.getDisplayName().toString()); 187 } 188 } 189 190 /** 191 * Process the carrier network config, the network config string is formatted as follow: 192 * 193 * "[Base64 Encoded SSID],[EAP Type]" 194 * Where EAP Type is the standard EAP method number, refer to 195 * http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml for more info. 196 197 * @param carrierConfig The bundle containing the carrier configuration 198 * @param carrierName The display name of the associated carrier 199 */ 200 private void processNetworkConfig(PersistableBundle carrierConfig, String carrierName) { 201 if (carrierConfig == null) { 202 return; 203 } 204 String[] networkConfigs = carrierConfig.getStringArray( 205 CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY); 206 if (networkConfigs == null) { 207 return; 208 } 209 210 for (String networkConfig : networkConfigs) { 211 String[] configArr = networkConfig.split(NETWORK_CONFIG_SEPARATOR); 212 if (configArr.length != CONFIG_ELEMENT_SIZE) { 213 Log.e(TAG, "Ignore invalid config: " + networkConfig); 214 continue; 215 } 216 try { 217 String ssid = new String(Base64.decode( 218 configArr[ENCODED_SSID_INDEX], Base64.DEFAULT)); 219 int eapType = parseEapType(Integer.parseInt(configArr[EAP_TYPE_INDEX])); 220 // Verify EAP type, must be a SIM based EAP type. 221 if (eapType == -1) { 222 Log.e(TAG, "Invalid EAP type: " + configArr[EAP_TYPE_INDEX]); 223 continue; 224 } 225 mCarrierNetworkMap.put(ssid, new NetworkInfo(eapType, carrierName)); 226 } catch (NumberFormatException e) { 227 Log.e(TAG, "Failed to parse EAP type: " + e.getMessage()); 228 } catch (IllegalArgumentException e) { 229 Log.e(TAG, "Failed to decode SSID: " + e.getMessage()); 230 } 231 } 232 } 233 234 /** 235 * Convert a standard SIM-based EAP type (SIM, AKA, AKA') to the internal EAP type as defined in 236 * {@link WifiEnterpriseConfig.Eap}. -1 will be returned if the given EAP type is not 237 * SIM-based. 238 * 239 * @return SIM-based EAP type as defined in {@link WifiEnterpriseConfig.Eap}, or -1 if not 240 * SIM-based EAP type 241 */ 242 private static int parseEapType(int eapType) { 243 if (eapType == EAPConstants.EAP_SIM) { 244 return WifiEnterpriseConfig.Eap.SIM; 245 } else if (eapType == EAPConstants.EAP_AKA) { 246 return WifiEnterpriseConfig.Eap.AKA; 247 } else if (eapType == EAPConstants.EAP_AKA_PRIME) { 248 return WifiEnterpriseConfig.Eap.AKA_PRIME; 249 } 250 return -1; 251 } 252} 253