// 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; } }