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