1 /*
2 * Copyright (C) 2017 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 <stdlib.h>
18 #include <string.h>
19 #include <sys/statvfs.h>
20 #include <sys/xattr.h>
21
22 #include <android-base/logging.h>
23 #include <android-base/stringprintf.h>
24 #include <cutils/properties.h>
25 #include <gtest/gtest.h>
26
27 #include "InstalldNativeService.h"
28 #include "globals.h"
29 #include "utils.h"
30
31 using android::base::StringPrintf;
32
33 namespace android {
34 namespace installd {
35
36 constexpr const char* kTestUuid = "TEST";
37
38 constexpr int64_t kKbInBytes = 1024;
39 constexpr int64_t kMbInBytes = 1024 * kKbInBytes;
40 constexpr int64_t kGbInBytes = 1024 * kMbInBytes;
41 constexpr int64_t kTbInBytes = 1024 * kGbInBytes;
42
43 #define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2
44 #define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA
45 #define FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES InstalldNativeService::FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES
46
get_property(const char * key,char * value,const char * default_value)47 int get_property(const char *key, char *value, const char *default_value) {
48 return property_get(key, value, default_value);
49 }
50
calculate_oat_file_path(char path[PKG_PATH_MAX]ATTRIBUTE_UNUSED,const char * oat_dir ATTRIBUTE_UNUSED,const char * apk_path ATTRIBUTE_UNUSED,const char * instruction_set ATTRIBUTE_UNUSED)51 bool calculate_oat_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
52 const char *oat_dir ATTRIBUTE_UNUSED,
53 const char *apk_path ATTRIBUTE_UNUSED,
54 const char *instruction_set ATTRIBUTE_UNUSED) {
55 return false;
56 }
57
calculate_odex_file_path(char path[PKG_PATH_MAX]ATTRIBUTE_UNUSED,const char * apk_path ATTRIBUTE_UNUSED,const char * instruction_set ATTRIBUTE_UNUSED)58 bool calculate_odex_file_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
59 const char *apk_path ATTRIBUTE_UNUSED,
60 const char *instruction_set ATTRIBUTE_UNUSED) {
61 return false;
62 }
63
create_cache_path(char path[PKG_PATH_MAX]ATTRIBUTE_UNUSED,const char * src ATTRIBUTE_UNUSED,const char * instruction_set ATTRIBUTE_UNUSED)64 bool create_cache_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED,
65 const char *src ATTRIBUTE_UNUSED,
66 const char *instruction_set ATTRIBUTE_UNUSED) {
67 return false;
68 }
69
mkdir(const char * path)70 static void mkdir(const char* path) {
71 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
72 ::mkdir(fullPath.c_str(), 0755);
73 }
74
touch(const char * path,int len,int time)75 static void touch(const char* path, int len, int time) {
76 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
77 int fd = ::open(fullPath.c_str(), O_RDWR | O_CREAT, 0644);
78 ::fallocate(fd, 0, 0, len);
79 ::close(fd);
80 struct utimbuf times;
81 times.actime = times.modtime = std::time(0) + time;
82 ::utime(fullPath.c_str(), ×);
83 }
84
exists(const char * path)85 static int exists(const char* path) {
86 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
87 return ::access(fullPath.c_str(), F_OK);
88 }
89
size(const char * path)90 static int64_t size(const char* path) {
91 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
92 struct stat buf;
93 if (!stat(fullPath.c_str(), &buf)) {
94 return buf.st_size;
95 } else {
96 return -1;
97 }
98 }
99
free()100 static int64_t free() {
101 struct statvfs buf;
102 if (!statvfs("/data/local/tmp", &buf)) {
103 return static_cast<int64_t>(buf.f_bavail) * buf.f_frsize;
104 } else {
105 PLOG(ERROR) << "Failed to statvfs";
106 return -1;
107 }
108 }
109
setxattr(const char * path,const char * key)110 static void setxattr(const char* path, const char* key) {
111 const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path);
112 ::setxattr(fullPath.c_str(), key, "", 0, 0);
113 }
114
115 class CacheTest : public testing::Test {
116 protected:
117 InstalldNativeService* service;
118 std::optional<std::string> testUuid;
119
SetUp()120 virtual void SetUp() {
121 setenv("ANDROID_LOG_TAGS", "*:v", 1);
122 android::base::InitLogging(nullptr);
123
124 service = new InstalldNativeService();
125 testUuid = kTestUuid;
126 system("rm -rf /data/local/tmp/user");
127 system("mkdir -p /data/local/tmp/user/0");
128 }
129
TearDown()130 virtual void TearDown() {
131 delete service;
132 system("rm -rf /data/local/tmp/user");
133 }
134 };
135
TEST_F(CacheTest,FreeCache_All)136 TEST_F(CacheTest, FreeCache_All) {
137 LOG(INFO) << "FreeCache_All";
138
139 mkdir("com.example");
140 touch("com.example/normal", 1 * kMbInBytes, 60);
141 mkdir("com.example/cache");
142 mkdir("com.example/cache/foo");
143 touch("com.example/cache/foo/one", 1 * kMbInBytes, 60);
144 touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
145
146 EXPECT_EQ(0, exists("com.example/normal"));
147 EXPECT_EQ(0, exists("com.example/cache/foo/one"));
148 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
149
150 service->freeCache(testUuid, kTbInBytes,
151 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
152
153 EXPECT_EQ(0, exists("com.example/normal"));
154 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
155 EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
156 }
157
TEST_F(CacheTest,FreeCache_NonAggressive)158 TEST_F(CacheTest, FreeCache_NonAggressive) {
159 LOG(INFO) << "FreeCache_NonAggressive";
160
161 mkdir("com.example");
162 touch("com.example/normal", 1 * kMbInBytes, 60);
163 mkdir("com.example/cache");
164 mkdir("com.example/cache/foo");
165 touch("com.example/cache/foo/one", 65 * kMbInBytes, 60);
166 touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
167
168 EXPECT_EQ(0, exists("com.example/normal"));
169 EXPECT_EQ(0, exists("com.example/cache/foo/one"));
170 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
171
172 service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2);
173
174 EXPECT_EQ(0, exists("com.example/normal"));
175 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
176 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
177
178 service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2);
179
180 EXPECT_EQ(0, exists("com.example/normal"));
181 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
182 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
183 }
184
TEST_F(CacheTest,FreeCache_DefyTargetFreeBytes)185 TEST_F(CacheTest, FreeCache_DefyTargetFreeBytes) {
186 LOG(INFO) << "FreeCache_DefyTargetFreeBytes";
187
188 mkdir("com.example");
189 touch("com.example/normal", 1 * kMbInBytes, 60);
190 mkdir("com.example/cache");
191 mkdir("com.example/cache/foo");
192 touch("com.example/cache/foo/one", 65 * kMbInBytes, 60);
193 touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
194
195 EXPECT_EQ(0, exists("com.example/normal"));
196 EXPECT_EQ(0, exists("com.example/cache/foo/one"));
197 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
198
199 service->freeCache(testUuid, kMbInBytes, FLAG_FREE_CACHE_V2
200 | FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES);
201
202 EXPECT_EQ(0, exists("com.example/normal"));
203 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
204 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
205
206 service->freeCache(testUuid, kMbInBytes, FLAG_FREE_CACHE_V2
207 | FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES);
208
209 EXPECT_EQ(0, exists("com.example/normal"));
210 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
211 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
212 }
213
TEST_F(CacheTest,FreeCache_Age)214 TEST_F(CacheTest, FreeCache_Age) {
215 LOG(INFO) << "FreeCache_Age";
216
217 mkdir("com.example");
218 mkdir("com.example/cache");
219 mkdir("com.example/cache/foo");
220 touch("com.example/cache/foo/one", kMbInBytes, 60);
221 touch("com.example/cache/foo/two", kMbInBytes, 120);
222
223 service->freeCache(testUuid, free() + kKbInBytes,
224 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
225
226 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
227 EXPECT_EQ(0, exists("com.example/cache/foo/two"));
228
229 service->freeCache(testUuid, free() + kKbInBytes,
230 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
231
232 EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
233 EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
234 }
235
TEST_F(CacheTest,FreeCache_Tombstone)236 TEST_F(CacheTest, FreeCache_Tombstone) {
237 LOG(INFO) << "FreeCache_Tombstone";
238
239 mkdir("com.example");
240 mkdir("com.example/cache");
241 mkdir("com.example/cache/foo");
242 touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
243 touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 60);
244 mkdir("com.example/cache/bar");
245 touch("com.example/cache/bar/bar1", 2 * kMbInBytes, 120);
246 touch("com.example/cache/bar/bar2", 2 * kMbInBytes, 120);
247
248 setxattr("com.example/cache/bar", "user.cache_tombstone");
249
250 EXPECT_EQ(0, exists("com.example/cache/foo/foo1"));
251 EXPECT_EQ(0, exists("com.example/cache/foo/foo2"));
252 EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
253 EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
254 EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
255 EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
256
257 service->freeCache(testUuid, kTbInBytes,
258 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
259
260 EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
261 EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
262 EXPECT_EQ(0, exists("com.example/cache/bar/bar1"));
263 EXPECT_EQ(0, exists("com.example/cache/bar/bar2"));
264 EXPECT_EQ(0, size("com.example/cache/bar/bar1"));
265 EXPECT_EQ(0, size("com.example/cache/bar/bar2"));
266 }
267
TEST_F(CacheTest,FreeCache_Group)268 TEST_F(CacheTest, FreeCache_Group) {
269 LOG(INFO) << "FreeCache_Group";
270
271 mkdir("com.example");
272 mkdir("com.example/cache");
273 mkdir("com.example/cache/foo");
274 touch("com.example/cache/foo/foo1", 1 * kMbInBytes, 60);
275 touch("com.example/cache/foo/foo2", 1 * kMbInBytes, 120);
276
277 setxattr("com.example/cache/foo", "user.cache_group");
278
279 service->freeCache(testUuid, free() + kKbInBytes,
280 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
281
282 EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
283 EXPECT_EQ(-1, exists("com.example/cache/foo/foo2"));
284 }
285
TEST_F(CacheTest,FreeCache_GroupTombstone)286 TEST_F(CacheTest, FreeCache_GroupTombstone) {
287 LOG(INFO) << "FreeCache_GroupTombstone";
288
289 mkdir("com.example");
290 mkdir("com.example/cache");
291
292 // this dir must look really old for some reason?
293 mkdir("com.example/cache/group");
294 touch("com.example/cache/group/file1", kMbInBytes, 120);
295 touch("com.example/cache/group/file2", kMbInBytes, 120);
296 mkdir("com.example/cache/group/dir");
297 touch("com.example/cache/group/dir/file1", kMbInBytes, 120);
298 touch("com.example/cache/group/dir/file2", kMbInBytes, 120);
299 mkdir("com.example/cache/group/tomb");
300 touch("com.example/cache/group/tomb/file1", kMbInBytes, 120);
301 touch("com.example/cache/group/tomb/file2", kMbInBytes, 120);
302 mkdir("com.example/cache/group/tomb/dir");
303 touch("com.example/cache/group/tomb/dir/file1", kMbInBytes, 120);
304 touch("com.example/cache/group/tomb/dir/file2", kMbInBytes, 120);
305
306 mkdir("com.example/cache/tomb");
307 touch("com.example/cache/tomb/file1", kMbInBytes, 240);
308 touch("com.example/cache/tomb/file2", kMbInBytes, 240);
309 mkdir("com.example/cache/tomb/dir");
310 touch("com.example/cache/tomb/dir/file1", kMbInBytes, 240);
311 touch("com.example/cache/tomb/dir/file2", kMbInBytes, 240);
312 mkdir("com.example/cache/tomb/group");
313 touch("com.example/cache/tomb/group/file1", kMbInBytes, 60);
314 touch("com.example/cache/tomb/group/file2", kMbInBytes, 60);
315 mkdir("com.example/cache/tomb/group/dir");
316 touch("com.example/cache/tomb/group/dir/file1", kMbInBytes, 60);
317 touch("com.example/cache/tomb/group/dir/file2", kMbInBytes, 60);
318
319 setxattr("com.example/cache/group", "user.cache_group");
320 setxattr("com.example/cache/group/tomb", "user.cache_tombstone");
321 setxattr("com.example/cache/tomb", "user.cache_tombstone");
322 setxattr("com.example/cache/tomb/group", "user.cache_group");
323
324 service->freeCache(testUuid, free() + kKbInBytes,
325 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
326
327 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1"));
328 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file2"));
329 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file1"));
330 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/dir/file2"));
331 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file1"));
332 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/file2"));
333 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file1"));
334 EXPECT_EQ(kMbInBytes, size("com.example/cache/group/tomb/dir/file2"));
335
336 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
337 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
338 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
339 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
340 EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
341 EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
342 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
343 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
344
345 service->freeCache(testUuid, free() + kKbInBytes,
346 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
347
348 EXPECT_EQ(-1, size("com.example/cache/group/file1"));
349 EXPECT_EQ(-1, size("com.example/cache/group/file2"));
350 EXPECT_EQ(-1, size("com.example/cache/group/dir/file1"));
351 EXPECT_EQ(-1, size("com.example/cache/group/dir/file2"));
352 EXPECT_EQ(0, size("com.example/cache/group/tomb/file1"));
353 EXPECT_EQ(0, size("com.example/cache/group/tomb/file2"));
354 EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1"));
355 EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2"));
356
357 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file1"));
358 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/file2"));
359 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file1"));
360 EXPECT_EQ(kMbInBytes, size("com.example/cache/tomb/dir/file2"));
361 EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
362 EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
363 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
364 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
365
366 service->freeCache(testUuid, kTbInBytes,
367 FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
368
369 EXPECT_EQ(-1, size("com.example/cache/group/file1"));
370 EXPECT_EQ(-1, size("com.example/cache/group/file2"));
371 EXPECT_EQ(-1, size("com.example/cache/group/dir/file1"));
372 EXPECT_EQ(-1, size("com.example/cache/group/dir/file2"));
373 EXPECT_EQ(0, size("com.example/cache/group/tomb/file1"));
374 EXPECT_EQ(0, size("com.example/cache/group/tomb/file2"));
375 EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file1"));
376 EXPECT_EQ(0, size("com.example/cache/group/tomb/dir/file2"));
377
378 EXPECT_EQ(0, size("com.example/cache/tomb/file1"));
379 EXPECT_EQ(0, size("com.example/cache/tomb/file2"));
380 EXPECT_EQ(0, size("com.example/cache/tomb/dir/file1"));
381 EXPECT_EQ(0, size("com.example/cache/tomb/dir/file2"));
382 EXPECT_EQ(0, size("com.example/cache/tomb/group/file1"));
383 EXPECT_EQ(0, size("com.example/cache/tomb/group/file2"));
384 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
385 EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
386 }
387
388 } // namespace installd
389 } // namespace android
390