/* * Copyright (C) 2017 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.cts.apicoverage; import com.android.compatibility.common.util.ReadElf; import com.android.cts.apicommon.ApiCoverage; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; /** * Class that outputs an XML report of the {@link ApiCoverage} collected. It can be viewed in a * browser when used with the api-coverage.css and api-coverage.xsl files. */ class NdkApiXmlReport { private static final String API_TAG = "api"; private static final String PACKAGE_TAG = "package"; private static final String CLASS_TAG = "class"; private static final String METHOD_TAG = "method"; private static final String FIELD_TAG = "field"; private static final String ATTRIBUTE_NAME = "name"; private static final String NDK_PACKAGE_NAME = "ndk"; private static final String NDK_DUMMY_RETURN_TYPE = "na"; private static final Map sInternalSymMap; static { sInternalSymMap = new HashMap(); sInternalSymMap.put("__bss_start", "bss"); sInternalSymMap.put("_end", "initialized data"); sInternalSymMap.put("_edata", "uninitialized data"); } private static final FilenameFilter SUPPORTED_FILE_NAME_FILTER = new FilenameFilter() { public boolean accept(File dir, String name) { String fileName = name.toLowerCase(); return fileName.endsWith(".so"); } }; private static void printUsage() { System.out.println("Usage: ndk-api-xml-report [OPTION]... [APK]..."); System.out.println(); System.out.println("Generates a report about what Android NDK methods."); System.out.println(); System.out.println("this must be used from the $ANDROID_BUILD_TOP"); System.out.println("make cts-test-coverage"); System.out.println("unzip the target ndk_platform.tar.bz2 to a folder."); System.out.println( "$ANDROID_HOST_OUT/bin/ndk-api-report " + "-o $ANDROID_BUILD_TOP/cts/tools/cts-api-coverage/etc/ndk-api.xml " + "-n /platforms/android-current/arch-arm64/usr/lib"); System.out.println(); System.out.println("Options:"); System.out.println(" -o FILE output file or standard out if not given"); System.out.println(" -n PATH path to the NDK Lib Folder"); System.out.println(); System.exit(1); } /** Get the argument or print out the usage and exit. */ private static String getExpectedArg(String[] args, int index) { if (index < args.length) { return args[index]; } else { printUsage(); return null; // Never will happen because printUsage will call exit(1) } } public static void main(String[] args) throws IOException { List ndkSos = new ArrayList(); int numNdkSos = 0; String ndkLibPath = ""; String outputFilePath = "./ndk-api.xml"; for (int i = 0; i < args.length; i++) { if (args[i].startsWith("-")) { if ("-o".equals(args[i])) { outputFilePath = getExpectedArg(args, ++i); } else if ("-n".equals(args[i])) { ndkLibPath = getExpectedArg(args, ++i); File file = new File(ndkLibPath); if (file.isDirectory()) { ndkSos.addAll(Arrays.asList(file.listFiles(SUPPORTED_FILE_NAME_FILTER))); } else { printUsage(); } } else { printUsage(); } } else { printUsage(); } } Document dom; // instance of a DocumentBuilderFactory DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { // use factory to get an instance of document builder DocumentBuilder db = dbf.newDocumentBuilder(); // create instance of DOM dom = db.newDocument(); // create the root element Element apiEle = dom.createElement(API_TAG); Element pkgEle = dom.createElement(PACKAGE_TAG); setAttribute(dom, pkgEle, ATTRIBUTE_NAME, NDK_PACKAGE_NAME); apiEle.appendChild(pkgEle); dom.appendChild(apiEle); for (File ndkSo : ndkSos) { ReadElf re = ReadElf.read(ndkSo); re.getDynamicSymbol(""); ReadElf.Symbol[] symArr = re.getDynSymArr(); System.out.println(ndkSo.getName()); Element classEle = addToDom(dom, pkgEle, symArr, ndkSo.getName().toLowerCase()); pkgEle.appendChild(classEle); } try { Transformer tr = TransformerFactory.newInstance().newTransformer(); // enable indent in result file tr.setOutputProperty(OutputKeys.INDENT, "yes"); tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); // send DOM to file tr.transform( new DOMSource(dom), new StreamResult(new FileOutputStream(outputFilePath))); } catch (TransformerException te) { System.out.println(te.getMessage()); } catch (IOException ioe) { System.out.println(ioe.getMessage()); } } catch (ParserConfigurationException pce) { System.out.println("UsersXML: Error trying to instantiate DocumentBuilder " + pce); } } public static Element addToDom( Document dom, Element pkgEle, ReadElf.Symbol[] symArr, String libName) { Element classEle = createClassEle(dom, libName); for (int i = 0; i < symArr.length; i++) { if (symArr[i].isExtern()) { Element methodEle; if(isInternalSymbol(symArr[i])) { continue; } if (symArr[i].type == ReadElf.Symbol.STT_OBJECT) { methodEle = createFieldEle(dom, symArr[i].name); } else { methodEle = createMethodEle(dom, symArr[i].name); } System.out.println(symArr[i].name); classEle.appendChild(methodEle); } } return classEle; } public static void addToDom(Document dom, Element pkgEle, ReadElf.Symbol[] symArr) { HashMap classEleMap = new HashMap(); for (int i = 0; i < symArr.length; i++) { if (symArr[i].isExtern()) { Element methodEle; if (symArr[i].type == ReadElf.Symbol.STT_OBJECT) { methodEle = createFieldEle(dom, symArr[i].name); } else { methodEle = createMethodEle(dom, symArr[i].name); } System.out.println(symArr[i].name); // add to the class element String libName = symArr[i].getVerDefLibName(); Element classEle = classEleMap.get(libName); if (classEle == null) { classEle = createClassEle(dom, libName); classEleMap.put(libName, classEle); } classEle.appendChild(methodEle); } } Iterator ite = classEleMap.entrySet().iterator(); while (ite.hasNext()) { Map.Entry entry = (Map.Entry) ite.next(); pkgEle.appendChild(entry.getValue()); } } public static void saveToXML(String xml, ReadElf.Symbol[] symArr) { Document dom; Element ele = null; // instance of a DocumentBuilderFactory DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { // use factory to get an instance of document builder DocumentBuilder db = dbf.newDocumentBuilder(); // create instance of DOM dom = db.newDocument(); // create the root element Element apiEle = dom.createElement(API_TAG); Element packageEle = dom.createElement(PACKAGE_TAG); setAttribute(dom, packageEle, ATTRIBUTE_NAME, NDK_PACKAGE_NAME); Element classEle = createClassEle(dom, "class"); packageEle.appendChild(classEle); apiEle.appendChild(packageEle); dom.appendChild(apiEle); for (int i = 0; i < symArr.length; i++) { if (symArr[i].isExtern()) { Element methodEle; if (symArr[i].type == ReadElf.Symbol.STT_OBJECT) { methodEle = createFieldEle(dom, symArr[i].name); } else { methodEle = createMethodEle(dom, symArr[i].name); } classEle.appendChild(methodEle); } } try { Transformer tr = TransformerFactory.newInstance().newTransformer(); // enable indent in result file tr.setOutputProperty(OutputKeys.INDENT, "yes"); tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); // send DOM to file tr.transform(new DOMSource(dom), new StreamResult(new FileOutputStream(xml))); } catch (TransformerException te) { System.out.println(te.getMessage()); } catch (IOException ioe) { System.out.println(ioe.getMessage()); } } catch (ParserConfigurationException pce) { System.out.println("UsersXML: Error trying to instantiate DocumentBuilder " + pce); } } protected static boolean isInternalSymbol(ReadElf.Symbol sym) { String value = sInternalSymMap.get(sym.name); if (value == null) { return false; } else { return true; } } protected static void setAttribute(Document doc, Node elem, String name, String value) { Attr attr = doc.createAttribute(name); attr.setNodeValue(value); elem.getAttributes().setNamedItem(attr); } protected static Element createClassEle(Document doc, String name) { Element ele = doc.createElement(CLASS_TAG); setAttribute(doc, ele, ATTRIBUTE_NAME, name); setAttribute(doc, ele, "abstract", "false"); setAttribute(doc, ele, "static", "false"); setAttribute(doc, ele, "final", "true"); setAttribute(doc, ele, "deprecated", "not deprecated"); setAttribute(doc, ele, "visibility", "public"); return ele; } protected static Element createMethodEle(Document doc, String name) { Element ele = doc.createElement(METHOD_TAG); setAttribute(doc, ele, ATTRIBUTE_NAME, name); setAttribute(doc, ele, "return", NDK_DUMMY_RETURN_TYPE); setAttribute(doc, ele, "abstract", "false"); setAttribute(doc, ele, "native", "true"); setAttribute(doc, ele, "synchronized", "true"); setAttribute(doc, ele, "static", "false"); setAttribute(doc, ele, "final", "true"); setAttribute(doc, ele, "deprecated", "not deprecated"); setAttribute(doc, ele, "visibility", "public"); return ele; } protected static Element createFieldEle(Document doc, String name) { Element ele = doc.createElement(FIELD_TAG); setAttribute(doc, ele, ATTRIBUTE_NAME, name); setAttribute(doc, ele, "type", "native"); setAttribute(doc, ele, "transient", "false"); setAttribute(doc, ele, "volatile", "false"); setAttribute(doc, ele, "value", ""); setAttribute(doc, ele, "static", "false"); setAttribute(doc, ele, "final", "true"); setAttribute(doc, ele, "deprecated", "not deprecated"); setAttribute(doc, ele, "visibility", "public"); return ele; } }