blob: f3fde2a413e844c14213bf7291e912faedc7c71c [file] [log] [blame]
Songchun Fan3c82a302019-11-29 14:23:45 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -070019#include <android/content/pm/BnDataLoaderStatusListener.h>
Songchun Fan3c82a302019-11-29 14:23:45 -080020#include <android/content/pm/DataLoaderParamsParcel.h>
Alex Buynytskyycca2c112020-05-05 12:48:41 -070021#include <android/content/pm/FileSystemControlParcel.h>
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -070022#include <android/content/pm/IDataLoaderStatusListener.h>
23#include <android/os/incremental/BnIncrementalServiceConnector.h>
24#include <binder/IAppOpsCallback.h>
Songchun Fan3c82a302019-11-29 14:23:45 -080025#include <utils/String16.h>
26#include <utils/StrongPointer.h>
Yurii Zubrytskyida208012020-04-07 15:35:21 -070027#include <ziparchive/zip_archive.h>
Songchun Fan3c82a302019-11-29 14:23:45 -080028
29#include <atomic>
30#include <chrono>
Yurii Zubrytskyida208012020-04-07 15:35:21 -070031#include <condition_variable>
32#include <functional>
Songchun Fan3c82a302019-11-29 14:23:45 -080033#include <limits>
34#include <map>
35#include <mutex>
Songchun Fan9b753082020-02-26 13:08:06 -080036#include <span>
Songchun Fan3c82a302019-11-29 14:23:45 -080037#include <string>
38#include <string_view>
Yurii Zubrytskyida208012020-04-07 15:35:21 -070039#include <thread>
Songchun Fan3c82a302019-11-29 14:23:45 -080040#include <unordered_map>
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -070041#include <unordered_set>
Songchun Fan3c82a302019-11-29 14:23:45 -080042#include <utility>
43#include <vector>
44
45#include "ServiceWrappers.h"
Songchun Fan3c82a302019-11-29 14:23:45 -080046#include "incfs.h"
47#include "path.h"
48
Songchun Fan3c82a302019-11-29 14:23:45 -080049namespace android::incremental {
50
51using MountId = int;
52using StorageId = int;
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -080053using FileId = incfs::FileId;
Songchun Fan3c82a302019-11-29 14:23:45 -080054using BlockIndex = incfs::BlockIndex;
55using RawMetadata = incfs::RawMetadata;
56using Clock = std::chrono::steady_clock;
57using TimePoint = std::chrono::time_point<Clock>;
58using Seconds = std::chrono::seconds;
59
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -070060using IDataLoaderStatusListener = ::android::content::pm::IDataLoaderStatusListener;
61using DataLoaderStatusListener = ::android::sp<IDataLoaderStatusListener>;
Alex Buynytskyy04f73912020-02-10 08:34:18 -080062
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -080063class IncrementalService final {
Songchun Fan3c82a302019-11-29 14:23:45 -080064public:
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -080065 explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir);
Songchun Fan3c82a302019-11-29 14:23:45 -080066
67#pragma GCC diagnostic push
68#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
69 ~IncrementalService();
70#pragma GCC diagnostic pop
71
72 static constexpr StorageId kInvalidStorageId = -1;
73 static constexpr StorageId kMaxStorageId = std::numeric_limits<int>::max();
74
75 enum CreateOptions {
76 TemporaryBind = 1,
77 PermanentBind = 2,
78 CreateNew = 4,
79 OpenExisting = 8,
80
81 Default = TemporaryBind | CreateNew
82 };
83
84 enum class BindKind {
85 Temporary = 0,
86 Permanent = 1,
87 };
88
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -080089 static FileId idFromMetadata(std::span<const uint8_t> metadata);
90 static inline FileId idFromMetadata(std::span<const char> metadata) {
91 return idFromMetadata({(const uint8_t*)metadata.data(), metadata.size()});
92 }
93
Alex Buynytskyy18b07a42020-02-03 20:06:00 -080094 void onDump(int fd);
95
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -070096 void onSystemReady();
Songchun Fan3c82a302019-11-29 14:23:45 -080097
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -070098 StorageId createStorage(std::string_view mountPoint,
99 content::pm::DataLoaderParamsParcel&& dataLoaderParams,
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800100 const DataLoaderStatusListener& dataLoaderStatusListener,
Songchun Fan3c82a302019-11-29 14:23:45 -0800101 CreateOptions options = CreateOptions::Default);
102 StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
103 CreateOptions options = CreateOptions::Default);
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800104 StorageId openStorage(std::string_view path);
Songchun Fan3c82a302019-11-29 14:23:45 -0800105
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800106 int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind);
Songchun Fan3c82a302019-11-29 14:23:45 -0800107 int unbind(StorageId storage, std::string_view target);
108 void deleteStorage(StorageId storage);
109
Alex Buynytskyy5e860ba2020-03-31 15:30:21 -0700110 int setStorageParams(StorageId storage, bool enableReadLogs);
111
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800112 int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
113 incfs::NewFileParams params);
Songchun Fan96100932020-02-03 19:20:58 -0800114 int makeDir(StorageId storage, std::string_view path, int mode = 0755);
115 int makeDirs(StorageId storage, std::string_view path, int mode = 0755);
Songchun Fan3c82a302019-11-29 14:23:45 -0800116
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800117 int link(StorageId sourceStorageId, std::string_view oldPath, StorageId destStorageId,
118 std::string_view newPath);
119 int unlink(StorageId storage, std::string_view path);
Songchun Fan3c82a302019-11-29 14:23:45 -0800120
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800121 bool isRangeLoaded(StorageId storage, FileId file, std::pair<BlockIndex, BlockIndex> range) {
Songchun Fan3c82a302019-11-29 14:23:45 -0800122 return false;
123 }
124
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700125 RawMetadata getMetadata(StorageId storage, std::string_view path) const;
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800126 RawMetadata getMetadata(StorageId storage, FileId node) const;
Songchun Fan3c82a302019-11-29 14:23:45 -0800127
Songchun Fan3c82a302019-11-29 14:23:45 -0800128 bool startLoading(StorageId storage) const;
Yurii Zubrytskyida208012020-04-07 15:35:21 -0700129
Songchun Fan0f8b6fe2020-02-05 17:41:25 -0800130 bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
131 std::string_view libDirRelativePath, std::string_view abi);
Yurii Zubrytskyida208012020-04-07 15:35:21 -0700132 bool waitForNativeBinariesExtraction(StorageId storage);
Alex Buynytskyy96e350b2020-04-02 20:03:47 -0700133
Alex Buynytskyy96e350b2020-04-02 20:03:47 -0700134 class AppOpsListener : public android::BnAppOpsCallback {
135 public:
Yurii Zubrytskyida208012020-04-07 15:35:21 -0700136 AppOpsListener(IncrementalService& incrementalService, std::string packageName)
137 : incrementalService(incrementalService), packageName(std::move(packageName)) {}
Alex Buynytskyyf4156792020-04-07 14:26:55 -0700138 void opChanged(int32_t op, const String16& packageName) final;
Alex Buynytskyy96e350b2020-04-02 20:03:47 -0700139
140 private:
141 IncrementalService& incrementalService;
142 const std::string packageName;
143 };
144
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700145 class IncrementalServiceConnector : public os::incremental::BnIncrementalServiceConnector {
Alex Buynytskyyf4156792020-04-07 14:26:55 -0700146 public:
147 IncrementalServiceConnector(IncrementalService& incrementalService, int32_t storage)
Alex Buynytskyy5f9e3a02020-04-07 21:13:41 -0700148 : incrementalService(incrementalService), storage(storage) {}
Alex Buynytskyyf4156792020-04-07 14:26:55 -0700149 binder::Status setStorageParams(bool enableReadLogs, int32_t* _aidl_return) final;
150
151 private:
152 IncrementalService& incrementalService;
Alex Buynytskyy5f9e3a02020-04-07 21:13:41 -0700153 int32_t const storage;
Alex Buynytskyyf4156792020-04-07 14:26:55 -0700154 };
155
Songchun Fan3c82a302019-11-29 14:23:45 -0800156private:
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700157 struct IncFsMount;
158
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700159 class DataLoaderStub : public content::pm::BnDataLoaderStatusListener {
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700160 public:
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700161 DataLoaderStub(IncrementalService& service, MountId id,
162 content::pm::DataLoaderParamsParcel&& params,
163 content::pm::FileSystemControlParcel&& control,
Alex Buynytskyyd0855a32020-05-07 18:40:51 -0700164 const DataLoaderStatusListener* externalListener, std::string&& healthPath);
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700165 ~DataLoaderStub();
Alex Buynytskyy9a54579a2020-04-17 15:34:47 -0700166 // Cleans up the internal state and invalidates DataLoaderStub. Any subsequent calls will
167 // result in an error.
168 void cleanupResources();
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700169
Alex Buynytskyyab65cb12020-04-17 10:01:47 -0700170 bool requestCreate();
Alex Buynytskyy0b202662020-04-13 09:53:04 -0700171 bool requestStart();
Alex Buynytskyyab65cb12020-04-17 10:01:47 -0700172 bool requestDestroy();
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700173
Alex Buynytskyyab65cb12020-04-17 10:01:47 -0700174 void onDump(int fd);
175
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700176 MountId id() const { return mId; }
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700177 const content::pm::DataLoaderParamsParcel& params() const { return mParams; }
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700178
179 private:
180 binder::Status onStatusChanged(MountId mount, int newStatus) final;
181
Alex Buynytskyyd0855a32020-05-07 18:40:51 -0700182 void registerForPendingReads();
183 void unregisterFromPendingReads();
184 int onPendingReads();
Alex Buynytskyycca2c112020-05-05 12:48:41 -0700185
Alex Buynytskyy9a54579a2020-04-17 15:34:47 -0700186 bool isValid() const { return mId != kInvalidStorageId; }
Alex Buynytskyy0bdbccf2020-04-23 20:36:42 -0700187 sp<content::pm::IDataLoader> getDataLoader();
Alex Buynytskyy9a54579a2020-04-17 15:34:47 -0700188
Alex Buynytskyyea1390f2020-04-22 16:08:50 -0700189 bool bind();
Alex Buynytskyyab65cb12020-04-17 10:01:47 -0700190 bool create();
Alex Buynytskyy0b202662020-04-13 09:53:04 -0700191 bool start();
Alex Buynytskyyab65cb12020-04-17 10:01:47 -0700192 bool destroy();
193
194 bool setTargetStatus(int status);
Alex Buynytskyy7e0a1a82020-04-27 17:06:10 -0700195 void setTargetStatusLocked(int status);
Alex Buynytskyyab65cb12020-04-17 10:01:47 -0700196
197 bool fsmStep();
Alex Buynytskyy0b202662020-04-13 09:53:04 -0700198
Alex Buynytskyyd0855a32020-05-07 18:40:51 -0700199 // Watching for pending reads.
200 void healthStatusOk();
201 // Pending reads detected, waiting for Xsecs to confirm blocked state.
202 void healthStatusReadsPending();
203 // There are reads pending for X+secs, waiting for additional Ysecs to confirm unhealthy
204 // state.
205 void healthStatusBlocked();
206 // There are reads pending for X+Ysecs, marking storage as unhealthy.
207 void healthStatusUnhealthy();
208
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700209 IncrementalService& mService;
Alex Buynytskyyb0ea4482020-05-04 18:39:58 -0700210
211 std::mutex mMutex;
Alex Buynytskyy9a54579a2020-04-17 15:34:47 -0700212 MountId mId = kInvalidStorageId;
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700213 content::pm::DataLoaderParamsParcel mParams;
214 content::pm::FileSystemControlParcel mControl;
Alex Buynytskyy9a54579a2020-04-17 15:34:47 -0700215 DataLoaderStatusListener mListener;
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700216
Alex Buynytskyy0b202662020-04-13 09:53:04 -0700217 std::condition_variable mStatusCondition;
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700218 int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
219 int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
Alex Buynytskyyab65cb12020-04-17 10:01:47 -0700220 TimePoint mTargetStatusTs = {};
Alex Buynytskyycca2c112020-05-05 12:48:41 -0700221
Alex Buynytskyyd0855a32020-05-07 18:40:51 -0700222 std::string mHealthPath;
223 incfs::UniqueControl mHealthControl;
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700224 };
225 using DataLoaderStubPtr = sp<DataLoaderStub>;
226
Songchun Fan3c82a302019-11-29 14:23:45 -0800227 struct IncFsMount {
228 struct Bind {
229 StorageId storage;
230 std::string savedFilename;
231 std::string sourceDir;
232 BindKind kind;
233 };
234
235 struct Storage {
236 std::string name;
Songchun Fan3c82a302019-11-29 14:23:45 -0800237 };
238
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800239 using Control = incfs::UniqueControl;
Songchun Fan3c82a302019-11-29 14:23:45 -0800240
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700241 using BindMap = std::map<std::string, Bind, path::PathLess>;
Songchun Fan3c82a302019-11-29 14:23:45 -0800242 using StorageMap = std::unordered_map<StorageId, Storage>;
243
244 mutable std::mutex lock;
245 const std::string root;
246 Control control;
247 /*const*/ MountId mountId;
248 StorageMap storages;
249 BindMap bindPoints;
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700250 DataLoaderStubPtr dataLoaderStub;
Songchun Fan3c82a302019-11-29 14:23:45 -0800251 std::atomic<int> nextStorageDirNo{0};
Songchun Fan3c82a302019-11-29 14:23:45 -0800252 const IncrementalService& incrementalService;
253
254 IncFsMount(std::string root, MountId mountId, Control control,
255 const IncrementalService& incrementalService)
256 : root(std::move(root)),
257 control(std::move(control)),
258 mountId(mountId),
Alex Buynytskyy1d892162020-04-03 23:00:19 -0700259 incrementalService(incrementalService) {}
Songchun Fan3c82a302019-11-29 14:23:45 -0800260 IncFsMount(IncFsMount&&) = delete;
261 IncFsMount& operator=(IncFsMount&&) = delete;
262 ~IncFsMount();
263
264 StorageMap::iterator makeStorage(StorageId id);
265
266 static void cleanupFilesystem(std::string_view root);
267 };
268
269 using IfsMountPtr = std::shared_ptr<IncFsMount>;
270 using MountMap = std::unordered_map<MountId, IfsMountPtr>;
271 using BindPathMap = std::map<std::string, IncFsMount::BindMap::iterator, path::PathLess>;
272
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700273 static bool perfLoggingEnabled();
274
275 std::unordered_set<std::string_view> adoptMountedInstances();
276 void mountExistingImages(const std::unordered_set<std::string_view>& mountedRootNames);
Yurii Zubrytskyi107ae352020-04-03 13:12:51 -0700277 bool mountExistingImage(std::string_view root);
Songchun Fan3c82a302019-11-29 14:23:45 -0800278
279 IfsMountPtr getIfs(StorageId storage) const;
280 const IfsMountPtr& getIfsLocked(StorageId storage) const;
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800281 int addBindMount(IncFsMount& ifs, StorageId storage, std::string_view storageRoot,
282 std::string&& source, std::string&& target, BindKind kind,
283 std::unique_lock<std::mutex>& mainLock);
Songchun Fan3c82a302019-11-29 14:23:45 -0800284
285 int addBindMountWithMd(IncFsMount& ifs, StorageId storage, std::string&& metadataName,
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800286 std::string&& source, std::string&& target, BindKind kind,
Songchun Fan3c82a302019-11-29 14:23:45 -0800287 std::unique_lock<std::mutex>& mainLock);
288
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700289 void addBindMountRecordLocked(IncFsMount& ifs, StorageId storage, std::string&& metadataName,
290 std::string&& source, std::string&& target, BindKind kind);
291
292 DataLoaderStubPtr prepareDataLoader(IncFsMount& ifs,
293 content::pm::DataLoaderParamsParcel&& params,
Alex Buynytskyy0ea4ff42020-04-09 17:25:42 -0700294 const DataLoaderStatusListener* externalListener = nullptr);
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700295 void prepareDataLoaderLocked(IncFsMount& ifs, content::pm::DataLoaderParamsParcel&& params,
296 const DataLoaderStatusListener* externalListener = nullptr);
Alex Buynytskyybf1c0632020-03-10 15:49:29 -0700297
Songchun Fan3c82a302019-11-29 14:23:45 -0800298 BindPathMap::const_iterator findStorageLocked(std::string_view path) const;
299 StorageId findStorageId(std::string_view path) const;
300
301 void deleteStorage(IncFsMount& ifs);
302 void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock);
303 MountMap::iterator getStorageSlotLocked();
Yurii Zubrytskyiefebb452020-04-22 13:59:06 -0700304 std::string normalizePathToStorage(const IncFsMount& incfs, StorageId storage,
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700305 std::string_view path) const;
Yurii Zubrytskyiefebb452020-04-22 13:59:06 -0700306 std::string normalizePathToStorageLocked(const IncFsMount& incfs,
307 IncFsMount::StorageMap::const_iterator storageIt,
Yurii Zubrytskyi629051fd2020-04-17 23:13:47 -0700308 std::string_view path) const;
Yurii Zubrytskyiefebb452020-04-22 13:59:06 -0700309 int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
Alex Buynytskyy1d892162020-04-03 23:00:19 -0700310 binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
Alex Buynytskyy96e350b2020-04-02 20:03:47 -0700311
312 void registerAppOpsCallback(const std::string& packageName);
Alex Buynytskyy1d892162020-04-03 23:00:19 -0700313 bool unregisterAppOpsCallback(const std::string& packageName);
314 void onAppOpChanged(const std::string& packageName);
Alex Buynytskyy96e350b2020-04-02 20:03:47 -0700315
Yurii Zubrytskyida208012020-04-07 15:35:21 -0700316 void runJobProcessing();
317 void extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile, ZipEntry& entry,
318 const incfs::FileId& libFileId, std::string_view targetLibPath,
319 Clock::time_point scheduledTs);
320
Alex Buynytskyycca2c112020-05-05 12:48:41 -0700321 void runCmdLooper();
322
Yurii Zubrytskyida208012020-04-07 15:35:21 -0700323private:
Yurii Zubrytskyi86321402020-04-09 19:22:30 -0700324 const std::unique_ptr<VoldServiceWrapper> mVold;
325 const std::unique_ptr<DataLoaderManagerWrapper> mDataLoaderManager;
326 const std::unique_ptr<IncFsWrapper> mIncFs;
327 const std::unique_ptr<AppOpsManagerWrapper> mAppOpsManager;
328 const std::unique_ptr<JniWrapper> mJni;
Alex Buynytskyycca2c112020-05-05 12:48:41 -0700329 const std::unique_ptr<LooperWrapper> mLooper;
Songchun Fan3c82a302019-11-29 14:23:45 -0800330 const std::string mIncrementalDir;
331
332 mutable std::mutex mLock;
333 mutable std::mutex mMountOperationLock;
334 MountMap mMounts;
335 BindPathMap mBindsByPath;
336
Alex Buynytskyy96e350b2020-04-02 20:03:47 -0700337 std::mutex mCallbacksLock;
Alex Buynytskyy1d892162020-04-03 23:00:19 -0700338 std::map<std::string, sp<AppOpsListener>> mCallbackRegistered;
Alex Buynytskyy96e350b2020-04-02 20:03:47 -0700339
Songchun Fan3c82a302019-11-29 14:23:45 -0800340 std::atomic_bool mSystemReady = false;
341 StorageId mNextId = 0;
Yurii Zubrytskyida208012020-04-07 15:35:21 -0700342
Alex Buynytskyycca2c112020-05-05 12:48:41 -0700343 std::atomic_bool mRunning{true};
344
Yurii Zubrytskyida208012020-04-07 15:35:21 -0700345 using Job = std::function<void()>;
Yurii Zubrytskyi721ac4d2020-04-13 11:34:32 -0700346 std::unordered_map<MountId, std::vector<Job>> mJobQueue;
347 MountId mPendingJobsMount = kInvalidStorageId;
Yurii Zubrytskyida208012020-04-07 15:35:21 -0700348 std::condition_variable mJobCondition;
349 std::mutex mJobMutex;
350 std::thread mJobProcessor;
Alex Buynytskyycca2c112020-05-05 12:48:41 -0700351
352 std::thread mCmdLooperThread;
Songchun Fan3c82a302019-11-29 14:23:45 -0800353};
354
355} // namespace android::incremental