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[] systemApiFiles;
40     protected String[] previousApiFiles;
41     protected String[] baseApiFiles;
42     private String[] unexpectedApiFiles;
43 
44     @Override
initializeFromArgs(Bundle instrumentationArgs)45     protected void initializeFromArgs(Bundle instrumentationArgs) {
46         systemApiFiles = getCommaSeparatedList(instrumentationArgs, "system-api-files");
47         baseApiFiles = getCommaSeparatedList(instrumentationArgs, "base-api-files");
48         unexpectedApiFiles = getCommaSeparatedList(instrumentationArgs, "unexpected-api-files");
49         previousApiFiles = getCommaSeparatedList(instrumentationArgs, "previous-api-files");
50     }
51 
52     /**
53      * Tests that the device's API matches the expected set defined in xml.
54      * <p/>
55      * Will check the entire API, and then report the complete list of failures
56      */
testSignature()57     public void testSignature() {
58         runWithTestResultObserver(mResultObserver -> {
59             Set<JDiffClassDescription> unexpectedClasses = loadUnexpectedClasses();
60             for (JDiffClassDescription classDescription : unexpectedClasses) {
61                 Class<?> unexpectedClass = findUnexpectedClass(classDescription, mClassProvider);
62                 if (unexpectedClass != null) {
63                     mResultObserver.notifyFailure(
64                             FailureType.UNEXPECTED_CLASS,
65                             classDescription.getAbsoluteClassName(),
66                             "Class should not be accessible to this APK");
67                 }
68             }
69 
70             ApiComplianceChecker complianceChecker =
71                     new ApiComplianceChecker(mResultObserver, mClassProvider);
72 
73             // Load classes from any API files that form the base which the expected APIs extend.
74             loadBaseClasses(complianceChecker);
75             // Load classes from system API files and check for signature compliance.
76             checkClassesSignatureCompliance(complianceChecker, systemApiFiles, unexpectedClasses,
77                     false /* isPreviousApi */);
78             // Load classes from previous API files and check for signature compliance.
79             checkClassesSignatureCompliance(complianceChecker, previousApiFiles, unexpectedClasses,
80                     true /* isPreviousApi */);
81 
82             // After done parsing all expected API files, perform any deferred checks.
83             complianceChecker.checkDeferred();
84         });
85     }
86 
not(Predicate<T> predicate)87     private static <T> Predicate<T> not(Predicate<T> predicate) {
88         return predicate.negate();
89     }
90 
findUnexpectedClass(JDiffClassDescription classDescription, ClassProvider classProvider)91     private Class<?> findUnexpectedClass(JDiffClassDescription classDescription,
92             ClassProvider classProvider) {
93         try {
94             return ReflectionHelper.findMatchingClass(classDescription, classProvider);
95         } catch (ClassNotFoundException e) {
96             return null;
97         }
98     }
99 
loadUnexpectedClasses()100     private Set<JDiffClassDescription> loadUnexpectedClasses() {
101         ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
102         return parseApiResourcesAsStream(apiDocumentParser, unexpectedApiFiles)
103                 .collect(Collectors.toCollection(SignatureTest::newSetOfClassDescriptions));
104     }
105 
newSetOfClassDescriptions()106     private static TreeSet<JDiffClassDescription> newSetOfClassDescriptions() {
107         return new TreeSet<>(Comparator.comparing(JDiffClassDescription::getAbsoluteClassName));
108     }
109 
loadBaseClasses(ApiComplianceChecker complianceChecker)110     private void loadBaseClasses(ApiComplianceChecker complianceChecker) {
111         ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
112         parseApiResourcesAsStream(apiDocumentParser, baseApiFiles)
113                 .forEach(complianceChecker::addBaseClass);
114     }
115 
checkClassesSignatureCompliance(ApiComplianceChecker complianceChecker, String[] classes, Set<JDiffClassDescription> unexpectedClasses, boolean isPreviousApi)116     private void checkClassesSignatureCompliance(ApiComplianceChecker complianceChecker,
117             String[] classes, Set<JDiffClassDescription> unexpectedClasses, boolean isPreviousApi) {
118         ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
119         parseApiResourcesAsStream(apiDocumentParser, classes)
120                 .filter(not(unexpectedClasses::contains))
121                 .map(clazz -> clazz.setPreviousApiFlag(isPreviousApi))
122                 .forEach(complianceChecker::checkSignatureCompliance);
123     }
124 
125 }
126