1 /*
2 * Copyright 2016 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 "driver_base/DriverBase.h"
18
19 #include <dirent.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <fstream>
28 #include <iostream>
29 #include <sstream>
30 #include <string>
31 #include <vector>
32
33 #include "test/vts/proto/ComponentSpecificationMessage.pb.h"
34
35 #include "component_loader/DllLoader.h"
36 #include "utils/InterfaceSpecUtil.h"
37
38 #include "GcdaParser.h"
39
40 using namespace std;
41 using namespace android;
42
43 #define USE_GCOV 1
44
45 #if SANCOV
46 extern "C" {
47 typedef unsigned long uptr;
48
49 #define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
50 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_cov(uint32_t * guard)51 void __sanitizer_cov(uint32_t* guard) {
52 printf("sancov\n");
53 coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
54 guard);
55 }
56
57 SANITIZER_INTERFACE_ATTRIBUTE
__asan_memset(void * block,int c,uptr size)58 void* __asan_memset(void* block, int c, uptr size) {
59 ASAN_MEMSET_IMPL(nullptr, block, c, size);
60 return block;
61 }
62
63 SANITIZER_INTERFACE_ATTRIBUTE
__asan_register_globals()64 void __asan_register_globals() {}
65 SANITIZER_INTERFACE_ATTRIBUTE
__asan_unregister_globals()66 void __asan_unregister_globals() {}
67 SANITIZER_INTERFACE_ATTRIBUTE
__asan_handle_no_return()68 void __asan_handle_no_return() {}
69 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_load1()70 void __asan_report_load1() {}
71 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_load2()72 void __asan_report_load2() {}
73 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_load4()74 void __asan_report_load4() {}
75 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_load8()76 void __asan_report_load8() {}
77 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_load16()78 void __asan_report_load16() {}
79 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_store1()80 void __asan_report_store1() {}
81 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_store2()82 void __asan_report_store2() {}
83 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_store4()84 void __asan_report_store4() {}
85 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_store8()86 void __asan_report_store8() {}
87 SANITIZER_INTERFACE_ATTRIBUTE
__asan_report_store16()88 void __asan_report_store16() {}
89 SANITIZER_INTERFACE_ATTRIBUTE
__asan_set_error_report_callback()90 void __asan_set_error_report_callback() {}
91
92 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_1(uptr size,uptr real_stack)93 uptr __asan_stack_malloc_1(uptr size, uptr real_stack) {
94 return (uptr)malloc(size);
95 }
96
97 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_2(uptr size,uptr real_stack)98 uptr __asan_stack_malloc_2(uptr size, uptr real_stack) {
99 return (uptr)malloc(size);
100 }
101
102 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_3(uptr size,uptr real_stack)103 uptr __asan_stack_malloc_3(uptr size, uptr real_stack) {
104 return (uptr)malloc(size);
105 }
106
107 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_4(uptr size,uptr real_stack)108 uptr __asan_stack_malloc_4(uptr size, uptr real_stack) {
109 return (uptr)malloc(size);
110 }
111
112 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_5(uptr size,uptr real_stack)113 uptr __asan_stack_malloc_5(uptr size, uptr real_stack) {
114 return (uptr)malloc(size);
115 }
116
117 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_6(uptr size,uptr real_stack)118 uptr __asan_stack_malloc_6(uptr size, uptr real_stack) {
119 return (uptr)malloc(size);
120 }
121
122 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_7(uptr size,uptr real_stack)123 uptr __asan_stack_malloc_7(uptr size, uptr real_stack) {
124 return (uptr)malloc(size);
125 }
126
127 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_8(uptr size,uptr real_stack)128 uptr __asan_stack_malloc_8(uptr size, uptr real_stack) {
129 return (uptr)malloc(size);
130 }
131
132 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_9(uptr size,uptr real_stack)133 uptr __asan_stack_malloc_9(uptr size, uptr real_stack) {
134 return (uptr)malloc(size);
135 }
136
137 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_malloc_10(uptr size,uptr real_stack)138 uptr __asan_stack_malloc_10(uptr size, uptr real_stack) {
139 return (uptr)malloc(size);
140 }
141
142 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_1(uptr ptr,uptr size,uptr real_stack)143 void __asan_stack_free_1(uptr ptr, uptr size, uptr real_stack) {
144 free((void*)ptr);
145 }
146
147 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_2(uptr ptr,uptr size,uptr real_stack)148 void __asan_stack_free_2(uptr ptr, uptr size, uptr real_stack) {
149 free((void*)ptr);
150 }
151
152 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_3(uptr ptr,uptr size,uptr real_stack)153 void __asan_stack_free_3(uptr ptr, uptr size, uptr real_stack) {
154 free((void*)ptr);
155 }
156
157 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_4(uptr ptr,uptr size,uptr real_stack)158 void __asan_stack_free_4(uptr ptr, uptr size, uptr real_stack) {
159 free((void*)ptr);
160 }
161
162 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_5(uptr ptr,uptr size,uptr real_stack)163 void __asan_stack_free_5(uptr ptr, uptr size, uptr real_stack) {
164 free((void*)ptr);
165 }
166
167 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_6(uptr ptr,uptr size,uptr real_stack)168 void __asan_stack_free_6(uptr ptr, uptr size, uptr real_stack) {
169 free((void*)ptr);
170 }
171
172 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_7(uptr ptr,uptr size,uptr real_stack)173 void __asan_stack_free_7(uptr ptr, uptr size, uptr real_stack) {
174 free((void*)ptr);
175 }
176
177 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_8(uptr ptr,uptr size,uptr real_stack)178 void __asan_stack_free_8(uptr ptr, uptr size, uptr real_stack) {
179 free((void*)ptr);
180 }
181
182 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_9(uptr ptr,uptr size,uptr real_stack)183 void __asan_stack_free_9(uptr ptr, uptr size, uptr real_stack) {
184 free((void*)ptr);
185 }
186
187 SANITIZER_INTERFACE_ATTRIBUTE
__asan_stack_free_10(uptr ptr,uptr size,uptr real_stack)188 void __asan_stack_free_10(uptr ptr, uptr size, uptr real_stack) {
189 free((void*)ptr);
190 }
191
192 SANITIZER_INTERFACE_ATTRIBUTE
__asan_version_mismatch_check_v6()193 void __asan_version_mismatch_check_v6() {
194 // Do nothing.
195 }
196
__sanitizer_cov_module_init(int32_t * guards,uptr npcs,uint8_t * counters,const char * comp_unit_name)197 SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(
198 int32_t* guards, uptr npcs, uint8_t* counters, const char* comp_unit_name) {
199 }
200
201 SANITIZER_INTERFACE_ATTRIBUTE
__asan_init()202 void __asan_init() {
203 static int inited = 0;
204 if (inited) return;
205 inited = 1;
206 #if __WORDSIZE == 64
207 unsigned long start = 0x100000000000;
208 unsigned long size = 0x100000000000;
209 #else
210 unsigned long start = 0x20000000;
211 unsigned long size = 0x20000000;
212 #endif
213 void* res = mmap((void*)start, size, PROT_READ | PROT_WRITE,
214 MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0);
215 if (res == (void*)start) {
216 fprintf(stderr, "Fake AddressSanitizer run-time initialized ok at %p\n",
217 res);
218 } else {
219 fprintf(stderr,
220 "Fake AddressSanitizer run-time failed to initialize.\n"
221 "You have been warned. Aborting.");
222 abort();
223 }
224 }
225 }
226 #endif
227
228 namespace android {
229 namespace vts {
230
231 const string default_gcov_output_basepath = "/data/misc/gcov";
232
RemoveDir(char * path)233 static void RemoveDir(char* path) {
234 struct dirent* entry = NULL;
235 DIR* dir = opendir(path);
236
237 while ((entry = readdir(dir)) != NULL) {
238 DIR* sub_dir = NULL;
239 FILE* file = NULL;
240 char abs_path[4096] = {0};
241
242 if (*(entry->d_name) != '.') {
243 sprintf(abs_path, "%s/%s", path, entry->d_name);
244 if ((sub_dir = opendir(abs_path)) != NULL) {
245 closedir(sub_dir);
246 RemoveDir(abs_path);
247 } else if ((file = fopen(abs_path, "r")) != NULL) {
248 fclose(file);
249 remove(abs_path);
250 }
251 }
252 }
253 remove(path);
254 }
255
DriverBase(int target_class)256 DriverBase::DriverBase(int target_class)
257 : device_(NULL),
258 hmi_(NULL),
259 target_dll_path_(NULL),
260 target_class_(target_class),
261 component_filename_(NULL),
262 gcov_output_basepath_(NULL) {}
263
~DriverBase()264 DriverBase::~DriverBase() { free(component_filename_); }
265
wfn()266 void wfn() { cout << __func__ << endl; }
267
ffn()268 void ffn() { cout << __func__ << endl; }
269
LoadTargetComponent(const char * target_dll_path)270 bool DriverBase::LoadTargetComponent(const char* target_dll_path) {
271 cout << __func__ << ":" << __LINE__ << " entry" << endl;
272 if (target_dll_path && target_dll_path_ &&
273 !strcmp(target_dll_path, target_dll_path_)) {
274 cout << __func__ << " skip loading" << endl;
275 return true;
276 }
277
278 if (!target_loader_.Load(target_dll_path)) return false;
279 target_dll_path_ = (char*)malloc(strlen(target_dll_path) + 1);
280 strcpy(target_dll_path_, target_dll_path);
281 cout << __FUNCTION__ << ":" << __LINE__ << " loaded the target" << endl;
282 if (target_class_ == HAL_LEGACY) return true;
283 cout << __FUNCTION__ << ":" << __LINE__ << " loaded a non-legacy HAL file."
284 << endl;
285 if (target_class_ == HAL_CONVENTIONAL) {
286 hmi_ = target_loader_.InitConventionalHal();
287 if (!hmi_) {
288 free(target_dll_path_);
289 target_dll_path_ = NULL;
290 return false;
291 }
292 }
293 #if SANCOV
294 cout << __FUNCTION__ << "sancov reset "
295 << target_loader_.SancovResetCoverage() << endl;
296 #endif
297
298 if (target_dll_path_) {
299 cout << __func__ << ":" << __LINE__ << " target DLL path "
300 << target_dll_path_ << endl;
301 string target_path(target_dll_path_);
302
303 size_t offset = target_path.rfind("/", target_path.length());
304 if (offset != string::npos) {
305 string filename =
306 target_path.substr(offset + 1, target_path.length() - offset);
307 filename = filename.substr(0, filename.length() - 3 /* for .so */);
308 component_filename_ = (char*)malloc(filename.length() + 1);
309 strcpy(component_filename_, filename.c_str());
310 cout << __FUNCTION__ << ":" << __LINE__
311 << " module file name: " << component_filename_ << endl;
312 }
313 cout << __FUNCTION__ << ":" << __LINE__ << " target_dll_path "
314 << target_dll_path_ << endl;
315 }
316
317 #if USE_GCOV
318 cout << __FUNCTION__ << ": gcov init " << target_loader_.GcovInit(wfn, ffn)
319 << endl;
320 #endif
321 return true;
322 }
323
SetTargetObject(void * object_pointer)324 bool DriverBase::SetTargetObject(void* object_pointer) {
325 device_ = NULL;
326 hmi_ = reinterpret_cast<struct hw_module_t*>(object_pointer);
327 return true;
328 }
329
GetService(bool,const char *)330 bool DriverBase::GetService(bool /*get_stub*/, const char* /*service_name*/) {
331 cerr << __func__ << " not impl" << endl;
332 return false;
333 }
334
OpenConventionalHal(const char * module_name)335 int DriverBase::OpenConventionalHal(const char* module_name) {
336 cout << __func__ << endl;
337 if (module_name) cout << __func__ << " " << module_name << endl;
338 device_ = target_loader_.OpenConventionalHal(module_name);
339 if (!device_) return -1;
340 cout << __func__ << " device_" << device_ << endl;
341 return 0;
342 }
343
Fuzz(vts::ComponentSpecificationMessage * message,void ** result)344 bool DriverBase::Fuzz(vts::ComponentSpecificationMessage* message,
345 void** result) {
346 cout << __func__ << " Fuzzing target component: "
347 << "class " << message->component_class() << " type "
348 << message->component_type() << " version "
349 << message->component_type_version() << endl;
350
351 string function_name_prefix = GetFunctionNamePrefix(*message);
352 function_name_prefix_ = function_name_prefix.c_str();
353 for (vts::FunctionSpecificationMessage func_msg :
354 *message->mutable_interface()->mutable_api()) {
355 Fuzz(&func_msg, result, "");
356 }
357 return true;
358 }
359
FunctionCallBegin()360 void DriverBase::FunctionCallBegin() {
361 char product_path[4096];
362 char product[128];
363 char module_basepath[4096];
364
365 cout << __func__ << ":" << __LINE__ << " begin" << endl;
366 snprintf(product_path, 4096, "%s/%s", default_gcov_output_basepath.c_str(),
367 "proc/self/cwd/out/target/product");
368 DIR* srcdir = opendir(product_path);
369 if (!srcdir) {
370 cerr << __func__ << " couldn't open " << product_path << endl;
371 return;
372 }
373
374 int dir_count = 0;
375 struct dirent* dent;
376 while ((dent = readdir(srcdir)) != NULL) {
377 struct stat st;
378 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
379 continue;
380 }
381 if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
382 cerr << __func__ << " error " << dent->d_name << endl;
383 continue;
384 }
385 if (S_ISDIR(st.st_mode)) {
386 cout << __func__ << ":" << __LINE__ << " dir " << dent->d_name << endl;
387 strcpy(product, dent->d_name);
388 dir_count++;
389 }
390 }
391 closedir(srcdir);
392 if (dir_count != 1) {
393 cerr << __func__ << " more than one product dir found." << endl;
394 return;
395 }
396
397 int n = snprintf(module_basepath, 4096, "%s/%s/obj/SHARED_LIBRARIES",
398 product_path, product);
399 if (n <= 0 || n >= 4096) {
400 cerr << __func__ << " couln't get module_basepath" << endl;
401 return;
402 }
403 srcdir = opendir(module_basepath);
404 if (!srcdir) {
405 cerr << __func__ << " couln't open " << module_basepath << endl;
406 return;
407 }
408
409 if (component_filename_ != NULL) {
410 dir_count = 0;
411 string target = string(component_filename_) + "_intermediates";
412 bool hit = false;
413 cout << __func__ << ":" << __LINE__ << endl;
414 while ((dent = readdir(srcdir)) != NULL) {
415 struct stat st;
416 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
417 continue;
418 }
419 if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
420 cerr << __func__ << " error " << dent->d_name << endl;
421 continue;
422 }
423 if (S_ISDIR(st.st_mode)) {
424 cout << "module_basepath " << string(dent->d_name) << endl;
425 if (string(dent->d_name) == target) {
426 cout << "hit" << endl;
427 hit = true;
428 }
429 dir_count++;
430 }
431 }
432 if (hit) {
433 free(gcov_output_basepath_);
434 gcov_output_basepath_ =
435 (char*)malloc(strlen(module_basepath) + target.length() + 2);
436 if (!gcov_output_basepath_) {
437 cerr << __FUNCTION__ << ": couldn't alloc memory" << endl;
438 return;
439 }
440 sprintf(gcov_output_basepath_, "%s/%s", module_basepath, target.c_str());
441 RemoveDir(gcov_output_basepath_);
442 }
443 } else {
444 cerr << __func__ << ":" << __LINE__ << " component_filename_ is NULL"
445 << endl;
446 }
447 // TODO: check how it works when there already is a file.
448 closedir(srcdir);
449 cout << __func__ << ":" << __LINE__ << " end" << endl;
450 }
451
ReadGcdaFile(const string & basepath,const string & filename,FunctionSpecificationMessage * msg)452 bool DriverBase::ReadGcdaFile(const string& basepath, const string& filename,
453 FunctionSpecificationMessage* msg) {
454 #if VTS_GCOV_DEBUG
455 cout << __func__ << ":" << __LINE__ << " file = " << dent->d_name << endl;
456 #endif
457 if (string(filename).rfind(".gcda") != string::npos) {
458 string buffer = basepath + "/" + filename;
459 vector<unsigned> processed_data =
460 android::vts::GcdaRawCoverageParser(buffer.c_str()).Parse();
461 for (const auto& data : processed_data) {
462 msg->mutable_processed_coverage_data()->Add(data);
463 }
464
465 FILE* gcda_file = fopen(buffer.c_str(), "rb");
466 if (!gcda_file) {
467 cerr << __func__ << ":" << __LINE__ << " Unable to open a gcda file. "
468 << buffer << endl;
469 } else {
470 cout << __func__ << ":" << __LINE__ << " Opened a gcda file. " << buffer
471 << endl;
472 fseek(gcda_file, 0, SEEK_END);
473 long gcda_file_size = ftell(gcda_file);
474 #if VTS_GCOV_DEBUG
475 cout << __func__ << ":" << __LINE__ << " File size " << gcda_file_size
476 << " bytes" << endl;
477 #endif
478 fseek(gcda_file, 0, SEEK_SET);
479
480 char* gcda_file_buffer = (char*)malloc(gcda_file_size + 1);
481 if (!gcda_file_buffer) {
482 cerr << __func__ << ":" << __LINE__
483 << "Unable to allocate memory to read a gcda file. " << endl;
484 } else {
485 if (fread(gcda_file_buffer, gcda_file_size, 1, gcda_file) != 1) {
486 cerr << __func__ << ":" << __LINE__ << "File read error" << endl;
487 } else {
488 #if VTS_GCOV_DEBUG
489 cout << __func__ << ":" << __LINE__ << " GCDA field populated."
490 << endl;
491 #endif
492 gcda_file_buffer[gcda_file_size] = '\0';
493 NativeCodeCoverageRawDataMessage* raw_msg =
494 msg->mutable_raw_coverage_data()->Add();
495 raw_msg->set_file_path(filename.c_str());
496 string gcda_file_buffer_string(gcda_file_buffer, gcda_file_size);
497 raw_msg->set_gcda(gcda_file_buffer_string);
498 free(gcda_file_buffer);
499 }
500 }
501 fclose(gcda_file);
502 }
503 #if USE_GCOV_DEBUG
504 if (result) {
505 for (unsigned int index = 0; index < result->size(); index++) {
506 cout << result->at(index) << endl;
507 }
508 }
509 #endif
510 return true;
511 }
512 return false;
513 }
514
ScanAllGcdaFiles(const string & basepath,FunctionSpecificationMessage * msg)515 bool DriverBase::ScanAllGcdaFiles(const string& basepath,
516 FunctionSpecificationMessage* msg) {
517 DIR* srcdir = opendir(basepath.c_str());
518 if (!srcdir) {
519 cerr << __func__ << ":" << __LINE__ << " couln't open " << basepath << endl;
520 return false;
521 }
522
523 struct dirent* dent;
524 while ((dent = readdir(srcdir)) != NULL) {
525 #if VTS_GCOV_DEBUG
526 cout << __func__ << ":" << __LINE__ << " readdir(" << basepath << ") for "
527 << dent->d_name << endl;
528 #endif
529 struct stat st;
530 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
531 continue;
532 }
533 if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
534 cerr << "error " << dent->d_name << endl;
535 continue;
536 }
537 if (S_ISDIR(st.st_mode)) {
538 ScanAllGcdaFiles(basepath + "/" + dent->d_name, msg);
539 } else {
540 ReadGcdaFile(basepath, dent->d_name, msg);
541 }
542 }
543 #if VTS_GCOV_DEBUG
544 cout << __func__ << ":" << __LINE__ << " closedir(" << srcdir << ")" << endl;
545 #endif
546 closedir(srcdir);
547 return true;
548 }
549
FunctionCallEnd(FunctionSpecificationMessage * msg)550 bool DriverBase::FunctionCallEnd(FunctionSpecificationMessage* msg) {
551 cout << __func__ << ": gcov flush " << endl;
552 #if USE_GCOV
553 target_loader_.GcovFlush();
554 // find the file.
555 if (!gcov_output_basepath_) {
556 cerr << __FUNCTION__ << ": no gcov basepath set" << endl;
557 return ScanAllGcdaFiles(default_gcov_output_basepath, msg);
558 }
559 DIR* srcdir = opendir(gcov_output_basepath_);
560 if (!srcdir) {
561 cerr << __func__ << " couln't open " << gcov_output_basepath_ << endl;
562 return false;
563 }
564
565 struct dirent* dent;
566 while ((dent = readdir(srcdir)) != NULL) {
567 cout << __func__ << ": readdir(" << srcdir << ") for " << dent->d_name
568 << endl;
569
570 struct stat st;
571 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
572 continue;
573 }
574 if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
575 cerr << "error " << dent->d_name << endl;
576 continue;
577 }
578 if (!S_ISDIR(st.st_mode) &&
579 ReadGcdaFile(gcov_output_basepath_, dent->d_name, msg)) {
580 break;
581 }
582 }
583 cout << __func__ << ": closedir(" << srcdir << ")" << endl;
584 closedir(srcdir);
585 #endif
586 return true;
587 }
588
589 } // namespace vts
590 } // namespace android
591