| /* |
| * Copyright (C) 2018 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.hotspot2.omadm; |
| |
| import android.annotation.NonNull; |
| import android.content.Context; |
| import android.net.wifi.EAPConstants; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.server.wifi.hotspot2.SystemInfo; |
| |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| /** |
| * Provides serialization API for DevDetail MO (Management Object). |
| */ |
| public class DevDetailMo { |
| private static final String TAG = "DevDetailMo"; |
| // Refer to 9.2 DevDetail MO vendor specific extensions |
| // in the Hotspot2.0 R2 Technical Specification document in detail |
| @VisibleForTesting |
| public static final String URN = "urn:oma:mo:oma-dm-devdetail:1.0"; |
| @VisibleForTesting |
| public static final String HS20_URN = "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"; |
| |
| private static final String MO_NAME = "DevDetail"; |
| |
| private static final String TAG_EXT = "ext"; |
| private static final String TAG_ORG_WIFI = "org.wi-fi"; |
| private static final String TAG_WIFI = "Wi-Fi"; |
| private static final String TAG_EAP_METHOD_LIST = "EAPMethodList"; //Required field |
| private static final String TAG_EAP_METHOD = "EAPMethod"; //Required field |
| private static final String TAG_EAP_TYPE = "EAPType"; //Required field |
| private static final String TAG_VENDOR_ID = "VendorId"; |
| private static final String TAG_VENDOR_TYPE = "VendorType"; |
| private static final String TAG_INNER_EAP_TYPE = "InnerEAPType"; |
| private static final String TAG_INNER_VENDOR_ID = "InnerVendorID"; |
| private static final String TAG_INNER_VENDOR_TYPE = "InnerVendorType"; |
| private static final String TAG_INNER_METHOD = "InnerMethod"; //Required field |
| |
| // Mobile device information related to certificates provisioned by SPs |
| private static final String TAG_SP_CERTIFICATE = "SPCertificate"; |
| private static final String TAG_CERTIFICATE_ISSUER_NAME = "CertificateIssuerName"; |
| |
| // Required if the mobile device is in possession of an IEEE 802.1ar-compliant |
| // manufacturing certificate and is authorized to use that certificate for |
| // mobile device AAA authentication |
| private static final String TAG_MANUFACTURING_CERT = "ManufacturingCertificate"; |
| // Required for a device having a SIM, but will not provide the IMSI to an SP that |
| // did not issue the IMSI. |
| private static final String TAG_IMSI = "IMSI"; |
| // Required for the device having a SIM. |
| private static final String TAG_IMEI_MEID = "IMEI_MEID"; |
| |
| private static final String TAG_WIFI_MAC_ADDR = "Wi-FiMACAddress"; // Required field |
| |
| // Required field |
| private static final String TAG_CLIENT_TRIGGER_REDIRECT_URI = "ClientTriggerRedirectURI"; |
| |
| private static final String TAG_OPS = "Ops"; |
| private static final String TAG_LAUNCH_BROWSER_TO_URI = "launchBrowserToURI"; |
| private static final String TAG_NEGOTIATE_CLIENT_CERT_TLS = "negotiateClientCertTLS"; |
| private static final String TAG_GET_CERTIFICATE = "getCertificate"; |
| |
| private static final List<String> sSupportedOps = new ArrayList<>(); |
| private static final String TAG_URI = "URI"; |
| private static final String TAG_MAX_DEPTH = "MaxDepth"; |
| private static final String TAG_MAX_TOT_LEN = "MaxTotLen"; |
| private static final String TAG_MAX_SEG_LEN = "MaxSegLen"; |
| private static final String TAG_OEM = "OEM"; |
| private static final String TAG_SW_VER = "SwV"; |
| private static final String TAG_LRG_ORJ = "LrgOrj"; |
| |
| private static final String INNER_METHOD_PAP = "PAP"; |
| private static final String INNER_METHOD_MS_CHAP = "MS-CHAP"; |
| private static final String INNER_METHOD_MS_CHAP_V2 = "MS-CHAP-V2"; |
| |
| private static final String IFNAME = "wlan0"; |
| |
| private static final List<Pair<Integer, String>> sEapMethods = new ArrayList<>(); |
| static { |
| sEapMethods.add(Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_MS_CHAP_V2)); |
| sEapMethods.add(Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_MS_CHAP)); |
| sEapMethods.add(Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_PAP)); |
| |
| sEapMethods.add(Pair.create(EAPConstants.EAP_TLS, null)); |
| sEapMethods.add(Pair.create(EAPConstants.EAP_SIM, null)); |
| sEapMethods.add(Pair.create(EAPConstants.EAP_AKA, null)); |
| sEapMethods.add(Pair.create(EAPConstants.EAP_AKA_PRIME, null)); |
| |
| sSupportedOps.add(TAG_LAUNCH_BROWSER_TO_URI); |
| } |
| |
| // Whether to send IMSI and IMEI information or not during OSU provisioning flow; Mandatory (as |
| // per standard) for mobile devices possessing a SIM card. However, it is unclear why this is |
| // needed. Default to false due to privacy concerns. |
| private static boolean sAllowToSendImsiImeiInfo = false; |
| |
| /** |
| * Allow or disallow to send IMSI and IMEI information during OSU provisioning flow. |
| * |
| * @param allowToSendImsiImeiInfo flag to allow/disallow to send IMSI and IMEI. |
| */ |
| @VisibleForTesting |
| public static void setAllowToSendImsiImeiInfo(boolean allowToSendImsiImeiInfo) { |
| sAllowToSendImsiImeiInfo = allowToSendImsiImeiInfo; |
| } |
| |
| /** |
| * Make a format of XML based on the DDF(Data Definition Format) of DevDetail MO. |
| * |
| * expected_output : refer to Figure 73: example sppPostDevData SOAP message in Hotspot 2.0 |
| * Rel 2.0 Specification document. |
| * @param context {@link Context} |
| * @param info {@link SystemInfo} |
| * @param redirectUri redirect uri that server uses as completion of subscription. |
| * @return the XML that has format of OMA DM DevDetail Management Object, <code>null</code> in |
| * case of any failure. |
| */ |
| public static String serializeToXml(@NonNull Context context, @NonNull SystemInfo info, |
| @NonNull String redirectUri) { |
| String macAddress = info.getMacAddress(IFNAME); |
| if (macAddress != null) { |
| macAddress = macAddress.replace(":", ""); |
| } |
| if (TextUtils.isEmpty(macAddress)) { |
| Log.e(TAG, "mac address is empty"); |
| return null; |
| } |
| MoSerializer moSerializer; |
| try { |
| moSerializer = new MoSerializer(); |
| } catch (ParserConfigurationException e) { |
| Log.e(TAG, "failed to create the MoSerializer: " + e); |
| return null; |
| } |
| |
| // Create the XML document for DevInfoMo |
| Document doc = moSerializer.createNewDocument(); |
| Element rootElement = moSerializer.createMgmtTree(doc); |
| rootElement.appendChild(moSerializer.writeVersion(doc)); |
| // <Node><NodeName>DevDetail</NodeName> |
| Element moNode = moSerializer.createNode(doc, MO_NAME); |
| |
| |
| moNode.appendChild(moSerializer.createNodeForUrn(doc, URN)); |
| // <Node><NodeName>Ext</NodeName> |
| Element extNode = moSerializer.createNode(doc, TAG_EXT); |
| // <Node><NodeName>org.wi-fi</NodeName> |
| Element orgNode = moSerializer.createNode(doc, TAG_ORG_WIFI); |
| orgNode.appendChild(moSerializer.createNodeForUrn(doc, HS20_URN)); |
| // <Node><NodeName>Wi-Fi</NodeName> |
| Element wifiNode = moSerializer.createNode(doc, TAG_WIFI); |
| // <Node><NodeName>EAPMethodList</NodeName> |
| Element eapMethodListNode = moSerializer.createNode(doc, TAG_EAP_METHOD_LIST); |
| |
| String tagName; |
| Element eapMethodNode; |
| |
| int i = 0; |
| for (Pair<Integer, String> entry : sEapMethods) { |
| tagName = String.format("%s%02d", TAG_EAP_METHOD, ++i); |
| eapMethodNode = moSerializer.createNode(doc, tagName); |
| eapMethodNode.appendChild( |
| moSerializer.createNodeForValue(doc, TAG_EAP_TYPE, entry.first.toString())); |
| if (entry.second != null) { |
| eapMethodNode.appendChild( |
| moSerializer.createNodeForValue(doc, TAG_INNER_METHOD, entry.second)); |
| } |
| eapMethodListNode.appendChild(eapMethodNode); |
| |
| } |
| wifiNode.appendChild(eapMethodListNode); // TAG_EAP_METHOD_LIST |
| |
| wifiNode.appendChild(moSerializer.createNodeForValue(doc, TAG_MANUFACTURING_CERT, "FALSE")); |
| wifiNode.appendChild(moSerializer.createNodeForValue(doc, TAG_CLIENT_TRIGGER_REDIRECT_URI, |
| redirectUri)); |
| wifiNode.appendChild(moSerializer.createNodeForValue(doc, TAG_WIFI_MAC_ADDR, macAddress)); |
| |
| // TODO(b/132188983): Inject this using WifiInjector |
| TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); |
| String imsi = telephonyManager |
| .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId()) |
| .getSubscriberId(); |
| if (imsi != null && sAllowToSendImsiImeiInfo) { |
| // Don't provide the IMSI to an SP that did not issue the IMSI |
| wifiNode.appendChild(moSerializer.createNodeForValue(doc, TAG_IMSI, imsi)); |
| wifiNode.appendChild( |
| moSerializer.createNodeForValue(doc, TAG_IMEI_MEID, info.getDeviceId())); |
| } |
| |
| // <Node><NodeName>Ops</NodeName> |
| Element opsNode = moSerializer.createNode(doc, TAG_OPS); |
| for (String op: sSupportedOps) { |
| opsNode.appendChild(moSerializer.createNodeForValue(doc, op, "")); |
| } |
| wifiNode.appendChild(opsNode); // TAG_OPS |
| orgNode.appendChild(wifiNode); // TAG_WIFI |
| extNode.appendChild(orgNode); // TAG_ORG_WIFI |
| moNode.appendChild(extNode); // TAG_EXT |
| // <Node><NodeName>URI</NodeName> |
| Element uriNode = moSerializer.createNode(doc, TAG_URI); |
| |
| uriNode.appendChild(moSerializer.createNodeForValue(doc, TAG_MAX_DEPTH, "32")); |
| uriNode.appendChild(moSerializer.createNodeForValue(doc, TAG_MAX_TOT_LEN, "2048")); |
| uriNode.appendChild(moSerializer.createNodeForValue(doc, TAG_MAX_SEG_LEN, "64")); |
| moNode.appendChild(uriNode); // TAG_URI |
| |
| moNode.appendChild( |
| moSerializer.createNodeForValue(doc, TAG_OEM, info.getDeviceManufacturer())); |
| moNode.appendChild( |
| moSerializer.createNodeForValue(doc, TAG_SW_VER, info.getSoftwareVersion())); |
| moNode.appendChild(moSerializer.createNodeForValue(doc, TAG_LRG_ORJ, "TRUE")); |
| rootElement.appendChild(moNode); // TAG_DEVDETAIL |
| |
| return moSerializer.serialize(doc); |
| } |
| } |