• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.imsserviceentitlement.utils;
18 
19 import static com.android.imsserviceentitlement.ts43.Ts43Constants.ResponseXmlAttributes.APP_ID;
20 import static com.android.imsserviceentitlement.ts43.Ts43Constants.ResponseXmlNode.APPLICATION;
21 
22 import android.text.TextUtils;
23 import android.util.ArrayMap;
24 import android.util.Log;
25 
26 import androidx.annotation.Nullable;
27 
28 import com.android.imsserviceentitlement.debug.DebugUtils;
29 
30 import org.w3c.dom.Document;
31 import org.w3c.dom.Element;
32 import org.w3c.dom.NamedNodeMap;
33 import org.w3c.dom.Node;
34 import org.w3c.dom.NodeList;
35 import org.xml.sax.InputSource;
36 import org.xml.sax.SAXException;
37 
38 import java.io.IOException;
39 import java.io.StringReader;
40 import java.util.Map;
41 import java.util.Optional;
42 
43 import javax.xml.parsers.DocumentBuilder;
44 import javax.xml.parsers.DocumentBuilderFactory;
45 import javax.xml.parsers.ParserConfigurationException;
46 
47 /** Wrap the raw content and parse it into nodes. */
48 public class XmlDoc {
49     private static final String TAG = "IMSSE-XmlDoc";
50 
51     private static final String NODE_CHARACTERISTIC = "characteristic";
52     private static final String NODE_PARM = "parm";
53     private static final String PARM_NAME = "name";
54     private static final String PARM_VALUE = "value";
55 
56     private final Map<String, Map<String, String>> mNodesMap = new ArrayMap<>();
57 
XmlDoc(String responseBody)58     public XmlDoc(String responseBody) {
59         parseXmlResponse(responseBody);
60     }
61 
62     /** Returns param value for given node and key. */
get(String node, String key, @Nullable String appId)63     public Optional<String> get(String node, String key, @Nullable String appId) {
64         Map<String, String> paramsMap = mNodesMap.get(combineKeyWithAppId(node, appId));
65         return Optional.ofNullable(paramsMap == null ? null : paramsMap.get(key));
66     }
67 
combineKeyWithAppId(String node, @Nullable String appId)68     private String combineKeyWithAppId(String node, @Nullable String appId) {
69         return APPLICATION.equals(node) && !TextUtils.isEmpty(appId) ? node + "_" + appId : node;
70     }
71 
72     /**
73      * Parses the response body as per format defined in TS.43 2.7.2 New Characteristics for
74      * XML-Based Document.
75      */
parseXmlResponse(String responseBody)76     private void parseXmlResponse(String responseBody) {
77         if (responseBody == null) {
78             return;
79         }
80 
81         // Workaround: some server doesn't escape "&" in XML response and that will cause XML parser
82         // failure later.
83         // This is a quick impl of escaping w/o intorducing a ton of new dependencies.
84         responseBody = responseBody.replace("&", "&amp;").replace("&amp;amp;", "&amp;");
85 
86         try {
87             InputSource inputSource = new InputSource(new StringReader(responseBody));
88             DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
89             DocumentBuilder docBuilder = builderFactory.newDocumentBuilder();
90             Document doc = docBuilder.parse(inputSource);
91             doc.getDocumentElement().normalize();
92 
93             if (DebugUtils.isPiiLoggable()) {
94                 Log.d(
95                         TAG,
96                         "parseXmlResponseForNode() Root element: "
97                                 + doc.getDocumentElement().getNodeName());
98             }
99 
100             NodeList nodeList = doc.getElementsByTagName(NODE_CHARACTERISTIC);
101             for (int i = 0; i < nodeList.getLength(); i++) {
102                 NamedNodeMap map = nodeList.item(i).getAttributes();
103                 if (DebugUtils.isPiiLoggable()) {
104                     Log.d(
105                             TAG,
106                             "parseAuthenticateResponse() node name="
107                                     + nodeList.item(i).getNodeName()
108                                     + " node value="
109                                     + map.item(0).getNodeValue());
110                 }
111                 Map<String, String> paramsMap = new ArrayMap<>();
112                 Element element = (Element) nodeList.item(i);
113                 paramsMap.putAll(parseParams(element.getElementsByTagName(NODE_PARM)));
114                 mNodesMap.put(
115                         combineKeyWithAppId(map.item(0).getNodeValue(), paramsMap.get(APP_ID)),
116                         paramsMap);
117             }
118         } catch (ParserConfigurationException | IOException | SAXException e) {
119             Log.e(TAG, "Failed to parse XML node. " + e);
120         }
121     }
122 
parseParams(NodeList nodeList)123     private static Map<String, String> parseParams(NodeList nodeList) {
124         Map<String, String> nameValue = new ArrayMap<>();
125         for (int i = 0; i < nodeList.getLength(); i++) {
126             Node node = nodeList.item(i);
127             NamedNodeMap map = node.getAttributes();
128             String name = "";
129             String value = "";
130             for (int j = 0; j < map.getLength(); j++) {
131                 if (PARM_NAME.equals(map.item(j).getNodeName())) {
132                     name = map.item(j).getNodeValue();
133                 } else if (PARM_VALUE.equals(map.item(j).getNodeName())) {
134                     value = map.item(j).getNodeValue();
135                 }
136             }
137             if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) {
138                 continue;
139             }
140             nameValue.put(name, value);
141 
142             if (DebugUtils.isPiiLoggable()) {
143                 Log.d(TAG, "parseParams() put name '" + name + "' with value " + value);
144             }
145         }
146         return nameValue;
147     }
148 }
149