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