blob: 4f0152d17a8f56429461835c8d4357625f20a555 [file] [log] [blame]
/*
* 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_DEV_TYPE = "DevType";
private static final String TAG_OEM = "OEM";
private static final String TAG_FW_VER = "FwV";
private static final String TAG_SW_VER = "SwV";
private static final String TAG_HW_VER = "HwV";
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 String DEVICE_TYPE = "Smartphone";
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_DEV_TYPE, DEVICE_TYPE));
moNode.appendChild(
moSerializer.createNodeForValue(doc, TAG_OEM, info.getDeviceManufacturer()));
moNode.appendChild(
moSerializer.createNodeForValue(doc, TAG_FW_VER, info.getFirmwareVersion()));
moNode.appendChild(
moSerializer.createNodeForValue(doc, TAG_SW_VER, info.getSoftwareVersion()));
moNode.appendChild(moSerializer.createNodeForValue(doc, TAG_HW_VER, info.getHwVersion()));
moNode.appendChild(moSerializer.createNodeForValue(doc, TAG_LRG_ORJ, "TRUE"));
rootElement.appendChild(moNode); // TAG_DEVDETAIL
return moSerializer.serialize(doc);
}
}