/* * 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. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ServiceWrappers.h" #include "incfs.h" #include "path.h" namespace android::incremental { using MountId = int; using StorageId = int; using FileId = incfs::FileId; using BlockIndex = incfs::BlockIndex; using RawMetadata = incfs::RawMetadata; using Seconds = std::chrono::seconds; using BootClockTsUs = uint64_t; using IDataLoaderStatusListener = ::android::content::pm::IDataLoaderStatusListener; using DataLoaderStatusListener = ::android::sp; using StorageHealthCheckParams = ::android::os::incremental::StorageHealthCheckParams; using IStorageHealthListener = ::android::os::incremental::IStorageHealthListener; using StorageHealthListener = ::android::sp; using IStorageLoadingProgressListener = ::android::os::incremental::IStorageLoadingProgressListener; using StorageLoadingProgressListener = ::android::sp; class IncrementalService final { public: explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" ~IncrementalService(); #pragma GCC diagnostic pop static constexpr StorageId kInvalidStorageId = -1; static constexpr StorageId kMaxStorageId = std::numeric_limits::max(); static constexpr BootClockTsUs kMaxBootClockTsUs = std::numeric_limits::max(); enum CreateOptions { TemporaryBind = 1, PermanentBind = 2, CreateNew = 4, OpenExisting = 8, Default = TemporaryBind | CreateNew }; enum class BindKind { Temporary = 0, Permanent = 1, }; enum StorageFlags { ReadLogsEnabled = 1, }; static FileId idFromMetadata(std::span metadata); static inline FileId idFromMetadata(std::span metadata) { return idFromMetadata({(const uint8_t*)metadata.data(), metadata.size()}); } void onDump(int fd); void onSystemReady(); StorageId createStorage(std::string_view mountPoint, content::pm::DataLoaderParamsParcel&& dataLoaderParams, CreateOptions options, const DataLoaderStatusListener& statusListener, StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener& healthListener); StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage, CreateOptions options = CreateOptions::Default); StorageId openStorage(std::string_view path); int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind); int unbind(StorageId storage, std::string_view target); void deleteStorage(StorageId storage); void disableReadLogs(StorageId storage); int setStorageParams(StorageId storage, bool enableReadLogs); int makeFile(StorageId storage, std::string_view path, int mode, FileId id, incfs::NewFileParams params, std::span data); int makeDir(StorageId storage, std::string_view path, int mode = 0755); int makeDirs(StorageId storage, std::string_view path, int mode = 0755); int link(StorageId sourceStorageId, std::string_view oldPath, StorageId destStorageId, std::string_view newPath); int unlink(StorageId storage, std::string_view path); int isFileFullyLoaded(StorageId storage, const std::string& path) const; float getLoadingProgress(StorageId storage) const; bool registerLoadingProgressListener(StorageId storage, const StorageLoadingProgressListener& progressListener); bool unregisterLoadingProgressListener(StorageId storage); bool registerStorageHealthListener(StorageId storage, StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener& healthListener); void unregisterStorageHealthListener(StorageId storage); RawMetadata getMetadata(StorageId storage, std::string_view path) const; RawMetadata getMetadata(StorageId storage, FileId node) const; bool startLoading(StorageId storage) const; bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath, std::string_view libDirRelativePath, std::string_view abi, bool extractNativeLibs); bool waitForNativeBinariesExtraction(StorageId storage); class AppOpsListener : public android::BnAppOpsCallback { public: AppOpsListener(IncrementalService& incrementalService, std::string packageName) : incrementalService(incrementalService), packageName(std::move(packageName)) {} void opChanged(int32_t op, const String16& packageName) final; private: IncrementalService& incrementalService; const std::string packageName; }; class IncrementalServiceConnector : public os::incremental::BnIncrementalServiceConnector { public: IncrementalServiceConnector(IncrementalService& incrementalService, int32_t storage) : incrementalService(incrementalService), storage(storage) {} binder::Status setStorageParams(bool enableReadLogs, int32_t* _aidl_return) final; private: IncrementalService& incrementalService; int32_t const storage; }; private: struct IncFsMount; class DataLoaderStub : public content::pm::BnDataLoaderStatusListener { public: DataLoaderStub(IncrementalService& service, MountId id, content::pm::DataLoaderParamsParcel&& params, content::pm::FileSystemControlParcel&& control, const DataLoaderStatusListener* statusListener, StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener* healthListener, std::string&& healthPath); ~DataLoaderStub(); // Cleans up the internal state and invalidates DataLoaderStub. Any subsequent calls will // result in an error. void cleanupResources(); bool requestCreate(); bool requestStart(); bool requestDestroy(); void onDump(int fd); MountId id() const { return mId.load(std::memory_order_relaxed); } const content::pm::DataLoaderParamsParcel& params() const { return mParams; } void setHealthListener(StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener* healthListener); private: binder::Status onStatusChanged(MountId mount, int newStatus) final; binder::Status reportStreamHealth(MountId mount, int newStatus) final; sp getDataLoader(); bool bind(); bool create(); bool start(); bool destroy(); bool setTargetStatus(int status); void setTargetStatusLocked(int status); bool fsmStep(); bool fsmStep(int currentStatus, int targetStatus); void onHealthStatus(StorageHealthListener healthListener, int healthStatus); void updateHealthStatus(bool baseline = false); bool isValid() const { return id() != kInvalidStorageId; } bool isHealthParamsValid() const; const incfs::UniqueControl& initializeHealthControl(); void resetHealthControl(); BootClockTsUs getOldestPendingReadTs(); void registerForPendingReads(); void unregisterFromPendingReads(); IncrementalService& mService; std::mutex mMutex; std::atomic mId = kInvalidStorageId; content::pm::DataLoaderParamsParcel mParams; content::pm::FileSystemControlParcel mControl; DataLoaderStatusListener mStatusListener; StorageHealthListener mHealthListener; std::condition_variable mStatusCondition; int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; TimePoint mTargetStatusTs = {}; std::string mHealthPath; incfs::UniqueControl mHealthControl; struct { TimePoint userTs; BootClockTsUs kernelTsUs; } mHealthBase = {TimePoint::max(), kMaxBootClockTsUs}; StorageHealthCheckParams mHealthCheckParams; int mStreamStatus = content::pm::IDataLoaderStatusListener::STREAM_HEALTHY; std::vector mLastPendingReads; }; using DataLoaderStubPtr = sp; struct IncFsMount { struct Bind { StorageId storage; std::string savedFilename; std::string sourceDir; BindKind kind; }; struct Storage { std::string name; }; using Control = incfs::UniqueControl; using BindMap = std::map; using StorageMap = std::unordered_map; mutable std::mutex lock; const std::string root; Control control; /*const*/ MountId mountId; int32_t flags = StorageFlags::ReadLogsEnabled; StorageMap storages; BindMap bindPoints; DataLoaderStubPtr dataLoaderStub; std::atomic nextStorageDirNo{0}; const IncrementalService& incrementalService; IncFsMount(std::string root, MountId mountId, Control control, const IncrementalService& incrementalService) : root(std::move(root)), control(std::move(control)), mountId(mountId), incrementalService(incrementalService) {} IncFsMount(IncFsMount&&) = delete; IncFsMount& operator=(IncFsMount&&) = delete; ~IncFsMount(); StorageMap::iterator makeStorage(StorageId id); void disableReadLogs() { flags &= ~StorageFlags::ReadLogsEnabled; } int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); } static void cleanupFilesystem(std::string_view root); }; using IfsMountPtr = std::shared_ptr; using MountMap = std::unordered_map; using BindPathMap = std::map; static bool perfLoggingEnabled(); std::unordered_set adoptMountedInstances(); void mountExistingImages(const std::unordered_set& mountedRootNames); bool mountExistingImage(std::string_view root); IfsMountPtr getIfs(StorageId storage) const; const IfsMountPtr& getIfsLocked(StorageId storage) const; int addBindMount(IncFsMount& ifs, StorageId storage, std::string_view storageRoot, std::string&& source, std::string&& target, BindKind kind, std::unique_lock& mainLock); int addBindMountWithMd(IncFsMount& ifs, StorageId storage, std::string&& metadataName, std::string&& source, std::string&& target, BindKind kind, std::unique_lock& mainLock); void addBindMountRecordLocked(IncFsMount& ifs, StorageId storage, std::string&& metadataName, std::string&& source, std::string&& target, BindKind kind); DataLoaderStubPtr prepareDataLoader(IncFsMount& ifs, content::pm::DataLoaderParamsParcel&& params, const DataLoaderStatusListener* statusListener = nullptr, StorageHealthCheckParams&& healthCheckParams = {}, const StorageHealthListener* healthListener = nullptr); void prepareDataLoaderLocked(IncFsMount& ifs, content::pm::DataLoaderParamsParcel&& params, const DataLoaderStatusListener* statusListener = nullptr, StorageHealthCheckParams&& healthCheckParams = {}, const StorageHealthListener* healthListener = nullptr); BindPathMap::const_iterator findStorageLocked(std::string_view path) const; StorageId findStorageId(std::string_view path) const; void deleteStorage(IncFsMount& ifs); void deleteStorageLocked(IncFsMount& ifs, std::unique_lock&& ifsLock); MountMap::iterator getStorageSlotLocked(); std::string normalizePathToStorage(const IncFsMount& incfs, StorageId storage, std::string_view path) const; std::string normalizePathToStorageLocked(const IncFsMount& incfs, IncFsMount::StorageMap::const_iterator storageIt, std::string_view path) const; int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode); binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const; float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const; int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId, std::string_view debugFilePath, std::span data) const; void registerAppOpsCallback(const std::string& packageName); bool unregisterAppOpsCallback(const std::string& packageName); void onAppOpChanged(const std::string& packageName); void runJobProcessing(); void extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile, ZipEntry& entry, const incfs::FileId& libFileId, std::string_view debugLibPath, Clock::time_point scheduledTs); void runCmdLooper(); bool addTimedJob(TimedQueueWrapper& timedQueue, MountId id, Milliseconds after, Job what); bool removeTimedJobs(TimedQueueWrapper& timedQueue, MountId id); bool updateLoadingProgress(int32_t storageId, const StorageLoadingProgressListener& progressListener); private: const std::unique_ptr mVold; const std::unique_ptr mDataLoaderManager; const std::unique_ptr mIncFs; const std::unique_ptr mAppOpsManager; const std::unique_ptr mJni; const std::unique_ptr mLooper; const std::unique_ptr mTimedQueue; const std::unique_ptr mProgressUpdateJobQueue; const std::unique_ptr mFs; const std::string mIncrementalDir; mutable std::mutex mLock; mutable std::mutex mMountOperationLock; MountMap mMounts; BindPathMap mBindsByPath; std::mutex mCallbacksLock; std::map> mCallbackRegistered; std::atomic_bool mSystemReady = false; StorageId mNextId = 0; std::atomic_bool mRunning{true}; std::unordered_map> mJobQueue; MountId mPendingJobsMount = kInvalidStorageId; std::condition_variable mJobCondition; std::mutex mJobMutex; std::thread mJobProcessor; std::thread mCmdLooperThread; }; } // namespace android::incremental