• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 "apex_classpath.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/scopeguard.h>
21 #include <android-base/stringprintf.h>
22 #include <android-base/strings.h>
23 #include <logwrap/logwrap.h>
24 
25 #include <fstream>
26 #include <regex>
27 
28 namespace android {
29 namespace apex {
30 
31 using ::android::base::Error;
32 using ::android::base::StringPrintf;
33 
DeriveClassPath(const std::vector<std::string> & temp_mounted_apex_paths,const std::string & sdkext_module_name)34 android::base::Result<ClassPath> ClassPath::DeriveClassPath(
35     const std::vector<std::string>& temp_mounted_apex_paths,
36     const std::string& sdkext_module_name) {
37   if (temp_mounted_apex_paths.empty()) {
38     return Error()
39            << "Invalid argument: There are no APEX to derive claspath from";
40   }
41   // Call derive_classpath binary to generate required information
42 
43   // Prefer using the binary from staged session if possible
44   std::string apex_of_binary =
45       StringPrintf("/apex/%s", sdkext_module_name.c_str());
46   for (const auto& temp_mounted_apex_path : temp_mounted_apex_paths) {
47     if (temp_mounted_apex_path.starts_with(apex_of_binary + "@")) {
48       apex_of_binary = temp_mounted_apex_path;
49       break;
50     }
51   }
52   std::string binary_path =
53       StringPrintf("%s/bin/derive_classpath", apex_of_binary.c_str());
54   std::string scan_dirs_flag =
55       StringPrintf("--scan-dirs=%s",
56                    android::base::Join(temp_mounted_apex_paths, ",").c_str());
57 
58   // Create a temp file to write output
59   auto temp_output_path = "/apex/derive_classpath_temp";
60   auto cleanup = [temp_output_path]() {
61     android::base::RemoveFileIfExists(temp_output_path);
62   };
63   auto scope_guard = android::base::make_scope_guard(cleanup);
64   // Cleanup to ensure we are creating an empty file
65   cleanup();
66   // Create the empty file where derive_classpath will write into
67   std::ofstream _(temp_output_path);
68 
69   const char* const argv[] = {binary_path.c_str(), scan_dirs_flag.c_str(),
70                               temp_output_path};
71   auto rc = logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG,
72                                 false, nullptr);
73   if (rc != 0) {
74     return Error() << "Running derive_classpath failed; binary path: " +
75                           binary_path;
76   }
77 
78   return ClassPath::ParseFromFile(temp_output_path);
79 }
80 
81 // Parse the string output into structured information
82 // The raw output from derive_classpath has the following format:
83 // ```
84 // export BOOTCLASSPATH path/to/jar1:/path/to/jar2
85 // export DEX2OATBOOTCLASSPATH
86 // export SYSTEMSERVERCLASSPATH path/to/some/jar
ParseFromFile(const std::string & file_path)87 android::base::Result<ClassPath> ClassPath::ParseFromFile(
88     const std::string& file_path) {
89   ClassPath result;
90 
91   std::string contents;
92   auto read_status = android::base::ReadFileToString(file_path, &contents,
93                                                      /*follow_symlinks=*/false);
94   if (!read_status) {
95     return Error() << "Failed to read classpath info from file";
96   }
97 
98   // Jars in apex have the following format: /apex/<package-name>/*
99   const std::regex capture_apex_package_name("^/apex/([^/]+)/");
100 
101   for (const auto& line : android::base::Split(contents, "\n")) {
102     // Split the line by space. The second element determines which type of
103     // classpath we are dealing with and the third element are the jars
104     // separated by :
105     auto tokens = android::base::Split(line, " ");
106     if (tokens.size() < 3) {
107       continue;
108     }
109     auto jars_list = tokens[2];
110     for (const auto& jar_path : android::base::Split(jars_list, ":")) {
111       std::smatch match;
112       if (std::regex_search(jar_path, match, capture_apex_package_name)) {
113         auto package_name = match[1];
114         result.AddPackageWithClasspathJars(package_name);
115       }
116     }
117   }
118   return result;
119 }
120 
AddPackageWithClasspathJars(const std::string & package)121 void ClassPath::AddPackageWithClasspathJars(const std::string& package) {
122   packages_with_classpath_jars.insert(package);
123 }
124 
HasClassPathJars(const std::string & package)125 bool ClassPath::HasClassPathJars(const std::string& package) {
126   return packages_with_classpath_jars.find(package) !=
127          packages_with_classpath_jars.end();
128 }
129 
130 }  // namespace apex
131 }  // namespace android
132