Skip to content

Commit

Permalink
Add support for app bundle (#26)
Browse files Browse the repository at this point in the history
Summary:
PR is related to #19

**Problem**
SoLoader cannot load `.so`libraries when an application is installed through [App Bundle](https://developer.android.com/platform/technology/app-bundle/) with config:
```
bundle {
    abi {
        enableSplit = true
    }
}
```

The reason is that app bundle installation consists of multiple apks. For instance:
- `base.apk`
- `split_config.arm64_v8a.apk`
- `split_config.xxxhdpi.apk`

`SoLoader` class creates `ApkSoSource` pointing only to  `base.apk`. But `*.so` files exist only in `split_config.arm64_v8a.apk`

**Solution**
Create separate `ApkSoSource` for each apk in the application folder. I couldn't figure out how to filter out apk without lib folder.

**Testing**
I haven't tried this with dynamic feature modules yet. So I can't say that it fully fixes #19. But at least it fixes facebook/fresco#2253
Any help with dynamic modules testing would be much appreciated.

**P.S.**
Due to some reasons, I can't wait for the new release of SoLoader lib, so I have published a temporary patched version to bintray in [my account](https://bintray.com/nnesterov/maven/patched-soloader)
with different grouipId `com.avito.android:patched-soloader:0.1.0`
If you are against publishing your lib in a different account, please let me know. I'll remove my version immediately.
Pull Request resolved: #26

Reviewed By: oprisnik

Differential Revision: D13669370

Pulled By: passy

fbshipit-source-id: dd605bf54662a38cb1fd04461e1e962f76da7d51
  • Loading branch information
Nikolay Nesterov authored and facebook-github-bot committed Jan 23, 2019
1 parent ea661f5 commit c7980fc
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 15 deletions.
9 changes: 5 additions & 4 deletions java/com/facebook/soloader/ApkSoSource.java
Expand Up @@ -21,10 +21,7 @@
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import javax.annotation.Nullable;

/**
* {@link SoSource} that extracts libraries from an APK to the filesystem.
Expand All @@ -47,10 +44,14 @@ public class ApkSoSource extends ExtractFromZipSoSource {
private final int mFlags;

public ApkSoSource(Context context, String name, int flags) {
this(context, new File(context.getApplicationInfo().sourceDir), name, flags);
}

public ApkSoSource(Context context, File apkPath, String name, int flags) {
super(
context,
name,
new File(context.getApplicationInfo().sourceDir),
apkPath,
// The regular expression matches libraries that would ordinarily be unpacked
// during installation.
"^lib/([^/]+)/([^/]+\\.so)$");
Expand Down
56 changes: 45 additions & 11 deletions java/com/facebook/soloader/SoLoader.java
Expand Up @@ -91,10 +91,10 @@ public class SoLoader {

private static int sSoSourcesVersion = 0;

/** A backup SoSource to try if a lib file is corrupted */
/** A backup SoSources to try if a lib file is corrupted */
@GuardedBy("sSoSourcesLock")
@Nullable
private static UnpackingSoSource sBackupSoSource;
private static UnpackingSoSource[] sBackupSoSources;

/**
* A SoSource for the Context.ApplicationInfo.nativeLibsDir that can be updated if the application
Expand All @@ -121,9 +121,14 @@ public class SoLoader {
/** Wrapper for System.loadLlibrary. */
@Nullable private static SystemLoadLibraryWrapper sSystemLoadLibraryWrapper = null;

/** Name of the directory we use for extracted DSOs from built-in SO sources (APK, exopackage) */
/**
* Name of the directory we use for extracted DSOs from built-in SO sources (main APK, exopackage)
*/
private static final String SO_STORE_NAME_MAIN = "lib-main";

/** Name of the directory we use for extracted DSOs from split APKs */
private static final String SO_STORE_NAME_SPLITTED = "lib-";

/** Enable the exopackage SoSource. */
public static final int SOLOADER_ENABLE_EXOPACKAGE = (1 << 0);

Expand Down Expand Up @@ -230,7 +235,7 @@ private static void initSoSources(Context context, int flags, @Nullable SoFileLo
//

if ((flags & SOLOADER_ENABLE_EXOPACKAGE) != 0) {
sBackupSoSource = null;
sBackupSoSources = null;
Log.d(TAG, "adding exo package source: " + SO_STORE_NAME_MAIN);
soSources.add(0, new ExoSoSource(context, SO_STORE_NAME_MAIN));
} else {
Expand Down Expand Up @@ -260,11 +265,34 @@ private static void initSoSources(Context context, int flags, @Nullable SoFileLo
}

if ((sFlags & SOLOADER_DISABLE_BACKUP_SOSOURCE) != 0) {
sBackupSoSource = null;
sBackupSoSources = null;
} else {
sBackupSoSource = new ApkSoSource(context, SO_STORE_NAME_MAIN, apkSoSourceFlags);
Log.d(TAG, "adding backup source: " + sBackupSoSource.toString());
soSources.add(0, sBackupSoSource);

final File mainApkDir = new File(context.getApplicationInfo().sourceDir);
ArrayList<UnpackingSoSource> backupSources = new ArrayList<>();
ApkSoSource mainApkSource =
new ApkSoSource(context, mainApkDir, SO_STORE_NAME_MAIN, apkSoSourceFlags);
backupSources.add(mainApkSource);
Log.d(TAG, "adding backup source from : " + mainApkSource.toString());

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
&& context.getApplicationInfo().splitSourceDirs != null) {
Log.d(TAG, "adding backup sources from split apks");
int splitIndex = 0;
for (String splitApkDir : context.getApplicationInfo().splitSourceDirs) {
ApkSoSource splittedApkSource =
new ApkSoSource(
context,
new File(splitApkDir),
SO_STORE_NAME_SPLITTED + (splitIndex++),
apkSoSourceFlags);
Log.d(TAG, "adding backup source: " + splittedApkSource.toString());
backupSources.add(splittedApkSource);
}
}

sBackupSoSources = backupSources.toArray(new UnpackingSoSource[backupSources.size()]);
soSources.addAll(0, backupSources);
}
}
}
Expand Down Expand Up @@ -651,11 +679,17 @@ private static void doLoadLibraryBySoName(
for (int i = 0; result == SoSource.LOAD_RESULT_NOT_FOUND && i < sSoSources.length; ++i) {
SoSource currentSource = sSoSources[i];
result = currentSource.loadLibrary(soName, loadFlags, oldPolicy);
if (result == SoSource.LOAD_RESULT_CORRUPTED_LIB_FILE && sBackupSoSource != null) {
if (result == SoSource.LOAD_RESULT_CORRUPTED_LIB_FILE && sBackupSoSources != null) {
// Let's try from the backup source
Log.d(TAG, "Trying backup SoSource for " + soName);
sBackupSoSource.prepare(soName);
result = sBackupSoSource.loadLibrary(soName, loadFlags, oldPolicy);
for (UnpackingSoSource backupSoSource : sBackupSoSources) {
backupSoSource.prepare(soName);
int resultFromBackup = backupSoSource.loadLibrary(soName, loadFlags, oldPolicy);
if (resultFromBackup == SoSource.LOAD_RESULT_LOADED) {
result = resultFromBackup;
break;
}
}
break;
}
}
Expand Down

0 comments on commit c7980fc

Please sign in to comment.