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