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