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 package android.signature.cts.api; 17 18 import android.os.Bundle; 19 import android.provider.Settings; 20 import android.signature.cts.ApiDocumentParser; 21 import android.signature.cts.ClassProvider; 22 import android.signature.cts.ExcludingClassProvider; 23 import android.signature.cts.FailureType; 24 import android.signature.cts.JDiffClassDescription; 25 import android.signature.cts.VirtualPath; 26 import android.signature.cts.VirtualPath.LocalFilePath; 27 import android.signature.cts.VirtualPath.ResourcePath; 28 import android.util.Log; 29 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.PrintWriter; 33 import java.io.StringWriter; 34 import java.nio.file.Files; 35 import java.nio.file.Path; 36 import java.util.stream.Stream; 37 import java.util.zip.ZipFile; 38 import repackaged.android.test.InstrumentationTestCase; 39 import repackaged.android.test.InstrumentationTestRunner; 40 41 /** 42 */ 43 public class AbstractApiTest extends InstrumentationTestCase { 44 45 private static final String TAG = "SignatureTest"; 46 47 private TestResultObserver mResultObserver; 48 49 ClassProvider mClassProvider; 50 getGlobalExemptions()51 protected String getGlobalExemptions() { 52 return Settings.Global.getString( 53 getInstrumentation().getContext().getContentResolver(), 54 Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS); 55 } 56 getGlobalHiddenApiPolicy()57 protected String getGlobalHiddenApiPolicy() { 58 return Settings.Global.getString( 59 getInstrumentation().getContext().getContentResolver(), 60 Settings.Global.HIDDEN_API_POLICY); 61 } 62 63 @Override setUp()64 protected void setUp() throws Exception { 65 super.setUp(); 66 mResultObserver = new TestResultObserver(); 67 68 // Get the arguments passed to the instrumentation. 69 Bundle instrumentationArgs = 70 ((InstrumentationTestRunner) getInstrumentation()).getArguments(); 71 72 // Check that the device is in the correct state for running this test. 73 assertEquals( 74 String.format("Device in bad state: %s is not as expected", 75 Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS), 76 getExpectedBlocklistExemptions(), 77 getGlobalExemptions()); 78 assertEquals( 79 String.format("Device in bad state: %s is not as expected", 80 Settings.Global.HIDDEN_API_POLICY), 81 null, 82 getGlobalHiddenApiPolicy()); 83 84 85 // Prepare for a class provider that loads classes from bootclasspath but filters 86 // out known inaccessible classes. 87 // Note that com.android.internal.R.* inner classes are also excluded as they are 88 // not part of API though exist in the runtime. 89 mClassProvider = new ExcludingClassProvider( 90 new BootClassPathClassesProvider(), 91 name -> name != null && name.startsWith("com.android.internal.R.")); 92 93 initializeFromArgs(instrumentationArgs); 94 } 95 getExpectedBlocklistExemptions()96 protected String getExpectedBlocklistExemptions() { 97 return null; 98 } 99 initializeFromArgs(Bundle instrumentationArgs)100 protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception { 101 102 } 103 104 protected interface RunnableWithTestResultObserver { run(TestResultObserver observer)105 void run(TestResultObserver observer) throws Exception; 106 } 107 runWithTestResultObserver(RunnableWithTestResultObserver runnable)108 void runWithTestResultObserver(RunnableWithTestResultObserver runnable) { 109 try { 110 runnable.run(mResultObserver); 111 } catch (Exception e) { 112 StringWriter writer = new StringWriter(); 113 writer.write(e.toString()); 114 writer.write("\n"); 115 e.printStackTrace(new PrintWriter(writer)); 116 mResultObserver.notifyFailure(FailureType.CAUGHT_EXCEPTION, e.getClass().getName(), 117 writer.toString()); 118 } 119 mResultObserver.onTestComplete(); // Will throw is there are failures 120 } 121 getCommaSeparatedList(Bundle instrumentationArgs, String key)122 static String[] getCommaSeparatedList(Bundle instrumentationArgs, String key) { 123 String argument = instrumentationArgs.getString(key); 124 if (argument == null) { 125 return new String[0]; 126 } 127 return argument.split(","); 128 } 129 readResource(String resourceName)130 private Stream<VirtualPath> readResource(String resourceName) { 131 try { 132 ResourcePath resourcePath = 133 VirtualPath.get(getClass().getClassLoader(), resourceName); 134 if (resourceName.endsWith(".zip")) { 135 // Extract to a temporary file and read from there. 136 Path file = extractResourceToFile(resourceName, resourcePath.newInputStream()); 137 return flattenPaths(VirtualPath.get(file.toString())); 138 } else { 139 return Stream.of(resourcePath); 140 } 141 } catch (IOException e) { 142 throw new RuntimeException(e); 143 } 144 } 145 extractResourceToFile(String resourceName, InputStream is)146 Path extractResourceToFile(String resourceName, InputStream is) throws IOException { 147 Path tempDirectory = Files.createTempDirectory("signature"); 148 Path file = tempDirectory.resolve(resourceName); 149 Log.i(TAG, "extractResourceToFile: extracting " + resourceName + " to " + file); 150 Files.copy(is, file); 151 is.close(); 152 return file; 153 } 154 155 /** 156 * Given a path in the local file system (possibly of a zip file) flatten it into a stream of 157 * virtual paths. 158 */ flattenPaths(LocalFilePath path)159 private Stream<VirtualPath> flattenPaths(LocalFilePath path) { 160 try { 161 if (path.toString().endsWith(".zip")) { 162 return getZipEntryFiles(path); 163 } else { 164 return Stream.of(path); 165 } 166 } catch (IOException e) { 167 throw new RuntimeException(e); 168 } 169 } 170 parseApiResourcesAsStream( ApiDocumentParser apiDocumentParser, String[] apiResources)171 Stream<JDiffClassDescription> parseApiResourcesAsStream( 172 ApiDocumentParser apiDocumentParser, String[] apiResources) { 173 return Stream.of(apiResources) 174 .flatMap(this::readResource) 175 .flatMap(apiDocumentParser::parseAsStream); 176 } 177 178 /** 179 * Get the zip entries that are files. 180 * 181 * @param path the path to the zip file. 182 * @return paths to zip entries 183 */ getZipEntryFiles(LocalFilePath path)184 protected Stream<VirtualPath> getZipEntryFiles(LocalFilePath path) throws IOException { 185 @SuppressWarnings("resource") 186 ZipFile zip = new ZipFile(path.toFile()); 187 return zip.stream().map(entry -> VirtualPath.get(zip, entry)); 188 } 189 } 190