Various updates to CTS coverage tool

-Report coverage of superclass methods.
e.g. MidiSender.connect is now covered
-Show coverage of abstract methods.
e.g. android.hardware.camera2.CameraDevice is now covered
-Display method visibility, if static and if final
-Fix a bug in the xsl that prevented toggling visibility on
classes with a class name duplicated elswhere in the codebase

bug: 19272826

Change-Id: I2c4b97a3367f51b7a9fe637a570b6d881a428c0e
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiClass.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiClass.java
index 31e1f8d..f5abd5e5 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiClass.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiClass.java
@@ -34,10 +34,25 @@
 
     private final List<ApiMethod> mApiMethods = new ArrayList<ApiMethod>();
 
-    ApiClass(String name, boolean deprecated, boolean classAbstract) {
+    private final String mSuperClassName;
+
+    private ApiClass mSuperClass;
+
+    /**
+     * @param name The name of the class
+     * @param deprecated true iff the class is marked as deprecated
+     * @param classAbstract true iff the class is abstract
+     * @param superClassName The fully qualified name of the super class
+     */
+    ApiClass(
+            String name,
+            boolean deprecated,
+            boolean classAbstract,
+            String superClassName) {
         mName = name;
         mDeprecated = deprecated;
         mAbstract = classAbstract;
+        mSuperClassName = superClassName;
     }
 
     @Override
@@ -54,22 +69,20 @@
         return mDeprecated;
     }
 
+    public String getSuperClassName() {
+        return mSuperClassName;
+    }
+
     public boolean isAbstract() {
         return mAbstract;
     }
 
+    public void setSuperClass(ApiClass superClass) { mSuperClass = superClass; }
+
     public void addConstructor(ApiConstructor constructor) {
         mApiConstructors.add(constructor);
     }
 
-    public ApiConstructor getConstructor(List<String> parameterTypes) {
-        for (ApiConstructor constructor : mApiConstructors) {
-            if (parameterTypes.equals(constructor.getParameterTypes())) {
-                return constructor;
-            }
-        }
-        return null;
-    }
 
     public Collection<ApiConstructor> getConstructors() {
         return Collections.unmodifiableList(mApiConstructors);
@@ -79,15 +92,29 @@
         mApiMethods.add(method);
     }
 
-    public ApiMethod getMethod(String name, List<String> parameterTypes, String returnType) {
-        for (ApiMethod method : mApiMethods) {
-            if (name.equals(method.getName())
-                    && parameterTypes.equals(method.getParameterTypes())
-                    && returnType.equals(method.getReturnType())) {
-                return method;
-            }
+    /** Look for a matching constructor and mark it as covered */
+    public void markConstructorCovered(List<String> parameterTypes) {
+        if (mSuperClass != null) {
+            // Mark matching constructors in the superclass
+            mSuperClass.markConstructorCovered(parameterTypes);
         }
-        return null;
+        ApiConstructor apiConstructor = getConstructor(parameterTypes);
+        if (apiConstructor != null) {
+            apiConstructor.setCovered(true);
+        }
+
+    }
+
+    /** Look for a matching method and if found and mark it as covered */
+    public void markMethodCovered(String name, List<String> parameterTypes, String returnType) {
+        if (mSuperClass != null) {
+            // Mark matching methods in the super class
+            mSuperClass.markMethodCovered(name, parameterTypes, returnType);
+        }
+        ApiMethod apiMethod = getMethod(name, parameterTypes, returnType);
+        if (apiMethod != null) {
+            apiMethod.setCovered(true);
+        }
     }
 
     public Collection<ApiMethod> getMethods() {
@@ -126,4 +153,24 @@
     public int getMemberSize() {
         return getTotalMethods();
     }
+
+    private ApiMethod getMethod(String name, List<String> parameterTypes, String returnType) {
+        for (ApiMethod method : mApiMethods) {
+            if (name.equals(method.getName())
+                    && parameterTypes.equals(method.getParameterTypes())
+                    && returnType.equals(method.getReturnType())) {
+                return method;
+            }
+        }
+        return null;
+    }
+
+    private ApiConstructor getConstructor(List<String> parameterTypes) {
+        for (ApiConstructor constructor : mApiConstructors) {
+            if (parameterTypes.equals(constructor.getParameterTypes())) {
+                return constructor;
+            }
+        }
+        return null;
+    }
 }
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiCoverage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiCoverage.java
index adf2ea9..953aab3 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiCoverage.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiCoverage.java
@@ -39,10 +39,11 @@
         return Collections.unmodifiableCollection(mPackages.values());
     }
 
-    public void removeEmptyAbstractClasses() {
+    /** Iterate through all packages and update all classes to include its superclass */
+    public void resolveSuperClasses() {
         for (Map.Entry<String, ApiPackage> entry : mPackages.entrySet()) {
             ApiPackage pkg = entry.getValue();
-            pkg.removeEmptyAbstractClasses();
+            pkg.resolveSuperClasses(mPackages);
         }
     }
 }
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiMethod.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiMethod.java
index 053cd12..582c2b6 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiMethod.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiMethod.java
@@ -29,15 +29,35 @@
 
     private final String mReturnType;
 
-    private boolean mDeprecated;
+    private final boolean mDeprecated;
+
+    private final String mVisibility;
+
+    private final boolean mStaticMethod;
+
+    private final boolean mFinalMethod;
+
+    private final boolean mAbstractMethod;
 
     private boolean mIsCovered;
 
-    ApiMethod(String name, List<String> parameterTypes, String returnType, boolean deprecated) {
+    ApiMethod(
+            String name,
+            List<String> parameterTypes,
+            String returnType,
+            boolean deprecated,
+            String visibility,
+            boolean staticMethod,
+            boolean finalMethod,
+            boolean abstractMethod) {
         mName = name;
         mParameterTypes = new ArrayList<String>(parameterTypes);
         mReturnType = returnType;
         mDeprecated = deprecated;
+        mVisibility = visibility;
+        mStaticMethod = staticMethod;
+        mFinalMethod = finalMethod;
+        mAbstractMethod = abstractMethod;
     }
 
     @Override
@@ -65,6 +85,14 @@
         return mIsCovered;
     }
 
+    public String getVisibility() { return mVisibility; }
+
+    public boolean isAbstractMethod() { return mAbstractMethod; }
+
+    public boolean isStaticMethod() { return mStaticMethod; }
+
+    public boolean isFinalMethod() { return mFinalMethod; }
+
     public void setCovered(boolean covered) {
         mIsCovered = covered;
     }
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiPackage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiPackage.java
index e0bf73f..7be7e3c 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiPackage.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/ApiPackage.java
@@ -77,14 +77,26 @@
         return getTotalMethods();
     }
 
-    public void removeEmptyAbstractClasses() {
+    /** Iterate through all classes and add superclass. */
+    public void resolveSuperClasses(Map<String, ApiPackage> packageMap) {
         Iterator<Entry<String, ApiClass>> it = mApiClassMap.entrySet().iterator();
         while (it.hasNext()) {
             Map.Entry<String, ApiClass> entry = it.next();
-            ApiClass cls = entry.getValue();
-            if (cls.isAbstract() && (cls.getTotalMethods() == 0)) {
-                // this is essentially interface
-                it.remove();
+            ApiClass apiClass = entry.getValue();
+            if (apiClass.getSuperClassName() != null) {
+                String superClassName = apiClass.getSuperClassName();
+                // Split the fully qualified class name into package and class name.
+                String packageName = superClassName.substring(0, superClassName.lastIndexOf('.'));
+                String className = superClassName.substring(
+                        superClassName.lastIndexOf('.') + 1, superClassName.length());
+                if (packageMap.containsKey(packageName)) {
+                    ApiPackage apiPackage = packageMap.get(packageName);
+                    ApiClass superClass = apiPackage.getClass(className);
+                    if (superClass != null) {
+                        // Add the super class
+                        apiClass.setSuperClass(superClass);
+                    }
+                }
             }
         }
     }
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
index 05cb4e19..3f2f353 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CtsApiCoverage.java
@@ -117,7 +117,8 @@
          */
 
         ApiCoverage apiCoverage = getEmptyApiCoverage(apiXmlPath);
-        apiCoverage.removeEmptyAbstractClasses();
+        // Add superclass information into api coverage.
+        apiCoverage.resolveSuperClasses();
         for (File testApk : testApks) {
             addApiCoverage(apiCoverage, testApk, dexDeps);
         }
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CurrentXmlHandler.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CurrentXmlHandler.java
index b9f9e9c..de9f5d5 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/CurrentXmlHandler.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/CurrentXmlHandler.java
@@ -40,6 +40,12 @@
 
     private boolean mCurrentMethodIsAbstract;
 
+    private String mCurrentMethodVisibility;
+
+    private boolean mCurrentMethodStaticMethod;
+
+    private boolean mCurrentMethodFinalMethod;
+
     private boolean mDeprecated;
 
 
@@ -69,7 +75,9 @@
             mIgnoreCurrentClass = false;
             mCurrentClassName = getValue(attributes, "name");
             mDeprecated = isDeprecated(attributes);
-            ApiClass apiClass = new ApiClass(mCurrentClassName, mDeprecated, isAbstract(attributes));
+            String superClass = attributes.getValue("extends");
+            ApiClass apiClass = new ApiClass(
+                    mCurrentClassName, mDeprecated, is(attributes, "abstract"), superClass);
             ApiPackage apiPackage = mApiCoverage.getPackage(mCurrentPackageName);
             apiPackage.addClass(apiClass);
         } else if ("interface".equalsIgnoreCase(localName)) {
@@ -82,7 +90,10 @@
             mDeprecated = isDeprecated(attributes);
             mCurrentMethodName = getValue(attributes, "name");
             mCurrentMethodReturnType = getValue(attributes, "return");
-            mCurrentMethodIsAbstract = isAbstract(attributes);
+            mCurrentMethodIsAbstract = is(attributes, "abstract");
+            mCurrentMethodVisibility = getValue(attributes, "visibility");
+            mCurrentMethodStaticMethod = is(attributes, "static");
+            mCurrentMethodFinalMethod = is(attributes, "final");
             mCurrentParameterTypes.clear();
         } else if ("parameter".equalsIgnoreCase(localName)) {
             mCurrentParameterTypes.add(getValue(attributes, "type"));
@@ -107,11 +118,15 @@
             ApiClass apiClass = apiPackage.getClass(mCurrentClassName);
             apiClass.addConstructor(apiConstructor);
         }  else if ("method".equalsIgnoreCase(localName)) {
-            if (mCurrentMethodIsAbstract) { // do not add abstract method
-                return;
-            }
-            ApiMethod apiMethod = new ApiMethod(mCurrentMethodName, mCurrentParameterTypes,
-                    mCurrentMethodReturnType, mDeprecated);
+            ApiMethod apiMethod = new ApiMethod(
+                    mCurrentMethodName,
+                    mCurrentParameterTypes,
+                    mCurrentMethodReturnType,
+                    mDeprecated,
+                    mCurrentMethodVisibility,
+                    mCurrentMethodStaticMethod,
+                    mCurrentMethodFinalMethod,
+                    mCurrentMethodIsAbstract);
             ApiPackage apiPackage = mApiCoverage.getPackage(mCurrentPackageName);
             ApiClass apiClass = apiPackage.getClass(mCurrentClassName);
             apiClass.addMethod(apiMethod);
@@ -129,8 +144,8 @@
         return "deprecated".equals(attributes.getValue("deprecated"));
     }
 
-    private boolean isAbstract(Attributes attributes) {
-        return "true".equals(attributes.getValue("abstract"));
+    private static boolean is(Attributes attributes, String valueName) {
+        return "true".equals(attributes.getValue(valueName));
     }
 
     private boolean isEnum(Attributes attributes) {
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/DexDepsXmlHandler.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/DexDepsXmlHandler.java
index 0a90bdd..3df532e 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/DexDepsXmlHandler.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/DexDepsXmlHandler.java
@@ -73,10 +73,7 @@
             if (apiPackage != null) {
                 ApiClass apiClass = apiPackage.getClass(mCurrentClassName);
                 if (apiClass != null) {
-                    ApiConstructor apiConstructor = apiClass.getConstructor(mCurrentParameterTypes);
-                    if (apiConstructor != null) {
-                        apiConstructor.setCovered(true);
-                    }
+                    apiClass.markConstructorCovered(mCurrentParameterTypes);
                 }
             }
         }  else if ("method".equalsIgnoreCase(localName)) {
@@ -84,11 +81,8 @@
             if (apiPackage != null) {
                 ApiClass apiClass = apiPackage.getClass(mCurrentClassName);
                 if (apiClass != null) {
-                    ApiMethod apiMethod = apiClass.getMethod(mCurrentMethodName,
-                            mCurrentParameterTypes, mCurrentMethodReturnType);
-                    if (apiMethod != null) {
-                        apiMethod.setCovered(true);
-                    }
+                    apiClass.markMethodCovered(
+                            mCurrentMethodName, mCurrentParameterTypes, mCurrentMethodReturnType);
                 }
             }
         }
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java
index e3e2e7c..3adc020 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TextReport.java
@@ -103,7 +103,18 @@
     private static void printMethod(ApiMethod method, PrintStream out) {
         StringBuilder builder = new StringBuilder("    [")
                 .append(method.isCovered() ? "X" : " ")
-                .append("] ").append(method.getReturnType()).append(" ")
+                .append("] ")
+                .append(method.getVisibility()).append(" ");
+        if (method.isAbstractMethod()) {
+            builder.append("abstract ");
+        }
+        if (method.isStaticMethod()) {
+            builder.append("static ");
+        }
+        if (method.isFinalMethod()) {
+            builder.append("final ");
+        }
+        builder.append(method.getReturnType()).append(" ")
                 .append(method.getName()).append("(");
         List<String> parameterTypes = method.getParameterTypes();
         int numParameterTypes = parameterTypes.size();
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java
index 570b316..4310d20 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/XmlReport.java
@@ -66,7 +66,7 @@
                         + "\" numCovered=\"" + pkgTotalCovered
                         + "\" numTotal=\"" + pkgTotal
                         + "\" coveragePercentage=\""
-                            + Math.round(pkg.getCoveragePercentage())
+                        + Math.round(pkg.getCoveragePercentage())
                         + "\">");
 
                 List<ApiClass> classes = new ArrayList<ApiClass>(pkg.getClasses());
@@ -103,6 +103,10 @@
                             out.println("<method name=\"" + method.getName()
                                     + "\" returnType=\"" + method.getReturnType()
                                     + "\" deprecated=\"" + method.isDeprecated()
+                                    + "\" static=\"" + method.isStaticMethod()
+                                    + "\" final=\"" + method.isFinalMethod()
+                                    + "\" visibility=\"" + method.getVisibility()
+                                    + "\" abstract=\"" + method.isAbstractMethod()
                                     + "\" covered=\"" + method.isCovered() + "\">");
                             if (method.isDeprecated()) {
                                 if (method.isCovered()) {
diff --git a/tools/cts-api-coverage/src/res/api-coverage.xsl b/tools/cts-api-coverage/src/res/api-coverage.xsl
index b11a8c4..1a56eb0 100644
--- a/tools/cts-api-coverage/src/res/api-coverage.xsl
+++ b/tools/cts-api-coverage/src/res/api-coverage.xsl
@@ -101,14 +101,17 @@
                     <xsl:for-each select="api-coverage/api/package">
                         <xsl:call-template name="packageOrClassListItem">
                             <xsl:with-param name="bulletClass" select="'package'" />
+                            <xsl:with-param name="toggleId" select="@name" />
                         </xsl:call-template>
                         <div class="packageDetails" id="{@name}" style="display: none">
                             <ul>
                                 <xsl:for-each select="class">
+                                    <xsl:variable name="packageClassId" select="concat(../@name, '.', @name)"/>
                                     <xsl:call-template name="packageOrClassListItem">
                                         <xsl:with-param name="bulletClass" select="'class'" />
+                                        <xsl:with-param name="toggleId" select="$packageClassId" />
                                     </xsl:call-template>
-                                    <div class="classDetails" id="{@name}" style="display: none">
+                                    <div class="classDetails" id="{$packageClassId}" style="display: none">
                                         <xsl:for-each select="constructor">
                                             <xsl:call-template name="methodListItem" />
                                         </xsl:for-each>
@@ -124,9 +127,10 @@
             </body>
         </html>
     </xsl:template>
-    
+
     <xsl:template name="packageOrClassListItem">
         <xsl:param name="bulletClass" />
+        <xsl:param name="toggleId"/>
 
         <xsl:variable name="colorClass">
             <xsl:choose>
@@ -135,7 +139,7 @@
                 <xsl:otherwise>green</xsl:otherwise>
             </xsl:choose>
         </xsl:variable>
-        
+
         <xsl:variable name="deprecatedClass">
             <xsl:choose>
                 <xsl:when test="@deprecated = 'true'">deprecated</xsl:when>
@@ -143,15 +147,15 @@
             </xsl:choose>
         </xsl:variable>
 
-        <li class="{$bulletClass}" onclick="toggleVisibility('{@name}')">
+        <li class="{$bulletClass}" onclick="toggleVisibility('{$toggleId}')">
             <span class="{$colorClass} {$deprecatedClass}">
                 <b><xsl:value-of select="@name" /></b>
                 &nbsp;<xsl:value-of select="@coveragePercentage" />%
                 &nbsp;(<xsl:value-of select="@numCovered" />/<xsl:value-of select="@numTotal" />)
             </span>
-        </li>   
+        </li>
     </xsl:template>
-  
+
   <xsl:template name="methodListItem">
 
     <xsl:variable name="deprecatedClass">
@@ -166,6 +170,10 @@
         <xsl:when test="@covered = 'true'">[X]</xsl:when>
         <xsl:otherwise>[ ]</xsl:otherwise>
       </xsl:choose>
+      <xsl:if test="@visibility != ''">&nbsp;<xsl:value-of select="@visibility" /></xsl:if>
+      <xsl:if test="@abstract = 'true'">&nbsp;abstract</xsl:if>
+      <xsl:if test="@static = 'true'">&nbsp;static</xsl:if>
+      <xsl:if test="@final = 'true'">&nbsp;final</xsl:if>
       <xsl:if test="@returnType != ''">&nbsp;<xsl:value-of select="@returnType" /></xsl:if>
       <b>&nbsp;<xsl:value-of select="@name" /></b><xsl:call-template name="formatParameters" />
     </span>