Home | History | Annotate | Download | only in conscrypt
      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