Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include report of unscannable files in MissingLibraryException #108

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.getkeepsafe.relinker;

import java.io.File;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

public class AbiSupportInfo {
private final Set<String> supportedAbis;
private final Map<File, Exception> unScannableFileAndReasons;

public AbiSupportInfo(Set<String> supportedAbis) {
this(supportedAbis, Collections.emptyMap());
}

public AbiSupportInfo(Set<String> supportedAbis, Map<File, Exception> unscannableFilesAndReasons) {
this.supportedAbis = supportedAbis;
this.unScannableFileAndReasons = unscannableFilesAndReasons;
}

public String[] getSupportedAbis() {
String[] arr = new String[supportedAbis.size()];
supportedAbis.toArray(arr);
return arr;
}

public String[] getUnscannableFileNameAndReasons() {
String[] arr = new String[unScannableFileAndReasons.size()];
int i = 0;
for (Map.Entry<File, Exception> entry : unScannableFileAndReasons.entrySet()) {
arr[i++] = entry.getKey().getName() + " => " + entry.getValue();
}
return arr;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.getkeepsafe.relinker;

import java.io.File;
import java.io.IOException;
import java.util.zip.ZipFile;

public class ActualZipFileFactory implements ZipFileFactory {
@Override
public ZipFile create(File file, int openMode) throws IOException {
return new ZipFile(file, openMode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -39,6 +42,16 @@ public class ApkLibraryInstaller implements ReLinker.LibraryInstaller {
private static final int MAX_TRIES = 5;
private static final int COPY_BUFFER_SIZE = 4096;

private final ZipFileFactory zipFileFactory;

ApkLibraryInstaller() {
this(new ActualZipFileFactory());
}

ApkLibraryInstaller(ZipFileFactory zipFileFactory) {
this.zipFileFactory = zipFileFactory;
}

private String[] sourceDirectories(final Context context) {
final ApplicationInfo appInfo = context.getApplicationInfo();

Expand Down Expand Up @@ -74,7 +87,7 @@ private ZipFileInZipEntry findAPKWithLibrary(final Context context,
int tries = 0;
while (tries++ < MAX_TRIES) {
try {
zipFile = new ZipFile(new File(sourceDir), ZipFile.OPEN_READ);
zipFile = zipFileFactory.create(new File(sourceDir), ZipFile.OPEN_READ);
break;
} catch (IOException ignored) {
}
Expand Down Expand Up @@ -116,15 +129,18 @@ private ZipFileInZipEntry findAPKWithLibrary(final Context context,
// 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) {
private AbiSupportInfo 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>();
Map<File, Exception> unscannableFiles = new HashMap<File, Exception>();
for (String sourceDir : sourceDirectories(context)) {
File source = new File(sourceDir);
try {
zipFile = new ZipFile(new File(sourceDir), ZipFile.OPEN_READ);
} catch (IOException ignored) {
zipFile = zipFileFactory.create(source, ZipFile.OPEN_READ);
} catch (IOException e) {
unscannableFiles.put(source, e);
continue;
}

Expand All @@ -138,8 +154,7 @@ private String[] getSupportedABIs(Context context, String mappedLibraryName) {
}
}

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

/**
Expand All @@ -163,14 +178,13 @@ public void installLibrary(final Context context,
if (found == null) {
// Does not exist in any APK. Report exactly what ReLinker is looking for and
// what is actually supported by the APK.
String[] supportedABIs;
AbiSupportInfo 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();
supportedABIs = new AbiSupportInfo(Collections.singleton(e.toString()));
}
throw new MissingLibraryException(mappedLibraryName, abis, supportedABIs);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,28 @@
import java.util.Arrays;

public class MissingLibraryException extends RuntimeException {
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) + ".");
private final String library;
private final String[] wantedABIs;
private final AbiSupportInfo abiSupportInfo;

public MissingLibraryException(String library, String[] wantedABIs, AbiSupportInfo abiSupportInfo) {
this.library = library;
this.wantedABIs = wantedABIs;
this.abiSupportInfo = abiSupportInfo;
}

@Override
public String getMessage() {
StringBuilder sb = new StringBuilder();
sb.append("Could not find '").append(library).append("'. ");
sb.append("Looked for: ").append(Arrays.toString(wantedABIs)).append(", ");
sb.append("but only found: ").append(Arrays.toString(abiSupportInfo.getSupportedAbis())).append(".");

String[] unscannableFileNames = abiSupportInfo.getUnscannableFileNameAndReasons();
if (unscannableFileNames.length != 0) {
sb.append(" Additionally, encountered errors while scanning: ").append(Arrays.toString(unscannableFileNames)).append(".");
}

return sb.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.getkeepsafe.relinker;

import java.io.File;
import java.io.IOException;
import java.util.zip.ZipFile;

public interface ZipFileFactory {
ZipFile create(File file, int openMode) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@ public void throwsMissingLibraryExceptionWhenABIIsMissing() throws IOException {
}
}

@Test
public void throwsMissingLibraryExceptionWhenABIIsMissingAndThereWereUnscannableDirs() 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(new FaultyZipFileFactory());
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: []. Additionally, encountered errors while scanning: [fake.apk => java.io.IOException: Could not create zip file.].", e.getMessage());
}
}

private String fileToString(final File file) throws IOException {
final long size = file.length();
if (size > Integer.MAX_VALUE) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.getkeepsafe.relinker;

import java.io.File;
import java.io.IOException;
import java.util.zip.ZipFile;

public class FaultyZipFileFactory implements ZipFileFactory {
@Override
public ZipFile create(File file, int openMode) throws IOException {
throw new IOException("Could not create zip file.");
}
}