1 /* 2 * Copyright (C) 2018 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.android.server.wifi.hotspot2.omadm; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.net.wifi.EAPConstants; 22 import android.telephony.SubscriptionManager; 23 import android.telephony.TelephonyManager; 24 import android.text.TextUtils; 25 import android.util.Log; 26 import android.util.Pair; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.server.wifi.hotspot2.SystemInfo; 30 31 import org.w3c.dom.Document; 32 import org.w3c.dom.Element; 33 34 import java.util.ArrayList; 35 import java.util.List; 36 37 import javax.xml.parsers.ParserConfigurationException; 38 39 /** 40 * Provides serialization API for DevDetail MO (Management Object). 41 */ 42 public class DevDetailMo { 43 private static final String TAG = "DevDetailMo"; 44 // Refer to 9.2 DevDetail MO vendor specific extensions 45 // in the Hotspot2.0 R2 Technical Specification document in detail 46 @VisibleForTesting 47 public static final String URN = "urn:oma:mo:oma-dm-devdetail:1.0"; 48 @VisibleForTesting 49 public static final String HS20_URN = "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"; 50 51 private static final String MO_NAME = "DevDetail"; 52 53 private static final String TAG_EXT = "ext"; 54 private static final String TAG_ORG_WIFI = "org.wi-fi"; 55 private static final String TAG_WIFI = "Wi-Fi"; 56 private static final String TAG_EAP_METHOD_LIST = "EAPMethodList"; //Required field 57 private static final String TAG_EAP_METHOD = "EAPMethod"; //Required field 58 private static final String TAG_EAP_TYPE = "EAPType"; //Required field 59 private static final String TAG_VENDOR_ID = "VendorId"; 60 private static final String TAG_VENDOR_TYPE = "VendorType"; 61 private static final String TAG_INNER_EAP_TYPE = "InnerEAPType"; 62 private static final String TAG_INNER_VENDOR_ID = "InnerVendorID"; 63 private static final String TAG_INNER_VENDOR_TYPE = "InnerVendorType"; 64 private static final String TAG_INNER_METHOD = "InnerMethod"; //Required field 65 66 // Mobile device information related to certificates provisioned by SPs 67 private static final String TAG_SP_CERTIFICATE = "SPCertificate"; 68 private static final String TAG_CERTIFICATE_ISSUER_NAME = "CertificateIssuerName"; 69 70 // Required if the mobile device is in possession of an IEEE 802.1ar-compliant 71 // manufacturing certificate and is authorized to use that certificate for 72 // mobile device AAA authentication 73 private static final String TAG_MANUFACTURING_CERT = "ManufacturingCertificate"; 74 // Required for a device having a SIM, but will not provide the IMSI to an SP that 75 // did not issue the IMSI. 76 private static final String TAG_IMSI = "IMSI"; 77 // Required for the device having a SIM. 78 private static final String TAG_IMEI_MEID = "IMEI_MEID"; 79 80 private static final String TAG_WIFI_MAC_ADDR = "Wi-FiMACAddress"; // Required field 81 82 // Required field 83 private static final String TAG_CLIENT_TRIGGER_REDIRECT_URI = "ClientTriggerRedirectURI"; 84 85 private static final String TAG_OPS = "Ops"; 86 private static final String TAG_LAUNCH_BROWSER_TO_URI = "launchBrowserToURI"; 87 private static final String TAG_NEGOTIATE_CLIENT_CERT_TLS = "negotiateClientCertTLS"; 88 private static final String TAG_GET_CERTIFICATE = "getCertificate"; 89 90 private static final List<String> sSupportedOps = new ArrayList<>(); 91 private static final String TAG_URI = "URI"; 92 private static final String TAG_MAX_DEPTH = "MaxDepth"; 93 private static final String TAG_MAX_TOT_LEN = "MaxTotLen"; 94 private static final String TAG_MAX_SEG_LEN = "MaxSegLen"; 95 private static final String TAG_DEV_TYPE = "DevType"; 96 private static final String TAG_OEM = "OEM"; 97 private static final String TAG_FW_VER = "FwV"; 98 private static final String TAG_SW_VER = "SwV"; 99 private static final String TAG_HW_VER = "HwV"; 100 private static final String TAG_LRG_ORJ = "LrgOrj"; 101 102 private static final String INNER_METHOD_PAP = "PAP"; 103 private static final String INNER_METHOD_MS_CHAP = "MS-CHAP"; 104 private static final String INNER_METHOD_MS_CHAP_V2 = "MS-CHAP-V2"; 105 106 private static final String IFNAME = "wlan0"; 107 private static final String DEVICE_TYPE = "Smartphone"; 108 109 private static final List<Pair<Integer, String>> sEapMethods = new ArrayList<>(); 110 static { Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_MS_CHAP_V2)111 sEapMethods.add(Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_MS_CHAP_V2)); Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_MS_CHAP)112 sEapMethods.add(Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_MS_CHAP)); Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_PAP)113 sEapMethods.add(Pair.create(EAPConstants.EAP_TTLS, INNER_METHOD_PAP)); 114 Pair.create(EAPConstants.EAP_TLS, null)115 sEapMethods.add(Pair.create(EAPConstants.EAP_TLS, null)); Pair.create(EAPConstants.EAP_SIM, null)116 sEapMethods.add(Pair.create(EAPConstants.EAP_SIM, null)); Pair.create(EAPConstants.EAP_AKA, null)117 sEapMethods.add(Pair.create(EAPConstants.EAP_AKA, null)); Pair.create(EAPConstants.EAP_AKA_PRIME, null)118 sEapMethods.add(Pair.create(EAPConstants.EAP_AKA_PRIME, null)); 119 120 sSupportedOps.add(TAG_LAUNCH_BROWSER_TO_URI); 121 } 122 123 // Whether to send IMSI and IMEI information or not during OSU provisioning flow; Mandatory (as 124 // per standard) for mobile devices possessing a SIM card. However, it is unclear why this is 125 // needed. Default to false due to privacy concerns. 126 private static boolean sAllowToSendImsiImeiInfo = false; 127 128 /** 129 * Allow or disallow to send IMSI and IMEI information during OSU provisioning flow. 130 * 131 * @param allowToSendImsiImeiInfo flag to allow/disallow to send IMSI and IMEI. 132 */ 133 @VisibleForTesting setAllowToSendImsiImeiInfo(boolean allowToSendImsiImeiInfo)134 public static void setAllowToSendImsiImeiInfo(boolean allowToSendImsiImeiInfo) { 135 sAllowToSendImsiImeiInfo = allowToSendImsiImeiInfo; 136 } 137 138 /** 139 * Make a format of XML based on the DDF(Data Definition Format) of DevDetail MO. 140 * 141 * expected_output : refer to Figure 73: example sppPostDevData SOAP message in Hotspot 2.0 142 * Rel 2.0 Specification document. 143 * @param context {@link Context} 144 * @param info {@link SystemInfo} 145 * @param redirectUri redirect uri that server uses as completion of subscription. 146 * @return the XML that has format of OMA DM DevDetail Management Object, <code>null</code> in 147 * case of any failure. 148 */ serializeToXml(@onNull Context context, @NonNull SystemInfo info, @NonNull String redirectUri)149 public static String serializeToXml(@NonNull Context context, @NonNull SystemInfo info, 150 @NonNull String redirectUri) { 151 String macAddress = info.getMacAddress(IFNAME); 152 if (macAddress != null) { 153 macAddress = macAddress.replace(":", ""); 154 } 155 if (TextUtils.isEmpty(macAddress)) { 156 Log.e(TAG, "mac address is empty"); 157 return null; 158 } 159 MoSerializer moSerializer; 160 try { 161 moSerializer = new MoSerializer(); 162 } catch (ParserConfigurationException e) { 163 Log.e(TAG, "failed to create the MoSerializer: " + e); 164 return null; 165 } 166 167 // Create the XML document for DevInfoMo 168 Document doc = moSerializer.createNewDocument(); 169 Element rootElement = moSerializer.createMgmtTree(doc); 170 rootElement.appendChild(moSerializer.writeVersion(doc)); 171 // <Node><NodeName>DevDetail</NodeName> 172 Element moNode = moSerializer.createNode(doc, MO_NAME); 173 174 175 moNode.appendChild(moSerializer.createNodeForUrn(doc, URN)); 176 // <Node><NodeName>Ext</NodeName> 177 Element extNode = moSerializer.createNode(doc, TAG_EXT); 178 // <Node><NodeName>org.wi-fi</NodeName> 179 Element orgNode = moSerializer.createNode(doc, TAG_ORG_WIFI); 180 orgNode.appendChild(moSerializer.createNodeForUrn(doc, HS20_URN)); 181 // <Node><NodeName>Wi-Fi</NodeName> 182 Element wifiNode = moSerializer.createNode(doc, TAG_WIFI); 183 // <Node><NodeName>EAPMethodList</NodeName> 184 Element eapMethodListNode = moSerializer.createNode(doc, TAG_EAP_METHOD_LIST); 185 186 String tagName; 187 Element eapMethodNode; 188 189 int i = 0; 190 for (Pair<Integer, String> entry : sEapMethods) { 191 tagName = String.format("%s%02d", TAG_EAP_METHOD, ++i); 192 eapMethodNode = moSerializer.createNode(doc, tagName); 193 eapMethodNode.appendChild( 194 moSerializer.createNodeForValue(doc, TAG_EAP_TYPE, entry.first.toString())); 195 if (entry.second != null) { 196 eapMethodNode.appendChild( 197 moSerializer.createNodeForValue(doc, TAG_INNER_METHOD, entry.second)); 198 } 199 eapMethodListNode.appendChild(eapMethodNode); 200 201 } 202 wifiNode.appendChild(eapMethodListNode); // TAG_EAP_METHOD_LIST 203 204 wifiNode.appendChild(moSerializer.createNodeForValue(doc, TAG_MANUFACTURING_CERT, "FALSE")); 205 wifiNode.appendChild(moSerializer.createNodeForValue(doc, TAG_CLIENT_TRIGGER_REDIRECT_URI, 206 redirectUri)); 207 wifiNode.appendChild(moSerializer.createNodeForValue(doc, TAG_WIFI_MAC_ADDR, macAddress)); 208 209 // TODO(b/132188983): Inject this using WifiInjector 210 TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); 211 String imsi = telephonyManager 212 .createForSubscriptionId(SubscriptionManager.getDefaultDataSubscriptionId()) 213 .getSubscriberId(); 214 if (imsi != null && sAllowToSendImsiImeiInfo) { 215 // Don't provide the IMSI to an SP that did not issue the IMSI 216 wifiNode.appendChild(moSerializer.createNodeForValue(doc, TAG_IMSI, imsi)); 217 wifiNode.appendChild( 218 moSerializer.createNodeForValue(doc, TAG_IMEI_MEID, info.getDeviceId())); 219 } 220 221 // <Node><NodeName>Ops</NodeName> 222 Element opsNode = moSerializer.createNode(doc, TAG_OPS); 223 for (String op: sSupportedOps) { 224 opsNode.appendChild(moSerializer.createNodeForValue(doc, op, "")); 225 } 226 wifiNode.appendChild(opsNode); // TAG_OPS 227 orgNode.appendChild(wifiNode); // TAG_WIFI 228 extNode.appendChild(orgNode); // TAG_ORG_WIFI 229 moNode.appendChild(extNode); // TAG_EXT 230 // <Node><NodeName>URI</NodeName> 231 Element uriNode = moSerializer.createNode(doc, TAG_URI); 232 233 uriNode.appendChild(moSerializer.createNodeForValue(doc, TAG_MAX_DEPTH, "32")); 234 uriNode.appendChild(moSerializer.createNodeForValue(doc, TAG_MAX_TOT_LEN, "2048")); 235 uriNode.appendChild(moSerializer.createNodeForValue(doc, TAG_MAX_SEG_LEN, "64")); 236 moNode.appendChild(uriNode); // TAG_URI 237 238 moNode.appendChild(moSerializer.createNodeForValue(doc, TAG_DEV_TYPE, DEVICE_TYPE)); 239 moNode.appendChild( 240 moSerializer.createNodeForValue(doc, TAG_OEM, info.getDeviceManufacturer())); 241 moNode.appendChild( 242 moSerializer.createNodeForValue(doc, TAG_FW_VER, info.getFirmwareVersion())); 243 moNode.appendChild( 244 moSerializer.createNodeForValue(doc, TAG_SW_VER, info.getSoftwareVersion())); 245 moNode.appendChild(moSerializer.createNodeForValue(doc, TAG_HW_VER, info.getHwVersion())); 246 moNode.appendChild(moSerializer.createNodeForValue(doc, TAG_LRG_ORJ, "TRUE")); 247 rootElement.appendChild(moNode); // TAG_DEVDETAIL 248 249 return moSerializer.serialize(doc); 250 } 251 } 252