• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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