• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base.library_loader;
6 
7 import android.annotation.SuppressLint;
8 import android.content.Context;
9 import android.content.SharedPreferences;
10 import android.content.pm.ApplicationInfo;
11 import android.os.Build;
12 import android.os.Bundle;
13 import android.system.Os;
14 
15 import androidx.annotation.IntDef;
16 import androidx.annotation.VisibleForTesting;
17 
18 import org.chromium.base.BaseSwitches;
19 import org.chromium.base.CommandLine;
20 import org.chromium.base.ContextUtils;
21 import org.chromium.base.Log;
22 import org.chromium.base.NativeLibraryLoadedStatus;
23 import org.chromium.base.NativeLibraryLoadedStatus.NativeLibraryLoadedStatusProvider;
24 import org.chromium.base.StrictModeContext;
25 import org.chromium.base.TimeUtils.CurrentThreadTimeMillisTimer;
26 import org.chromium.base.TimeUtils.UptimeMillisTimer;
27 import org.chromium.base.TraceEvent;
28 import org.chromium.base.annotations.JNINamespace;
29 import org.chromium.base.annotations.NativeMethods;
30 import org.chromium.base.metrics.RecordHistogram;
31 import org.chromium.base.metrics.UmaRecorderHolder;
32 import org.chromium.build.BuildConfig;
33 import org.chromium.build.NativeLibraries;
34 import org.chromium.build.annotations.MainDex;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 
39 import javax.annotation.concurrent.GuardedBy;
40 
41 /**
42  * This class provides functionality to load and register the native libraries.
43  * Callers are allowed to separate loading the libraries from initializing
44  * them. When a zygote process is used (WebView or AppZygote) the per process
45  * initialization happens after the application processes are forked from the
46  * zygote process.
47  *
48  * The libraries may be loaded and initialized from any thread. Synchronization
49  * primitives are used to ensure that overlapping requests from different
50  * threads are handled sequentially.
51  *
52  * See also base/android/library_loader/library_loader_hooks.cc, which contains
53  * the native counterpart to this class.
54  */
55 @MainDex
56 @JNINamespace("base::android")
57 public class LibraryLoader {
58     private static final String TAG = "LibraryLoader";
59 
60     // Constant guarding debug logging in this class.
61     static final boolean DEBUG = false;
62 
63     // Shared preferences key for the reached code profiler.
64     private static final String DEPRECATED_REACHED_CODE_PROFILER_KEY =
65             "reached_code_profiler_enabled";
66     private static final String REACHED_CODE_SAMPLING_INTERVAL_KEY =
67             "reached_code_sampling_interval";
68 
69     // Compile time switch for sharing RELRO between the browser and the app zygote.
70     // TODO(crbug.com/1154224): remove when the issue is closed.
71     private static final boolean ALLOW_CHROMIUM_LINKER_IN_ZYGOTE = true;
72 
73     // Default sampling interval for reached code profiler in microseconds.
74     private static final int DEFAULT_REACHED_CODE_SAMPLING_INTERVAL_US = 10000;
75 
76     // Shared preferences key for the background thread pool setting.
77     private static final String BACKGROUND_THREAD_POOL_KEY = "background_thread_pool_enabled";
78 
79     // The singleton instance of LibraryLoader. Never null (not final for tests).
80     private static LibraryLoader sInstance = new LibraryLoader();
81 
82     private static boolean sBrowserStartupBlockedForTesting;
83 
84     // Helps mInitializedForTesting and mLoadStateForTesting to be removed by R8.
85     private static boolean sEnableStateForTesting;
86 
87     // One-way switch becomes true when the libraries are initialized (by calling
88     // LibraryLoaderJni.get().libraryLoaded, which forwards to LibraryLoaded(...) in
89     // library_loader_hooks.cc). Note that this member should remain a one-way switch, since it
90     // accessed from multiple threads without a lock.
91     private volatile boolean mInitialized;
92 
93     // One way switch used by initInAppZygote() when the current platform does not support loading
94     // using a Chromium Linker in the App Zygote. Because of this limited usage it can avoid
95     // synchronization.
96     private boolean mFallbackToSystemLinker;
97 
98     // State that only transitions one-way from 0->1->2. Volatile for the same reasons as
99     // mInitialized.
100     @IntDef({LoadState.NOT_LOADED, LoadState.MAIN_DEX_LOADED, LoadState.LOADED})
101     @Retention(RetentionPolicy.SOURCE)
102     private @interface LoadState {
103         int NOT_LOADED = 0;
104         int MAIN_DEX_LOADED = 1;
105         int LOADED = 2;
106     }
107     private volatile @LoadState int mLoadState;
108 
109     // Tracks mLoadState, but can be reset to NOT_LOADED between tests to ensure that each test that
110     // requires native explicitly loads it.
111     private @LoadState int mLoadStateForTesting;
112 
113     // Tracks mInitialized, but can be reset to false between tests to ensure that each test that
114     // requires native explicitly loads it.
115     private boolean mInitializedForTesting;
116 
117     // Whether to use the Chromium linker vs. the system linker.
118     // Avoids locking: should be initialized very early.
119     private boolean mUseChromiumLinker = NativeLibraries.sUseLinker;
120 
121     // The type of process the shared library is loaded in. Gets passed to native after loading.
122     // Avoids locking: should be initialized very early.
123     private @LibraryProcessType int mLibraryProcessType;
124 
125     // Mediates all communication between Linker instances in different processes.
126     private final MultiProcessMediator mMediator = new MultiProcessMediator();
127 
128     // Guards all the fields below.
129     private final Object mLock = new Object();
130 
131     // When a Chromium linker is used, this field represents the concrete class serving as a Linker.
132     // Always accessed via getLinker() because the choice of the class can be influenced by
133     // public setLinkerImplementation() below.
134     @GuardedBy("mLock")
135     private Linker mLinker;
136 
137     @GuardedBy("mLock")
138     private NativeLibraryPreloader mLibraryPreloader;
139 
140     @GuardedBy("mLock")
141     private boolean mLibraryPreloaderCalled;
142 
143     // Similar to |mLoadState| but is limited case of being loaded in app zygote.
144     // This is exposed to clients.
145     @GuardedBy("mLock")
146     private boolean mLoadedByZygote;
147 
148     // One-way switch becomes true when the Java command line is switched to
149     // native.
150     @GuardedBy("mLock")
151     private boolean mCommandLineSwitched;
152 
153     // Enumeration telling which init* methods were used, and therefore
154     // which process the library is loaded in.
155     @IntDef({CreatedIn.MAIN, CreatedIn.ZYGOTE, CreatedIn.CHILD_WITHOUT_ZYGOTE})
156     @Retention(RetentionPolicy.SOURCE)
157     private @interface CreatedIn {
158         int MAIN = 0;
159         int ZYGOTE = 1;
160         int CHILD_WITHOUT_ZYGOTE = 2;
161     }
162 
163     // Returns true when sharing RELRO between the browser process and the app zygote should *not*
164     // be attempted.
mainProcessIntendsToProvideRelroFd()165     public static boolean mainProcessIntendsToProvideRelroFd() {
166         return !ALLOW_CHROMIUM_LINKER_IN_ZYGOTE || Build.VERSION.SDK_INT <= Build.VERSION_CODES.R;
167     }
168 
169     /**
170      * Inner class encapsulating points of communication between instances of LibraryLoader in
171      * different processes.
172      *
173      * Usage:
174      *
175      * 0. In the main (Browser) process this mediator can be bypassed by
176      *    {@link LibraryLoader#ensureInitialized()}. It is convenient for targets that do not pay
177      *    attention to RELRO sharing and load time statistics, but it is also more error prone. The
178      *    {@link #ensureInitializedInMainProcess()} is recommended.
179      *
180      * 1. For a {@link LibraryLoader} requiring the knowledge of the load address before
181      *    initialization, {@link #takeLoadAddressFromBundle(Bundle)} should be called first. It is
182      *    done very early after establishing a Binder connection.
183      *
184      * 2. After the load address is received, the object needs to be initialized using one of
185      *    {@link #ensureInitializedInMainProcess()}, {@link #initInChildProcess()} and
186      *    {@link #initInAppZygote()}. For the main process the subsequent calls to initialization
187      *    are ignored, primarily to simplify tests.
188      *
189      * 3. Later {@link #putLoadAddressToBundle(Bundle)} and
190      *    {@link #takeLoadAddressFromBundle(Bundle)} should be called for passing the RELRO
191      *    information between library loaders.
192      *
193      * Internally the {@link LibraryLoader} may ignore these messages because it can fall back to
194      * not sharing RELRO.
195      *
196      * In general the class is *not* thread safe. The client must guarantee that the steps 1-3 above
197      * happen sequentially in the memory model sense. After that the class is safe to use from
198      * multiple threads concurrently.
199      */
200     public class MultiProcessMediator {
201         // Currently clients initialize |mLoadAddress| strictly before any other method can get
202         // executed on a different thread. Hence, synchronization is not required.
203         private long mLoadAddress;
204 
205         // Only ever switched from false to true.
206         private volatile boolean mInitDone;
207 
208         // How the mediator was created. The LibraryLoader.ensureInitialized() uses this default
209         // value.
210         private volatile @CreatedIn int mCreatedIn = CreatedIn.MAIN;
211 
212         /**
213          * Extracts the load address as provided by another process.
214          * @param bundle The Bundle to extract from.
215          */
takeLoadAddressFromBundle(Bundle bundle)216         public void takeLoadAddressFromBundle(Bundle bundle) {
217             assert !mInitDone;
218             mLoadAddress = Linker.extractLoadAddressFromBundle(bundle);
219         }
220 
getLoadAddress()221         private long getLoadAddress() {
222             synchronized (mLock) {
223                 return mLoadAddress;
224             }
225         }
226 
227         /**
228          * Initializes the Main (Browser) process side of communication. This process coordinates
229          * creation of other processes. Can be called more than once, subsequent calls are ignored.
230          */
ensureInitializedInMainProcess()231         public void ensureInitializedInMainProcess() {
232             if (mInitDone) return;
233             if (useChromiumLinker()) {
234                 boolean attemptProduceRelro = mainProcessIntendsToProvideRelroFd();
235                 // When the main process creates the shared region with relocations, it is faster
236                 // to randomize the load address than to find the reserved one
237                 // in /proc. When the main process relies on RELRO from the
238                 // zygote, then it should scan /proc to find the reserved range
239                 // because waiting for zygote to reveal its address would have
240                 // delayed startup.
241                 if (DEBUG) {
242                     Log.i(TAG, "ensureInitializedInMainProcess, producing RELRO FD: %b",
243                             attemptProduceRelro);
244                 }
245                 // For devices avoiding the App Zygote in
246                 // ChildConnectionAllocator.createVariableSize() the FIND_RESERVED search can be
247                 // avoided: a random region is sufficient. TODO(pasko): Investigate whether it is
248                 // worth coordinating with the ChildConnectionAllocator. To speed up process
249                 // creation.
250                 int preferAddress = attemptProduceRelro ? Linker.PreferAddress.RESERVE_RANDOM
251                                                         : Linker.PreferAddress.FIND_RESERVED;
252                 getLinker().ensureInitialized(
253                         attemptProduceRelro, preferAddress, /* addressHint= */ 0);
254             }
255             mCreatedIn = CreatedIn.MAIN;
256             mInitDone = true;
257         }
258 
259         /**
260          * Serializes the load address for communication, if any was determined during
261          * initialization. Must be called after the library has been loaded in this process.
262          * @param bundle Bundle to put the address to.
263          */
putLoadAddressToBundle(Bundle bundle)264         public void putLoadAddressToBundle(Bundle bundle) {
265             assert mInitDone;
266             if (useChromiumLinker()) {
267                 getLinker().putLoadAddressToBundle(bundle);
268             }
269         }
270 
271         /**
272          * Initializes in the App Zygote process. Will be followed by initInChildProcess() in all
273          * processes inheriting from the app zygote.
274          */
initInAppZygote()275         public void initInAppZygote() {
276             assert !mInitDone;
277             if (useChromiumLinker() && !mainProcessIntendsToProvideRelroFd()) {
278                 getLinker().ensureInitialized(
279                         /* asRelroProducer= */ true, Linker.PreferAddress.FIND_RESERVED, 0);
280             } else {
281                 // The main process will attempt to create RELRO FD without coordination. Fall back
282                 // to loading with the system linker. Can happen in tests and on dev builds with
283                 // forceSystemLinker(), should not happen in the field.
284                 mFallbackToSystemLinker = true;
285             }
286             mCreatedIn = CreatedIn.ZYGOTE;
287             // The initInChildProcess() will set |mInitDone| to |true| after fork(2).
288         }
289 
290         /**
291          * Initializes in processes other than "Main". Can be called only once in each non-main
292          * process.
293          */
initInChildProcess()294         public void initInChildProcess() {
295             assert !mInitDone;
296             if (!useChromiumLinker()) {
297                 mInitDone = true;
298                 return;
299             }
300             if (mainProcessIntendsToProvideRelroFd()) {
301                 if (DEBUG) {
302                     Log.i(TAG, "initInChildProcess: RELRO FD not provided by App Zygote");
303                 }
304                 getLinker().ensureInitialized(/* asRelroProducer= */ false,
305                         Linker.PreferAddress.RESERVE_HINT, getLoadAddress());
306             } else if (isLoadedByZygote()) {
307                 if (DEBUG) {
308                     Log.i(TAG,
309                             "initInChildProcess: already loaded by app zygote "
310                                     + "(mFallbackToSystemLinker=%b)",
311                             mFallbackToSystemLinker);
312                 }
313             } else if (mCreatedIn == CreatedIn.ZYGOTE) {
314                 if (DEBUG) {
315                     Log.i(TAG, "initInChildProcess: the app zygote failed to produce RELRO FD");
316                 }
317                 getLinker().ensureInitialized(/* asRelroProducer= */ false,
318                         Linker.PreferAddress.RESERVE_HINT, getLoadAddress());
319             } else {
320                 // The main process expects the app zygote to provide the RELRO FD, but this process
321                 // does not inherit from the app zygote. This could be because:
322                 // 1. Running in a privileged process - very common
323                 // 2. Running in a renderer process - App Zygote was disabled due to opt out on
324                 //    low end devices - somewhat common
325                 // To cover both cases start with FIND_RESERVED, and proceed with fallbacks built
326                 // into the Linker initialization.
327                 //
328                 // TODO(pasko): Investigate whether searching with FIND_RESERVED affects startup
329                 // speed on Go devices.
330                 if (DEBUG) {
331                     Log.i(TAG,
332                             "initInChildProcess: child process not from app zygote, with address "
333                                     + "hint: 0x%x",
334                             getLoadAddress());
335                 }
336                 getLinker().ensureInitialized(/* asRelroProducer= */ false,
337                         Linker.PreferAddress.FIND_RESERVED, getLoadAddress());
338             }
339             if (mCreatedIn != CreatedIn.ZYGOTE) mCreatedIn = CreatedIn.CHILD_WITHOUT_ZYGOTE;
340             mInitDone = true;
341         }
342 
343         /**
344          * Optionally extracts RELRO and saves it for replacing the RELRO section in this process.
345          * Can be invoked before initialization.
346          * @param bundle Where to deserialize from.
347          */
takeSharedRelrosFromBundle(Bundle bundle)348         public void takeSharedRelrosFromBundle(Bundle bundle) {
349             if (useChromiumLinker()) {
350                 getLinker().takeSharedRelrosFromBundle(bundle);
351             }
352         }
353 
354         /**
355          * Optionally puts the RELRO section information so that it can be memory-mapped in another
356          * process reading the bundle.
357          * @param bundle Where to serialize.
358          */
putSharedRelrosToBundle(Bundle bundle)359         public void putSharedRelrosToBundle(Bundle bundle) {
360             assert mInitDone;
361             if (useChromiumLinker()) {
362                 getLinker().putSharedRelrosToBundle(bundle);
363             }
364         }
365 
recordLinkerHistogramsAfterLibraryLoad()366         private void recordLinkerHistogramsAfterLibraryLoad() {
367             if (!useChromiumLinker()) return;
368             // When recording a sample in the App Zygote it gets copied to each forked process and
369             // hence gets duplicated in the uploads. Avoiding such duplication would require
370             // serializing the samples, sending them to the browser process and disambiguating by,
371             // for example, Zygote PID in ChildProcessConnection.java. A few rough performance
372             // estimations do not require this complexity.
373             getLinker().recordHistograms(creationAsString());
374         }
375 
creationAsString()376         private String creationAsString() {
377             switch (mCreatedIn) {
378                 case CreatedIn.MAIN:
379                     return "Browser";
380                 case CreatedIn.ZYGOTE:
381                     return "Zygote";
382                 case CreatedIn.CHILD_WITHOUT_ZYGOTE:
383                     return "Child";
384                 default:
385                     assert false : "Must initialize as one of {Browser,Zygote,Child}";
386                     return "";
387             }
388         }
389 
390         private static final String LINKER_HISTOGRAM_PREFIX = "ChromiumAndroidLinker.";
391 
recordLoadTimeHistogram(long loadTimeMs)392         private void recordLoadTimeHistogram(long loadTimeMs) {
393             RecordHistogram.recordTimesHistogram(
394                     LINKER_HISTOGRAM_PREFIX + creationAsString() + "LoadTime2", loadTimeMs);
395         }
396 
recordLoadThreadTimeHistogram(long threadLoadTimeMs)397         public void recordLoadThreadTimeHistogram(long threadLoadTimeMs) {
398             RecordHistogram.recordTimesHistogram(
399                     LINKER_HISTOGRAM_PREFIX + creationAsString() + "ThreadLoadTime",
400                     threadLoadTimeMs);
401         }
402     }
403 
getMediator()404     public final MultiProcessMediator getMediator() {
405         return mMediator;
406     }
407 
getInstance()408     public static LibraryLoader getInstance() {
409         return sInstance;
410     }
411 
412     @VisibleForTesting
LibraryLoader()413     protected LibraryLoader() {
414         if (DEBUG) {
415             logLinkerUsed();
416         }
417         if (BuildConfig.ENABLE_ASSERTS) {
418             NativeLibraryLoadedStatus.setProvider(new NativeLibraryLoadedStatusProvider() {
419                 @Override
420                 public boolean areNativeMethodsReady() {
421                     return isMainDexLoaded();
422                 }
423             });
424         }
425     }
426 
427     /**
428      * Set the {@link LibraryProcessType} for this process.
429      *
430      * Since this function is called extremely early on in startup, locking is not required.
431      *
432      * @param type the process type.
433      */
setLibraryProcessType(@ibraryProcessType int type)434     public void setLibraryProcessType(@LibraryProcessType int type) {
435         assert type != LibraryProcessType.PROCESS_UNINITIALIZED;
436         if (type == mLibraryProcessType) return;
437         if (mLibraryProcessType != LibraryProcessType.PROCESS_UNINITIALIZED) {
438             throw new IllegalStateException(
439                     String.format("Trying to change the LibraryProcessType from %d to %d",
440                             mLibraryProcessType, type));
441         }
442         mLibraryProcessType = type;
443     }
444 
445     /**
446      * Set native library preloader. If set and the Chromium linker is not used, the
447      * {@link NativeLibraryPreloader#loadLibrary(String)} ()} will be invoked before calling
448      * System.loadLibrary().
449      *
450      * @param loader the NativeLibraryPreloader, it shall only be set once and before the
451      *               native library is loaded.
452      */
setNativeLibraryPreloader(NativeLibraryPreloader loader)453     public void setNativeLibraryPreloader(NativeLibraryPreloader loader) {
454         synchronized (mLock) {
455             assert mLibraryPreloader == null;
456             assert mLoadState == LoadState.NOT_LOADED;
457             mLibraryPreloader = loader;
458         }
459     }
460 
461     /**
462      * Sets the configuration for library loading.
463      *
464      * Must be called before loading the library. Since this function is called extremely early on
465      * in startup, locking is not required.
466      *
467      * @param useChromiumLinker Whether to use a chromium linker.
468      */
setLinkerImplementation(boolean useChromiumLinker)469     public void setLinkerImplementation(boolean useChromiumLinker) {
470         assert !mInitialized;
471         mUseChromiumLinker = useChromiumLinker;
472         if (DEBUG) logLinkerUsed();
473     }
474 
logLinkerUsed()475     private void logLinkerUsed() {
476         Log.i(TAG, "Configuration: useChromiumLinker() = %b", useChromiumLinker());
477     }
478 
479     // Whether a Linker subclass replaces the system dynamic linker for loading. Even if returns
480     // |true|, when the Linker fails, the system dynamic linker is used as a fallback. Also it is
481     // common for App Zygote to choose loading with the system linker when sharing RELRO with the
482     // browser process is not supported.
useChromiumLinker()483     private boolean useChromiumLinker() {
484         return mUseChromiumLinker;
485     }
486 
487     /**
488      * Returns the singleton Linker instance.
489      *
490      * On N, O and P Monochrome is selected by Play Store. With Monochrome this code is not used,
491      * instead Chrome asks the WebView to provide the library (and the shared RELRO). If the WebView
492      * fails to provide the library, the system linker is used as a fallback.
493      *
494      * More: docs/android_native_libraries.md
495      *
496      * @return the Linker implementation instance.
497      */
getLinker()498     private Linker getLinker() {
499         assert useChromiumLinker();
500         synchronized (mLock) {
501             if (mLinker == null) mLinker = new Linker();
502             return mLinker;
503         }
504     }
505 
506     /**
507      * Return if library is already loaded successfully by the zygote.
508      */
isLoadedByZygote()509     public boolean isLoadedByZygote() {
510         synchronized (mLock) {
511             return mLoadedByZygote;
512         }
513     }
514 
515     /**
516      *  Blocks until the library is fully loaded and initialized. When this method is used (without
517      *  the {@link MultiProcessMediator}) the current process is treated as the Main process
518      *  (w.r.t. how it shares RELRO and reports metrics) unless it was initialized before.
519      */
ensureInitialized()520     public void ensureInitialized() {
521         if (isInitialized()) return;
522         ensureMainDexInitialized();
523         loadNonMainDex();
524     }
525 
526     /**
527      * This method blocks until the native library is initialized, and the Main Dex is loaded
528      * (MainDex JNI is registered).
529      *
530      * You should use this if you would like to use isolated parts of the native library that don't
531      * depend on content initialization, and only use MainDex classes with JNI.
532      *
533      * However, you should be careful not to call this too early in startup on the UI thread, or you
534      * may significantly increase the time to first draw.
535      */
ensureMainDexInitialized()536     public void ensureMainDexInitialized() {
537         synchronized (mLock) {
538             if (DEBUG) logLinkerUsed();
539             loadMainDexAlreadyLocked(
540                     ContextUtils.getApplicationContext().getApplicationInfo(), false);
541             initializeAlreadyLocked();
542         }
543     }
544 
545     /**
546      * Calls native library preloader (see {@link #setNativeLibraryPreloader}) with the app
547      * context. If there is no preloader set, this function does nothing.
548      * Preloader is called only once, so calling it explicitly via this method means
549      * that it won't be (implicitly) called during library loading.
550      */
preloadNow()551     public void preloadNow() {
552         preloadNowOverridePackageName(
553                 ContextUtils.getApplicationContext().getApplicationInfo().packageName);
554     }
555 
556     /**
557      * Similar to {@link #preloadNow}, but allows specifying app context to use.
558      */
preloadNowOverridePackageName(String packageName)559     public void preloadNowOverridePackageName(String packageName) {
560         synchronized (mLock) {
561             if (useChromiumLinker()) return;
562             preloadAlreadyLocked(packageName, /* inZygote= */ false);
563         }
564     }
565 
566     @GuardedBy("mLock")
preloadAlreadyLocked(String packageName, boolean inZygote)567     private void preloadAlreadyLocked(String packageName, boolean inZygote) {
568         try (TraceEvent te = TraceEvent.scoped("LibraryLoader.preloadAlreadyLocked")) {
569             // Preloader uses system linker, we shouldn't preload if Chromium linker is used.
570             assert !useChromiumLinker() || (inZygote && mainProcessIntendsToProvideRelroFd());
571             if (mLibraryPreloader != null && !mLibraryPreloaderCalled) {
572                 mLibraryPreloader.loadLibrary(packageName);
573                 mLibraryPreloaderCalled = true;
574             }
575         }
576     }
577 
578     /**
579      * Checks whether the native library is fully loaded.
580      *
581      * @deprecated: please avoid using in new code:
582      * https://crsrc.org/c/base/android/jni_generator/README.md#testing-for-readiness-use-get
583      */
584     @Deprecated
585     @VisibleForTesting
isLoaded()586     public boolean isLoaded() {
587         return mLoadState == LoadState.LOADED
588                 && (!sEnableStateForTesting || mLoadStateForTesting == LoadState.LOADED);
589     }
590 
isMainDexLoaded()591     private boolean isMainDexLoaded() {
592         return mLoadState >= LoadState.MAIN_DEX_LOADED
593                 && (!sEnableStateForTesting || mLoadStateForTesting >= LoadState.MAIN_DEX_LOADED);
594     }
595 
596     /**
597      * Checks whether the native library is fully loaded and initialized.
598      *
599      * @deprecated: please avoid using in new code:
600      * https://chromium.googlesource.com/chromium/src/+/main/base/android/jni_generator/README.md#testing-for-readiness_use
601      */
602     @Deprecated
isInitialized()603     public boolean isInitialized() {
604         return mInitialized && isLoaded() && (!sEnableStateForTesting || mInitializedForTesting);
605     }
606 
607     /**
608      * Loads the library and blocks until the load completes. The caller is responsible for
609      * subsequently calling ensureInitialized(). May be called on any thread, but should only be
610      * called once.
611      */
loadNow()612     public void loadNow() {
613         loadNowOverrideApplicationContext(ContextUtils.getApplicationContext());
614     }
615 
616     /**
617      * Causes LibraryLoader to pretend that native libraries have not yet been initialized.
618      */
resetForTesting()619     public void resetForTesting() {
620         mLoadStateForTesting = LoadState.NOT_LOADED;
621         mInitializedForTesting = false;
622         sEnableStateForTesting = true;
623     }
624 
625     /**
626      * Override kept for callers that need to load from a different app context. Do not use unless
627      * specifically required to load from another context that is not the current process's app
628      * context.
629      *
630      * @param appContext The overriding app context to be used to load libraries.
631      */
loadNowOverrideApplicationContext(Context appContext)632     public void loadNowOverrideApplicationContext(Context appContext) {
633         synchronized (mLock) {
634             if (mLoadState != LoadState.NOT_LOADED
635                     && appContext != ContextUtils.getApplicationContext()) {
636                 throw new IllegalStateException("Attempt to load again from alternate context.");
637             }
638             loadMainDexAlreadyLocked(appContext.getApplicationInfo(), /* inZygote= */ false);
639         }
640         loadNonMainDex();
641     }
642 
loadNowInZygote(ApplicationInfo appInfo)643     public void loadNowInZygote(ApplicationInfo appInfo) {
644         synchronized (mLock) {
645             assert mLoadState == LoadState.NOT_LOADED;
646             loadMainDexAlreadyLocked(appInfo, /* inZygote= */ true);
647             loadNonMainDex();
648             mLoadedByZygote = true;
649         }
650     }
651 
652     /**
653      * Initializes the native library: must be called on the thread that the
654      * native will call its "main" thread. The library must have previously been
655      * loaded with one of the loadNow*() variants.
656      */
initialize()657     public void initialize() {
658         synchronized (mLock) {
659             initializeAlreadyLocked();
660         }
661     }
662 
663     /**
664      * Enables the reached code profiler. The value comes from "ReachedCodeProfiler"
665      * finch experiment, and is pushed on every run. I.e. the effect of the finch experiment
666      * lags by one run, which is the best we can do considering that the profiler has to be enabled
667      * before finch is initialized. Note that since LibraryLoader is in //base, it can't depend
668      * on ChromeFeatureList, and has to rely on external code pushing the value.
669      *
670      * @param enabled whether to enable the reached code profiler.
671      * @param samplingIntervalUs the sampling interval for reached code profiler.
672      */
setReachedCodeProfilerEnabledOnNextRuns( boolean enabled, int samplingIntervalUs)673     public static void setReachedCodeProfilerEnabledOnNextRuns(
674             boolean enabled, int samplingIntervalUs) {
675         // Store 0 if the profiler is not enabled, otherwise store the sampling interval in
676         // microseconds.
677         if (enabled && samplingIntervalUs == 0) {
678             samplingIntervalUs = DEFAULT_REACHED_CODE_SAMPLING_INTERVAL_US;
679         } else if (!enabled) {
680             samplingIntervalUs = 0;
681         }
682         SharedPreferences.Editor editor = ContextUtils.getAppSharedPreferences().edit();
683         editor.remove(DEPRECATED_REACHED_CODE_PROFILER_KEY);
684         editor.putInt(REACHED_CODE_SAMPLING_INTERVAL_KEY, samplingIntervalUs).apply();
685     }
686 
687     /**
688      * @return sampling interval for reached code profiler, or 0 when the profiler is disabled. (see
689      *         setReachedCodeProfilerEnabledOnNextRuns()).
690      */
691     @VisibleForTesting
getReachedCodeSamplingIntervalUs()692     public static int getReachedCodeSamplingIntervalUs() {
693         try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
694             if (ContextUtils.getAppSharedPreferences().getBoolean(
695                         DEPRECATED_REACHED_CODE_PROFILER_KEY, false)) {
696                 return DEFAULT_REACHED_CODE_SAMPLING_INTERVAL_US;
697             }
698             return ContextUtils.getAppSharedPreferences().getInt(
699                     REACHED_CODE_SAMPLING_INTERVAL_KEY, 0);
700         }
701     }
702 
703     /**
704      * Enables the background priority thread pool group. The value comes from the
705      * "BackgroundThreadPool" finch experiment, and is pushed on every run, to take effect on the
706      * subsequent run. I.e. the effect of the finch experiment lags by one run, which is the best we
707      * can do considering that the thread pool has to be configured before finch is initialized.
708      * Note that since LibraryLoader is in //base, it can't depend on ChromeFeatureList, and has to
709      * rely on external code pushing the value.
710      *
711      * @param enabled whether to enable the background priority thread pool group.
712      */
setBackgroundThreadPoolEnabledOnNextRuns(boolean enabled)713     public static void setBackgroundThreadPoolEnabledOnNextRuns(boolean enabled) {
714         SharedPreferences.Editor editor = ContextUtils.getAppSharedPreferences().edit();
715         editor.putBoolean(BACKGROUND_THREAD_POOL_KEY, enabled).apply();
716     }
717 
718     /**
719      * @return whether the background priority thread pool group should be enabled. (see
720      *         setBackgroundThreadPoolEnabledOnNextRuns()).
721      */
722     @VisibleForTesting
isBackgroundThreadPoolEnabled()723     public static boolean isBackgroundThreadPoolEnabled() {
724         try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) {
725             return ContextUtils.getAppSharedPreferences().getBoolean(
726                     BACKGROUND_THREAD_POOL_KEY, false);
727         }
728     }
729 
loadWithChromiumLinker(ApplicationInfo appInfo, String library)730     private void loadWithChromiumLinker(ApplicationInfo appInfo, String library) {
731         Linker linker = getLinker();
732         String sourceDir = appInfo.sourceDir;
733         Log.i(TAG, "Loading %s from within %s", library, sourceDir);
734         linker.loadLibrary(library); // May throw UnsatisfiedLinkError.
735         getMediator().recordLinkerHistogramsAfterLibraryLoad();
736     }
737 
738     @GuardedBy("mLock")
739     @SuppressLint({"UnsafeDynamicallyLoadedCode"})
loadWithSystemLinkerAlreadyLocked(ApplicationInfo appInfo, boolean inZygote)740     private void loadWithSystemLinkerAlreadyLocked(ApplicationInfo appInfo, boolean inZygote) {
741         setEnvForNative();
742         preloadAlreadyLocked(appInfo.packageName, inZygote);
743         for (String library : NativeLibraries.LIBRARIES) {
744             System.loadLibrary(library);
745         }
746     }
747 
748     // Invokes either Linker.loadLibrary(...), System.loadLibrary(...) or System.load(...),
749     // triggering JNI_OnLoad in native code.
750     @GuardedBy("mLock")
751     @VisibleForTesting
loadMainDexAlreadyLocked(ApplicationInfo appInfo, boolean inZygote)752     protected void loadMainDexAlreadyLocked(ApplicationInfo appInfo, boolean inZygote) {
753         if (mLoadState >= LoadState.MAIN_DEX_LOADED) {
754             if (sEnableStateForTesting && mLoadStateForTesting == LoadState.NOT_LOADED) {
755                 mLoadStateForTesting = LoadState.MAIN_DEX_LOADED;
756             }
757             return;
758         }
759         try (TraceEvent te = TraceEvent.scoped("LibraryLoader.loadMainDexAlreadyLocked")) {
760             assert !mInitialized;
761             assert mLibraryProcessType != LibraryProcessType.PROCESS_UNINITIALIZED || inZygote;
762 
763             UptimeMillisTimer uptimeTimer = new UptimeMillisTimer();
764             CurrentThreadTimeMillisTimer threadTimeTimer = new CurrentThreadTimeMillisTimer();
765 
766             if (useChromiumLinker() && !mFallbackToSystemLinker) {
767                 if (DEBUG) Log.i(TAG, "Loading with the Chromium linker.");
768                 // See base/android/linker/config.gni, the chromium linker is only enabled when
769                 // we have a single library.
770                 assert NativeLibraries.LIBRARIES.length == 1;
771                 String library = NativeLibraries.LIBRARIES[0];
772                 loadWithChromiumLinker(appInfo, library);
773             } else {
774                 if (DEBUG) Log.i(TAG, "Loading with the System linker.");
775                 loadWithSystemLinkerAlreadyLocked(appInfo, inZygote);
776             }
777 
778             long loadTimeMs = uptimeTimer.getElapsedMillis();
779 
780             if (DEBUG) Log.i(TAG, "Time to load native libraries: %d ms", loadTimeMs);
781             mLoadState = LoadState.MAIN_DEX_LOADED;
782             if (sEnableStateForTesting) {
783                 mLoadStateForTesting = LoadState.MAIN_DEX_LOADED;
784             }
785 
786             getMediator().recordLoadTimeHistogram(loadTimeMs);
787             getMediator().recordLoadThreadTimeHistogram(threadTimeTimer.getElapsedMillis());
788         } catch (UnsatisfiedLinkError e) {
789             throw new ProcessInitException(LoaderErrors.NATIVE_LIBRARY_LOAD_FAILED, e);
790         }
791     }
792 
793     // This used to actually do stuff, but now we have removed the concept of MainDex/non-MainDex
794     // JNI. However, entirely removing the "middle state" (LoadState.MAIN_DEX) causes issues with
795     // robolectric tests using GURL. See https://crbug.com/1371542#c13.
796     @VisibleForTesting
loadNonMainDex()797     protected void loadNonMainDex() {
798         mLoadState = LoadState.LOADED;
799         if (sEnableStateForTesting) {
800             mLoadStateForTesting = LoadState.LOADED;
801         }
802     }
803 
804     // The WebView requires the Command Line to be switched over before
805     // initialization is done. This is okay in the WebView's case since the
806     // JNI is already loaded by this point.
switchCommandLineForWebView()807     public void switchCommandLineForWebView() {
808         synchronized (mLock) {
809             ensureCommandLineSwitchedAlreadyLocked();
810         }
811     }
812 
813     // Switch the CommandLine over from Java to native if it hasn't already been done.
814     // This must happen after the code is loaded and after JNI is ready (since after the
815     // switch the Java CommandLine will delegate all calls the native CommandLine).
816     @GuardedBy("mLock")
ensureCommandLineSwitchedAlreadyLocked()817     private void ensureCommandLineSwitchedAlreadyLocked() {
818         assert isMainDexLoaded();
819         if (mCommandLineSwitched) {
820             return;
821         }
822         CommandLine.enableNativeProxy();
823         mCommandLineSwitched = true;
824     }
825 
826     /**
827      * Assert that library process type in the LibraryLoader is compatible with provided type.
828      *
829      * @param libraryProcessType a library process type to assert.
830      */
assertCompatibleProcessType(@ibraryProcessType int libraryProcessType)831     public void assertCompatibleProcessType(@LibraryProcessType int libraryProcessType) {
832         assert libraryProcessType == mLibraryProcessType;
833     }
834 
835     // Invoke base::android::LibraryLoaded in library_loader_hooks.cc
836     @GuardedBy("mLock")
initializeAlreadyLocked()837     private void initializeAlreadyLocked() {
838         if (mInitialized) {
839             if (sEnableStateForTesting) {
840                 mInitializedForTesting = true;
841             }
842             return;
843         }
844         assert mLibraryProcessType != LibraryProcessType.PROCESS_UNINITIALIZED;
845 
846         if (mLibraryProcessType == LibraryProcessType.PROCESS_BROWSER) {
847             // Add a switch for the reached code profiler as late as possible since it requires a
848             // read from the shared preferences. At this point the shared preferences are usually
849             // warmed up.
850             int reachedCodeSamplingIntervalUs = getReachedCodeSamplingIntervalUs();
851             if (reachedCodeSamplingIntervalUs > 0) {
852                 CommandLine.getInstance().appendSwitch(BaseSwitches.ENABLE_REACHED_CODE_PROFILER);
853                 CommandLine.getInstance().appendSwitchWithValue(
854                         BaseSwitches.REACHED_CODE_SAMPLING_INTERVAL_US,
855                         Integer.toString(reachedCodeSamplingIntervalUs));
856             }
857 
858             // Similarly, append a switch to enable the background thread pool group if the cached
859             // preference indicates it should be enabled.
860             if (isBackgroundThreadPoolEnabled()) {
861                 CommandLine.getInstance().appendSwitch(BaseSwitches.ENABLE_BACKGROUND_THREAD_POOL);
862             }
863         }
864 
865         ensureCommandLineSwitchedAlreadyLocked();
866 
867         if (!LibraryLoaderJni.get().libraryLoaded(mLibraryProcessType)) {
868             Log.e(TAG, "error calling LibraryLoaderJni.get().libraryLoaded");
869             throw new ProcessInitException(LoaderErrors.FAILED_TO_REGISTER_JNI);
870         }
871 
872         // The "Successfully loaded native library" string is used by
873         // tools/android/build_speed/benchmark.py. Please update that script if this log message is
874         // changed.
875         Log.i(TAG, "Successfully loaded native library");
876         UmaRecorderHolder.onLibraryLoaded();
877 
878         // From now on, keep tracing in sync with native.
879         TraceEvent.onNativeTracingReady();
880 
881         // From this point on, native code is ready to use, but non-MainDex JNI may not yet have
882         // been registered. Check isInitialized() to be sure that initialization is fully complete.
883         // Note that this flag can be accessed asynchronously, so any initialization
884         // must be performed before.
885         mInitialized = true;
886         if (sEnableStateForTesting) {
887             mInitializedForTesting = true;
888         }
889     }
890 
891     /**
892      * Overrides the library loader (normally with a mock) for testing.
893      *
894      * @deprecated: please avoid using in new code:
895      * https://chromium.googlesource.com/chromium/src/+/main/base/android/jni_generator/README.md#testing-for-readiness_use
896      *
897      * @param loader the mock library loader.
898      */
899     @Deprecated
900     @VisibleForTesting
setLibraryLoaderForTesting(LibraryLoader loader)901     public static void setLibraryLoaderForTesting(LibraryLoader loader) {
902         sInstance = loader;
903     }
904 
905     /**
906      * Configure ubsan using $UBSAN_OPTIONS. This function needs to be called before any native
907      * libraries are loaded because ubsan reads its configuration from $UBSAN_OPTIONS when the
908      * native library is loaded.
909      */
setEnvForNative()910     public static void setEnvForNative() {
911         // The setenv API was added in L. On older versions of Android, we should still see ubsan
912         // reports, but they will not have stack traces.
913         if (BuildConfig.IS_UBSAN) {
914             try {
915                 // This value is duplicated in build/android/pylib/constants/__init__.py.
916                 Os.setenv("UBSAN_OPTIONS",
917                         "print_stacktrace=1 stack_trace_format='#%n pc %o %m' "
918                                 + "handle_segv=0 handle_sigbus=0 handle_sigfpe=0",
919                         true);
920             } catch (Exception e) {
921                 Log.w(TAG, "failed to set UBSAN_OPTIONS", e);
922             }
923         }
924     }
925 
926     /**
927      * This sets the LibraryLoader internal state to its fully initialized state and should *only*
928      * be used by clients like NativeTests which manually load their native libraries without using
929      * the LibraryLoader.
930      *
931      * Don't use in new code. Tests that require this call should be migrated to
932      * NativeUnitTest.
933      * https://chromium.googlesource.com/chromium/src/+/main/base/android/jni_generator/README.md#testing-for-readiness_use
934      */
setLibrariesLoadedForNativeTests()935     protected static void setLibrariesLoadedForNativeTests() {
936         LibraryLoader self = getInstance();
937         self.mLoadState = LoadState.LOADED;
938         self.mInitialized = true;
939         if (sEnableStateForTesting) {
940             self.mInitializedForTesting = true;
941             self.mLoadStateForTesting = LoadState.LOADED;
942         }
943     }
944 
setBrowserProcessStartupBlockedForTesting()945     public static void setBrowserProcessStartupBlockedForTesting() {
946         sBrowserStartupBlockedForTesting = true;
947     }
948 
isBrowserProcessStartupBlockedForTesting()949     public static boolean isBrowserProcessStartupBlockedForTesting() {
950         return sBrowserStartupBlockedForTesting;
951     }
952 
953     // The native methods below are defined in library_loader_hooks.cc.
954     @NativeMethods
955     interface Natives {
956         // Performs auxiliary initialization useful right after the native library load. Returns
957         // true on success and false on failure.
libraryLoaded(@ibraryProcessType int processType)958         boolean libraryLoaded(@LibraryProcessType int processType);
959     }
960 }
961