/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.soloader.nativeloader;

import java.io.IOException;

/** Facade to load native libraries for android */
public class NativeLoader {

  private static NativeLoaderDelegate sDelegate;

  /** Blocked default constructor */
  private NativeLoader() {}

  public static boolean loadLibrary(String shortName) {
    return loadLibrary(shortName, 0);
  }

  /**
   * Load a shared library, initializing any JNI binding it contains. Depending upon underlying
   * loader, behavior can be customized based on passed in flag
   *
   * @param shortName Name of library to find, without "lib" prefix or ".so" suffix
   * @param flags 0 for default behavior, otherwise NativeLoaderDelegate defines other behaviors.
   * @return Whether the library was loaded as a result of this call (true), or was already loaded
   *     through a previous call (false).
   */
  public static boolean loadLibrary(String shortName, int flags) {
    synchronized (NativeLoader.class) {
      if (sDelegate == null) {
        throw new IllegalStateException(
            "NativeLoader has not been initialized.  "
                + "To use standard native library loading, call "
                + "NativeLoader.init(new SystemDelegate()).");
      }
    }
    return sDelegate.loadLibrary(shortName, flags);
  }

  /**
   * Get the path of a loaded shared library.
   *
   * @param shortName Name of library to find, without "lib" prefix or ".so" suffix
   * @return The path for the loaded library, null otherwise.
   * @throws IOException IOException
   */
  public static String getLibraryPath(String shortName) throws IOException {
    synchronized (NativeLoader.class) {
      if (sDelegate == null) {
        throw new IllegalStateException(
            "NativeLoader has not been initialized.  "
                + "To use standard native library loading, call "
                + "NativeLoader.init(new SystemDelegate()).");
      }
    }

    return sDelegate.getLibraryPath(shortName);
  }

  /**
   * Get the version of the loader used.
   *
   * @return The version number for the loader.
   */
  public static int getSoSourcesVersion() {
    synchronized (NativeLoader.class) {
      if (sDelegate == null) {
        throw new IllegalStateException(
            "NativeLoader has not been initialized.  "
                + "To use standard native library loading, call "
                + "NativeLoader.init(new SystemDelegate()).");
      }
    }

    return sDelegate.getSoSourcesVersion();
  }

  /**
   * Initializes native code loading for this app. Should be called only once, before any calls to
   * {@link #loadLibrary(String)}.
   *
   * @param delegate Delegate to use for all {@code loadLibrary} calls.
   */
  public static synchronized void init(NativeLoaderDelegate delegate) {
    if (sDelegate != null) {
      throw new IllegalStateException("Cannot re-initialize NativeLoader.");
    }
    sDelegate = delegate;
  }

  /**
   * Determine whether {@code NativeLoader} has already been initialized. This method should not
   * normally be used, because initialization should be performed only once during app startup.
   * However, libraries that want to provide a default initialization for {@code NativeLoader} to
   * hide its existence from the app can use this method to avoid re-initializing.
   *
   * @return True if {@link #init(NativeLoaderDelegate)} has been called.
   */
  public static synchronized boolean isInitialized() {
    return sDelegate != null;
  }

  /**
   * Perform an initialization only if {@link NativeLoader} has not already been initialized. This
   * protects against race conditions where isInitialized and init are called by multiple threads
   * and both threads end up trying to perform an initialization
   *
   * @param delegate the NativeLoaderDelegate
   */
  public static synchronized void initIfUninitialized(NativeLoaderDelegate delegate) {
    if (!isInitialized()) {
      init(delegate);
    }
  }
}

@@ -1855,24 +1855,25 @@
     %7D%0A    %7D%0A
+%0A
     return s
@@ -3349,37 +3349,24 @@
 blic static 
-synchronized 
 void init(Na
@@ -3388,32 +3388,74 @@
 ate delegate) %7B%0A
+    synchronized (NativeLoader.class) %7B%0A  
     if (sDelegat
@@ -3463,24 +3463,26 @@
  != null) %7B%0A
+  
       throw 
@@ -3546,26 +3546,30 @@
 der.%22);%0A    
-%7D%0A
+  %7D%0A  
     sDelegat
@@ -3582,16 +3582,22 @@
 legate;%0A
+    %7D%0A
   %7D%0A%0A  /
@@ -4074,29 +4074,16 @@
  static 
-synchronized 
 boolean 
@@ -4096,24 +4096,66 @@
 ialized() %7B%0A
+    synchronized (NativeLoader.class) %7B%0A  
     return s
@@ -4172,16 +4172,22 @@
 = null;%0A
+    %7D%0A
   %7D%0A%0A  /
@@ -4524,29 +4524,16 @@
  static 
-synchronized 
 void ini

Fix a potential Thread Safety Violation: Read/Write race