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 <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #include <algorithm>
25 #include <chrono>
26 #include <condition_variable>
27 #include <csignal>
28 #include <filesystem>
29 #include <functional>
30 #include <memory>
31 #include <mutex>
32 #include <optional>
33 #include <string>
34 #include <thread>
35 #include <type_traits>
36 #include <unordered_set>
37 #include <utility>
38 #include <vector>
39
40 #include "aidl/com/android/server/art/ArtConstants.h"
41 #include "aidl/com/android/server/art/BnArtd.h"
42 #include "android-base/collections.h"
43 #include "android-base/errors.h"
44 #include "android-base/file.h"
45 #include "android-base/logging.h"
46 #include "android-base/parseint.h"
47 #include "android-base/result.h"
48 #include "android-base/scopeguard.h"
49 #include "android-base/strings.h"
50 #include "android/binder_auto_utils.h"
51 #include "android/binder_status.h"
52 #include "base/array_ref.h"
53 #include "base/common_art_test.h"
54 #include "exec_utils.h"
55 #include "fmt/format.h"
56 #include "gmock/gmock.h"
57 #include "gtest/gtest.h"
58 #include "oat_file.h"
59 #include "path_utils.h"
60 #include "profman/profman_result.h"
61 #include "testing.h"
62 #include "tools/system_properties.h"
63
64 namespace art {
65 namespace artd {
66 namespace {
67
68 using ::aidl::com::android::server::art::ArtConstants;
69 using ::aidl::com::android::server::art::ArtdDexoptResult;
70 using ::aidl::com::android::server::art::ArtifactsPath;
71 using ::aidl::com::android::server::art::DexMetadataPath;
72 using ::aidl::com::android::server::art::DexoptOptions;
73 using ::aidl::com::android::server::art::FileVisibility;
74 using ::aidl::com::android::server::art::FsPermission;
75 using ::aidl::com::android::server::art::GetDexoptStatusResult;
76 using ::aidl::com::android::server::art::IArtdCancellationSignal;
77 using ::aidl::com::android::server::art::OutputArtifacts;
78 using ::aidl::com::android::server::art::OutputProfile;
79 using ::aidl::com::android::server::art::PriorityClass;
80 using ::aidl::com::android::server::art::ProfilePath;
81 using ::aidl::com::android::server::art::VdexPath;
82 using ::android::base::Append;
83 using ::android::base::Error;
84 using ::android::base::make_scope_guard;
85 using ::android::base::ParseInt;
86 using ::android::base::ReadFdToString;
87 using ::android::base::ReadFileToString;
88 using ::android::base::Result;
89 using ::android::base::ScopeGuard;
90 using ::android::base::Split;
91 using ::android::base::WriteStringToFd;
92 using ::android::base::WriteStringToFile;
93 using ::testing::_;
94 using ::testing::AllOf;
95 using ::testing::AnyNumber;
96 using ::testing::AnyOf;
97 using ::testing::Contains;
98 using ::testing::ContainsRegex;
99 using ::testing::DoAll;
100 using ::testing::ElementsAre;
101 using ::testing::Field;
102 using ::testing::HasSubstr;
103 using ::testing::IsEmpty;
104 using ::testing::Matcher;
105 using ::testing::MockFunction;
106 using ::testing::Not;
107 using ::testing::Property;
108 using ::testing::ResultOf;
109 using ::testing::Return;
110 using ::testing::SetArgPointee;
111 using ::testing::UnorderedElementsAreArray;
112 using ::testing::WithArg;
113
114 using PrimaryCurProfilePath = ProfilePath::PrimaryCurProfilePath;
115 using PrimaryRefProfilePath = ProfilePath::PrimaryRefProfilePath;
116 using TmpProfilePath = ProfilePath::TmpProfilePath;
117
118 using ::fmt::literals::operator""_format; // NOLINT
119
120 constexpr uid_t kRootUid = 0;
121
ScopedSetLogger(android::base::LogFunction && logger)122 ScopeGuard<std::function<void()>> ScopedSetLogger(android::base::LogFunction&& logger) {
123 android::base::LogFunction old_logger = android::base::SetLogger(std::move(logger));
124 return make_scope_guard([old_logger = std::move(old_logger)]() mutable {
125 android::base::SetLogger(std::move(old_logger));
126 });
127 }
128
CheckContent(const std::string & path,const std::string & expected_content)129 void CheckContent(const std::string& path, const std::string& expected_content) {
130 std::string actual_content;
131 ASSERT_TRUE(ReadFileToString(path, &actual_content));
132 EXPECT_EQ(actual_content, expected_content);
133 }
134
CheckOtherReadable(const std::string & path,bool expected_value)135 void CheckOtherReadable(const std::string& path, bool expected_value) {
136 EXPECT_EQ((std::filesystem::status(path).permissions() & std::filesystem::perms::others_read) !=
137 std::filesystem::perms::none,
138 expected_value);
139 }
140
GetFlagValues(ArrayRef<const std::string> args,std::string_view flag)141 Result<std::vector<std::string>> GetFlagValues(ArrayRef<const std::string> args,
142 std::string_view flag) {
143 std::vector<std::string> values;
144 for (const std::string& arg : args) {
145 std::string_view value(arg);
146 if (android::base::ConsumePrefix(&value, flag)) {
147 values.emplace_back(value);
148 }
149 }
150 if (values.empty()) {
151 return Errorf("Flag '{}' not found", flag);
152 }
153 return values;
154 }
155
GetFlagValue(ArrayRef<const std::string> args,std::string_view flag)156 Result<std::string> GetFlagValue(ArrayRef<const std::string> args, std::string_view flag) {
157 std::vector<std::string> flag_values = OR_RETURN(GetFlagValues(args, flag));
158 if (flag_values.size() > 1) {
159 return Errorf("Duplicate flag '{}'", flag);
160 }
161 return flag_values[0];
162 }
163
WriteToFdFlagImpl(const std::vector<std::string> & args,std::string_view flag,std::string_view content,bool assume_empty)164 void WriteToFdFlagImpl(const std::vector<std::string>& args,
165 std::string_view flag,
166 std::string_view content,
167 bool assume_empty) {
168 std::string value = OR_FAIL(GetFlagValue(ArrayRef<const std::string>(args), flag));
169 ASSERT_NE(value, "");
170 int fd;
171 ASSERT_TRUE(ParseInt(value, &fd));
172 if (assume_empty) {
173 ASSERT_EQ(lseek(fd, /*offset=*/0, SEEK_CUR), 0);
174 } else {
175 ASSERT_EQ(ftruncate(fd, /*length=*/0), 0);
176 ASSERT_EQ(lseek(fd, /*offset=*/0, SEEK_SET), 0);
177 }
178 ASSERT_TRUE(WriteStringToFd(content, fd));
179 }
180
181 // Writes `content` to the FD specified by the `flag`.
ACTION_P(WriteToFdFlag,flag,content)182 ACTION_P(WriteToFdFlag, flag, content) {
183 WriteToFdFlagImpl(arg0, flag, content, /*assume_empty=*/true);
184 }
185
186 // Clears any existing content and writes `content` to the FD specified by the `flag`.
ACTION_P(ClearAndWriteToFdFlag,flag,content)187 ACTION_P(ClearAndWriteToFdFlag, flag, content) {
188 WriteToFdFlagImpl(arg0, flag, content, /*assume_empty=*/false);
189 }
190
191 // Matches a flag that starts with `flag` and whose value matches `matcher`.
192 MATCHER_P2(Flag, flag, matcher, "") {
193 std::string_view value(arg);
194 if (!android::base::ConsumePrefix(&value, flag)) {
195 return false;
196 }
197 return ExplainMatchResult(matcher, std::string(value), result_listener);
198 }
199
200 // Matches a flag that starts with `flag` and whose value is a colon-separated list that matches
201 // `matcher`. The matcher acts on an `std::vector<std::string>` of the split list argument.
202 MATCHER_P2(ListFlag, flag, matcher, "") {
203 return ExplainMatchResult(
204 Flag(flag, ResultOf(std::bind(Split, std::placeholders::_1, ":"), matcher)),
205 arg,
206 result_listener);
207 }
208
209 // Matches an FD of a file whose path matches `matcher`.
210 MATCHER_P(FdOf, matcher, "") {
211 std::string proc_path = "/proc/self/fd/{}"_format(arg);
212 char path[PATH_MAX];
213 ssize_t len = readlink(proc_path.c_str(), path, sizeof(path));
214 if (len < 0) {
215 return false;
216 }
217 return ExplainMatchResult(matcher, std::string(path, static_cast<size_t>(len)), result_listener);
218 }
219
220 // Matches an FD of a file whose content matches `matcher`.
221 MATCHER_P(FdHasContent, matcher, "") {
222 int fd;
223 if (!ParseInt(arg, &fd)) {
224 return false;
225 }
226 std::string actual_content;
227 if (!ReadFdToString(fd, &actual_content)) {
228 return false;
229 }
230 return ExplainMatchResult(matcher, actual_content, result_listener);
231 }
232
233 template <typename T, typename U>
SplitBy(const std::vector<T> & list,const U & separator)234 Result<std::pair<ArrayRef<const T>, ArrayRef<const T>>> SplitBy(const std::vector<T>& list,
235 const U& separator) {
236 auto it = std::find(list.begin(), list.end(), separator);
237 if (it == list.end()) {
238 return Errorf("'{}' not found", separator);
239 }
240 size_t pos = it - list.begin();
241 return std::make_pair(ArrayRef<const T>(list).SubArray(0, pos),
242 ArrayRef<const T>(list).SubArray(pos + 1));
243 }
244
245 // Matches a container that, when split by `separator`, the first part matches `head_matcher`, and
246 // the second part matches `tail_matcher`.
247 MATCHER_P3(WhenSplitBy, separator, head_matcher, tail_matcher, "") {
248 auto [head, tail] = OR_MISMATCH(SplitBy(arg, separator));
249 return ExplainMatchResult(head_matcher, head, result_listener) &&
250 ExplainMatchResult(tail_matcher, tail, result_listener);
251 }
252
253 MATCHER_P(HasKeepFdsForImpl, fd_flags, "") {
254 auto [head, tail] = OR_MISMATCH(SplitBy(arg, "--"));
255 std::string keep_fds_value = OR_MISMATCH(GetFlagValue(head, "--keep-fds="));
256 std::vector<std::string> keep_fds = Split(keep_fds_value, ":");
257 std::vector<std::string> fd_flag_values;
258 for (std::string_view fd_flag : fd_flags) {
259 for (const std::string& fd_flag_value : OR_MISMATCH(GetFlagValues(tail, fd_flag))) {
260 for (std::string& fd : Split(fd_flag_value, ":")) {
261 fd_flag_values.push_back(std::move(fd));
262 }
263 }
264 }
265 return ExplainMatchResult(UnorderedElementsAreArray(fd_flag_values), keep_fds, result_listener);
266 }
267
268 // Matches an argument list that has the "--keep-fds=" flag before "--", whose value is a
269 // semicolon-separated list that contains exactly the values of the given flags after "--".
270 //
271 // E.g., if the flags after "--" are "--foo=1", "--bar=2:3", "--baz=4", "--baz=5", and the matcher
272 // is `HasKeepFdsFor("--foo=", "--bar=", "--baz=")`, then it requires the "--keep-fds=" flag before
273 // "--" to contain exactly 1, 2, 3, 4, and 5.
274 template <typename... Args>
HasKeepFdsFor(Args &&...args)275 auto HasKeepFdsFor(Args&&... args) {
276 std::vector<std::string_view> fd_flags;
277 Append(fd_flags, std::forward<Args>(args)...);
278 return HasKeepFdsForImpl(fd_flags);
279 }
280
281 class MockSystemProperties : public tools::SystemProperties {
282 public:
283 MOCK_METHOD(std::string, GetProperty, (const std::string& key), (const, override));
284 };
285
286 class MockExecUtils : public ExecUtils {
287 public:
288 // A workaround to avoid MOCK_METHOD on a method with an `std::string*` parameter, which will lead
289 // to a conflict between gmock and android-base/logging.h (b/132668253).
ExecAndReturnResult(const std::vector<std::string> & arg_vector,int,const ExecCallbacks & callbacks,ProcessStat * stat,std::string *) const290 ExecResult ExecAndReturnResult(const std::vector<std::string>& arg_vector,
291 int,
292 const ExecCallbacks& callbacks,
293 ProcessStat* stat,
294 std::string*) const override {
295 Result<int> code = DoExecAndReturnCode(arg_vector, callbacks, stat);
296 if (code.ok()) {
297 return {.status = ExecResult::kExited, .exit_code = code.value()};
298 }
299 return {.status = ExecResult::kUnknown};
300 }
301
302 MOCK_METHOD(Result<int>,
303 DoExecAndReturnCode,
304 (const std::vector<std::string>& arg_vector,
305 const ExecCallbacks& callbacks,
306 ProcessStat* stat),
307 (const));
308 };
309
310 class ArtdTest : public CommonArtTest {
311 protected:
SetUp()312 void SetUp() override {
313 CommonArtTest::SetUp();
314 auto mock_props = std::make_unique<MockSystemProperties>();
315 mock_props_ = mock_props.get();
316 EXPECT_CALL(*mock_props_, GetProperty).Times(AnyNumber()).WillRepeatedly(Return(""));
317 auto mock_exec_utils = std::make_unique<MockExecUtils>();
318 mock_exec_utils_ = mock_exec_utils.get();
319 artd_ = ndk::SharedRefBase::make<Artd>(std::move(mock_props),
320 std::move(mock_exec_utils),
321 mock_kill_.AsStdFunction(),
322 mock_fstat_.AsStdFunction());
323 scratch_dir_ = std::make_unique<ScratchDir>();
324 scratch_path_ = scratch_dir_->GetPath();
325 // Remove the trailing '/';
326 scratch_path_.resize(scratch_path_.length() - 1);
327
328 ON_CALL(mock_fstat_, Call).WillByDefault(fstat);
329
330 // Use an arbitrary existing directory as ART root.
331 art_root_ = scratch_path_ + "/com.android.art";
332 std::filesystem::create_directories(art_root_);
333 setenv("ANDROID_ART_ROOT", art_root_.c_str(), /*overwrite=*/1);
334
335 // Use an arbitrary existing directory as Android data.
336 android_data_ = scratch_path_ + "/data";
337 std::filesystem::create_directories(android_data_);
338 setenv("ANDROID_DATA", android_data_.c_str(), /*overwrite=*/1);
339
340 // Use an arbitrary existing directory as Android expand.
341 android_expand_ = scratch_path_ + "/mnt/expand";
342 std::filesystem::create_directories(android_expand_);
343 setenv("ANDROID_EXPAND", android_expand_.c_str(), /*overwrite=*/1);
344
345 dex_file_ = scratch_path_ + "/a/b.apk";
346 isa_ = "arm64";
347 artifacts_path_ = ArtifactsPath{
348 .dexPath = dex_file_,
349 .isa = isa_,
350 .isInDalvikCache = false,
351 };
352 struct stat st;
353 ASSERT_EQ(stat(scratch_path_.c_str(), &st), 0);
354 output_artifacts_ = OutputArtifacts{
355 .artifactsPath = artifacts_path_,
356 .permissionSettings =
357 OutputArtifacts::PermissionSettings{
358 .dirFsPermission =
359 FsPermission{
360 .uid = static_cast<int32_t>(st.st_uid),
361 .gid = static_cast<int32_t>(st.st_gid),
362 .isOtherReadable = true,
363 .isOtherExecutable = true,
364 },
365 .fileFsPermission =
366 FsPermission{
367 .uid = static_cast<int32_t>(st.st_uid),
368 .gid = static_cast<int32_t>(st.st_gid),
369 .isOtherReadable = true,
370 },
371 },
372 };
373 clc_1_ = GetTestDexFileName("Main");
374 clc_2_ = GetTestDexFileName("Nested");
375 class_loader_context_ = "PCL[{}:{}]"_format(clc_1_, clc_2_);
376 compiler_filter_ = "speed";
377 TmpProfilePath tmp_profile_path{
378 .finalPath =
379 PrimaryRefProfilePath{.packageName = "com.android.foo", .profileName = "primary"},
380 .id = "12345"};
381 profile_path_ = tmp_profile_path;
382 vdex_path_ = artifacts_path_;
383 dm_path_ = DexMetadataPath{.dexPath = dex_file_};
384 std::filesystem::create_directories(
385 std::filesystem::path(OR_FATAL(BuildFinalProfilePath(tmp_profile_path))).parent_path());
386 }
387
TearDown()388 void TearDown() override {
389 scratch_dir_.reset();
390 CommonArtTest::TearDown();
391 }
392
RunDexopt(binder_exception_t expected_status=EX_NONE,Matcher<ArtdDexoptResult> aidl_return_matcher=Field (& ArtdDexoptResult::cancelled,false),std::shared_ptr<IArtdCancellationSignal> cancellation_signal=nullptr)393 void RunDexopt(binder_exception_t expected_status = EX_NONE,
394 Matcher<ArtdDexoptResult> aidl_return_matcher = Field(&ArtdDexoptResult::cancelled,
395 false),
396 std::shared_ptr<IArtdCancellationSignal> cancellation_signal = nullptr) {
397 RunDexopt(Property(&ndk::ScopedAStatus::getExceptionCode, expected_status),
398 std::move(aidl_return_matcher),
399 cancellation_signal);
400 }
401
RunDexopt(Matcher<ndk::ScopedAStatus> status_matcher,Matcher<ArtdDexoptResult> aidl_return_matcher=Field (& ArtdDexoptResult::cancelled,false),std::shared_ptr<IArtdCancellationSignal> cancellation_signal=nullptr)402 void RunDexopt(Matcher<ndk::ScopedAStatus> status_matcher,
403 Matcher<ArtdDexoptResult> aidl_return_matcher = Field(&ArtdDexoptResult::cancelled,
404 false),
405 std::shared_ptr<IArtdCancellationSignal> cancellation_signal = nullptr) {
406 InitFilesBeforeDexopt();
407 if (cancellation_signal == nullptr) {
408 ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
409 }
410 ArtdDexoptResult aidl_return;
411 ndk::ScopedAStatus status = artd_->dexopt(output_artifacts_,
412 dex_file_,
413 isa_,
414 class_loader_context_,
415 compiler_filter_,
416 profile_path_,
417 vdex_path_,
418 dm_path_,
419 priority_class_,
420 dexopt_options_,
421 cancellation_signal,
422 &aidl_return);
423 ASSERT_THAT(status, std::move(status_matcher)) << status.getMessage();
424 if (status.isOk()) {
425 ASSERT_THAT(aidl_return, std::move(aidl_return_matcher));
426 }
427 }
428
CreateFile(const std::string & filename,const std::string & content="")429 void CreateFile(const std::string& filename, const std::string& content = "") {
430 std::filesystem::path path(filename);
431 std::filesystem::create_directories(path.parent_path());
432 ASSERT_TRUE(WriteStringToFile(content, filename));
433 }
434
435 std::shared_ptr<Artd> artd_;
436 std::unique_ptr<ScratchDir> scratch_dir_;
437 std::string scratch_path_;
438 std::string art_root_;
439 std::string android_data_;
440 std::string android_expand_;
441 MockFunction<android::base::LogFunction> mock_logger_;
442 ScopedUnsetEnvironmentVariable art_root_env_ = ScopedUnsetEnvironmentVariable("ANDROID_ART_ROOT");
443 ScopedUnsetEnvironmentVariable android_data_env_ = ScopedUnsetEnvironmentVariable("ANDROID_DATA");
444 ScopedUnsetEnvironmentVariable android_expand_env_ =
445 ScopedUnsetEnvironmentVariable("ANDROID_EXPAND");
446 MockSystemProperties* mock_props_;
447 MockExecUtils* mock_exec_utils_;
448 MockFunction<int(pid_t, int)> mock_kill_;
449 MockFunction<int(int, struct stat*)> mock_fstat_;
450
451 std::string dex_file_;
452 std::string isa_;
453 ArtifactsPath artifacts_path_;
454 OutputArtifacts output_artifacts_;
455 std::string clc_1_;
456 std::string clc_2_;
457 std::optional<std::string> class_loader_context_;
458 std::string compiler_filter_;
459 std::optional<VdexPath> vdex_path_;
460 std::optional<DexMetadataPath> dm_path_;
461 PriorityClass priority_class_ = PriorityClass::BACKGROUND;
462 DexoptOptions dexopt_options_;
463 std::optional<ProfilePath> profile_path_;
464 bool dex_file_other_readable_ = true;
465 bool profile_other_readable_ = true;
466
467 private:
InitFilesBeforeDexopt()468 void InitFilesBeforeDexopt() {
469 // Required files.
470 CreateFile(dex_file_);
471 std::filesystem::permissions(dex_file_,
472 std::filesystem::perms::others_read,
473 dex_file_other_readable_ ? std::filesystem::perm_options::add :
474 std::filesystem::perm_options::remove);
475
476 // Optional files.
477 if (vdex_path_.has_value()) {
478 CreateFile(OR_FATAL(BuildVdexPath(vdex_path_.value())), "old_vdex");
479 }
480 if (dm_path_.has_value()) {
481 CreateFile(OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
482 }
483 if (profile_path_.has_value()) {
484 std::string path = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
485 CreateFile(path);
486 std::filesystem::permissions(path,
487 std::filesystem::perms::others_read,
488 profile_other_readable_ ? std::filesystem::perm_options::add :
489 std::filesystem::perm_options::remove);
490 }
491
492 // Files to be replaced.
493 std::string oat_path = OR_FATAL(BuildOatPath(artifacts_path_));
494 CreateFile(oat_path, "old_oat");
495 CreateFile(OatPathToVdexPath(oat_path), "old_vdex");
496 CreateFile(OatPathToArtPath(oat_path), "old_art");
497 }
498 };
499
TEST_F(ArtdTest,ConstantsAreInSync)500 TEST_F(ArtdTest, ConstantsAreInSync) { EXPECT_EQ(ArtConstants::REASON_VDEX, kReasonVdex); }
501
TEST_F(ArtdTest,isAlive)502 TEST_F(ArtdTest, isAlive) {
503 bool result = false;
504 artd_->isAlive(&result);
505 EXPECT_TRUE(result);
506 }
507
TEST_F(ArtdTest,deleteArtifacts)508 TEST_F(ArtdTest, deleteArtifacts) {
509 std::string oat_dir = scratch_path_ + "/a/oat/arm64";
510 std::filesystem::create_directories(oat_dir);
511 ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
512 ASSERT_TRUE(WriteStringToFile("ab", oat_dir + "/b.vdex")); // 2 bytes.
513 ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
514
515 int64_t result = -1;
516 EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
517 EXPECT_EQ(result, 4 + 2 + 1);
518
519 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.odex"));
520 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.vdex"));
521 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.art"));
522 }
523
TEST_F(ArtdTest,deleteArtifactsMissingFile)524 TEST_F(ArtdTest, deleteArtifactsMissingFile) {
525 // Missing VDEX file.
526 std::string oat_dir = android_data_ + "/dalvik-cache/arm64";
527 std::filesystem::create_directories(oat_dir);
528 ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/a@b.apk@classes.dex")); // 4 bytes.
529 ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/a@b.apk@classes.art")); // 1 byte.
530
531 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
532 EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(0);
533
534 int64_t result = -1;
535 EXPECT_TRUE(artd_
536 ->deleteArtifacts(
537 ArtifactsPath{
538 .dexPath = "/a/b.apk",
539 .isa = "arm64",
540 .isInDalvikCache = true,
541 },
542 &result)
543 .isOk());
544 EXPECT_EQ(result, 4 + 1);
545
546 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/a@b.apk@classes.dex"));
547 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/a@b.apk@classes.art"));
548 }
549
TEST_F(ArtdTest,deleteArtifactsNoFile)550 TEST_F(ArtdTest, deleteArtifactsNoFile) {
551 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
552 EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(0);
553
554 int64_t result = -1;
555 EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
556 EXPECT_EQ(result, 0);
557 }
558
TEST_F(ArtdTest,deleteArtifactsPermissionDenied)559 TEST_F(ArtdTest, deleteArtifactsPermissionDenied) {
560 std::string oat_dir = scratch_path_ + "/a/oat/arm64";
561 std::filesystem::create_directories(oat_dir);
562 ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
563 ASSERT_TRUE(WriteStringToFile("ab", oat_dir + "/b.vdex")); // 2 bytes.
564 ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
565
566 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
567 EXPECT_CALL(mock_logger_, Call(_, _, _, _, _, HasSubstr("Failed to get the file size"))).Times(3);
568
569 auto scoped_inaccessible = ScopedInaccessible(oat_dir);
570 auto scoped_unroot = ScopedUnroot();
571
572 int64_t result = -1;
573 EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
574 EXPECT_EQ(result, 0);
575 }
576
TEST_F(ArtdTest,deleteArtifactsFileIsDir)577 TEST_F(ArtdTest, deleteArtifactsFileIsDir) {
578 // VDEX file is a directory.
579 std::string oat_dir = scratch_path_ + "/a/oat/arm64";
580 std::filesystem::create_directories(oat_dir);
581 std::filesystem::create_directories(oat_dir + "/b.vdex");
582 ASSERT_TRUE(WriteStringToFile("abcd", oat_dir + "/b.odex")); // 4 bytes.
583 ASSERT_TRUE(WriteStringToFile("a", oat_dir + "/b.art")); // 1 byte.
584
585 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
586 EXPECT_CALL(mock_logger_,
587 Call(_, _, _, _, _, ContainsRegex(R"re(Failed to get the file size.*b\.vdex)re")))
588 .Times(1);
589
590 int64_t result = -1;
591 EXPECT_TRUE(artd_->deleteArtifacts(artifacts_path_, &result).isOk());
592 EXPECT_EQ(result, 4 + 1);
593
594 // The directory is kept because getting the file size failed.
595 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.odex"));
596 EXPECT_TRUE(std::filesystem::exists(oat_dir + "/b.vdex"));
597 EXPECT_FALSE(std::filesystem::exists(oat_dir + "/b.art"));
598 }
599
TEST_F(ArtdTest,dexopt)600 TEST_F(ArtdTest, dexopt) {
601 dexopt_options_.generateAppImage = true;
602
603 EXPECT_CALL(
604 *mock_exec_utils_,
605 DoExecAndReturnCode(
606 AllOf(WhenSplitBy(
607 "--",
608 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
609 AllOf(Contains(art_root_ + "/bin/dex2oat32"),
610 Contains(Flag("--zip-fd=", FdOf(dex_file_))),
611 Contains(Flag("--zip-location=", dex_file_)),
612 Contains(Flag("--oat-location=", scratch_path_ + "/a/oat/arm64/b.odex")),
613 Contains(Flag("--instruction-set=", "arm64")),
614 Contains(Flag("--compiler-filter=", "speed")),
615 Contains(Flag(
616 "--profile-file-fd=",
617 FdOf(android_data_ +
618 "/misc/profiles/ref/com.android.foo/primary.prof.12345.tmp"))),
619 Contains(Flag("--input-vdex-fd=",
620 FdOf(scratch_path_ + "/a/oat/arm64/b.vdex"))),
621 Contains(Flag("--dm-fd=", FdOf(scratch_path_ + "/a/b.dm"))))),
622 HasKeepFdsFor("--zip-fd=",
623 "--profile-file-fd=",
624 "--input-vdex-fd=",
625 "--dm-fd=",
626 "--oat-fd=",
627 "--output-vdex-fd=",
628 "--app-image-fd=",
629 "--class-loader-context-fds=",
630 "--swap-fd=")),
631 _,
632 _))
633 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "oat")),
634 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "vdex")),
635 WithArg<0>(WriteToFdFlag("--app-image-fd=", "art")),
636 SetArgPointee<2>(ProcessStat{.wall_time_ms = 100, .cpu_time_ms = 400}),
637 Return(0)));
638 RunDexopt(
639 EX_NONE,
640 AllOf(Field(&ArtdDexoptResult::cancelled, false),
641 Field(&ArtdDexoptResult::wallTimeMs, 100),
642 Field(&ArtdDexoptResult::cpuTimeMs, 400),
643 Field(&ArtdDexoptResult::sizeBytes, strlen("art") + strlen("oat") + strlen("vdex")),
644 Field(&ArtdDexoptResult::sizeBeforeBytes,
645 strlen("old_art") + strlen("old_oat") + strlen("old_vdex"))));
646
647 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "oat");
648 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "vdex");
649 CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "art");
650 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.odex", true);
651 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.vdex", true);
652 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.art", true);
653 }
654
TEST_F(ArtdTest,dexoptClassLoaderContext)655 TEST_F(ArtdTest, dexoptClassLoaderContext) {
656 EXPECT_CALL(
657 *mock_exec_utils_,
658 DoExecAndReturnCode(
659 WhenSplitBy("--",
660 _,
661 AllOf(Contains(ListFlag("--class-loader-context-fds=",
662 ElementsAre(FdOf(clc_1_), FdOf(clc_2_)))),
663 Contains(Flag("--class-loader-context=", class_loader_context_)),
664 Contains(Flag("--classpath-dir=", scratch_path_ + "/a")))),
665 _,
666 _))
667 .WillOnce(Return(0));
668 RunDexopt();
669 }
670
TEST_F(ArtdTest,dexoptClassLoaderContextNull)671 TEST_F(ArtdTest, dexoptClassLoaderContextNull) {
672 class_loader_context_ = std::nullopt;
673
674 EXPECT_CALL(
675 *mock_exec_utils_,
676 DoExecAndReturnCode(WhenSplitBy("--",
677 _,
678 AllOf(Not(Contains(Flag("--class-loader-context-fds=", _))),
679 Not(Contains(Flag("--class-loader-context=", _))),
680 Not(Contains(Flag("--classpath-dir=", _))))),
681 _,
682 _))
683 .WillOnce(Return(0));
684 RunDexopt();
685 }
686
TEST_F(ArtdTest,dexoptNoOptionalInputFiles)687 TEST_F(ArtdTest, dexoptNoOptionalInputFiles) {
688 profile_path_ = std::nullopt;
689 vdex_path_ = std::nullopt;
690 dm_path_ = std::nullopt;
691
692 EXPECT_CALL(*mock_exec_utils_,
693 DoExecAndReturnCode(WhenSplitBy("--",
694 _,
695 AllOf(Not(Contains(Flag("--profile-file-fd=", _))),
696 Not(Contains(Flag("--input-vdex-fd=", _))),
697 Not(Contains(Flag("--dm-fd=", _))))),
698 _,
699 _))
700 .WillOnce(Return(0));
701 RunDexopt();
702 }
703
TEST_F(ArtdTest,dexoptPriorityClassBoot)704 TEST_F(ArtdTest, dexoptPriorityClassBoot) {
705 priority_class_ = PriorityClass::BOOT;
706 EXPECT_CALL(*mock_exec_utils_,
707 DoExecAndReturnCode(WhenSplitBy("--",
708 AllOf(Not(Contains(Flag("--set-task-profile=", _))),
709 Not(Contains(Flag("--set-priority=", _)))),
710 Contains(Flag("--compact-dex-level=", "none"))),
711 _,
712 _))
713 .WillOnce(Return(0));
714 RunDexopt();
715 }
716
TEST_F(ArtdTest,dexoptPriorityClassInteractive)717 TEST_F(ArtdTest, dexoptPriorityClassInteractive) {
718 priority_class_ = PriorityClass::INTERACTIVE;
719 EXPECT_CALL(*mock_exec_utils_,
720 DoExecAndReturnCode(
721 WhenSplitBy("--",
722 AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBootComplete")),
723 Contains(Flag("--set-priority=", "background"))),
724 Contains(Flag("--compact-dex-level=", "none"))),
725 _,
726 _))
727 .WillOnce(Return(0));
728 RunDexopt();
729 }
730
TEST_F(ArtdTest,dexoptPriorityClassInteractiveFast)731 TEST_F(ArtdTest, dexoptPriorityClassInteractiveFast) {
732 priority_class_ = PriorityClass::INTERACTIVE_FAST;
733 EXPECT_CALL(*mock_exec_utils_,
734 DoExecAndReturnCode(
735 WhenSplitBy("--",
736 AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBootComplete")),
737 Contains(Flag("--set-priority=", "background"))),
738 Contains(Flag("--compact-dex-level=", "none"))),
739 _,
740 _))
741 .WillOnce(Return(0));
742 RunDexopt();
743 }
744
TEST_F(ArtdTest,dexoptPriorityClassBackground)745 TEST_F(ArtdTest, dexoptPriorityClassBackground) {
746 priority_class_ = PriorityClass::BACKGROUND;
747 EXPECT_CALL(*mock_exec_utils_,
748 DoExecAndReturnCode(
749 WhenSplitBy("--",
750 AllOf(Contains(Flag("--set-task-profile=", "Dex2OatBackground")),
751 Contains(Flag("--set-priority=", "background"))),
752 Not(Contains(Flag("--compact-dex-level=", _)))),
753 _,
754 _))
755 .WillOnce(Return(0));
756 RunDexopt();
757 }
758
TEST_F(ArtdTest,dexoptDexoptOptions)759 TEST_F(ArtdTest, dexoptDexoptOptions) {
760 dexopt_options_ = DexoptOptions{
761 .compilationReason = "install",
762 .targetSdkVersion = 123,
763 .debuggable = false,
764 .generateAppImage = false,
765 .hiddenApiPolicyEnabled = false,
766 .comments = "my-comments",
767 };
768
769 EXPECT_CALL(
770 *mock_exec_utils_,
771 DoExecAndReturnCode(WhenSplitBy("--",
772 _,
773 AllOf(Contains(Flag("--compilation-reason=", "install")),
774 Contains(Flag("-Xtarget-sdk-version:", "123")),
775 Not(Contains("--debuggable")),
776 Not(Contains(Flag("--app-image-fd=", _))),
777 Not(Contains(Flag("-Xhidden-api-policy:", _))),
778 Contains(Flag("--comments=", "my-comments")))),
779 _,
780 _))
781 .WillOnce(Return(0));
782
783 // `sizeBeforeBytes` should include the size of the old ART file even if no new ART file is
784 // generated.
785 RunDexopt(EX_NONE,
786 Field(&ArtdDexoptResult::sizeBeforeBytes,
787 strlen("old_art") + strlen("old_oat") + strlen("old_vdex")));
788 }
789
TEST_F(ArtdTest,dexoptDexoptOptions2)790 TEST_F(ArtdTest, dexoptDexoptOptions2) {
791 dexopt_options_ = DexoptOptions{
792 .compilationReason = "bg-dexopt",
793 .targetSdkVersion = 456,
794 .debuggable = true,
795 .generateAppImage = true,
796 .hiddenApiPolicyEnabled = true,
797 };
798
799 EXPECT_CALL(
800 *mock_exec_utils_,
801 DoExecAndReturnCode(WhenSplitBy("--",
802 _,
803 AllOf(Contains(Flag("--compilation-reason=", "bg-dexopt")),
804 Contains(Flag("-Xtarget-sdk-version:", "456")),
805 Contains("--debuggable"),
806 Contains(Flag("--app-image-fd=", _)),
807 Contains(Flag("-Xhidden-api-policy:", "enabled")))),
808 _,
809 _))
810 .WillOnce(Return(0));
811
812 RunDexopt();
813 }
814
TEST_F(ArtdTest,dexoptDefaultFlagsWhenNoSystemProps)815 TEST_F(ArtdTest, dexoptDefaultFlagsWhenNoSystemProps) {
816 dexopt_options_.generateAppImage = true;
817
818 EXPECT_CALL(*mock_exec_utils_,
819 DoExecAndReturnCode(
820 WhenSplitBy("--",
821 _,
822 AllOf(Contains(Flag("--swap-fd=", FdOf(_))),
823 Not(Contains(Flag("--instruction-set-features=", _))),
824 Not(Contains(Flag("--instruction-set-variant=", _))),
825 Not(Contains(Flag("--max-image-block-size=", _))),
826 Not(Contains(Flag("--very-large-app-threshold=", _))),
827 Not(Contains(Flag("--resolve-startup-const-strings=", _))),
828 Not(Contains("--generate-debug-info")),
829 Not(Contains("--generate-mini-debug-info")),
830 Contains("-Xdeny-art-apex-data-files"),
831 Not(Contains(Flag("--cpu-set=", _))),
832 Not(Contains(Flag("-j", _))),
833 Not(Contains(Flag("-Xms", _))),
834 Not(Contains(Flag("-Xmx", _))),
835 Not(Contains("--compile-individually")),
836 Not(Contains(Flag("--image-format=", _))),
837 Not(Contains("--force-jit-zygote")),
838 Not(Contains(Flag("--boot-image=", _))))),
839 _,
840 _))
841 .WillOnce(Return(0));
842 RunDexopt();
843 }
844
TEST_F(ArtdTest,dexoptFlagsFromSystemProps)845 TEST_F(ArtdTest, dexoptFlagsFromSystemProps) {
846 dexopt_options_.generateAppImage = true;
847
848 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-swap")).WillOnce(Return("0"));
849 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.isa.arm64.features"))
850 .WillOnce(Return("features"));
851 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.isa.arm64.variant")).WillOnce(Return("variant"));
852 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-max-image-block-size"))
853 .WillOnce(Return("size"));
854 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-very-large"))
855 .WillOnce(Return("threshold"));
856 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-resolve-startup-strings"))
857 .WillOnce(Return("strings"));
858 EXPECT_CALL(*mock_props_, GetProperty("debug.generate-debug-info")).WillOnce(Return("1"));
859 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-minidebuginfo")).WillOnce(Return("1"));
860 EXPECT_CALL(*mock_props_, GetProperty("odsign.verification.success")).WillOnce(Return("1"));
861 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-Xms")).WillOnce(Return("xms"));
862 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.dex2oat-Xmx")).WillOnce(Return("xmx"));
863 EXPECT_CALL(*mock_props_, GetProperty("ro.config.low_ram")).WillOnce(Return("1"));
864 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.appimageformat")).WillOnce(Return("imgfmt"));
865 EXPECT_CALL(*mock_props_, GetProperty("dalvik.vm.boot-image")).WillOnce(Return("boot-image"));
866
867 EXPECT_CALL(*mock_exec_utils_,
868 DoExecAndReturnCode(
869 WhenSplitBy("--",
870 _,
871 AllOf(Not(Contains(Flag("--swap-fd=", _))),
872 Contains(Flag("--instruction-set-features=", "features")),
873 Contains(Flag("--instruction-set-variant=", "variant")),
874 Contains(Flag("--max-image-block-size=", "size")),
875 Contains(Flag("--very-large-app-threshold=", "threshold")),
876 Contains(Flag("--resolve-startup-const-strings=", "strings")),
877 Contains("--generate-debug-info"),
878 Contains("--generate-mini-debug-info"),
879 Not(Contains("-Xdeny-art-apex-data-files")),
880 Contains(Flag("-Xms", "xms")),
881 Contains(Flag("-Xmx", "xmx")),
882 Contains("--compile-individually"),
883 Contains(Flag("--image-format=", "imgfmt")),
884 Not(Contains("--force-jit-zygote")),
885 Contains(Flag("--boot-image=", "boot-image")))),
886 _,
887 _))
888 .WillOnce(Return(0));
889 RunDexopt();
890 }
891
TEST_F(ArtdTest,dexoptFlagsForceJitZygote)892 TEST_F(ArtdTest, dexoptFlagsForceJitZygote) {
893 EXPECT_CALL(*mock_props_,
894 GetProperty("persist.device_config.runtime_native_boot.profilebootclasspath"))
895 .WillOnce(Return("true"));
896 ON_CALL(*mock_props_, GetProperty("dalvik.vm.boot-image")).WillByDefault(Return("boot-image"));
897
898 EXPECT_CALL(*mock_exec_utils_,
899 DoExecAndReturnCode(WhenSplitBy("--",
900 _,
901 AllOf(Contains("--force-jit-zygote"),
902 Not(Contains(Flag("--boot-image=", _))))),
903 _,
904 _))
905 .WillOnce(Return(0));
906 RunDexopt();
907 }
908
SetDefaultResourceControlProps(MockSystemProperties * mock_props)909 static void SetDefaultResourceControlProps(MockSystemProperties* mock_props) {
910 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-cpu-set")).WillRepeatedly(Return("0,2"));
911 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-threads")).WillRepeatedly(Return("4"));
912 }
913
TEST_F(ArtdTest,dexoptDefaultResourceControlBoot)914 TEST_F(ArtdTest, dexoptDefaultResourceControlBoot) {
915 SetDefaultResourceControlProps(mock_props_);
916
917 // The default resource control properties don't apply to BOOT.
918 EXPECT_CALL(
919 *mock_exec_utils_,
920 DoExecAndReturnCode(
921 WhenSplitBy(
922 "--", _, AllOf(Not(Contains(Flag("--cpu-set=", _))), Contains(Not(Flag("-j", _))))),
923 _,
924 _))
925 .WillOnce(Return(0));
926 priority_class_ = PriorityClass::BOOT;
927 RunDexopt();
928 }
929
TEST_F(ArtdTest,dexoptDefaultResourceControlOther)930 TEST_F(ArtdTest, dexoptDefaultResourceControlOther) {
931 SetDefaultResourceControlProps(mock_props_);
932
933 EXPECT_CALL(
934 *mock_exec_utils_,
935 DoExecAndReturnCode(
936 WhenSplitBy(
937 "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2")), Contains(Flag("-j", "4")))),
938 _,
939 _))
940 .Times(3)
941 .WillRepeatedly(Return(0));
942 priority_class_ = PriorityClass::INTERACTIVE_FAST;
943 RunDexopt();
944 priority_class_ = PriorityClass::INTERACTIVE;
945 RunDexopt();
946 priority_class_ = PriorityClass::BACKGROUND;
947 RunDexopt();
948 }
949
SetAllResourceControlProps(MockSystemProperties * mock_props)950 static void SetAllResourceControlProps(MockSystemProperties* mock_props) {
951 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-cpu-set")).WillRepeatedly(Return("0,2"));
952 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.dex2oat-threads")).WillRepeatedly(Return("4"));
953 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.boot-dex2oat-cpu-set"))
954 .WillRepeatedly(Return("0,1,2,3"));
955 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.boot-dex2oat-threads"))
956 .WillRepeatedly(Return("8"));
957 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.restore-dex2oat-cpu-set"))
958 .WillRepeatedly(Return("0,2,3"));
959 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.restore-dex2oat-threads"))
960 .WillRepeatedly(Return("6"));
961 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.background-dex2oat-cpu-set"))
962 .WillRepeatedly(Return("0"));
963 EXPECT_CALL(*mock_props, GetProperty("dalvik.vm.background-dex2oat-threads"))
964 .WillRepeatedly(Return("2"));
965 }
966
TEST_F(ArtdTest,dexoptAllResourceControlBoot)967 TEST_F(ArtdTest, dexoptAllResourceControlBoot) {
968 SetAllResourceControlProps(mock_props_);
969
970 EXPECT_CALL(
971 *mock_exec_utils_,
972 DoExecAndReturnCode(
973 WhenSplitBy(
974 "--", _, AllOf(Contains(Flag("--cpu-set=", "0,1,2,3")), Contains(Flag("-j", "8")))),
975 _,
976 _))
977 .WillOnce(Return(0));
978 priority_class_ = PriorityClass::BOOT;
979 RunDexopt();
980 }
981
TEST_F(ArtdTest,dexoptAllResourceControlInteractiveFast)982 TEST_F(ArtdTest, dexoptAllResourceControlInteractiveFast) {
983 SetAllResourceControlProps(mock_props_);
984
985 EXPECT_CALL(
986 *mock_exec_utils_,
987 DoExecAndReturnCode(
988 WhenSplitBy(
989 "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2,3")), Contains(Flag("-j", "6")))),
990 _,
991 _))
992 .WillOnce(Return(0));
993 priority_class_ = PriorityClass::INTERACTIVE_FAST;
994 RunDexopt();
995 }
996
TEST_F(ArtdTest,dexoptAllResourceControlInteractive)997 TEST_F(ArtdTest, dexoptAllResourceControlInteractive) {
998 SetAllResourceControlProps(mock_props_);
999
1000 // INTERACTIVE always uses the default resource control properties.
1001 EXPECT_CALL(
1002 *mock_exec_utils_,
1003 DoExecAndReturnCode(
1004 WhenSplitBy(
1005 "--", _, AllOf(Contains(Flag("--cpu-set=", "0,2")), Contains(Flag("-j", "4")))),
1006 _,
1007 _))
1008 .WillOnce(Return(0));
1009 priority_class_ = PriorityClass::INTERACTIVE;
1010 RunDexopt();
1011 }
1012
TEST_F(ArtdTest,dexoptAllResourceControlBackground)1013 TEST_F(ArtdTest, dexoptAllResourceControlBackground) {
1014 SetAllResourceControlProps(mock_props_);
1015
1016 EXPECT_CALL(
1017 *mock_exec_utils_,
1018 DoExecAndReturnCode(
1019 WhenSplitBy("--", _, AllOf(Contains(Flag("--cpu-set=", "0")), Contains(Flag("-j", "2")))),
1020 _,
1021 _))
1022 .WillOnce(Return(0));
1023 priority_class_ = PriorityClass::BACKGROUND;
1024 RunDexopt();
1025 }
1026
TEST_F(ArtdTest,dexoptFailed)1027 TEST_F(ArtdTest, dexoptFailed) {
1028 dexopt_options_.generateAppImage = true;
1029 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1030 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
1031 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
1032 WithArg<0>(WriteToFdFlag("--app-image-fd=", "new_art")),
1033 Return(1)));
1034 RunDexopt(EX_SERVICE_SPECIFIC);
1035
1036 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
1037 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
1038 CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
1039 }
1040
TEST_F(ArtdTest,dexoptFailedToCommit)1041 TEST_F(ArtdTest, dexoptFailedToCommit) {
1042 std::unique_ptr<ScopeGuard<std::function<void()>>> scoped_inaccessible;
1043 std::unique_ptr<ScopeGuard<std::function<void()>>> scoped_unroot;
1044
1045 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1046 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
1047 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
1048 [&](auto, auto, auto) {
1049 scoped_inaccessible = std::make_unique<ScopeGuard<std::function<void()>>>(
1050 ScopedInaccessible(scratch_path_ + "/a/oat/arm64"));
1051 scoped_unroot =
1052 std::make_unique<ScopeGuard<std::function<void()>>>(ScopedUnroot());
1053 return 0;
1054 }));
1055
1056 RunDexopt(
1057 EX_SERVICE_SPECIFIC,
1058 AllOf(Field(&ArtdDexoptResult::sizeBytes, 0), Field(&ArtdDexoptResult::sizeBeforeBytes, 0)));
1059 }
1060
TEST_F(ArtdTest,dexoptCancelledBeforeDex2oat)1061 TEST_F(ArtdTest, dexoptCancelledBeforeDex2oat) {
1062 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
1063 ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
1064
1065 constexpr pid_t kPid = 123;
1066
1067 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1068 .WillOnce([&](auto, const ExecCallbacks& callbacks, auto) {
1069 callbacks.on_start(kPid);
1070 callbacks.on_end(kPid);
1071 return Error();
1072 });
1073 EXPECT_CALL(mock_kill_, Call(kPid, SIGKILL));
1074
1075 cancellation_signal->cancel();
1076
1077 RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, true), cancellation_signal);
1078
1079 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
1080 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
1081 CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
1082 }
1083
TEST_F(ArtdTest,dexoptCancelledDuringDex2oat)1084 TEST_F(ArtdTest, dexoptCancelledDuringDex2oat) {
1085 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
1086 ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
1087
1088 constexpr pid_t kPid = 123;
1089 constexpr std::chrono::duration<int> kTimeout = std::chrono::seconds(1);
1090
1091 std::condition_variable process_started_cv, process_killed_cv;
1092 std::mutex mu;
1093
1094 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1095 .WillOnce([&](auto, const ExecCallbacks& callbacks, auto) {
1096 std::unique_lock<std::mutex> lock(mu);
1097 // Step 2.
1098 callbacks.on_start(kPid);
1099 process_started_cv.notify_one();
1100 EXPECT_EQ(process_killed_cv.wait_for(lock, kTimeout), std::cv_status::no_timeout);
1101 // Step 5.
1102 callbacks.on_end(kPid);
1103 return Error();
1104 });
1105
1106 EXPECT_CALL(mock_kill_, Call(kPid, SIGKILL)).WillOnce([&](auto, auto) {
1107 // Step 4.
1108 process_killed_cv.notify_one();
1109 return 0;
1110 });
1111
1112 std::thread t;
1113 {
1114 std::unique_lock<std::mutex> lock(mu);
1115 // Step 1.
1116 t = std::thread([&] {
1117 RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, true), cancellation_signal);
1118 });
1119 EXPECT_EQ(process_started_cv.wait_for(lock, kTimeout), std::cv_status::no_timeout);
1120 // Step 3.
1121 cancellation_signal->cancel();
1122 }
1123
1124 t.join();
1125
1126 // Step 6.
1127 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "old_oat");
1128 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "old_vdex");
1129 CheckContent(scratch_path_ + "/a/oat/arm64/b.art", "old_art");
1130 }
1131
TEST_F(ArtdTest,dexoptCancelledAfterDex2oat)1132 TEST_F(ArtdTest, dexoptCancelledAfterDex2oat) {
1133 std::shared_ptr<IArtdCancellationSignal> cancellation_signal;
1134 ASSERT_TRUE(artd_->createCancellationSignal(&cancellation_signal).isOk());
1135
1136 constexpr pid_t kPid = 123;
1137
1138 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1139 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--oat-fd=", "new_oat")),
1140 WithArg<0>(WriteToFdFlag("--output-vdex-fd=", "new_vdex")),
1141 [&](auto, const ExecCallbacks& callbacks, auto) {
1142 callbacks.on_start(kPid);
1143 callbacks.on_end(kPid);
1144 return 0;
1145 }));
1146 EXPECT_CALL(mock_kill_, Call).Times(0);
1147
1148 RunDexopt(EX_NONE, Field(&ArtdDexoptResult::cancelled, false), cancellation_signal);
1149
1150 // This signal should be ignored.
1151 cancellation_signal->cancel();
1152
1153 CheckContent(scratch_path_ + "/a/oat/arm64/b.odex", "new_oat");
1154 CheckContent(scratch_path_ + "/a/oat/arm64/b.vdex", "new_vdex");
1155 EXPECT_FALSE(std::filesystem::exists(scratch_path_ + "/a/oat/arm64/b.art"));
1156 }
1157
TEST_F(ArtdTest,dexoptDexFileNotOtherReadable)1158 TEST_F(ArtdTest, dexoptDexFileNotOtherReadable) {
1159 dex_file_other_readable_ = false;
1160 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
1161 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1162 Property(&ndk::ScopedAStatus::getMessage,
1163 HasSubstr("Outputs cannot be other-readable because the dex file"))));
1164 }
1165
TEST_F(ArtdTest,dexoptProfileNotOtherReadable)1166 TEST_F(ArtdTest, dexoptProfileNotOtherReadable) {
1167 profile_other_readable_ = false;
1168 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
1169 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1170 Property(&ndk::ScopedAStatus::getMessage,
1171 HasSubstr("Outputs cannot be other-readable because the profile"))));
1172 }
1173
TEST_F(ArtdTest,dexoptOutputNotOtherReadable)1174 TEST_F(ArtdTest, dexoptOutputNotOtherReadable) {
1175 output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
1176 dex_file_other_readable_ = false;
1177 profile_other_readable_ = false;
1178 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(0));
1179 RunDexopt();
1180 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.odex", false);
1181 CheckOtherReadable(scratch_path_ + "/a/oat/arm64/b.vdex", false);
1182 }
1183
TEST_F(ArtdTest,dexoptUidMismatch)1184 TEST_F(ArtdTest, dexoptUidMismatch) {
1185 output_artifacts_.permissionSettings.fileFsPermission.uid = 12345;
1186 output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
1187 dex_file_other_readable_ = false;
1188 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
1189 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1190 Property(&ndk::ScopedAStatus::getMessage,
1191 HasSubstr("Outputs' owner doesn't match the dex file"))));
1192 }
1193
TEST_F(ArtdTest,dexoptGidMismatch)1194 TEST_F(ArtdTest, dexoptGidMismatch) {
1195 output_artifacts_.permissionSettings.fileFsPermission.gid = 12345;
1196 output_artifacts_.permissionSettings.fileFsPermission.isOtherReadable = false;
1197 dex_file_other_readable_ = false;
1198 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).Times(0);
1199 RunDexopt(AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1200 Property(&ndk::ScopedAStatus::getMessage,
1201 HasSubstr("Outputs' owner doesn't match the dex file"))));
1202 }
1203
TEST_F(ArtdTest,dexoptGidMatchesUid)1204 TEST_F(ArtdTest, dexoptGidMatchesUid) {
1205 output_artifacts_.permissionSettings.fileFsPermission = {
1206 .uid = 123, .gid = 123, .isOtherReadable = false};
1207 EXPECT_CALL(mock_fstat_, Call(_, _)).WillRepeatedly(fstat); // For profile.
1208 EXPECT_CALL(mock_fstat_, Call(FdOf(dex_file_), _))
1209 .WillOnce(DoAll(SetArgPointee<1>((struct stat){
1210 .st_mode = S_IRUSR | S_IRGRP, .st_uid = 123, .st_gid = 456}),
1211 Return(0)));
1212 ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
1213 // It's okay to fail on chown. This happens when the test is not run as root.
1214 RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
1215 AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1216 Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
1217 }
1218
TEST_F(ArtdTest,dexoptGidMatchesGid)1219 TEST_F(ArtdTest, dexoptGidMatchesGid) {
1220 output_artifacts_.permissionSettings.fileFsPermission = {
1221 .uid = 123, .gid = 456, .isOtherReadable = false};
1222 EXPECT_CALL(mock_fstat_, Call(_, _)).WillRepeatedly(fstat); // For profile.
1223 EXPECT_CALL(mock_fstat_, Call(FdOf(dex_file_), _))
1224 .WillOnce(DoAll(SetArgPointee<1>((struct stat){
1225 .st_mode = S_IRUSR | S_IRGRP, .st_uid = 123, .st_gid = 456}),
1226 Return(0)));
1227 ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
1228 // It's okay to fail on chown. This happens when the test is not run as root.
1229 RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
1230 AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1231 Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
1232 }
1233
TEST_F(ArtdTest,dexoptUidGidChangeOk)1234 TEST_F(ArtdTest, dexoptUidGidChangeOk) {
1235 // The dex file is other-readable, so we don't check uid and gid.
1236 output_artifacts_.permissionSettings.fileFsPermission = {
1237 .uid = 12345, .gid = 12345, .isOtherReadable = false};
1238 ON_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillByDefault(Return(0));
1239 // It's okay to fail on chown. This happens when the test is not run as root.
1240 RunDexopt(AnyOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_NONE),
1241 AllOf(Property(&ndk::ScopedAStatus::getExceptionCode, EX_SERVICE_SPECIFIC),
1242 Property(&ndk::ScopedAStatus::getMessage, HasSubstr("Failed to chown")))));
1243 }
1244
TEST_F(ArtdTest,dexoptNoUidGidChange)1245 TEST_F(ArtdTest, dexoptNoUidGidChange) {
1246 output_artifacts_.permissionSettings.fileFsPermission = {
1247 .uid = -1, .gid = -1, .isOtherReadable = false};
1248 dex_file_other_readable_ = false;
1249 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(0));
1250 RunDexopt();
1251 }
1252
TEST_F(ArtdTest,isProfileUsable)1253 TEST_F(ArtdTest, isProfileUsable) {
1254 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1255 CreateFile(profile_file);
1256 CreateFile(dex_file_);
1257
1258 EXPECT_CALL(
1259 *mock_exec_utils_,
1260 DoExecAndReturnCode(
1261 AllOf(WhenSplitBy(
1262 "--",
1263 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1264 AllOf(Contains(art_root_ + "/bin/profman"),
1265 Contains(Flag("--reference-profile-file-fd=", FdOf(profile_file))),
1266 Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
1267 HasKeepFdsFor("--reference-profile-file-fd=", "--apk-fd=")),
1268 _,
1269 _))
1270 .WillOnce(Return(ProfmanResult::kSkipCompilationSmallDelta));
1271
1272 bool result;
1273 EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
1274 EXPECT_TRUE(result);
1275 }
1276
TEST_F(ArtdTest,isProfileUsableFalse)1277 TEST_F(ArtdTest, isProfileUsableFalse) {
1278 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1279 CreateFile(profile_file);
1280 CreateFile(dex_file_);
1281
1282 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1283 .WillOnce(Return(ProfmanResult::kSkipCompilationEmptyProfiles));
1284
1285 bool result;
1286 EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
1287 EXPECT_FALSE(result);
1288 }
1289
TEST_F(ArtdTest,isProfileUsableNotFound)1290 TEST_F(ArtdTest, isProfileUsableNotFound) {
1291 CreateFile(dex_file_);
1292
1293 bool result;
1294 EXPECT_TRUE(artd_->isProfileUsable(profile_path_.value(), dex_file_, &result).isOk());
1295 EXPECT_FALSE(result);
1296 }
1297
TEST_F(ArtdTest,isProfileUsableFailed)1298 TEST_F(ArtdTest, isProfileUsableFailed) {
1299 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1300 CreateFile(profile_file);
1301 CreateFile(dex_file_);
1302
1303 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(100));
1304
1305 bool result;
1306 ndk::ScopedAStatus status = artd_->isProfileUsable(profile_path_.value(), dex_file_, &result);
1307
1308 EXPECT_FALSE(status.isOk());
1309 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1310 EXPECT_THAT(status.getMessage(), HasSubstr("profman returned an unexpected code: 100"));
1311 }
1312
TEST_F(ArtdTest,copyAndRewriteProfile)1313 TEST_F(ArtdTest, copyAndRewriteProfile) {
1314 const TmpProfilePath& src = profile_path_->get<ProfilePath::tmpProfilePath>();
1315 std::string src_file = OR_FATAL(BuildTmpProfilePath(src));
1316 CreateFile(src_file, "abc");
1317 OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1318 dst.profilePath.id = "";
1319 dst.profilePath.tmpPath = "";
1320
1321 CreateFile(dex_file_);
1322
1323 EXPECT_CALL(
1324 *mock_exec_utils_,
1325 DoExecAndReturnCode(
1326 AllOf(WhenSplitBy(
1327 "--",
1328 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1329 AllOf(Contains(art_root_ + "/bin/profman"),
1330 Contains("--copy-and-update-profile-key"),
1331 Contains(Flag("--profile-file-fd=", FdOf(src_file))),
1332 Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
1333 HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")),
1334 _,
1335 _))
1336 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "def")),
1337 Return(ProfmanResult::kCopyAndUpdateSuccess)));
1338
1339 bool result;
1340 EXPECT_TRUE(artd_->copyAndRewriteProfile(src, &dst, dex_file_, &result).isOk());
1341 EXPECT_TRUE(result);
1342 EXPECT_THAT(dst.profilePath.id, Not(IsEmpty()));
1343 std::string real_path = OR_FATAL(BuildTmpProfilePath(dst.profilePath));
1344 EXPECT_EQ(dst.profilePath.tmpPath, real_path);
1345 CheckContent(real_path, "def");
1346 }
1347
TEST_F(ArtdTest,copyAndRewriteProfileFalse)1348 TEST_F(ArtdTest, copyAndRewriteProfileFalse) {
1349 const TmpProfilePath& src = profile_path_->get<ProfilePath::tmpProfilePath>();
1350 std::string src_file = OR_FATAL(BuildTmpProfilePath(src));
1351 CreateFile(src_file, "abc");
1352 OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1353 dst.profilePath.id = "";
1354 dst.profilePath.tmpPath = "";
1355
1356 CreateFile(dex_file_);
1357
1358 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _))
1359 .WillOnce(Return(ProfmanResult::kCopyAndUpdateNoMatch));
1360
1361 bool result;
1362 EXPECT_TRUE(artd_->copyAndRewriteProfile(src, &dst, dex_file_, &result).isOk());
1363 EXPECT_FALSE(result);
1364 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1365 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1366 }
1367
TEST_F(ArtdTest,copyAndRewriteProfileNotFound)1368 TEST_F(ArtdTest, copyAndRewriteProfileNotFound) {
1369 CreateFile(dex_file_);
1370
1371 const TmpProfilePath& src = profile_path_->get<ProfilePath::tmpProfilePath>();
1372 OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1373 dst.profilePath.id = "";
1374 dst.profilePath.tmpPath = "";
1375
1376 bool result;
1377 EXPECT_TRUE(artd_->copyAndRewriteProfile(src, &dst, dex_file_, &result).isOk());
1378 EXPECT_FALSE(result);
1379 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1380 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1381 }
1382
TEST_F(ArtdTest,copyAndRewriteProfileFailed)1383 TEST_F(ArtdTest, copyAndRewriteProfileFailed) {
1384 const TmpProfilePath& src = profile_path_->get<ProfilePath::tmpProfilePath>();
1385 std::string src_file = OR_FATAL(BuildTmpProfilePath(src));
1386 CreateFile(src_file, "abc");
1387 OutputProfile dst{.profilePath = src, .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1388 dst.profilePath.id = "";
1389 dst.profilePath.tmpPath = "";
1390
1391 CreateFile(dex_file_);
1392
1393 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode(_, _, _)).WillOnce(Return(100));
1394
1395 bool result;
1396 ndk::ScopedAStatus status = artd_->copyAndRewriteProfile(src, &dst, dex_file_, &result);
1397
1398 EXPECT_FALSE(status.isOk());
1399 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1400 EXPECT_THAT(status.getMessage(), HasSubstr("profman returned an unexpected code: 100"));
1401 EXPECT_THAT(dst.profilePath.id, IsEmpty());
1402 EXPECT_THAT(dst.profilePath.tmpPath, IsEmpty());
1403 }
1404
TEST_F(ArtdTest,commitTmpProfile)1405 TEST_F(ArtdTest, commitTmpProfile) {
1406 const TmpProfilePath& tmp_profile_path = profile_path_->get<ProfilePath::tmpProfilePath>();
1407 std::string tmp_profile_file = OR_FATAL(BuildTmpProfilePath(tmp_profile_path));
1408 CreateFile(tmp_profile_file);
1409
1410 EXPECT_TRUE(artd_->commitTmpProfile(tmp_profile_path).isOk());
1411
1412 EXPECT_FALSE(std::filesystem::exists(tmp_profile_file));
1413 EXPECT_TRUE(std::filesystem::exists(OR_FATAL(BuildFinalProfilePath(tmp_profile_path))));
1414 }
1415
TEST_F(ArtdTest,commitTmpProfileFailed)1416 TEST_F(ArtdTest, commitTmpProfileFailed) {
1417 const TmpProfilePath& tmp_profile_path = profile_path_->get<ProfilePath::tmpProfilePath>();
1418 ndk::ScopedAStatus status = artd_->commitTmpProfile(tmp_profile_path);
1419
1420 EXPECT_FALSE(status.isOk());
1421 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1422 EXPECT_THAT(
1423 status.getMessage(),
1424 ContainsRegex(R"re(Failed to move .*primary\.prof\.12345\.tmp.* to .*primary\.prof)re"));
1425
1426 EXPECT_FALSE(std::filesystem::exists(OR_FATAL(BuildFinalProfilePath(tmp_profile_path))));
1427 }
1428
TEST_F(ArtdTest,deleteProfile)1429 TEST_F(ArtdTest, deleteProfile) {
1430 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1431 CreateFile(profile_file);
1432
1433 EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
1434
1435 EXPECT_FALSE(std::filesystem::exists(profile_file));
1436 }
1437
TEST_F(ArtdTest,deleteProfileDoesNotExist)1438 TEST_F(ArtdTest, deleteProfileDoesNotExist) {
1439 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
1440 EXPECT_CALL(mock_logger_, Call).Times(0);
1441
1442 EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
1443 }
1444
TEST_F(ArtdTest,deleteProfileFailed)1445 TEST_F(ArtdTest, deleteProfileFailed) {
1446 auto scoped_set_logger = ScopedSetLogger(mock_logger_.AsStdFunction());
1447 EXPECT_CALL(
1448 mock_logger_,
1449 Call(_, _, _, _, _, ContainsRegex(R"re(Failed to remove .*primary\.prof\.12345\.tmp)re")));
1450
1451 std::string profile_file = OR_FATAL(BuildProfileOrDmPath(profile_path_.value()));
1452 auto scoped_inaccessible = ScopedInaccessible(std::filesystem::path(profile_file).parent_path());
1453 auto scoped_unroot = ScopedUnroot();
1454
1455 EXPECT_TRUE(artd_->deleteProfile(profile_path_.value()).isOk());
1456 }
1457
1458 class ArtdGetVisibilityTest : public ArtdTest {
1459 protected:
1460 template <typename PathType>
1461 using Method = ndk::ScopedAStatus (Artd::*)(const PathType&, FileVisibility*);
1462
1463 template <typename PathType>
TestGetVisibilityOtherReadable(Method<PathType> method,const PathType & input,const std::string & path)1464 void TestGetVisibilityOtherReadable(Method<PathType> method,
1465 const PathType& input,
1466 const std::string& path) {
1467 CreateFile(path);
1468 std::filesystem::permissions(
1469 path, std::filesystem::perms::others_read, std::filesystem::perm_options::add);
1470
1471 FileVisibility result;
1472 ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
1473 EXPECT_EQ(result, FileVisibility::OTHER_READABLE);
1474 }
1475
1476 template <typename PathType>
TestGetVisibilityNotOtherReadable(Method<PathType> method,const PathType & input,const std::string & path)1477 void TestGetVisibilityNotOtherReadable(Method<PathType> method,
1478 const PathType& input,
1479 const std::string& path) {
1480 CreateFile(path);
1481 std::filesystem::permissions(
1482 path, std::filesystem::perms::others_read, std::filesystem::perm_options::remove);
1483
1484 FileVisibility result;
1485 ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
1486 EXPECT_EQ(result, FileVisibility::NOT_OTHER_READABLE);
1487 }
1488
1489 template <typename PathType>
TestGetVisibilityNotFound(Method<PathType> method,const PathType & input)1490 void TestGetVisibilityNotFound(Method<PathType> method, const PathType& input) {
1491 FileVisibility result;
1492 ASSERT_TRUE(((*artd_).*method)(input, &result).isOk());
1493 EXPECT_EQ(result, FileVisibility::NOT_FOUND);
1494 }
1495
1496 template <typename PathType>
TestGetVisibilityPermissionDenied(Method<PathType> method,const PathType & input,const std::string & path)1497 void TestGetVisibilityPermissionDenied(Method<PathType> method,
1498 const PathType& input,
1499 const std::string& path) {
1500 CreateFile(path);
1501
1502 auto scoped_inaccessible = ScopedInaccessible(std::filesystem::path(path).parent_path());
1503 auto scoped_unroot = ScopedUnroot();
1504
1505 FileVisibility result;
1506 ndk::ScopedAStatus status = ((*artd_).*method)(input, &result);
1507 EXPECT_FALSE(status.isOk());
1508 EXPECT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
1509 EXPECT_THAT(status.getMessage(), HasSubstr("Failed to get status of"));
1510 }
1511 };
1512
TEST_F(ArtdGetVisibilityTest,getProfileVisibilityOtherReadable)1513 TEST_F(ArtdGetVisibilityTest, getProfileVisibilityOtherReadable) {
1514 TestGetVisibilityOtherReadable(&Artd::getProfileVisibility,
1515 profile_path_.value(),
1516 OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
1517 }
1518
TEST_F(ArtdGetVisibilityTest,getProfileVisibilityNotOtherReadable)1519 TEST_F(ArtdGetVisibilityTest, getProfileVisibilityNotOtherReadable) {
1520 TestGetVisibilityNotOtherReadable(&Artd::getProfileVisibility,
1521 profile_path_.value(),
1522 OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
1523 }
1524
TEST_F(ArtdGetVisibilityTest,getProfileVisibilityNotFound)1525 TEST_F(ArtdGetVisibilityTest, getProfileVisibilityNotFound) {
1526 TestGetVisibilityNotFound(&Artd::getProfileVisibility, profile_path_.value());
1527 }
1528
TEST_F(ArtdGetVisibilityTest,getProfileVisibilityPermissionDenied)1529 TEST_F(ArtdGetVisibilityTest, getProfileVisibilityPermissionDenied) {
1530 TestGetVisibilityPermissionDenied(&Artd::getProfileVisibility,
1531 profile_path_.value(),
1532 OR_FATAL(BuildProfileOrDmPath(profile_path_.value())));
1533 }
1534
TEST_F(ArtdGetVisibilityTest,getArtifactsVisibilityOtherReadable)1535 TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityOtherReadable) {
1536 TestGetVisibilityOtherReadable(
1537 &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_)));
1538 }
1539
TEST_F(ArtdGetVisibilityTest,getArtifactsVisibilityNotOtherReadable)1540 TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotOtherReadable) {
1541 TestGetVisibilityNotOtherReadable(
1542 &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_)));
1543 }
1544
TEST_F(ArtdGetVisibilityTest,getArtifactsVisibilityNotFound)1545 TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityNotFound) {
1546 TestGetVisibilityNotFound(&Artd::getArtifactsVisibility, artifacts_path_);
1547 }
1548
TEST_F(ArtdGetVisibilityTest,getArtifactsVisibilityPermissionDenied)1549 TEST_F(ArtdGetVisibilityTest, getArtifactsVisibilityPermissionDenied) {
1550 TestGetVisibilityPermissionDenied(
1551 &Artd::getArtifactsVisibility, artifacts_path_, OR_FATAL(BuildOatPath(artifacts_path_)));
1552 }
1553
TEST_F(ArtdGetVisibilityTest,getDexFileVisibilityOtherReadable)1554 TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityOtherReadable) {
1555 TestGetVisibilityOtherReadable(&Artd::getDexFileVisibility, dex_file_, dex_file_);
1556 }
1557
TEST_F(ArtdGetVisibilityTest,getDexFileVisibilityNotOtherReadable)1558 TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityNotOtherReadable) {
1559 TestGetVisibilityNotOtherReadable(&Artd::getDexFileVisibility, dex_file_, dex_file_);
1560 }
1561
TEST_F(ArtdGetVisibilityTest,getDexFileVisibilityNotFound)1562 TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityNotFound) {
1563 TestGetVisibilityNotFound(&Artd::getDexFileVisibility, dex_file_);
1564 }
1565
TEST_F(ArtdGetVisibilityTest,getDexFileVisibilityPermissionDenied)1566 TEST_F(ArtdGetVisibilityTest, getDexFileVisibilityPermissionDenied) {
1567 TestGetVisibilityPermissionDenied(&Artd::getDexFileVisibility, dex_file_, dex_file_);
1568 }
1569
TEST_F(ArtdGetVisibilityTest,getDmFileVisibilityOtherReadable)1570 TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityOtherReadable) {
1571 TestGetVisibilityOtherReadable(&Artd::getDmFileVisibility,
1572 dm_path_.value(),
1573 OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
1574 }
1575
TEST_F(ArtdGetVisibilityTest,getDmFileVisibilityNotOtherReadable)1576 TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityNotOtherReadable) {
1577 TestGetVisibilityNotOtherReadable(&Artd::getDmFileVisibility,
1578 dm_path_.value(),
1579 OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
1580 }
1581
TEST_F(ArtdGetVisibilityTest,getDmFileVisibilityNotFound)1582 TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityNotFound) {
1583 TestGetVisibilityNotFound(&Artd::getDmFileVisibility, dm_path_.value());
1584 }
1585
TEST_F(ArtdGetVisibilityTest,getDmFileVisibilityPermissionDenied)1586 TEST_F(ArtdGetVisibilityTest, getDmFileVisibilityPermissionDenied) {
1587 TestGetVisibilityPermissionDenied(&Artd::getDmFileVisibility,
1588 dm_path_.value(),
1589 OR_FATAL(BuildDexMetadataPath(dm_path_.value())));
1590 }
1591
TEST_F(ArtdTest,mergeProfiles)1592 TEST_F(ArtdTest, mergeProfiles) {
1593 const TmpProfilePath& reference_profile_path = profile_path_->get<ProfilePath::tmpProfilePath>();
1594 std::string reference_profile_file = OR_FATAL(BuildTmpProfilePath(reference_profile_path));
1595 CreateFile(reference_profile_file, "abc");
1596
1597 // Doesn't exist.
1598 PrimaryCurProfilePath profile_0_path{
1599 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
1600 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
1601
1602 PrimaryCurProfilePath profile_1_path{
1603 .userId = 1, .packageName = "com.android.foo", .profileName = "primary"};
1604 std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path));
1605 CreateFile(profile_1_file, "def");
1606
1607 OutputProfile output_profile{.profilePath = reference_profile_path,
1608 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1609 output_profile.profilePath.id = "";
1610 output_profile.profilePath.tmpPath = "";
1611
1612 std::string dex_file_1 = scratch_path_ + "/a/b.apk";
1613 std::string dex_file_2 = scratch_path_ + "/a/c.apk";
1614 CreateFile(dex_file_1);
1615 CreateFile(dex_file_2);
1616
1617 EXPECT_CALL(
1618 *mock_exec_utils_,
1619 DoExecAndReturnCode(
1620 AllOf(WhenSplitBy(
1621 "--",
1622 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1623 AllOf(Contains(art_root_ + "/bin/profman"),
1624 Not(Contains(Flag("--profile-file-fd=", FdOf(profile_0_file)))),
1625 Contains(Flag("--profile-file-fd=", FdOf(profile_1_file))),
1626 Contains(Flag("--reference-profile-file-fd=", FdHasContent("abc"))),
1627 Contains(Flag("--apk-fd=", FdOf(dex_file_1))),
1628 Contains(Flag("--apk-fd=", FdOf(dex_file_2))),
1629 Not(Contains("--force-merge")),
1630 Not(Contains("--boot-image-merge")))),
1631 HasKeepFdsFor("--profile-file-fd=", "--reference-profile-file-fd=", "--apk-fd=")),
1632 _,
1633 _))
1634 .WillOnce(DoAll(WithArg<0>(ClearAndWriteToFdFlag("--reference-profile-file-fd=", "merged")),
1635 Return(ProfmanResult::kCompile)));
1636
1637 bool result;
1638 EXPECT_TRUE(artd_
1639 ->mergeProfiles({profile_0_path, profile_1_path},
1640 reference_profile_path,
1641 &output_profile,
1642 {dex_file_1, dex_file_2},
1643 /*in_options=*/{},
1644 &result)
1645 .isOk());
1646 EXPECT_TRUE(result);
1647 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
1648 std::string real_path = OR_FATAL(BuildTmpProfilePath(output_profile.profilePath));
1649 EXPECT_EQ(output_profile.profilePath.tmpPath, real_path);
1650 CheckContent(real_path, "merged");
1651 }
1652
TEST_F(ArtdTest,mergeProfilesEmptyReferenceProfile)1653 TEST_F(ArtdTest, mergeProfilesEmptyReferenceProfile) {
1654 PrimaryCurProfilePath profile_0_path{
1655 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
1656 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
1657 CreateFile(profile_0_file, "def");
1658
1659 OutputProfile output_profile{.profilePath = profile_path_->get<ProfilePath::tmpProfilePath>(),
1660 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1661 output_profile.profilePath.id = "";
1662 output_profile.profilePath.tmpPath = "";
1663
1664 CreateFile(dex_file_);
1665
1666 EXPECT_CALL(
1667 *mock_exec_utils_,
1668 DoExecAndReturnCode(
1669 WhenSplitBy("--",
1670 AllOf(Contains(art_root_ + "/bin/art_exec"), Contains("--drop-capabilities")),
1671 AllOf(Contains(art_root_ + "/bin/profman"),
1672 Contains(Flag("--profile-file-fd=", FdOf(profile_0_file))),
1673 Contains(Flag("--reference-profile-file-fd=", FdHasContent(""))),
1674 Contains(Flag("--apk-fd=", FdOf(dex_file_))))),
1675 _,
1676 _))
1677 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--reference-profile-file-fd=", "merged")),
1678 Return(ProfmanResult::kCompile)));
1679
1680 bool result;
1681 EXPECT_TRUE(artd_
1682 ->mergeProfiles({profile_0_path},
1683 std::nullopt,
1684 &output_profile,
1685 {dex_file_},
1686 /*in_options=*/{},
1687 &result)
1688 .isOk());
1689 EXPECT_TRUE(result);
1690 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
1691 EXPECT_THAT(output_profile.profilePath.tmpPath, Not(IsEmpty()));
1692 }
1693
TEST_F(ArtdTest,mergeProfilesProfilesDontExist)1694 TEST_F(ArtdTest, mergeProfilesProfilesDontExist) {
1695 const TmpProfilePath& reference_profile_path = profile_path_->get<ProfilePath::tmpProfilePath>();
1696 std::string reference_profile_file = OR_FATAL(BuildTmpProfilePath(reference_profile_path));
1697 CreateFile(reference_profile_file, "abc");
1698
1699 // Doesn't exist.
1700 PrimaryCurProfilePath profile_0_path{
1701 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
1702 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
1703
1704 // Doesn't exist.
1705 PrimaryCurProfilePath profile_1_path{
1706 .userId = 1, .packageName = "com.android.foo", .profileName = "primary"};
1707 std::string profile_1_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_1_path));
1708
1709 OutputProfile output_profile{.profilePath = reference_profile_path,
1710 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1711 output_profile.profilePath.id = "";
1712 output_profile.profilePath.tmpPath = "";
1713
1714 CreateFile(dex_file_);
1715
1716 EXPECT_CALL(*mock_exec_utils_, DoExecAndReturnCode).Times(0);
1717
1718 bool result;
1719 EXPECT_TRUE(artd_
1720 ->mergeProfiles({profile_0_path},
1721 std::nullopt,
1722 &output_profile,
1723 {dex_file_},
1724 /*in_options=*/{},
1725 &result)
1726 .isOk());
1727 EXPECT_FALSE(result);
1728 EXPECT_THAT(output_profile.profilePath.id, IsEmpty());
1729 EXPECT_THAT(output_profile.profilePath.tmpPath, IsEmpty());
1730 }
1731
TEST_F(ArtdTest,mergeProfilesWithOptionsForceMerge)1732 TEST_F(ArtdTest, mergeProfilesWithOptionsForceMerge) {
1733 PrimaryCurProfilePath profile_0_path{
1734 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
1735 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
1736 CreateFile(profile_0_file, "def");
1737
1738 OutputProfile output_profile{.profilePath = profile_path_->get<ProfilePath::tmpProfilePath>(),
1739 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1740 output_profile.profilePath.id = "";
1741 output_profile.profilePath.tmpPath = "";
1742
1743 CreateFile(dex_file_);
1744
1745 EXPECT_CALL(
1746 *mock_exec_utils_,
1747 DoExecAndReturnCode(
1748 WhenSplitBy("--", _, AllOf(Contains("--force-merge"), Contains("--boot-image-merge"))),
1749 _,
1750 _))
1751 .WillOnce(Return(ProfmanResult::kSuccess));
1752
1753 bool result;
1754 EXPECT_TRUE(artd_
1755 ->mergeProfiles({profile_0_path},
1756 std::nullopt,
1757 &output_profile,
1758 {dex_file_},
1759 {.forceMerge = true, .forBootImage = true},
1760 &result)
1761 .isOk());
1762 EXPECT_TRUE(result);
1763 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
1764 EXPECT_THAT(output_profile.profilePath.tmpPath, Not(IsEmpty()));
1765 }
1766
TEST_F(ArtdTest,mergeProfilesWithOptionsDumpOnly)1767 TEST_F(ArtdTest, mergeProfilesWithOptionsDumpOnly) {
1768 PrimaryCurProfilePath profile_0_path{
1769 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
1770 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
1771 CreateFile(profile_0_file, "def");
1772
1773 OutputProfile output_profile{.profilePath = profile_path_->get<ProfilePath::tmpProfilePath>(),
1774 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1775 output_profile.profilePath.id = "";
1776 output_profile.profilePath.tmpPath = "";
1777
1778 CreateFile(dex_file_);
1779
1780 EXPECT_CALL(*mock_exec_utils_,
1781 DoExecAndReturnCode(
1782 AllOf(WhenSplitBy("--",
1783 _,
1784 AllOf(Contains("--dump-only"),
1785 Not(Contains(Flag("--reference-profile-file-fd=", _))))),
1786 HasKeepFdsFor("--profile-file-fd=", "--apk-fd=", "--dump-output-to-fd=")),
1787 _,
1788 _))
1789 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--dump-output-to-fd=", "dump")),
1790 Return(ProfmanResult::kSuccess)));
1791
1792 bool result;
1793 EXPECT_TRUE(artd_
1794 ->mergeProfiles({profile_0_path},
1795 std::nullopt,
1796 &output_profile,
1797 {dex_file_},
1798 {.dumpOnly = true},
1799 &result)
1800 .isOk());
1801 EXPECT_TRUE(result);
1802 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
1803 CheckContent(output_profile.profilePath.tmpPath, "dump");
1804 }
1805
TEST_F(ArtdTest,mergeProfilesWithOptionsDumpClassesAndMethods)1806 TEST_F(ArtdTest, mergeProfilesWithOptionsDumpClassesAndMethods) {
1807 PrimaryCurProfilePath profile_0_path{
1808 .userId = 0, .packageName = "com.android.foo", .profileName = "primary"};
1809 std::string profile_0_file = OR_FATAL(BuildPrimaryCurProfilePath(profile_0_path));
1810 CreateFile(profile_0_file, "def");
1811
1812 OutputProfile output_profile{.profilePath = profile_path_->get<ProfilePath::tmpProfilePath>(),
1813 .fsPermission = FsPermission{.uid = -1, .gid = -1}};
1814 output_profile.profilePath.id = "";
1815 output_profile.profilePath.tmpPath = "";
1816
1817 CreateFile(dex_file_);
1818
1819 EXPECT_CALL(*mock_exec_utils_,
1820 DoExecAndReturnCode(
1821 WhenSplitBy("--",
1822 _,
1823 AllOf(Contains("--dump-classes-and-methods"),
1824 Not(Contains(Flag("--reference-profile-file-fd=", _))))),
1825 _,
1826 _))
1827 .WillOnce(DoAll(WithArg<0>(WriteToFdFlag("--dump-output-to-fd=", "dump")),
1828 Return(ProfmanResult::kSuccess)));
1829
1830 bool result;
1831 EXPECT_TRUE(artd_
1832 ->mergeProfiles({profile_0_path},
1833 std::nullopt,
1834 &output_profile,
1835 {dex_file_},
1836 {.dumpClassesAndMethods = true},
1837 &result)
1838 .isOk());
1839 EXPECT_TRUE(result);
1840 EXPECT_THAT(output_profile.profilePath.id, Not(IsEmpty()));
1841 CheckContent(output_profile.profilePath.tmpPath, "dump");
1842 }
1843
TEST_F(ArtdTest,cleanup)1844 TEST_F(ArtdTest, cleanup) {
1845 // TODO(b/289037540): Fix this.
1846 if (getuid() != kRootUid) {
1847 GTEST_SKIP() << "This test requires root access";
1848 }
1849
1850 std::vector<std::string> gc_removed_files;
1851 std::vector<std::string> gc_kept_files;
1852
1853 auto CreateGcRemovedFile = [&](const std::string& path) {
1854 CreateFile(path);
1855 gc_removed_files.push_back(path);
1856 };
1857
1858 auto CreateGcKeptFile = [&](const std::string& path) {
1859 CreateFile(path);
1860 gc_kept_files.push_back(path);
1861 };
1862
1863 // Unmanaged files.
1864 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/1.odex");
1865 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.odex");
1866 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/1.txt");
1867 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.txt");
1868 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.tmp");
1869
1870 // Files to keep.
1871 CreateGcKeptFile(android_data_ + "/misc/profiles/cur/1/com.android.foo/primary.prof");
1872 CreateGcKeptFile(android_data_ + "/misc/profiles/cur/3/com.android.foo/primary.prof");
1873 CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.dex");
1874 CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.vdex");
1875 CreateGcKeptFile(android_data_ + "/dalvik-cache/arm64/system@app@Foo@Foo.apk@classes.art");
1876 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.vdex");
1877 CreateGcKeptFile(
1878 android_expand_ +
1879 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.odex");
1880 CreateGcKeptFile(
1881 android_expand_ +
1882 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.vdex");
1883 CreateGcKeptFile(
1884 android_expand_ +
1885 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/oat/arm64/base.art");
1886 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.odex");
1887 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.vdex");
1888 CreateGcKeptFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/2.art");
1889
1890 // Files to remove.
1891 CreateGcRemovedFile(android_data_ + "/misc/profiles/ref/com.android.foo/primary.prof");
1892 CreateGcRemovedFile(android_data_ + "/misc/profiles/cur/2/com.android.foo/primary.prof");
1893 CreateGcRemovedFile(android_data_ + "/misc/profiles/cur/3/com.android.bar/primary.prof");
1894 CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/extra.odex");
1895 CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.dex");
1896 CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.vdex");
1897 CreateGcRemovedFile(android_data_ + "/dalvik-cache/arm64/system@app@Bar@Bar.apk@classes.art");
1898 CreateGcRemovedFile(
1899 android_expand_ +
1900 "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.odex");
1901 CreateGcRemovedFile(
1902 android_expand_ +
1903 "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.vdex");
1904 CreateGcRemovedFile(
1905 android_expand_ +
1906 "/123456-7890/app/~~daewfweaf==/com.android.foo-fjuwidhia==/oat/arm64/base.art");
1907 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/1.prof");
1908 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/1.prof.123456.tmp");
1909 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.odex");
1910 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.vdex");
1911 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.art");
1912 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/1.odex.123456.tmp");
1913 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/oat/arm64/2.odex.123456.tmp");
1914 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.odex");
1915 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.art");
1916 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/oat/arm64/1.vdex.123456.tmp");
1917 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.odex");
1918 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.vdex");
1919 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art");
1920 CreateGcRemovedFile(android_data_ +
1921 "/user_de/0/com.android.foo/aaa/bbb/oat/arm64/1.art.123456.tmp");
1922 CreateGcRemovedFile(android_data_ + "/user_de/0/com.android.bar/aaa/oat/arm64/1.vdex");
1923
1924 int64_t aidl_return;
1925 ASSERT_TRUE(
1926 artd_
1927 ->cleanup(
1928 {
1929 PrimaryCurProfilePath{
1930 .userId = 1, .packageName = "com.android.foo", .profileName = "primary"},
1931 PrimaryCurProfilePath{
1932 .userId = 3, .packageName = "com.android.foo", .profileName = "primary"},
1933 },
1934 {
1935 ArtifactsPath{.dexPath = "/system/app/Foo/Foo.apk",
1936 .isa = "arm64",
1937 .isInDalvikCache = true},
1938 ArtifactsPath{
1939 .dexPath =
1940 android_expand_ +
1941 "/123456-7890/app/~~nkfeankfna==/com.android.bar-jfoeaofiew==/base.apk",
1942 .isa = "arm64",
1943 .isInDalvikCache = false},
1944 ArtifactsPath{.dexPath = android_data_ + "/user_de/0/com.android.foo/aaa/2.apk",
1945 .isa = "arm64",
1946 .isInDalvikCache = false},
1947 },
1948 {
1949 VdexPath{ArtifactsPath{
1950 .dexPath = android_data_ + "/user_de/0/com.android.foo/aaa/1.apk",
1951 .isa = "arm64",
1952 .isInDalvikCache = false}},
1953 },
1954 &aidl_return)
1955 .isOk());
1956
1957 for (const std::string& path : gc_removed_files) {
1958 EXPECT_FALSE(std::filesystem::exists(path)) << "'{}' should be removed"_format(path);
1959 }
1960
1961 for (const std::string& path : gc_kept_files) {
1962 EXPECT_TRUE(std::filesystem::exists(path)) << "'{}' should be kept"_format(path);
1963 }
1964 }
1965
1966 } // namespace
1967 } // namespace artd
1968 } // namespace art
1969