blob: 4c92d6b391ae050c31472685564c480c6c2e9bcf [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.net.wifi.EAPConstants;
import android.net.wifi.WifiEnterpriseConfig;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Base64;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Class for maintaining/caching carrier Wi-Fi network configurations.
*/
public class CarrierNetworkConfig {
private static final String TAG = "CarrierNetworkConfig";
private static final String NETWORK_CONFIG_SEPARATOR = ",";
private static final int ENCODED_SSID_INDEX = 0;
private static final int EAP_TYPE_INDEX = 1;
private static final int CONFIG_ELEMENT_SIZE = 2;
private static final Uri CONTENT_URI = Uri.parse("content://carrier_information/carrier");
private boolean mDbg = false;
private final Map<String, NetworkInfo> mCarrierNetworkMap;
private boolean mIsCarrierImsiEncryptionInfoAvailable = false;
private ImsiEncryptionInfo mLastImsiEncryptionInfo = null; // used for dumpsys only
/**
* Enable/disable verbose logging.
*/
public void enableVerboseLogging(int verbose) {
mDbg = verbose > 0;
}
public CarrierNetworkConfig(@NonNull Context context, @NonNull Looper looper,
@NonNull FrameworkFacade framework) {
mCarrierNetworkMap = new HashMap<>();
updateNetworkConfig(context);
// Monitor for carrier config changes.
IntentFilter filter = new IntentFilter();
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateNetworkConfig(context);
}
}, filter);
framework.registerContentObserver(context, CONTENT_URI, false,
new ContentObserver(new Handler(looper)) {
@Override
public void onChange(boolean selfChange) {
updateNetworkConfig(context);
}
});
}
/**
* @return true if the given SSID is associated with a carrier network
*/
public boolean isCarrierNetwork(String ssid) {
return mCarrierNetworkMap.containsKey(ssid);
}
/**
* @return the EAP type associated with a carrier AP, or -1 if the specified AP
* is not associated with a carrier network
*/
public int getNetworkEapType(String ssid) {
NetworkInfo info = mCarrierNetworkMap.get(ssid);
return info == null ? -1 : info.mEapType;
}
/**
* @return the name of carrier associated with a carrier AP, or null if the specified AP
* is not associated with a carrier network.
*/
public String getCarrierName(String ssid) {
NetworkInfo info = mCarrierNetworkMap.get(ssid);
return info == null ? null : info.mCarrierName;
}
/**
* @return True if carrier IMSI encryption info is available, False otherwise.
*/
public boolean isCarrierEncryptionInfoAvailable() {
return mIsCarrierImsiEncryptionInfoAvailable;
}
/**
* Verify whether carrier IMSI encryption info is available.
*
* @param context Current application context
*
* @return True if carrier IMSI encryption info is available, False otherwise.
*/
private boolean verifyCarrierImsiEncryptionInfoIsAvailable(Context context) {
// TODO(b/132188983): Inject this using WifiInjector
TelephonyManager telephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager == null) {
return false;
}
try {
mLastImsiEncryptionInfo = telephonyManager
.createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId())
.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN);
if (mLastImsiEncryptionInfo == null) {
return false;
}
} catch (RuntimeException e) {
Log.e(TAG, "Failed to get imsi encryption info: " + e.getMessage());
return false;
}
return true;
}
/**
* Utility class for storing carrier network information.
*/
private static class NetworkInfo {
final int mEapType;
final String mCarrierName;
NetworkInfo(int eapType, String carrierName) {
mEapType = eapType;
mCarrierName = carrierName;
}
@Override
public String toString() {
return new StringBuffer("NetworkInfo: eap=").append(mEapType).append(
", carrier=").append(mCarrierName).toString();
}
}
/**
* Update the carrier network map based on the current carrier configuration of the active
* subscriptions.
*
* @param context Current application context
*/
private void updateNetworkConfig(Context context) {
mIsCarrierImsiEncryptionInfoAvailable = verifyCarrierImsiEncryptionInfoIsAvailable(context);
// Reset network map.
mCarrierNetworkMap.clear();
CarrierConfigManager carrierConfigManager =
(CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (carrierConfigManager == null) {
return;
}
SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
if (subscriptionManager == null) {
return;
}
List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList();
if (subInfoList == null) {
return;
}
// Process the carrier config for each active subscription.
for (SubscriptionInfo subInfo : subInfoList) {
CharSequence displayNameCs = subInfo.getDisplayName();
String displayNameStr = displayNameCs == null ? "" : displayNameCs.toString();
PersistableBundle bundle = carrierConfigManager.getConfigForSubId(
subInfo.getSubscriptionId());
processNetworkConfig(bundle, displayNameStr);
}
}
/**
* Process the carrier network config, the network config string is formatted as follow:
*
* "[Base64 Encoded SSID],[EAP Type]"
* Where EAP Type is the standard EAP method number, refer to
* http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml for more info.
* @param carrierConfig The bundle containing the carrier configuration
* @param carrierName The display name of the associated carrier
*/
private void processNetworkConfig(PersistableBundle carrierConfig, String carrierName) {
if (carrierConfig == null) {
return;
}
String[] networkConfigs = carrierConfig.getStringArray(
CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY);
if (mDbg) {
Log.v(TAG, "processNetworkConfig: networkConfigs="
+ Arrays.deepToString(networkConfigs));
}
if (networkConfigs == null) {
return;
}
for (String networkConfig : networkConfigs) {
String[] configArr = networkConfig.split(NETWORK_CONFIG_SEPARATOR);
if (configArr.length != CONFIG_ELEMENT_SIZE) {
Log.e(TAG, "Ignore invalid config: " + networkConfig);
continue;
}
try {
String ssid = new String(Base64.decode(
configArr[ENCODED_SSID_INDEX], Base64.NO_WRAP));
int eapType = parseEapType(Integer.parseInt(configArr[EAP_TYPE_INDEX]));
// Verify EAP type, must be a SIM based EAP type.
if (eapType == -1) {
Log.e(TAG, "Invalid EAP type: " + configArr[EAP_TYPE_INDEX]);
continue;
}
mCarrierNetworkMap.put(ssid, new NetworkInfo(eapType, carrierName));
} catch (NumberFormatException e) {
Log.e(TAG, "Failed to parse EAP type: '" + configArr[EAP_TYPE_INDEX] + "' "
+ e.getMessage());
} catch (IllegalArgumentException e) {
Log.e(TAG, "Failed to decode SSID: '" + configArr[ENCODED_SSID_INDEX] + "' "
+ e.getMessage());
}
}
}
/**
* Convert a standard SIM-based EAP type (SIM, AKA, AKA') to the internal EAP type as defined in
* {@link WifiEnterpriseConfig.Eap}. -1 will be returned if the given EAP type is not
* SIM-based.
*
* @return SIM-based EAP type as defined in {@link WifiEnterpriseConfig.Eap}, or -1 if not
* SIM-based EAP type
*/
private static int parseEapType(int eapType) {
if (eapType == EAPConstants.EAP_SIM) {
return WifiEnterpriseConfig.Eap.SIM;
} else if (eapType == EAPConstants.EAP_AKA) {
return WifiEnterpriseConfig.Eap.AKA;
} else if (eapType == EAPConstants.EAP_AKA_PRIME) {
return WifiEnterpriseConfig.Eap.AKA_PRIME;
}
return -1;
}
/** Dump state. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG + ": ");
pw.println("mCarrierNetworkMap=" + mCarrierNetworkMap);
pw.println("mIsCarrierImsiEncryptionInfoAvailable="
+ mIsCarrierImsiEncryptionInfoAvailable);
pw.println("mLastImsiEncryptionInfo=" + mLastImsiEncryptionInfo);
}
}