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