blob: efad16f25bf6885349de6101114050be9863b793 [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// 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.
#include "starboard/android/shared/file_internal.h"
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/log.h>
#include <jni.h>
#include <string>
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/android/shared/jni_utils.h"
#include "starboard/common/log.h"
#include "starboard/common/string.h"
#include "starboard/memory.h"
namespace starboard {
namespace android {
namespace shared {
const char* g_app_assets_dir = "/cobalt/assets";
const char* g_app_files_dir = NULL;
const char* g_app_cache_dir = NULL;
const char* g_app_lib_dir = NULL;
namespace {
jobject g_java_asset_manager;
AAssetManager* g_asset_manager;
// Copies the characters from a jstring and returns a newly allocated buffer
// with the result.
const char* DuplicateJavaString(JniEnvExt* env, jstring j_string) {
SB_DCHECK(j_string);
std::string utf_str = env->GetStringStandardUTFOrAbort(j_string);
const char* result = strdup(utf_str.c_str());
return result;
}
} // namespace
void SbFileAndroidInitialize() {
JniEnvExt* env = JniEnvExt::Get();
SB_DCHECK(g_java_asset_manager == NULL);
SB_DCHECK(g_asset_manager == NULL);
ScopedLocalJavaRef<jstring> j_app(env->CallStarboardObjectMethodOrAbort(
"getApplicationContext", "()Landroid/content/Context;"));
g_java_asset_manager =
env->ConvertLocalRefToGlobalRef(env->CallObjectMethodOrAbort(
j_app.Get(), "getAssets", "()Landroid/content/res/AssetManager;"));
g_asset_manager = AAssetManager_fromJava(env, g_java_asset_manager);
SB_DCHECK(g_app_files_dir == NULL);
ScopedLocalJavaRef<jstring> j_string(env->CallStarboardObjectMethodOrAbort(
"getFilesAbsolutePath", "()Ljava/lang/String;"));
g_app_files_dir = DuplicateJavaString(env, j_string.Get());
SB_DLOG(INFO) << "Files dir: " << g_app_files_dir;
SB_DCHECK(g_app_cache_dir == NULL);
j_string.Reset(env->CallStarboardObjectMethodOrAbort("getCacheAbsolutePath",
"()Ljava/lang/String;"));
g_app_cache_dir = DuplicateJavaString(env, j_string.Get());
SB_DLOG(INFO) << "Cache dir: " << g_app_cache_dir;
SB_DCHECK(g_app_lib_dir == NULL);
ScopedLocalJavaRef<jobject> j_app_info(
env->CallObjectMethodOrAbort(j_app.Get(), "getApplicationInfo",
"()Landroid/content/pm/ApplicationInfo;"));
j_string.Reset(
env->GetStringFieldOrAbort(j_app_info.Get(), "nativeLibraryDir"));
g_app_lib_dir = DuplicateJavaString(env, j_string.Get());
SB_DLOG(INFO) << "Lib dir: " << g_app_lib_dir;
}
void SbFileAndroidTeardown() {
JniEnvExt* env = JniEnvExt::Get();
if (g_java_asset_manager) {
env->DeleteGlobalRef(g_java_asset_manager);
g_java_asset_manager = NULL;
g_asset_manager = NULL;
}
if (g_app_files_dir) {
free(const_cast<char*>(g_app_files_dir));
g_app_files_dir = NULL;
}
if (g_app_cache_dir) {
free(const_cast<char*>(g_app_cache_dir));
g_app_cache_dir = NULL;
}
}
bool IsAndroidAssetPath(const char* path) {
size_t prefix_len = strlen(g_app_assets_dir);
return path != NULL && strncmp(g_app_assets_dir, path, prefix_len) == 0 &&
(path[prefix_len] == '/' || path[prefix_len] == '\0');
}
AAsset* OpenAndroidAsset(const char* path) {
if (!IsAndroidAssetPath(path) || g_asset_manager == NULL) {
return NULL;
}
const char* asset_path = path + strlen(g_app_assets_dir) + 1;
return AAssetManager_open(g_asset_manager, asset_path, AASSET_MODE_RANDOM);
}
AAssetDir* OpenAndroidAssetDir(const char* path) {
// Note that the asset directory will not be found if |path| points to a
// specific file instead of a directory. |path| should also not end with '/'.
if (!IsAndroidAssetPath(path) || g_asset_manager == NULL) {
return NULL;
}
const char* asset_path = path + strlen(g_app_assets_dir);
if (*asset_path == '/') {
asset_path++;
}
AAssetDir* asset_directory =
AAssetManager_openDir(g_asset_manager, asset_path);
// AAssetManager_openDir() always returns a pointer to an initialized object,
// even if the given directory does not exist. To determine if the directory
// actually exists, AAssetDir_getNextFileName() is called to check for any
// files in the given directory. However, when iterating the contents of a
// directory, the NDK Asset Manager does not allow subdirectories to be seen.
// So this is not a 100% accurate way to determine if a directory actually
// exists and false negatives will be given for directories that contain
// subdirectories but no files in their immediate directory.
const char* file = AAssetDir_getNextFileName(asset_directory);
if (file == NULL) {
AAssetDir_close(asset_directory);
return NULL;
} else {
AAssetDir_rewind(asset_directory);
return asset_directory;
}
}
} // namespace shared
} // namespace android
} // namespace starboard