| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * 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. |
| */ |
| #define LOG_TAG "incfs-dataloaderconnector" |
| |
| #include <android-base/logging.h> |
| #include <nativehelper/JNIHelp.h> |
| #include <sys/stat.h> |
| #include <utils/Looper.h> |
| |
| #include <thread> |
| #include <unordered_map> |
| |
| #include "JNIHelpers.h" |
| #include "ManagedDataLoader.h" |
| #include "dataloader.h" |
| #include "incfs.h" |
| |
| namespace { |
| |
| using namespace android::dataloader; |
| using namespace std::literals; |
| |
| using FileId = android::incfs::FileId; |
| using RawMetadata = android::incfs::RawMetadata; |
| using UniqueControl = android::incfs::UniqueControl; |
| |
| struct JniIds { |
| struct { |
| jint DATA_LOADER_CREATED; |
| jint DATA_LOADER_DESTROYED; |
| jint DATA_LOADER_STARTED; |
| jint DATA_LOADER_STOPPED; |
| jint DATA_LOADER_IMAGE_READY; |
| jint DATA_LOADER_IMAGE_NOT_READY; |
| jint DATA_LOADER_UNRECOVERABLE; |
| |
| jint DATA_LOADER_TYPE_NONE; |
| jint DATA_LOADER_TYPE_STREAMING; |
| jint DATA_LOADER_TYPE_INCREMENTAL; |
| |
| jint DATA_LOADER_LOCATION_DATA_APP; |
| jint DATA_LOADER_LOCATION_MEDIA_OBB; |
| jint DATA_LOADER_LOCATION_MEDIA_DATA; |
| } constants; |
| |
| jmethodID parcelFileDescriptorGetFileDescriptor; |
| |
| jfieldID incremental; |
| jfieldID callback; |
| |
| jfieldID controlCmd; |
| jfieldID controlPendingReads; |
| jfieldID controlLog; |
| |
| jfieldID paramsType; |
| jfieldID paramsPackageName; |
| jfieldID paramsClassName; |
| jfieldID paramsArguments; |
| |
| jclass listener; |
| jmethodID listenerOnStatusChanged; |
| |
| jmethodID callbackControlWriteData; |
| |
| jmethodID listGet; |
| jmethodID listSize; |
| |
| jfieldID installationFileLocation; |
| jfieldID installationFileName; |
| jfieldID installationFileLengthBytes; |
| jfieldID installationFileMetadata; |
| |
| JniIds(JNIEnv* env) { |
| listener = (jclass)env->NewGlobalRef( |
| FindClassOrDie(env, "android/content/pm/IDataLoaderStatusListener")); |
| listenerOnStatusChanged = GetMethodIDOrDie(env, listener, "onStatusChanged", "(II)V"); |
| |
| constants.DATA_LOADER_CREATED = |
| GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_CREATED"); |
| constants.DATA_LOADER_DESTROYED = |
| GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_DESTROYED"); |
| constants.DATA_LOADER_STARTED = |
| GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_STARTED"); |
| constants.DATA_LOADER_STOPPED = |
| GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_STOPPED"); |
| constants.DATA_LOADER_IMAGE_READY = |
| GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_IMAGE_READY"); |
| constants.DATA_LOADER_IMAGE_NOT_READY = |
| GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_IMAGE_NOT_READY"); |
| |
| constants.DATA_LOADER_UNRECOVERABLE = |
| GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_UNRECOVERABLE"); |
| |
| CHECK(constants.DATA_LOADER_UNRECOVERABLE == DATA_LOADER_UNRECOVERABLE); |
| |
| auto packageInstaller = (jclass)FindClassOrDie(env, "android/content/pm/PackageInstaller"); |
| |
| constants.DATA_LOADER_TYPE_NONE = |
| GetStaticIntFieldValueOrDie(env, packageInstaller, "DATA_LOADER_TYPE_NONE"); |
| constants.DATA_LOADER_TYPE_STREAMING = |
| GetStaticIntFieldValueOrDie(env, packageInstaller, "DATA_LOADER_TYPE_STREAMING"); |
| constants.DATA_LOADER_TYPE_INCREMENTAL = |
| GetStaticIntFieldValueOrDie(env, packageInstaller, "DATA_LOADER_TYPE_INCREMENTAL"); |
| |
| CHECK(constants.DATA_LOADER_TYPE_NONE == DATA_LOADER_TYPE_NONE); |
| CHECK(constants.DATA_LOADER_TYPE_STREAMING == DATA_LOADER_TYPE_STREAMING); |
| CHECK(constants.DATA_LOADER_TYPE_INCREMENTAL == DATA_LOADER_TYPE_INCREMENTAL); |
| |
| constants.DATA_LOADER_LOCATION_DATA_APP = |
| GetStaticIntFieldValueOrDie(env, packageInstaller, "LOCATION_DATA_APP"); |
| constants.DATA_LOADER_LOCATION_MEDIA_OBB = |
| GetStaticIntFieldValueOrDie(env, packageInstaller, "LOCATION_MEDIA_OBB"); |
| constants.DATA_LOADER_LOCATION_MEDIA_DATA = |
| GetStaticIntFieldValueOrDie(env, packageInstaller, "LOCATION_MEDIA_DATA"); |
| |
| CHECK(constants.DATA_LOADER_LOCATION_DATA_APP == DATA_LOADER_LOCATION_DATA_APP); |
| CHECK(constants.DATA_LOADER_LOCATION_MEDIA_OBB == DATA_LOADER_LOCATION_MEDIA_OBB); |
| CHECK(constants.DATA_LOADER_LOCATION_MEDIA_DATA == DATA_LOADER_LOCATION_MEDIA_DATA); |
| |
| auto parcelFileDescriptor = FindClassOrDie(env, "android/os/ParcelFileDescriptor"); |
| parcelFileDescriptorGetFileDescriptor = |
| GetMethodIDOrDie(env, parcelFileDescriptor, "getFileDescriptor", |
| "()Ljava/io/FileDescriptor;"); |
| |
| auto control = FindClassOrDie(env, "android/content/pm/FileSystemControlParcel"); |
| incremental = |
| GetFieldIDOrDie(env, control, "incremental", |
| "Landroid/os/incremental/IncrementalFileSystemControlParcel;"); |
| callback = |
| GetFieldIDOrDie(env, control, "callback", |
| "Landroid/content/pm/IPackageInstallerSessionFileSystemConnector;"); |
| |
| auto incControl = |
| FindClassOrDie(env, "android/os/incremental/IncrementalFileSystemControlParcel"); |
| controlCmd = GetFieldIDOrDie(env, incControl, "cmd", "Landroid/os/ParcelFileDescriptor;"); |
| controlPendingReads = GetFieldIDOrDie(env, incControl, "pendingReads", |
| "Landroid/os/ParcelFileDescriptor;"); |
| controlLog = GetFieldIDOrDie(env, incControl, "log", "Landroid/os/ParcelFileDescriptor;"); |
| |
| auto params = FindClassOrDie(env, "android/content/pm/DataLoaderParamsParcel"); |
| paramsType = GetFieldIDOrDie(env, params, "type", "I"); |
| paramsPackageName = GetFieldIDOrDie(env, params, "packageName", "Ljava/lang/String;"); |
| paramsClassName = GetFieldIDOrDie(env, params, "className", "Ljava/lang/String;"); |
| paramsArguments = GetFieldIDOrDie(env, params, "arguments", "Ljava/lang/String;"); |
| |
| auto callbackControl = |
| FindClassOrDie(env, |
| "android/content/pm/IPackageInstallerSessionFileSystemConnector"); |
| callbackControlWriteData = |
| GetMethodIDOrDie(env, callbackControl, "writeData", |
| "(Ljava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V"); |
| |
| auto list = (jclass)FindClassOrDie(env, "java/util/List"); |
| listGet = GetMethodIDOrDie(env, list, "get", "(I)Ljava/lang/Object;"); |
| listSize = GetMethodIDOrDie(env, list, "size", "()I"); |
| |
| auto installationFileParcel = |
| (jclass)FindClassOrDie(env, "android/content/pm/InstallationFileParcel"); |
| installationFileLocation = GetFieldIDOrDie(env, installationFileParcel, "location", "I"); |
| installationFileName = |
| GetFieldIDOrDie(env, installationFileParcel, "name", "Ljava/lang/String;"); |
| installationFileLengthBytes = GetFieldIDOrDie(env, installationFileParcel, "size", "J"); |
| installationFileMetadata = GetFieldIDOrDie(env, installationFileParcel, "metadata", "[B"); |
| } |
| }; |
| |
| const JniIds& jniIds(JNIEnv* env) { |
| static const JniIds ids(env); |
| return ids; |
| } |
| |
| bool reportStatusViaCallback(JNIEnv* env, jobject listener, jint storageId, jint status) { |
| if (listener == nullptr) { |
| ALOGE("No listener object to talk to IncrementalService. " |
| "DataLoaderId=%d, " |
| "status=%d", |
| storageId, status); |
| return false; |
| } |
| |
| const auto& jni = jniIds(env); |
| |
| env->CallVoidMethod(listener, jni.listenerOnStatusChanged, storageId, status); |
| ALOGI("Reported status back to IncrementalService. DataLoaderId=%d, " |
| "status=%d", |
| storageId, status); |
| |
| return true; |
| } |
| |
| class DataLoaderConnector; |
| using DataLoaderConnectorPtr = std::shared_ptr<DataLoaderConnector>; |
| using DataLoaderConnectorsMap = std::unordered_map<int, DataLoaderConnectorPtr>; |
| |
| struct Globals { |
| Globals() { |
| managedDataLoaderFactory = new details::DataLoaderFactoryImpl( |
| [](auto jvm, auto) { return std::make_unique<ManagedDataLoader>(jvm); }); |
| } |
| |
| DataLoaderFactory* managedDataLoaderFactory = nullptr; |
| DataLoaderFactory* dataLoaderFactory = nullptr; |
| |
| std::mutex dataLoaderConnectorsLock; |
| // id->DataLoader map |
| DataLoaderConnectorsMap dataLoaderConnectors GUARDED_BY(dataLoaderConnectorsLock); |
| |
| std::atomic_bool stopped; |
| std::thread cmdLooperThread; |
| std::thread logLooperThread; |
| std::vector<ReadInfo> pendingReads; |
| std::vector<ReadInfo> pageReads; |
| }; |
| |
| static Globals& globals() { |
| static Globals globals; |
| return globals; |
| } |
| |
| struct IncFsLooper : public android::Looper { |
| IncFsLooper() : Looper(/*allowNonCallbacks=*/false) {} |
| ~IncFsLooper() {} |
| }; |
| |
| static android::Looper& cmdLooper() { |
| static IncFsLooper cmdLooper; |
| return cmdLooper; |
| } |
| |
| static android::Looper& logLooper() { |
| static IncFsLooper logLooper; |
| return logLooper; |
| } |
| |
| struct DataLoaderParamsPair { |
| static DataLoaderParamsPair createFromManaged(JNIEnv* env, jobject params); |
| |
| const android::dataloader::DataLoaderParams& dataLoaderParams() const { |
| return mDataLoaderParams; |
| } |
| const ::DataLoaderParams& ndkDataLoaderParams() const { return mNDKDataLoaderParams; } |
| |
| private: |
| DataLoaderParamsPair(android::dataloader::DataLoaderParams&& dataLoaderParams); |
| |
| android::dataloader::DataLoaderParams mDataLoaderParams; |
| ::DataLoaderParams mNDKDataLoaderParams; |
| }; |
| |
| static constexpr auto kPendingReadsBufferSize = 256; |
| |
| class DataLoaderConnector : public FilesystemConnector, public StatusListener { |
| public: |
| DataLoaderConnector(JNIEnv* env, jobject service, jint storageId, UniqueControl control, |
| jobject callbackControl, jobject listener) |
| : mService(env->NewGlobalRef(service)), |
| mCallbackControl(env->NewGlobalRef(callbackControl)), |
| mListener(env->NewGlobalRef(listener)), |
| mStorageId(storageId), |
| mControl(std::move(control)) { |
| env->GetJavaVM(&mJvm); |
| CHECK(mJvm != nullptr); |
| } |
| DataLoaderConnector(const DataLoaderConnector&) = delete; |
| DataLoaderConnector(const DataLoaderConnector&&) = delete; |
| virtual ~DataLoaderConnector() { |
| JNIEnv* env = GetOrAttachJNIEnvironment(mJvm); |
| |
| env->DeleteGlobalRef(mService); |
| env->DeleteGlobalRef(mCallbackControl); |
| env->DeleteGlobalRef(mListener); |
| } // to avoid delete-non-virtual-dtor |
| |
| bool onCreate(const DataLoaderParamsPair& params, jobject managedParams) { |
| CHECK(mDataLoader == nullptr); |
| |
| if (auto factory = globals().dataLoaderFactory) { |
| // Let's try the non-default first. |
| mDataLoader = factory->onCreate(factory, ¶ms.ndkDataLoaderParams(), this, this, |
| mJvm, mService, managedParams); |
| if (checkAndClearJavaException(__func__)) { |
| return false; |
| } |
| } |
| |
| if (!mDataLoader) { |
| // Didn't work, let's try the default. |
| auto factory = globals().managedDataLoaderFactory; |
| mDataLoader = factory->onCreate(factory, ¶ms.ndkDataLoaderParams(), this, this, |
| mJvm, mService, managedParams); |
| if (checkAndClearJavaException(__func__)) { |
| return false; |
| } |
| } |
| |
| if (!mDataLoader) { |
| return false; |
| } |
| |
| return true; |
| } |
| bool onStart() { |
| CHECK(mDataLoader); |
| bool result = mDataLoader->onStart(mDataLoader); |
| if (checkAndClearJavaException(__func__)) { |
| result = false; |
| } |
| return result; |
| } |
| void onStop() { |
| CHECK(mDataLoader); |
| mDataLoader->onStop(mDataLoader); |
| checkAndClearJavaException(__func__); |
| } |
| void onDestroy() { |
| CHECK(mDataLoader); |
| mDataLoader->onDestroy(mDataLoader); |
| checkAndClearJavaException(__func__); |
| } |
| |
| bool onPrepareImage(const DataLoaderInstallationFiles& addedFiles) { |
| CHECK(mDataLoader); |
| bool result = |
| mDataLoader->onPrepareImage(mDataLoader, addedFiles.data(), addedFiles.size()); |
| if (checkAndClearJavaException(__func__)) { |
| result = false; |
| } |
| return result; |
| } |
| |
| int onCmdLooperEvent(std::vector<ReadInfo>& pendingReads) { |
| CHECK(mDataLoader); |
| while (true) { |
| pendingReads.resize(kPendingReadsBufferSize); |
| if (android::incfs::waitForPendingReads(mControl, 0ms, &pendingReads) != |
| android::incfs::WaitResult::HaveData || |
| pendingReads.empty()) { |
| return 1; |
| } |
| mDataLoader->onPendingReads(mDataLoader, pendingReads.data(), pendingReads.size()); |
| } |
| return 1; |
| } |
| int onLogLooperEvent(std::vector<ReadInfo>& pageReads) { |
| CHECK(mDataLoader); |
| while (true) { |
| pageReads.clear(); |
| if (android::incfs::waitForPageReads(mControl, 0ms, &pageReads) != |
| android::incfs::WaitResult::HaveData || |
| pageReads.empty()) { |
| return 1; |
| } |
| mDataLoader->onPageReads(mDataLoader, pageReads.data(), pageReads.size()); |
| } |
| return 1; |
| } |
| |
| void writeData(jstring name, jlong offsetBytes, jlong lengthBytes, jobject incomingFd) const { |
| CHECK(mDataLoader); |
| JNIEnv* env = GetOrAttachJNIEnvironment(mJvm); |
| const auto& jni = jniIds(env); |
| return env->CallVoidMethod(mCallbackControl, jni.callbackControlWriteData, name, |
| offsetBytes, lengthBytes, incomingFd); |
| } |
| |
| android::incfs::UniqueFd openForSpecialOps(FileId fid) const { |
| return android::incfs::openForSpecialOps(mControl, fid); |
| } |
| |
| int writeBlocks(Span<const IncFsDataBlock> blocks) const { |
| return android::incfs::writeBlocks(blocks); |
| } |
| |
| int getRawMetadata(FileId fid, char buffer[], size_t* bufferSize) const { |
| return IncFs_GetMetadataById(mControl, fid, buffer, bufferSize); |
| } |
| |
| bool reportStatus(DataLoaderStatus status) { |
| if (status < DATA_LOADER_FIRST_STATUS || DATA_LOADER_LAST_STATUS < status) { |
| ALOGE("Unable to report invalid status. status=%d", status); |
| return false; |
| } |
| JNIEnv* env = GetOrAttachJNIEnvironment(mJvm); |
| return reportStatusViaCallback(env, mListener, mStorageId, status); |
| } |
| |
| bool checkAndClearJavaException(std::string_view method) { |
| JNIEnv* env = GetOrAttachJNIEnvironment(mJvm); |
| |
| if (!env->ExceptionCheck()) { |
| return false; |
| } |
| |
| LOG(ERROR) << "Java exception during DataLoader::" << method; |
| env->ExceptionDescribe(); |
| env->ExceptionClear(); |
| return true; |
| } |
| |
| const UniqueControl& control() const { return mControl; } |
| jobject listener() const { return mListener; } |
| |
| private: |
| JavaVM* mJvm = nullptr; |
| jobject const mService; |
| jobject const mCallbackControl; |
| jobject const mListener; |
| |
| ::DataLoader* mDataLoader = nullptr; |
| const jint mStorageId; |
| const UniqueControl mControl; |
| }; |
| |
| static int onCmdLooperEvent(int fd, int events, void* data) { |
| if (globals().stopped) { |
| // No more listeners. |
| return 0; |
| } |
| auto&& dataLoaderConnector = (DataLoaderConnector*)data; |
| return dataLoaderConnector->onCmdLooperEvent(globals().pendingReads); |
| } |
| |
| static int onLogLooperEvent(int fd, int events, void* data) { |
| if (globals().stopped) { |
| // No more listeners. |
| return 0; |
| } |
| auto&& dataLoaderConnector = (DataLoaderConnector*)data; |
| return dataLoaderConnector->onLogLooperEvent(globals().pageReads); |
| } |
| |
| static int createFdFromManaged(JNIEnv* env, jobject pfd) { |
| if (!pfd) { |
| return -1; |
| } |
| |
| const auto& jni = jniIds(env); |
| auto managedFd = env->CallObjectMethod(pfd, jni.parcelFileDescriptorGetFileDescriptor); |
| return dup(jniGetFDFromFileDescriptor(env, managedFd)); |
| } |
| |
| static jobject createCallbackControl(JNIEnv* env, jobject managedControl) { |
| const auto& jni = jniIds(env); |
| return env->GetObjectField(managedControl, jni.callback); |
| } |
| |
| static UniqueControl createIncFsControlFromManaged(JNIEnv* env, jobject managedControl) { |
| const auto& jni = jniIds(env); |
| auto managedIncControl = env->GetObjectField(managedControl, jni.incremental); |
| if (!managedIncControl) { |
| return UniqueControl(); |
| } |
| auto cmd = createFdFromManaged(env, env->GetObjectField(managedIncControl, jni.controlCmd)); |
| auto pr = createFdFromManaged(env, |
| env->GetObjectField(managedIncControl, jni.controlPendingReads)); |
| auto log = createFdFromManaged(env, env->GetObjectField(managedIncControl, jni.controlLog)); |
| return android::incfs::createControl(cmd, pr, log); |
| } |
| |
| DataLoaderParamsPair::DataLoaderParamsPair(android::dataloader::DataLoaderParams&& dataLoaderParams) |
| : mDataLoaderParams(std::move(dataLoaderParams)) { |
| mNDKDataLoaderParams.type = mDataLoaderParams.type(); |
| mNDKDataLoaderParams.packageName = mDataLoaderParams.packageName().c_str(); |
| mNDKDataLoaderParams.className = mDataLoaderParams.className().c_str(); |
| mNDKDataLoaderParams.arguments = mDataLoaderParams.arguments().c_str(); |
| } |
| |
| DataLoaderParamsPair DataLoaderParamsPair::createFromManaged(JNIEnv* env, jobject managedParams) { |
| const auto& jni = jniIds(env); |
| |
| const DataLoaderType type = (DataLoaderType)env->GetIntField(managedParams, jni.paramsType); |
| |
| std::string packageName( |
| env->GetStringUTFChars((jstring)env->GetObjectField(managedParams, |
| jni.paramsPackageName), |
| nullptr)); |
| std::string className( |
| env->GetStringUTFChars((jstring)env->GetObjectField(managedParams, jni.paramsClassName), |
| nullptr)); |
| std::string arguments( |
| env->GetStringUTFChars((jstring)env->GetObjectField(managedParams, jni.paramsArguments), |
| nullptr)); |
| |
| return DataLoaderParamsPair(android::dataloader::DataLoaderParams(type, std::move(packageName), |
| std::move(className), |
| std::move(arguments))); |
| } |
| |
| static void cmdLooperThread() { |
| constexpr auto kTimeoutMsecs = 60 * 1000; |
| while (!globals().stopped) { |
| cmdLooper().pollAll(kTimeoutMsecs); |
| } |
| } |
| |
| static void logLooperThread() { |
| constexpr auto kTimeoutMsecs = 60 * 1000; |
| while (!globals().stopped) { |
| logLooper().pollAll(kTimeoutMsecs); |
| } |
| } |
| |
| static std::string pathFromFd(int fd) { |
| static constexpr char fdNameFormat[] = "/proc/self/fd/%d"; |
| char fdNameBuffer[NELEM(fdNameFormat) + 11 + 1]; // max int length + '\0' |
| snprintf(fdNameBuffer, NELEM(fdNameBuffer), fdNameFormat, fd); |
| |
| std::string res; |
| // lstat() is supposed to return us exactly the needed buffer size, but |
| // somehow it may also return a smaller (but still >0) st_size field. |
| // That's why let's only use it for the initial estimate. |
| struct stat st = {}; |
| if (::lstat(fdNameBuffer, &st) || st.st_size == 0) { |
| st.st_size = PATH_MAX; |
| } |
| auto bufSize = st.st_size; |
| for (;;) { |
| res.resize(bufSize + 1, '\0'); |
| auto size = ::readlink(fdNameBuffer, &res[0], res.size()); |
| if (size < 0) { |
| return {}; |
| } |
| if (size > bufSize) { |
| // File got renamed in between lstat() and readlink() calls? Retry. |
| bufSize *= 2; |
| continue; |
| } |
| res.resize(size); |
| return res; |
| } |
| } |
| |
| } // namespace |
| |
| void DataLoader_Initialize(struct ::DataLoaderFactory* factory) { |
| CHECK(factory) << "DataLoader factory is invalid."; |
| globals().dataLoaderFactory = factory; |
| } |
| |
| void DataLoader_FilesystemConnector_writeData(DataLoaderFilesystemConnectorPtr ifs, jstring name, |
| jlong offsetBytes, jlong lengthBytes, |
| jobject incomingFd) { |
| auto connector = static_cast<DataLoaderConnector*>(ifs); |
| return connector->writeData(name, offsetBytes, lengthBytes, incomingFd); |
| } |
| |
| int DataLoader_FilesystemConnector_openForSpecialOps(DataLoaderFilesystemConnectorPtr ifs, |
| IncFsFileId fid) { |
| auto connector = static_cast<DataLoaderConnector*>(ifs); |
| return connector->openForSpecialOps(fid).release(); |
| } |
| |
| int DataLoader_FilesystemConnector_writeBlocks(DataLoaderFilesystemConnectorPtr ifs, |
| const IncFsDataBlock blocks[], int blocksCount) { |
| auto connector = static_cast<DataLoaderConnector*>(ifs); |
| return connector->writeBlocks({blocks, static_cast<size_t>(blocksCount)}); |
| } |
| |
| int DataLoader_FilesystemConnector_getRawMetadata(DataLoaderFilesystemConnectorPtr ifs, |
| IncFsFileId fid, char buffer[], |
| size_t* bufferSize) { |
| auto connector = static_cast<DataLoaderConnector*>(ifs); |
| return connector->getRawMetadata(fid, buffer, bufferSize); |
| } |
| |
| int DataLoader_StatusListener_reportStatus(DataLoaderStatusListenerPtr listener, |
| DataLoaderStatus status) { |
| auto connector = static_cast<DataLoaderConnector*>(listener); |
| return connector->reportStatus(status); |
| } |
| |
| bool DataLoaderService_OnCreate(JNIEnv* env, jobject service, jint storageId, jobject control, |
| jobject params, jobject listener) { |
| auto reportDestroyed = [env, storageId](jobject listener) { |
| const auto& jni = jniIds(env); |
| reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_DESTROYED); |
| }; |
| std::unique_ptr<_jobject, decltype(reportDestroyed)> reportDestroyedOnExit(listener, |
| reportDestroyed); |
| |
| auto nativeControl = createIncFsControlFromManaged(env, control); |
| ALOGE("DataLoader::create1 cmd: %d/%s", nativeControl.cmd(), |
| pathFromFd(nativeControl.cmd()).c_str()); |
| ALOGE("DataLoader::create1 log: %d/%s", nativeControl.logs(), |
| pathFromFd(nativeControl.logs()).c_str()); |
| |
| auto nativeParams = DataLoaderParamsPair::createFromManaged(env, params); |
| ALOGE("DataLoader::create2: %d/%s/%s/%s", nativeParams.dataLoaderParams().type(), |
| nativeParams.dataLoaderParams().packageName().c_str(), |
| nativeParams.dataLoaderParams().className().c_str(), |
| nativeParams.dataLoaderParams().arguments().c_str()); |
| |
| auto callbackControl = createCallbackControl(env, control); |
| |
| auto dataLoaderConnector = |
| std::make_unique<DataLoaderConnector>(env, service, storageId, std::move(nativeControl), |
| callbackControl, listener); |
| { |
| std::lock_guard lock{globals().dataLoaderConnectorsLock}; |
| auto [dlIt, dlInserted] = |
| globals().dataLoaderConnectors.try_emplace(storageId, |
| std::move(dataLoaderConnector)); |
| if (!dlInserted) { |
| ALOGE("Failed to insert id(%d)->DataLoader mapping, fd already " |
| "exists", |
| storageId); |
| return false; |
| } |
| if (!dlIt->second->onCreate(nativeParams, params)) { |
| globals().dataLoaderConnectors.erase(dlIt); |
| return false; |
| } |
| } |
| |
| reportDestroyedOnExit.release(); |
| |
| const auto& jni = jniIds(env); |
| reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_CREATED); |
| |
| return true; |
| } |
| |
| bool DataLoaderService_OnStart(JNIEnv* env, jint storageId) { |
| auto reportStopped = [env, storageId](jobject listener) { |
| const auto& jni = jniIds(env); |
| reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_STOPPED); |
| }; |
| std::unique_ptr<_jobject, decltype(reportStopped)> reportStoppedOnExit(nullptr, reportStopped); |
| |
| const UniqueControl* control; |
| jobject listener; |
| DataLoaderConnectorPtr dataLoaderConnector; |
| { |
| std::lock_guard lock{globals().dataLoaderConnectorsLock}; |
| auto dlIt = globals().dataLoaderConnectors.find(storageId); |
| if (dlIt == globals().dataLoaderConnectors.end()) { |
| ALOGE("Failed to start id(%d): not found", storageId); |
| return false; |
| } |
| |
| listener = dlIt->second->listener(); |
| reportStoppedOnExit.reset(listener); |
| |
| dataLoaderConnector = dlIt->second; |
| if (!dataLoaderConnector->onStart()) { |
| ALOGE("Failed to start id(%d): onStart returned false", storageId); |
| return false; |
| } |
| |
| control = &(dataLoaderConnector->control()); |
| |
| // Create loopers while we are under lock. |
| if (control->cmd() >= 0 && !globals().cmdLooperThread.joinable()) { |
| cmdLooper(); |
| globals().cmdLooperThread = std::thread(&cmdLooperThread); |
| } |
| if (control->logs() >= 0 && !globals().logLooperThread.joinable()) { |
| logLooper(); |
| globals().logLooperThread = std::thread(&logLooperThread); |
| } |
| } |
| |
| if (control->cmd() >= 0) { |
| cmdLooper().addFd(control->cmd(), android::Looper::POLL_CALLBACK, |
| android::Looper::EVENT_INPUT, &onCmdLooperEvent, |
| dataLoaderConnector.get()); |
| cmdLooper().wake(); |
| } |
| |
| if (control->logs() >= 0) { |
| logLooper().addFd(control->logs(), android::Looper::POLL_CALLBACK, |
| android::Looper::EVENT_INPUT, &onLogLooperEvent, |
| dataLoaderConnector.get()); |
| logLooper().wake(); |
| } |
| |
| reportStoppedOnExit.release(); |
| |
| const auto& jni = jniIds(env); |
| reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_STARTED); |
| |
| return true; |
| } |
| |
| bool DataLoaderService_OnStop(JNIEnv* env, jint storageId) { |
| auto reportStopped = [env, storageId](jobject listener) { |
| const auto& jni = jniIds(env); |
| reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_STOPPED); |
| }; |
| std::unique_ptr<_jobject, decltype(reportStopped)> reportStoppedOnExit(nullptr, reportStopped); |
| |
| const UniqueControl* control; |
| { |
| std::lock_guard lock{globals().dataLoaderConnectorsLock}; |
| auto dlIt = globals().dataLoaderConnectors.find(storageId); |
| if (dlIt == globals().dataLoaderConnectors.end()) { |
| ALOGE("Failed to stop id(%d): not found", storageId); |
| return false; |
| } |
| control = &(dlIt->second->control()); |
| |
| reportStoppedOnExit.reset(dlIt->second->listener()); |
| } |
| |
| if (control->cmd() >= 0) { |
| cmdLooper().removeFd(control->cmd()); |
| cmdLooper().wake(); |
| } |
| if (control->logs() >= 0) { |
| logLooper().removeFd(control->logs()); |
| logLooper().wake(); |
| } |
| |
| { |
| std::lock_guard lock{globals().dataLoaderConnectorsLock}; |
| auto dlIt = globals().dataLoaderConnectors.find(storageId); |
| if (dlIt == globals().dataLoaderConnectors.end()) { |
| ALOGE("Failed to stop id(%d): not found", storageId); |
| return false; |
| } |
| auto&& dataLoaderConnector = dlIt->second; |
| if (dataLoaderConnector) { |
| dataLoaderConnector->onStop(); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool DataLoaderService_OnDestroy(JNIEnv* env, jint storageId) { |
| DataLoaderService_OnStop(env, storageId); |
| |
| auto reportDestroyed = [env, storageId](jobject listener) { |
| const auto& jni = jniIds(env); |
| reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_DESTROYED); |
| }; |
| std::unique_ptr<_jobject, decltype(reportDestroyed)> reportDestroyedOnExit(nullptr, |
| reportDestroyed); |
| |
| std::lock_guard lock{globals().dataLoaderConnectorsLock}; |
| auto dlIt = globals().dataLoaderConnectors.find(storageId); |
| if (dlIt == globals().dataLoaderConnectors.end()) { |
| ALOGE("Failed to remove id(%d): not found", storageId); |
| return false; |
| } |
| reportDestroyedOnExit.reset(env->NewLocalRef(dlIt->second->listener())); |
| |
| auto&& dataLoaderConnector = dlIt->second; |
| dataLoaderConnector->onDestroy(); |
| globals().dataLoaderConnectors.erase(dlIt); |
| |
| return true; |
| } |
| |
| struct DataLoaderInstallationFilesPair { |
| static DataLoaderInstallationFilesPair createFromManaged(JNIEnv* env, jobjectArray jfiles); |
| |
| using Files = std::vector<android::dataloader::DataLoaderInstallationFile>; |
| const Files& files() const { return mFiles; } |
| |
| using NDKFiles = std::vector<::DataLoaderInstallationFile>; |
| const NDKFiles& ndkFiles() const { return mNDKFiles; } |
| |
| private: |
| DataLoaderInstallationFilesPair(Files&& files); |
| |
| Files mFiles; |
| NDKFiles mNDKFiles; |
| }; |
| |
| DataLoaderInstallationFilesPair DataLoaderInstallationFilesPair::createFromManaged( |
| JNIEnv* env, jobjectArray jfiles) { |
| const auto& jni = jniIds(env); |
| |
| // jfiles is a Java array of InstallationFileParcel |
| auto size = env->GetArrayLength(jfiles); |
| DataLoaderInstallationFilesPair::Files files; |
| files.reserve(size); |
| |
| for (int i = 0; i < size; ++i) { |
| jobject jfile = env->GetObjectArrayElement(jfiles, i); |
| |
| DataLoaderLocation location = |
| (DataLoaderLocation)env->GetIntField(jfile, jni.installationFileLocation); |
| std::string name = |
| env->GetStringUTFChars((jstring)env->GetObjectField(jfile, |
| jni.installationFileName), |
| nullptr); |
| IncFsSize size = env->GetLongField(jfile, jni.installationFileLengthBytes); |
| |
| auto jmetadataBytes = (jbyteArray)env->GetObjectField(jfile, jni.installationFileMetadata); |
| auto metadataElements = env->GetByteArrayElements(jmetadataBytes, nullptr); |
| auto metadataLength = env->GetArrayLength(jmetadataBytes); |
| RawMetadata metadata(metadataElements, metadataElements + metadataLength); |
| env->ReleaseByteArrayElements(jmetadataBytes, metadataElements, 0); |
| |
| files.emplace_back(location, std::move(name), size, std::move(metadata)); |
| } |
| |
| return DataLoaderInstallationFilesPair(std::move(files)); |
| } |
| |
| DataLoaderInstallationFilesPair::DataLoaderInstallationFilesPair(Files&& files) |
| : mFiles(std::move(files)) { |
| const auto size = mFiles.size(); |
| mNDKFiles.resize(size); |
| for (size_t i = 0; i < size; ++i) { |
| const auto& file = mFiles[i]; |
| auto&& ndkFile = mNDKFiles[i]; |
| |
| ndkFile.location = file.location(); |
| ndkFile.name = file.name().c_str(); |
| ndkFile.size = file.size(); |
| ndkFile.metadata.data = file.metadata().data(); |
| ndkFile.metadata.size = file.metadata().size(); |
| } |
| } |
| |
| bool DataLoaderService_OnPrepareImage(JNIEnv* env, jint storageId, jobjectArray addedFiles, |
| jobjectArray removedFiles) { |
| jobject listener; |
| DataLoaderConnectorPtr dataLoaderConnector; |
| { |
| std::lock_guard lock{globals().dataLoaderConnectorsLock}; |
| auto dlIt = globals().dataLoaderConnectors.find(storageId); |
| if (dlIt == globals().dataLoaderConnectors.end()) { |
| ALOGE("Failed to handle onPrepareImage for id(%d): not found", storageId); |
| return false; |
| } |
| listener = dlIt->second->listener(); |
| dataLoaderConnector = dlIt->second; |
| } |
| |
| auto addedFilesPair = DataLoaderInstallationFilesPair::createFromManaged(env, addedFiles); |
| bool result = dataLoaderConnector->onPrepareImage(addedFilesPair.ndkFiles()); |
| |
| const auto& jni = jniIds(env); |
| reportStatusViaCallback(env, listener, storageId, |
| result ? jni.constants.DATA_LOADER_IMAGE_READY |
| : jni.constants.DATA_LOADER_IMAGE_NOT_READY); |
| |
| return result; |
| } |