1 /*
2 * Copyright 2020 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 "MockWatchdogServiceHelper.h"
18 #include "PackageInfoResolver.h"
19 #include "PackageInfoTestUtils.h"
20
21 #include <android-base/stringprintf.h>
22 #include <android/automotive/watchdog/internal/ApplicationCategoryType.h>
23 #include <android/automotive/watchdog/internal/ComponentType.h>
24 #include <android/automotive/watchdog/internal/UidType.h>
25 #include <gmock/gmock.h>
26 #include <gtest/gtest.h>
27
28 namespace android {
29 namespace automotive {
30 namespace watchdog {
31
32 using ::android::sp;
33 using ::android::automotive::watchdog::internal::ApplicationCategoryType;
34 using ::android::automotive::watchdog::internal::ComponentType;
35 using ::android::automotive::watchdog::internal::PackageInfo;
36 using ::android::automotive::watchdog::internal::UidType;
37 using ::android::base::StringAppendF;
38 using ::testing::_;
39 using ::testing::DoAll;
40 using ::testing::NotNull;
41 using ::testing::Pair;
42 using ::testing::Return;
43 using ::testing::SetArgPointee;
44 using ::testing::UnorderedElementsAre;
45 using ::testing::UnorderedElementsAreArray;
46
47 namespace {
48
49 using PackageToAppCategoryMap =
50 std::unordered_map<std::string,
51 android::automotive::watchdog::internal::ApplicationCategoryType>;
52
toString(const std::unordered_map<uid_t,PackageInfo> & mappings)53 std::string toString(const std::unordered_map<uid_t, PackageInfo>& mappings) {
54 std::string buffer = "{";
55 for (const auto& [uid, info] : mappings) {
56 if (buffer.size() > 1) {
57 StringAppendF(&buffer, ", ");
58 }
59 StringAppendF(&buffer, "{%d: %s}", uid, info.toString().c_str());
60 }
61 StringAppendF(&buffer, "}");
62 return buffer;
63 }
64
65 } // namespace
66
67 namespace internal {
68
69 class PackageInfoResolverPeer final {
70 public:
PackageInfoResolverPeer()71 PackageInfoResolverPeer() {
72 PackageInfoResolver::getInstance();
73 mPackageInfoResolver = PackageInfoResolver::sInstance;
74 mockWatchdogServiceHelper = sp<MockWatchdogServiceHelper>::make();
75 mPackageInfoResolver->initWatchdogServiceHelper(mockWatchdogServiceHelper);
76 }
77
~PackageInfoResolverPeer()78 ~PackageInfoResolverPeer() {
79 PackageInfoResolver::sInstance.clear();
80 PackageInfoResolver::sGetpwuidHandler = &getpwuid;
81 clearMappingCache();
82 }
83
injectCacheMapping(const std::unordered_map<uid_t,PackageInfo> & mapping)84 void injectCacheMapping(const std::unordered_map<uid_t, PackageInfo>& mapping) {
85 mPackageInfoResolver->mUidToPackageInfoMapping = mapping;
86 }
87
setPackageConfigurations(const std::unordered_set<std::string> & vendorPackagePrefixes,const PackageToAppCategoryMap & packagesToAppCategories)88 void setPackageConfigurations(const std::unordered_set<std::string>& vendorPackagePrefixes,
89 const PackageToAppCategoryMap& packagesToAppCategories) {
90 mPackageInfoResolver->setPackageConfigurations(vendorPackagePrefixes,
91 packagesToAppCategories);
92 }
93
stubGetpwuid(const std::unordered_map<uid_t,std::string> & nativeUidToPackageNameMapping)94 void stubGetpwuid(const std::unordered_map<uid_t, std::string>& nativeUidToPackageNameMapping) {
95 updateNativeUidToPackageNameMapping(nativeUidToPackageNameMapping);
96 PackageInfoResolver::sGetpwuidHandler = [&](uid_t uid) -> struct passwd* {
97 const auto& it = mNativeUidToPackageNameMapping.find(uid);
98 if (it == mNativeUidToPackageNameMapping.end()) {
99 return nullptr;
100 }
101 return &it->second;
102 };
103 }
104
105 sp<MockWatchdogServiceHelper> mockWatchdogServiceHelper;
106
107 private:
updateNativeUidToPackageNameMapping(const std::unordered_map<uid_t,std::string> & mapping)108 void updateNativeUidToPackageNameMapping(
109 const std::unordered_map<uid_t, std::string>& mapping) {
110 clearMappingCache();
111 for (const auto& it : mapping) {
112 size_t packageNameLen = it.second.size() + 1;
113 char* packageName = new char[packageNameLen];
114 if (packageName == nullptr) {
115 continue;
116 }
117 memset(packageName, 0, packageNameLen);
118 snprintf(packageName, packageNameLen, "%s", it.second.c_str());
119
120 struct passwd pwd {
121 .pw_name = packageName, .pw_uid = it.first
122 };
123 mNativeUidToPackageNameMapping.insert(std::make_pair(it.first, pwd));
124 }
125 }
126
clearMappingCache()127 void clearMappingCache() {
128 for (const auto it : mNativeUidToPackageNameMapping) {
129 // Delete the previously allocated char array before clearing the mapping.
130 delete it.second.pw_name;
131 }
132 mNativeUidToPackageNameMapping.clear();
133 }
134
135 sp<PackageInfoResolver> mPackageInfoResolver;
136 std::unordered_map<uid_t, struct passwd> mNativeUidToPackageNameMapping;
137 };
138
139 } // namespace internal
140
TEST(PackageInfoResolverTest,TestGetPackageInfosForUidsViaGetpwuid)141 TEST(PackageInfoResolverTest, TestGetPackageInfosForUidsViaGetpwuid) {
142 internal::PackageInfoResolverPeer peer;
143 auto packageInfoResolver = PackageInfoResolver::getInstance();
144 PackageToAppCategoryMap packagesToAppCategories = {
145 // These mappings should be ignored for native packages.
146 {"system.package.B", ApplicationCategoryType::MAPS},
147 {"vendor.package.A", ApplicationCategoryType::MEDIA},
148 {"vendor.pkg.maps", ApplicationCategoryType::MAPS},
149 };
150 peer.setPackageConfigurations({"vendor.pkg"}, packagesToAppCategories);
151
152 std::unordered_map<uid_t, PackageInfo> expectedMappings{
153 {7700,
154 constructPackageInfo("system.package.B", 7700, UidType::NATIVE, ComponentType::SYSTEM,
155 ApplicationCategoryType::OTHERS)},
156 {5100,
157 constructPackageInfo("vendor.package.A", 5100, UidType::NATIVE, ComponentType::VENDOR,
158 ApplicationCategoryType::OTHERS)},
159 {6700,
160 constructPackageInfo("vendor.package.B", 6700, UidType::NATIVE, ComponentType::VENDOR,
161 ApplicationCategoryType::OTHERS)},
162 {9997,
163 constructPackageInfo("vendor.pkg.C", 9997, UidType::NATIVE, ComponentType::VENDOR,
164 ApplicationCategoryType::OTHERS)},
165 };
166
167 peer.stubGetpwuid({{7700, "system.package.B"},
168 {5100, "vendor.package.A"},
169 {6700, "vendor.package.B"},
170 {9997, "vendor.pkg.C"}});
171 EXPECT_CALL(*peer.mockWatchdogServiceHelper, getPackageInfosForUids(_, _, _)).Times(0);
172
173 auto actualMappings = packageInfoResolver->getPackageInfosForUids({7700, 5100, 6700, 9997});
174
175 EXPECT_THAT(actualMappings, UnorderedElementsAreArray(expectedMappings))
176 << "Expected: " << toString(expectedMappings)
177 << "\nActual: " << toString(actualMappings);
178 }
179
TEST(PackageInfoResolverTest,TestGetPackageInfosForUidsViaWatchdogService)180 TEST(PackageInfoResolverTest, TestGetPackageInfosForUidsViaWatchdogService) {
181 internal::PackageInfoResolverPeer peer;
182 auto packageInfoResolver = PackageInfoResolver::getInstance();
183 PackageToAppCategoryMap packagesToAppCategories = {
184 // system.package.B is native package so this should be ignored.
185 {"system.package.B", ApplicationCategoryType::MAPS},
186 {"vendor.package.A", ApplicationCategoryType::MEDIA},
187 {"shared:vendor.package.C", ApplicationCategoryType::MEDIA},
188 {"vendor.package.shared.uid.D", ApplicationCategoryType::MAPS},
189 };
190 peer.setPackageConfigurations({"vendor.pkg"}, packagesToAppCategories);
191 /*
192 * Shared UID should be resolved with car watchdog service as well to get the shared packages
193 * list.
194 */
195 peer.stubGetpwuid({{6100, "shared:system.package.A"}});
196
197 std::unordered_map<uid_t, PackageInfo> expectedMappings{
198 {6100,
199 constructPackageInfo("shared:system.package.A", 6100, UidType::NATIVE,
200 ComponentType::SYSTEM, ApplicationCategoryType::OTHERS,
201 {"system.pkg.1", "system.pkg.2"})},
202 {7700,
203 constructPackageInfo("system.package.B", 7700, UidType::NATIVE, ComponentType::SYSTEM,
204 ApplicationCategoryType::OTHERS)},
205 {15100,
206 constructPackageInfo("vendor.package.A", 15100, UidType::APPLICATION,
207 ComponentType::VENDOR, ApplicationCategoryType::OTHERS)},
208 {16700,
209 constructPackageInfo("vendor.pkg", 16700, UidType::NATIVE, ComponentType::VENDOR,
210 ApplicationCategoryType::OTHERS)},
211 {18100,
212 constructPackageInfo("shared:vendor.package.C", 18100, UidType::APPLICATION,
213 ComponentType::VENDOR, ApplicationCategoryType::OTHERS)},
214 {19100,
215 constructPackageInfo("shared:vendor.package.D", 19100, UidType::APPLICATION,
216 ComponentType::VENDOR, ApplicationCategoryType::OTHERS,
217 {"vendor.package.shared.uid.D"})},
218 };
219
220 std::vector<int32_t> expectedUids = {6100, 7700, 15100, 16700, 18100, 19100};
221 std::vector<std::string> expectedPrefixes = {"vendor.pkg"};
222 std::vector<PackageInfo> injectPackageInfos = {expectedMappings.at(6100),
223 expectedMappings.at(7700),
224 expectedMappings.at(15100),
225 expectedMappings.at(16700),
226 expectedMappings.at(18100),
227 expectedMappings.at(19100)};
228
229 expectedMappings.at(15100).appCategoryType = ApplicationCategoryType::MEDIA;
230 expectedMappings.at(18100).appCategoryType = ApplicationCategoryType::MEDIA;
231 expectedMappings.at(19100).appCategoryType = ApplicationCategoryType::MAPS;
232
233 EXPECT_CALL(*peer.mockWatchdogServiceHelper,
234 getPackageInfosForUids(expectedUids, expectedPrefixes, _))
235 .WillOnce(DoAll(SetArgPointee<2>(injectPackageInfos), Return(binder::Status::ok())));
236
237 auto actualMappings =
238 packageInfoResolver->getPackageInfosForUids({6100, 7700, 15100, 16700, 18100, 19100});
239
240 EXPECT_THAT(actualMappings, UnorderedElementsAreArray(expectedMappings))
241 << "Expected: " << toString(expectedMappings)
242 << "\nActual: " << toString(actualMappings);
243 }
244
TEST(PackageInfoResolverTest,TestResolvesApplicationUidFromLocalCache)245 TEST(PackageInfoResolverTest, TestResolvesApplicationUidFromLocalCache) {
246 internal::PackageInfoResolverPeer peer;
247 auto packageInfoResolver = PackageInfoResolver::getInstance();
248 std::unordered_map<uid_t, PackageInfo> expectedMappings{
249 {1003456,
250 constructPackageInfo("vendor.package", 1003456, UidType::NATIVE, ComponentType::SYSTEM,
251 ApplicationCategoryType::OTHERS)}};
252 peer.injectCacheMapping(expectedMappings);
253
254 peer.stubGetpwuid({});
255 EXPECT_CALL(*peer.mockWatchdogServiceHelper, getPackageInfosForUids(_, _, _)).Times(0);
256
257 auto actualMappings = packageInfoResolver->getPackageInfosForUids({1003456});
258
259 EXPECT_THAT(actualMappings, UnorderedElementsAreArray(expectedMappings))
260 << "Expected: " << toString(expectedMappings)
261 << "\nActual: " << toString(actualMappings);
262 }
263
264 } // namespace watchdog
265 } // namespace automotive
266 } // namespace android
267