1 /* Copyright (C) 2008 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 #include "android/avd/info.h" 13 #include "android/utils/path.h" 14 #include "android/utils/bufprint.h" 15 #include "android/utils/filelock.h" 16 #include "android/utils/tempfile.h" 17 #include "android/utils/debug.h" 18 #include "android/utils/dirscanner.h" 19 #include <ctype.h> 20 #include <stddef.h> 21 #include <string.h> 22 #include <stdlib.h> 23 #include <stdio.h> 24 #include <errno.h> 25 26 /* global variables - see android/globals.h */ 27 AvdInfoParams android_avdParams[1]; 28 AvdInfo* android_avdInfo; 29 30 /* for debugging */ 31 #define D(...) VERBOSE_PRINT(init,__VA_ARGS__) 32 #define DD(...) VERBOSE_PRINT(avd_config,__VA_ARGS__) 33 34 /* technical note on how all of this is supposed to work: 35 * 36 * Each AVD corresponds to a "content directory" that is used to 37 * store persistent disk images and configuration files. Most remarkable 38 * are: 39 * 40 * - a "config.ini" file used to hold configuration information for the 41 * AVD 42 * 43 * - mandatory user data image ("userdata-qemu.img") and cache image 44 * ("cache.img") 45 * 46 * - optional mutable system image ("system-qemu.img"), kernel image 47 * ("kernel-qemu") and read-only ramdisk ("ramdisk.img") 48 * 49 * When starting up an AVD, the emulator looks for relevant disk images 50 * in the content directory. If it doesn't find a given image there, it 51 * will try to search in the list of system directories listed in the 52 * 'config.ini' file through one of the following (key,value) pairs: 53 * 54 * images.sysdir.1 = <first search path> 55 * images.sysdir.2 = <second search path> 56 * 57 * The search paths can be absolute, or relative to the root SDK installation 58 * path (which is determined from the emulator program's location, or from the 59 * ANDROID_SDK_ROOT environment variable). 60 * 61 * Individual image disk search patch can be over-riden on the command-line 62 * with one of the usual options. 63 */ 64 65 /* this is the subdirectory of $HOME/.android where all 66 * root configuration files (and default content directories) 67 * are located. 68 */ 69 #define ANDROID_AVD_DIR "avd" 70 71 /* the prefix of config.ini keys that will be used for search directories 72 * of system images. 73 */ 74 #define SEARCH_PREFIX "image.sysdir." 75 76 /* the maximum number of search path keys we're going to read from the 77 * config.ini file 78 */ 79 #define MAX_SEARCH_PATHS 2 80 81 /* the config.ini key that will be used to indicate the full relative 82 * path to the skin directory (including the skin name). 83 */ 84 #define SKIN_PATH "skin.path" 85 86 /* default skin name */ 87 #define SKIN_DEFAULT "HVGA" 88 89 /* the config.ini key that is used to indicate the absolute path 90 * to the SD Card image file, if you don't want to place it in 91 * the content directory. 92 */ 93 #define SDCARD_PATH "sdcard.path" 94 95 /* certain disk image files are mounted read/write by the emulator 96 * to ensure that several emulators referencing the same files 97 * do not corrupt these files, we need to lock them and respond 98 * to collision depending on the image type. 99 * 100 * the enumeration below is used to record information about 101 * each image file path. 102 * 103 * READONLY means that the file will be mounted read-only 104 * and this doesn't need to be locked. must be first in list 105 * 106 * MUSTLOCK means that the file should be locked before 107 * being mounted by the emulator 108 * 109 * TEMPORARY means that the file has been copied to a 110 * temporary image, which can be mounted read/write 111 * but doesn't require locking. 112 */ 113 typedef enum { 114 IMAGE_STATE_READONLY, /* unlocked */ 115 IMAGE_STATE_MUSTLOCK, /* must be locked */ 116 IMAGE_STATE_LOCKED, /* locked */ 117 IMAGE_STATE_LOCKED_EMPTY, /* locked and empty */ 118 IMAGE_STATE_TEMPORARY, /* copied to temp file (no lock needed) */ 119 } AvdImageState; 120 121 struct AvdInfo { 122 /* for the Android build system case */ 123 char inAndroidBuild; 124 char* androidOut; 125 char* androidBuildRoot; 126 127 /* for the normal virtual device case */ 128 char* deviceName; 129 char* sdkRootPath; 130 char sdkRootPathFromEnv; 131 char* searchPaths[ MAX_SEARCH_PATHS ]; 132 int numSearchPaths; 133 char* contentPath; 134 IniFile* rootIni; /* root <foo>.ini file */ 135 IniFile* configIni; /* virtual device's config.ini */ 136 IniFile* hardwareIni; /* skin-specific hardware.ini */ 137 138 /* for both */ 139 char* skinName; /* skin name */ 140 char* skinDirPath; /* skin directory */ 141 142 /* image files */ 143 char* imagePath [ AVD_IMAGE_MAX ]; 144 char imageState[ AVD_IMAGE_MAX ]; 145 }; 146 147 148 void 149 avdInfo_free( AvdInfo* i ) 150 { 151 if (i) { 152 int nn; 153 154 for (nn = 0; nn < AVD_IMAGE_MAX; nn++) 155 AFREE(i->imagePath[nn]); 156 157 AFREE(i->skinName); 158 AFREE(i->skinDirPath); 159 160 for (nn = 0; nn < i->numSearchPaths; nn++) 161 AFREE(i->searchPaths[nn]); 162 163 i->numSearchPaths = 0; 164 165 if (i->configIni) { 166 iniFile_free(i->configIni); 167 i->configIni = NULL; 168 } 169 170 if (i->hardwareIni) { 171 iniFile_free(i->hardwareIni); 172 i->hardwareIni = NULL; 173 } 174 175 if (i->rootIni) { 176 iniFile_free(i->rootIni); 177 i->rootIni = NULL; 178 } 179 180 AFREE(i->contentPath); 181 AFREE(i->sdkRootPath); 182 183 if (i->inAndroidBuild) { 184 AFREE(i->androidOut); 185 AFREE(i->androidBuildRoot); 186 } 187 188 AFREE(i->deviceName); 189 AFREE(i); 190 } 191 } 192 193 /* list of default file names for each supported image file type */ 194 static const char* const _imageFileNames[ AVD_IMAGE_MAX ] = { 195 #define _AVD_IMG(x,y,z) y, 196 AVD_IMAGE_LIST 197 #undef _AVD_IMG 198 }; 199 200 /* list of short text description for each supported image file type */ 201 static const char* const _imageFileText[ AVD_IMAGE_MAX ] = { 202 #define _AVD_IMG(x,y,z) z, 203 AVD_IMAGE_LIST 204 #undef _AVD_IMG 205 }; 206 207 /*************************************************************** 208 *************************************************************** 209 ***** 210 ***** NORMAL VIRTUAL DEVICE SUPPORT 211 ***** 212 *****/ 213 214 /* compute path to the root SDK directory 215 * assume we are in $SDKROOT/tools/emulator[.exe] 216 */ 217 static int 218 _getSdkRoot( AvdInfo* i ) 219 { 220 const char* env; 221 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 222 223 #define SDK_ROOT_ENV "ANDROID_SDK_ROOT" 224 225 env = getenv(SDK_ROOT_ENV); 226 if (env != NULL && env[0] != 0) { 227 if (path_exists(env)) { 228 D("found " SDK_ROOT_ENV ": %s", env); 229 i->sdkRootPath = ASTRDUP(env); 230 i->sdkRootPathFromEnv = 1; 231 return 0; 232 } 233 D(SDK_ROOT_ENV " points to unknown directory: %s", env); 234 } 235 236 (void) bufprint_app_dir(temp, end); 237 238 i->sdkRootPath = path_parent(temp, 1); 239 if (i->sdkRootPath == NULL) { 240 derror("can't find root of SDK directory"); 241 return -1; 242 } 243 D("found SDK root at %s", i->sdkRootPath); 244 return 0; 245 } 246 247 static void 248 _getSearchPaths( AvdInfo* i ) 249 { 250 char temp[PATH_MAX], *p = temp, *end= p+sizeof temp; 251 int nn, count = 0; 252 253 254 255 for (nn = 0; nn < MAX_SEARCH_PATHS; nn++) { 256 char* path; 257 258 p = bufprint(temp, end, "%s%d", SEARCH_PREFIX, nn+1 ); 259 if (p >= end) 260 continue; 261 262 path = iniFile_getString( i->configIni, temp ); 263 if (path != NULL) { 264 DD(" found image search path: %s", path); 265 if (!path_is_absolute(path)) { 266 p = bufprint(temp, end, "%s/%s", i->sdkRootPath, path); 267 AFREE(path); 268 path = ASTRDUP(temp); 269 } 270 i->searchPaths[count++] = path; 271 } 272 } 273 274 i->numSearchPaths = count; 275 if (count == 0) { 276 derror("no search paths found in this AVD's configuration.\n" 277 "Weird, the AVD's config.ini file is malformed. Try re-creating it.\n"); 278 exit(2); 279 } 280 else 281 DD("found a total of %d search paths for this AVD", count); 282 } 283 284 static int 285 _checkAvdName( const char* name ) 286 { 287 int len = strlen(name); 288 int len2 = strspn(name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 289 "abcdefghijklmnopqrstuvwxyz" 290 "0123456789_.-"); 291 return (len == len2); 292 } 293 294 /* parse the root config .ini file. it is located in 295 * ~/.android/avd/<name>.ini or Windows equivalent 296 */ 297 static int 298 _getRootIni( AvdInfo* i ) 299 { 300 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 301 302 p = bufprint_config_path(temp, end); 303 p = bufprint(p, end, "/" ANDROID_AVD_DIR "/%s.ini", i->deviceName); 304 if (p >= end) { 305 derror("device name too long"); 306 return -1; 307 } 308 309 i->rootIni = iniFile_newFromFile(temp); 310 if (i->rootIni == NULL) { 311 derror("unknown virtual device name: '%s'", i->deviceName); 312 return -1; 313 } 314 D("root virtual device file at %s", temp); 315 return 0; 316 } 317 318 /* the .ini variable name that points to the content directory 319 * in a root AVD ini file. This is required */ 320 # define ROOT_PATH_KEY "path" 321 322 static int 323 _getContentPath( AvdInfo* i ) 324 { 325 i->contentPath = iniFile_getString(i->rootIni, ROOT_PATH_KEY); 326 327 if (i->contentPath == NULL) { 328 derror("bad config: %s", 329 "virtual device file lacks a "ROOT_PATH_KEY" entry"); 330 return -1; 331 } 332 D("virtual device content at %s", i->contentPath); 333 return 0; 334 } 335 336 /* find and parse the config.ini file from the content directory */ 337 static int 338 _getConfigIni(AvdInfo* i) 339 { 340 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 341 342 p = bufprint(p, end, "%s/config.ini", i->contentPath); 343 if (p >= end) { 344 derror("can't access virtual device content directory"); 345 return -1; 346 } 347 348 #if 1 /* XXX: TODO: remove this in the future */ 349 /* for now, allow a non-existing config.ini */ 350 if (!path_exists(temp)) { 351 D("virtual device has no config file - no problem"); 352 return 0; 353 } 354 #endif 355 356 i->configIni = iniFile_newFromFile(temp); 357 if (i->configIni == NULL) { 358 derror("bad config: %s", 359 "virtual device directory lacks config.ini"); 360 return -1; 361 } 362 D("virtual device config file: %s", temp); 363 return 0; 364 } 365 366 /*************************************************************** 367 *************************************************************** 368 ***** 369 ***** KERNEL/DISK IMAGE LOADER 370 ***** 371 *****/ 372 373 /* a structure used to handle the loading of 374 * kernel/disk images. 375 */ 376 typedef struct { 377 AvdInfo* info; 378 AvdInfoParams* params; 379 AvdImageType id; 380 const char* imageFile; 381 const char* imageText; 382 char** pPath; 383 char* pState; 384 char temp[PATH_MAX]; 385 } ImageLoader; 386 387 static void 388 imageLoader_init( ImageLoader* l, AvdInfo* info, AvdInfoParams* params ) 389 { 390 memset(l, 0, sizeof(*l)); 391 l->info = info; 392 l->params = params; 393 } 394 395 /* set the type of the image to load */ 396 static void 397 imageLoader_set( ImageLoader* l, AvdImageType id ) 398 { 399 l->id = id; 400 l->imageFile = _imageFileNames[id]; 401 l->imageText = _imageFileText[id]; 402 l->pPath = &l->info->imagePath[id]; 403 l->pState = &l->info->imageState[id]; 404 405 l->pState[0] = IMAGE_STATE_READONLY; 406 } 407 408 /* change the image path */ 409 static char* 410 imageLoader_setPath( ImageLoader* l, const char* path ) 411 { 412 path = path ? ASTRDUP(path) : NULL; 413 414 AFREE(l->pPath[0]); 415 l->pPath[0] = (char*) path; 416 417 return (char*) path; 418 } 419 420 static char* 421 imageLoader_extractPath( ImageLoader* l ) 422 { 423 char* result = l->pPath[0]; 424 l->pPath[0] = NULL; 425 return result; 426 } 427 428 /* flags used when loading images */ 429 enum { 430 IMAGE_REQUIRED = (1<<0), /* image is required */ 431 IMAGE_SEARCH_SDK = (1<<1), /* search image in SDK */ 432 IMAGE_EMPTY_IF_MISSING = (1<<2), /* create empty file if missing */ 433 IMAGE_DONT_LOCK = (1<<4), /* don't try to lock image */ 434 IMAGE_IGNORE_IF_LOCKED = (1<<5), /* ignore file if it's locked */ 435 }; 436 437 #define IMAGE_OPTIONAL 0 438 439 /* find an image from the SDK search directories. 440 * returns the full path or NULL if the file could not be found. 441 * 442 * note: this stores the result in the image's path as well 443 */ 444 static char* 445 imageLoader_lookupSdk( ImageLoader* l ) 446 { 447 AvdInfo* i = l->info; 448 const char* image = l->imageFile; 449 char* temp = l->temp, *p = temp, *end = p + sizeof(l->temp); 450 451 do { 452 /* try the search paths */ 453 int nn; 454 455 for (nn = 0; nn < i->numSearchPaths; nn++) { 456 const char* searchDir = i->searchPaths[nn]; 457 458 p = bufprint(temp, end, "%s/%s", searchDir, image); 459 if (p < end && path_exists(temp)) { 460 DD("found %s in search dir: %s", image, searchDir); 461 goto FOUND; 462 } 463 DD(" no %s in search dir: %s", image, searchDir); 464 } 465 466 return NULL; 467 468 } while (0); 469 470 FOUND: 471 l->pState[0] = IMAGE_STATE_READONLY; 472 473 return imageLoader_setPath(l, temp); 474 } 475 476 /* search for a file in the content directory. 477 * returns NULL if the file cannot be found. 478 * 479 * note that this formats l->temp with the file's path 480 * allowing you to retrieve it if the function returns NULL 481 */ 482 static char* 483 imageLoader_lookupContent( ImageLoader* l ) 484 { 485 AvdInfo* i = l->info; 486 char* temp = l->temp, *p = temp, *end = p + sizeof(l->temp); 487 488 p = bufprint(temp, end, "%s/%s", i->contentPath, l->imageFile); 489 if (p >= end) { 490 derror("content directory path too long"); 491 exit(2); 492 } 493 if (!path_exists(temp)) { 494 DD(" no %s in content directory", l->imageFile); 495 return NULL; 496 } 497 DD("found %s in content directory", l->imageFile); 498 499 /* assume content image files must be locked */ 500 l->pState[0] = IMAGE_STATE_MUSTLOCK; 501 502 return imageLoader_setPath(l, temp); 503 } 504 505 /* lock a file image depending on its state and user flags 506 * note that this clears l->pPath[0] if the lock could not 507 * be acquired and that IMAGE_IGNORE_IF_LOCKED is used. 508 */ 509 static void 510 imageLoader_lock( ImageLoader* l, unsigned flags ) 511 { 512 const char* path = l->pPath[0]; 513 514 if (flags & IMAGE_DONT_LOCK) 515 return; 516 517 if (l->pState[0] != IMAGE_STATE_MUSTLOCK) 518 return; 519 520 D(" locking %s image at %s", l->imageText, path); 521 522 if (filelock_create(path) != NULL) { 523 /* succesful lock */ 524 l->pState[0] = IMAGE_STATE_LOCKED; 525 return; 526 } 527 528 if (flags & IMAGE_IGNORE_IF_LOCKED) { 529 dwarning("ignoring locked %s image at %s", l->imageText, path); 530 imageLoader_setPath(l, NULL); 531 return; 532 } 533 534 derror("the %s image is used by another emulator. aborting", 535 l->imageText); 536 exit(2); 537 } 538 539 /* make a file image empty, this may require locking */ 540 static void 541 imageLoader_empty( ImageLoader* l, unsigned flags ) 542 { 543 const char* path; 544 545 imageLoader_lock(l, flags); 546 547 path = l->pPath[0]; 548 if (path == NULL) /* failed to lock, caller will handle it */ 549 return; 550 551 if (path_empty_file(path) < 0) { 552 derror("could not create %s image at %s: %s", 553 l->imageText, path, strerror(errno)); 554 exit(2); 555 } 556 l->pState[0] = IMAGE_STATE_LOCKED_EMPTY; 557 } 558 559 560 /* copy image file from a given source 561 * assumes locking is needed. 562 */ 563 static void 564 imageLoader_copyFrom( ImageLoader* l, const char* srcPath ) 565 { 566 const char* dstPath = NULL; 567 568 /* find destination file */ 569 if (l->params) { 570 dstPath = l->params->forcePaths[l->id]; 571 } 572 if (!dstPath) { 573 imageLoader_lookupContent(l); 574 dstPath = l->temp; 575 } 576 577 /* lock destination */ 578 imageLoader_setPath(l, dstPath); 579 l->pState[0] = IMAGE_STATE_MUSTLOCK; 580 imageLoader_lock(l, 0); 581 582 /* make the copy */ 583 if (path_copy_file(dstPath, srcPath) < 0) { 584 derror("can't initialize %s image from SDK: %s: %s", 585 l->imageText, dstPath, strerror(errno)); 586 exit(2); 587 } 588 } 589 590 /* this will load and eventually lock and image file, depending 591 * on the flags being used. on exit, this function udpates 592 * l->pState[0] and l->pPath[0] 593 * 594 * returns the path to the file. Note that it returns NULL 595 * only if the file was optional and could not be found. 596 * 597 * if the file is required and missing, the function aborts 598 * the program. 599 */ 600 static char* 601 imageLoader_load( ImageLoader* l, 602 unsigned flags ) 603 { 604 const char* path = NULL; 605 606 /* set image state */ 607 l->pState[0] = (flags & IMAGE_DONT_LOCK) == 0 608 ? IMAGE_STATE_MUSTLOCK 609 : IMAGE_STATE_READONLY; 610 611 /* check user-provided path */ 612 path = l->params->forcePaths[l->id]; 613 if (path != NULL) { 614 imageLoader_setPath(l, path); 615 if (path_exists(path)) { 616 DD("found user-provided %s image: %s", l->imageText, l->imageFile); 617 goto EXIT; 618 } 619 D("user-provided %s image does not exist: %s", 620 l->imageText, path); 621 622 /* if the file is required, abort */ 623 if (flags & IMAGE_REQUIRED) { 624 derror("user-provided %s image at %s doesn't exist", 625 l->imageText, path); 626 exit(2); 627 } 628 } 629 else { 630 const char* contentFile; 631 632 /* second, look in the content directory */ 633 path = imageLoader_lookupContent(l); 634 if (path) goto EXIT; 635 636 contentFile = ASTRDUP(l->temp); 637 638 /* it's not there */ 639 if (flags & IMAGE_SEARCH_SDK) { 640 /* third, look in the SDK directory */ 641 path = imageLoader_lookupSdk(l); 642 if (path) { 643 AFREE((char*)contentFile); 644 goto EXIT; 645 } 646 } 647 DD("found no %s image (%s)", l->imageText, l->imageFile); 648 649 /* if the file is required, abort */ 650 if (flags & IMAGE_REQUIRED) { 651 AvdInfo* i = l->info; 652 653 derror("could not find required %s image (%s).", 654 l->imageText, l->imageFile); 655 656 if (i->inAndroidBuild) { 657 dprint( "Did you build everything ?" ); 658 } else if (!i->sdkRootPathFromEnv) { 659 dprint( "Maybe defining %s to point to a valid SDK " 660 "installation path might help ?", SDK_ROOT_ENV ); 661 } else { 662 dprint( "Your %s is probably wrong: %s", SDK_ROOT_ENV, 663 i->sdkRootPath ); 664 } 665 exit(2); 666 } 667 668 path = imageLoader_setPath(l, contentFile); 669 AFREE((char*)contentFile); 670 } 671 672 /* otherwise, do we need to create it ? */ 673 if (flags & IMAGE_EMPTY_IF_MISSING) { 674 imageLoader_empty(l, flags); 675 return l->pPath[0]; 676 } 677 return NULL; 678 679 EXIT: 680 imageLoader_lock(l, flags); 681 return l->pPath[0]; 682 } 683 684 685 686 /* find the correct path of all image files we're going to need 687 * and lock the files that need it. 688 */ 689 static int 690 _getImagePaths(AvdInfo* i, AvdInfoParams* params ) 691 { 692 int wipeData = (params->flags & AVDINFO_WIPE_DATA) != 0; 693 int wipeCache = (params->flags & AVDINFO_WIPE_CACHE) != 0; 694 int noCache = (params->flags & AVDINFO_NO_CACHE) != 0; 695 int noSdCard = (params->flags & AVDINFO_NO_SDCARD) != 0; 696 697 ImageLoader l[1]; 698 699 imageLoader_init(l, i, params); 700 701 /* pick up the kernel and ramdisk image files - these don't 702 * need a specific handling. 703 */ 704 imageLoader_set ( l, AVD_IMAGE_KERNEL ); 705 imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK ); 706 707 imageLoader_set ( l, AVD_IMAGE_RAMDISK ); 708 imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK ); 709 710 /* the system image 711 * 712 * if there is one in the content directory just lock 713 * and use it. 714 */ 715 imageLoader_set ( l, AVD_IMAGE_INITSYSTEM ); 716 imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK ); 717 718 /* the data partition - this one is special because if it 719 * is missing, we need to copy the initial image file into it. 720 * 721 * first, try to see if it is in the content directory 722 * (or the user-provided path) 723 */ 724 imageLoader_set( l, AVD_IMAGE_USERDATA ); 725 if ( !imageLoader_load( l, IMAGE_OPTIONAL | 726 IMAGE_EMPTY_IF_MISSING | 727 IMAGE_DONT_LOCK ) ) 728 { 729 /* it's not, we're going to initialize it. simply 730 * forcing a data wipe should be enough */ 731 D("initializing new data partition image: %s", l->pPath[0]); 732 wipeData = 1; 733 } 734 735 if (wipeData) { 736 /* find SDK source file */ 737 const char* srcPath; 738 739 imageLoader_set( l, AVD_IMAGE_INITDATA ); 740 if (imageLoader_lookupSdk(l) == NULL) { 741 derror("can't locate initial %s image in SDK", 742 l->imageText); 743 exit(2); 744 } 745 srcPath = imageLoader_extractPath(l); 746 747 imageLoader_set( l, AVD_IMAGE_USERDATA ); 748 imageLoader_copyFrom( l, srcPath ); 749 AFREE((char*) srcPath); 750 } 751 else 752 { 753 /* lock the data partition image */ 754 l->pState[0] = IMAGE_STATE_MUSTLOCK; 755 imageLoader_lock( l, 0 ); 756 } 757 758 /* the cache partition: unless the user doesn't want one, 759 * we're going to create it in the content directory 760 */ 761 if (!noCache) { 762 imageLoader_set (l, AVD_IMAGE_CACHE); 763 imageLoader_load(l, IMAGE_OPTIONAL | 764 IMAGE_EMPTY_IF_MISSING ); 765 766 if (wipeCache) { 767 if (path_empty_file(l->pPath[0]) < 0) { 768 derror("cannot wipe %s image at %s: %s", 769 l->imageText, l->pPath[0], 770 strerror(errno)); 771 exit(2); 772 } 773 } 774 } 775 776 /* the SD Card image. unless the user doesn't want to, we're 777 * going to mount it if available. Note that if the image is 778 * already used, we must ignore it. 779 */ 780 if (!noSdCard) { 781 imageLoader_set (l, AVD_IMAGE_SDCARD); 782 imageLoader_load(l, IMAGE_OPTIONAL | 783 IMAGE_IGNORE_IF_LOCKED); 784 785 /* if the file was not found, ignore it */ 786 if (l->pPath[0] && !path_exists(l->pPath[0])) 787 { 788 D("ignoring non-existing %s at %s: %s", 789 l->imageText, l->pPath[0], strerror(errno)); 790 791 /* if the user provided the SD Card path by hand, 792 * warn him. */ 793 if (params->forcePaths[AVD_IMAGE_SDCARD] != NULL) 794 dwarning("ignoring non-existing SD Card image"); 795 796 imageLoader_setPath(l, NULL); 797 } 798 } 799 800 return 0; 801 } 802 803 /* check that a given directory contains a valid skin. 804 * returns 1 on success, 0 on failure. 805 */ 806 static int 807 _checkSkinPath( const char* skinPath ) 808 { 809 char temp[MAX_PATH], *p=temp, *end=p+sizeof(temp); 810 811 /* for now, if it has a 'layout' file, it is a valid skin path */ 812 p = bufprint(temp, end, "%s/layout", skinPath); 813 if (p >= end || !path_exists(temp)) 814 return 0; 815 816 return 1; 817 } 818 819 /* check that there is a skin named 'skinName' listed from 'skinDirRoot' 820 * this returns 1 on success, 0 on failure 821 * on success, the 'temp' buffer will get the path containing the real 822 * skin directory (after alias expansion), including the skin name. 823 */ 824 static int 825 _checkSkinDir( char* temp, 826 char* end, 827 const char* skinDirRoot, 828 const char* skinName ) 829 { 830 DirScanner* scanner; 831 char *p; 832 int result; 833 834 p = bufprint(temp, end, "%s/skins/%s", 835 skinDirRoot, skinName); 836 837 if (p >= end || !path_exists(temp)) { 838 DD(" ignore bad skin directory %s", temp); 839 return 0; 840 } 841 842 /* first, is this a normal skin directory ? */ 843 if (_checkSkinPath(temp)) { 844 /* yes */ 845 DD(" found skin directory: %s", temp); 846 return 1; 847 } 848 849 /* second, is it an alias to another skin ? */ 850 *p = 0; 851 result = 0; 852 scanner = dirScanner_new(temp); 853 if (scanner != NULL) { 854 for (;;) { 855 const char* file = dirScanner_next(scanner); 856 857 if (file == NULL) 858 break; 859 860 if (strncmp(file, "alias-", 6) || file[6] == 0) 861 continue; 862 863 p = bufprint(temp, end, "%s/skins/%s", 864 skinDirRoot, file+6); 865 866 if (p < end && _checkSkinPath(temp)) { 867 /* yes, it's an alias */ 868 DD(" skin alias '%s' points to skin directory: %s", 869 file+6, temp); 870 result = 1; 871 break; 872 } 873 } 874 dirScanner_free(scanner); 875 } 876 return result; 877 } 878 879 /* try to see if the skin name leads to a magic skin or skin path directly 880 * returns 1 on success, 0 on error. 881 * on success, this sets up 'skinDirPath' and 'skinName' in the AvdInfo. 882 */ 883 static int 884 _getSkinPathFromName( AvdInfo* i, const char* skinName ) 885 { 886 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 887 888 /* if the skin name has the format 'NNNNxNNN' where 889 * NNN is a decimal value, then this is a 'magic' skin 890 * name that doesn't require a skin directory 891 */ 892 if (isdigit(skinName[0])) { 893 int width, height; 894 if (sscanf(skinName, "%dx%d", &width, &height) == 2) { 895 D("'magic' skin format detected: %s", skinName); 896 i->skinName = ASTRDUP(skinName); 897 i->skinDirPath = NULL; 898 return 1; 899 } 900 } 901 902 /* is the skin name a direct path to the skin directory ? */ 903 if (_checkSkinPath(skinName)) { 904 goto FOUND_IT; 905 } 906 907 /* is the skin name a relative path from the SDK root ? */ 908 p = bufprint(temp, end, "%s/%s", i->sdkRootPath, skinName); 909 if (p < end && _checkSkinPath(temp)) { 910 skinName = temp; 911 goto FOUND_IT; 912 } 913 914 /* nope */ 915 return 0; 916 917 FOUND_IT: 918 if (path_split(skinName, &i->skinDirPath, &i->skinName) < 0) { 919 derror("malformed skin name: %s", skinName); 920 exit(2); 921 } 922 D("found skin '%s' in directory: %s", i->skinName, i->skinDirPath); 923 return 1; 924 } 925 926 /* return 0 on success, -1 on error */ 927 static int 928 _getSkin( AvdInfo* i, AvdInfoParams* params ) 929 { 930 char* skinName; 931 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 932 char explicitSkin = 1; 933 934 /* this function is used to compute the 'skinName' and 'skinDirPath' 935 * fields of the AvdInfo. 936 */ 937 938 /* processing here is a bit tricky, so here's how it happens 939 * 940 * - command-line option '-skin <name>' can be used to specify the 941 * name of a skin, to override the AVD settings. 942 * 943 * - skins are searched from <dir>/../skins for each <dir> in the 944 * images search list, unless a '-skindir <path>' option has been 945 * provided on the command-line 946 * 947 * - otherwise, the config.ini can also contain a SKIN_PATH key that 948 * shall give the full path to the skin directory, either relative 949 * to the SDK root, or an absolute path. 950 * 951 * - skin names like '320x480' corresponds to "magic skins" that 952 * simply display a framebuffer, without any ornaments of the 953 * corresponding size. They do not correspond to any real skin 954 * directory / files and are handled later. But they must be 955 * recognized here and report a NULL skindir. 956 */ 957 if (params->skinName) { 958 skinName = ASTRDUP(params->skinName); 959 } else { 960 skinName = iniFile_getString( i->configIni, SKIN_PATH ); 961 explicitSkin = 0; 962 } 963 964 /* first, check that the skin name is not magic or a direct 965 * directory path 966 */ 967 if (skinName != NULL && _getSkinPathFromName(i, skinName)) { 968 AFREE(skinName); 969 return 0; 970 } 971 972 /* if not, the default skinName is "HVGA" */ 973 if (skinName == NULL) { 974 skinName = ASTRDUP(SKIN_DEFAULT); 975 explicitSkin = 0; 976 } 977 978 i->skinName = skinName; 979 980 /* now try to find the skin directory for that name - 981 * first try the content directory */ 982 do { 983 /* if there is a single 'skin' directory in 984 * the content directory, assume that's what the 985 * user wants, unless an explicit name was given 986 */ 987 if (!explicitSkin) { 988 p = bufprint(temp, end, "%s/skin", i->contentPath); 989 if (p < end && _checkSkinPath(temp)) { 990 D("using skin content from %s", temp); 991 AFREE(i->skinName); 992 i->skinName = ASTRDUP("skin"); 993 i->skinDirPath = ASTRDUP(i->contentPath); 994 return 0; 995 } 996 } 997 998 /* look in content directory */ 999 if (_checkSkinDir(temp, end, i->contentPath, skinName)) 1000 break; 1001 1002 /* look in the search paths. For each <dir> in the list, 1003 * look the skins in <dir>/.. */ 1004 { 1005 int nn; 1006 for (nn = 0; nn < i->numSearchPaths; nn++) { 1007 char* parentDir = path_parent(i->searchPaths[nn], 1); 1008 int ret; 1009 if (parentDir == NULL) 1010 continue; 1011 ret=_checkSkinDir(temp, end, parentDir, skinName); 1012 AFREE(parentDir); 1013 if (ret) 1014 break; 1015 } 1016 if (nn < i->numSearchPaths) 1017 break; 1018 } 1019 1020 /* didn't find it */ 1021 if (explicitSkin) { 1022 derror("could not find directory for skin '%s'," 1023 " please use a different name", skinName); 1024 exit(2); 1025 } else { 1026 dwarning("no skin directory matched '%s', so reverted to default", 1027 skinName); 1028 AFREE(i->skinName); 1029 params->skinName = SKIN_DEFAULT; 1030 return _getSkin(i, params); 1031 } 1032 1033 return -1; 1034 1035 } while (0); 1036 1037 /* separate skin name from parent directory. the skin name 1038 * returned in 'temp' might be different from the original 1039 * one due to alias expansion so strip it. 1040 */ 1041 AFREE(i->skinName); 1042 1043 if (path_split(temp, &i->skinDirPath, &i->skinName) < 0) { 1044 derror("weird skin path: %s", temp); 1045 return -1; 1046 } 1047 DD("found skin '%s' in directory: %s", i->skinName, i->skinDirPath); 1048 return 0; 1049 } 1050 1051 /* If the user didn't explicitely provide an SD Card path, 1052 * check the SDCARD_PATH key in config.ini and use that if 1053 * available. 1054 */ 1055 static void 1056 _getSDCardPath( AvdInfo* i, AvdInfoParams* params ) 1057 { 1058 const char* path; 1059 1060 if (params->forcePaths[AVD_IMAGE_SDCARD] != NULL) 1061 return; 1062 1063 path = iniFile_getString(i->configIni, SDCARD_PATH); 1064 if (path == NULL) 1065 return; 1066 1067 params->forcePaths[AVD_IMAGE_SDCARD] = path; 1068 } 1069 1070 AvdInfo* 1071 avdInfo_new( const char* name, AvdInfoParams* params ) 1072 { 1073 AvdInfo* i; 1074 1075 if (name == NULL) 1076 return NULL; 1077 1078 if (!_checkAvdName(name)) { 1079 derror("virtual device name contains invalid characters"); 1080 exit(1); 1081 } 1082 1083 ANEW0(i); 1084 i->deviceName = ASTRDUP(name); 1085 1086 if ( _getSdkRoot(i) < 0 || 1087 _getRootIni(i) < 0 || 1088 _getContentPath(i) < 0 || 1089 _getConfigIni(i) < 0 ) 1090 goto FAIL; 1091 1092 /* look for image search paths. handle post 1.1/pre cupcake 1093 * obsolete SDKs. 1094 */ 1095 _getSearchPaths(i); 1096 _getSDCardPath(i, params); 1097 1098 /* don't need this anymore */ 1099 iniFile_free(i->rootIni); 1100 i->rootIni = NULL; 1101 1102 if ( _getImagePaths(i, params) < 0 || 1103 _getSkin (i, params) < 0 ) 1104 goto FAIL; 1105 1106 return i; 1107 1108 FAIL: 1109 avdInfo_free(i); 1110 return NULL; 1111 } 1112 1113 /*************************************************************** 1114 *************************************************************** 1115 ***** 1116 ***** ANDROID BUILD SUPPORT 1117 ***** 1118 ***** The code below corresponds to the case where we're 1119 ***** starting the emulator inside the Android build 1120 ***** system. The main differences are that: 1121 ***** 1122 ***** - the $ANDROID_PRODUCT_OUT directory is used as the 1123 ***** content file. 1124 ***** 1125 ***** - built images must not be modified by the emulator, 1126 ***** so system.img must be copied to a temporary file 1127 ***** and userdata.img must be copied to userdata-qemu.img 1128 ***** if the latter doesn't exist. 1129 ***** 1130 ***** - the kernel and default skin directory are taken from 1131 ***** prebuilt 1132 ***** 1133 ***** - there is no root .ini file, or any config.ini in 1134 ***** the content directory, no SDK images search path. 1135 *****/ 1136 1137 /* used to fake a config.ini located in the content directory */ 1138 static int 1139 _getBuildConfigIni( AvdInfo* i ) 1140 { 1141 /* a blank file is ok at the moment */ 1142 i->configIni = iniFile_newFromMemory( "", 0 ); 1143 return 0; 1144 } 1145 1146 static int 1147 _getBuildImagePaths( AvdInfo* i, AvdInfoParams* params ) 1148 { 1149 int wipeData = (params->flags & AVDINFO_WIPE_DATA) != 0; 1150 int noCache = (params->flags & AVDINFO_NO_CACHE) != 0; 1151 int noSdCard = (params->flags & AVDINFO_NO_SDCARD) != 0; 1152 1153 char temp[PATH_MAX], *p=temp, *end=p+sizeof temp; 1154 char* srcData; 1155 ImageLoader l[1]; 1156 1157 imageLoader_init(l, i, params); 1158 1159 /** load the kernel image 1160 **/ 1161 1162 /* if it is not in the out directory, get it from prebuilt 1163 */ 1164 imageLoader_set ( l, AVD_IMAGE_KERNEL ); 1165 1166 if ( !imageLoader_load( l, IMAGE_OPTIONAL | 1167 IMAGE_DONT_LOCK ) ) 1168 { 1169 #define PREBUILT_KERNEL_PATH "prebuilt/android-arm/kernel/kernel-qemu" 1170 p = bufprint(temp, end, "%s/%s", i->androidBuildRoot, 1171 PREBUILT_KERNEL_PATH); 1172 if (p >= end || !path_exists(temp)) { 1173 derror("bad workspace: cannot find prebuilt kernel in: %s", temp); 1174 exit(1); 1175 } 1176 imageLoader_setPath(l, temp); 1177 } 1178 1179 /** load the data partition. note that we use userdata-qemu.img 1180 ** since we don't want to modify userdata.img at all 1181 **/ 1182 imageLoader_set ( l, AVD_IMAGE_USERDATA ); 1183 imageLoader_load( l, IMAGE_OPTIONAL | IMAGE_DONT_LOCK ); 1184 1185 /* get the path of the source file, and check that it actually exists 1186 * if the user didn't provide an explicit data file 1187 */ 1188 srcData = imageLoader_extractPath(l); 1189 if (srcData == NULL && params->forcePaths[AVD_IMAGE_USERDATA] == NULL) { 1190 derror("There is no %s image in your build directory. Please make a full build", 1191 l->imageText, l->imageFile); 1192 exit(2); 1193 } 1194 1195 /* get the path of the target file */ 1196 l->imageFile = "userdata-qemu.img"; 1197 imageLoader_load( l, IMAGE_OPTIONAL | 1198 IMAGE_EMPTY_IF_MISSING | 1199 IMAGE_IGNORE_IF_LOCKED ); 1200 1201 /* force a data wipe if we just created the image */ 1202 if (l->pState[0] == IMAGE_STATE_LOCKED_EMPTY) 1203 wipeData = 1; 1204 1205 /* if the image was already locked, create a temp file 1206 * then force a data wipe. 1207 */ 1208 if (l->pPath[0] == NULL) { 1209 TempFile* temp = tempfile_create(); 1210 imageLoader_setPath(l, tempfile_path(temp)); 1211 dwarning( "Another emulator is running. user data changes will *NOT* be saved"); 1212 wipeData = 1; 1213 } 1214 1215 /* in the case of a data wipe, copy userdata.img into 1216 * the destination */ 1217 if (wipeData) { 1218 if (srcData == NULL || !path_exists(srcData)) { 1219 derror("There is no %s image in your build directory. Please make a full build", 1220 l->imageText, _imageFileNames[l->id]); 1221 exit(2); 1222 } 1223 if (path_copy_file( l->pPath[0], srcData ) < 0) { 1224 derror("could not initialize %s image from %s: %s", 1225 l->imageText, temp, strerror(errno)); 1226 exit(2); 1227 } 1228 } 1229 1230 AFREE(srcData); 1231 1232 /** load the ramdisk image 1233 **/ 1234 imageLoader_set ( l, AVD_IMAGE_RAMDISK ); 1235 imageLoader_load( l, IMAGE_REQUIRED | 1236 IMAGE_DONT_LOCK ); 1237 1238 /** load the system image. read-only. the caller must 1239 ** take care of checking the state 1240 **/ 1241 imageLoader_set ( l, AVD_IMAGE_INITSYSTEM ); 1242 imageLoader_load( l, IMAGE_REQUIRED | IMAGE_DONT_LOCK ); 1243 1244 /* force the system image to read-only status */ 1245 l->pState[0] = IMAGE_STATE_READONLY; 1246 1247 /** cache partition handling 1248 **/ 1249 if (!noCache) { 1250 imageLoader_set (l, AVD_IMAGE_CACHE); 1251 1252 /* if the user provided one cache image, lock & use it */ 1253 if ( params->forcePaths[l->id] != NULL ) { 1254 imageLoader_load(l, IMAGE_REQUIRED | 1255 IMAGE_IGNORE_IF_LOCKED); 1256 } 1257 } 1258 1259 /** SD Card image 1260 **/ 1261 if (!noSdCard) { 1262 imageLoader_set (l, AVD_IMAGE_SDCARD); 1263 imageLoader_load(l, IMAGE_OPTIONAL | IMAGE_IGNORE_IF_LOCKED); 1264 } 1265 1266 return 0; 1267 } 1268 1269 static int 1270 _getBuildSkin( AvdInfo* i, AvdInfoParams* params ) 1271 { 1272 /* the (current) default skin name for our build system */ 1273 const char* skinName = params->skinName; 1274 const char* skinDir = params->skinRootPath; 1275 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 1276 char* q; 1277 1278 if (!skinName) { 1279 /* the (current) default skin name for the build system */ 1280 skinName = SKIN_DEFAULT; 1281 D("selecting default skin name '%s'", skinName); 1282 } 1283 1284 if (!skinDir) { 1285 1286 #define PREBUILT_SKINS_DIR "sdk/emulator/skins" 1287 #define PRODUCT_SKIN_DIR "skin" 1288 1289 do { 1290 /* look for the product skin in $ANDROID_PRODUCT_OUT/skin if no skin name is defined */ 1291 if (!params->skinName) { 1292 /* look for <product_out>/skin first */ 1293 p = bufprint( temp, end, "%s/skin", 1294 i->androidOut ); 1295 if (path_exists(temp)) { 1296 p = bufprint( temp, end, "%s", 1297 i->androidOut ); 1298 skinName = PRODUCT_SKIN_DIR; 1299 D("selecting default product skin at '%s/%s'", temp, skinName); 1300 break; 1301 } 1302 } 1303 1304 /* next try in <sysdir>/../skins */ 1305 p = bufprint( temp, end, "%s/../skins", 1306 i->androidBuildRoot ); 1307 if (path_exists(temp)) 1308 break; 1309 1310 /* the (current) default skin directory */ 1311 p = bufprint( temp, end, "%s/%s", 1312 i->androidBuildRoot, PREBUILT_SKINS_DIR ); 1313 } while (0); 1314 1315 } else { 1316 p = bufprint( temp, end, "%s", skinDir ); 1317 } 1318 1319 i->skinName = ASTRDUP(skinName); 1320 1321 q = bufprint(p, end, "/%s/layout", skinName); 1322 if (q >= end || !path_exists(temp)) { 1323 DD("skin content directory does not exist: %s", temp); 1324 if (skinDir) 1325 dwarning("could not find valid skin '%s' in %s:\n", 1326 skinName, temp); 1327 return -1; 1328 } 1329 *p = 0; 1330 DD("found skin path: %s", temp); 1331 i->skinDirPath = ASTRDUP(temp); 1332 1333 return 0; 1334 } 1335 1336 /* Read a hardware.ini if it is located in the skin directory */ 1337 static int 1338 _getBuildHardwareIni( AvdInfo* i ) 1339 { 1340 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 1341 1342 if (i->skinDirPath == NULL || i->skinName == NULL) 1343 return 0; 1344 1345 p = bufprint(temp, end, "%s/%s/hardware.ini", i->skinDirPath, i->skinName); 1346 if (p >= end || !path_exists(temp)) { 1347 DD("no skin-specific hardware.ini in %s", i->skinDirPath); 1348 return 0; 1349 } 1350 1351 D("found skin-specific hardware.ini: %s", temp); 1352 i->hardwareIni = iniFile_newFromFile(temp); 1353 if (i->hardwareIni == NULL) 1354 return -1; 1355 1356 return 0; 1357 } 1358 1359 1360 AvdInfo* 1361 avdInfo_newForAndroidBuild( const char* androidBuildRoot, 1362 const char* androidOut, 1363 AvdInfoParams* params ) 1364 { 1365 AvdInfo* i; 1366 1367 ANEW0(i); 1368 1369 i->inAndroidBuild = 1; 1370 i->androidBuildRoot = ASTRDUP(androidBuildRoot); 1371 i->androidOut = ASTRDUP(androidOut); 1372 i->contentPath = ASTRDUP(androidOut); 1373 1374 /* TODO: find a way to provide better information from the build files */ 1375 i->deviceName = ASTRDUP("<build>"); 1376 1377 if (_getBuildConfigIni(i) < 0 || 1378 _getBuildImagePaths(i, params) < 0 ) 1379 goto FAIL; 1380 1381 /* we don't need to fail if there is no valid skin */ 1382 _getBuildSkin(i, params); 1383 _getBuildHardwareIni(i); 1384 1385 return i; 1386 1387 FAIL: 1388 avdInfo_free(i); 1389 return NULL; 1390 } 1391 1392 const char* 1393 avdInfo_getName( AvdInfo* i ) 1394 { 1395 return i ? i->deviceName : NULL; 1396 } 1397 1398 const char* 1399 avdInfo_getImageFile( AvdInfo* i, AvdImageType imageType ) 1400 { 1401 if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX) 1402 return NULL; 1403 1404 return i->imagePath[imageType]; 1405 } 1406 1407 uint64_t 1408 avdInfo_getImageFileSize( AvdInfo* i, AvdImageType imageType ) 1409 { 1410 const char* file = avdInfo_getImageFile(i, imageType); 1411 uint64_t size; 1412 1413 if (file == NULL) 1414 return 0ULL; 1415 1416 if (path_get_size(file, &size) < 0) 1417 return 0ULL; 1418 1419 return size; 1420 } 1421 1422 int 1423 avdInfo_isImageReadOnly( AvdInfo* i, AvdImageType imageType ) 1424 { 1425 if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX) 1426 return 1; 1427 1428 return (i->imageState[imageType] == IMAGE_STATE_READONLY); 1429 } 1430 1431 const char* 1432 avdInfo_getSkinName( AvdInfo* i ) 1433 { 1434 return i->skinName; 1435 } 1436 1437 const char* 1438 avdInfo_getSkinDir ( AvdInfo* i ) 1439 { 1440 return i->skinDirPath; 1441 } 1442 1443 int 1444 avdInfo_getHwConfig( AvdInfo* i, AndroidHwConfig* hw ) 1445 { 1446 IniFile* ini = i->configIni; 1447 int ret; 1448 1449 if (ini == NULL) 1450 ini = iniFile_newFromMemory("", 0); 1451 1452 ret = androidHwConfig_read(hw, ini); 1453 1454 if (ini != i->configIni) 1455 iniFile_free(ini); 1456 1457 if (ret == 0 && i->hardwareIni != NULL) { 1458 ret = androidHwConfig_read(hw, i->hardwareIni); 1459 } 1460 1461 /* special product-specific hardware configuration */ 1462 if (i->androidOut != NULL) 1463 { 1464 char* p = strrchr(i->androidOut, '/'); 1465 if (p != NULL && p[0] != 0) { 1466 if (p[1] == 's') { 1467 hw->hw_keyboard = 0; 1468 } 1469 } 1470 } 1471 1472 return ret; 1473 } 1474 1475 const char* 1476 avdInfo_getContentPath( AvdInfo* i ) 1477 { 1478 return i->contentPath; 1479 } 1480 1481 int 1482 avdInfo_inAndroidBuild( AvdInfo* i ) 1483 { 1484 return i->inAndroidBuild; 1485 } 1486 1487 char* 1488 avdInfo_getTracePath( AvdInfo* i, const char* traceName ) 1489 { 1490 char tmp[MAX_PATH], *p=tmp, *end=p + sizeof(tmp); 1491 1492 if (i == NULL || traceName == NULL || traceName[0] == 0) 1493 return NULL; 1494 1495 if (i->inAndroidBuild) { 1496 p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s", 1497 i->androidOut, traceName ); 1498 } else { 1499 p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s", 1500 i->contentPath, traceName ); 1501 } 1502 return ASTRDUP(tmp); 1503 } 1504