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