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