Skip to content

Commit

Permalink
Fix library loads after split installation
Browse files Browse the repository at this point in the history
Summary:
Whether we need to call `System.load` or `System.loadLibrary` depends on the context.
* `System.load` rejects paths starting with `/mnt/asec`, but [accepts paths starting with `/data`](https://cs.android.com/android/platform/superproject/main/+/main:art/libnativeloader/library_namespaces.cpp;l=78?q=%22%2Fdata:%2F%22). We can never use it to load libs from the base.apk via full path if the app is installed on sd-card
* `System.loadLibrary` considers outdated paths after a split apk is installed. It can never be used to load libraries after `/data/app` subdir is moved
* Similar concerns apply to implicit loading - implicit dependencies are resolved on the linker namespace level and that one is not updated after `/data/app` subdir is moved
* Apps installed to sd-card are killed during split installation even when --dont-kill flag is used. In that scenario we shouldn't need to bother with recovery.

This commit implements loading strategy that satisfies constraints listed above:
* `DirectSplitSoSourceWithImplicitLoads` is an SoSource supporting implicit loads and loading libs by name via `System.loadLibrary`. It works with apps installed to both internal storage and sdcard, provided that non splits were installed without killing the app.
* `DirectSplitSoSourceWithStrictPathControl` is an SoSource that does not support implicit loads and loads libs by full path via `System.load`. It works independently from split installation.
* `DirectSplitSoSourceWithImplicitLoads.recover` returns `DirectSplitSoSourceWithStrictPathControl` pointing othe same split. Base apk change can only be detected when the app is installed to the internal storage, this will not cause problems with sd-cards and will allow apps installed to /data/app to continue working after splits are installed.

Reviewed By: adicatana

Differential Revision: D54950872

fbshipit-source-id: 0100792da730fcce37055f88dc3e3951b9aa7fe0
  • Loading branch information
michalgr authored and facebook-github-bot committed Mar 20, 2024
1 parent 5da8e80 commit 50dc99c
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 21 deletions.
33 changes: 13 additions & 20 deletions java/com/facebook/soloader/DirectSplitSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.facebook.soloader;

import android.annotation.SuppressLint;
import android.os.Build;
import android.os.StrictMode;
import java.io.File;
Expand All @@ -26,11 +25,11 @@
import java.util.Set;
import javax.annotation.Nullable;

public class DirectSplitSoSource extends SoSource {
private final String mSplitName;
public abstract class DirectSplitSoSource extends SoSource {
protected final String mSplitName;

private @Nullable Manifest mManifest = null;
private @Nullable Set<String> mLibs = null;
protected @Nullable Manifest mManifest = null;
protected @Nullable Set<String> mLibs = null;

public DirectSplitSoSource(String splitName) {
mSplitName = splitName;
Expand All @@ -42,6 +41,13 @@ public DirectSplitSoSource(String splitName, Manifest manifest) {
mLibs = new HashSet<String>(manifest.libs);
}

public DirectSplitSoSource(
String splitName, @Nullable Manifest manifest, @Nullable Set<String> libs) {
mSplitName = splitName;
mManifest = manifest;
mLibs = libs;
}

Manifest getManifest() {
if (mManifest == null) {
throw new IllegalStateException("prepare not called");
Expand All @@ -66,21 +72,13 @@ public int loadLibrary(String soName, int loadFlags, StrictMode.ThreadPolicy thr
}

if (mLibs.contains(soName)) {
if ((loadFlags & LOAD_FLAG_ALLOW_IMPLICIT_PROVISION) != 0) {
return LOAD_RESULT_IMPLICITLY_PROVIDED;
}

callSystemLoadLibrary(soName.substring(3, soName.length() - 3));
return LOAD_RESULT_LOADED;
return loadLibraryImpl(soName, loadFlags);
}

return LOAD_RESULT_NOT_FOUND;
}

@SuppressLint("MissingSoLoaderLibrary")
void callSystemLoadLibrary(String name) {
System.loadLibrary(name);
}
protected abstract int loadLibraryImpl(String soName, int loadFlags);

@Override
@Nullable
Expand Down Expand Up @@ -156,9 +154,4 @@ public String[] getSoSourceAbis() {
}
return new String[] {mManifest.arch};
}

@Override
public String getName() {
return "DirectSplitSoSource";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.facebook.soloader;

import android.annotation.SuppressLint;
import android.content.Context;

public class DirectSplitSoSourceWithImplicitLoads extends DirectSplitSoSource
implements RecoverableSoSource {
public DirectSplitSoSourceWithImplicitLoads(String splitName) {
super(splitName);
}

@Override
@SuppressLint("MissingSoLoaderLibrary")
protected int loadLibraryImpl(String soName, int loadFlags) {
if ((loadFlags & LOAD_FLAG_ALLOW_IMPLICIT_PROVISION) != 0) {
return LOAD_RESULT_IMPLICITLY_PROVIDED;
}

System.loadLibrary(soName.substring(3, soName.length() - 3));
return LOAD_RESULT_LOADED;
}

@Override
public String getName() {
return "DirectSplitSoSourceWithImplicitLoads";
}

@Override
public SoSource recover(Context context) {
return new DirectSplitSoSourceWithStrictPathControl(mSplitName, mManifest, mLibs);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.facebook.soloader;

import android.annotation.SuppressLint;
import java.util.Set;
import javax.annotation.Nullable;

public class DirectSplitSoSourceWithStrictPathControl extends DirectSplitSoSource {
public DirectSplitSoSourceWithStrictPathControl(
String splitName, @Nullable Manifest manifest, @Nullable Set<String> libs) {
super(splitName, manifest, libs);
}

@Override
@SuppressLint("MissingSoLoaderLibrary")
protected int loadLibraryImpl(String soName, int loadFlags) {
System.load(getLibraryPath(soName));
return LOAD_RESULT_LOADED;
}

@Override
public String getName() {
return "DirectSplitSoSourceWithStrictPathControl";
}
}
2 changes: 1 addition & 1 deletion java/com/facebook/soloader/SoLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ private static void initSoSources(Context context, int flags) throws IOException
addSystemLoadWrapperSoSource(context, soSources);
} else if (isEnabledBaseApkSplitSource) {
addSystemLibSoSource(soSources);
soSources.add(0, new DirectSplitSoSource("base"));
soSources.add(0, new DirectSplitSoSourceWithImplicitLoads("base"));
} else {
addSystemLibSoSource(soSources);

Expand Down

0 comments on commit 50dc99c

Please sign in to comment.