1 /* 2 * Copyright (C) 2024 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 com.android.ravenwood.common; 17 18 import com.android.ravenwood.common.divergence.RavenwoodDivergence; 19 20 import java.io.File; 21 import java.io.FileDescriptor; 22 import java.io.FileInputStream; 23 import java.io.PrintStream; 24 import java.util.Arrays; 25 26 public class RavenwoodCommonUtils { 27 private static final String TAG = "RavenwoodCommonUtils"; 28 RavenwoodCommonUtils()29 private RavenwoodCommonUtils() { 30 } 31 32 private static final Object sLock = new Object(); 33 34 /** Name of `libravenwood_runtime` */ 35 private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime"; 36 37 /** Directory name of `out/host/linux-x86/testcases/ravenwood-runtime` */ 38 private static final String RAVENWOOD_RUNTIME_DIR_NAME = "ravenwood-runtime"; 39 40 private static boolean sEnableExtraRuntimeCheck = 41 "1".equals(System.getenv("RAVENWOOD_ENABLE_EXTRA_RUNTIME_CHECK")); 42 43 private static final boolean IS_ON_RAVENWOOD = RavenwoodDivergence.isOnRavenwood(); 44 45 private static final String RAVEWOOD_RUNTIME_PATH = getRavenwoodRuntimePathInternal(); 46 47 public static final String RAVENWOOD_SYSPROP = "ro.is_on_ravenwood"; 48 49 // @GuardedBy("sLock") 50 private static boolean sIntegrityChecked = false; 51 52 /** 53 * @return if we're running on Ravenwood. 54 */ isOnRavenwood()55 public static boolean isOnRavenwood() { 56 return IS_ON_RAVENWOOD; 57 } 58 59 /** 60 * Throws if the runtime is not Ravenwood. 61 */ ensureOnRavenwood()62 public static void ensureOnRavenwood() { 63 if (!isOnRavenwood()) { 64 throw new RavenwoodRuntimeException("This is only supposed to be used on Ravenwood"); 65 } 66 } 67 68 /** 69 * @return if the various extra runtime check should be enabled. 70 */ shouldEnableExtraRuntimeCheck()71 public static boolean shouldEnableExtraRuntimeCheck() { 72 return sEnableExtraRuntimeCheck; 73 } 74 75 /** 76 * Load the main runtime JNI library. 77 */ loadRavenwoodNativeRuntime()78 public static void loadRavenwoodNativeRuntime() { 79 ensureOnRavenwood(); 80 loadJniLibrary(RAVENWOOD_NATIVE_RUNTIME_NAME); 81 } 82 83 /** 84 * Internal implementation of 85 * {@link android.platform.test.ravenwood.RavenwoodUtils#loadJniLibrary(String)} 86 */ loadJniLibrary(String libname)87 public static void loadJniLibrary(String libname) { 88 if (RavenwoodCommonUtils.isOnRavenwood()) { 89 loadJniLibraryInternal(libname); 90 } else { 91 System.loadLibrary(libname); 92 } 93 } 94 95 /** 96 * Function equivalent to ART's System.loadLibrary. See RavenwoodUtils for why we need it. 97 */ loadJniLibraryInternal(String libname)98 private static void loadJniLibraryInternal(String libname) { 99 var path = System.getProperty("java.library.path"); 100 var filename = "lib" + libname + ".so"; 101 102 System.out.println("Looking for library " + libname + ".so in java.library.path:" + path); 103 104 try { 105 if (path == null) { 106 throw new UnsatisfiedLinkError("Cannot load library " + libname + "." 107 + " Property java.library.path not set!"); 108 } 109 for (var dir : path.split(":")) { 110 var file = new File(dir + "/" + filename); 111 if (file.exists()) { 112 System.load(file.getAbsolutePath()); 113 return; 114 } 115 } 116 throw new UnsatisfiedLinkError("Library " + libname + " not found in " 117 + "java.library.path: " + path); 118 } catch (Throwable e) { 119 dumpFiles(System.out); 120 throw e; 121 } 122 } 123 dumpFiles(PrintStream out)124 private static void dumpFiles(PrintStream out) { 125 try { 126 var path = System.getProperty("java.library.path"); 127 out.println("# java.library.path=" + path); 128 129 for (var dir : path.split(":")) { 130 listFiles(out, new File(dir), ""); 131 132 var gparent = new File((new File(dir)).getAbsolutePath() + "../../..") 133 .getCanonicalFile(); 134 if (gparent.getName().contains("testcases")) { 135 // Special case: if we found this directory, dump its contents too. 136 listFiles(out, gparent, ""); 137 } 138 } 139 140 var gparent = new File("../..").getCanonicalFile(); 141 out.println("# ../..=" + gparent); 142 listFiles(out, gparent, ""); 143 } catch (Throwable th) { 144 out.println("Error: " + th.toString()); 145 th.printStackTrace(out); 146 } 147 } 148 listFiles(PrintStream out, File dir, String prefix)149 private static void listFiles(PrintStream out, File dir, String prefix) { 150 if (!dir.isDirectory()) { 151 out.println(prefix + dir.getAbsolutePath() + " is not a directory!"); 152 return; 153 } 154 out.println(prefix + ":" + dir.getAbsolutePath() + "/"); 155 // First, list the files. 156 for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) { 157 out.println(prefix + " " + file.getName() + "" + (file.isDirectory() ? "/" : "")); 158 } 159 160 // Then recurse. 161 if (dir.getAbsolutePath().startsWith("/usr") || dir.getAbsolutePath().startsWith("/lib")) { 162 // There would be too many files, so don't recurse. 163 return; 164 } 165 for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) { 166 if (file.isDirectory()) { 167 listFiles(out, file, prefix + " "); 168 } 169 } 170 } 171 172 /** 173 * @return the full directory path that contains the "ravenwood-runtime" files. 174 * 175 * This method throws if called on the device side. 176 */ getRavenwoodRuntimePath()177 public static String getRavenwoodRuntimePath() { 178 ensureOnRavenwood(); 179 return RAVEWOOD_RUNTIME_PATH; 180 } 181 getRavenwoodRuntimePathInternal()182 private static String getRavenwoodRuntimePathInternal() { 183 if (!isOnRavenwood()) { 184 return null; 185 } 186 var path = System.getProperty("java.library.path"); 187 188 System.out.println("Looking for " + RAVENWOOD_RUNTIME_DIR_NAME + " directory" 189 + " in java.library.path:" + path); 190 191 try { 192 if (path == null) { 193 throw new IllegalStateException("java.library.path shouldn't be null"); 194 } 195 for (var dir : path.split(":")) { 196 197 // For each path, see if the path contains RAVENWOOD_RUNTIME_DIR_NAME. 198 var d = new File(dir); 199 for (;;) { 200 if (d.getParent() == null) { 201 break; // Root dir, stop. 202 } 203 if (RAVENWOOD_RUNTIME_DIR_NAME.equals(d.getName())) { 204 var ret = d.getAbsolutePath() + "/"; 205 System.out.println("Found: " + ret); 206 return ret; 207 } 208 d = d.getParentFile(); 209 } 210 } 211 throw new IllegalStateException(RAVENWOOD_RUNTIME_DIR_NAME + " not found"); 212 } catch (Throwable e) { 213 dumpFiles(System.out); 214 throw e; 215 } 216 } 217 218 /** Close an {@link AutoCloseable}. */ closeQuietly(AutoCloseable c)219 public static void closeQuietly(AutoCloseable c) { 220 if (c != null) { 221 try { 222 c.close(); 223 } catch (Exception e) { 224 // Ignore 225 } 226 } 227 } 228 229 /** Close a {@link FileDescriptor}. */ closeQuietly(FileDescriptor fd)230 public static void closeQuietly(FileDescriptor fd) { 231 var is = new FileInputStream(fd); 232 RavenwoodCommonUtils.closeQuietly(is); 233 } 234 } 235