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