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 java.util.function.Predicate; 20 21 import android.os.Bundle; 22 import android.provider.Settings; 23 import android.signature.cts.DexField; 24 import android.signature.cts.DexMember; 25 import android.signature.cts.DexMemberChecker; 26 import android.signature.cts.DexMethod; 27 import android.signature.cts.FailureType; 28 import repackaged.android.test.InstrumentationTestRunner; 29 30 public abstract class BaseKillswitchTest extends AbstractApiTest { 31 32 protected String mErrorMessageAppendix; 33 34 @Override setUp()35 protected void setUp() throws Exception { 36 super.setUp(); 37 DexMemberChecker.init(); 38 } 39 getGlobalExemptions()40 protected String getGlobalExemptions() { 41 return Settings.Global.getString( 42 getInstrumentation().getContext().getContentResolver(), 43 Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS); 44 } 45 46 // We have four methods to split up the load, keeping individual test runs small. 47 // Tests shared by all the subclasses. 48 49 private final static Predicate<DexMember> METHOD_FILTER = 50 dexMember -> (dexMember instanceof DexMethod); 51 52 private final static Predicate<DexMember> FIELD_FILTER = 53 dexMember -> (dexMember instanceof DexField); 54 testKillswitchMechanismMethodsThroughReflection()55 public void testKillswitchMechanismMethodsThroughReflection() { 56 doTestKillswitchMechanism(METHOD_FILTER, /* reflection= */ true, /* jni= */ false); 57 } 58 testKillswitchMechanismMethodsThroughJni()59 public void testKillswitchMechanismMethodsThroughJni() { 60 doTestKillswitchMechanism(METHOD_FILTER, /* reflection= */ false, /* jni= */ true); 61 } 62 testKillswitchMechanismFieldsThroughReflection()63 public void testKillswitchMechanismFieldsThroughReflection() { 64 doTestKillswitchMechanism(FIELD_FILTER, /* reflection= */ true, /* jni= */ false); 65 } 66 testKillswitchMechanismFieldsThroughJni()67 public void testKillswitchMechanismFieldsThroughJni() { 68 doTestKillswitchMechanism(FIELD_FILTER, /* reflection= */ false, /* jni= */ true); 69 } 70 doTestKillswitchMechanism(Predicate<DexMember> memberFilter, boolean reflection, boolean jni)71 private void doTestKillswitchMechanism(Predicate<DexMember> memberFilter, boolean reflection, 72 boolean jni) { 73 runWithTestResultObserver(resultObserver -> { 74 DexMemberChecker.Observer observer = new DexMemberChecker.Observer() { 75 @Override 76 public void classAccessible(boolean accessible, DexMember member) { 77 if (!accessible) { 78 resultObserver.notifyFailure( 79 FailureType.MISSING_CLASS, 80 member.toString(), 81 "Class from boot classpath is not accessible" 82 + mErrorMessageAppendix); 83 } 84 } 85 86 @Override 87 public void fieldAccessibleViaReflection(boolean accessible, DexField field) { 88 if (!accessible) { 89 resultObserver.notifyFailure( 90 FailureType.MISSING_FIELD, 91 field.toString(), 92 "Field from boot classpath is not accessible via reflection" 93 + mErrorMessageAppendix); 94 } 95 } 96 97 @Override 98 public void fieldAccessibleViaJni(boolean accessible, DexField field) { 99 if (!accessible) { 100 resultObserver.notifyFailure( 101 FailureType.MISSING_FIELD, 102 field.toString(), 103 "Field from boot classpath is not accessible via JNI" 104 + mErrorMessageAppendix); 105 } 106 } 107 108 @Override 109 public void methodAccessibleViaReflection(boolean accessible, DexMethod method) { 110 if (method.isStaticConstructor()) { 111 // Skip static constructors. They cannot be discovered with reflection. 112 return; 113 } 114 115 if (!accessible) { 116 resultObserver.notifyFailure( 117 FailureType.MISSING_METHOD, 118 method.toString(), 119 "Method from boot classpath is not accessible via reflection" 120 + mErrorMessageAppendix); 121 } 122 } 123 124 @Override 125 public void methodAccessibleViaJni(boolean accessible, DexMethod method) { 126 if (!accessible) { 127 resultObserver.notifyFailure( 128 FailureType.MISSING_METHOD, 129 method.toString(), 130 "Method from boot classpath is not accessible via JNI" 131 + mErrorMessageAppendix); 132 } 133 } 134 135 }; 136 classProvider.getAllClasses().forEach(klass -> { 137 classProvider.getAllMembers(klass) 138 .filter(memberFilter) 139 .forEach(member -> { 140 DexMemberChecker.checkSingleMember(member, reflection, jni, observer); 141 }); 142 }); 143 }); 144 } 145 } 146