1 /* 2 * Copyright (C) 2018 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.DexApiDocumentParser; 21 import android.signature.cts.DexField; 22 import android.signature.cts.DexMember; 23 import android.signature.cts.DexMemberChecker; 24 import android.signature.cts.DexMethod; 25 import android.signature.cts.FailureType; 26 import android.signature.cts.ResultObserver; 27 28 import java.io.File; 29 import java.lang.reflect.Constructor; 30 import java.lang.reflect.Field; 31 import java.lang.reflect.Method; 32 import java.util.Comparator; 33 import java.util.List; 34 import java.util.Set; 35 import java.util.stream.Stream; 36 import java.text.ParseException; 37 38 import static android.signature.cts.CurrentApi.API_FILE_DIRECTORY; 39 40 /** 41 * Checks that it is not possible to access hidden APIs. 42 */ 43 public class HiddenApiTest extends AbstractApiTest { 44 45 private String[] hiddenApiFiles; 46 47 @Override 48 protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception { 49 hiddenApiFiles = getCommaSeparatedList(instrumentationArgs, "hidden-api-files"); 50 } 51 52 @Override 53 protected void setUp() throws Exception { 54 super.setUp(); 55 DexMemberChecker.init(); 56 } 57 58 /** 59 * Tests that the device does not expose APIs on the provided lists of 60 * DEX signatures. 61 * 62 * Will check the entire API, and then report the complete list of failures 63 */ 64 public void testSignature() { 65 runWithTestResultObserver(resultObserver -> { 66 DexMemberChecker.Observer observer = new DexMemberChecker.Observer() { 67 @Override 68 public void classAccessible(boolean accessible, DexMember member) { 69 } 70 71 @Override 72 public void fieldAccessibleViaReflection(boolean accessible, DexField field) { 73 if (accessible) { 74 resultObserver.notifyFailure( 75 FailureType.EXTRA_FIELD, 76 field.toString(), 77 "Hidden field accessible through reflection"); 78 } 79 } 80 81 @Override 82 public void fieldAccessibleViaJni(boolean accessible, DexField field) { 83 if (accessible) { 84 resultObserver.notifyFailure( 85 FailureType.EXTRA_FIELD, 86 field.toString(), 87 "Hidden field accessible through JNI"); 88 } 89 } 90 91 @Override 92 public void methodAccessibleViaReflection(boolean accessible, DexMethod method) { 93 if (accessible) { 94 resultObserver.notifyFailure( 95 FailureType.EXTRA_METHOD, 96 method.toString(), 97 "Hidden method accessible through reflection"); 98 } 99 } 100 101 @Override 102 public void methodAccessibleViaJni(boolean accessible, DexMethod method) { 103 if (accessible) { 104 resultObserver.notifyFailure( 105 FailureType.EXTRA_METHOD, 106 method.toString(), 107 "Hidden method accessible through JNI"); 108 } 109 } 110 }; 111 parseDexApiFilesAsStream(hiddenApiFiles).forEach(dexMember -> { 112 DexMemberChecker.checkSingleMember(dexMember, observer); 113 }); 114 }); 115 } 116 117 private static Stream<DexMember> parseDexApiFilesAsStream(String[] apiFiles) { 118 DexApiDocumentParser dexApiDocumentParser = new DexApiDocumentParser(); 119 return Stream.of(apiFiles) 120 .map(name -> new File(API_FILE_DIRECTORY + "/" + name)) 121 .flatMap(file -> readFile(file)) 122 .flatMap(stream -> dexApiDocumentParser.parseAsStream(stream)); 123 } 124 125 } 126