1 /*
2 * Copyright (C) 2020 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 #define ATRACE_TAG ATRACE_TAG_ADB
18 #define LOG_TAG "PackageManagerShellCommandDataLoader-jni"
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <android-base/no_destructor.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/unique_fd.h>
24 #include <core_jni_helpers.h>
25 #include <cutils/trace.h>
26 #include <endian.h>
27 #include <nativehelper/JNIHelp.h>
28 #include <sys/eventfd.h>
29 #include <sys/poll.h>
30
31 #include <charconv>
32 #include <chrono>
33 #include <span>
34 #include <string>
35 #include <thread>
36 #include <unordered_map>
37 #include <unordered_set>
38
39 #include "dataloader.h"
40
41 namespace android {
42
43 namespace {
44
45 using android::base::borrowed_fd;
46 using android::base::ReadFully;
47 using android::base::unique_fd;
48
49 using namespace std::literals;
50
51 using BlockSize = int16_t;
52 using FileIdx = int16_t;
53 using BlockIdx = int32_t;
54 using NumBlocks = int32_t;
55 using BlockType = int8_t;
56 using CompressionType = int8_t;
57 using RequestType = int16_t;
58 using MagicType = uint32_t;
59
60 static constexpr int BUFFER_SIZE = 256 * 1024;
61 static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE;
62
63 static constexpr int COMMAND_SIZE = 4 + 2 + 2 + 4; // bytes
64 static constexpr int HEADER_SIZE = 2 + 1 + 1 + 4 + 2; // bytes
65 static constexpr std::string_view OKAY = "OKAY"sv;
66 static constexpr MagicType INCR = 0x52434e49; // BE INCR
67
68 static constexpr auto PollTimeoutMs = 5000;
69 static constexpr auto TraceTagCheckInterval = 1s;
70
71 struct JniIds {
72 jclass packageManagerShellCommandDataLoader;
73 jmethodID pmscdLookupShellCommand;
74 jmethodID pmscdGetStdIn;
75 jmethodID pmscdGetLocalFile;
76
JniIdsandroid::__anonfb7abe8f0111::JniIds77 JniIds(JNIEnv* env) {
78 packageManagerShellCommandDataLoader = (jclass)env->NewGlobalRef(
79 FindClassOrDie(env, "com/android/server/pm/PackageManagerShellCommandDataLoader"));
80 pmscdLookupShellCommand =
81 GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader,
82 "lookupShellCommand",
83 "(Ljava/lang/String;)Landroid/os/ShellCommand;");
84 pmscdGetStdIn = GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader,
85 "getStdIn", "(Landroid/os/ShellCommand;)I");
86 pmscdGetLocalFile =
87 GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getLocalFile",
88 "(Landroid/os/ShellCommand;Ljava/lang/String;)I");
89 }
90 };
91
jniIds(JNIEnv * env)92 const JniIds& jniIds(JNIEnv* env) {
93 static const JniIds ids(env);
94 return ids;
95 }
96
97 struct BlockHeader {
98 FileIdx fileIdx = -1;
99 BlockType blockType = -1;
100 CompressionType compressionType = -1;
101 BlockIdx blockIdx = -1;
102 BlockSize blockSize = -1;
103 } __attribute__((packed));
104
105 static_assert(sizeof(BlockHeader) == HEADER_SIZE);
106
107 static constexpr RequestType EXIT = 0;
108 static constexpr RequestType BLOCK_MISSING = 1;
109 static constexpr RequestType PREFETCH = 2;
110
111 struct RequestCommand {
112 MagicType magic;
113 RequestType requestType;
114 FileIdx fileIdx;
115 BlockIdx blockIdx;
116 } __attribute__((packed));
117
118 static_assert(COMMAND_SIZE == sizeof(RequestCommand));
119
sendRequest(int fd,RequestType requestType,FileIdx fileIdx=-1,BlockIdx blockIdx=-1)120 static bool sendRequest(int fd, RequestType requestType, FileIdx fileIdx = -1,
121 BlockIdx blockIdx = -1) {
122 const RequestCommand command{.magic = INCR,
123 .requestType = static_cast<int16_t>(be16toh(requestType)),
124 .fileIdx = static_cast<int16_t>(be16toh(fileIdx)),
125 .blockIdx = static_cast<int32_t>(be32toh(blockIdx))};
126 return android::base::WriteFully(fd, &command, sizeof(command));
127 }
128
waitForDataOrSignal(int fd,int event_fd)129 static int waitForDataOrSignal(int fd, int event_fd) {
130 struct pollfd pfds[2] = {{fd, POLLIN, 0}, {event_fd, POLLIN, 0}};
131 // Wait indefinitely until either data is ready or stop signal is received
132 int res = poll(pfds, 2, PollTimeoutMs);
133 if (res <= 0) {
134 return res;
135 }
136 // First check if there is a stop signal
137 if (pfds[1].revents == POLLIN) {
138 return event_fd;
139 }
140 // Otherwise check if incoming data is ready
141 if (pfds[0].revents == POLLIN) {
142 return fd;
143 }
144 return -1;
145 }
146
readChunk(int fd,std::vector<uint8_t> & data)147 static bool readChunk(int fd, std::vector<uint8_t>& data) {
148 int32_t size;
149 if (!android::base::ReadFully(fd, &size, sizeof(size))) {
150 return false;
151 }
152 size = int32_t(be32toh(size));
153 if (size <= 0) {
154 return false;
155 }
156 data.resize(size);
157 return android::base::ReadFully(fd, data.data(), data.size());
158 }
159
160 BlockHeader readHeader(std::span<uint8_t>& data);
161
readLEInt32(borrowed_fd fd)162 static inline int32_t readLEInt32(borrowed_fd fd) {
163 int32_t result;
164 ReadFully(fd, &result, sizeof(result));
165 result = int32_t(le32toh(result));
166 return result;
167 }
168
readBytes(borrowed_fd fd)169 static inline std::vector<char> readBytes(borrowed_fd fd) {
170 int32_t size = readLEInt32(fd);
171 std::vector<char> result(size);
172 ReadFully(fd, result.data(), size);
173 return result;
174 }
175
skipIdSigHeaders(borrowed_fd fd)176 static inline int32_t skipIdSigHeaders(borrowed_fd fd) {
177 readLEInt32(fd); // version
178 readBytes(fd); // hashingInfo
179 readBytes(fd); // signingInfo
180 return readLEInt32(fd); // size of the verity tree
181 }
182
verityTreeSizeForFile(IncFsSize fileSize)183 static inline IncFsSize verityTreeSizeForFile(IncFsSize fileSize) {
184 constexpr int SHA256_DIGEST_SIZE = 32;
185 constexpr int digest_size = SHA256_DIGEST_SIZE;
186 constexpr int hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
187
188 IncFsSize total_tree_block_count = 0;
189
190 auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE;
191 auto hash_block_count = block_count;
192 for (auto i = 0; hash_block_count > 1; i++) {
193 hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
194 total_tree_block_count += hash_block_count;
195 }
196 return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
197 }
198
199 enum MetadataMode : int8_t {
200 STDIN = 0,
201 LOCAL_FILE = 1,
202 DATA_ONLY_STREAMING = 2,
203 STREAMING = 3,
204 };
205
206 struct InputDesc {
207 unique_fd fd;
208 IncFsSize size;
209 IncFsBlockKind kind = INCFS_BLOCK_KIND_DATA;
210 bool waitOnEof = false;
211 bool streaming = false;
212 MetadataMode mode = STDIN;
213 };
214 using InputDescs = std::vector<InputDesc>;
215
216 template <class T>
read(IncFsSpan & data)217 std::optional<T> read(IncFsSpan& data) {
218 if (data.size < (int32_t)sizeof(T)) {
219 return {};
220 }
221 T res;
222 memcpy(&res, data.data, sizeof(res));
223 data.data += sizeof(res);
224 data.size -= sizeof(res);
225 return res;
226 }
227
openLocalFile(JNIEnv * env,const JniIds & jni,jobject shellCommand,IncFsSize size,const std::string & filePath)228 static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
229 IncFsSize size, const std::string& filePath) {
230 InputDescs result;
231 result.reserve(2);
232
233 const std::string idsigPath = filePath + ".idsig";
234
235 unique_fd idsigFd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
236 jni.pmscdGetLocalFile, shellCommand,
237 env->NewStringUTF(idsigPath.c_str()))};
238 if (idsigFd.ok()) {
239 auto treeSize = verityTreeSizeForFile(size);
240 auto actualTreeSize = skipIdSigHeaders(idsigFd);
241 if (treeSize != actualTreeSize) {
242 ALOGE("Verity tree size mismatch: %d vs .idsig: %d.", int(treeSize),
243 int(actualTreeSize));
244 return {};
245 }
246 result.push_back(InputDesc{
247 .fd = std::move(idsigFd),
248 .size = treeSize,
249 .kind = INCFS_BLOCK_KIND_HASH,
250 });
251 }
252
253 unique_fd fileFd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
254 jni.pmscdGetLocalFile, shellCommand,
255 env->NewStringUTF(filePath.c_str()))};
256 if (fileFd.ok()) {
257 result.push_back(InputDesc{
258 .fd = std::move(fileFd),
259 .size = size,
260 });
261 }
262
263 return result;
264 }
265
openInputs(JNIEnv * env,const JniIds & jni,jobject shellCommand,IncFsSize size,IncFsSpan metadata)266 static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shellCommand,
267 IncFsSize size, IncFsSpan metadata) {
268 auto mode = read<int8_t>(metadata).value_or(STDIN);
269 if (mode == LOCAL_FILE) {
270 // local file and possibly signature
271 return openLocalFile(env, jni, shellCommand, size,
272 std::string(metadata.data, metadata.size));
273 }
274
275 unique_fd fd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
276 jni.pmscdGetStdIn, shellCommand)};
277 if (!fd.ok()) {
278 return {};
279 }
280
281 InputDescs result;
282 switch (mode) {
283 case STDIN: {
284 result.push_back(InputDesc{
285 .fd = std::move(fd),
286 .size = size,
287 .waitOnEof = true,
288 });
289 break;
290 }
291 case DATA_ONLY_STREAMING: {
292 // verity tree from stdin, rest is streaming
293 auto treeSize = verityTreeSizeForFile(size);
294 result.push_back(InputDesc{
295 .fd = std::move(fd),
296 .size = treeSize,
297 .kind = INCFS_BLOCK_KIND_HASH,
298 .waitOnEof = true,
299 .streaming = true,
300 .mode = DATA_ONLY_STREAMING,
301 });
302 break;
303 }
304 case STREAMING: {
305 result.push_back(InputDesc{
306 .fd = std::move(fd),
307 .size = 0,
308 .streaming = true,
309 .mode = STREAMING,
310 });
311 break;
312 }
313 }
314 return result;
315 }
316
GetJNIEnvironment(JavaVM * vm)317 static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) {
318 JNIEnv* env;
319 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
320 return 0;
321 }
322 return env;
323 }
324
GetOrAttachJNIEnvironment(JavaVM * jvm)325 static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm) {
326 JNIEnv* env = GetJNIEnvironment(jvm);
327 if (!env) {
328 int result = jvm->AttachCurrentThread(&env, nullptr);
329 CHECK_EQ(result, JNI_OK) << "thread attach failed";
330 struct VmDetacher {
331 VmDetacher(JavaVM* vm) : mVm(vm) {}
332 ~VmDetacher() { mVm->DetachCurrentThread(); }
333
334 private:
335 JavaVM* const mVm;
336 };
337 static thread_local VmDetacher detacher(jvm);
338 }
339 return env;
340 }
341
342 class PMSCDataLoader;
343
344 struct OnTraceChanged {
345 OnTraceChanged();
~OnTraceChangedandroid::__anonfb7abe8f0111::OnTraceChanged346 ~OnTraceChanged() {
347 mRunning = false;
348 mChecker.join();
349 }
350
registerCallbackandroid::__anonfb7abe8f0111::OnTraceChanged351 void registerCallback(PMSCDataLoader* callback) {
352 std::unique_lock lock(mMutex);
353 mCallbacks.insert(callback);
354 }
355
unregisterCallbackandroid::__anonfb7abe8f0111::OnTraceChanged356 void unregisterCallback(PMSCDataLoader* callback) {
357 std::unique_lock lock(mMutex);
358 mCallbacks.erase(callback);
359 }
360
361 private:
362 std::mutex mMutex;
363 std::unordered_set<PMSCDataLoader*> mCallbacks;
364 std::atomic<bool> mRunning{true};
365 std::thread mChecker;
366 };
367
onTraceChanged()368 static OnTraceChanged& onTraceChanged() {
369 static android::base::NoDestructor<OnTraceChanged> instance;
370 return *instance;
371 }
372
373 class PMSCDataLoader : public android::dataloader::DataLoader {
374 public:
PMSCDataLoader(JavaVM * jvm)375 PMSCDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); }
~PMSCDataLoader()376 ~PMSCDataLoader() { onTraceChanged().unregisterCallback(this); }
377
updateReadLogsState(const bool enabled)378 void updateReadLogsState(const bool enabled) {
379 if (enabled != mReadLogsEnabled.exchange(enabled)) {
380 mIfs->setParams({.readLogsEnabled = enabled});
381 }
382 }
383
384 private:
385 // Lifecycle.
onCreate(const android::dataloader::DataLoaderParams & params,android::dataloader::FilesystemConnectorPtr ifs,android::dataloader::StatusListenerPtr statusListener,android::dataloader::ServiceConnectorPtr,android::dataloader::ServiceParamsPtr)386 bool onCreate(const android::dataloader::DataLoaderParams& params,
387 android::dataloader::FilesystemConnectorPtr ifs,
388 android::dataloader::StatusListenerPtr statusListener,
389 android::dataloader::ServiceConnectorPtr,
390 android::dataloader::ServiceParamsPtr) final {
391 CHECK(ifs) << "ifs can't be null";
392 CHECK(statusListener) << "statusListener can't be null";
393 mArgs = params.arguments();
394 mIfs = ifs;
395 mStatusListener = statusListener;
396 updateReadLogsState(atrace_is_tag_enabled(ATRACE_TAG));
397 onTraceChanged().registerCallback(this);
398 return true;
399 }
onStart()400 bool onStart() final { return true; }
onStop()401 void onStop() final {
402 mStopReceiving = true;
403 eventfd_write(mEventFd, 1);
404 if (mReceiverThread.joinable()) {
405 mReceiverThread.join();
406 }
407 }
onDestroy()408 void onDestroy() final {
409 onTraceChanged().unregisterCallback(this);
410 // Make sure the receiver thread stopped.
411 CHECK(!mReceiverThread.joinable());
412 }
413
414 // Installation.
onPrepareImage(dataloader::DataLoaderInstallationFiles addedFiles)415 bool onPrepareImage(dataloader::DataLoaderInstallationFiles addedFiles) final {
416 ALOGE("onPrepareImage: start.");
417
418 JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
419 const auto& jni = jniIds(env);
420
421 jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
422 jni.pmscdLookupShellCommand,
423 env->NewStringUTF(mArgs.c_str()));
424 if (!shellCommand) {
425 ALOGE("Missing shell command.");
426 return false;
427 }
428
429 std::vector<char> buffer;
430 buffer.reserve(BUFFER_SIZE);
431
432 std::vector<IncFsDataBlock> blocks;
433 blocks.reserve(BLOCKS_COUNT);
434
435 unique_fd streamingFd;
436 MetadataMode streamingMode;
437 for (auto&& file : addedFiles) {
438 auto inputs = openInputs(env, jni, shellCommand, file.size, file.metadata);
439 if (inputs.empty()) {
440 ALOGE("Failed to open an input file for metadata: %.*s, final file name is: %s. "
441 "Error %d",
442 int(file.metadata.size), file.metadata.data, file.name, errno);
443 return false;
444 }
445
446 const auto fileId = IncFs_FileIdFromMetadata(file.metadata);
447 const base::unique_fd incfsFd(mIfs->openForSpecialOps(fileId).release());
448 if (incfsFd < 0) {
449 ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
450 "Error %d",
451 int(file.metadata.size), file.metadata.data, file.name, errno);
452 return false;
453 }
454
455 for (auto&& input : inputs) {
456 if (input.streaming && !streamingFd.ok()) {
457 streamingFd.reset(dup(input.fd));
458 streamingMode = input.mode;
459 }
460 if (!copyToIncFs(incfsFd, input.size, input.kind, input.fd, input.waitOnEof,
461 &buffer, &blocks)) {
462 ALOGE("Failed to copy data to IncFS file for metadata: %.*s, final file name "
463 "is: %s. "
464 "Error %d",
465 int(file.metadata.size), file.metadata.data, file.name, errno);
466 return false;
467 }
468 }
469 }
470
471 if (streamingFd.ok()) {
472 ALOGE("onPrepareImage: done, proceeding to streaming.");
473 return initStreaming(std::move(streamingFd), streamingMode);
474 }
475
476 ALOGE("onPrepareImage: done.");
477 return true;
478 }
479
copyToIncFs(borrowed_fd incfsFd,IncFsSize size,IncFsBlockKind kind,borrowed_fd incomingFd,bool waitOnEof,std::vector<char> * buffer,std::vector<IncFsDataBlock> * blocks)480 bool copyToIncFs(borrowed_fd incfsFd, IncFsSize size, IncFsBlockKind kind,
481 borrowed_fd incomingFd, bool waitOnEof, std::vector<char>* buffer,
482 std::vector<IncFsDataBlock>* blocks) {
483 IncFsSize remaining = size;
484 IncFsSize totalSize = 0;
485 IncFsBlockIndex blockIdx = 0;
486 while (remaining > 0) {
487 constexpr auto capacity = BUFFER_SIZE;
488 auto size = buffer->size();
489 if (capacity - size < INCFS_DATA_FILE_BLOCK_SIZE) {
490 if (!flashToIncFs(incfsFd, kind, false, &blockIdx, buffer, blocks)) {
491 return false;
492 }
493 continue;
494 }
495
496 auto toRead = std::min<IncFsSize>(remaining, capacity - size);
497 buffer->resize(size + toRead);
498 auto read = ::read(incomingFd.get(), buffer->data() + size, toRead);
499 if (read == 0) {
500 if (waitOnEof) {
501 // eof of stdin, waiting...
502 ALOGE("eof of stdin, waiting...: %d, remaining: %d, block: %d, read: %d",
503 int(totalSize), int(remaining), int(blockIdx), int(read));
504 using namespace std::chrono_literals;
505 std::this_thread::sleep_for(10ms);
506 continue;
507 }
508 break;
509 }
510 if (read < 0) {
511 return false;
512 }
513
514 buffer->resize(size + read);
515 remaining -= read;
516 totalSize += read;
517 }
518 if (!buffer->empty() && !flashToIncFs(incfsFd, kind, true, &blockIdx, buffer, blocks)) {
519 return false;
520 }
521 return true;
522 }
523
flashToIncFs(borrowed_fd incfsFd,IncFsBlockKind kind,bool eof,IncFsBlockIndex * blockIdx,std::vector<char> * buffer,std::vector<IncFsDataBlock> * blocks)524 bool flashToIncFs(borrowed_fd incfsFd, IncFsBlockKind kind, bool eof, IncFsBlockIndex* blockIdx,
525 std::vector<char>* buffer, std::vector<IncFsDataBlock>* blocks) {
526 int consumed = 0;
527 const auto fullBlocks = buffer->size() / INCFS_DATA_FILE_BLOCK_SIZE;
528 for (int i = 0; i < fullBlocks; ++i) {
529 const auto inst = IncFsDataBlock{
530 .fileFd = incfsFd.get(),
531 .pageIndex = (*blockIdx)++,
532 .compression = INCFS_COMPRESSION_KIND_NONE,
533 .kind = kind,
534 .dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
535 .data = buffer->data() + consumed,
536 };
537 blocks->push_back(inst);
538 consumed += INCFS_DATA_FILE_BLOCK_SIZE;
539 }
540 const auto remain = buffer->size() - fullBlocks * INCFS_DATA_FILE_BLOCK_SIZE;
541 if (remain && eof) {
542 const auto inst = IncFsDataBlock{
543 .fileFd = incfsFd.get(),
544 .pageIndex = (*blockIdx)++,
545 .compression = INCFS_COMPRESSION_KIND_NONE,
546 .kind = kind,
547 .dataSize = static_cast<uint16_t>(remain),
548 .data = buffer->data() + consumed,
549 };
550 blocks->push_back(inst);
551 consumed += remain;
552 }
553
554 auto res = mIfs->writeBlocks({blocks->data(), blocks->size()});
555
556 blocks->clear();
557 buffer->erase(buffer->begin(), buffer->begin() + consumed);
558
559 if (res < 0) {
560 ALOGE("Failed to write block to IncFS: %d", int(res));
561 return false;
562 }
563 return true;
564 }
565
566 // Read tracing.
567 struct TracedRead {
568 uint64_t timestampUs;
569 android::dataloader::FileId fileId;
570 uint32_t firstBlockIdx;
571 uint32_t count;
572 };
573
onPageReads(android::dataloader::PageReads pageReads)574 void onPageReads(android::dataloader::PageReads pageReads) final {
575 auto trace = atrace_is_tag_enabled(ATRACE_TAG);
576 if (CC_LIKELY(!trace)) {
577 return;
578 }
579
580 TracedRead last = {};
581 for (auto&& read : pageReads) {
582 if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) {
583 traceRead(last);
584 last = TracedRead{
585 .timestampUs = read.bootClockTsUs,
586 .fileId = read.id,
587 .firstBlockIdx = (uint32_t)read.block,
588 .count = 1,
589 };
590 } else {
591 ++last.count;
592 }
593 }
594 traceRead(last);
595 }
596
traceRead(const TracedRead & read)597 void traceRead(const TracedRead& read) {
598 if (!read.count) {
599 return;
600 }
601
602 FileIdx fileIdx = convertFileIdToFileIndex(read.fileId);
603 auto str = android::base::StringPrintf("page_read: index=%lld count=%lld file=%d",
604 static_cast<long long>(read.firstBlockIdx),
605 static_cast<long long>(read.count),
606 static_cast<int>(fileIdx));
607 ATRACE_BEGIN(str.c_str());
608 ATRACE_END();
609 }
610
611 // Streaming.
initStreaming(unique_fd inout,MetadataMode mode)612 bool initStreaming(unique_fd inout, MetadataMode mode) {
613 mEventFd.reset(eventfd(0, EFD_CLOEXEC));
614 if (mEventFd < 0) {
615 ALOGE("Failed to create eventfd.");
616 return false;
617 }
618
619 // Awaiting adb handshake.
620 char okay_buf[OKAY.size()];
621 if (!android::base::ReadFully(inout, okay_buf, OKAY.size())) {
622 ALOGE("Failed to receive OKAY. Abort.");
623 return false;
624 }
625 if (std::string_view(okay_buf, OKAY.size()) != OKAY) {
626 ALOGE("Received '%.*s', expecting '%.*s'", (int)OKAY.size(), okay_buf, (int)OKAY.size(),
627 OKAY.data());
628 return false;
629 }
630
631 {
632 std::lock_guard lock{mOutFdLock};
633 mOutFd.reset(::dup(inout));
634 if (mOutFd < 0) {
635 ALOGE("Failed to create streaming fd.");
636 }
637 }
638
639 mReceiverThread = std::thread(
640 [this, io = std::move(inout), mode]() mutable { receiver(std::move(io), mode); });
641 ALOGI("Started streaming...");
642 return true;
643 }
644
645 // IFS callbacks.
onPendingReads(dataloader::PendingReads pendingReads)646 void onPendingReads(dataloader::PendingReads pendingReads) final {
647 std::lock_guard lock{mOutFdLock};
648 if (mOutFd < 0) {
649 return;
650 }
651 CHECK(mIfs);
652 for (auto&& pendingRead : pendingReads) {
653 const android::dataloader::FileId& fileId = pendingRead.id;
654 const auto blockIdx = static_cast<BlockIdx>(pendingRead.block);
655 /*
656 ALOGI("Missing: %d", (int) blockIdx);
657 */
658 FileIdx fileIdx = convertFileIdToFileIndex(fileId);
659 if (fileIdx < 0) {
660 ALOGE("Failed to handle event for fileid=%s. Ignore.",
661 android::incfs::toString(fileId).c_str());
662 continue;
663 }
664 if (mRequestedFiles.insert(fileIdx).second &&
665 !sendRequest(mOutFd, PREFETCH, fileIdx, blockIdx)) {
666 mRequestedFiles.erase(fileIdx);
667 }
668 sendRequest(mOutFd, BLOCK_MISSING, fileIdx, blockIdx);
669 }
670 }
671
receiver(unique_fd inout,MetadataMode mode)672 void receiver(unique_fd inout, MetadataMode mode) {
673 std::vector<uint8_t> data;
674 std::vector<IncFsDataBlock> instructions;
675 std::unordered_map<FileIdx, unique_fd> writeFds;
676 while (!mStopReceiving) {
677 const int res = waitForDataOrSignal(inout, mEventFd);
678 if (res == 0) {
679 continue;
680 }
681 if (res < 0) {
682 ALOGE("Failed to poll. Abort.");
683 mStatusListener->reportStatus(DATA_LOADER_UNRECOVERABLE);
684 break;
685 }
686 if (res == mEventFd) {
687 ALOGE("Received stop signal. Sending EXIT to server.");
688 sendRequest(inout, EXIT);
689 break;
690 }
691 if (!readChunk(inout, data)) {
692 ALOGE("Failed to read a message. Abort.");
693 mStatusListener->reportStatus(DATA_LOADER_UNRECOVERABLE);
694 break;
695 }
696 auto remainingData = std::span(data);
697 while (!remainingData.empty()) {
698 auto header = readHeader(remainingData);
699 if (header.fileIdx == -1 && header.blockType == 0 && header.compressionType == 0 &&
700 header.blockIdx == 0 && header.blockSize == 0) {
701 ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).",
702 int(remainingData.size()));
703
704 sendRequest(inout, EXIT);
705 mStopReceiving = true;
706 break;
707 }
708 if (header.fileIdx < 0 || header.blockSize <= 0 || header.blockType < 0 ||
709 header.compressionType < 0 || header.blockIdx < 0) {
710 ALOGE("invalid header received. Abort.");
711 mStopReceiving = true;
712 break;
713 }
714 const FileIdx fileIdx = header.fileIdx;
715 const android::dataloader::FileId fileId = convertFileIndexToFileId(mode, fileIdx);
716 if (!android::incfs::isValidFileId(fileId)) {
717 ALOGE("Unknown data destination for file ID %d. "
718 "Ignore.",
719 header.fileIdx);
720 continue;
721 }
722
723 auto& writeFd = writeFds[fileIdx];
724 if (writeFd < 0) {
725 writeFd.reset(this->mIfs->openForSpecialOps(fileId).release());
726 if (writeFd < 0) {
727 ALOGE("Failed to open file %d for writing (%d). Aborting.", header.fileIdx,
728 -writeFd);
729 break;
730 }
731 }
732
733 const auto inst = IncFsDataBlock{
734 .fileFd = writeFd,
735 .pageIndex = static_cast<IncFsBlockIndex>(header.blockIdx),
736 .compression = static_cast<IncFsCompressionKind>(header.compressionType),
737 .kind = static_cast<IncFsBlockKind>(header.blockType),
738 .dataSize = static_cast<uint16_t>(header.blockSize),
739 .data = (const char*)remainingData.data(),
740 };
741 instructions.push_back(inst);
742 remainingData = remainingData.subspan(header.blockSize);
743 }
744 writeInstructions(instructions);
745 }
746 writeInstructions(instructions);
747
748 {
749 std::lock_guard lock{mOutFdLock};
750 mOutFd.reset();
751 }
752 }
753
writeInstructions(std::vector<IncFsDataBlock> & instructions)754 void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
755 auto res = this->mIfs->writeBlocks(instructions);
756 if (res != instructions.size()) {
757 ALOGE("Dailed to write data to Incfs (res=%d when expecting %d)", res,
758 int(instructions.size()));
759 }
760 instructions.clear();
761 }
762
convertFileIdToFileIndex(android::dataloader::FileId fileId)763 FileIdx convertFileIdToFileIndex(android::dataloader::FileId fileId) {
764 // FileId has format '\2FileIdx'.
765 const char* meta = (const char*)&fileId;
766
767 int8_t mode = *meta;
768 if (mode != DATA_ONLY_STREAMING && mode != STREAMING) {
769 return -1;
770 }
771
772 int fileIdx;
773 auto res = std::from_chars(meta + 1, meta + sizeof(fileId), fileIdx);
774 if (res.ec != std::errc{} || fileIdx < std::numeric_limits<FileIdx>::min() ||
775 fileIdx > std::numeric_limits<FileIdx>::max()) {
776 return -1;
777 }
778
779 return FileIdx(fileIdx);
780 }
781
convertFileIndexToFileId(MetadataMode mode,FileIdx fileIdx)782 android::dataloader::FileId convertFileIndexToFileId(MetadataMode mode, FileIdx fileIdx) {
783 IncFsFileId fileId = {};
784 char* meta = (char*)&fileId;
785 *meta = mode;
786 if (auto [p, ec] = std::to_chars(meta + 1, meta + sizeof(fileId), fileIdx);
787 ec != std::errc()) {
788 return {};
789 }
790 return fileId;
791 }
792
793 JavaVM* const mJvm;
794 std::string mArgs;
795 android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
796 android::dataloader::StatusListenerPtr mStatusListener = nullptr;
797 std::mutex mOutFdLock;
798 android::base::unique_fd mOutFd;
799 android::base::unique_fd mEventFd;
800 std::thread mReceiverThread;
801 std::atomic<bool> mStopReceiving = false;
802 std::atomic<bool> mReadLogsEnabled = false;
803 /** Tracks which files have been requested */
804 std::unordered_set<FileIdx> mRequestedFiles;
805 };
806
OnTraceChanged()807 OnTraceChanged::OnTraceChanged() {
808 mChecker = std::thread([this]() {
809 bool oldTrace = atrace_is_tag_enabled(ATRACE_TAG);
810 while (mRunning) {
811 bool newTrace = atrace_is_tag_enabled(ATRACE_TAG);
812 if (oldTrace != newTrace) {
813 std::unique_lock lock(mMutex);
814 for (auto&& callback : mCallbacks) {
815 callback->updateReadLogsState(newTrace);
816 }
817 }
818 oldTrace = newTrace;
819 std::this_thread::sleep_for(TraceTagCheckInterval);
820 }
821 });
822 }
823
readHeader(std::span<uint8_t> & data)824 BlockHeader readHeader(std::span<uint8_t>& data) {
825 BlockHeader header;
826 if (data.size() < sizeof(header)) {
827 return header;
828 }
829
830 header.fileIdx = static_cast<FileIdx>(be16toh(*reinterpret_cast<const uint16_t*>(&data[0])));
831 header.blockType = static_cast<BlockType>(data[2]);
832 header.compressionType = static_cast<CompressionType>(data[3]);
833 header.blockIdx = static_cast<BlockIdx>(be32toh(*reinterpret_cast<const uint32_t*>(&data[4])));
834 header.blockSize =
835 static_cast<BlockSize>(be16toh(*reinterpret_cast<const uint16_t*>(&data[8])));
836 data = data.subspan(sizeof(header));
837
838 return header;
839 }
840
nativeInitialize(JNIEnv * env,jclass klass)841 static void nativeInitialize(JNIEnv* env, jclass klass) {
842 jniIds(env);
843 }
844
845 static const JNINativeMethod method_table[] = {
846 {"nativeInitialize", "()V", (void*)nativeInitialize},
847 };
848
849 } // namespace
850
register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv * env)851 int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(
852 JNIEnv* env) {
853 android::dataloader::DataLoader::initialize(
854 [](auto jvm, const auto& params) -> android::dataloader::DataLoaderPtr {
855 if (params.type() == DATA_LOADER_TYPE_INCREMENTAL) {
856 // This DataLoader only supports incremental installations.
857 return std::make_unique<PMSCDataLoader>(jvm);
858 }
859 return {};
860 });
861 return jniRegisterNativeMethods(env,
862 "com/android/server/pm/PackageManagerShellCommandDataLoader",
863 method_table, NELEM(method_table));
864 }
865
866 } // namespace android
867