You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
520 lines
20 KiB
520 lines
20 KiB
/*
|
|
* 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 <android/content/pm/BnDataLoaderStatusListener.h>
|
|
#include <android/content/pm/DataLoaderParamsParcel.h>
|
|
#include <android/content/pm/FileSystemControlParcel.h>
|
|
#include <android/content/pm/IDataLoaderStatusListener.h>
|
|
#include <android/os/incremental/BnIncrementalService.h>
|
|
#include <android/os/incremental/BnIncrementalServiceConnector.h>
|
|
#include <android/os/incremental/BnStorageHealthListener.h>
|
|
#include <android/os/incremental/BnStorageLoadingProgressListener.h>
|
|
#include <android/os/incremental/PerUidReadTimeouts.h>
|
|
#include <android/os/incremental/StorageHealthCheckParams.h>
|
|
#include <binder/IAppOpsCallback.h>
|
|
#include <binder/PersistableBundle.h>
|
|
#include <utils/String16.h>
|
|
#include <utils/StrongPointer.h>
|
|
#include <ziparchive/zip_archive.h>
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <functional>
|
|
#include <limits>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <set>
|
|
#include <span>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#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<IDataLoaderStatusListener>;
|
|
|
|
using StorageHealthCheckParams = ::android::os::incremental::StorageHealthCheckParams;
|
|
using IStorageHealthListener = ::android::os::incremental::IStorageHealthListener;
|
|
using StorageHealthListener = ::android::sp<IStorageHealthListener>;
|
|
using IStorageLoadingProgressListener = ::android::os::incremental::IStorageLoadingProgressListener;
|
|
using StorageLoadingProgressListener = ::android::sp<IStorageLoadingProgressListener>;
|
|
|
|
using PerUidReadTimeouts = ::android::os::incremental::PerUidReadTimeouts;
|
|
|
|
struct IfsState {
|
|
// If mount is fully loaded.
|
|
bool fullyLoaded = false;
|
|
// If read logs are enabled on this mount. Populated only if fullyLoaded == true.
|
|
bool readLogsEnabled = false;
|
|
// If there was an error fetching any of the above.
|
|
bool error = false;
|
|
};
|
|
// Returns true if wants to be called again.
|
|
using IfsStateCallback = std::function<bool(StorageId, IfsState)>;
|
|
|
|
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<int>::max() - 1;
|
|
static constexpr StorageId kAllStoragesId = kMaxStorageId + 1;
|
|
|
|
static constexpr BootClockTsUs kMaxBootClockTsUs = std::numeric_limits<BootClockTsUs>::max();
|
|
|
|
enum CreateOptions {
|
|
TemporaryBind = 1,
|
|
PermanentBind = 2,
|
|
CreateNew = 4,
|
|
OpenExisting = 8,
|
|
|
|
Default = TemporaryBind | CreateNew
|
|
};
|
|
|
|
enum class BindKind {
|
|
Temporary = 0,
|
|
Permanent = 1,
|
|
};
|
|
|
|
enum StorageFlags {
|
|
ReadLogsAllowed = 1 << 0,
|
|
ReadLogsEnabled = 1 << 1,
|
|
ReadLogsRequested = 1 << 2,
|
|
|
|
ReadTimeoutsEnabled = 1 << 3,
|
|
ReadTimeoutsRequested = 1 << 4,
|
|
};
|
|
|
|
struct LoadingProgress {
|
|
ssize_t filledBlocks;
|
|
ssize_t totalBlocks;
|
|
|
|
bool isError() const { return totalBlocks < 0; }
|
|
bool started() const { return totalBlocks > 0; }
|
|
bool fullyLoaded() const { return !isError() && (totalBlocks == filledBlocks); }
|
|
|
|
int blocksRemainingOrError() const {
|
|
return totalBlocks <= 0 ? totalBlocks : totalBlocks - filledBlocks;
|
|
}
|
|
|
|
float getProgress() const {
|
|
return totalBlocks < 0
|
|
? totalBlocks
|
|
: totalBlocks > 0 ? double(filledBlocks) / double(totalBlocks) : 1.f;
|
|
}
|
|
};
|
|
|
|
static FileId idFromMetadata(std::span<const uint8_t> metadata);
|
|
static inline FileId idFromMetadata(std::span<const char> 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);
|
|
StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
|
|
CreateOptions options = CreateOptions::Default);
|
|
StorageId openStorage(std::string_view path);
|
|
|
|
bool startLoading(StorageId storage, content::pm::DataLoaderParamsParcel dataLoaderParams,
|
|
DataLoaderStatusListener statusListener,
|
|
const StorageHealthCheckParams& healthCheckParams,
|
|
StorageHealthListener healthListener,
|
|
std::vector<PerUidReadTimeouts> perUidReadTimeouts);
|
|
void onInstallationComplete(StorageId storage);
|
|
|
|
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 disallowReadLogs(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<const uint8_t> 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);
|
|
|
|
incfs::LoadingState isFileFullyLoaded(StorageId storage, std::string_view filePath) const;
|
|
incfs::LoadingState isMountFullyLoaded(StorageId storage) const;
|
|
|
|
LoadingProgress getLoadingProgress(StorageId storage) const;
|
|
|
|
bool registerLoadingProgressListener(StorageId storage,
|
|
StorageLoadingProgressListener progressListener);
|
|
bool unregisterLoadingProgressListener(StorageId storage);
|
|
|
|
RawMetadata getMetadata(StorageId storage, std::string_view path) const;
|
|
RawMetadata getMetadata(StorageId storage, FileId node) const;
|
|
|
|
bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
|
|
std::string_view libDirRelativePath, std::string_view abi,
|
|
bool extractNativeLibs);
|
|
bool waitForNativeBinariesExtraction(StorageId storage);
|
|
|
|
void getMetrics(int32_t storageId, android::os::PersistableBundle* _aidl_return);
|
|
|
|
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,
|
|
DataLoaderStatusListener&& statusListener,
|
|
const StorageHealthCheckParams& healthCheckParams,
|
|
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; }
|
|
bool isSystemDataLoader() const;
|
|
void setHealthListener(const StorageHealthCheckParams& healthCheckParams,
|
|
StorageHealthListener&& healthListener);
|
|
void getMetrics(android::os::PersistableBundle* _aidl_return);
|
|
|
|
private:
|
|
binder::Status onStatusChanged(MountId mount, int newStatus) final;
|
|
|
|
void setCurrentStatus(int newStatus);
|
|
void compareAndSetCurrentStatus(int expectedStatus, int newStatus);
|
|
|
|
sp<content::pm::IDataLoader> getDataLoader();
|
|
|
|
bool bind();
|
|
bool create();
|
|
bool start();
|
|
bool destroy();
|
|
|
|
bool setTargetStatus(int status);
|
|
void setTargetStatusLocked(int status);
|
|
|
|
bool fsmStep();
|
|
|
|
void onHealthStatus(const 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();
|
|
BootClockTsUs getOldestTsFromLastPendingReads();
|
|
Milliseconds elapsedMsSinceKernelTs(TimePoint now, BootClockTsUs kernelTsUs);
|
|
long elapsedMsSinceOldestPendingRead();
|
|
|
|
// If the stub has to bind to the DL.
|
|
// Returns {} if bind operation is already in progress.
|
|
// Or bind delay in ms.
|
|
std::optional<Milliseconds> needToBind();
|
|
|
|
void registerForPendingReads();
|
|
void unregisterFromPendingReads();
|
|
|
|
IncrementalService& mService;
|
|
|
|
std::mutex mMutex;
|
|
std::atomic<MountId> mId = kInvalidStorageId;
|
|
content::pm::DataLoaderParamsParcel mParams;
|
|
content::pm::FileSystemControlParcel mControl;
|
|
DataLoaderStatusListener mStatusListener;
|
|
StorageHealthListener mHealthListener;
|
|
std::atomic<int> mHealthStatus = IStorageHealthListener::HEALTH_STATUS_OK;
|
|
|
|
std::condition_variable mStatusCondition;
|
|
int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
|
|
TimePoint mCurrentStatusTs = {};
|
|
int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
|
|
TimePoint mTargetStatusTs = {};
|
|
|
|
TimePoint mPreviousBindTs = {};
|
|
Milliseconds mPreviousBindDelay = {};
|
|
|
|
std::string mHealthPath;
|
|
incfs::UniqueControl mHealthControl;
|
|
struct {
|
|
TimePoint userTs;
|
|
BootClockTsUs kernelTsUs;
|
|
} mHealthBase = {TimePoint::max(), kMaxBootClockTsUs};
|
|
StorageHealthCheckParams mHealthCheckParams;
|
|
std::vector<incfs::ReadInfoWithUid> mLastPendingReads;
|
|
};
|
|
using DataLoaderStubPtr = sp<DataLoaderStub>;
|
|
|
|
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<std::string, Bind, path::PathLess>;
|
|
using StorageMap = std::unordered_map<StorageId, Storage>;
|
|
|
|
mutable std::mutex lock;
|
|
const std::string root;
|
|
const std::string metricsKey;
|
|
Control control;
|
|
/*const*/ MountId mountId;
|
|
int32_t flags = StorageFlags::ReadLogsAllowed;
|
|
StorageMap storages;
|
|
BindMap bindPoints;
|
|
DataLoaderStubPtr dataLoaderStub;
|
|
TimePoint startLoadingTs = {};
|
|
std::atomic<int> nextStorageDirNo{0};
|
|
const IncrementalService& incrementalService;
|
|
|
|
IncFsMount(std::string root, std::string metricsKey, MountId mountId, Control control,
|
|
const IncrementalService& incrementalService)
|
|
: root(std::move(root)),
|
|
metricsKey(std::move(metricsKey)),
|
|
control(std::move(control)),
|
|
mountId(mountId),
|
|
incrementalService(incrementalService) {}
|
|
IncFsMount(IncFsMount&&) = delete;
|
|
IncFsMount& operator=(IncFsMount&&) = delete;
|
|
~IncFsMount();
|
|
|
|
StorageMap::iterator makeStorage(StorageId id);
|
|
|
|
void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; }
|
|
int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); }
|
|
|
|
void setReadLogsEnabled(bool value) {
|
|
return setFlag(StorageFlags::ReadLogsEnabled, value);
|
|
}
|
|
int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
|
|
|
|
void setReadLogsRequested(bool value) {
|
|
return setFlag(StorageFlags::ReadLogsRequested, value);
|
|
}
|
|
int32_t readLogsRequested() const { return (flags & StorageFlags::ReadLogsRequested); }
|
|
|
|
void setReadTimeoutsEnabled(bool value) {
|
|
return setFlag(StorageFlags::ReadTimeoutsEnabled, value);
|
|
}
|
|
int32_t readTimeoutsEnabled() const { return (flags & StorageFlags::ReadTimeoutsEnabled); }
|
|
|
|
void setReadTimeoutsRequested(bool value) {
|
|
return setFlag(StorageFlags::ReadTimeoutsRequested, value);
|
|
}
|
|
int32_t readTimeoutsRequested() const {
|
|
return (flags & StorageFlags::ReadTimeoutsRequested);
|
|
}
|
|
|
|
static void cleanupFilesystem(std::string_view root);
|
|
|
|
private:
|
|
void setFlag(StorageFlags flag, bool value);
|
|
};
|
|
|
|
using IfsMountPtr = std::shared_ptr<IncFsMount>;
|
|
using MountMap = std::unordered_map<MountId, IfsMountPtr>;
|
|
using BindPathMap = std::map<std::string, IncFsMount::BindMap::iterator, path::PathLess>;
|
|
|
|
static bool perfLoggingEnabled();
|
|
|
|
void setUidReadTimeouts(StorageId storage,
|
|
std::vector<PerUidReadTimeouts>&& perUidReadTimeouts);
|
|
void clearUidReadTimeouts(StorageId storage);
|
|
bool checkUidReadTimeouts(StorageId storage, IfsState state, Clock::time_point timeLimit);
|
|
|
|
std::unordered_set<std::string_view> adoptMountedInstances();
|
|
void mountExistingImages(const std::unordered_set<std::string_view>& 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<std::mutex>& mainLock);
|
|
|
|
int addBindMountWithMd(IncFsMount& ifs, StorageId storage, std::string&& metadataName,
|
|
std::string&& source, std::string&& target, BindKind kind,
|
|
std::unique_lock<std::mutex>& mainLock);
|
|
|
|
void addBindMountRecordLocked(IncFsMount& ifs, StorageId storage, std::string&& metadataName,
|
|
std::string&& source, std::string&& target, BindKind kind);
|
|
|
|
bool needStartDataLoaderLocked(IncFsMount& ifs);
|
|
|
|
void prepareDataLoaderLocked(IncFsMount& ifs, content::pm::DataLoaderParamsParcel&& params,
|
|
DataLoaderStatusListener&& statusListener = {},
|
|
const StorageHealthCheckParams& healthCheckParams = {},
|
|
StorageHealthListener&& healthListener = {});
|
|
|
|
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<std::mutex>&& 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);
|
|
|
|
int disableReadLogsLocked(IncFsMount& ifs);
|
|
int applyStorageParamsLocked(IncFsMount& ifs);
|
|
|
|
LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
|
|
|
|
int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
|
|
std::string_view debugFilePath, std::span<const uint8_t> 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);
|
|
|
|
void addIfsStateCallback(StorageId storageId, IfsStateCallback callback);
|
|
void removeIfsStateCallbacks(StorageId storageId);
|
|
void processIfsStateCallbacks();
|
|
void processIfsStateCallbacks(StorageId storageId, std::vector<IfsStateCallback>& callbacks);
|
|
|
|
bool updateLoadingProgress(int32_t storageId,
|
|
StorageLoadingProgressListener&& progressListener);
|
|
|
|
void trimReservedSpaceV1(const IncFsMount& ifs);
|
|
int64_t elapsedUsSinceMonoTs(uint64_t monoTsUs);
|
|
|
|
private:
|
|
const std::unique_ptr<VoldServiceWrapper> mVold;
|
|
const std::unique_ptr<DataLoaderManagerWrapper> mDataLoaderManager;
|
|
const std::unique_ptr<IncFsWrapper> mIncFs;
|
|
const std::unique_ptr<AppOpsManagerWrapper> mAppOpsManager;
|
|
const std::unique_ptr<JniWrapper> mJni;
|
|
const std::unique_ptr<LooperWrapper> mLooper;
|
|
const std::unique_ptr<TimedQueueWrapper> mTimedQueue;
|
|
const std::unique_ptr<TimedQueueWrapper> mProgressUpdateJobQueue;
|
|
const std::unique_ptr<FsWrapper> mFs;
|
|
const std::unique_ptr<ClockWrapper> mClock;
|
|
const std::string mIncrementalDir;
|
|
|
|
mutable std::mutex mLock;
|
|
mutable std::mutex mMountOperationLock;
|
|
MountMap mMounts;
|
|
BindPathMap mBindsByPath;
|
|
|
|
std::mutex mCallbacksLock;
|
|
std::unordered_map<std::string, sp<AppOpsListener>> mCallbackRegistered;
|
|
|
|
using IfsStateCallbacks = std::map<StorageId, std::vector<IfsStateCallback>>;
|
|
std::mutex mIfsStateCallbacksLock;
|
|
IfsStateCallbacks mIfsStateCallbacks;
|
|
|
|
std::atomic_bool mSystemReady = false;
|
|
StorageId mNextId = 0;
|
|
|
|
std::atomic_bool mRunning{true};
|
|
|
|
std::unordered_map<MountId, std::vector<Job>> mJobQueue;
|
|
MountId mPendingJobsMount = kInvalidStorageId;
|
|
std::condition_variable mJobCondition;
|
|
std::mutex mJobMutex;
|
|
std::thread mJobProcessor;
|
|
|
|
std::thread mCmdLooperThread;
|
|
};
|
|
|
|
} // namespace android::incremental
|