1 /*
2 * Copyright (C) 2022 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 #include "artd.h"
18
19 #include <fcntl.h>
20 #include <stdlib.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24
25 #include <algorithm>
26 #include <chrono>
27 #include <condition_variable>
28 #include <csignal>
29 #include <cstdint>
30 #include <cstdio>
31 #include <cstring>
32 #include <filesystem>
33 #include <functional>
34 #include <memory>
35 #include <mutex>
36 #include <optional>
37 #include <string>
38 #include <thread>
39 #include <tuple>
40 #include <type_traits>
41 #include <utility>
42 #include <vector>
43
44 #include "aidl/com/android/server/art/ArtConstants.h"
45 #include "aidl/com/android/server/art/BnArtd.h"
46 #include "android-base/collections.h"
47 #include "android-base/errors.h"
48 #include "android-base/file.h"
49 #include "android-base/logging.h"
50 #include "android-base/parseint.h"
51 #include "android-base/result-gmock.h"
52 #include "android-base/result.h"
53 #include "android-base/scopeguard.h"
54 #include "android-base/strings.h"
55 #include "android/binder_auto_utils.h"
56 #include "android/binder_status.h"
57 #include "base/array_ref.h"
58 #include "base/common_art_test.h"
59 #include "base/macros.h"
60 #include "base/pidfd.h"
61 #include "exec_utils.h"
62 #include "file_utils.h"
63 #include "gmock/gmock.h"
64 #include "gtest/gtest.h"
65 #include "oat/oat_file.h"
66 #include "path_utils.h"
67 #include "profile/profile_compilation_info.cc"
68 #include "profman/profman_result.h"
69 #include "testing.h"
70 #include "tools/binder_utils.h"
71 #include "tools/system_properties.h"
72 #include "tools/testing.h"
73 #include "vdex_file.h"
74 #include "ziparchive/zip_writer.h"
75
76 extern char** environ;
77
78 namespace art {
79 namespace artd {
80 namespace {
81
82 using ::aidl::com::android::server::art::ArtConstants;
83 using ::aidl::com::android::server::art::ArtdDexoptResult;
84 using ::aidl::com::android::server::art::ArtifactsPath;
85 using ::aidl::com::android::server::art::CopyAndRewriteProfileResult;
86 using ::aidl::com::android::server::art::DexMetadataPath;
87 using ::aidl::com::android::server::art::DexoptOptions;
88 using ::aidl::com::android::server::art::FileVisibility;
89 using ::aidl::com::android::server::art::FsPermission;
90 using ::aidl::com::android::server::art::IArtdCancellationSignal;
91 using ::aidl::com::android::server::art::IArtdNotification;
92 using ::aidl::com::android::server::art::OutputArtifacts;
93 using ::aidl::com::android::server::art::OutputProfile;
94 using ::aidl::com::android::server::art::PriorityClass;
95 using ::aidl::com::android::server::art::ProfilePath;
96 using ::aidl::com::android::server::art::RuntimeArtifactsPath;
97 using ::aidl::com::android::server::art::VdexPath;
98 using ::android::base::Append;
99 using ::android::base::Dirname;
100 using ::android::base::Error;
101 using ::android::base::make_scope_guard;
102 using ::android::base::ParseInt;
103 using ::android::base::ReadFdToString;
104 using ::android::base::ReadFileToString;
105 using ::android::base::Result;
106 using ::android::base::ScopeGuard;
107 using ::android::base::Split;
108 using ::android::base::WriteStringToFd;
109 using ::android::base::WriteStringToFile;
110 using ::android::base::testing::HasValue;
111 using ::art::tools::GetBin;
112 using ::art::tools::ScopedExec;
113 using ::testing::_;
114 using ::testing::AllOf;
115 using ::testing::AnyNumber;
116 using ::testing::AnyOf;
117 using ::testing::Contains;
118 using ::testing::ContainsRegex;
119 using ::testing::DoAll;
120 using ::testing::ElementsAre;
121 using ::testing::Field;
122 using ::testing::HasSubstr;
123 using ::testing::InSequence;
124 using ::testing::IsEmpty;
125 using ::testing::Matcher;
126 using ::testing::MockFunction;
127 using ::testing::NiceMock;
128 using ::testing::Not;
129 using ::testing::Property;
130 using ::testing::ResultOf;
131 using ::testing::Return;
132 using ::testing::SetArgPointee;
133 using ::testing::StrEq;
134 using ::testing::UnorderedElementsAreArray;
135 using ::testing::WithArg;
136
137 using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath;
138 using PrimaryRefProfilePath = ProfilePath::PrimaryRefProfilePath;
139 using TmpProfilePath = ProfilePath::TmpProfilePath;
140 using WritableProfilePath = ProfilePath::WritableProfilePath;
141
142 using std::literals::operator""s; // NOLINT
143
144 // User build is missing the SELinux permission for the test process (run as `shell`) to reopen
145 // the memfd that it creates itself
146 // (https://cs.android.com/android/platform/superproject/main/+/main:system/sepolicy/private/shell.te;l=221;drc=3335a04676d400bda57d42d4af0ef4b1d311de21).
147 #define TEST_DISABLED_FOR_SHELL_WITHOUT_MEMFD_ACCESS() TEST_DISABLED_FOR_USER_BUILD()
148
ScopedSetLogger(android::base::LogFunction && logger)149 ScopeGuard<std::function<void()>> ScopedSetLogger(android::base::LogFunction&& logger) {
150 android::base::LogFunction old_logger = android::base::SetLogger(std::move(logger));
151 return make_scope_guard([old_logger = std::move(old_logger)]() mutable {
152 android::base::SetLogger(std::move(old_logger));
153 });
154 }
155
CheckContent(const std::string & path,const std::string & expected_content)156 void CheckContent(const std::string& path, const std::string& expected_content) {
157 std::string actual_content;
158 ASSERT_TRUE(ReadFileToString(path, &actual_content));
159 EXPECT_EQ(actual_content, expected_content);
160 }
161
CheckOtherReadable(const std::string & path,bool expected_value)162 void CheckOtherReadable(const std::string& path, bool expected_value) {
163 EXPECT_EQ((std::filesystem::status(path).permissions() & std::filesystem::perms::others_read) !=
164 std::filesystem::perms::none,
165 expected_value);
166 }
167
GetFlagValues(ArrayRef<const std::string> args,std::string_view flag)168 Result<std::vector<std::string>> GetFlagValues(ArrayRef<const std::string> args,
169 std::string_view flag) {
170 std::vector<std::string> values;
171 for (const std::string& arg : args) {
172 std::string_view value(arg);
173 if (android::base::ConsumePrefix(&value, flag)) {
174 values.emplace_back(value);
175 }
176 }
177 if (values.empty()) {
178 return Errorf("Flag '{}' not found", flag);
179 }
180 return values;
181 }
182
GetFlagValue(ArrayRef<const std::string> args,std::string_view flag)183 Result<std::string> GetFlagValue(ArrayRef<const std::string> args, std::string_view flag) {
184 std::vector<std::string> flag_values = OR_RETURN(GetFlagValues(args, flag));
185 if (flag_values.size() > 1) {
186 return Errorf("Duplicate flag '{}'", flag);
187 }
188 return flag_values[0];
189 }
190
WriteToFdFlagImpl(const std::vector<std::string> & args,std::string_view flag,std::string_view content,bool assume_empty)191 void WriteToFdFlagImpl(const std::vector<std::string>& args,
192 std::string_view flag,
193 std::string_view content,
194 bool assume_empty) {
195 std::string value = OR_FAIL(GetFlagValue(ArrayRef<const std::string>(args), flag));
196 ASSERT_NE(value, "");
197 int fd;
198 ASSERT_TRUE(ParseInt(value, &fd));
199 if (assume_empty) {
200 ASSERT_EQ(lseek(fd, /*offset=*/0, SEEK_CUR), 0);
201 } else {
202 ASSERT_EQ(ftruncate(fd, /*length=*/0), 0);
203 ASSERT_EQ(lseek(fd, /*offset=*/0, SEEK_SET), 0);
204 }
205 ASSERT_TRUE(WriteStringToFd(content, fd));
206 }
207
208 // Writes `content` to the FD specified by the `flag`.
ACTION_P(WriteToFdFlag,flag,content)209 ACTION_P(WriteToFdFlag, flag, content) {
210 WriteToFdFlagImpl(arg0, flag, content, /*assume_empty=*/true);
211 }
212
213 // Clears any existing content and writes `content` to the FD specified by the `flag`.
ACTION_P(ClearAndWriteToFdFlag,flag,content)214 ACTION_P(ClearAndWriteToFdFlag, flag, content) {
215 WriteToFdFlagImpl(arg0, flag, content, /*assume_empty=*/false);
216 }
217
218 // Matches a flag that starts with `flag` and whose value matches `matcher`.
219 MATCHER_P2(Flag, flag, matcher, "") {
220 std::string_view value(arg);
221 if (!android::base::ConsumePrefix(&value, flag)) {
222 return false;
223 }
224 return ExplainMatchResult(matcher, std::string(value), result_listener);
225 }
226
227 // Matches a flag that starts with `flag` and whose value is a colon-separated list that matches
228 // `matcher`. The matcher acts on an `std::vector<std::string>` of the split list argument.
229 MATCHER_P2(ListFlag, flag, matcher, "") {
230 return ExplainMatchResult(
231 Flag(flag, ResultOf(std::bind(Split, std::placeholders::_1, ":"), matcher)),
232 arg,
233 result_listener);
234 }
235
236 // Matches an FD of a file whose path matches `matcher`.
237 MATCHER_P(FdOf, matcher, "") {
238 std::string proc_path = ART_FORMAT("/proc/self/fd/{}", arg);
239 char path[PATH_MAX];
240 ssize_t len = readlink(proc_path.c_str(), path, sizeof(path));
241 if (len < 0) {
242 return false;
243 }
244 return ExplainMatchResult(matcher, std::string(path, static_cast<size_t>(len)), result_listener);
245 }
246
247 // Matches an FD of a file whose content matches `matcher`.
248 MATCHER_P(FdHasContent, matcher, "") {
249 int fd;
250 if (!ParseInt(arg, &fd)) {
251 return false;
252 }
253 std::string actual_content;
254 if (!ReadFdToString(fd, &actual_content)) {
255 return false;
256 }
257 return ExplainMatchResult(matcher, actual_content, result_listener);
258 }
259
260 template <typename T, typename U>
SplitBy(const std::vector<T> & list,const U & separator)261 Result<std::pair<ArrayRef<const T>, ArrayRef<const T>>> SplitBy(const std::vector<T>& list,
262 const U& separator) {
263 auto it = std::find(list.begin(), list.end(), separator);
264 if (it == list.end()) {
265 return Errorf("'{}' not found", separator);
266 }
267 size_t pos = it - list.begin();
268 return std::make_pair(ArrayRef<const T>(list).SubArray(0, pos),
269 ArrayRef<const T>(list).SubArray(pos + 1));
270 }
271
272 // Matches a container that, when split by `separator`, the first part matches `head_matcher`, and
273 // the second part matches `tail_matcher`.
274 MATCHER_P3(WhenSplitBy, separator, head_matcher, tail_matcher, "") {
275 auto [head, tail] = OR_MISMATCH(SplitBy(arg, separator));
276 return ExplainMatchResult(head_matcher, head, result_listener) &&
277 ExplainMatchResult(tail_matcher, tail, result_listener);
278 }
279
280 MATCHER_P(HasKeepFdsForImpl, fd_flags, "") {
281 auto [head, tail] = OR_MISMATCH(SplitBy(arg, "--"));
282 std::string keep_fds_value = OR_MISMATCH(GetFlagValue(head, "--keep-fds="));
283 std::vector<std::string> keep_fds = Split(keep_fds_value, ":");
284 std::vector<std::string> fd_flag_values;
285 for (std::string_view fd_flag : fd_flags) {
286 for (const std::string& fd_flag_value : OR_MISMATCH(GetFlagValues(tail, fd_flag))) {
287 for (std::string& fd : Split(fd_flag_value, ":")) {
288 fd_flag_values.push_back(std::move(fd));
289 }
290 }
291 }
292 return ExplainMatchResult(UnorderedElementsAreArray(fd_flag_values), keep_fds, result_listener);
293 }
294
295 // Matches an argument list that has the "--keep-fds=" flag before "--", whose value is a
296 // semicolon-separated list that contains exactly the values of the given flags after "--".
297 //
298 // E.g., if the flags after "--" are "--foo=1", "--bar=2:3", "--baz=4", "--baz=5", and the matcher
299 // is `HasKeepFdsFor("--foo=", "--bar=", "--baz=")`, then it requires the "--keep-fds=" flag before
300 // "--" to contain exactly 1, 2, 3, 4, and 5.
301 template <typename... Args>
HasKeepFdsFor(Args &&...args)302 auto HasKeepFdsFor(Args&&... args) {
303 std::vector<std::string_view> fd_flags;
304 Append(fd_flags, std::forward<Args>(args)...);
305 return HasKeepFdsForImpl(fd_flags);
306 }
307
308 class MockSystemProperties : public tools::SystemProperties {
309 public:
310 MOCK_METHOD(std::string, GetProperty, (const std::string& key), (const, override));
311 };
312
313 class MockExecUtils : public ExecUtils {
314 public:
315 // A workaround to avoid MOCK_METHOD on a method with an `std::string*` parameter, which will lead
316 // to a conflict between gmock and android-base/logging.h (b/132668253).
ExecAndReturnResult(const std::vector<std::string> & arg_vector,int,const ExecCallbacks & callbacks,bool,ProcessStat * stat,std::string *) const317 ExecResult ExecAndReturnResult(const std::vector<std::string>& arg_vector,
318 int,
319 const ExecCallbacks& callbacks,
320 bool,
321 ProcessStat* stat,
322 std::string*) const override {
323 Result<int> code = DoExecAndReturnCode(arg_vector, callbacks, stat);
324 if (code.ok()) {
325 return {.status = ExecResult::kExited, .exit_code = code.value()};
326 } else {
327 return {.status = ExecResult::kSignaled, .signal = SIGKILL};
328 }
329 }
330
331 MOCK_METHOD(Result<int>,
332 DoExecAndReturnCode,
333 (const std::vector<std::string>& arg_vector,
334 const ExecCallbacks& callbacks,
335 ProcessStat* stat),
336 (const));
337 };
338
339 class ArtdTest : public CommonArtTest {
340 protected:
SetUp()341 void SetUp() override {
342 CommonArtTest::SetUp();
343 auto mock_props = std::make_unique<MockSystemProperties>();
344 mock_props_ = mock_props.get();
345 EXPECT_CALL(*mock_props_, GetProperty).Times(AnyNumber()).WillRepeatedly(Return(""));
346 auto mock_exec_utils = std::make_unique<MockExecUtils>();
347 mock_exec_utils_ = mock_exec_utils.get();
348 artd_ = ndk::SharedRefBase::make<Artd>(Options(),
349 std::move(mock_props),
350 std::move(mock_exec_utils),
351 mock_kill_.AsStdFunction(),
352 mock_fstat_.AsStdFunction(),
353 mock_poll_.AsStdFunction());
354 scratch_dir_ = std::make_unique<ScratchDir>();
355 scratch_path_ = scratch_dir_->GetPath();
356 // Remove the trailing '/';
357 scratch_path_.resize(scratch_path_.length() - 1);
358
359 TestOnlySetListRootDir(scratch_path_);
360
361 ON_CALL(mock_fstat_, Call).WillByDefault(fstat);
362
363 // Use an arbitrary existing directory as ART root.
364 art_root_ = scratch_path_ + "/com.android.art";
365 std::filesystem::create_directories(art_root_);
366 setenv("ANDROID_ART_ROOT", art_root_.c_str(), /*overwrite=*/1);
367
368 // Use an arbitrary existing directory as Android data.
369 android_data_ = scratch_path_ + "/data";
370 std::filesystem::create_directories(android_data_);
371 setenv("ANDROID_DATA", android_data_.c_str(), /*overwrite=*/1);
372
373 // Use an arbitrary existing directory as Android expand.
374 android_expand_ = scratch_path_ + "/mnt/expand";
375 std::filesystem::create_directories(android_expand_);
376 setenv("ANDROID_EXPAND", android_expand_.c_str(), /*overwrite=*/1);
377
378 dex_file_ = scratch_path_ + "/a/b.apk";
379 isa_ = "arm64";
380 artifacts_path_ = ArtifactsPath{
381 .dexPath = dex_file_,
382 .isa = isa_,
383 .isInDalvikCache = false,
384 };
385 struct stat st;
386 ASSERT_EQ(stat(scratch_path_.c_str(), &st), 0);
387 output_artifacts_ = OutputArtifacts{
388 .artifactsPath = artifacts_path_,
389 .permissionSettings =
390 OutputArtifacts::PermissionSettings{
391 .dirFsPermission =
392 FsPermission{
393 .uid = static_cast<int32_t>(st.st_uid),
394 .gid = static_cast<int32_t>(st.st_gid),
395 .isOtherReadable = true,
396 .isOtherExecutable = true,
397 },
398 .fileFsPermission =
399 FsPermission{
400 .uid = static_cast<int32_t>(st.st_uid),
401 .gid = static_cast<int32_t>(st.st_gid),
402 .isOtherReadable = true,
403 },
404 },
405 };
406 clc_1_ = GetTestDexFileName("Main");
407 clc_2_ = GetTestDexFileName("Nested");
408 class_loader_context_ = ART_FORMAT("PCL[{}:{}]", clc_1_, clc_2_);
409 compiler_filter_ = "speed";
410 tmp_profile_path_ =
411 TmpProfilePath{.finalPath = PrimaryRefProfilePath{.packageName = "com.android.foo",
412 .profileName = "primary",
413 .isPreReboot = false},
414 .id = "12345"};
415 profile_path_ = tmp_profile_path_;
416 vdex_path_ = artifacts_path_;
417 dm_path_ = DexMetadataPath{.dexPath = dex_file_};
418 std::filesystem::create_directories(
419 std::filesystem::path(OR_FATAL(BuildFinalProfilePath(tmp_profile_path_))).parent_path());
420 }
421
TearDown()422 void TearDown() override {
423 scratch_dir_.reset();
424 CommonArtTest::TearDown();
425 }
426
RunDexopt(binder_exception_t expected_status=EX_NONE,Matcher<ArtdDexoptResult> aidl_return_matcher=Field (& ArtdDexoptResult::cancelled,false),std::shared_ptr<IArtdCancellationSignal> cancellation_signal=nullptr)427 void RunDexopt(binder_exception_t expected_status = EX_NONE,
428 Matcher<ArtdDexoptResult> aidl_return_matcher = Field(&ArtdDexoptResult::cancelled,
429 false),
430 std::shared_ptr<IArtdCancellationSignal> cancellation_signal = nullptr) {
431 RunDexopt(Property(&ndk::ScopedAStatus::getExceptionCode, expected_status),
432 std::move(aidl_return_matcher),
433 std::move(cancellation_signal));
434 }
435
RunDexopt(Matcher<ndk::ScopedAStatus> status_matcher,Matcher<ArtdDexoptResult> aidl_return_matcher=Field (& ArtdDexoptResult::cancelled,false),std::shared_ptr<IArtdCancellationSignal> cancellation_signal=nullptr)436 void RunDexopt(Matcher<ndk::ScopedAStatus> status_matcher,
437 Matcher<ArtdDexoptResult> aidl_return_matcher = Field(&ArtdDexoptResult::cancelled,
438 false),
439 std::shared_ptr<IArtdCancellationSignal> cancellation_signal = nullptr) {
440 InitFilesBeforeDexopt();
441 if (cancellation_signal == nullptr) {
442 ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
443 }
444 ArtdDexoptResult aidl_return;
445 ndk::ScopedAStatus status = artd_->dexopt(output_artifacts_,
446 dex_file_,
447 isa_,
448 class_loader_context_,
449 compiler_filter_,
450 profile_path_,
451 vdex_path_,
452 dm_path_,
453 priority_class_,
454 dexopt_options_,
455 cancellation_signal,
456 &aidl_return);
457 ASSERT_THAT(status, std::move(status_matcher)) << status.getMessage();
458 if (status.isOk()) {
459 ASSERT_THAT(aidl_return, std::move(aidl_return_matcher));
460 }
461 }
462
463 template <bool kExpectOk>
464 using RunCopyAndRewriteProfileResult = Result<
465 std::pair<std::conditional_t<kExpectOk, CopyAndRewriteProfileResult, ndk::ScopedAStatus>,
466 OutputProfile>>;
467
468 // Runs `copyAndRewriteProfile` with `profile_path_` and `dex_file_`.
469 template <bool kExpectOk = true>
RunCopyAndRewriteProfile()470 RunCopyAndRewriteProfileResult<kExpectOk> RunCopyAndRewriteProfile() {
471 OutputProfile dst{.profilePath = tmp_profile_path_,
472 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
473 dst.profilePath.id = "";
474 dst.profilePath.tmpPath = "";
475
476 CopyAndRewriteProfileResult result;
477 ndk::ScopedAStatus status =
478 artd_->copyAndRewriteProfile(profile_path_.value(), &dst, dex_file_, &result);
479 if constexpr (kExpectOk) {
480 if (!status.isOk()) {
481 return Error() << status.getMessage();
482 }
483 return std::make_pair(std::move(result), std::move(dst));
484 } else {
485 return std::make_pair(std::move(status), std::move(dst));
486 }
487 }
488
489 // Runs `copyAndRewriteEmbeddedProfile` with `dex_file_`.
490 template <bool kExpectOk = true>
RunCopyAndRewriteEmbeddedProfile()491 RunCopyAndRewriteProfileResult<kExpectOk> RunCopyAndRewriteEmbeddedProfile() {
492 OutputProfile dst{.profilePath = tmp_profile_path_,
493 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
494 dst.profilePath.id = "";
495 dst.profilePath.tmpPath = "";
496
497 CopyAndRewriteProfileResult result;
498 ndk::ScopedAStatus status = artd_->copyAndRewriteEmbeddedProfile(&dst, dex_file_, &result);
499 if constexpr (kExpectOk) {
500 if (!status.isOk()) {
501 return Error() << status.getMessage();
502 }
503 return std::make_pair(std::move(result), std::move(dst));
504 } else {
505 return std::make_pair(std::move(status), std::move(dst));
506 }
507 }
508
CreateFile(const std::string & filename,const std::string & content="")509 void CreateFile(const std::string& filename, const std::string& content = "") {
510 std::filesystem::path path(filename);
511 std::filesystem::create_directories(path.parent_path());
512 ASSERT_TRUE(WriteStringToFile(content, filename));
513 }
514
CreateZipWithSingleEntry(const std::string & filename,const std::string & entry_name,const std::string & content="")515 void CreateZipWithSingleEntry(const std::string& filename,
516 const std::string& entry_name,
517 const std::string& content = "") {
518 std::filesystem::path path(filename);
519 std::filesystem::create_directories(path.parent_path());
520 std::unique_ptr<File> file(OS::CreateEmptyFileWriteOnly(filename.c_str()));
521 ASSERT_NE(file, nullptr) << strerror(errno);
522 file->MarkUnchecked(); // `writer.Finish()` flushes the file and the destructor closes it.
523 ZipWriter writer(fdopen(file->Fd(), "wb"));
524 ASSERT_EQ(writer.StartEntry(entry_name, /*flags=*/0), 0);
525 ASSERT_EQ(writer.WriteBytes(content.c_str(), content.size()), 0);
526 ASSERT_EQ(writer.FinishEntry(), 0);
527 ASSERT_EQ(writer.Finish(), 0);
528 }
529
530 std::shared_ptr<Artd> artd_;
531 std::unique_ptr<ScratchDir> scratch_dir_;
532 std::string scratch_path_;
533 std::string art_root_;
534 std::string android_data_;
535 std::string android_expand_;
536 MockFunction<android::base::LogFunction> mock_logger_;
537 ScopedUnsetEnvironmentVariable art_root_env_ = ScopedUnsetEnvironmentVariable("ANDROID_ART_ROOT");
538 ScopedUnsetEnvironmentVariable android_data_env_ = ScopedUnsetEnvironmentVariable("ANDROID_DATA");
539 ScopedUnsetEnvironmentVariable android_expand_env_ =
540 ScopedUnsetEnvironmentVariable("ANDROID_EXPAND");
541 MockSystemProperties* mock_props_;
542 MockExecUtils* mock_exec_utils_;
543 MockFunction<KillFn> mock_kill_;
544 MockFunction<FstatFn> mock_fstat_;
545 MockFunction<PollFn> mock_poll_;
546
547 std::string dex_file_;
548 std::string isa_;
549 ArtifactsPath artifacts_path_;
550 OutputArtifacts output_artifacts_;
551 std::string clc_1_;
552 std::string clc_2_;
553 std::optional<std::string> class_loader_context_;
554 std::string compiler_filter_;
555 std::optional<VdexPath> vdex_path_;
556 std::optional<DexMetadataPath> dm_path_;
557 PriorityClass priority_class_ = PriorityClass::BACKGROUND;
558 DexoptOptions dexopt_options_;
559 std::optional<ProfilePath> profile_path_;
560 TmpProfilePath tmp_profile_path_;
561 bool dex_file_other_readable_ = true;
562 bool profile_other_readable_ = true;
563
564 private:
InitFilesBeforeDexopt()565 void InitFilesBeforeDexopt() {
566 // Required files.
567 CreateFile(dex_file_);
568 std::filesystem::permissions(dex_file_,
569 std::filesystem::perms::others_read,
570 dex_file_other_readable_ ? std::filesystem::perm_options::add :
571 std::filesystem::perm_options::remove);
572
573 // Optional files.
574 if (vdex_path_.has_value()) {
575 CreateFile(OR_FATAL(BuildVdexPath(vdex_path_.value())), "old_vdex");
576 }
577 if (dm_path_.has_value()) {
578 CreateFile(OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
579 }
580 if (profile_path_.has_value()) {
581 std::string path = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
582 CreateFile(path);
583 std::filesystem::permissions(path,
584 std::filesystem::perms::others_read,
585 profile_other_readable_ ? std::filesystem::perm_options::add :
586 std::filesystem::perm_options::remove);
587 }
588
589 // Files to be replaced.
590 RawArtifactsPath artifacts_path = OR_FATAL(BuildArtifactsPath(artifacts_path_));
591 CreateFile(artifacts_path.oat_path, "old_oat");
592 CreateFile(artifacts_path.vdex_path, "old_vdex");
593 CreateFile(artifacts_path.art_path, "old_art");
594 }
595 };
596
TEST_F(ArtdTest,ConstantsAreInSync)597 TEST_F(ArtdTest, ConstantsAreInSync) {
598 EXPECT_STREQ(ArtConstants::REASON_VDEX, kReasonVdex);
599 EXPECT_STREQ(ArtConstants::DEX_METADATA_FILE_EXT, kDmExtension);
600 EXPECT_STREQ(ArtConstants::SECURE_DEX_METADATA_FILE_EXT, kSdmExtension);
601 EXPECT_STREQ(ArtConstants::DEX_METADATA_PROFILE_ENTRY,
602 ProfileCompilationInfo::kDexMetadataProfileEntry);
603 EXPECT_STREQ(ArtConstants::DEX_METADATA_VDEX_ENTRY, VdexFile::kVdexNameInDmFile);
604 }
605
TEST_F(ArtdTest,isAlive)606 TEST_F(ArtdTest, isAlive) {
607 bool result = false;
608 artd_->isAlive(&result);
609 EXPECT_TRUE(result);
610 }
611
TEST_F(ArtdTest,deleteArtifacts)612 TEST_F(ArtdTest, deleteArtifacts) {
613 std::string oat_dir = scratch_path_ + "/a/oat/arm64";
614 std::filesystem::create_directories(oat_dir);
615 ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
616 ASSERT_TRUE(WriteStringToFile("ab", oat_dir + "/b.vdex")); // 2 bytes.
617 ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
618
619 int64_t result = -1;
620 EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
621 EXPECT_EQ(result, 4 + 2 + 1);
622
623 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.odex"));
624 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.vdex"));
625 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.art"));
626 }
627
TEST_F(ArtdTest,deleteArtifactsMissingFile)628 TEST_F(ArtdTest, deleteArtifactsMissingFile) {
629 // Missing VDEX file.
630 std::string oat_dir = android_data_ + "/dalvik-cache/arm64";
631 std::filesystem::create_directories(oat_dir);
632 ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/[email protected]@classes.dex")); // 4 bytes.
633 ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/[email protected]@classes.art")); // 1 byte.
634
635 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
636 EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(0);
637
638 int64_t result = -1;
639 EXPECT_TRUE(artd_
640 ->deleteArtifacts(
641 ArtifactsPath{
642 .dexPath = "/a/b.apk",
643 .isa = "arm64",
644 .isInDalvikCache = true,
645 },
646 &result)
647 .isOk());
648 EXPECT_EQ(result, 4 + 1);
649
650 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/[email protected]@classes.dex"));
651 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/[email protected]@classes.art"));
652 }
653
TEST_F(ArtdTest,deleteArtifactsNoFile)654 TEST_F(ArtdTest, deleteArtifactsNoFile) {
655 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
656 EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(0);
657
658 int64_t result = -1;
659 EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
660 EXPECT_EQ(result, 0);
661 }
662
TEST_F(ArtdTest,deleteArtifactsPermissionDenied)663 TEST_F(ArtdTest, deleteArtifactsPermissionDenied) {
664 std::string oat_dir = scratch_path_ + "/a/oat/arm64";
665 std::filesystem::create_directories(oat_dir);
666 ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
667 ASSERT_TRUE(WriteStringToFile("ab", oat_dir + "/b.vdex")); // 2 bytes.
668 ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
669
670 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
671 EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(3);
672
673 auto scoped_inaccessible = ScopedInaccessible(oat_dir);
674 auto scoped_unroot = ScopedUnroot();
675
676 int64_t result = -1;
677 EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
678 EXPECT_EQ(result, 0);
679 }
680
TEST_F(ArtdTest,deleteArtifactsFileIsDir)681 TEST_F(ArtdTest, deleteArtifactsFileIsDir) {
682 // VDEX file is a directory.
683 std::string oat_dir = scratch_path_ + "/a/oat/arm64";
684 std::filesystem::create_directories(oat_dir);
685 std::filesystem::create_directories(oat_dir + "/b.vdex");
686 ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
687 ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
688
689 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
690 EXPECT_CALL(mock_logger_,
691 Call(_, _, _, _, _, ContainsRegex(R"re(Failed to get the file size.*b\.vdex)re")))
692 .Times(1);
693
694 int64_t result = -1;
695 EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
696 EXPECT_EQ(result, 4 + 1);
697
698 // The directory is kept because getting the file size failed.
699 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.odex"));
700 EXPECT_TRUE(std::filesystem::exists(oat_dir + "/b.vdex"));
701 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.art"));
702 }
703
TEST_F(ArtdTest,dexopt)704 TEST_F(ArtdTest, dexopt) {
705 dexopt_options_.generateAppImage = true;
706
707 EXPECT_CALL(
708 *mock_exec_utils_,
709 DoExecAndReturnCode(
710 AllOf(WhenSplitBy(
711 "--",
712 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
713 AllOf(Contains(art_root_ + "/bin/dex2oat32"),
714 Contains(Flag("--zip-fd=", FdOf(dex_file_))),
715 Contains(Flag("--zip-location=", dex_file_)),
716 Contains(Flag("--oat-location=", scratch_path_ + "/a/oat/arm64/b.odex")),
717 Contains(Flag("--instruction-set=", "arm64")),
718 Contains(Flag("--compiler-filter=", "speed")),
719 Contains(Flag(
720 "--profile-file-fd=",
721 FdOf(android_data_ +
722 "/misc/profiles/ref/com.android.foo/primary.prof.12345.tmp"))),
723 Contains(Flag("--input-vdex-fd=",
724 FdOf(scratch_path_ + "/a/oat/arm64/b.vdex"))),
725 Contains(Flag("--dm-fd=", FdOf(scratch_path_ + "/a/b.dm"))))),
726 HasKeepFdsFor("--zip-fd=",
727 "--profile-file-fd=",
728 "--input-vdex-fd=",
729 "--dm-fd=",
730 "--oat-fd=",
731 "--output-vdex-fd=",
732 "--app-image-fd=",
733 "--class-loader-context-fds=",
734 "--swap-fd=")),
735 _,
736 _))
737 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "oat")),
738 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "vdex")),
739 WithArg<0>(WriteToFdFlag("--app-image-fd=", "art")),
740 SetArgPointee<2>(ProcessStat{.wall_time_ms = 100, .cpu_time_ms = 400}),
741 Return(0)));
742 RunDexopt(
743 EX_NONE,
744 AllOf(Field(&ArtdDexoptResult::cancelled, false),
745 Field(&ArtdDexoptResult::wallTimeMs, 100),
746 Field(&ArtdDexoptResult::cpuTimeMs, 400),
747 Field(&ArtdDexoptResult::sizeBytes, strlen("art") + strlen("oat") + strlen("vdex")),
748 Field(&ArtdDexoptResult::sizeBeforeBytes,
749 strlen("old_art") + strlen("old_oat") + strlen("old_vdex"))));
750
751 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "oat");
752 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "vdex");
753 CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "art");
754 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.odex", true);
755 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.vdex", true);
756 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.art", true);
757 }
758
TEST_F(ArtdTest,dexoptClassLoaderContext)759 TEST_F(ArtdTest, dexoptClassLoaderContext) {
760 EXPECT_CALL(
761 *mock_exec_utils_,
762 DoExecAndReturnCode(
763 WhenSplitBy("--",
764 _,
765 AllOf(Contains(ListFlag("--class-loader-context-fds=",
766 ElementsAre(FdOf(clc_1_), FdOf(clc_2_)))),
767 Contains(Flag("--class-loader-context=", class_loader_context_)),
768 Contains(Flag("--classpath-dir=", scratch_path_ + "/a")))),
769 _,
770 _))
771 .WillOnce(Return(0));
772 RunDexopt();
773 }
774
TEST_F(ArtdTest,dexoptClassLoaderContextNull)775 TEST_F(ArtdTest, dexoptClassLoaderContextNull) {
776 class_loader_context_ = std::nullopt;
777
778 EXPECT_CALL(
779 *mock_exec_utils_,
780 DoExecAndReturnCode(WhenSplitBy("--",
781 _,
782 AllOf(Not(Contains(Flag("--class-loader-context-fds=", _))),
783 Not(Contains(Flag("--class-loader-context=", _))),
784 Not(Contains(Flag("--classpath-dir=", _))))),
785 _,
786 _))
787 .WillOnce(Return(0));
788 RunDexopt();
789 }
790
TEST_F(ArtdTest,dexoptNoOptionalInputFiles)791 TEST_F(ArtdTest, dexoptNoOptionalInputFiles) {
792 profile_path_ = std::nullopt;
793 vdex_path_ = std::nullopt;
794 dm_path_ = std::nullopt;
795
796 EXPECT_CALL(*mock_exec_utils_,
797 DoExecAndReturnCode(WhenSplitBy("--",
798 _,
799 AllOf(Not(Contains(Flag("--profile-file-fd=", _))),
800 Not(Contains(Flag("--input-vdex-fd=", _))),
801 Not(Contains(Flag("--dm-fd=", _))))),
802 _,
803 _))
804 .WillOnce(Return(0));
805 RunDexopt();
806 }
807
TEST_F(ArtdTest,dexoptPriorityClassBoot)808 TEST_F(ArtdTest, dexoptPriorityClassBoot) {
809 priority_class_ = PriorityClass::BOOT;
810 EXPECT_CALL(*mock_exec_utils_,
811 DoExecAndReturnCode(WhenSplitBy("--",
812 AllOf(Not(Contains(Flag("--set-task-profile=", _))),
813 Not(Contains(Flag("--set-priority=", _)))),
814 Contains(Flag("--compact-dex-level=", "none"))),
815 _,
816 _))
817 .WillOnce(Return(0));
818 RunDexopt();
819 }
820
TEST_F(ArtdTest,dexoptPriorityClassInteractive)821 TEST_F(ArtdTest, dexoptPriorityClassInteractive) {
822 priority_class_ = PriorityClass::INTERACTIVE;
823 EXPECT_CALL(*mock_exec_utils_,
824 DoExecAndReturnCode(
825 WhenSplitBy("--",
826 AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBootComplete")),
827 Contains(Flag("--set-priority=", "background"))),
828 Contains(Flag("--compact-dex-level=", "none"))),
829 _,
830 _))
831 .WillOnce(Return(0));
832 RunDexopt();
833 }
834
TEST_F(ArtdTest,dexoptPriorityClassInteractiveFast)835 TEST_F(ArtdTest, dexoptPriorityClassInteractiveFast) {
836 priority_class_ = PriorityClass::INTERACTIVE_FAST;
837 EXPECT_CALL(*mock_exec_utils_,
838 DoExecAndReturnCode(
839 WhenSplitBy("--",
840 AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBootComplete")),
841 Contains(Flag("--set-priority=", "background"))),
842 Contains(Flag("--compact-dex-level=", "none"))),
843 _,
844 _))
845 .WillOnce(Return(0));
846 RunDexopt();
847 }
848
TEST_F(ArtdTest,dexoptPriorityClassBackground)849 TEST_F(ArtdTest, dexoptPriorityClassBackground) {
850 priority_class_ = PriorityClass::BACKGROUND;
851 EXPECT_CALL(*mock_exec_utils_,
852 DoExecAndReturnCode(
853 WhenSplitBy("--",
854 AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBackground")),
855 Contains(Flag("--set-priority=", "background"))),
856 Not(Contains(Flag("--compact-dex-level=", _)))),
857 _,
858 _))
859 .WillOnce(Return(0));
860 RunDexopt();
861 }
862
TEST_F(ArtdTest,dexoptDexoptOptions)863 TEST_F(ArtdTest, dexoptDexoptOptions) {
864 dexopt_options_ = DexoptOptions{
865 .compilationReason = "install",
866 .targetSdkVersion = 123,
867 .debuggable = false,
868 .generateAppImage = false,
869 .hiddenApiPolicyEnabled = false,
870 .comments = "my-comments",
871 };
872
873 EXPECT_CALL(
874 *mock_exec_utils_,
875 DoExecAndReturnCode(WhenSplitBy("--",
876 _,
877 AllOf(Contains(Flag("--compilation-reason=", "install")),
878 Contains(Flag("-Xtarget-sdk-version:", "123")),
879 Not(Contains("--debuggable")),
880 Not(Contains(Flag("--app-image-fd=", _))),
881 Not(Contains(Flag("-Xhidden-api-policy:", _))),
882 Contains(Flag("--comments=", "my-comments")))),
883 _,
884 _))
885 .WillOnce(Return(0));
886
887 // `sizeBeforeBytes` should include the size of the old ART file even if no new ART file is
888 // generated.
889 RunDexopt(EX_NONE,
890 Field(&ArtdDexoptResult::sizeBeforeBytes,
891 strlen("old_art") + strlen("old_oat") + strlen("old_vdex")));
892 }
893
TEST_F(ArtdTest,dexoptDexoptOptions2)894 TEST_F(ArtdTest, dexoptDexoptOptions2) {
895 dexopt_options_ = DexoptOptions{
896 .compilationReason = "bg-dexopt",
897 .targetSdkVersion = 456,
898 .debuggable = true,
899 .generateAppImage = true,
900 .hiddenApiPolicyEnabled = true,
901 };
902
903 EXPECT_CALL(
904 *mock_exec_utils_,
905 DoExecAndReturnCode(WhenSplitBy("--",
906 _,
907 AllOf(Contains(Flag("--compilation-reason=", "bg-dexopt")),
908 Contains(Flag("-Xtarget-sdk-version:", "456")),
909 Contains("--debuggable"),
910 Contains(Flag("--app-image-fd=", _)),
911 Contains(Flag("-Xhidden-api-policy:", "enabled")))),
912 _,
913 _))
914 .WillOnce(Return(0));
915
916 RunDexopt();
917 }
918
TEST_F(ArtdTest,dexoptDefaultFlagsWhenNoSystemProps)919 TEST_F(ArtdTest, dexoptDefaultFlagsWhenNoSystemProps) {
920 dexopt_options_.generateAppImage = true;
921
922 EXPECT_CALL(*mock_exec_utils_,
923 DoExecAndReturnCode(
924 WhenSplitBy("--",
925 _,
926 AllOf(Contains(Flag("--swap-fd=", FdOf(_))),
927 Not(Contains(Flag("--instruction-set-features=", _))),
928 Not(Contains(Flag("--instruction-set-variant=", _))),
929 Not(Contains(Flag("--max-image-block-size=", _))),
930 Not(Contains(Flag("--very-large-app-threshold=", _))),
931 Not(Contains(Flag("--resolve-startup-const-strings=", _))),
932 Not(Contains("--generate-debug-info")),
933 Not(Contains("--generate-mini-debug-info")),
934 Contains("-Xdeny-art-apex-data-files"),
935 Not(Contains(Flag("--cpu-set=", _))),
936 Not(Contains(Flag("-j", _))),
937 Not(Contains(Flag("-Xms", _))),
938 Not(Contains(Flag("-Xmx", _))),
939 Not(Contains("--compile-individually")),
940 Not(Contains(Flag("--image-format=", _))),
941 Not(Contains("--force-jit-zygote")),
942 Not(Contains(Flag("--boot-image=", _))))),
943 _,
944 _))
945 .WillOnce(Return(0));
946 RunDexopt();
947 }
948
TEST_F(ArtdTest,dexoptFlagsFromSystemProps)949 TEST_F(ArtdTest, dexoptFlagsFromSystemProps) {
950 dexopt_options_.generateAppImage = true;
951
952 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-swap")).WillOnce(Return("0"));
953 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.isa.arm64.features"))
954 .WillOnce(Return("features"));
955 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.isa.arm64.variant")).WillOnce(Return("variant"));
956 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-max-image-block-size"))
957 .WillOnce(Return("size"));
958 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-very-large"))
959 .WillOnce(Return("threshold"));
960 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-resolve-startup-strings"))
961 .WillOnce(Return("strings"));
962 EXPECT_CALL(*mock_props_, GetProperty("debug.generate-debug-info")).WillOnce(Return("1"));
963 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-minidebuginfo")).WillOnce(Return("1"));
964 EXPECT_CALL(*mock_props_, GetProperty("odsign.verification.success")).WillOnce(Return("1"));
965 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-Xms")).WillOnce(Return("xms"));
966 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-Xmx")).WillOnce(Return("xmx"));
967 EXPECT_CALL(*mock_props_, GetProperty("ro.config.low_ram")).WillOnce(Return("1"));
968 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.appimageformat")).WillOnce(Return("imgfmt"));
969 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.boot-image")).WillOnce(Return("boot-image"));
970 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-flags"))
971 .WillOnce(Return("--flag1 --flag2 --flag3"));
972
973 EXPECT_CALL(*mock_exec_utils_,
974 DoExecAndReturnCode(
975 WhenSplitBy("--",
976 _,
977 AllOf(Not(Contains(Flag("--swap-fd=", _))),
978 Contains(Flag("--instruction-set-features=", "features")),
979 Contains(Flag("--instruction-set-variant=", "variant")),
980 Contains(Flag("--max-image-block-size=", "size")),
981 Contains(Flag("--very-large-app-threshold=", "threshold")),
982 Contains(Flag("--resolve-startup-const-strings=", "strings")),
983 Contains("--generate-debug-info"),
984 Contains("--generate-mini-debug-info"),
985 Not(Contains("-Xdeny-art-apex-data-files")),
986 Contains(Flag("-Xms", "xms")),
987 Contains(Flag("-Xmx", "xmx")),
988 Contains("--compile-individually"),
989 Contains(Flag("--image-format=", "imgfmt")),
990 Not(Contains("--force-jit-zygote")),
991 Contains(Flag("--boot-image=", "boot-image")),
992 Contains("--flag1"),
993 Contains("--flag2"),
994 Contains("--flag3"))),
995 _,
996 _))
997 .WillOnce(Return(0));
998 RunDexopt();
999 }
1000
TEST_F(ArtdTest,dexoptFlagsForceJitZygote)1001 TEST_F(ArtdTest, dexoptFlagsForceJitZygote) {
1002 EXPECT_CALL(*mock_props_,
1003 GetProperty("persist.device_config.runtime_native_boot.profilebootclasspath"))
1004 .WillOnce(Return("true"));
1005 ON_CALL(*mock_props_, GetProperty("dalvik.vm.boot-image")).WillByDefault(Return("boot-image"));
1006
1007 EXPECT_CALL(*mock_exec_utils_,
1008 DoExecAndReturnCode(WhenSplitBy("--",
1009 _,
1010 AllOf(Contains("--force-jit-zygote"),
1011 Not(Contains(Flag("--boot-image=", _))))),
1012 _,
1013 _))
1014 .WillOnce(Return(0));
1015 RunDexopt();
1016 }
1017
SetDefaultResourceControlProps(MockSystemProperties * mock_props)1018 static void SetDefaultResourceControlProps(MockSystemProperties* mock_props) {
1019 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-cpu-set")).WillRepeatedly(Return("0,2"));
1020 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-threads")).WillRepeatedly(Return("4"));
1021 }
1022
TEST_F(ArtdTest,dexoptDefaultResourceControlBoot)1023 TEST_F(ArtdTest, dexoptDefaultResourceControlBoot) {
1024 SetDefaultResourceControlProps(mock_props_);
1025
1026 // The default resource control properties don't apply to BOOT.
1027 EXPECT_CALL(
1028 *mock_exec_utils_,
1029 DoExecAndReturnCode(
1030 WhenSplitBy(
1031 "--", _, AllOf(Not(Contains(Flag("--cpu-set=", _))), Contains(Not(Flag("-j", _))))),
1032 _,
1033 _))
1034 .WillOnce(Return(0));
1035 priority_class_ = PriorityClass::BOOT;
1036 RunDexopt();
1037 }
1038
TEST_F(ArtdTest,dexoptDefaultResourceControlOther)1039 TEST_F(ArtdTest, dexoptDefaultResourceControlOther) {
1040 SetDefaultResourceControlProps(mock_props_);
1041
1042 EXPECT_CALL(
1043 *mock_exec_utils_,
1044 DoExecAndReturnCode(
1045 WhenSplitBy(
1046 "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2")), Contains(Flag("-j", "4")))),
1047 _,
1048 _))
1049 .Times(3)
1050 .WillRepeatedly(Return(0));
1051 priority_class_ = PriorityClass::INTERACTIVE_FAST;
1052 RunDexopt();
1053 priority_class_ = PriorityClass::INTERACTIVE;
1054 RunDexopt();
1055 priority_class_ = PriorityClass::BACKGROUND;
1056 RunDexopt();
1057 }
1058
SetAllResourceControlProps(MockSystemProperties * mock_props)1059 static void SetAllResourceControlProps(MockSystemProperties* mock_props) {
1060 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-cpu-set")).WillRepeatedly(Return("0,2"));
1061 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-threads")).WillRepeatedly(Return("4"));
1062 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.boot-dex2oat-cpu-set"))
1063 .WillRepeatedly(Return("0,1,2,3"));
1064 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.boot-dex2oat-threads"))
1065 .WillRepeatedly(Return("8"));
1066 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.restore-dex2oat-cpu-set"))
1067 .WillRepeatedly(Return("0,2,3"));
1068 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.restore-dex2oat-threads"))
1069 .WillRepeatedly(Return("6"));
1070 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.background-dex2oat-cpu-set"))
1071 .WillRepeatedly(Return("0"));
1072 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.background-dex2oat-threads"))
1073 .WillRepeatedly(Return("2"));
1074 }
1075
TEST_F(ArtdTest,dexoptAllResourceControlBoot)1076 TEST_F(ArtdTest, dexoptAllResourceControlBoot) {
1077 SetAllResourceControlProps(mock_props_);
1078
1079 EXPECT_CALL(
1080 *mock_exec_utils_,
1081 DoExecAndReturnCode(
1082 WhenSplitBy(
1083 "--", _, AllOf(Contains(Flag("--cpu-set=", "0,1,2,3")), Contains(Flag("-j", "8")))),
1084 _,
1085 _))
1086 .WillOnce(Return(0));
1087 priority_class_ = PriorityClass::BOOT;
1088 RunDexopt();
1089 }
1090
TEST_F(ArtdTest,dexoptAllResourceControlInteractiveFast)1091 TEST_F(ArtdTest, dexoptAllResourceControlInteractiveFast) {
1092 SetAllResourceControlProps(mock_props_);
1093
1094 EXPECT_CALL(
1095 *mock_exec_utils_,
1096 DoExecAndReturnCode(
1097 WhenSplitBy(
1098 "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2,3")), Contains(Flag("-j", "6")))),
1099 _,
1100 _))
1101 .WillOnce(Return(0));
1102 priority_class_ = PriorityClass::INTERACTIVE_FAST;
1103 RunDexopt();
1104 }
1105
TEST_F(ArtdTest,dexoptAllResourceControlInteractive)1106 TEST_F(ArtdTest, dexoptAllResourceControlInteractive) {
1107 SetAllResourceControlProps(mock_props_);
1108
1109 // INTERACTIVE always uses the default resource control properties.
1110 EXPECT_CALL(
1111 *mock_exec_utils_,
1112 DoExecAndReturnCode(
1113 WhenSplitBy(
1114 "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2")), Contains(Flag("-j", "4")))),
1115 _,
1116 _))
1117 .WillOnce(Return(0));
1118 priority_class_ = PriorityClass::INTERACTIVE;
1119 RunDexopt();
1120 }
1121
TEST_F(ArtdTest,dexoptAllResourceControlBackground)1122 TEST_F(ArtdTest, dexoptAllResourceControlBackground) {
1123 SetAllResourceControlProps(mock_props_);
1124
1125 EXPECT_CALL(
1126 *mock_exec_utils_,
1127 DoExecAndReturnCode(
1128 WhenSplitBy("--", _, AllOf(Contains(Flag("--cpu-set=", "0")), Contains(Flag("-j", "2")))),
1129 _,
1130 _))
1131 .WillOnce(Return(0));
1132 priority_class_ = PriorityClass::BACKGROUND;
1133 RunDexopt();
1134 }
1135
TEST_F(ArtdTest,dexoptTerminatedBySignal)1136 TEST_F(ArtdTest, dexoptTerminatedBySignal) {
1137 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1138 .WillOnce(Return(Result<int>(Error())));
1139 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1140 Property(&ndk::ScopedAStatus::getMessage,
1141 HasSubstr(ART_FORMAT("[status={},exit_code=-1,signal={}]",
1142 static_cast<int>(ExecResult::kSignaled),
1143 SIGKILL)))));
1144 }
1145
TEST_F(ArtdTest,dexoptFailed)1146 TEST_F(ArtdTest, dexoptFailed) {
1147 dexopt_options_.generateAppImage = true;
1148 constexpr int kExitCode = 135;
1149 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1150 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
1151 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
1152 WithArg<0>(WriteToFdFlag("--app-image-fd=", "new_art")),
1153 Return(kExitCode)));
1154 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1155 Property(&ndk::ScopedAStatus::getMessage,
1156 HasSubstr(ART_FORMAT("[status={},exit_code={},signal=0]",
1157 static_cast<int>(ExecResult::kExited),
1158 kExitCode)))));
1159
1160 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
1161 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
1162 CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
1163 }
1164
TEST_F(ArtdTest,dexoptFailedToCommit)1165 TEST_F(ArtdTest, dexoptFailedToCommit) {
1166 std::unique_ptr<ScopeGuard<std::function<void()>>> scoped_inaccessible;
1167 std::unique_ptr<ScopeGuard<std::function<void()>>> scoped_unroot;
1168
1169 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1170 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
1171 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
1172 [&](auto, auto, auto) {
1173 scoped_inaccessible = std::make_unique<ScopeGuard<std::function<void()>>>(
1174 ScopedInaccessible(scratch_path_ + "/a/oat/arm64"));
1175 scoped_unroot =
1176 std::make_unique<ScopeGuard<std::function<void()>>>(ScopedUnroot());
1177 return 0;
1178 }));
1179
1180 RunDexopt(
1181 EX_SERVICE_SPECIFIC,
1182 AllOf(Field(&ArtdDexoptResult::sizeBytes, 0), Field(&ArtdDexoptResult::sizeBeforeBytes, 0)));
1183 }
1184
TEST_F(ArtdTest,dexoptCancelledBeforeDex2oat)1185 TEST_F(ArtdTest, dexoptCancelledBeforeDex2oat) {
1186 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
1187 ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
1188
1189 constexpr pid_t kPid = 123;
1190
1191 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1192 .WillOnce([&](auto, const ExecCallbacks& callbacks, auto) {
1193 callbacks.on_start(kPid);
1194 callbacks.on_end(kPid);
1195 return Error();
1196 });
1197 EXPECT_CALL(mock_kill_, Call(-kPid, SIGKILL));
1198
1199 cancellation_signal->cancel();
1200
1201 RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, true), cancellation_signal);
1202
1203 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
1204 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
1205 CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
1206 }
1207
TEST_F(ArtdTest,dexoptCancelledDuringDex2oat)1208 TEST_F(ArtdTest, dexoptCancelledDuringDex2oat) {
1209 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
1210 ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
1211
1212 constexpr pid_t kPid = 123;
1213 constexpr std::chrono::duration<int> kTimeout = std::chrono::seconds(1);
1214
1215 std::condition_variable process_started_cv, process_killed_cv;
1216 std::mutex mu;
1217
1218 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1219 .WillOnce([&](auto, const ExecCallbacks& callbacks, auto) {
1220 std::unique_lock<std::mutex> lock(mu);
1221 // Step 2.
1222 callbacks.on_start(kPid);
1223 process_started_cv.notify_one();
1224 EXPECT_EQ(process_killed_cv.wait_for(lock, kTimeout), std::cv_status::no_timeout);
1225 // Step 5.
1226 callbacks.on_end(kPid);
1227 return Error();
1228 });
1229
1230 EXPECT_CALL(mock_kill_, Call(-kPid, SIGKILL)).WillOnce([&](auto, auto) {
1231 // Step 4.
1232 process_killed_cv.notify_one();
1233 return 0;
1234 });
1235
1236 std::thread t;
1237 {
1238 std::unique_lock<std::mutex> lock(mu);
1239 // Step 1.
1240 t = std::thread([&] {
1241 RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, true), cancellation_signal);
1242 });
1243 EXPECT_EQ(process_started_cv.wait_for(lock, kTimeout), std::cv_status::no_timeout);
1244 // Step 3.
1245 cancellation_signal->cancel();
1246 }
1247
1248 t.join();
1249
1250 // Step 6.
1251 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
1252 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
1253 CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
1254 }
1255
TEST_F(ArtdTest,dexoptCancelledAfterDex2oat)1256 TEST_F(ArtdTest, dexoptCancelledAfterDex2oat) {
1257 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
1258 ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
1259
1260 constexpr pid_t kPid = 123;
1261
1262 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1263 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
1264 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
1265 [&](auto, const ExecCallbacks& callbacks, auto) {
1266 callbacks.on_start(kPid);
1267 callbacks.on_end(kPid);
1268 return 0;
1269 }));
1270 EXPECT_CALL(mock_kill_, Call).Times(0);
1271
1272 RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, false), cancellation_signal);
1273
1274 // This signal should be ignored.
1275 cancellation_signal->cancel();
1276
1277 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "new_oat");
1278 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "new_vdex");
1279 EXPECT_FALSE(std::filesystem::exists(scratch_path_ + "/a/oat/arm64/b.art"));
1280 }
1281
TEST_F(ArtdTest,dexoptDexFileNotOtherReadable)1282 TEST_F(ArtdTest, dexoptDexFileNotOtherReadable) {
1283 dex_file_other_readable_ = false;
1284 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
1285 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1286 Property(&ndk::ScopedAStatus::getMessage,
1287 HasSubstr("Outputs cannot be other-readable because the dex file"))));
1288 }
1289
TEST_F(ArtdTest,dexoptProfileNotOtherReadable)1290 TEST_F(ArtdTest, dexoptProfileNotOtherReadable) {
1291 profile_other_readable_ = false;
1292 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
1293 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1294 Property(&ndk::ScopedAStatus::getMessage,
1295 HasSubstr("Outputs cannot be other-readable because the profile"))));
1296 }
1297
TEST_F(ArtdTest,dexoptOutputNotOtherReadable)1298 TEST_F(ArtdTest, dexoptOutputNotOtherReadable) {
1299 output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
1300 dex_file_other_readable_ = false;
1301 profile_other_readable_ = false;
1302 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(0));
1303 RunDexopt();
1304 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.odex", false);
1305 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.vdex", false);
1306 }
1307
TEST_F(ArtdTest,dexoptUidMismatch)1308 TEST_F(ArtdTest, dexoptUidMismatch) {
1309 output_artifacts_.permissionSettings.fileFsPermission.uid = 12345;
1310 output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
1311 dex_file_other_readable_ = false;
1312 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
1313 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1314 Property(&ndk::ScopedAStatus::getMessage,
1315 HasSubstr("Outputs' owner doesn't match the dex file"))));
1316 }
1317
TEST_F(ArtdTest,dexoptGidMismatch)1318 TEST_F(ArtdTest, dexoptGidMismatch) {
1319 output_artifacts_.permissionSettings.fileFsPermission.gid = 12345;
1320 output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
1321 dex_file_other_readable_ = false;
1322 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
1323 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1324 Property(&ndk::ScopedAStatus::getMessage,
1325 HasSubstr("Outputs' owner doesn't match the dex file"))));
1326 }
1327
TEST_F(ArtdTest,dexoptGidMatchesUid)1328 TEST_F(ArtdTest, dexoptGidMatchesUid) {
1329 output_artifacts_.permissionSettings.fileFsPermission = {
1330 .uid = 123, .gid = 123, .isOtherReadable = false};
1331 EXPECT_CALL(mock_fstat_, Call(_, _)).WillRepeatedly(fstat); // For profile.
1332 EXPECT_CALL(mock_fstat_, Call(FdOf(dex_file_), _))
1333 .WillOnce(DoAll(SetArgPointee<1>((struct stat){
1334 .st_mode = S_IRUSR | S_IRGRP, .st_uid = 123, .st_gid = 456}),
1335 Return(0)));
1336 ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
1337 // It's okay to fail on chown. This happens when the test is not run as root.
1338 RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
1339 AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1340 Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
1341 }
1342
TEST_F(ArtdTest,dexoptGidMatchesGid)1343 TEST_F(ArtdTest, dexoptGidMatchesGid) {
1344 output_artifacts_.permissionSettings.fileFsPermission = {
1345 .uid = 123, .gid = 456, .isOtherReadable = false};
1346 EXPECT_CALL(mock_fstat_, Call(_, _)).WillRepeatedly(fstat); // For profile.
1347 EXPECT_CALL(mock_fstat_, Call(FdOf(dex_file_), _))
1348 .WillOnce(DoAll(SetArgPointee<1>((struct stat){
1349 .st_mode = S_IRUSR | S_IRGRP, .st_uid = 123, .st_gid = 456}),
1350 Return(0)));
1351 ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
1352 // It's okay to fail on chown. This happens when the test is not run as root.
1353 RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
1354 AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1355 Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
1356 }
1357
TEST_F(ArtdTest,dexoptUidGidChangeOk)1358 TEST_F(ArtdTest, dexoptUidGidChangeOk) {
1359 // The dex file is other-readable, so we don't check uid and gid.
1360 output_artifacts_.permissionSettings.fileFsPermission = {
1361 .uid = 12345, .gid = 12345, .isOtherReadable = false};
1362 ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
1363 // It's okay to fail on chown. This happens when the test is not run as root.
1364 RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
1365 AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1366 Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
1367 }
1368
TEST_F(ArtdTest,dexoptNoUidGidChange)1369 TEST_F(ArtdTest, dexoptNoUidGidChange) {
1370 output_artifacts_.permissionSettings.fileFsPermission = {
1371 .uid = -1, .gid = -1, .isOtherReadable = false};
1372 dex_file_other_readable_ = false;
1373 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(0));
1374 RunDexopt();
1375 }
1376
TEST_F(ArtdTest,isProfileUsable)1377 TEST_F(ArtdTest, isProfileUsable) {
1378 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1379 CreateFile(profile_file);
1380 CreateFile(dex_file_);
1381
1382 EXPECT_CALL(
1383 *mock_exec_utils_,
1384 DoExecAndReturnCode(
1385 AllOf(WhenSplitBy(
1386 "--",
1387 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1388 AllOf(Contains(art_root_ + "/bin/profman"),
1389 Contains(Flag("--reference-profile-file-fd=", FdOf(profile_file))),
1390 Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
1391 HasKeepFdsFor("--reference-profile-file-fd=", "--apk-fd=")),
1392 _,
1393 _))
1394 .WillOnce(Return(ProfmanResult::kSkipCompilationSmallDelta));
1395
1396 bool result;
1397 EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
1398 EXPECT_TRUE(result);
1399 }
1400
TEST_F(ArtdTest,isProfileUsableFalse)1401 TEST_F(ArtdTest, isProfileUsableFalse) {
1402 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1403 CreateFile(profile_file);
1404 CreateFile(dex_file_);
1405
1406 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1407 .WillOnce(Return(ProfmanResult::kSkipCompilationEmptyProfiles));
1408
1409 bool result;
1410 EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
1411 EXPECT_FALSE(result);
1412 }
1413
TEST_F(ArtdTest,isProfileUsableNotFound)1414 TEST_F(ArtdTest, isProfileUsableNotFound) {
1415 CreateFile(dex_file_);
1416
1417 bool result;
1418 EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
1419 EXPECT_FALSE(result);
1420 }
1421
TEST_F(ArtdTest,isProfileUsableFailed)1422 TEST_F(ArtdTest, isProfileUsableFailed) {
1423 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1424 CreateFile(profile_file);
1425 CreateFile(dex_file_);
1426
1427 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(100));
1428
1429 bool result;
1430 ndk::ScopedAStatus status = artd_->isProfileUsable(profile_path_.value(), dex_file_, &result);
1431
1432 EXPECT_FALSE(status.isOk());
1433 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1434 EXPECT_THAT(status.getMessage(), HasSubstr("profman returned an unexpected code: 100"));
1435 }
1436
TEST_F(ArtdTest,copyAndRewriteProfileSuccess)1437 TEST_F(ArtdTest, copyAndRewriteProfileSuccess) {
1438 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1439 CreateFile(src_file, "valid_profile");
1440
1441 CreateFile(dex_file_);
1442
1443 EXPECT_CALL(
1444 *mock_exec_utils_,
1445 DoExecAndReturnCode(
1446 AllOf(WhenSplitBy(
1447 "--",
1448 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1449 AllOf(Contains(art_root_ + "/bin/profman"),
1450 Contains("--copy-and-update-profile-key"),
1451 Contains(Flag("--profile-file-fd=", FdOf(src_file))),
1452 Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
1453 HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")),
1454 _,
1455 _))
1456 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "def")),
1457 Return(ProfmanResult::kCopyAndUpdateSuccess)));
1458
1459 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1460
1461 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::SUCCESS);
1462 EXPECT_THAT(dst.profilePath.id, Not(IsEmpty()));
1463 std::string real_path = OR_FATAL(BuildTmpProfilePath(dst.profilePath));
1464 EXPECT_EQ(dst.profilePath.tmpPath, real_path);
1465 CheckContent(real_path, "def");
1466 }
1467
1468 // The input is a plain profile file in the wrong format.
TEST_F(ArtdTest,copyAndRewriteProfileBadProfileWrongFormat)1469 TEST_F(ArtdTest, copyAndRewriteProfileBadProfileWrongFormat) {
1470 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1471 CreateFile(src_file, "wrong_format");
1472
1473 CreateFile(dex_file_);
1474
1475 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1476 .WillOnce(Return(ProfmanResult::kCopyAndUpdateErrorFailedToLoadProfile));
1477
1478 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1479
1480 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::BAD_PROFILE);
1481 EXPECT_THAT(result.errorMsg,
1482 HasSubstr("The profile is in the wrong format or an I/O error has occurred"));
1483 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1484 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1485 }
1486
1487 // The input is a plain profile file that doesn't match the APK.
TEST_F(ArtdTest,copyAndRewriteProfileBadProfileNoMatch)1488 TEST_F(ArtdTest, copyAndRewriteProfileBadProfileNoMatch) {
1489 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1490 CreateFile(src_file, "no_match");
1491
1492 CreateFile(dex_file_);
1493
1494 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1495 .WillOnce(Return(ProfmanResult::kCopyAndUpdateNoMatch));
1496
1497 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1498
1499 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::BAD_PROFILE);
1500 EXPECT_THAT(result.errorMsg, HasSubstr("The profile does not match the APK"));
1501 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1502 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1503 }
1504
1505 // The input is a plain profile file that is empty.
TEST_F(ArtdTest,copyAndRewriteProfileNoProfileEmpty)1506 TEST_F(ArtdTest, copyAndRewriteProfileNoProfileEmpty) {
1507 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1508 CreateFile(src_file, "");
1509
1510 CreateFile(dex_file_);
1511
1512 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1513 .WillOnce(Return(ProfmanResult::kCopyAndUpdateNoMatch));
1514
1515 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1516
1517 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::NO_PROFILE);
1518 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1519 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1520 }
1521
1522 // The input does not exist.
TEST_F(ArtdTest,copyAndRewriteProfileNoProfileNoFile)1523 TEST_F(ArtdTest, copyAndRewriteProfileNoProfileNoFile) {
1524 CreateFile(dex_file_);
1525
1526 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1527
1528 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::NO_PROFILE);
1529 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1530 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1531 }
1532
1533 // The input is a dm file with a profile entry in the wrong format.
TEST_F(ArtdTest,copyAndRewriteProfileNoProfileDmWrongFormat)1534 TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmWrongFormat) {
1535 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1536 CreateZipWithSingleEntry(src_file, "primary.prof", "wrong_format");
1537
1538 CreateFile(dex_file_);
1539
1540 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1541 .WillOnce(Return(ProfmanResult::kCopyAndUpdateErrorFailedToLoadProfile));
1542
1543 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1544
1545 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::BAD_PROFILE);
1546 EXPECT_THAT(result.errorMsg,
1547 HasSubstr("The profile is in the wrong format or an I/O error has occurred"));
1548 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1549 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1550 }
1551
1552 // The input is a dm file with a profile entry that doesn't match the APK.
TEST_F(ArtdTest,copyAndRewriteProfileNoProfileDmNoMatch)1553 TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmNoMatch) {
1554 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1555 CreateZipWithSingleEntry(src_file, "primary.prof", "no_match");
1556
1557 CreateFile(dex_file_);
1558
1559 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1560 .WillOnce(Return(ProfmanResult::kCopyAndUpdateNoMatch));
1561
1562 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1563
1564 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::BAD_PROFILE);
1565 EXPECT_THAT(result.errorMsg, HasSubstr("The profile does not match the APK"));
1566 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1567 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1568 }
1569
1570 // The input is a dm file with a profile entry that is empty.
TEST_F(ArtdTest,copyAndRewriteProfileNoProfileDmEmpty)1571 TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmEmpty) {
1572 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1573 CreateZipWithSingleEntry(src_file, "primary.prof");
1574
1575 CreateFile(dex_file_);
1576
1577 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1578 .WillOnce(Return(ProfmanResult::kCopyAndUpdateNoMatch));
1579
1580 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1581
1582 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::NO_PROFILE);
1583 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1584 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1585 }
1586
1587 // The input is a dm file without a profile entry.
TEST_F(ArtdTest,copyAndRewriteProfileNoProfileDmNoEntry)1588 TEST_F(ArtdTest, copyAndRewriteProfileNoProfileDmNoEntry) {
1589 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1590 CreateZipWithSingleEntry(src_file, "primary.vdex");
1591
1592 CreateFile(dex_file_);
1593
1594 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1595 .WillOnce(Return(ProfmanResult::kCopyAndUpdateNoMatch));
1596
1597 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
1598
1599 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::NO_PROFILE);
1600 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1601 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1602 }
1603
TEST_F(ArtdTest,copyAndRewriteProfileException)1604 TEST_F(ArtdTest, copyAndRewriteProfileException) {
1605 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1606 CreateFile(src_file, "valid_profile");
1607
1608 CreateFile(dex_file_);
1609
1610 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(100));
1611
1612 auto [status, dst] = OR_FAIL(RunCopyAndRewriteProfile</*kExpectOk=*/false>());
1613
1614 EXPECT_FALSE(status.isOk());
1615 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1616 EXPECT_THAT(status.getMessage(), HasSubstr("profman returned an unexpected code: 100"));
1617 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1618 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1619 }
1620
TEST_F(ArtdTest,copyAndRewriteEmbeddedProfileSuccess)1621 TEST_F(ArtdTest, copyAndRewriteEmbeddedProfileSuccess) {
1622 TEST_DISABLED_FOR_SHELL_WITHOUT_MEMFD_ACCESS();
1623
1624 CreateZipWithSingleEntry(dex_file_, "assets/art-profile/baseline.prof", "valid_profile");
1625
1626 EXPECT_CALL(
1627 *mock_exec_utils_,
1628 DoExecAndReturnCode(
1629 AllOf(WhenSplitBy(
1630 "--",
1631 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1632 AllOf(Contains(art_root_ + "/bin/profman"),
1633 Contains("--copy-and-update-profile-key"),
1634 Contains(Flag("--profile-file-fd=", FdHasContent("valid_profile"))),
1635 Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
1636 HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")),
1637 _,
1638 _))
1639 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "def")),
1640 Return(ProfmanResult::kCopyAndUpdateSuccess)));
1641
1642 auto [result, dst] = OR_FAIL(RunCopyAndRewriteEmbeddedProfile());
1643
1644 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::SUCCESS);
1645 EXPECT_THAT(dst.profilePath.id, Not(IsEmpty()));
1646 std::string real_path = OR_FATAL(BuildTmpProfilePath(dst.profilePath));
1647 EXPECT_EQ(dst.profilePath.tmpPath, real_path);
1648 CheckContent(real_path, "def");
1649 }
1650
1651 // The input is a plain dex file.
TEST_F(ArtdTest,copyAndRewriteEmbeddedProfileNoProfilePlainDex)1652 TEST_F(ArtdTest, copyAndRewriteEmbeddedProfileNoProfilePlainDex) {
1653 TEST_DISABLED_FOR_SHELL_WITHOUT_MEMFD_ACCESS();
1654
1655 constexpr const char* kDexMagic = "dex\n";
1656 CreateFile(dex_file_, kDexMagic + "dex_code"s);
1657
1658 auto [result, dst] = OR_FAIL(RunCopyAndRewriteEmbeddedProfile());
1659
1660 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::NO_PROFILE);
1661 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1662 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1663 }
1664
1665 // The input is neither a zip nor a plain dex file.
TEST_F(ArtdTest,copyAndRewriteEmbeddedProfileNotZipNotDex)1666 TEST_F(ArtdTest, copyAndRewriteEmbeddedProfileNotZipNotDex) {
1667 TEST_DISABLED_FOR_SHELL_WITHOUT_MEMFD_ACCESS();
1668
1669 CreateFile(dex_file_, "wrong_format");
1670
1671 auto [status, dst] = OR_FAIL(RunCopyAndRewriteEmbeddedProfile</*kExpectOk=*/false>());
1672
1673 EXPECT_FALSE(status.isOk());
1674 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1675 EXPECT_THAT(status.getMessage(), HasSubstr("File is neither a zip file nor a plain dex file"));
1676 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1677 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1678 }
1679
1680 // The input is a zip file without a profile entry.
TEST_F(ArtdTest,copyAndRewriteEmbeddedProfileNoProfileZipNoEntry)1681 TEST_F(ArtdTest, copyAndRewriteEmbeddedProfileNoProfileZipNoEntry) {
1682 TEST_DISABLED_FOR_SHELL_WITHOUT_MEMFD_ACCESS();
1683
1684 CreateZipWithSingleEntry(dex_file_, "classes.dex", "dex_code");
1685
1686 auto [result, dst] = OR_FAIL(RunCopyAndRewriteEmbeddedProfile());
1687
1688 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::NO_PROFILE);
1689 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1690 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1691 }
1692
1693 // The input is a zip file with a profile entry that doesn't match itself.
TEST_F(ArtdTest,copyAndRewriteEmbeddedProfileBadProfileNoMatch)1694 TEST_F(ArtdTest, copyAndRewriteEmbeddedProfileBadProfileNoMatch) {
1695 TEST_DISABLED_FOR_SHELL_WITHOUT_MEMFD_ACCESS();
1696
1697 CreateZipWithSingleEntry(dex_file_, "assets/art-profile/baseline.prof", "no_match");
1698
1699 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1700 .WillOnce(Return(ProfmanResult::kCopyAndUpdateNoMatch));
1701
1702 auto [result, dst] = OR_FAIL(RunCopyAndRewriteEmbeddedProfile());
1703
1704 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::BAD_PROFILE);
1705 EXPECT_THAT(result.errorMsg, HasSubstr("The profile does not match the APK"));
1706 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1707 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1708 }
1709
TEST_F(ArtdTest,commitTmpProfile)1710 TEST_F(ArtdTest, commitTmpProfile) {
1711 std::string tmp_profile_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path_));
1712 CreateFile(tmp_profile_file);
1713
1714 EXPECT_TRUE(artd_->commitTmpProfile(tmp_profile_path_).isOk());
1715
1716 EXPECT_FALSE(std::filesystem::exists(tmp_profile_file));
1717 EXPECT_TRUE(std::filesystem::exists(OR_FATAL(BuildFinalProfilePath(tmp_profile_path_))));
1718 }
1719
TEST_F(ArtdTest,commitTmpProfileFailed)1720 TEST_F(ArtdTest, commitTmpProfileFailed) {
1721 ndk::ScopedAStatus status = artd_->commitTmpProfile(tmp_profile_path_);
1722
1723 EXPECT_FALSE(status.isOk());
1724 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1725 EXPECT_THAT(
1726 status.getMessage(),
1727 ContainsRegex(R"re(Failed to move .*primary\.prof\.12345\.tmp.* to .*primary\.prof)re"));
1728
1729 EXPECT_FALSE(std::filesystem::exists(OR_FATAL(BuildFinalProfilePath(tmp_profile_path_))));
1730 }
1731
TEST_F(ArtdTest,deleteProfile)1732 TEST_F(ArtdTest, deleteProfile) {
1733 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1734 CreateFile(profile_file);
1735
1736 EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
1737
1738 EXPECT_FALSE(std::filesystem::exists(profile_file));
1739 }
1740
TEST_F(ArtdTest,deleteProfileDoesNotExist)1741 TEST_F(ArtdTest, deleteProfileDoesNotExist) {
1742 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
1743 EXPECT_CALL(mock_logger_, Call).Times(0);
1744
1745 EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
1746 }
1747
TEST_F(ArtdTest,deleteProfileFailed)1748 TEST_F(ArtdTest, deleteProfileFailed) {
1749 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
1750 EXPECT_CALL(
1751 mock_logger_,
1752 Call(_, _, _, _, _, ContainsRegex(R"re(Failed to remove .*primary\.prof\.12345\.tmp)re")));
1753
1754 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1755 auto scoped_inaccessible = ScopedInaccessible(std::filesystem::path(profile_file).parent_path());
1756 auto scoped_unroot = ScopedUnroot();
1757
1758 EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
1759 }
1760
1761 class ArtdGetVisibilityTest : public ArtdTest {
1762 protected:
1763 template <typename PathType>
1764 using Method = ndk::ScopedAStatus (Artd::*)(const PathType&, FileVisibility*);
1765
1766 template <typename PathType>
TestGetVisibilityOtherReadable(Method<PathType> method,const PathType & input,const std::string & path)1767 void TestGetVisibilityOtherReadable(Method<PathType> method,
1768 const PathType& input,
1769 const std::string& path) {
1770 CreateFile(path);
1771 std::filesystem::permissions(
1772 path, std::filesystem::perms::others_read, std::filesystem::perm_options::add);
1773
1774 FileVisibility result;
1775 ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
1776 EXPECT_EQ(result, FileVisibility::OTHER_READABLE);
1777 }
1778
1779 template <typename PathType>
TestGetVisibilityNotOtherReadable(Method<PathType> method,const PathType & input,const std::string & path)1780 void TestGetVisibilityNotOtherReadable(Method<PathType> method,
1781 const PathType& input,
1782 const std::string& path) {
1783 CreateFile(path);
1784 std::filesystem::permissions(
1785 path, std::filesystem::perms::others_read, std::filesystem::perm_options::remove);
1786
1787 FileVisibility result;
1788 ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
1789 EXPECT_EQ(result, FileVisibility::NOT_OTHER_READABLE);
1790 }
1791
1792 template <typename PathType>
TestGetVisibilityNotFound(Method<PathType> method,const PathType & input)1793 void TestGetVisibilityNotFound(Method<PathType> method, const PathType& input) {
1794 FileVisibility result;
1795 ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
1796 EXPECT_EQ(result, FileVisibility::NOT_FOUND);
1797 }
1798
1799 template <typename PathType>
TestGetVisibilityPermissionDenied(Method<PathType> method,const PathType & input,const std::string & path)1800 void TestGetVisibilityPermissionDenied(Method<PathType> method,
1801 const PathType& input,
1802 const std::string& path) {
1803 CreateFile(path);
1804
1805 auto scoped_inaccessible = ScopedInaccessible(std::filesystem::path(path).parent_path());
1806 auto scoped_unroot = ScopedUnroot();
1807
1808 FileVisibility result;
1809 ndk::ScopedAStatus status = ((*artd_).*method)(input, &result);
1810 EXPECT_FALSE(status.isOk());
1811 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1812 EXPECT_THAT(status.getMessage(), HasSubstr("Failed to get status of"));
1813 }
1814 };
1815
TEST_F(ArtdGetVisibilityTest,getProfileVisibilityOtherReadable)1816 TEST_F(ArtdGetVisibilityTest, getProfileVisibilityOtherReadable) {
1817 TestGetVisibilityOtherReadable(&Artd::getProfileVisibility,
1818 profile_path_.value(),
1819 OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
1820 }
1821
TEST_F(ArtdGetVisibilityTest,getProfileVisibilityNotOtherReadable)1822 TEST_F(ArtdGetVisibilityTest, getProfileVisibilityNotOtherReadable) {
1823 TestGetVisibilityNotOtherReadable(&Artd::getProfileVisibility,
1824 profile_path_.value(),
1825 OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
1826 }
1827
TEST_F(ArtdGetVisibilityTest,getProfileVisibilityNotFound)1828 TEST_F(ArtdGetVisibilityTest, getProfileVisibilityNotFound) {
1829 TestGetVisibilityNotFound(&Artd::getProfileVisibility, profile_path_.value());
1830 }
1831
TEST_F(ArtdGetVisibilityTest,getProfileVisibilityPermissionDenied)1832 TEST_F(ArtdGetVisibilityTest, getProfileVisibilityPermissionDenied) {
1833 TestGetVisibilityPermissionDenied(&Artd::getProfileVisibility,
1834 profile_path_.value(),
1835 OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
1836 }
1837
TEST_F(ArtdGetVisibilityTest,getArtifactsVisibilityOtherReadable)1838 TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityOtherReadable) {
1839 TestGetVisibilityOtherReadable(&Artd::getArtifactsVisibility,
1840 artifacts_path_,
1841 OR_FATAL(BuildArtifactsPath(artifacts_path_)).oat_path);
1842 }
1843
TEST_F(ArtdGetVisibilityTest,getArtifactsVisibilityNotOtherReadable)1844 TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotOtherReadable) {
1845 TestGetVisibilityNotOtherReadable(&Artd::getArtifactsVisibility,
1846 artifacts_path_,
1847 OR_FATAL(BuildArtifactsPath(artifacts_path_)).oat_path);
1848 }
1849
TEST_F(ArtdGetVisibilityTest,getArtifactsVisibilityNotFound)1850 TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotFound) {
1851 TestGetVisibilityNotFound(&Artd::getArtifactsVisibility, artifacts_path_);
1852 }
1853
TEST_F(ArtdGetVisibilityTest,getArtifactsVisibilityPermissionDenied)1854 TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityPermissionDenied) {
1855 TestGetVisibilityPermissionDenied(&Artd::getArtifactsVisibility,
1856 artifacts_path_,
1857 OR_FATAL(BuildArtifactsPath(artifacts_path_)).oat_path);
1858 }
1859
TEST_F(ArtdGetVisibilityTest,getDexFileVisibilityOtherReadable)1860 TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityOtherReadable) {
1861 TestGetVisibilityOtherReadable(&Artd::getDexFileVisibility, dex_file_, dex_file_);
1862 }
1863
TEST_F(ArtdGetVisibilityTest,getDexFileVisibilityNotOtherReadable)1864 TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityNotOtherReadable) {
1865 TestGetVisibilityNotOtherReadable(&Artd::getDexFileVisibility, dex_file_, dex_file_);
1866 }
1867
TEST_F(ArtdGetVisibilityTest,getDexFileVisibilityNotFound)1868 TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityNotFound) {
1869 TestGetVisibilityNotFound(&Artd::getDexFileVisibility, dex_file_);
1870 }
1871
TEST_F(ArtdGetVisibilityTest,getDexFileVisibilityPermissionDenied)1872 TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityPermissionDenied) {
1873 TestGetVisibilityPermissionDenied(&Artd::getDexFileVisibility, dex_file_, dex_file_);
1874 }
1875
TEST_F(ArtdGetVisibilityTest,getDmFileVisibilityOtherReadable)1876 TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityOtherReadable) {
1877 TestGetVisibilityOtherReadable(&Artd::getDmFileVisibility,
1878 dm_path_.value(),
1879 OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
1880 }
1881
TEST_F(ArtdGetVisibilityTest,getDmFileVisibilityNotOtherReadable)1882 TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityNotOtherReadable) {
1883 TestGetVisibilityNotOtherReadable(&Artd::getDmFileVisibility,
1884 dm_path_.value(),
1885 OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
1886 }
1887
TEST_F(ArtdGetVisibilityTest,getDmFileVisibilityNotFound)1888 TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityNotFound) {
1889 TestGetVisibilityNotFound(&Artd::getDmFileVisibility, dm_path_.value());
1890 }
1891
TEST_F(ArtdGetVisibilityTest,getDmFileVisibilityPermissionDenied)1892 TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityPermissionDenied) {
1893 TestGetVisibilityPermissionDenied(&Artd::getDmFileVisibility,
1894 dm_path_.value(),
1895 OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
1896 }
1897
TEST_F(ArtdTest,mergeProfiles)1898 TEST_F(ArtdTest, mergeProfiles) {
1899 std::string reference_profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1900 CreateFile(reference_profile_file, "abc");
1901
1902 // Doesn't exist.
1903 PrimaryCurProfilePath profile_0_path{
1904 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
1905 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
1906
1907 PrimaryCurProfilePath profile_1_path{
1908 .userId = 1, .packageName = "com.android.foo", .profileName = "primary"};
1909 std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path));
1910 CreateFile(profile_1_file, "def");
1911
1912 OutputProfile output_profile{.profilePath = tmp_profile_path_,
1913 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1914 output_profile.profilePath.id = "";
1915 output_profile.profilePath.tmpPath = "";
1916
1917 std::string dex_file_1 = scratch_path_ + "/a/b.apk";
1918 std::string dex_file_2 = scratch_path_ + "/a/c.apk";
1919 CreateFile(dex_file_1);
1920 CreateFile(dex_file_2);
1921
1922 EXPECT_CALL(
1923 *mock_exec_utils_,
1924 DoExecAndReturnCode(
1925 AllOf(WhenSplitBy(
1926 "--",
1927 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1928 AllOf(Contains(art_root_ + "/bin/profman"),
1929 Not(Contains(Flag("--profile-file-fd=", FdOf(profile_0_file)))),
1930 Contains(Flag("--profile-file-fd=", FdOf(profile_1_file))),
1931 Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))),
1932 Contains(Flag("--apk-fd=", FdOf(dex_file_1))),
1933 Contains(Flag("--apk-fd=", FdOf(dex_file_2))),
1934 Not(Contains("--force-merge-and-analyze")),
1935 Not(Contains("--boot-image-merge")))),
1936 HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")),
1937 _,
1938 _))
1939 .WillOnce(DoAll(WithArg<0>(ClearAndWriteToFdFlag("--reference-profile-file-fd=", "merged")),
1940 Return(ProfmanResult::kCompile)));
1941
1942 bool result;
1943 EXPECT_TRUE(artd_
1944 ->mergeProfiles({profile_0_path, profile_1_path},
1945 profile_path_,
1946 &output_profile,
1947 {dex_file_1, dex_file_2},
1948 /*in_options=*/{},
1949 &result)
1950 .isOk());
1951 EXPECT_TRUE(result);
1952 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
1953 std::string real_path = OR_FATAL(BuildTmpProfilePath(output_profile.profilePath));
1954 EXPECT_EQ(output_profile.profilePath.tmpPath, real_path);
1955 CheckContent(real_path, "merged");
1956 }
1957
TEST_F(ArtdTest,mergeProfilesEmptyReferenceProfile)1958 TEST_F(ArtdTest, mergeProfilesEmptyReferenceProfile) {
1959 PrimaryCurProfilePath profile_0_path{
1960 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
1961 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
1962 CreateFile(profile_0_file, "def");
1963
1964 OutputProfile output_profile{.profilePath = tmp_profile_path_,
1965 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1966 output_profile.profilePath.id = "";
1967 output_profile.profilePath.tmpPath = "";
1968
1969 CreateFile(dex_file_);
1970
1971 EXPECT_CALL(
1972 *mock_exec_utils_,
1973 DoExecAndReturnCode(
1974 WhenSplitBy("--",
1975 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1976 AllOf(Contains(art_root_ + "/bin/profman"),
1977 Contains(Flag("--profile-file-fd=", FdOf(profile_0_file))),
1978 Contains(Flag("--reference-profile-file-fd=", FdHasContent(""))),
1979 Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
1980 _,
1981 _))
1982 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "merged")),
1983 Return(ProfmanResult::kCompile)));
1984
1985 bool result;
1986 EXPECT_TRUE(artd_
1987 ->mergeProfiles({profile_0_path},
1988 std::nullopt,
1989 &output_profile,
1990 {dex_file_},
1991 /*in_options=*/{},
1992 &result)
1993 .isOk());
1994 EXPECT_TRUE(result);
1995 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
1996 EXPECT_THAT(output_profile.profilePath.tmpPath, Not(IsEmpty()));
1997 }
1998
TEST_F(ArtdTest,mergeProfilesProfilesDontExist)1999 TEST_F(ArtdTest, mergeProfilesProfilesDontExist) {
2000 // Doesn't exist.
2001 PrimaryCurProfilePath profile_0_path{
2002 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
2003 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
2004
2005 // Doesn't exist.
2006 PrimaryCurProfilePath profile_1_path{
2007 .userId = 1, .packageName = "com.android.foo", .profileName = "primary"};
2008 std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path));
2009
2010 OutputProfile output_profile{.profilePath = tmp_profile_path_,
2011 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
2012 output_profile.profilePath.id = "";
2013 output_profile.profilePath.tmpPath = "";
2014
2015 CreateFile(dex_file_);
2016
2017 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode).Times(0);
2018
2019 bool result;
2020 EXPECT_TRUE(artd_
2021 ->mergeProfiles({profile_0_path},
2022 /*in_referenceProfile=*/std::nullopt,
2023 &output_profile,
2024 {dex_file_},
2025 /*in_options=*/{},
2026 &result)
2027 .isOk());
2028 EXPECT_FALSE(result);
2029 EXPECT_THAT(output_profile.profilePath.id, IsEmpty());
2030 EXPECT_THAT(output_profile.profilePath.tmpPath, IsEmpty());
2031 }
2032
TEST_F(ArtdTest,mergeProfilesWithOptionsForceMerge)2033 TEST_F(ArtdTest, mergeProfilesWithOptionsForceMerge) {
2034 PrimaryCurProfilePath profile_0_path{
2035 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
2036 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
2037 CreateFile(profile_0_file, "def");
2038
2039 OutputProfile output_profile{.profilePath = tmp_profile_path_,
2040 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
2041 output_profile.profilePath.id = "";
2042 output_profile.profilePath.tmpPath = "";
2043
2044 CreateFile(dex_file_);
2045
2046 EXPECT_CALL(*mock_exec_utils_,
2047 DoExecAndReturnCode(WhenSplitBy("--",
2048 _,
2049 AllOf(Contains("--force-merge-and-analyze"),
2050 Contains("--boot-image-merge"))),
2051 _,
2052 _))
2053 .WillOnce(Return(ProfmanResult::kCompile));
2054
2055 bool result;
2056 EXPECT_TRUE(artd_
2057 ->mergeProfiles({profile_0_path},
2058 std::nullopt,
2059 &output_profile,
2060 {dex_file_},
2061 {.forceMerge = true, .forBootImage = true},
2062 &result)
2063 .isOk());
2064 EXPECT_TRUE(result);
2065 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
2066 EXPECT_THAT(output_profile.profilePath.tmpPath, Not(IsEmpty()));
2067 }
2068
TEST_F(ArtdTest,mergeProfilesWithOptionsDumpOnly)2069 TEST_F(ArtdTest, mergeProfilesWithOptionsDumpOnly) {
2070 PrimaryCurProfilePath profile_0_path{
2071 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
2072 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
2073 CreateFile(profile_0_file, "def");
2074
2075 OutputProfile output_profile{.profilePath = tmp_profile_path_,
2076 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
2077 output_profile.profilePath.id = "";
2078 output_profile.profilePath.tmpPath = "";
2079
2080 CreateFile(dex_file_);
2081
2082 EXPECT_CALL(*mock_exec_utils_,
2083 DoExecAndReturnCode(
2084 AllOf(WhenSplitBy("--",
2085 _,
2086 AllOf(Contains("--dump-only"),
2087 Not(Contains(Flag("--reference-profile-file-fd=", _))))),
2088 HasKeepFdsFor("--profile-file-fd=", "--apk-fd=", "--dump-output-to-fd=")),
2089 _,
2090 _))
2091 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--dump-output-to-fd=", "dump")),
2092 Return(ProfmanResult::kSuccess)));
2093
2094 bool result;
2095 EXPECT_TRUE(artd_
2096 ->mergeProfiles({profile_0_path},
2097 std::nullopt,
2098 &output_profile,
2099 {dex_file_},
2100 {.dumpOnly = true},
2101 &result)
2102 .isOk());
2103 EXPECT_TRUE(result);
2104 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
2105 CheckContent(output_profile.profilePath.tmpPath, "dump");
2106 }
2107
TEST_F(ArtdTest,mergeProfilesWithOptionsDumpClassesAndMethods)2108 TEST_F(ArtdTest, mergeProfilesWithOptionsDumpClassesAndMethods) {
2109 PrimaryCurProfilePath profile_0_path{
2110 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
2111 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
2112 CreateFile(profile_0_file, "def");
2113
2114 OutputProfile output_profile{.profilePath = tmp_profile_path_,
2115 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
2116 output_profile.profilePath.id = "";
2117 output_profile.profilePath.tmpPath = "";
2118
2119 CreateFile(dex_file_);
2120
2121 EXPECT_CALL(*mock_exec_utils_,
2122 DoExecAndReturnCode(
2123 WhenSplitBy("--",
2124 _,
2125 AllOf(Contains("--dump-classes-and-methods"),
2126 Not(Contains(Flag("--reference-profile-file-fd=", _))))),
2127 _,
2128 _))
2129 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--dump-output-to-fd=", "dump")),
2130 Return(ProfmanResult::kSuccess)));
2131
2132 bool result;
2133 EXPECT_TRUE(artd_
2134 ->mergeProfiles({profile_0_path},
2135 std::nullopt,
2136 &output_profile,
2137 {dex_file_},
2138 {.dumpClassesAndMethods = true},
2139 &result)
2140 .isOk());
2141 EXPECT_TRUE(result);
2142 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
2143 CheckContent(output_profile.profilePath.tmpPath, "dump");
2144 }
2145
2146 class ArtdCleanupTest : public ArtdTest {
2147 protected:
SetUpForCleanup()2148 void SetUpForCleanup() {
2149 // Unmanaged files.
2150 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/1.odex");
2151 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.odex");
2152 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.txt");
2153 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.txt");
2154 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.tmp");
2155
2156 // Files to keep.
2157 CreateGcKeptFile(android_data_ + "/misc/profiles/cur/1/com.android.foo/primary.prof");
2158 CreateGcKeptFile(android_data_ + "/misc/profiles/cur/3/com.android.foo/primary.prof");
2159 CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.dex");
2160 CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.vdex");
2161 CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.art");
2162 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.vdex");
2163 CreateGcKeptFile(
2164 android_expand_ +
2165 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex");
2166 CreateGcKeptFile(
2167 android_expand_ +
2168 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.vdex");
2169 CreateGcKeptFile(
2170 android_expand_ +
2171 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.art");
2172 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex");
2173 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.vdex");
2174 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.art");
2175 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art");
2176 CreateGcKeptFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art");
2177 CreateGcKeptFile(android_data_ + "/user/1/com.android.foo/cache/oat_primary/arm64/base.art");
2178 CreateGcKeptFile(android_expand_ +
2179 "/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art");
2180 CreateGcKeptFile(android_data_ +
2181 "/user/0/com.android.foo/cache/not_oat_dir/oat_primary/arm64/base.art");
2182
2183 // Files to remove.
2184 CreateGcRemovedFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof");
2185 CreateGcRemovedFile(android_data_ + "/misc/profiles/cur/2/com.android.foo/primary.prof");
2186 CreateGcRemovedFile(android_data_ + "/misc/profiles/cur/3/com.android.bar/primary.prof");
2187 CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/extra.odex");
2188 CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.dex");
2189 CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.vdex");
2190 CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.art");
2191 CreateGcRemovedFile(
2192 android_expand_ +
2193 "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.odex");
2194 CreateGcRemovedFile(
2195 android_expand_ +
2196 "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.vdex");
2197 CreateGcRemovedFile(
2198 android_expand_ +
2199 "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.art");
2200 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/1.prof");
2201 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/1.prof.123456.tmp");
2202 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.odex");
2203 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.vdex");
2204 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.art");
2205 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.odex.123456.tmp");
2206 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/2.odex.123456.tmp");
2207 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.odex");
2208 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.art");
2209 CreateGcRemovedFile(android_data_ +
2210 "/user_de/0/com.android.foo/aaa/oat/arm64/1.vdex.123456.tmp");
2211 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.odex");
2212 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.vdex");
2213 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art");
2214 CreateGcRemovedFile(android_data_ +
2215 "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art.123456.tmp");
2216 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.bar/aaa/oat/arm64/1.vdex");
2217 CreateGcRemovedFile(android_data_ +
2218 "/user/0/com.android.different_package/cache/oat_primary/arm64/base.art");
2219 CreateGcRemovedFile(android_data_ +
2220 "/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art");
2221 CreateGcRemovedFile(android_data_ +
2222 "/user/0/com.android.foo/cache/oat_primary/different_isa/base.art");
2223 }
2224
CreateGcRemovedFile(const std::string & path)2225 void CreateGcRemovedFile(const std::string& path) {
2226 CreateFile(path);
2227 gc_removed_files_.push_back(path);
2228 }
2229
CreateGcKeptFile(const std::string & path)2230 void CreateGcKeptFile(const std::string& path) {
2231 CreateFile(path);
2232 gc_kept_files_.push_back(path);
2233 }
2234
RunCleanup(bool keepPreRebootStagedFiles)2235 void RunCleanup(bool keepPreRebootStagedFiles) {
2236 int64_t aidl_return;
2237 ASSERT_STATUS_OK(artd_->cleanup(
2238 {
2239 PrimaryCurProfilePath{
2240 .userId = 1, .packageName = "com.android.foo", .profileName = "primary"},
2241 PrimaryCurProfilePath{
2242 .userId = 3, .packageName = "com.android.foo", .profileName = "primary"},
2243 },
2244 {
2245 ArtifactsPath{
2246 .dexPath = "/system/app/Foo/Foo.apk", .isa = "arm64", .isInDalvikCache = true},
2247 ArtifactsPath{
2248 .dexPath = android_expand_ +
2249 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/base.apk",
2250 .isa = "arm64",
2251 .isInDalvikCache = false},
2252 ArtifactsPath{.dexPath = android_data_ + "/user_de/0/com.android.foo/aaa/2.apk",
2253 .isa = "arm64",
2254 .isInDalvikCache = false},
2255 },
2256 {
2257 VdexPath{
2258 ArtifactsPath{.dexPath = android_data_ + "/user_de/0/com.android.foo/aaa/1.apk",
2259 .isa = "arm64",
2260 .isInDalvikCache = false}},
2261 },
2262 {
2263 RuntimeArtifactsPath{
2264 .packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"},
2265 },
2266 keepPreRebootStagedFiles,
2267 &aidl_return));
2268 }
2269
Verify()2270 void Verify() {
2271 for (const std::string& path : gc_removed_files_) {
2272 EXPECT_FALSE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be removed", path);
2273 }
2274
2275 for (const std::string& path : gc_kept_files_) {
2276 EXPECT_TRUE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be kept", path);
2277 }
2278 }
2279
2280 private:
2281 std::vector<std::string> gc_removed_files_;
2282 std::vector<std::string> gc_kept_files_;
2283 };
2284
TEST_F(ArtdCleanupTest,cleanupKeepingPreRebootStagedFiles)2285 TEST_F(ArtdCleanupTest, cleanupKeepingPreRebootStagedFiles) {
2286 SetUpForCleanup();
2287 CreateGcKeptFile(
2288 android_expand_ +
2289 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex.staged");
2290 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex.staged");
2291
2292 ASSERT_NO_FATAL_FAILURE(RunCleanup(/*keepPreRebootStagedFiles=*/true));
2293 Verify();
2294 }
2295
TEST_F(ArtdCleanupTest,cleanupRemovingPreRebootStagedFiles)2296 TEST_F(ArtdCleanupTest, cleanupRemovingPreRebootStagedFiles) {
2297 SetUpForCleanup();
2298 CreateGcRemovedFile(
2299 android_expand_ +
2300 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex.staged");
2301 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex.staged");
2302
2303 ASSERT_NO_FATAL_FAILURE(RunCleanup(/*keepPreRebootStagedFiles=*/false));
2304 Verify();
2305 }
2306
TEST_F(ArtdCleanupTest,cleanUpPreRebootStagedFiles)2307 TEST_F(ArtdCleanupTest, cleanUpPreRebootStagedFiles) {
2308 // Unmanaged file.
2309 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/1.odex.staged");
2310
2311 // Not Pre-reboot staged files.
2312 CreateGcKeptFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof");
2313 CreateGcKeptFile(
2314 android_expand_ +
2315 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex");
2316 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex");
2317
2318 // Pre-reboot staged files.
2319 CreateGcRemovedFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof.staged");
2320 CreateGcRemovedFile(
2321 android_expand_ +
2322 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex.staged");
2323 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex.staged");
2324
2325 ASSERT_STATUS_OK(artd_->cleanUpPreRebootStagedFiles());
2326 Verify();
2327 }
2328
TEST_F(ArtdTest,isInDalvikCache)2329 TEST_F(ArtdTest, isInDalvikCache) {
2330 TEST_DISABLED_FOR_HOST();
2331
2332 auto is_in_dalvik_cache = [this](const std::string& dex_file) -> Result<bool> {
2333 bool result;
2334 ndk::ScopedAStatus status = artd_->isInDalvikCache(dex_file, &result);
2335 if (!status.isOk()) {
2336 return Error() << status.getMessage();
2337 }
2338 return result;
2339 };
2340
2341 EXPECT_THAT(is_in_dalvik_cache("/system/app/base.apk"), HasValue(true));
2342 EXPECT_THAT(is_in_dalvik_cache("/system_ext/app/base.apk"), HasValue(true));
2343 EXPECT_THAT(is_in_dalvik_cache("/vendor/app/base.apk"), HasValue(true));
2344 EXPECT_THAT(is_in_dalvik_cache("/product/app/base.apk"), HasValue(true));
2345 EXPECT_THAT(is_in_dalvik_cache("/data/app/base.apk"), HasValue(false));
2346
2347 // Test a path where we don't expect to find packages. The method should still work.
2348 EXPECT_THAT(is_in_dalvik_cache("/foo"), HasValue(true));
2349 }
2350
TEST_F(ArtdTest,deleteRuntimeArtifacts)2351 TEST_F(ArtdTest, deleteRuntimeArtifacts) {
2352 std::vector<std::string> removed_files;
2353 std::vector<std::string> kept_files;
2354
2355 auto CreateRemovedFile = [&](const std::string& path) {
2356 CreateFile(path);
2357 removed_files.push_back(path);
2358 };
2359
2360 auto CreateKeptFile = [&](const std::string& path) {
2361 CreateFile(path);
2362 kept_files.push_back(path);
2363 };
2364
2365 CreateKeptFile(android_data_ +
2366 "/user/0/com.android.different_package/cache/oat_primary/arm64/base.art");
2367 CreateKeptFile(android_data_ +
2368 "/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art");
2369 CreateKeptFile(android_data_ +
2370 "/user/0/com.android.foo/cache/oat_primary/different_isa/base.art");
2371 CreateKeptFile(android_data_ +
2372 "/user/0/com.android.foo/cache/not_oat_dir/oat_primary/arm64/base.art");
2373
2374 CreateRemovedFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art");
2375 CreateRemovedFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art");
2376 CreateRemovedFile(android_data_ + "/user/1/com.android.foo/cache/oat_primary/arm64/base.art");
2377 CreateRemovedFile(android_expand_ +
2378 "/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art");
2379
2380 int64_t aidl_return;
2381 ASSERT_TRUE(
2382 artd_
2383 ->deleteRuntimeArtifacts(
2384 {.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"},
2385 &aidl_return)
2386 .isOk());
2387
2388 for (const std::string& path : removed_files) {
2389 EXPECT_FALSE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be removed", path);
2390 }
2391
2392 for (const std::string& path : kept_files) {
2393 EXPECT_TRUE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be kept", path);
2394 }
2395 }
2396
TEST_F(ArtdTest,deleteRuntimeArtifactsAndroidDataNotExist)2397 TEST_F(ArtdTest, deleteRuntimeArtifactsAndroidDataNotExist) {
2398 // Will be cleaned up by `android_data_env_`
2399 setenv("ANDROID_DATA", "/non-existing", /*replace=*/1);
2400
2401 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
2402 EXPECT_CALL(mock_logger_,
2403 Call(_, _, _, _, _, HasSubstr("Failed to find directory /non-existing")));
2404
2405 int64_t aidl_return;
2406 ASSERT_TRUE(
2407 artd_
2408 ->deleteRuntimeArtifacts(
2409 {.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"},
2410 &aidl_return)
2411 .isOk());
2412
2413 EXPECT_EQ(aidl_return, 0);
2414 }
2415
TEST_F(ArtdTest,deleteRuntimeArtifactsSpecialChars)2416 TEST_F(ArtdTest, deleteRuntimeArtifactsSpecialChars) {
2417 std::vector<std::string> removed_files;
2418 std::vector<std::string> kept_files;
2419
2420 auto CreateRemovedFile = [&](const std::string& path) {
2421 CreateFile(path);
2422 removed_files.push_back(path);
2423 };
2424
2425 auto CreateKeptFile = [&](const std::string& path) {
2426 CreateFile(path);
2427 kept_files.push_back(path);
2428 };
2429
2430 CreateKeptFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art");
2431
2432 CreateRemovedFile(android_data_ + "/user/0/*/cache/oat_primary/arm64/base.art");
2433 CreateRemovedFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/*/base.art");
2434 CreateRemovedFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/*.art");
2435
2436 int64_t aidl_return;
2437 ASSERT_TRUE(
2438 artd_
2439 ->deleteRuntimeArtifacts({.packageName = "*", .dexPath = "/a/b/base.apk", .isa = "arm64"},
2440 &aidl_return)
2441 .isOk());
2442 ASSERT_TRUE(artd_
2443 ->deleteRuntimeArtifacts(
2444 {.packageName = "com.android.foo", .dexPath = "/a/b/*.apk", .isa = "arm64"},
2445 &aidl_return)
2446 .isOk());
2447 ASSERT_TRUE(artd_
2448 ->deleteRuntimeArtifacts(
2449 {.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "*"},
2450 &aidl_return)
2451 .isOk());
2452
2453 for (const std::string& path : removed_files) {
2454 EXPECT_FALSE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be removed", path);
2455 }
2456
2457 for (const std::string& path : kept_files) {
2458 EXPECT_TRUE(std::filesystem::exists(path)) << ART_FORMAT("'{}' should be kept", path);
2459 }
2460 }
2461
TEST_F(ArtdTest,getArtifactsSize)2462 TEST_F(ArtdTest, getArtifactsSize) {
2463 std::string oat_dir = scratch_path_ + "/a/oat/arm64";
2464 CreateFile(oat_dir + "/b.odex", std::string(1, '*'));
2465 CreateFile(oat_dir + "/b.vdex", std::string(2, '*'));
2466 CreateFile(oat_dir + "/b.art", std::string(4, '*'));
2467
2468 // Irrelevant.
2469 CreateFile(oat_dir + "/c.vdex", std::string(8, '*'));
2470
2471 int64_t aidl_return = -1;
2472 ASSERT_TRUE(
2473 artd_
2474 ->getArtifactsSize(
2475 {.dexPath = scratch_path_ + "/a/b.apk", .isa = "arm64", .isInDalvikCache = false},
2476 &aidl_return)
2477 .isOk());
2478 EXPECT_EQ(aidl_return, 1 + 2 + 4);
2479 }
2480
TEST_F(ArtdTest,getVdexFileSize)2481 TEST_F(ArtdTest, getVdexFileSize) {
2482 std::string oat_dir = scratch_path_ + "/a/oat/arm64";
2483 CreateFile(oat_dir + "/b.vdex", std::string(1, '*'));
2484
2485 // Irrelevant.
2486 CreateFile(oat_dir + "/b.odex", std::string(2, '*'));
2487 CreateFile(oat_dir + "/b.art", std::string(4, '*'));
2488 CreateFile(oat_dir + "/c.vdex", std::string(8, '*'));
2489
2490 int64_t aidl_return = -1;
2491 ASSERT_TRUE(artd_
2492 ->getVdexFileSize(ArtifactsPath{.dexPath = scratch_path_ + "/a/b.apk",
2493 .isa = "arm64",
2494 .isInDalvikCache = false},
2495 &aidl_return)
2496 .isOk());
2497 EXPECT_EQ(aidl_return, 1);
2498 }
2499
TEST_F(ArtdTest,getRuntimeArtifactsSize)2500 TEST_F(ArtdTest, getRuntimeArtifactsSize) {
2501 CreateFile(android_data_ + "/user_de/0/com.android.foo/cache/oat_primary/arm64/base.art",
2502 std::string(1, '*'));
2503 CreateFile(android_data_ + "/user/0/com.android.foo/cache/oat_primary/arm64/base.art",
2504 std::string(2, '*'));
2505 CreateFile(android_data_ + "/user/1/com.android.foo/cache/oat_primary/arm64/base.art",
2506 std::string(4, '*'));
2507 CreateFile(
2508 android_expand_ + "/123456-7890/user/1/com.android.foo/cache/oat_primary/arm64/base.art",
2509 std::string(8, '*'));
2510
2511 // Irrelevant.
2512 CreateFile(android_expand_ + "/user/0/com.android.foo/cache/oat_primary/arm64/different_dex.art",
2513 std::string(16, '*'));
2514
2515 int64_t aidl_return = -1;
2516 ASSERT_TRUE(
2517 artd_
2518 ->getRuntimeArtifactsSize(
2519 {.packageName = "com.android.foo", .dexPath = "/a/b/base.apk", .isa = "arm64"},
2520 &aidl_return)
2521 .isOk());
2522
2523 EXPECT_EQ(aidl_return, 1 + 2 + 4 + 8);
2524 }
2525
TEST_F(ArtdTest,getProfileSize)2526 TEST_F(ArtdTest, getProfileSize) {
2527 CreateFile(android_data_ + "/misc/profiles/cur/0/com.android.foo/primary.prof",
2528 std::string(1, '*'));
2529
2530 // Irrelevant.
2531 CreateFile(android_data_ + "/misc/profiles/cur/0/com.android.foo/split_0.split.prof",
2532 std::string(2, '*'));
2533 CreateFile(android_data_ + "/misc/profiles/cur/0/com.android.bar/primary.prof",
2534 std::string(4, '*'));
2535 CreateFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof",
2536 std::string(8, '*'));
2537
2538 int64_t aidl_return = -1;
2539 ASSERT_TRUE(artd_
2540 ->getProfileSize(
2541 PrimaryCurProfilePath{
2542 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"},
2543 &aidl_return)
2544 .isOk());
2545 EXPECT_EQ(aidl_return, 1);
2546 }
2547
2548 class ArtdProfileSaveNotificationTest : public ArtdTest {
2549 protected:
SetUp()2550 void SetUp() override {
2551 ArtdTest::SetUp();
2552
2553 std::vector<std::string> args{GetBin("sleep"), "10"};
2554 std::tie(pid_, scope_guard_) = ScopedExec(args, /*wait=*/false);
2555 notification_file_ = OR_FAIL(BuildPrimaryCurProfilePath(profile_path_));
2556 std::filesystem::create_directories(Dirname(notification_file_));
2557 }
2558
2559 const PrimaryCurProfilePath profile_path_{
2560 .userId = 0,
2561 .packageName = "com.android.foo",
2562 .profileName = "primary",
2563 };
2564 std::string notification_file_;
2565 int pid_;
2566 std::unique_ptr<ScopeGuard<std::function<void()>>> scope_guard_;
2567 };
2568
TEST_F(ArtdProfileSaveNotificationTest,initAndWaitSuccess)2569 TEST_F(ArtdProfileSaveNotificationTest, initAndWaitSuccess) {
2570 // Use a condvar to sequence the NewFile::CommitOrAbandon calls.
2571 constexpr std::chrono::duration<int> kTimeout = std::chrono::seconds(1);
2572 std::condition_variable wait_started_cv;
2573 std::mutex mu;
2574
2575 EXPECT_CALL(mock_poll_, Call)
2576 .Times(2)
2577 .WillRepeatedly(DoAll(
2578 [&](auto, auto, auto) {
2579 // Step 3, 5.
2580 std::unique_lock<std::mutex> lock(mu);
2581 wait_started_cv.notify_one();
2582 },
2583 poll));
2584
2585 std::shared_ptr<IArtdNotification> notification;
2586 ASSERT_STATUS_OK(artd_->initProfileSaveNotification(profile_path_, pid_, ¬ification));
2587
2588 std::unique_lock<std::mutex> lock(mu);
2589
2590 // Step 1.
2591 std::thread t([&] {
2592 // Step 2.
2593 bool aidl_return;
2594 ASSERT_STATUS_OK(notification->wait(/*in_timeoutMs=*/1000, &aidl_return));
2595 // Step 7.
2596 EXPECT_TRUE(aidl_return);
2597 });
2598 wait_started_cv.wait_for(lock, kTimeout);
2599
2600 // Step 4.
2601 std::unique_ptr<NewFile> unrelated_file = OR_FAIL(NewFile::Create(
2602 Dirname(notification_file_) + "/unrelated.prof", FsPermission{.uid = -1, .gid = -1}));
2603 OR_FAIL(unrelated_file->CommitOrAbandon());
2604 wait_started_cv.wait_for(lock, kTimeout);
2605
2606 // Step 6.
2607 std::unique_ptr<NewFile> file =
2608 OR_FAIL(NewFile::Create(notification_file_, FsPermission{.uid = -1, .gid = -1}));
2609 OR_FAIL(file->CommitOrAbandon());
2610
2611 t.join();
2612 }
2613
TEST_F(ArtdProfileSaveNotificationTest,initAndWaitProcessGone)2614 TEST_F(ArtdProfileSaveNotificationTest, initAndWaitProcessGone) {
2615 EXPECT_CALL(mock_poll_, Call).WillOnce(poll);
2616
2617 std::shared_ptr<IArtdNotification> notification;
2618 ASSERT_STATUS_OK(artd_->initProfileSaveNotification(profile_path_, pid_, ¬ification));
2619
2620 std::thread t([&] {
2621 bool aidl_return;
2622 ASSERT_STATUS_OK(notification->wait(/*in_timeoutMs=*/1000, &aidl_return));
2623 EXPECT_TRUE(aidl_return);
2624 });
2625
2626 kill(pid_, SIGKILL);
2627
2628 t.join();
2629 }
2630
TEST_F(ArtdProfileSaveNotificationTest,initAndWaitTimeout)2631 TEST_F(ArtdProfileSaveNotificationTest, initAndWaitTimeout) {
2632 EXPECT_CALL(mock_poll_, Call).WillOnce(poll).WillOnce(Return(0));
2633
2634 std::shared_ptr<IArtdNotification> notification;
2635 ASSERT_STATUS_OK(artd_->initProfileSaveNotification(profile_path_, pid_, ¬ification));
2636
2637 std::unique_ptr<NewFile> unrelated_file = OR_FAIL(NewFile::Create(
2638 Dirname(notification_file_) + "/unrelated.prof", FsPermission{.uid = -1, .gid = -1}));
2639 OR_FAIL(unrelated_file->CommitOrAbandon());
2640
2641 bool aidl_return;
2642 ASSERT_STATUS_OK(notification->wait(/*in_timeoutMs=*/1000, &aidl_return));
2643 EXPECT_FALSE(aidl_return);
2644 }
2645
TEST_F(ArtdProfileSaveNotificationTest,initProcessGone)2646 TEST_F(ArtdProfileSaveNotificationTest, initProcessGone) {
2647 // Kill the process before pidfd_open.
2648 scope_guard_.reset();
2649
2650 EXPECT_CALL(mock_poll_, Call).Times(0);
2651
2652 std::shared_ptr<IArtdNotification> notification;
2653 ASSERT_STATUS_OK(artd_->initProfileSaveNotification(profile_path_, pid_, ¬ification));
2654
2655 bool aidl_return;
2656 ASSERT_STATUS_OK(notification->wait(/*in_timeoutMs=*/1000, &aidl_return));
2657 EXPECT_TRUE(aidl_return);
2658 }
2659
TEST_F(ArtdTest,commitPreRebootStagedFiles)2660 TEST_F(ArtdTest, commitPreRebootStagedFiles) {
2661 CreateFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.dex.staged",
2662 "new_odex_1");
2663 CreateFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.vdex.staged",
2664 "new_vdex_1");
2665 CreateFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.art.staged",
2666 "new_art_1");
2667
2668 CreateFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.dex",
2669 "old_odex_1");
2670 CreateFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.vdex",
2671 "old_vdex_1");
2672 CreateFile(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.art", "old_art_1");
2673
2674 CreateFile(android_data_ + "/app/com.android.foo/oat/arm64/base.odex", "old_odex_2");
2675 CreateFile(android_data_ + "/app/com.android.foo/oat/arm64/base.vdex", "old_vdex_2");
2676 CreateFile(android_data_ + "/app/com.android.foo/oat/arm64/base.art", "old_art_2");
2677
2678 CreateFile(android_data_ + "/app/com.android.foo/oat/arm64/base.odex.staged", "new_odex_2");
2679 CreateFile(android_data_ + "/app/com.android.foo/oat/arm64/base.vdex.staged", "new_vdex_2");
2680
2681 CreateFile(android_data_ + "/app/com.android.foo/oat/arm/base.odex", "old_odex_3");
2682 CreateFile(android_data_ + "/app/com.android.foo/oat/arm/base.vdex", "old_vdex_3");
2683 CreateFile(android_data_ + "/app/com.android.foo/oat/arm/base.art", "old_art_3");
2684
2685 CreateFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof", "old_prof_1");
2686 CreateFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof.staged",
2687 "new_prof_1");
2688
2689 CreateFile(android_data_ + "/misc/profiles/ref/com.android.bar/primary.prof", "old_prof_2");
2690
2691 bool aidl_return;
2692 ASSERT_STATUS_OK(artd_->commitPreRebootStagedFiles(
2693 {
2694 // Has all new files. All old files should be replaced.
2695 ArtifactsPath{
2696 .dexPath = "/system/app/Foo/Foo.apk", .isa = "arm64", .isInDalvikCache = true},
2697 // Has new files but not ".art" file. Old ".odex" and ".vdex" files should be replaced,
2698 // and old ".art" file should be removed.
2699 ArtifactsPath{.dexPath = android_data_ + "/app/com.android.foo/base.apk",
2700 .isa = "arm64",
2701 .isInDalvikCache = false},
2702 // Has no new file. All old files should be kept.
2703 ArtifactsPath{.dexPath = android_data_ + "/app/com.android.foo/base.apk",
2704 .isa = "arm",
2705 .isInDalvikCache = false},
2706 },
2707 {
2708 // Has new file.
2709 PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "primary"},
2710 // Has no new file.
2711 PrimaryRefProfilePath{.packageName = "com.android.bar", .profileName = "primary"},
2712 },
2713 &aidl_return));
2714 EXPECT_TRUE(aidl_return);
2715
2716 CheckContent(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.dex",
2717 "new_odex_1");
2718 CheckContent(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.vdex",
2719 "new_vdex_1");
2720 CheckContent(android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.art",
2721 "new_art_1");
2722
2723 CreateFile(android_data_ + "/app/com.android.foo/oat/arm64/base.odex", "new_odex_2");
2724 CreateFile(android_data_ + "/app/com.android.foo/oat/arm64/base.vdex", "new_vdex_2");
2725 EXPECT_FALSE(std::filesystem::exists(android_data_ + "/app/com.android.foo/oat/arm64/base.art"));
2726
2727 CheckContent(android_data_ + "/app/com.android.foo/oat/arm/base.odex", "old_odex_3");
2728 CheckContent(android_data_ + "/app/com.android.foo/oat/arm/base.vdex", "old_vdex_3");
2729 CheckContent(android_data_ + "/app/com.android.foo/oat/arm/base.art", "old_art_3");
2730
2731 CheckContent(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof", "new_prof_1");
2732
2733 CheckContent(android_data_ + "/misc/profiles/ref/com.android.bar/primary.prof", "old_prof_2");
2734
2735 // All staged files are gone.
2736 EXPECT_FALSE(std::filesystem::exists(
2737 android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.dex.staged"));
2738 EXPECT_FALSE(std::filesystem::exists(
2739 android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.vdex.staged"));
2740 EXPECT_FALSE(std::filesystem::exists(
2741 android_data_ + "/dalvik-cache/arm64/system@app@[email protected]@classes.art.staged"));
2742 EXPECT_FALSE(
2743 std::filesystem::exists(android_data_ + "/app/com.android.foo/oat/arm64/base.odex.staged"));
2744 EXPECT_FALSE(
2745 std::filesystem::exists(android_data_ + "/app/com.android.foo/oat/arm64/base.vdex.staged"));
2746 EXPECT_FALSE(std::filesystem::exists(android_data_ +
2747 "/misc/profiles/ref/com.android.foo/primary.prof.staged"));
2748 }
2749
TEST_F(ArtdTest,commitPreRebootStagedFilesNoNewFile)2750 TEST_F(ArtdTest, commitPreRebootStagedFilesNoNewFile) {
2751 bool aidl_return;
2752 ASSERT_STATUS_OK(artd_->commitPreRebootStagedFiles(
2753 {
2754 ArtifactsPath{.dexPath = android_data_ + "/app/com.android.foo/base.apk",
2755 .isa = "arm",
2756 .isInDalvikCache = false},
2757 },
2758 {},
2759 &aidl_return));
2760 EXPECT_FALSE(aidl_return);
2761 }
2762
TEST_F(ArtdTest,checkPreRebootSystemRequirements)2763 TEST_F(ArtdTest, checkPreRebootSystemRequirements) {
2764 EXPECT_CALL(*mock_props_, GetProperty("ro.build.version.release")).WillRepeatedly(Return("15"));
2765 std::string chroot_dir = scratch_path_ + "/chroot";
2766 bool aidl_return;
2767
2768 constexpr const char* kTemplate = R"(
2769 # Comment.
2770 unrelated.system.property=abc
2771
2772 ro.build.version.release={}
2773 )";
2774
2775 CreateFile(chroot_dir + "/system/build.prop", ART_FORMAT(kTemplate, 15));
2776 ASSERT_STATUS_OK(artd_->checkPreRebootSystemRequirements(chroot_dir, &aidl_return));
2777 EXPECT_TRUE(aidl_return);
2778
2779 CreateFile(chroot_dir + "/system/build.prop", ART_FORMAT(kTemplate, 16));
2780 ASSERT_STATUS_OK(artd_->checkPreRebootSystemRequirements(chroot_dir, &aidl_return));
2781 EXPECT_TRUE(aidl_return);
2782
2783 CreateFile(chroot_dir + "/system/build.prop", ART_FORMAT(kTemplate, 17));
2784 ASSERT_STATUS_OK(artd_->checkPreRebootSystemRequirements(chroot_dir, &aidl_return));
2785 EXPECT_FALSE(aidl_return);
2786 }
2787
TEST_F(ArtdTest,BuildSystemProperties)2788 TEST_F(ArtdTest, BuildSystemProperties) {
2789 constexpr const char* kContent = R"(
2790 # Comment.
2791 property.foo=123
2792 property.foo?=456
2793 property.bar?=000
2794 property.bar=789
2795 property.baz?=111
2796 import /vendor/my_import.prop ro.*
2797 import=222
2798 )";
2799
2800 CreateFile(scratch_path_ + "/build.prop", kContent);
2801 BuildSystemProperties props =
2802 OR_FAIL(BuildSystemProperties::Create(scratch_path_ + "/build.prop"));
2803 EXPECT_EQ(props.GetOrEmpty("property.foo"), "123");
2804 EXPECT_EQ(props.GetOrEmpty("property.bar"), "789");
2805 EXPECT_EQ(props.GetOrEmpty("property.baz"), "111");
2806 EXPECT_EQ(props.GetOrEmpty("import"), "222");
2807 }
2808
2809 class ArtdPreRebootTest : public ArtdTest {
2810 protected:
SetUp()2811 void SetUp() override {
2812 ArtdTest::SetUp();
2813
2814 pre_reboot_tmp_dir_ = scratch_path_ + "/artd_tmp";
2815 std::filesystem::create_directories(pre_reboot_tmp_dir_);
2816 init_environ_rc_path_ = scratch_path_ + "/init.environ.rc";
2817
2818 auto mock_props = std::make_unique<NiceMock<MockSystemProperties>>();
2819 mock_props_ = mock_props.get();
2820 ON_CALL(*mock_props_, GetProperty).WillByDefault(Return(""));
2821 auto mock_exec_utils = std::make_unique<MockExecUtils>();
2822 mock_exec_utils_ = mock_exec_utils.get();
2823 artd_ = ndk::SharedRefBase::make<Artd>(Options{.is_pre_reboot = true},
2824 std::move(mock_props),
2825 std::move(mock_exec_utils),
2826 mock_kill_.AsStdFunction(),
2827 mock_fstat_.AsStdFunction(),
2828 mock_poll_.AsStdFunction(),
2829 mock_mount_.AsStdFunction(),
2830 mock_restorecon_.AsStdFunction(),
2831 pre_reboot_tmp_dir_,
2832 init_environ_rc_path_);
2833
2834 ON_CALL(mock_restorecon_, Call).WillByDefault(Return(Result<void>()));
2835
2836 constexpr const char* kInitEnvironRcTmpl = R"(
2837 on early-init
2838 export ANDROID_ART_ROOT {}
2839 export ANDROID_DATA {}
2840 )";
2841 ASSERT_TRUE(WriteStringToFile(ART_FORMAT(kInitEnvironRcTmpl, art_root_, android_data_),
2842 init_environ_rc_path_));
2843
2844 tmp_profile_path_.finalPath.get<WritableProfilePath::forPrimary>().isPreReboot = true;
2845 output_artifacts_.artifactsPath.isPreReboot = true;
2846 }
2847
2848 std::string pre_reboot_tmp_dir_;
2849 std::string init_environ_rc_path_;
2850 MockFunction<int(const char*, const char*, const char*, uint32_t, const void*)> mock_mount_;
2851 MockFunction<Result<void>(const std::string&,
2852 const std::optional<OutputArtifacts::PermissionSettings::SeContext>&,
2853 bool)>
2854 mock_restorecon_;
2855 };
2856
TEST_F(ArtdPreRebootTest,preRebootInit)2857 TEST_F(ArtdPreRebootTest, preRebootInit) {
2858 // Color the env vars to make sure that the expected values are not from the parent process but
2859 // from "/init.environ.rc".
2860 ASSERT_EQ(setenv("ANDROID_ART_ROOT", "old_value", /*replace=*/1), 0);
2861 ASSERT_EQ(setenv("ANDROID_DATA", "old_value", /*replace=*/1), 0);
2862 ASSERT_EQ(setenv("BOOTCLASSPATH", "old_value", /*replace=*/1), 0);
2863
2864 // Add an env var that doesn't get overridden, to check that it gets removed.
2865 ASSERT_EQ(setenv("FOO", "old_value", /*replace=*/1), 0);
2866
2867 InSequence seq;
2868
2869 EXPECT_CALL(*mock_exec_utils_,
2870 DoExecAndReturnCode(
2871 AllOf(WhenSplitBy("--",
2872 AllOf(Contains(art_root_ + "/bin/art_exec"),
2873 Contains("--drop-capabilities")),
2874 Contains("/apex/com.android.sdkext/bin/derive_classpath")),
2875 HasKeepFdsFor("/proc/self/fd/")),
2876 _,
2877 _))
2878 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("/proc/self/fd/", "export BOOTCLASSPATH /foo:/bar")),
2879 Return(0)));
2880
2881 EXPECT_CALL(mock_mount_,
2882 Call(StrEq(pre_reboot_tmp_dir_ + "/art_apex_data"),
2883 StrEq("/data/misc/apexdata/com.android.art"),
2884 /*fs_type=*/nullptr,
2885 MS_BIND | MS_PRIVATE,
2886 /*data=*/nullptr))
2887 .WillOnce(Return(0));
2888
2889 EXPECT_CALL(mock_mount_,
2890 Call(StrEq(pre_reboot_tmp_dir_ + "/odrefresh"),
2891 StrEq("/data/misc/odrefresh"),
2892 /*fs_type=*/nullptr,
2893 MS_BIND | MS_PRIVATE,
2894 /*data=*/nullptr))
2895 .WillOnce(Return(0));
2896
2897 EXPECT_CALL(*mock_exec_utils_,
2898 DoExecAndReturnCode(WhenSplitBy("--",
2899 AllOf(Contains(art_root_ + "/bin/art_exec"),
2900 Contains("--drop-capabilities")),
2901 AllOf(Contains(art_root_ + "/bin/odrefresh"),
2902 Contains("--only-boot-images"),
2903 Contains("--compile"))),
2904 _,
2905 _))
2906 .WillOnce(Return(0));
2907
2908 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
2909 ASSERT_STATUS_OK(artd_->createCancellationSignal(&cancellation_signal));
2910
2911 bool aidl_return;
2912 ASSERT_STATUS_OK(artd_->preRebootInit(cancellation_signal, &aidl_return));
2913 EXPECT_TRUE(aidl_return);
2914
2915 auto env_var_count = []() {
2916 int count = 0;
2917 for (char** it = environ; *it != nullptr; it++) {
2918 count++;
2919 }
2920 return count;
2921 };
2922
2923 EXPECT_EQ(getenv("ANDROID_ART_ROOT"), art_root_);
2924 EXPECT_EQ(getenv("ANDROID_DATA"), android_data_);
2925 EXPECT_STREQ(getenv("BOOTCLASSPATH"), "/foo:/bar");
2926 EXPECT_EQ(env_var_count(), 3);
2927 EXPECT_TRUE(std::filesystem::exists(pre_reboot_tmp_dir_ + "/preparation_done"));
2928
2929 // Color the env vars again to simulate that artd died and restarted.
2930 ASSERT_EQ(setenv("ANDROID_ART_ROOT", "old_value", /*replace=*/1), 0);
2931 ASSERT_EQ(setenv("ANDROID_DATA", "old_value", /*replace=*/1), 0);
2932 ASSERT_EQ(setenv("BOOTCLASSPATH", "old_value", /*replace=*/1), 0);
2933
2934 // Calling again will not involve `mount`, `derive_classpath`, or `odrefresh` but only restore env
2935 // vars.
2936 ASSERT_STATUS_OK(artd_->preRebootInit(/*in_cancellationSignal=*/nullptr, &aidl_return));
2937 EXPECT_TRUE(aidl_return);
2938 EXPECT_EQ(getenv("ANDROID_ART_ROOT"), art_root_);
2939 EXPECT_EQ(getenv("ANDROID_DATA"), android_data_);
2940 EXPECT_STREQ(getenv("BOOTCLASSPATH"), "/foo:/bar");
2941 EXPECT_EQ(env_var_count(), 3);
2942 }
2943
TEST_F(ArtdPreRebootTest,preRebootInitFailed)2944 TEST_F(ArtdPreRebootTest, preRebootInitFailed) {
2945 EXPECT_CALL(*mock_exec_utils_,
2946 DoExecAndReturnCode(Contains("/apex/com.android.sdkext/bin/derive_classpath"), _, _))
2947 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("/proc/self/fd/", "export BOOTCLASSPATH /foo:/bar")),
2948 Return(0)));
2949
2950 EXPECT_CALL(mock_mount_, Call).Times(2).WillRepeatedly(Return(0));
2951
2952 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(Contains(art_root_ + "/bin/odrefresh"), _, _))
2953 .WillOnce(Return(1));
2954
2955 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
2956 ASSERT_STATUS_OK(artd_->createCancellationSignal(&cancellation_signal));
2957
2958 bool aidl_return;
2959 ndk::ScopedAStatus status = artd_->preRebootInit(cancellation_signal, &aidl_return);
2960 EXPECT_FALSE(status.isOk());
2961 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
2962 EXPECT_STREQ(status.getMessage(), "odrefresh returned an unexpected code: 1");
2963 }
2964
TEST_F(ArtdPreRebootTest,preRebootInitNoRetry)2965 TEST_F(ArtdPreRebootTest, preRebootInitNoRetry) {
2966 // Simulate that a previous attempt failed halfway.
2967 ASSERT_TRUE(WriteStringToFile("", pre_reboot_tmp_dir_ + "/classpath.txt"));
2968
2969 bool aidl_return;
2970 ndk::ScopedAStatus status = artd_->preRebootInit(/*in_cancellationSignal=*/nullptr, &aidl_return);
2971 EXPECT_FALSE(status.isOk());
2972 EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_STATE);
2973 EXPECT_STREQ(
2974 status.getMessage(),
2975 "preRebootInit must not be concurrently called or retried after cancellation or failure");
2976 }
2977
TEST_F(ArtdPreRebootTest,preRebootInitCancelled)2978 TEST_F(ArtdPreRebootTest, preRebootInitCancelled) {
2979 EXPECT_CALL(*mock_exec_utils_,
2980 DoExecAndReturnCode(Contains("/apex/com.android.sdkext/bin/derive_classpath"), _, _))
2981 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("/proc/self/fd/", "export BOOTCLASSPATH /foo:/bar")),
2982 Return(0)));
2983
2984 EXPECT_CALL(mock_mount_, Call).Times(2).WillRepeatedly(Return(0));
2985
2986 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
2987 ASSERT_STATUS_OK(artd_->createCancellationSignal(&cancellation_signal));
2988
2989 constexpr pid_t kPid = 123;
2990 constexpr std::chrono::duration<int> kTimeout = std::chrono::seconds(1);
2991
2992 std::condition_variable process_started_cv, process_killed_cv;
2993 std::mutex mu;
2994
2995 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(Contains(art_root_ + "/bin/odrefresh"), _, _))
2996 .WillOnce([&](auto, const ExecCallbacks& callbacks, auto) {
2997 std::unique_lock<std::mutex> lock(mu);
2998 // Step 2.
2999 callbacks.on_start(kPid);
3000 process_started_cv.notify_one();
3001 EXPECT_EQ(process_killed_cv.wait_for(lock, kTimeout), std::cv_status::no_timeout);
3002 // Step 5.
3003 callbacks.on_end(kPid);
3004 return Error();
3005 });
3006
3007 EXPECT_CALL(mock_kill_, Call(-kPid, SIGKILL)).WillOnce([&](auto, auto) {
3008 // Step 4.
3009 process_killed_cv.notify_one();
3010 return 0;
3011 });
3012
3013 std::thread t;
3014 bool aidl_return;
3015 {
3016 std::unique_lock<std::mutex> lock(mu);
3017 // Step 1.
3018 t = std::thread(
3019 [&] { ASSERT_STATUS_OK(artd_->preRebootInit(cancellation_signal, &aidl_return)); });
3020 EXPECT_EQ(process_started_cv.wait_for(lock, kTimeout), std::cv_status::no_timeout);
3021 // Step 3.
3022 cancellation_signal->cancel();
3023 }
3024
3025 t.join();
3026
3027 // Step 6.
3028 EXPECT_FALSE(aidl_return);
3029 }
3030
TEST_F(ArtdPreRebootTest,dexopt)3031 TEST_F(ArtdPreRebootTest, dexopt) {
3032 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
3033
3034 dexopt_options_.generateAppImage = true;
3035
3036 EXPECT_CALL(
3037 *mock_exec_utils_,
3038 DoExecAndReturnCode(
3039 WhenSplitBy("--", _, Contains(Flag("--profile-file-fd=", FdOf(profile_file)))), _, _))
3040 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "oat")),
3041 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "vdex")),
3042 WithArg<0>(WriteToFdFlag("--app-image-fd=", "art")),
3043 Return(0)));
3044 RunDexopt();
3045
3046 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex.staged", "oat");
3047 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex.staged", "vdex");
3048 CheckContent(scratch_path_ + "/a/oat/arm64/b.art.staged", "art");
3049 }
3050
TEST_F(ArtdPreRebootTest,dexoptPreRebootProfile)3051 TEST_F(ArtdPreRebootTest, dexoptPreRebootProfile) {
3052 profile_path_->get<ProfilePath::tmpProfilePath>()
3053 .finalPath.get<WritableProfilePath::forPrimary>()
3054 .isPreReboot = true;
3055 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
3056
3057 dexopt_options_.generateAppImage = true;
3058
3059 EXPECT_CALL(
3060 *mock_exec_utils_,
3061 DoExecAndReturnCode(
3062 WhenSplitBy("--", _, Contains(Flag("--profile-file-fd=", FdOf(profile_file)))), _, _))
3063 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "oat")),
3064 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "vdex")),
3065 WithArg<0>(WriteToFdFlag("--app-image-fd=", "art")),
3066 Return(0)));
3067 RunDexopt();
3068
3069 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex.staged", "oat");
3070 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex.staged", "vdex");
3071 CheckContent(scratch_path_ + "/a/oat/arm64/b.art.staged", "art");
3072 }
3073
TEST_F(ArtdPreRebootTest,copyAndRewriteProfile)3074 TEST_F(ArtdPreRebootTest, copyAndRewriteProfile) {
3075 std::string src_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
3076 CreateFile(src_file, "valid_profile");
3077
3078 CreateFile(dex_file_);
3079
3080 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode)
3081 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "def")),
3082 Return(ProfmanResult::kCopyAndUpdateSuccess)));
3083
3084 auto [result, dst] = OR_FAIL(RunCopyAndRewriteProfile());
3085
3086 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::SUCCESS);
3087 EXPECT_THAT(dst.profilePath.tmpPath, ContainsRegex(R"re(/primary\.prof\.staged\.\w+\.tmp$)re"));
3088 CheckContent(dst.profilePath.tmpPath, "def");
3089 }
3090
TEST_F(ArtdPreRebootTest,copyAndRewriteEmbeddedProfile)3091 TEST_F(ArtdPreRebootTest, copyAndRewriteEmbeddedProfile) {
3092 TEST_DISABLED_FOR_SHELL_WITHOUT_MEMFD_ACCESS();
3093
3094 CreateZipWithSingleEntry(dex_file_, "assets/art-profile/baseline.prof", "valid_profile");
3095
3096 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode)
3097 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "def")),
3098 Return(ProfmanResult::kCopyAndUpdateSuccess)));
3099
3100 auto [result, dst] = OR_FAIL(RunCopyAndRewriteEmbeddedProfile());
3101
3102 EXPECT_EQ(result.status, CopyAndRewriteProfileResult::Status::SUCCESS);
3103 EXPECT_THAT(dst.profilePath.tmpPath, ContainsRegex(R"re(/primary\.prof\.staged\.\w+\.tmp$)re"));
3104 CheckContent(dst.profilePath.tmpPath, "def");
3105 }
3106
TEST_F(ArtdPreRebootTest,mergeProfiles)3107 TEST_F(ArtdPreRebootTest, mergeProfiles) {
3108 std::string reference_profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
3109 CreateFile(reference_profile_file, "abc");
3110
3111 PrimaryCurProfilePath profile_1_path{
3112 .userId = 1, .packageName = "com.android.foo", .profileName = "primary"};
3113 std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path));
3114 CreateFile(profile_1_file, "def");
3115
3116 OutputProfile output_profile{.profilePath = tmp_profile_path_,
3117 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
3118 output_profile.profilePath.id = "";
3119 output_profile.profilePath.tmpPath = "";
3120
3121 std::string dex_file_1 = scratch_path_ + "/a/b.apk";
3122 CreateFile(dex_file_1);
3123
3124 EXPECT_CALL(
3125 *mock_exec_utils_,
3126 DoExecAndReturnCode(
3127 WhenSplitBy("--",
3128 _,
3129 AllOf(Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))),
3130 Contains(Flag("--profile-file-fd=", FdHasContent("def"))))),
3131 _,
3132 _))
3133 .WillOnce(DoAll(WithArg<0>(ClearAndWriteToFdFlag("--reference-profile-file-fd=", "merged")),
3134 Return(ProfmanResult::kCompile)));
3135
3136 bool result;
3137 ASSERT_STATUS_OK(artd_->mergeProfiles({profile_1_path},
3138 profile_path_,
3139 &output_profile,
3140 {dex_file_1},
3141 /*in_options=*/{},
3142 &result));
3143 EXPECT_TRUE(result);
3144 EXPECT_THAT(output_profile.profilePath.tmpPath,
3145 ContainsRegex(R"re(/primary\.prof\.staged\.\w+\.tmp$)re"));
3146 CheckContent(output_profile.profilePath.tmpPath, "merged");
3147 }
3148
TEST_F(ArtdPreRebootTest,mergeProfilesPreRebootReference)3149 TEST_F(ArtdPreRebootTest, mergeProfilesPreRebootReference) {
3150 profile_path_->get<ProfilePath::tmpProfilePath>()
3151 .finalPath.get<WritableProfilePath::forPrimary>()
3152 .isPreReboot = true;
3153 std::string reference_profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
3154 CreateFile(reference_profile_file, "abc");
3155
3156 PrimaryCurProfilePath profile_1_path{
3157 .userId = 1, .packageName = "com.android.foo", .profileName = "primary"};
3158 std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path));
3159 CreateFile(profile_1_file, "def");
3160
3161 OutputProfile output_profile{.profilePath = tmp_profile_path_,
3162 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
3163 output_profile.profilePath.id = "";
3164 output_profile.profilePath.tmpPath = "";
3165
3166 std::string dex_file_1 = scratch_path_ + "/a/b.apk";
3167 CreateFile(dex_file_1);
3168
3169 EXPECT_CALL(
3170 *mock_exec_utils_,
3171 DoExecAndReturnCode(
3172 WhenSplitBy("--",
3173 _,
3174 AllOf(Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))),
3175 Contains(Flag("--profile-file-fd=", FdHasContent("def"))))),
3176 _,
3177 _))
3178 .WillOnce(DoAll(WithArg<0>(ClearAndWriteToFdFlag("--reference-profile-file-fd=", "merged")),
3179 Return(ProfmanResult::kCompile)));
3180
3181 bool result;
3182 ASSERT_STATUS_OK(artd_->mergeProfiles({profile_1_path},
3183 profile_path_,
3184 &output_profile,
3185 {dex_file_1},
3186 /*in_options=*/{},
3187 &result));
3188 EXPECT_TRUE(result);
3189 EXPECT_THAT(output_profile.profilePath.tmpPath,
3190 ContainsRegex(R"re(/primary\.prof\.staged\.\w+\.tmp$)re"));
3191 CheckContent(output_profile.profilePath.tmpPath, "merged");
3192 }
3193
3194 } // namespace
3195 } // namespace artd
3196 } // namespace art
3197