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