1 /* 2 * Copyright 2014 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 org.conscrypt; 18 19 import java.util.ArrayList; 20 import java.util.Collections; 21 import java.util.Comparator; 22 import java.util.List; 23 import org.conscrypt.NativeLibraryLoader.LoadResult; 24 25 /** 26 * Helper to initialize the JNI libraries. This version runs when compiled as part of a host OpenJDK 27 * build. 28 */ 29 final class NativeCryptoJni { 30 private static final String STATIC_LIB_NAME = "conscrypt"; 31 private static final String DYNAMIC_LIB_NAME_PREFIX = "conscrypt_openjdk_jni"; 32 33 /** 34 * Attempts to load the shared JNI library. First try loading the platform-specific library 35 * name (e.g. conscrypt_openjdk_jni-linux-x86_64). If that doesn't work, try to load the 36 * library via just the prefix (e.g. conscrypt_openjdk_jni). If not found, try the static 37 * library name. 38 * 39 * The non-suffixed dynamic library name is used by the Android build system, which builds 40 * the appropriate library for the system it's being run on under that name. 41 * 42 * The static library name is needed in order to support Java 8 static linking 43 * (http://openjdk.java.net/jeps/178), where the library name is used to invoke a 44 * library-specific load method (i.e. {@code JNI_OnLoad_conscrypt}). 45 * 46 * @throws UnsatisfiedLinkError if the library failed to load. 47 */ 48 static void init() throws UnsatisfiedLinkError { 49 List<LoadResult> results = new ArrayList<LoadResult>(); 50 if (!NativeLibraryLoader.loadFirstAvailable(classLoader(), results, 51 platformLibName(), DYNAMIC_LIB_NAME_PREFIX, STATIC_LIB_NAME)) { 52 logResults(results); 53 throwBestError(results); 54 } 55 } 56 57 private NativeCryptoJni() {} 58 59 private static void logResults(List<LoadResult> results) { 60 for (LoadResult result : results) { 61 result.log(); 62 } 63 } 64 65 private static void throwBestError(List<LoadResult> results) { 66 Collections.sort(results, ErrorComparator.INSTANCE); 67 68 Throwable bestError = results.get(0).error; 69 for (LoadResult result : results.subList(1, results.size())) { 70 // Suppress all of the other errors, so that they're available to the caller if 71 // desired. 72 bestError.addSuppressed(result.error); 73 } 74 75 if (bestError instanceof Error) { 76 throw (Error) bestError; 77 } 78 79 throw (Error) new UnsatisfiedLinkError(bestError.getMessage()).initCause(bestError); 80 } 81 82 private static ClassLoader classLoader() { 83 return NativeCrypto.class.getClassLoader(); 84 } 85 86 private static String platformLibName() { 87 return DYNAMIC_LIB_NAME_PREFIX + "-" + osName() + '-' + archName(); 88 } 89 90 private static String osName() { 91 return HostProperties.OS.getFileComponent(); 92 } 93 94 private static String archName() { 95 return HostProperties.ARCH.getFileComponent(); 96 } 97 98 /** 99 * Sorts the errors in a list in descending order of value. After a list is sorted, 100 * the first element is the most important error. 101 */ 102 private static final class ErrorComparator implements Comparator<LoadResult> { 103 static final ErrorComparator INSTANCE = new ErrorComparator(); 104 105 @Override 106 public int compare(LoadResult o1, LoadResult o2) { 107 Throwable e1 = o1.error; 108 Throwable e2 = o2.error; 109 110 // First, sort by error type. 111 int value1 = e1 instanceof UnsatisfiedLinkError ? 1 : 0; 112 int value2 = e2 instanceof UnsatisfiedLinkError ? 1 : 0; 113 if (value1 != value2) { 114 // Order so that the UnsatisfiedLinkError is first. 115 return value2 - value1; 116 } 117 118 // Both are either link errors or not. Compare the message. Treat messages in 119 // the form "no <libName> in java.library.path" as lower value, since there may be 120 // a more interesting message for a library that was found. 121 String m1 = e1.getMessage(); 122 String m2 = e2.getMessage(); 123 value1 = m1 != null && m1.contains("java.library.path") ? 0 : 1; 124 value2 = m2 != null && m2.contains("java.library.path") ? 0 : 1; 125 return value2 - value1; 126 } 127 } 128 } 129