blob: 76b711a4f0fa866df2cbfa0dbf865a17aa8ecc1f [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.text.TextUtils;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import java.io.IOException;
/**
* A utility class to retrieve references to uncompressed assets insides the apk. A reference is
* defined as tuple (file descriptor, offset, size) enabling direct mapping without deflation.
* This can be used even within the renderer process, since it just dup's the apk's fd.
*/
@JNINamespace("base::android")
public class ApkAssets {
private static final String TAG = "ApkAssets";
// This isn't thread safe, but that's ok because it's only used for debugging.
// Note reference operations are atomic so there is no security issue.
private static String sLastError;
@CalledByNative
public static long[] open(String fileName, String splitName) {
sLastError = null;
AssetFileDescriptor afd = null;
try {
Context context = ContextUtils.getApplicationContext();
if (!TextUtils.isEmpty(splitName) && BundleUtils.isIsolatedSplitInstalled(splitName)) {
context = BundleUtils.createIsolatedSplitContext(context, splitName);
}
AssetManager manager = context.getAssets();
afd = manager.openNonAssetFd(fileName);
return new long[] {
afd.getParcelFileDescriptor().detachFd(), afd.getStartOffset(), afd.getLength()
};
} catch (IOException e) {
sLastError = "Error while loading asset " + fileName + " from " + splitName + ": " + e;
// As a general rule there's no point logging here because the caller should handle
// receiving an fd of -1 sensibly, and the log message is either mirrored later, or
// unwanted (in the case where a missing file is expected), or wanted but will be
// ignored, as most non-fatal logs are.
// It makes sense to log here when the file exists, but is unable to be opened as an fd
// because (for example) it is unexpectedly compressed in an apk. In that case, the log
// message might save someone some time working out what has gone wrong.
// For that reason, we only suppress the message when the exception message doesn't look
// informative (Android framework passes the filename as the message on actual file not
// found, and the empty string also wouldn't give any useful information for debugging).
if (!e.getMessage().equals("") && !e.getMessage().equals(fileName)) {
Log.e(TAG, sLastError);
}
return new long[] {-1, -1, -1};
} finally {
try {
if (afd != null) {
afd.close();
}
} catch (IOException e2) {
Log.e(TAG, "Unable to close AssetFileDescriptor", e2);
}
}
}
@CalledByNative
private static String takeLastErrorString() {
String rv = sLastError;
sLastError = null;
return rv;
}
}