1 /*
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 #define LOG_TAG "incfs-dataloaderconnector"
17
18 #include <android-base/logging.h>
19 #include <fcntl.h>
20 #include <nativehelper/JNIPlatformHelp.h>
21 #include <nativehelper/scoped_local_ref.h>
22 #include <nativehelper/scoped_utf_chars.h>
23 #include <sys/stat.h>
24 #include <utils/Looper.h>
25
26 #include <thread>
27 #include <unordered_map>
28
29 #include "JNIHelpers.h"
30 #include "ManagedDataLoader.h"
31 #include "dataloader.h"
32 #include "incfs.h"
33 #include "path.h"
34
35 namespace {
36
37 using namespace std::literals;
38
39 using ReadInfo = android::dataloader::ReadInfo;
40 using ReadInfoWithUid = android::dataloader::ReadInfoWithUid;
41
42 using FileId = android::incfs::FileId;
43 using RawMetadata = android::incfs::RawMetadata;
44 using UniqueControl = android::incfs::UniqueControl;
45
46 struct JniIds {
47 struct {
48 jint DATA_LOADER_CREATED;
49 jint DATA_LOADER_DESTROYED;
50 jint DATA_LOADER_STARTED;
51 jint DATA_LOADER_STOPPED;
52 jint DATA_LOADER_IMAGE_READY;
53 jint DATA_LOADER_IMAGE_NOT_READY;
54 jint DATA_LOADER_UNAVAILABLE;
55 jint DATA_LOADER_UNRECOVERABLE;
56
57 jint DATA_LOADER_TYPE_NONE;
58 jint DATA_LOADER_TYPE_STREAMING;
59 jint DATA_LOADER_TYPE_INCREMENTAL;
60
61 jint DATA_LOADER_LOCATION_DATA_APP;
62 jint DATA_LOADER_LOCATION_MEDIA_OBB;
63 jint DATA_LOADER_LOCATION_MEDIA_DATA;
64 } constants;
65
66 jmethodID parcelFileDescriptorGetFileDescriptor;
67
68 jfieldID incremental;
69 jfieldID service;
70 jfieldID callback;
71
72 jfieldID controlCmd;
73 jfieldID controlPendingReads;
74 jfieldID controlLog;
75 jfieldID controlBlocksWritten;
76
77 jfieldID paramsType;
78 jfieldID paramsPackageName;
79 jfieldID paramsClassName;
80 jfieldID paramsArguments;
81
82 jclass listener;
83 jmethodID listenerOnStatusChanged;
84
85 jmethodID callbackControlWriteData;
86
87 jmethodID listGet;
88 jmethodID listSize;
89
90 jfieldID installationFileLocation;
91 jfieldID installationFileName;
92 jfieldID installationFileLengthBytes;
93 jfieldID installationFileMetadata;
94
95 jmethodID incrementalServiceConnectorSetStorageParams;
96
JniIds__anonb17d6ec20111::JniIds97 JniIds(JNIEnv* env) {
98 listener = (jclass)env->NewGlobalRef(
99 FindClassOrDie(env, "android/content/pm/IDataLoaderStatusListener"));
100 listenerOnStatusChanged = GetMethodIDOrDie(env, listener, "onStatusChanged", "(II)V");
101
102 constants.DATA_LOADER_CREATED =
103 GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_CREATED");
104 constants.DATA_LOADER_DESTROYED =
105 GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_DESTROYED");
106 constants.DATA_LOADER_STARTED =
107 GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_STARTED");
108 constants.DATA_LOADER_STOPPED =
109 GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_STOPPED");
110 constants.DATA_LOADER_IMAGE_READY =
111 GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_IMAGE_READY");
112 constants.DATA_LOADER_IMAGE_NOT_READY =
113 GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_IMAGE_NOT_READY");
114
115 constants.DATA_LOADER_UNAVAILABLE =
116 GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_UNAVAILABLE");
117 constants.DATA_LOADER_UNRECOVERABLE =
118 GetStaticIntFieldValueOrDie(env, listener, "DATA_LOADER_UNRECOVERABLE");
119
120 auto packageInstaller = (jclass)FindClassOrDie(env, "android/content/pm/PackageInstaller");
121
122 constants.DATA_LOADER_TYPE_NONE =
123 GetStaticIntFieldValueOrDie(env, packageInstaller, "DATA_LOADER_TYPE_NONE");
124 constants.DATA_LOADER_TYPE_STREAMING =
125 GetStaticIntFieldValueOrDie(env, packageInstaller, "DATA_LOADER_TYPE_STREAMING");
126 constants.DATA_LOADER_TYPE_INCREMENTAL =
127 GetStaticIntFieldValueOrDie(env, packageInstaller, "DATA_LOADER_TYPE_INCREMENTAL");
128
129 CHECK(constants.DATA_LOADER_TYPE_NONE == DATA_LOADER_TYPE_NONE);
130 CHECK(constants.DATA_LOADER_TYPE_STREAMING == DATA_LOADER_TYPE_STREAMING);
131 CHECK(constants.DATA_LOADER_TYPE_INCREMENTAL == DATA_LOADER_TYPE_INCREMENTAL);
132
133 constants.DATA_LOADER_LOCATION_DATA_APP =
134 GetStaticIntFieldValueOrDie(env, packageInstaller, "LOCATION_DATA_APP");
135 constants.DATA_LOADER_LOCATION_MEDIA_OBB =
136 GetStaticIntFieldValueOrDie(env, packageInstaller, "LOCATION_MEDIA_OBB");
137 constants.DATA_LOADER_LOCATION_MEDIA_DATA =
138 GetStaticIntFieldValueOrDie(env, packageInstaller, "LOCATION_MEDIA_DATA");
139
140 CHECK(constants.DATA_LOADER_LOCATION_DATA_APP == DATA_LOADER_LOCATION_DATA_APP);
141 CHECK(constants.DATA_LOADER_LOCATION_MEDIA_OBB == DATA_LOADER_LOCATION_MEDIA_OBB);
142 CHECK(constants.DATA_LOADER_LOCATION_MEDIA_DATA == DATA_LOADER_LOCATION_MEDIA_DATA);
143
144 auto parcelFileDescriptor = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
145 parcelFileDescriptorGetFileDescriptor =
146 GetMethodIDOrDie(env, parcelFileDescriptor, "getFileDescriptor",
147 "()Ljava/io/FileDescriptor;");
148
149 auto control = FindClassOrDie(env, "android/content/pm/FileSystemControlParcel");
150 incremental =
151 GetFieldIDOrDie(env, control, "incremental",
152 "Landroid/os/incremental/IncrementalFileSystemControlParcel;");
153 service = GetFieldIDOrDie(env, control, "service",
154 "Landroid/os/incremental/IIncrementalServiceConnector;");
155 callback =
156 GetFieldIDOrDie(env, control, "callback",
157 "Landroid/content/pm/IPackageInstallerSessionFileSystemConnector;");
158
159 auto incControl =
160 FindClassOrDie(env, "android/os/incremental/IncrementalFileSystemControlParcel");
161 controlCmd = GetFieldIDOrDie(env, incControl, "cmd", "Landroid/os/ParcelFileDescriptor;");
162 controlPendingReads = GetFieldIDOrDie(env, incControl, "pendingReads",
163 "Landroid/os/ParcelFileDescriptor;");
164 controlLog = GetFieldIDOrDie(env, incControl, "log", "Landroid/os/ParcelFileDescriptor;");
165 controlBlocksWritten = GetFieldIDOrDie(env, incControl, "blocksWritten",
166 "Landroid/os/ParcelFileDescriptor;");
167
168 auto params = FindClassOrDie(env, "android/content/pm/DataLoaderParamsParcel");
169 paramsType = GetFieldIDOrDie(env, params, "type", "I");
170 paramsPackageName = GetFieldIDOrDie(env, params, "packageName", "Ljava/lang/String;");
171 paramsClassName = GetFieldIDOrDie(env, params, "className", "Ljava/lang/String;");
172 paramsArguments = GetFieldIDOrDie(env, params, "arguments", "Ljava/lang/String;");
173
174 auto callbackControl =
175 FindClassOrDie(env,
176 "android/content/pm/IPackageInstallerSessionFileSystemConnector");
177 callbackControlWriteData =
178 GetMethodIDOrDie(env, callbackControl, "writeData",
179 "(Ljava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V");
180
181 auto list = (jclass)FindClassOrDie(env, "java/util/List");
182 listGet = GetMethodIDOrDie(env, list, "get", "(I)Ljava/lang/Object;");
183 listSize = GetMethodIDOrDie(env, list, "size", "()I");
184
185 auto installationFileParcel =
186 (jclass)FindClassOrDie(env, "android/content/pm/InstallationFileParcel");
187 installationFileLocation = GetFieldIDOrDie(env, installationFileParcel, "location", "I");
188 installationFileName =
189 GetFieldIDOrDie(env, installationFileParcel, "name", "Ljava/lang/String;");
190 installationFileLengthBytes = GetFieldIDOrDie(env, installationFileParcel, "size", "J");
191 installationFileMetadata = GetFieldIDOrDie(env, installationFileParcel, "metadata", "[B");
192
193 auto incrementalServiceConnector =
194 FindClassOrDie(env, "android/os/incremental/IIncrementalServiceConnector");
195 incrementalServiceConnectorSetStorageParams =
196 GetMethodIDOrDie(env, incrementalServiceConnector, "setStorageParams", "(Z)I");
197 }
198 };
199
getJavaVM(JNIEnv * env)200 JavaVM* getJavaVM(JNIEnv* env) {
201 CHECK(env);
202 JavaVM* jvm = nullptr;
203 env->GetJavaVM(&jvm);
204 CHECK(jvm);
205 return jvm;
206 }
207
jniIds(JNIEnv * env)208 const JniIds& jniIds(JNIEnv* env) {
209 static const JniIds ids(env);
210 return ids;
211 }
212
checkAndClearJavaException(JNIEnv * env,std::string_view method)213 bool checkAndClearJavaException(JNIEnv* env, std::string_view method) {
214 if (!env->ExceptionCheck()) {
215 return false;
216 }
217
218 LOG(ERROR) << "Java exception during DataLoader::" << method;
219 env->ExceptionDescribe();
220 env->ExceptionClear();
221 return true;
222 }
223
reportStatusViaCallback(JNIEnv * env,jobject listener,jint storageId,jint status)224 bool reportStatusViaCallback(JNIEnv* env, jobject listener, jint storageId, jint status) {
225 if (listener == nullptr) {
226 ALOGE("No listener object to talk to IncrementalService. "
227 "DataLoaderId=%d, "
228 "status=%d",
229 storageId, status);
230 return false;
231 }
232
233 const auto& jni = jniIds(env);
234
235 env->CallVoidMethod(listener, jni.listenerOnStatusChanged, storageId, status);
236 ALOGI("Reported status back to IncrementalService. DataLoaderId=%d, "
237 "status=%d",
238 storageId, status);
239
240 return true;
241 }
242
243 class DataLoaderConnector;
244 using DataLoaderConnectorPtr = std::shared_ptr<DataLoaderConnector>;
245 using DataLoaderConnectorsMap = std::unordered_map<int, DataLoaderConnectorPtr>;
246
247 struct Globals {
Globals__anonb17d6ec20111::Globals248 Globals() { managedDataLoaderFactory = new android::dataloader::ManagedDataLoaderFactory(); }
249
250 DataLoaderFactory* managedDataLoaderFactory = nullptr;
251 DataLoaderFactory* legacyDataLoaderFactory = nullptr;
252 DataLoaderFactory* dataLoaderFactory = nullptr;
253
254 std::mutex dataLoaderConnectorsLock;
255 // id->DataLoader map
256 DataLoaderConnectorsMap dataLoaderConnectors GUARDED_BY(dataLoaderConnectorsLock);
257
258 std::atomic_bool stopped;
259 std::thread pendingReadsLooperThread;
260 std::thread logLooperThread;
261 std::vector<ReadInfo> pendingReads;
262 std::vector<ReadInfo> pageReads;
263 std::vector<ReadInfoWithUid> pendingReadsWithUid;
264 std::vector<ReadInfoWithUid> pageReadsWithUid;
265 };
266
globals()267 static Globals& globals() {
268 static Globals globals;
269 return globals;
270 }
271
272 struct IncFsLooper : public android::Looper {
IncFsLooper__anonb17d6ec20111::IncFsLooper273 IncFsLooper() : Looper(/*allowNonCallbacks=*/false) {}
~IncFsLooper__anonb17d6ec20111::IncFsLooper274 ~IncFsLooper() {}
275 };
276
pendingReadsLooper()277 static android::Looper& pendingReadsLooper() {
278 static IncFsLooper pendingReadsLooper;
279 return pendingReadsLooper;
280 }
281
logLooper()282 static android::Looper& logLooper() {
283 static IncFsLooper logLooper;
284 return logLooper;
285 }
286
287 struct DataLoaderParamsPair {
288 static DataLoaderParamsPair createFromManaged(JNIEnv* env, jobject params);
289
dataLoaderParams__anonb17d6ec20111::DataLoaderParamsPair290 const android::dataloader::DataLoaderParams& dataLoaderParams() const {
291 return mDataLoaderParams;
292 }
ndkDataLoaderParams__anonb17d6ec20111::DataLoaderParamsPair293 const ::DataLoaderParams& ndkDataLoaderParams() const { return mNDKDataLoaderParams; }
294
295 private:
296 DataLoaderParamsPair(android::dataloader::DataLoaderParams&& dataLoaderParams);
297
298 android::dataloader::DataLoaderParams mDataLoaderParams;
299 ::DataLoaderParams mNDKDataLoaderParams;
300 };
301
302 static constexpr auto kPendingReadsBufferSize = 256;
303
304 class DataLoaderConnector : public android::dataloader::FilesystemConnector,
305 public android::dataloader::StatusListener {
306 public:
DataLoaderConnector(JNIEnv * env,jobject service,jint storageId,UniqueControl control,jobject serviceConnector,jobject callbackControl,jobject listener)307 DataLoaderConnector(JNIEnv* env, jobject service, jint storageId, UniqueControl control,
308 jobject serviceConnector, jobject callbackControl, jobject listener)
309 : mJvm(getJavaVM(env)),
310 mService(env->NewGlobalRef(service)),
311 mServiceConnector(env->NewGlobalRef(serviceConnector)),
312 mCallbackControl(env->NewGlobalRef(callbackControl)),
313 mListener(env->NewGlobalRef(listener)),
314 mStorageId(storageId),
315 mControl(std::move(control)) {
316 CHECK(mJvm != nullptr);
317 }
318 DataLoaderConnector(const DataLoaderConnector&) = delete;
319 DataLoaderConnector(const DataLoaderConnector&&) = delete;
~DataLoaderConnector()320 virtual ~DataLoaderConnector() {
321 if (mDataLoader && mDataLoader->onDestroy) {
322 mDataLoader->onDestroy(mDataLoader);
323 checkAndClearJavaException(__func__);
324 }
325 mDataLoader = nullptr;
326
327 JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
328
329 const auto& jni = jniIds(env);
330 reportStatusViaCallback(env, mListener, mStorageId, jni.constants.DATA_LOADER_DESTROYED);
331
332 env->DeleteGlobalRef(mService);
333 env->DeleteGlobalRef(mServiceConnector);
334 env->DeleteGlobalRef(mCallbackControl);
335 env->DeleteGlobalRef(mListener);
336 } // to avoid delete-non-virtual-dtor
337
tryFactory(DataLoaderFactory * factory,bool withFeatures,const DataLoaderParamsPair & params,jobject managedParams)338 bool tryFactory(DataLoaderFactory* factory, bool withFeatures,
339 const DataLoaderParamsPair& params, jobject managedParams) {
340 if (!factory) {
341 return true;
342 }
343
344 // Let's try the non-default first.
345 mDataLoader = factory->onCreate(factory, ¶ms.ndkDataLoaderParams(), this, this, mJvm,
346 mService, managedParams);
347 if (checkAndClearJavaException(__func__)) {
348 return false;
349 }
350 if (!mDataLoader) {
351 return true;
352 }
353
354 mDataLoaderFeatures = withFeatures && mDataLoader->getFeatures
355 ? mDataLoader->getFeatures(mDataLoader)
356 : DATA_LOADER_FEATURE_NONE;
357 if (mDataLoaderFeatures & DATA_LOADER_FEATURE_UID) {
358 ALOGE("DataLoader supports UID");
359 CHECK(mDataLoader->onPageReadsWithUid);
360 CHECK(mDataLoader->onPendingReadsWithUid);
361 }
362 return true;
363 }
364
onCreate(const DataLoaderParamsPair & params,jobject managedParams)365 bool onCreate(const DataLoaderParamsPair& params, jobject managedParams) {
366 CHECK(mDataLoader == nullptr);
367
368 if (!mDataLoader &&
369 !tryFactory(globals().dataLoaderFactory, /*withFeatures=*/true, params,
370 managedParams)) {
371 return false;
372 }
373 if (!mDataLoader &&
374 !tryFactory(globals().legacyDataLoaderFactory, /*withFeatures=*/false, params,
375 managedParams)) {
376 return false;
377 }
378 if (!mDataLoader &&
379 !tryFactory(globals().managedDataLoaderFactory, /*withFeatures=*/false, params,
380 managedParams)) {
381 return false;
382 }
383 if (!mDataLoader) {
384 return false;
385 }
386
387 return true;
388 }
onStart()389 bool onStart() {
390 CHECK(mDataLoader);
391 bool result = !mDataLoader->onStart || mDataLoader->onStart(mDataLoader);
392 if (checkAndClearJavaException(__func__)) {
393 result = false;
394 }
395 mRunning = result;
396 return result;
397 }
onStop()398 void onStop() {
399 CHECK(mDataLoader);
400
401 // Stopping both loopers and waiting for them to exit - we should be able to acquire/release
402 // both mutexes.
403 mRunning = false;
404 std::lock_guard{mPendingReadsLooperBusy}; // NOLINT
405 std::lock_guard{mLogLooperBusy}; // NOLINT
406
407 if (mDataLoader->onStop) {
408 mDataLoader->onStop(mDataLoader);
409 }
410 checkAndClearJavaException(__func__);
411 }
412
onPrepareImage(const android::dataloader::DataLoaderInstallationFiles & addedFiles)413 bool onPrepareImage(const android::dataloader::DataLoaderInstallationFiles& addedFiles) {
414 CHECK(mDataLoader);
415 bool result = !mDataLoader->onPrepareImage ||
416 mDataLoader->onPrepareImage(mDataLoader, addedFiles.data(), addedFiles.size());
417 return result;
418 }
419
420 template <class ReadInfoType>
onPendingReadsLooperEvent(std::vector<ReadInfoType> & pendingReads)421 int onPendingReadsLooperEvent(std::vector<ReadInfoType>& pendingReads) {
422 CHECK(mDataLoader);
423 std::lock_guard lock{mPendingReadsLooperBusy};
424 while (mRunning.load(std::memory_order_relaxed)) {
425 pendingReads.resize(kPendingReadsBufferSize);
426 if (android::incfs::waitForPendingReads(mControl, 0ms, &pendingReads) !=
427 android::incfs::WaitResult::HaveData ||
428 pendingReads.empty()) {
429 return 1;
430 }
431 if constexpr (std::is_same_v<ReadInfoType, ReadInfo>) {
432 if (mDataLoader->onPendingReads) {
433 mDataLoader->onPendingReads(mDataLoader, pendingReads.data(),
434 pendingReads.size());
435 }
436 } else {
437 if (mDataLoader->onPendingReadsWithUid) {
438 mDataLoader->onPendingReadsWithUid(mDataLoader, pendingReads.data(),
439 pendingReads.size());
440 }
441 }
442 }
443 return 1;
444 }
445
446 template <class ReadInfoType>
onLogLooperEvent(std::vector<ReadInfoType> & pageReads)447 int onLogLooperEvent(std::vector<ReadInfoType>& pageReads) {
448 CHECK(mDataLoader);
449 std::lock_guard lock{mLogLooperBusy};
450 while (mRunning.load(std::memory_order_relaxed)) {
451 pageReads.clear();
452 if (android::incfs::waitForPageReads(mControl, 0ms, &pageReads) !=
453 android::incfs::WaitResult::HaveData ||
454 pageReads.empty()) {
455 return 1;
456 }
457 if constexpr (std::is_same_v<ReadInfoType, ReadInfo>) {
458 if (mDataLoader->onPageReads) {
459 mDataLoader->onPageReads(mDataLoader, pageReads.data(), pageReads.size());
460 }
461 } else {
462 if (mDataLoader->onPageReadsWithUid) {
463 mDataLoader->onPageReadsWithUid(mDataLoader, pageReads.data(),
464 pageReads.size());
465 }
466 }
467 }
468 return 1;
469 }
470
onPendingReadsLooperEvent(std::vector<ReadInfo> & pendingReads,std::vector<ReadInfoWithUid> & pendingReadsWithUid)471 int onPendingReadsLooperEvent(std::vector<ReadInfo>& pendingReads,
472 std::vector<ReadInfoWithUid>& pendingReadsWithUid) {
473 CHECK(mDataLoader);
474 if (mDataLoaderFeatures & DATA_LOADER_FEATURE_UID) {
475 return this->onPendingReadsLooperEvent(pendingReadsWithUid);
476 } else {
477 return this->onPendingReadsLooperEvent(pendingReads);
478 }
479 }
480
onLogLooperEvent(std::vector<ReadInfo> & pageReads,std::vector<ReadInfoWithUid> & pageReadsWithUid)481 int onLogLooperEvent(std::vector<ReadInfo>& pageReads,
482 std::vector<ReadInfoWithUid>& pageReadsWithUid) {
483 CHECK(mDataLoader);
484 if (mDataLoaderFeatures & DATA_LOADER_FEATURE_UID) {
485 return this->onLogLooperEvent(pageReadsWithUid);
486 } else {
487 return this->onLogLooperEvent(pageReads);
488 }
489 }
490
writeData(jstring name,jlong offsetBytes,jlong lengthBytes,jobject incomingFd) const491 void writeData(jstring name, jlong offsetBytes, jlong lengthBytes, jobject incomingFd) const {
492 CHECK(mCallbackControl);
493 JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
494 const auto& jni = jniIds(env);
495 env->CallVoidMethod(mCallbackControl, jni.callbackControlWriteData, name, offsetBytes,
496 lengthBytes, incomingFd);
497 }
498
openForSpecialOps(FileId fid) const499 android::incfs::UniqueFd openForSpecialOps(FileId fid) const {
500 return android::incfs::openForSpecialOps(mControl, fid);
501 }
502
writeBlocks(android::dataloader::Span<const IncFsDataBlock> blocks) const503 int writeBlocks(android::dataloader::Span<const IncFsDataBlock> blocks) const {
504 return android::incfs::writeBlocks(blocks);
505 }
506
getRawMetadata(FileId fid,char buffer[],size_t * bufferSize) const507 int getRawMetadata(FileId fid, char buffer[], size_t* bufferSize) const {
508 return IncFs_GetMetadataById(mControl, fid, buffer, bufferSize);
509 }
510
setParams(DataLoaderFilesystemParams params) const511 bool setParams(DataLoaderFilesystemParams params) const {
512 CHECK(mServiceConnector);
513 JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
514 const auto& jni = jniIds(env);
515 int result = env->CallIntMethod(mServiceConnector,
516 jni.incrementalServiceConnectorSetStorageParams,
517 params.readLogsEnabled);
518 if (result != 0) {
519 LOG(ERROR) << "setStorageParams failed with error: " << result;
520 }
521 if (checkAndClearJavaException(__func__)) {
522 return false;
523 }
524 return (result == 0);
525 }
526
reportStatus(DataLoaderStatus status)527 bool reportStatus(DataLoaderStatus status) {
528 JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
529 const auto& jni = jniIds(env);
530
531 jint osStatus;
532 switch (status) {
533 case DATA_LOADER_UNAVAILABLE:
534 osStatus = jni.constants.DATA_LOADER_UNAVAILABLE;
535 break;
536 case DATA_LOADER_UNRECOVERABLE:
537 osStatus = jni.constants.DATA_LOADER_UNRECOVERABLE;
538 break;
539 default: {
540 ALOGE("Unable to report invalid status. status=%d", status);
541 return false;
542 }
543 }
544 return reportStatusViaCallback(env, mListener, mStorageId, osStatus);
545 }
546
checkAndClearJavaException(std::string_view method) const547 bool checkAndClearJavaException(std::string_view method) const {
548 JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
549 return ::checkAndClearJavaException(env, method);
550 }
551
control() const552 const UniqueControl& control() const { return mControl; }
getListenerLocalRef(JNIEnv * env) const553 jobject getListenerLocalRef(JNIEnv* env) const { return env->NewLocalRef(mListener); }
554
555 private:
556 JavaVM* const mJvm;
557 jobject const mService;
558 jobject const mServiceConnector;
559 jobject const mCallbackControl;
560 jobject const mListener;
561
562 jint const mStorageId;
563 UniqueControl const mControl;
564
565 ::DataLoader* mDataLoader = nullptr;
566 DataLoaderFeatures mDataLoaderFeatures = DATA_LOADER_FEATURE_NONE;
567
568 std::mutex mPendingReadsLooperBusy;
569 std::mutex mLogLooperBusy;
570 std::atomic<bool> mRunning{false};
571 };
572
onPendingReadsLooperEvent(int fd,int events,void * data)573 static int onPendingReadsLooperEvent(int fd, int events, void* data) {
574 if (globals().stopped) {
575 // No more listeners.
576 return 0;
577 }
578 auto&& dataLoaderConnector = (DataLoaderConnector*)data;
579 return dataLoaderConnector->onPendingReadsLooperEvent(globals().pendingReads,
580 globals().pendingReadsWithUid);
581 }
582
onLogLooperEvent(int fd,int events,void * data)583 static int onLogLooperEvent(int fd, int events, void* data) {
584 if (globals().stopped) {
585 // No more listeners.
586 return 0;
587 }
588 auto&& dataLoaderConnector = (DataLoaderConnector*)data;
589 return dataLoaderConnector->onLogLooperEvent(globals().pageReads, globals().pageReadsWithUid);
590 }
591
createFdFromManaged(JNIEnv * env,jobject pfd)592 static int createFdFromManaged(JNIEnv* env, jobject pfd) {
593 if (!pfd) {
594 return -1;
595 }
596
597 const auto& jni = jniIds(env);
598 auto managedFd = env->CallObjectMethod(pfd, jni.parcelFileDescriptorGetFileDescriptor);
599 return fcntl(jniGetFDFromFileDescriptor(env, managedFd), F_DUPFD_CLOEXEC, 0);
600 }
601
createServiceConnector(JNIEnv * env,jobject managedControl)602 static jobject createServiceConnector(JNIEnv* env, jobject managedControl) {
603 const auto& jni = jniIds(env);
604 return env->GetObjectField(managedControl, jni.service);
605 }
606
createCallbackControl(JNIEnv * env,jobject managedControl)607 static jobject createCallbackControl(JNIEnv* env, jobject managedControl) {
608 const auto& jni = jniIds(env);
609 return env->GetObjectField(managedControl, jni.callback);
610 }
611
createIncFsControlFromManaged(JNIEnv * env,jobject managedControl)612 static UniqueControl createIncFsControlFromManaged(JNIEnv* env, jobject managedControl) {
613 const auto& jni = jniIds(env);
614 auto managedIncControl = env->GetObjectField(managedControl, jni.incremental);
615 if (!managedIncControl) {
616 return UniqueControl();
617 }
618 auto cmd = createFdFromManaged(env, env->GetObjectField(managedIncControl, jni.controlCmd));
619 auto pr = createFdFromManaged(env,
620 env->GetObjectField(managedIncControl, jni.controlPendingReads));
621 auto log = createFdFromManaged(env, env->GetObjectField(managedIncControl, jni.controlLog));
622 auto blocksWritten =
623 createFdFromManaged(env,
624 env->GetObjectField(managedIncControl, jni.controlBlocksWritten));
625 return android::incfs::createControl(cmd, pr, log, blocksWritten);
626 }
627
DataLoaderParamsPair(android::dataloader::DataLoaderParams && dataLoaderParams)628 DataLoaderParamsPair::DataLoaderParamsPair(android::dataloader::DataLoaderParams&& dataLoaderParams)
629 : mDataLoaderParams(std::move(dataLoaderParams)) {
630 mNDKDataLoaderParams.type = mDataLoaderParams.type();
631 mNDKDataLoaderParams.packageName = mDataLoaderParams.packageName().c_str();
632 mNDKDataLoaderParams.className = mDataLoaderParams.className().c_str();
633 mNDKDataLoaderParams.arguments = mDataLoaderParams.arguments().c_str();
634 }
635
createFromManaged(JNIEnv * env,jobject managedParams)636 DataLoaderParamsPair DataLoaderParamsPair::createFromManaged(JNIEnv* env, jobject managedParams) {
637 const auto& jni = jniIds(env);
638
639 const DataLoaderType type = (DataLoaderType)env->GetIntField(managedParams, jni.paramsType);
640
641 ScopedLocalRef<jstring> paramsPackageName(env,
642 GetStringField(env, managedParams,
643 jni.paramsPackageName));
644 ScopedLocalRef<jstring> paramsClassName(env,
645 GetStringField(env, managedParams,
646 jni.paramsClassName));
647 ScopedLocalRef<jstring> paramsArguments(env,
648 GetStringField(env, managedParams,
649 jni.paramsArguments));
650 ScopedUtfChars package(env, paramsPackageName.get());
651 ScopedUtfChars className(env, paramsClassName.get());
652 ScopedUtfChars arguments(env, paramsArguments.get());
653 return DataLoaderParamsPair(android::dataloader::DataLoaderParams(type, package.c_str(),
654 className.c_str(),
655 arguments.c_str()));
656 }
657
pendingReadsLooperThread()658 static void pendingReadsLooperThread() {
659 constexpr auto kTimeoutMsecs = -1;
660 while (!globals().stopped) {
661 pendingReadsLooper().pollAll(kTimeoutMsecs);
662 }
663 }
664
logLooperThread()665 static void logLooperThread() {
666 constexpr auto kTimeoutMsecs = -1;
667 while (!globals().stopped) {
668 logLooper().pollAll(kTimeoutMsecs);
669 }
670 }
671
pathFromFd(int fd)672 static std::string pathFromFd(int fd) {
673 static constexpr char fdNameFormat[] = "/proc/self/fd/%d";
674 char fdNameBuffer[NELEM(fdNameFormat) + 11 + 1]; // max int length + '\0'
675 snprintf(fdNameBuffer, NELEM(fdNameBuffer), fdNameFormat, fd);
676
677 std::string res;
678 // lstat() is supposed to return us exactly the needed buffer size, but
679 // somehow it may also return a smaller (but still >0) st_size field.
680 // That's why let's only use it for the initial estimate.
681 struct stat st = {};
682 if (::lstat(fdNameBuffer, &st) || st.st_size == 0) {
683 st.st_size = PATH_MAX;
684 }
685 auto bufSize = st.st_size;
686 for (;;) {
687 res.resize(bufSize + 1, '\0');
688 auto size = ::readlink(fdNameBuffer, &res[0], res.size());
689 if (size < 0) {
690 return {};
691 }
692 if (size > bufSize) {
693 // File got renamed in between lstat() and readlink() calls? Retry.
694 bufSize *= 2;
695 continue;
696 }
697 res.resize(size);
698 return res;
699 }
700 }
701
702 } // namespace
703
DataLoader_Initialize(struct::DataLoaderFactory * factory)704 void DataLoader_Initialize(struct ::DataLoaderFactory* factory) {
705 CHECK(factory) << "DataLoader factory is invalid.";
706 globals().legacyDataLoaderFactory = factory;
707 }
708
DataLoader_Initialize_WithFeatures(struct::DataLoaderFactory * factory)709 void DataLoader_Initialize_WithFeatures(struct ::DataLoaderFactory* factory) {
710 CHECK(factory) << "DataLoader factory is invalid.";
711 globals().dataLoaderFactory = factory;
712 }
713
DataLoader_FilesystemConnector_writeData(DataLoaderFilesystemConnectorPtr ifs,jstring name,jlong offsetBytes,jlong lengthBytes,jobject incomingFd)714 void DataLoader_FilesystemConnector_writeData(DataLoaderFilesystemConnectorPtr ifs, jstring name,
715 jlong offsetBytes, jlong lengthBytes,
716 jobject incomingFd) {
717 auto connector = static_cast<DataLoaderConnector*>(ifs);
718 return connector->writeData(name, offsetBytes, lengthBytes, incomingFd);
719 }
720
DataLoader_FilesystemConnector_openForSpecialOps(DataLoaderFilesystemConnectorPtr ifs,IncFsFileId fid)721 int DataLoader_FilesystemConnector_openForSpecialOps(DataLoaderFilesystemConnectorPtr ifs,
722 IncFsFileId fid) {
723 auto connector = static_cast<DataLoaderConnector*>(ifs);
724 return connector->openForSpecialOps(fid).release();
725 }
726
DataLoader_FilesystemConnector_writeBlocks(DataLoaderFilesystemConnectorPtr ifs,const IncFsDataBlock blocks[],int blocksCount)727 int DataLoader_FilesystemConnector_writeBlocks(DataLoaderFilesystemConnectorPtr ifs,
728 const IncFsDataBlock blocks[], int blocksCount) {
729 auto connector = static_cast<DataLoaderConnector*>(ifs);
730 return connector->writeBlocks({blocks, static_cast<size_t>(blocksCount)});
731 }
732
DataLoader_FilesystemConnector_getRawMetadata(DataLoaderFilesystemConnectorPtr ifs,IncFsFileId fid,char buffer[],size_t * bufferSize)733 int DataLoader_FilesystemConnector_getRawMetadata(DataLoaderFilesystemConnectorPtr ifs,
734 IncFsFileId fid, char buffer[],
735 size_t* bufferSize) {
736 auto connector = static_cast<DataLoaderConnector*>(ifs);
737 return connector->getRawMetadata(fid, buffer, bufferSize);
738 }
739
DataLoader_FilesystemConnector_setParams(DataLoaderFilesystemConnectorPtr ifs,DataLoaderFilesystemParams params)740 bool DataLoader_FilesystemConnector_setParams(DataLoaderFilesystemConnectorPtr ifs,
741 DataLoaderFilesystemParams params) {
742 auto connector = static_cast<DataLoaderConnector*>(ifs);
743 return connector->setParams(params);
744 }
745
DataLoader_StatusListener_reportStatus(DataLoaderStatusListenerPtr listener,DataLoaderStatus status)746 int DataLoader_StatusListener_reportStatus(DataLoaderStatusListenerPtr listener,
747 DataLoaderStatus status) {
748 auto connector = static_cast<DataLoaderConnector*>(listener);
749 return connector->reportStatus(status);
750 }
751
DataLoaderService_OnCreate(JNIEnv * env,jobject service,jint storageId,jobject control,jobject params,jobject listener)752 bool DataLoaderService_OnCreate(JNIEnv* env, jobject service, jint storageId, jobject control,
753 jobject params, jobject listener) {
754 {
755 std::lock_guard lock{globals().dataLoaderConnectorsLock};
756 auto dlIt = globals().dataLoaderConnectors.find(storageId);
757 if (dlIt != globals().dataLoaderConnectors.end()) {
758 ALOGI("id(%d): already exist, skipping creation.", storageId);
759 return true;
760 }
761 }
762 auto nativeControl = createIncFsControlFromManaged(env, control);
763 if (nativeControl) {
764 using namespace android::incfs;
765 ALOGI("DataLoader::create incremental fds: %d/%d/%d/%d", nativeControl.cmd(),
766 nativeControl.pendingReads(), nativeControl.logs(), nativeControl.blocksWritten());
767 auto cmdPath = pathFromFd(nativeControl.cmd());
768 auto dir = path::dirName(cmdPath);
769 ALOGI("DataLoader::create incremental dir: %s, files: %s/%s/%s/%s",
770 details::c_str(dir).get(), details::c_str(path::baseName(cmdPath)).get(),
771 details::c_str(path::baseName(pathFromFd(nativeControl.pendingReads()))).get(),
772 details::c_str(path::baseName(pathFromFd(nativeControl.logs()))).get(),
773 details::c_str(path::baseName(pathFromFd(nativeControl.blocksWritten()))).get());
774 } else {
775 ALOGI("DataLoader::create no incremental control");
776 }
777
778 auto nativeParams = DataLoaderParamsPair::createFromManaged(env, params);
779 ALOGI("DataLoader::create params: %d|%s|%s|%s", nativeParams.dataLoaderParams().type(),
780 nativeParams.dataLoaderParams().packageName().c_str(),
781 nativeParams.dataLoaderParams().className().c_str(),
782 nativeParams.dataLoaderParams().arguments().c_str());
783
784 auto serviceConnector = createServiceConnector(env, control);
785 auto callbackControl = createCallbackControl(env, control);
786
787 auto reportUnavailable = [env, storageId](jobject listener) {
788 const auto& jni = jniIds(env);
789 reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_UNAVAILABLE);
790 };
791 // By default, it's disabled. Need to assign listener to enable.
792 std::unique_ptr<_jobject, decltype(reportUnavailable)>
793 reportUnavailableOnExit(nullptr, reportUnavailable);
794
795 auto dataLoaderConnector =
796 std::make_unique<DataLoaderConnector>(env, service, storageId, std::move(nativeControl),
797 serviceConnector, callbackControl, listener);
798 bool created = dataLoaderConnector->onCreate(nativeParams, params);
799 {
800 std::lock_guard lock{globals().dataLoaderConnectorsLock};
801 auto [dlIt, dlInserted] =
802 globals().dataLoaderConnectors.try_emplace(storageId,
803 std::move(dataLoaderConnector));
804 if (!dlInserted) {
805 ALOGE("id(%d): already exist, skipping creation.", storageId);
806 return false;
807 }
808
809 if (!created) {
810 globals().dataLoaderConnectors.erase(dlIt);
811 // Enable the reporter.
812 reportUnavailableOnExit.reset(listener);
813 return false;
814 }
815 }
816
817 const auto& jni = jniIds(env);
818 reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_CREATED);
819
820 return true;
821 }
822
DataLoaderService_OnStart(JNIEnv * env,jint storageId)823 bool DataLoaderService_OnStart(JNIEnv* env, jint storageId) {
824 auto destroyAndReportUnavailable = [env, storageId](jobject listener) {
825 // Because of the MT the installer can call commit and recreate/restart dataLoader before
826 // system server has a change to destroy it.
827 DataLoaderService_OnDestroy(env, storageId);
828 const auto& jni = jniIds(env);
829 reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_UNAVAILABLE);
830 };
831 // By default, it's disabled. Need to assign listener to enable.
832 std::unique_ptr<_jobject, decltype(destroyAndReportUnavailable)>
833 destroyAndReportUnavailableOnExit(nullptr, destroyAndReportUnavailable);
834
835 DataLoaderConnectorPtr dataLoaderConnector;
836 {
837 std::lock_guard lock{globals().dataLoaderConnectorsLock};
838 auto dlIt = globals().dataLoaderConnectors.find(storageId);
839 if (dlIt == globals().dataLoaderConnectors.end()) {
840 ALOGE("Failed to start id(%d): not found", storageId);
841 return false;
842 }
843 dataLoaderConnector = dlIt->second;
844 }
845 const UniqueControl* control = &(dataLoaderConnector->control());
846 jobject listener = dataLoaderConnector->getListenerLocalRef(env);
847
848 if (!dataLoaderConnector->onStart()) {
849 ALOGE("Failed to start id(%d): onStart returned false", storageId);
850 destroyAndReportUnavailableOnExit.reset(listener);
851 return false;
852 }
853
854 if (control->pendingReads() >= 0) {
855 auto&& looper = pendingReadsLooper();
856 if (!globals().pendingReadsLooperThread.joinable()) {
857 std::lock_guard lock{globals().dataLoaderConnectorsLock};
858 if (!globals().pendingReadsLooperThread.joinable()) {
859 globals().pendingReadsLooperThread = std::thread(&pendingReadsLooperThread);
860 }
861 }
862
863 looper.addFd(control->pendingReads(), android::Looper::POLL_CALLBACK,
864 android::Looper::EVENT_INPUT, &onPendingReadsLooperEvent,
865 dataLoaderConnector.get());
866 looper.wake();
867 }
868
869 if (control->logs() >= 0) {
870 auto&& looper = logLooper();
871 if (!globals().logLooperThread.joinable()) {
872 std::lock_guard lock{globals().dataLoaderConnectorsLock};
873 if (!globals().logLooperThread.joinable()) {
874 globals().logLooperThread = std::thread(&logLooperThread);
875 }
876 }
877
878 looper.addFd(control->logs(), android::Looper::POLL_CALLBACK, android::Looper::EVENT_INPUT,
879 &onLogLooperEvent, dataLoaderConnector.get());
880 looper.wake();
881 }
882
883 const auto& jni = jniIds(env);
884 reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_STARTED);
885
886 return true;
887 }
888
DataLoaderService_OnStop_NoStatus(const UniqueControl * control,const DataLoaderConnectorPtr & dataLoaderConnector)889 static void DataLoaderService_OnStop_NoStatus(const UniqueControl* control,
890 const DataLoaderConnectorPtr& dataLoaderConnector) {
891 if (control->pendingReads() >= 0) {
892 pendingReadsLooper().removeFd(control->pendingReads());
893 pendingReadsLooper().wake();
894 }
895 if (control->logs() >= 0) {
896 logLooper().removeFd(control->logs());
897 logLooper().wake();
898 }
899 dataLoaderConnector->onStop();
900 }
901
DataLoaderService_OnStop(JNIEnv * env,jint storageId)902 bool DataLoaderService_OnStop(JNIEnv* env, jint storageId) {
903 DataLoaderConnectorPtr dataLoaderConnector;
904 {
905 std::lock_guard lock{globals().dataLoaderConnectorsLock};
906 auto dlIt = globals().dataLoaderConnectors.find(storageId);
907 if (dlIt == globals().dataLoaderConnectors.end()) {
908 ALOGI("Failed to stop id(%d): not found", storageId);
909 return true;
910 }
911 dataLoaderConnector = dlIt->second;
912 }
913 const UniqueControl* control = &(dataLoaderConnector->control());
914 jobject listener = dataLoaderConnector->getListenerLocalRef(env);
915
916 // Just stop.
917 DataLoaderService_OnStop_NoStatus(control, dataLoaderConnector);
918
919 const auto& jni = jniIds(env);
920 reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_STOPPED);
921
922 return true;
923 }
924
DataLoaderService_OnDestroy(JNIEnv * env,jint storageId)925 bool DataLoaderService_OnDestroy(JNIEnv* env, jint storageId) {
926 DataLoaderConnectorPtr dataLoaderConnector;
927 {
928 std::lock_guard lock{globals().dataLoaderConnectorsLock};
929 auto dlIt = globals().dataLoaderConnectors.find(storageId);
930 if (dlIt == globals().dataLoaderConnectors.end()) {
931 ALOGI("Failed to destroy id(%d): not found", storageId);
932 return true;
933 }
934 dataLoaderConnector = std::move(dlIt->second);
935 globals().dataLoaderConnectors.erase(dlIt);
936 }
937 const UniqueControl* control = &(dataLoaderConnector->control());
938
939 // Stop/destroy.
940 DataLoaderService_OnStop_NoStatus(control, dataLoaderConnector);
941 // This will destroy the last instance of the DataLoaderConnectorPtr and should trigger the
942 // destruction of the DataLoader. However if there are any hanging instances, the destruction
943 // will be postponed. E.g. OnPrepareImage in progress at the same time we call OnDestroy.
944 dataLoaderConnector = {};
945
946 return true;
947 }
948
949 struct DataLoaderInstallationFilesPair {
950 static DataLoaderInstallationFilesPair createFromManaged(JNIEnv* env, jobjectArray jfiles);
951
952 using Files = std::vector<android::dataloader::DataLoaderInstallationFile>;
filesDataLoaderInstallationFilesPair953 const Files& files() const { return mFiles; }
954
955 using NDKFiles = std::vector<::DataLoaderInstallationFile>;
ndkFilesDataLoaderInstallationFilesPair956 const NDKFiles& ndkFiles() const { return mNDKFiles; }
957
958 private:
959 DataLoaderInstallationFilesPair(Files&& files);
960
961 Files mFiles;
962 NDKFiles mNDKFiles;
963 };
964
createFromManaged(JNIEnv * env,jobjectArray jfiles)965 DataLoaderInstallationFilesPair DataLoaderInstallationFilesPair::createFromManaged(
966 JNIEnv* env, jobjectArray jfiles) {
967 const auto& jni = jniIds(env);
968
969 // jfiles is a Java array of InstallationFileParcel
970 auto size = env->GetArrayLength(jfiles);
971 DataLoaderInstallationFilesPair::Files files;
972 files.reserve(size);
973
974 for (int i = 0; i < size; ++i) {
975 ScopedLocalRef<jobject> jfile(env, env->GetObjectArrayElement(jfiles, i));
976
977 DataLoaderLocation location =
978 (DataLoaderLocation)env->GetIntField(jfile.get(), jni.installationFileLocation);
979 ScopedUtfChars name(env, GetStringField(env, jfile.get(), jni.installationFileName));
980 IncFsSize size = env->GetLongField(jfile.get(), jni.installationFileLengthBytes);
981
982 ScopedLocalRef<jbyteArray> jmetadataBytes(env,
983 GetByteArrayField(env, jfile.get(),
984 jni.installationFileMetadata));
985 auto metadataElements = env->GetByteArrayElements(jmetadataBytes.get(), nullptr);
986 auto metadataLength = env->GetArrayLength(jmetadataBytes.get());
987 RawMetadata metadata(metadataElements, metadataElements + metadataLength);
988 env->ReleaseByteArrayElements(jmetadataBytes.get(), metadataElements, 0);
989
990 files.emplace_back(location, name.c_str(), size, std::move(metadata));
991 }
992
993 return DataLoaderInstallationFilesPair(std::move(files));
994 }
995
DataLoaderInstallationFilesPair(Files && files)996 DataLoaderInstallationFilesPair::DataLoaderInstallationFilesPair(Files&& files)
997 : mFiles(std::move(files)) {
998 const auto size = mFiles.size();
999 mNDKFiles.resize(size);
1000 for (size_t i = 0; i < size; ++i) {
1001 const auto& file = mFiles[i];
1002 auto&& ndkFile = mNDKFiles[i];
1003
1004 ndkFile.location = file.location();
1005 ndkFile.name = file.name().c_str();
1006 ndkFile.size = file.size();
1007 ndkFile.metadata.data = file.metadata().data();
1008 ndkFile.metadata.size = file.metadata().size();
1009 }
1010 }
1011
DataLoaderService_OnPrepareImage(JNIEnv * env,jint storageId,jobjectArray addedFiles,jobjectArray removedFiles)1012 bool DataLoaderService_OnPrepareImage(JNIEnv* env, jint storageId, jobjectArray addedFiles,
1013 jobjectArray removedFiles) {
1014 DataLoaderConnectorPtr dataLoaderConnector;
1015 {
1016 std::lock_guard lock{globals().dataLoaderConnectorsLock};
1017 auto dlIt = globals().dataLoaderConnectors.find(storageId);
1018 if (dlIt == globals().dataLoaderConnectors.end()) {
1019 ALOGE("Failed to handle onPrepareImage for id(%d): not found", storageId);
1020 return false;
1021 }
1022 dataLoaderConnector = dlIt->second;
1023 }
1024 jobject listener = dataLoaderConnector->getListenerLocalRef(env);
1025
1026 auto addedFilesPair = DataLoaderInstallationFilesPair::createFromManaged(env, addedFiles);
1027 bool result = dataLoaderConnector->onPrepareImage(addedFilesPair.ndkFiles());
1028
1029 const auto& jni = jniIds(env);
1030
1031 if (checkAndClearJavaException(env, "onPrepareImage")) {
1032 reportStatusViaCallback(env, listener, storageId, jni.constants.DATA_LOADER_UNAVAILABLE);
1033 return false;
1034 }
1035
1036 reportStatusViaCallback(env, listener, storageId,
1037 result ? jni.constants.DATA_LOADER_IMAGE_READY
1038 : jni.constants.DATA_LOADER_IMAGE_NOT_READY);
1039
1040 return result;
1041 }
1042