1 /*
2  * Copyright (C) 2011 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 android.signature.cts.api;
18 
19 import android.os.Bundle;
20 import android.signature.cts.ApiComplianceChecker;
21 import android.signature.cts.ApiDocumentParser;
22 import android.signature.cts.ClassProvider;
23 import android.signature.cts.FailureType;
24 import android.signature.cts.JDiffClassDescription;
25 import android.signature.cts.ReflectionHelper;
26 import java.util.Comparator;
27 import java.util.Set;
28 import java.util.TreeSet;
29 import java.util.function.Predicate;
30 import java.util.stream.Collectors;
31 
32 /**
33  * Performs the signature check via a JUnit test.
34  */
35 public class SignatureTest extends AbstractApiTest {
36 
37     private static final String TAG = SignatureTest.class.getSimpleName();
38 
39     protected String[] expectedApiFiles;
40     protected String[] baseApiFiles;
41     private String[] unexpectedApiFiles;
42 
43     @Override
initializeFromArgs(Bundle instrumentationArgs)44     protected void initializeFromArgs(Bundle instrumentationArgs) {
45         expectedApiFiles = getCommaSeparatedList(instrumentationArgs, "expected-api-files");
46         baseApiFiles = getCommaSeparatedList(instrumentationArgs, "base-api-files");
47         unexpectedApiFiles = getCommaSeparatedList(instrumentationArgs, "unexpected-api-files");
48     }
49 
50     /**
51      * Tests that the device's API matches the expected set defined in xml.
52      * <p/>
53      * Will check the entire API, and then report the complete list of failures
54      */
testSignature()55     public void testSignature() {
56         runWithTestResultObserver(mResultObserver -> {
57             Set<JDiffClassDescription> unexpectedClasses = loadUnexpectedClasses();
58             for (JDiffClassDescription classDescription : unexpectedClasses) {
59                 Class<?> unexpectedClass = findUnexpectedClass(classDescription, classProvider);
60                 if (unexpectedClass != null) {
61                     mResultObserver.notifyFailure(
62                             FailureType.UNEXPECTED_CLASS,
63                             classDescription.getAbsoluteClassName(),
64                             "Class should not be accessible to this APK");
65                 }
66             }
67 
68             ApiComplianceChecker complianceChecker =
69                     new ApiComplianceChecker(mResultObserver, classProvider);
70 
71             // Load classes from any API files that form the base which the expected APIs extend.
72             loadBaseClasses(complianceChecker);
73 
74             ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
75 
76             parseApiResourcesAsStream(apiDocumentParser, expectedApiFiles)
77                     .filter(not(unexpectedClasses::contains))
78                     .forEach(complianceChecker::checkSignatureCompliance);
79 
80             // After done parsing all expected API files, perform any deferred checks.
81             complianceChecker.checkDeferred();
82         });
83     }
84 
not(Predicate<T> predicate)85     private static <T> Predicate<T> not(Predicate<T> predicate) {
86         return predicate.negate();
87     }
88 
findUnexpectedClass(JDiffClassDescription classDescription, ClassProvider classProvider)89     private Class<?> findUnexpectedClass(JDiffClassDescription classDescription,
90             ClassProvider classProvider) {
91         try {
92             return ReflectionHelper.findMatchingClass(classDescription, classProvider);
93         } catch (ClassNotFoundException e) {
94             return null;
95         }
96     }
97 
loadUnexpectedClasses()98     private Set<JDiffClassDescription> loadUnexpectedClasses() {
99         ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
100         return parseApiResourcesAsStream(apiDocumentParser, unexpectedApiFiles)
101                 .collect(Collectors.toCollection(SignatureTest::newSetOfClassDescriptions));
102     }
103 
newSetOfClassDescriptions()104     private static TreeSet<JDiffClassDescription> newSetOfClassDescriptions() {
105         return new TreeSet<>(Comparator.comparing(JDiffClassDescription::getAbsoluteClassName));
106     }
107 
loadBaseClasses(ApiComplianceChecker complianceChecker)108     private void loadBaseClasses(ApiComplianceChecker complianceChecker) {
109         ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
110         parseApiResourcesAsStream(apiDocumentParser, baseApiFiles)
111                 .forEach(complianceChecker::addBaseClass);
112     }
113 }
114