Skip to content

Commit

Permalink
Better error message if supported ABI's cannot be found inside the AP…
Browse files Browse the repository at this point in the history
…K. (KeepSafe#65)
  • Loading branch information
cmelchior authored and emarc-m committed Jan 6, 2020
1 parent 4a0ad08 commit 0e19739
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

Expand Down Expand Up @@ -106,6 +111,36 @@ private ZipFileInZipEntry findAPKWithLibrary(final Context context,
return null;
}

// Loop over all APK's again in order to detect which ABI's are actually supported.
// This second loop is more expensive than trying to find a specific ABI, so it should
// only be ran when no matching libraries are found. This should keep the overhead of
// the happy path to a minimum.
private String[] getSupportedABIs(Context context, String mappedLibraryName) {
String p = "lib" + File.separatorChar + "([^\\" + File.separatorChar + "]*)" + File.separatorChar + mappedLibraryName;
Pattern pattern = Pattern.compile(p);
ZipFile zipFile;
Set<String> supportedABIs = new HashSet<String>();
for (String sourceDir : sourceDirectories(context)) {
try {
zipFile = new ZipFile(new File(sourceDir), ZipFile.OPEN_READ);
} catch (IOException ignored) {
continue;
}

Enumeration<? extends ZipEntry> elements = zipFile.entries();
while (elements.hasMoreElements()) {
ZipEntry el = elements.nextElement();
Matcher match = pattern.matcher(el.getName());
if (match.matches()) {
supportedABIs.add(match.group(1));
}
}
}

String[] result = new String[supportedABIs.size()];
return supportedABIs.toArray(result);
}

/**
* Attempts to unpack the given library to the given destination. Implements retry logic for
* IO operations to ensure they succeed.
Expand All @@ -124,8 +159,18 @@ public void installLibrary(final Context context,
try {
found = findAPKWithLibrary(context, abis, mappedLibraryName, instance);
if (found == null) {
// Does not exist in any APK
throw new MissingLibraryException(mappedLibraryName);
// Does not exist in any APK. Report exactly what ReLinker is looking for and
// what is actually supported by the APK.
String[] supportedABIs;
try {
supportedABIs = getSupportedABIs(context, mappedLibraryName);
} catch (Exception e) {
// Should never happen as this indicates a bug in ReLinker code, but just to be safe.
// User code should only ever crash with a MissingLibraryException if getting this far.
supportedABIs = new String[1];
supportedABIs[0] = e.toString();
}
throw new MissingLibraryException(mappedLibraryName, abis, supportedABIs);
}

int tries = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
*/
package com.getkeepsafe.relinker;

import java.util.Arrays;

public class MissingLibraryException extends RuntimeException {
public MissingLibraryException(final String library) {
super(library);
public MissingLibraryException(final String library, final String[] wantedABIs, final String[] supportedABIs) {
super("Could not find '" + library + "'. " +
"Looked for: " + Arrays.toString(wantedABIs) + ", " +
"but only found: " + Arrays.toString(supportedABIs) + ".");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -60,6 +61,25 @@ public void installsCorrectly() throws IOException {
assertThat(fileToString(destination), is("works!"));
}

@Test
public void throwsMissingLibraryExceptionWhenABIIsMissing() throws IOException {
final Context context = mock(Context.class);
final ApplicationInfo appInfo = mock(ApplicationInfo.class);
final ReLinkerInstance instance = mock(ReLinkerInstance.class);
final ApkLibraryInstaller installer = new ApkLibraryInstaller();
final File destination = tempFolder.newFile("test");
final String[] abis = new String[] {"armeabi-v7a"}; // For unit test running on a developer machine this is normally x86

when(context.getApplicationInfo()).thenReturn(appInfo);
appInfo.sourceDir = getClass().getResource("/fake.apk").getFile();

try {
installer.installLibrary(context, abis, "libtest.so", destination, instance);
} catch (MissingLibraryException e) {
assertEquals("Could not find 'libtest.so'. Looked for: [armeabi-v7a], but only found: [x86].", e.getMessage());
}
}

private String fileToString(final File file) throws IOException {
final long size = file.length();
if (size > Integer.MAX_VALUE) {
Expand Down

0 comments on commit 0e19739

Please sign in to comment.