blob: 0682497d19dad5b81a8aeaeb1125f76cd5de285a [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2006-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
13#include <signal.h>
14#include <unistd.h>
15#include <string.h>
16#include <sys/time.h>
17#ifdef _WIN32
18#include <process.h>
19#endif
Vladimir Chtchetkine7fbf4972010-08-11 15:30:32 -070020
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080021#include "sockets.h"
22
23#include "android/android.h"
24#include "qemu-common.h"
25#include "sysemu.h"
26#include "console.h"
David 'Digit' Turner34f29742010-05-25 18:16:10 -070027#include "user-events.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080028
29#include <SDL.h>
30#include <SDL_syswm.h>
31
32#include "math.h"
33
34#include "android/charmap.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080035#include "android/utils/debug.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080036#include "android/config.h"
37#include "android/config/config.h"
38
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080039#include "android/user-config.h"
40#include "android/utils/bufprint.h"
David 'Digit' Turner26d41532011-03-01 15:03:07 +010041#include "android/utils/filelock.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080042#include "android/utils/path.h"
David 'Digit' Turner622f1532011-02-01 17:48:37 +010043#include "android/utils/tempfile.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080044
David 'Digit' Turnerf8456272011-02-02 12:34:14 +010045#include "android/main-common.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080046#include "android/help.h"
47#include "hw/goldfish_nand.h"
48
49#include "android/globals.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080050
Xavier Ducrohetfc8ed802011-02-09 18:04:23 -080051#include "android/qemulator.h"
David 'Digit' Turner055ae422010-07-27 11:34:16 -070052#include "android/display.h"
Vladimir Chtchetkine01193622010-05-11 13:07:22 -070053
Ot ten Thijeae835ac2010-10-18 13:37:37 +010054#include "android/snapshot.h"
55
David 'Digit' Turnere3fdd072011-02-02 14:43:23 +010056#include "android/framebuffer.h"
Vladimir Chtchetkine9a33e852010-11-08 16:52:04 -080057#include "iolooper.h"
David 'Digit' Turner055ae422010-07-27 11:34:16 -070058
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080059AndroidRotation android_framebuffer_rotation;
60
61#define STRINGIFY(x) _STRINGIFY(x)
62#define _STRINGIFY(x) #x
63
David 'Digit' Turnera383d022009-12-03 13:50:00 -080064#ifdef ANDROID_SDK_TOOLS_REVISION
65# define VERSION_STRING STRINGIFY(ANDROID_SDK_TOOLS_REVISION)".0"
66#else
67# define VERSION_STRING "standalone"
68#endif
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080069
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080070#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
71
72extern int control_console_start( int port ); /* in control.c */
73
74extern int qemu_milli_needed;
75
76/* the default device DPI if none is specified by the skin
77 */
78#define DEFAULT_DEVICE_DPI 165
79
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080080#ifdef CONFIG_TRACE
81extern void start_tracing(void);
82extern void stop_tracing(void);
83#endif
84
85unsigned long android_verbose;
86
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080087int qemu_main(int argc, char **argv);
88
89/* this function dumps the QEMU help */
90extern void help( void );
91extern void emulator_help( void );
92
93#define VERBOSE_OPT(str,var) { str, &var }
94
95#define _VERBOSE_TAG(x,y) { #x, VERBOSE_##x, y },
96static const struct { const char* name; int flag; const char* text; }
97verbose_options[] = {
98 VERBOSE_TAG_LIST
99 { 0, 0, 0 }
100};
101
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800102void emulator_help( void )
103{
104 STRALLOC_DEFINE(out);
105 android_help_main(out);
106 printf( "%.*s", out->n, out->s );
107 stralloc_reset(out);
108 exit(1);
109}
110
David 'Digit' Turner40841b22011-03-01 14:04:00 +0100111/* TODO: Put in shared source file */
112static char*
113_getFullFilePath( const char* rootPath, const char* fileName )
114{
115 if (path_is_absolute(fileName)) {
116 return ASTRDUP(fileName);
117 } else {
118 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
119
120 p = bufprint(temp, end, "%s/%s", rootPath, fileName);
121 if (p >= end) {
122 return NULL;
123 }
124 return ASTRDUP(temp);
125 }
126}
127
128static uint64_t
129_adjustPartitionSize( const char* description,
130 uint64_t imageBytes,
131 uint64_t defaultBytes,
132 int inAndroidBuild )
133{
134 char temp[64];
135 unsigned imageMB;
136 unsigned defaultMB;
137
138 if (imageBytes <= defaultBytes)
139 return defaultBytes;
140
141 imageMB = convertBytesToMB(imageBytes);
142 defaultMB = convertBytesToMB(defaultBytes);
143
144 if (imageMB > defaultMB) {
145 snprintf(temp, sizeof temp, "(%d MB > %d MB)", imageMB, defaultMB);
146 } else {
147 snprintf(temp, sizeof temp, "(%lld bytes > %lld bytes)", imageBytes, defaultBytes);
148 }
149
150 if (inAndroidBuild) {
151 dwarning("%s partition size adjusted to match image file %s\n", description, temp);
152 }
153
154 return convertMBToBytes(imageMB);
155}
156
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800157int main(int argc, char **argv)
158{
159 char tmp[MAX_PATH];
160 char* tmpend = tmp + sizeof(tmp);
161 char* args[128];
162 int n;
163 char* opt;
David 'Digit' Turner5e736932011-03-18 00:02:14 +0100164 /* The emulator always uses the first serial port for kernel messages
165 * and the second one for qemud. So start at the third if we need one
166 * for logcat or 'shell'
167 */
168 int serial = 2;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800169 int shell_serial = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800170
171 AndroidHwConfig* hw;
David 'Digit' Turnercd059b12009-08-28 19:36:27 +0200172 AvdInfo* avd;
David 'Digit' Turner74d7ace2011-02-02 13:21:03 +0100173 AConfig* skinConfig;
174 char* skinPath;
Vladimir Chtchetkine83ffd662011-02-11 12:40:59 -0800175 int inAndroidBuild;
David 'Digit' Turner40841b22011-03-01 14:04:00 +0100176 uint64_t defaultPartitionSize = convertMBToBytes(66);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800177
178 AndroidOptions opts[1];
Vladimir Chtchetkineb25bf2a2010-09-08 11:09:23 -0700179 /* net.shared_net_ip boot property value. */
180 char boot_prop_ip[64];
181 boot_prop_ip[0] = '\0';
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800182
183 args[0] = argv[0];
184
185 if ( android_parse_options( &argc, &argv, opts ) < 0 ) {
186 exit(1);
187 }
188
David 'Digit' Turnerbdb6f2d2011-02-23 15:57:25 +0100189#ifdef _WIN32
190 socket_init();
191#endif
192
193 handle_ui_options(opts);
194
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800195 while (argc-- > 1) {
196 opt = (++argv)[0];
197
198 if(!strcmp(opt, "-qemu")) {
199 argc--;
200 argv++;
201 break;
202 }
203
204 if (!strcmp(opt, "-help")) {
205 emulator_help();
206 }
207
208 if (!strncmp(opt, "-help-",6)) {
209 STRALLOC_DEFINE(out);
210 opt += 6;
211
212 if (!strcmp(opt, "all")) {
213 android_help_all(out);
214 }
215 else if (android_help_for_option(opt, out) == 0) {
216 /* ok */
217 }
218 else if (android_help_for_topic(opt, out) == 0) {
219 /* ok */
220 }
221 if (out->n > 0) {
222 printf("\n%.*s", out->n, out->s);
223 exit(0);
224 }
225
226 fprintf(stderr, "unknown option: -help-%s\n", opt);
227 fprintf(stderr, "please use -help for a list of valid topics\n");
228 exit(1);
229 }
230
231 if (opt[0] == '-') {
232 fprintf(stderr, "unknown option: %s\n", opt);
233 fprintf(stderr, "please use -help for a list of valid options\n");
234 exit(1);
235 }
236
237 fprintf(stderr, "invalid command-line parameter: %s.\n", opt);
238 fprintf(stderr, "Hint: use '@foo' to launch a virtual device named 'foo'.\n");
239 fprintf(stderr, "please use -help for more information\n");
240 exit(1);
241 }
242
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800243 if (opts->version) {
244 printf("Android emulator version %s\n"
David 'Digit' Turner5f64b872011-02-28 23:23:05 +0100245 "Copyright (C) 2006-2011 The Android Open Source Project and many others.\n"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800246 "This program is a derivative of the QEMU CPU emulator (www.qemu.org).\n\n",
247#if defined ANDROID_BUILD_ID
248 VERSION_STRING " (build_id " STRINGIFY(ANDROID_BUILD_ID) ")" );
249#else
250 VERSION_STRING);
251#endif
252 printf(" This software is licensed under the terms of the GNU General Public\n"
253 " License version 2, as published by the Free Software Foundation, and\n"
254 " may be copied, distributed, and modified under those terms.\n\n"
255 " This program is distributed in the hope that it will be useful,\n"
256 " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
257 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
258 " GNU General Public License for more details.\n\n");
259
260 exit(0);
261 }
262
David 'Digit' Turner5f64b872011-02-28 23:23:05 +0100263 if (opts->snapshot_list) {
264 snapshot_print_and_exit(opts->snapstorage);
265 }
266
David 'Digit' Turner25eb6552011-02-25 15:07:11 +0100267 sanitizeOptions(opts);
268
David 'Digit' Turnerbdb6f2d2011-02-23 15:57:25 +0100269 /* Initialization of UI started with -attach-core should work differently
270 * than initialization of UI that starts the core. In particular....
271 */
272
273 /* -charmap is incompatible with -attach-core, because particular
274 * charmap gets set up in the running core. */
David 'Digit' Turner74d7ace2011-02-02 13:21:03 +0100275 if (android_charmap_setup(opts->charmap)) {
276 exit(1);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800277 }
278
Vladimir Chtchetkine83ffd662011-02-11 12:40:59 -0800279 /* Parses options and builds an appropriate AVD. */
David 'Digit' Turner462564f2011-02-23 13:32:37 +0100280 avd = android_avdInfo = createAVD(opts, &inAndroidBuild);
David 'Digit' Turnercd059b12009-08-28 19:36:27 +0200281
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800282 /* get the skin from the virtual device configuration */
David 'Digit' Turner25eb6552011-02-25 15:07:11 +0100283 if (opts->skindir != NULL) {
284 if (opts->skin == NULL) {
285 /* NOTE: Normally handled by sanitizeOptions(), just be safe */
286 derror("The -skindir <path> option requires a -skin <name> option");
287 exit(2);
288 }
289 } else {
290 char* skinName;
291 char* skinDir;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800292
David 'Digit' Turner25eb6552011-02-25 15:07:11 +0100293 avdInfo_getSkinInfo(avd, &skinName, &skinDir);
294
295 if (opts->skin == NULL) {
296 opts->skin = skinName;
297 D("autoconfig: -skin %s", opts->skin);
298 } else {
299 AFREE(skinName);
300 }
301
302 opts->skindir = skinDir;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800303 D("autoconfig: -skindir %s", opts->skindir);
304 }
305
306 /* Read hardware configuration */
307 hw = android_hw;
David 'Digit' Turnerb64325d2011-03-22 16:07:01 +0100308 if (avdInfo_initHwConfig(avd, hw) < 0) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800309 derror("could not read hardware configuration ?");
310 exit(1);
311 }
312
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800313 if (opts->keyset) {
314 parse_keyset(opts->keyset, opts);
315 if (!android_keyset) {
316 fprintf(stderr,
317 "emulator: WARNING: could not find keyset file named '%s',"
318 " using defaults instead\n",
319 opts->keyset);
320 }
321 }
322 if (!android_keyset) {
323 parse_keyset("default", opts);
324 if (!android_keyset) {
325 android_keyset = skin_keyset_new_from_text( skin_keyset_get_default() );
326 if (!android_keyset) {
327 fprintf(stderr, "PANIC: default keyset file is corrupted !!\n" );
328 fprintf(stderr, "PANIC: please update the code in android/skin/keyset.c\n" );
329 exit(1);
330 }
331 if (!opts->keyset)
332 write_default_keyset();
333 }
334 }
335
Dries Harnie40beab42010-05-15 17:04:47 +0200336 if (opts->shared_net_id) {
337 char* end;
338 long shared_net_id = strtol(opts->shared_net_id, &end, 0);
339 if (end == NULL || *end || shared_net_id < 1 || shared_net_id > 255) {
340 fprintf(stderr, "option -shared-net-id must be an integer between 1 and 255\n");
341 exit(1);
342 }
Vladimir Chtchetkineb25bf2a2010-09-08 11:09:23 -0700343 snprintf(boot_prop_ip, sizeof(boot_prop_ip),
344 "net.shared_net_ip=10.1.2.%ld", shared_net_id);
Dries Harnie40beab42010-05-15 17:04:47 +0200345 }
346
347
David 'Digit' Turner755811e2011-02-07 13:38:25 +0100348 user_config_init();
David 'Digit' Turner2507cab2011-02-10 16:29:17 +0100349 parse_skin_files(opts->skindir, opts->skin, opts, hw,
David 'Digit' Turner74d7ace2011-02-02 13:21:03 +0100350 &skinConfig, &skinPath);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800351
David 'Digit' Turner092361e2011-03-01 13:14:18 +0100352 if (!opts->netspeed && skin_network_speed) {
353 D("skin network speed: '%s'", skin_network_speed);
354 if (strcmp(skin_network_speed, NETWORK_SPEED_DEFAULT) != 0) {
355 opts->netspeed = (char*)skin_network_speed;
356 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800357 }
David 'Digit' Turner092361e2011-03-01 13:14:18 +0100358 if (!opts->netdelay && skin_network_delay) {
359 D("skin network delay: '%s'", skin_network_delay);
360 if (strcmp(skin_network_delay, NETWORK_DELAY_DEFAULT) != 0) {
361 opts->netdelay = (char*)skin_network_delay;
362 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800363 }
364
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800365 if (opts->trace) {
David 'Digit' Turnercd059b12009-08-28 19:36:27 +0200366 char* tracePath = avdInfo_getTracePath(avd, opts->trace);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800367 int ret;
368
369 if (tracePath == NULL) {
370 derror( "bad -trace parameter" );
371 exit(1);
372 }
373 ret = path_mkdir_if_needed( tracePath, 0755 );
374 if (ret < 0) {
375 fprintf(stderr, "could not create directory '%s'\n", tmp);
376 exit(2);
377 }
378 opts->trace = tracePath;
379 }
380
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800381 n = 1;
382 /* generate arguments for the underlying qemu main() */
David 'Digit' Turner238b4b02009-09-20 13:10:19 -0700383 {
David 'Digit' Turner0b019492011-03-01 14:02:42 +0100384 char* kernelFile = opts->kernel;
385 int kernelFileLen;
David 'Digit' Turner238b4b02009-09-20 13:10:19 -0700386
David 'Digit' Turner0b019492011-03-01 14:02:42 +0100387 if (kernelFile == NULL) {
388 kernelFile = avdInfo_getKernelPath(avd);
389 if (kernelFile == NULL) {
390 derror( "This AVD's configuration is missing a kernel file!!" );
391 exit(2);
392 }
393 D("autoconfig: -kernel %s", kernelFile);
394 }
395 if (!path_exists(kernelFile)) {
396 derror( "Invalid or missing kernel image file: %s", kernelFile );
397 exit(2);
398 }
399
400 hw->kernel_path = kernelFile;
David 'Digit' Turner238b4b02009-09-20 13:10:19 -0700401
402 /* If the kernel image name ends in "-armv7", then change the cpu
403 * type automatically. This is a poor man's approach to configuration
404 * management, but should allow us to get past building ARMv7
405 * system images with dex preopt pass without introducing too many
406 * changes to the emulator sources.
407 *
408 * XXX:
409 * A 'proper' change would require adding some sort of hardware-property
410 * to each AVD config file, then automatically determine its value for
411 * full Android builds (depending on some environment variable), plus
412 * some build system changes. I prefer not to do that for now for reasons
413 * of simplicity.
414 */
David 'Digit' Turner0b019492011-03-01 14:02:42 +0100415 kernelFileLen = strlen(kernelFile);
David 'Digit' Turner238b4b02009-09-20 13:10:19 -0700416 if (kernelFileLen > 6 && !memcmp(kernelFile + kernelFileLen - 6, "-armv7", 6)) {
417 args[n++] = "-cpu";
418 args[n++] = "cortex-a8";
419 }
420 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800421
Vladimir Chtchetkineb25bf2a2010-09-08 11:09:23 -0700422 if (boot_prop_ip[0]) {
423 args[n++] = "-boot-property";
424 args[n++] = boot_prop_ip;
425 }
426
Vladimir Chtchetkine318f17a2010-08-27 09:09:45 -0700427 if (opts->tcpdump) {
428 args[n++] = "-tcpdump";
429 args[n++] = opts->tcpdump;
430 }
431
Vladimir Chtchetkinee1316862010-08-26 09:03:54 -0700432#ifdef CONFIG_NAND_LIMITS
433 if (opts->nand_limits) {
434 args[n++] = "-nand-limits";
435 args[n++] = opts->nand_limits;
436 }
437#endif
438
David 'Digit' Turner74d7ace2011-02-02 13:21:03 +0100439 if (opts->timezone) {
440 args[n++] = "-timezone";
441 args[n++] = opts->timezone;
442 }
443
Vladimir Chtchetkinee1316862010-08-26 09:03:54 -0700444 if (opts->netspeed) {
445 args[n++] = "-netspeed";
446 args[n++] = opts->netspeed;
447 }
448 if (opts->netdelay) {
449 args[n++] = "-netdelay";
450 args[n++] = opts->netdelay;
451 }
452 if (opts->netfast) {
453 args[n++] = "-netfast";
454 }
455
Vladimir Chtchetkineb2438402010-08-24 08:55:33 -0700456 if (opts->audio) {
457 args[n++] = "-audio";
458 args[n++] = opts->audio;
459 }
460
Vladimir Chtchetkineb2438402010-08-24 08:55:33 -0700461 if (opts->cpu_delay) {
462 args[n++] = "-cpu-delay";
463 args[n++] = opts->cpu_delay;
464 }
465
Vladimir Chtchetkine7fbf4972010-08-11 15:30:32 -0700466 if (opts->dns_server) {
467 args[n++] = "-dns-server";
468 args[n++] = opts->dns_server;
469 }
470
David 'Digit' Turner0b019492011-03-01 14:02:42 +0100471 hw->disk_ramdisk_path = avdInfo_getRamdiskPath(avd);
472 D("autoconfig: -ramdisk %s", hw->disk_ramdisk_path);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800473
David 'Digit' Turner40841b22011-03-01 14:04:00 +0100474 /* -partition-size is used to specify the max size of both the system
475 * and data partition sizes.
476 */
477 if (opts->partition_size) {
478 char* end;
479 long sizeMB = strtol(opts->partition_size, &end, 0);
480 long minSizeMB = 10;
481 long maxSizeMB = LONG_MAX / ONE_MB;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800482
David 'Digit' Turner40841b22011-03-01 14:04:00 +0100483 if (sizeMB < 0 || *end != 0) {
484 derror( "-partition-size must be followed by a positive integer" );
485 exit(1);
486 }
487 if (sizeMB < minSizeMB || sizeMB > maxSizeMB) {
488 derror( "partition-size (%d) must be between %dMB and %dMB",
489 sizeMB, minSizeMB, maxSizeMB );
490 exit(1);
491 }
492 defaultPartitionSize = (uint64_t) sizeMB * ONE_MB;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800493 }
494
David 'Digit' Turner40841b22011-03-01 14:04:00 +0100495
496 /** SYSTEM PARTITION **/
497
498 if (opts->sysdir == NULL) {
499 if (avdInfo_inAndroidBuild(avd)) {
500 opts->sysdir = ASTRDUP(avdInfo_getContentPath(avd));
501 D("autoconfig: -sysdir %s", opts->sysdir);
502 }
503 }
504
505 if (opts->sysdir != NULL) {
506 if (!path_exists(opts->sysdir)) {
507 derror("Directory does not exist: %s", opts->sysdir);
508 exit(1);
509 }
510 }
511
512 {
513 char* rwImage = NULL;
514 char* initImage = NULL;
515
516 do {
517 if (opts->system == NULL) {
518 /* If -system is not used, try to find a runtime system image
519 * (i.e. system-qemu.img) in the content directory.
520 */
521 rwImage = avdInfo_getSystemImagePath(avd);
522 if (rwImage != NULL) {
523 break;
524 }
525 /* Otherwise, try to find the initial system image */
526 initImage = avdInfo_getSystemInitImagePath(avd);
527 if (initImage == NULL) {
528 derror("No initial system image for this configuration!");
529 exit(1);
530 }
531 break;
532 }
533
534 /* If -system <name> is used, use it to find the initial image */
535 if (opts->sysdir != NULL) {
536 initImage = _getFullFilePath(opts->sysdir, opts->system);
537 } else {
538 initImage = ASTRDUP(opts->system);
539 }
540 if (!path_exists(initImage)) {
541 derror("System image file doesn't exist: %s", initImage);
542 exit(1);
543 }
544
545 } while (0);
546
547 if (rwImage != NULL) {
548 /* Use the read/write image file directly */
549 hw->disk_systemPartition_path = rwImage;
550 hw->disk_systemPartition_initPath = NULL;
551 D("Using direct system image: %s", rwImage);
552 } else if (initImage != NULL) {
553 hw->disk_systemPartition_path = NULL;
554 hw->disk_systemPartition_initPath = initImage;
555 D("Using initial system image: %s", initImage);
556 }
557
558 /* Check the size of the system partition image.
559 * If we have an AVD, it must be smaller than
560 * the disk.systemPartition.size hardware property.
561 *
562 * Otherwise, we need to adjust the systemPartitionSize
563 * automatically, and print a warning.
564 *
565 */
566 const char* systemImage = hw->disk_systemPartition_path;
567 uint64_t systemBytes;
568
569 if (systemImage == NULL)
570 systemImage = hw->disk_systemPartition_initPath;
571
572 if (path_get_size(systemImage, &systemBytes) < 0) {
573 derror("Missing system image: %s", systemImage);
574 exit(1);
575 }
576
577 hw->disk_systemPartition_size =
578 _adjustPartitionSize("system", systemBytes, defaultPartitionSize,
579 avdInfo_inAndroidBuild(avd));
580 }
581
582 /** DATA PARTITION **/
583
David 'Digit' Turnerfd59c332011-03-01 00:48:52 +0100584 if (opts->datadir) {
585 if (!path_exists(opts->datadir)) {
586 derror("Invalid -datadir directory: %s", opts->datadir);
587 }
588 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800589
David 'Digit' Turnerfd59c332011-03-01 00:48:52 +0100590 {
591 char* dataImage = NULL;
592 char* initImage = NULL;
593
594 do {
595 if (!opts->data) {
596 dataImage = avdInfo_getDataImagePath(avd);
597 if (dataImage != NULL) {
598 D("autoconfig: -data %s", dataImage);
599 break;
600 }
601 dataImage = avdInfo_getDefaultDataImagePath(avd);
602 if (dataImage == NULL) {
603 derror("No data image path for this configuration!");
604 exit (1);
605 }
606 opts->wipe_data = 1;
607 break;
608 }
609
610 if (opts->datadir) {
611 dataImage = _getFullFilePath(opts->datadir, opts->data);
612 } else {
613 dataImage = ASTRDUP(opts->data);
614 }
615 } while (0);
616
617 if (opts->initdata != NULL) {
618 initImage = ASTRDUP(opts->initdata);
619 if (!path_exists(initImage)) {
620 derror("Invalid initial data image path: %s", initImage);
621 exit(1);
622 }
623 } else {
624 initImage = avdInfo_getDataInitImagePath(avd);
625 D("autoconfig: -initdata %s", initImage);
626 }
627
628 hw->disk_dataPartition_path = dataImage;
629 if (opts->wipe_data) {
630 hw->disk_dataPartition_initPath = initImage;
631 } else {
632 hw->disk_dataPartition_initPath = NULL;
633 }
634
635 uint64_t defaultBytes = defaultPartitionSize;
636 uint64_t dataBytes;
637 const char* dataPath = hw->disk_dataPartition_initPath;
638
639 if (dataPath == NULL)
640 dataPath = hw->disk_dataPartition_path;
641
642 path_get_size(dataPath, &dataBytes);
643
644 hw->disk_dataPartition_size =
645 _adjustPartitionSize("data", dataBytes, defaultBytes,
646 avdInfo_inAndroidBuild(avd));
647 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800648
David 'Digit' Turnerc480cca2011-02-25 16:43:34 +0100649 /** CACHE PARTITION **/
650
651 if (opts->no_cache) {
652 /* No cache partition at all */
653 hw->disk_cachePartition = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800654 }
David 'Digit' Turnerc480cca2011-02-25 16:43:34 +0100655 else if (!hw->disk_cachePartition) {
656 if (opts->cache) {
657 dwarning( "Emulated hardware doesn't support a cache partition. -cache option ignored!" );
658 opts->cache = NULL;
659 }
660 }
661 else
662 {
663 if (!opts->cache) {
664 /* Find the current cache partition file */
665 opts->cache = avdInfo_getCachePath(avd);
666 if (opts->cache == NULL) {
667 /* The file does not exists, we will force its creation
668 * if we are not in the Android build system. Otherwise,
669 * a temporary file will be used.
670 */
671 if (!avdInfo_inAndroidBuild(avd)) {
672 opts->cache = avdInfo_getDefaultCachePath(avd);
673 }
674 }
David 'Digit' Turner48a3c662011-03-01 14:03:20 +0100675 if (opts->cache) {
676 D("autoconfig: -cache %s", opts->cache);
677 }
David 'Digit' Turnerc480cca2011-02-25 16:43:34 +0100678 }
679
680 if (opts->cache) {
681 hw->disk_cachePartition_path = ASTRDUP(opts->cache);
682 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800683 }
684
David 'Digit' Turnerc480cca2011-02-25 16:43:34 +0100685 /** SD CARD PARTITION */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800686
David 'Digit' Turner48a3c662011-03-01 14:03:20 +0100687 if (!hw->hw_sdCard) {
688 /* No SD Card emulation, so -sdcard will be ignored */
689 if (opts->sdcard) {
690 dwarning( "Emulated hardware doesn't support SD Cards. -sdcard option ignored." );
691 opts->sdcard = NULL;
692 }
693 } else {
694 /* Auto-configure -sdcard if it is not available */
695 if (!opts->sdcard) {
696 do {
697 /* If -datadir <path> is used, look for a sdcard.img file here */
698 if (opts->datadir) {
699 bufprint(tmp, tmpend, "%s/%s", opts->datadir, "system.img");
700 if (path_exists(tmp)) {
701 opts->sdcard = strdup(tmp);
702 break;
703 }
704 }
705
706 /* Otherwise, look at the AVD's content */
707 opts->sdcard = avdInfo_getSdCardPath(avd);
708 if (opts->sdcard != NULL) {
709 break;
710 }
711
712 /* Nothing */
713 } while (0);
714
715 if (opts->sdcard) {
716 D("autoconfig: -sdcard %s", opts->sdcard);
717 }
718 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800719 }
720
721 if(opts->sdcard) {
722 uint64_t size;
723 if (path_get_size(opts->sdcard, &size) == 0) {
724 /* see if we have an sdcard image. get its size if it exists */
David 'Digit' Turner8b657e52009-12-01 13:38:21 -0800725 /* due to what looks like limitations of the MMC protocol, one has
726 * to use an SD Card image that is equal or larger than 9 MB
727 */
728 if (size < 9*1024*1024ULL) {
729 fprintf(stderr, "### WARNING: SD Card files must be at least 9MB, ignoring '%s'\n", opts->sdcard);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800730 } else {
David 'Digit' Turner48a3c662011-03-01 14:03:20 +0100731 hw->hw_sdCard_path = ASTRDUP(opts->sdcard);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800732 }
733 } else {
David 'Digit' Turner48a3c662011-03-01 14:03:20 +0100734 dwarning("no SD Card image at '%s'", opts->sdcard);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800735 }
736 }
737
David 'Digit' Turner48a3c662011-03-01 14:03:20 +0100738
739 /** SNAPSHOT STORAGE HANDLING */
740
David 'Digit' Turner5f64b872011-02-28 23:23:05 +0100741 /* Determine snapstorage path. -no-snapstorage disables all snapshotting
742 * support. This means you can't resume a snapshot at load, save it at
743 * exit, or even load/save them dynamically at runtime with the console.
744 */
745 if (opts->no_snapstorage) {
746
747 if (opts->snapshot) {
748 dwarning("ignoring -snapshot option due to the use of -no-snapstorage");
749 opts->snapshot = NULL;
750 }
751
752 if (opts->snapstorage) {
753 dwarning("ignoring -snapstorage option due to the use of -no-snapstorage");
754 opts->snapstorage = NULL;
755 }
756 }
757 else
758 {
759 if (!opts->snapstorage) {
760 opts->snapstorage = avdInfo_getSnapStoragePath(avd);
761 if (opts->snapstorage != NULL) {
762 D("autoconfig: -snapstorage %s", opts->snapstorage);
Ot ten Thije353b3b12010-10-05 17:53:30 +0100763 }
764 }
765
David 'Digit' Turner5f64b872011-02-28 23:23:05 +0100766 if (opts->snapstorage && !path_exists(opts->snapstorage)) {
767 D("no image at '%s', state snapshots disabled", opts->snapstorage);
768 opts->snapstorage = NULL;
Ot ten Thije353b3b12010-10-05 17:53:30 +0100769 }
David 'Digit' Turner5f64b872011-02-28 23:23:05 +0100770 }
771
772 /* If we have a valid snapshot storage path */
773
774 if (opts->snapstorage) {
775
776 hw->disk_snapStorage_path = ASTRDUP(opts->snapstorage);
777
778 /* -no-snapshot is equivalent to using both -no-snapshot-load
779 * and -no-snapshot-save. You can still load/save snapshots dynamically
780 * from the console though.
781 */
782 if (opts->no_snapshot) {
783
784 opts->no_snapshot_load = 1;
785 opts->no_snapshot_save = 1;
786
787 if (opts->snapshot) {
788 dwarning("ignoring -snapshot option due to the use of -no-snapshot.");
789 }
790 }
791
792 if (!opts->no_snapshot_load || !opts->no_snapshot_save) {
793 if (opts->snapshot == NULL) {
794 opts->snapshot = "default-boot";
795 D("autoconfig: -snapshot %s", opts->snapshot);
796 }
797 }
798
799 /* We still use QEMU command-line options for the following since
800 * they can change from one invokation to the next and don't really
801 * correspond to the hardware configuration itself.
802 */
803 if (!opts->no_snapshot_load) {
804 args[n++] = "-loadvm";
805 args[n++] = ASTRDUP(opts->snapshot);
806 }
807
808 if (!opts->no_snapshot_save) {
809 args[n++] = "-savevm-on-exit";
810 args[n++] = ASTRDUP(opts->snapshot);
811 }
812
Tim Baverstock622b8f42010-12-07 11:36:59 +0000813 if (opts->no_snapshot_update_time) {
David 'Digit' Turnerbdb6f2d2011-02-23 15:57:25 +0100814 args[n++] = "-snapshot-no-time-update";
Tim Baverstock622b8f42010-12-07 11:36:59 +0000815 }
Ot ten Thijeae835ac2010-10-18 13:37:37 +0100816 }
Ot ten Thije353b3b12010-10-05 17:53:30 +0100817
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800818 if (!opts->logcat || opts->logcat[0] == 0) {
819 opts->logcat = getenv("ANDROID_LOG_TAGS");
820 if (opts->logcat && opts->logcat[0] == 0)
821 opts->logcat = NULL;
822 }
823
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800824 /* we always send the kernel messages from ttyS0 to android_kmsg */
David 'Digit' Turner062dd6a2011-03-01 14:50:07 +0100825 if (opts->show_kernel) {
826 args[n++] = "-show-kernel";
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800827 }
828
829 /* XXXX: TODO: implement -shell and -logcat through qemud instead */
830 if (!opts->shell_serial) {
831#ifdef _WIN32
832 opts->shell_serial = "con:";
833#else
834 opts->shell_serial = "stdio";
835#endif
836 }
837 else
838 opts->shell = 1;
839
840 if (opts->shell || opts->logcat) {
841 args[n++] = "-serial";
842 args[n++] = opts->shell_serial;
843 shell_serial = serial++;
844 }
845
David 'Digit' Turner062dd6a2011-03-01 14:50:07 +0100846 if (opts->radio) {
847 args[n++] = "-radio";
848 args[n++] = opts->radio;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800849 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800850
David 'Digit' Turner062dd6a2011-03-01 14:50:07 +0100851 if (opts->gps) {
852 args[n++] = "-gps";
853 args[n++] = opts->gps;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800854 }
855
856 if (opts->memory) {
857 char* end;
858 long ramSize = strtol(opts->memory, &end, 0);
859 if (ramSize < 0 || *end != 0) {
860 derror( "-memory must be followed by a positive integer" );
861 exit(1);
862 }
863 if (ramSize < 32 || ramSize > 4096) {
864 derror( "physical memory size must be between 32 and 4096 MB" );
865 exit(1);
866 }
David 'Digit' Turner5377c5b2011-02-10 16:52:19 +0100867 hw->hw_ramSize = ramSize;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800868 }
869 if (!opts->memory) {
David 'Digit' Turner3bbc9192011-01-19 22:18:02 +0100870 int ramSize = hw->hw_ramSize;
871 if (ramSize <= 0) {
872 /* Compute the default RAM size based on the size of screen.
873 * This is only used when the skin doesn't provide the ram
874 * size through its hardware.ini (i.e. legacy ones) or when
875 * in the full Android build system.
876 */
David 'Digit' Turner5377c5b2011-02-10 16:52:19 +0100877 int64_t pixels = hw->hw_lcd_width * hw->hw_lcd_height;
David 'Digit' Turner3bbc9192011-01-19 22:18:02 +0100878 /* The following thresholds are a bit liberal, but we
879 * essentially want to ensure the following mappings:
880 *
881 * 320x480 -> 96
882 * 800x600 -> 128
883 * 1024x768 -> 256
884 *
885 * These are just simple heuristics, they could change in
886 * the future.
887 */
888 if (pixels <= 250000)
889 ramSize = 96;
890 else if (pixels <= 500000)
891 ramSize = 128;
892 else
893 ramSize = 256;
894 }
David 'Digit' Turner5377c5b2011-02-10 16:52:19 +0100895 hw->hw_ramSize = ramSize;
896 }
897
898 D("Physical RAM size: %dMB\n", hw->hw_ramSize);
899
900 if (hw->vm_heapSize == 0) {
901 /* Compute the default heap size based on the RAM size.
902 * Essentially, we want to ensure the following liberal mappings:
903 *
904 * 96MB RAM -> 16MB heap
905 * 128MB RAM -> 24MB heap
906 * 256MB RAM -> 48MB heap
907 */
908 int ramSize = hw->hw_ramSize;
909 int heapSize;
910
911 if (ramSize < 100)
912 heapSize = 16;
913 else if (ramSize < 192)
914 heapSize = 24;
915 else
916 heapSize = 48;
917
918 hw->vm_heapSize = heapSize;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800919 }
920
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800921 if (opts->trace) {
922 args[n++] = "-trace";
923 args[n++] = opts->trace;
924 args[n++] = "-tracing";
925 args[n++] = "off";
926 }
927
Vladimir Chtchetkineb25bf2a2010-09-08 11:09:23 -0700928 /* Pass boot properties to the core. */
929 if (opts->prop != NULL) {
930 ParamList* pl = opts->prop;
931 for ( ; pl != NULL; pl = pl->next ) {
932 args[n++] = "-boot-property";
933 args[n++] = pl->param;
934 }
935 }
936
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200937 /* Setup the kernel init options
938 */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800939 {
940 static char params[1024];
941 char *p = params, *end = p + sizeof(params);
942
David 'Digit' Turner062dd6a2011-03-01 14:50:07 +0100943 /* Don't worry about having a leading space here, this is handled
944 * by the core later. */
945
Jun Nakajima334ab472011-02-02 23:49:59 -0800946#ifdef TARGET_I386
947 p = bufprint(p, end, " androidboot.hardware=goldfish");
Jun Nakajimabac9add2011-02-08 22:10:52 -0800948 p = bufprint(p, end, " clocksource=pit");
Jun Nakajima334ab472011-02-02 23:49:59 -0800949#endif
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800950
951 if (opts->shell || opts->logcat) {
952 p = bufprint(p, end, " androidboot.console=ttyS%d", shell_serial );
953 }
954
955 if (opts->trace) {
956 p = bufprint(p, end, " android.tracing=1");
957 }
958
959 if (!opts->no_jni) {
960 p = bufprint(p, end, " android.checkjni=1");
961 }
962
963 if (opts->no_boot_anim) {
964 p = bufprint( p, end, " android.bootanim=0" );
965 }
966
967 if (opts->logcat) {
968 char* q = bufprint(p, end, " androidboot.logcat=%s", opts->logcat);
969
970 if (q < end) {
971 /* replace any space by a comma ! */
972 {
973 int nn;
974 for (nn = 1; p[nn] != 0; nn++)
975 if (p[nn] == ' ' || p[nn] == '\t')
976 p[nn] = ',';
977 p += nn;
978 }
979 }
980 p = q;
981 }
982
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800983 if (opts->bootchart) {
984 p = bufprint(p, end, " androidboot.bootchart=%s", opts->bootchart);
985 }
986
987 if (p >= end) {
988 fprintf(stderr, "### ERROR: kernel parameters too long\n");
989 exit(1);
990 }
991
David 'Digit' Turner0b019492011-03-01 14:02:42 +0100992 hw->kernel_parameters = strdup(params);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800993 }
994
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700995 if (opts->ports) {
996 args[n++] = "-android-ports";
997 args[n++] = opts->ports;
998 }
999
1000 if (opts->port) {
1001 args[n++] = "-android-port";
1002 args[n++] = opts->port;
1003 }
1004
1005 if (opts->report_console) {
1006 args[n++] = "-android-report-console";
1007 args[n++] = opts->report_console;
1008 }
1009
1010 if (opts->http_proxy) {
1011 args[n++] = "-http-proxy";
1012 args[n++] = opts->http_proxy;
1013 }
1014
Vladimir Chtchetkine43552dc2010-07-22 11:23:19 -07001015 if (opts->charmap) {
1016 args[n++] = "-charmap";
1017 args[n++] = opts->charmap;
1018 }
1019
Vladimir Chtchetkineb5365f32010-08-09 13:33:57 -07001020 if (opts->memcheck) {
1021 args[n++] = "-android-memcheck";
1022 args[n++] = opts->memcheck;
1023 }
1024
David 'Digit' Turner5377c5b2011-02-10 16:52:19 +01001025 /* physical memory is now in hw->hw_ramSize */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001026
David 'Digit' Turner53eb66d2011-03-01 14:58:23 +01001027 hw->avd_name = ASTRDUP(avdInfo_getName(avd));
David 'Digit' Turner6b8555c2011-02-17 04:28:19 +01001028
Dries Harnie40beab42010-05-15 17:04:47 +02001029 /* Set up the interfaces for inter-emulator networking */
1030 if (opts->shared_net_id) {
1031 unsigned int shared_net_id = atoi(opts->shared_net_id);
1032 char nic[37];
Dries Harnie40beab42010-05-15 17:04:47 +02001033
1034 args[n++] = "-net";
1035 args[n++] = "nic,vlan=0";
1036 args[n++] = "-net";
1037 args[n++] = "user,vlan=0";
Dries Harnie111d6f82010-06-09 21:42:18 +02001038
1039 args[n++] = "-net";
1040 snprintf(nic, sizeof nic, "nic,vlan=1,macaddr=52:54:00:12:34:%02x", shared_net_id);
1041 args[n++] = strdup(nic);
1042 args[n++] = "-net";
1043 args[n++] = "socket,vlan=1,mcast=230.0.0.10:1234";
Dries Harnie40beab42010-05-15 17:04:47 +02001044 }
1045
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001046 while(argc-- > 0) {
1047 args[n++] = *argv++;
1048 }
1049 args[n] = 0;
1050
David 'Digit' Turner26d41532011-03-01 15:03:07 +01001051 /* Generate a hardware-qemu.ini for this AVD. The real hardware
David 'Digit' Turner622f1532011-02-01 17:48:37 +01001052 * configuration is ususally stored in several files, e.g. the AVD's
1053 * config.ini plus the skin-specific hardware.ini.
1054 *
David 'Digit' Turner26d41532011-03-01 15:03:07 +01001055 * The new file will group all definitions and will be used to
David 'Digit' Turner622f1532011-02-01 17:48:37 +01001056 * launch the core with the -android-hw <file> option.
1057 */
1058 {
David 'Digit' Turner42074e52011-02-10 16:03:28 +01001059 const char* coreHwIniPath = avdInfo_getCoreHwIniPath(avd);
1060 IniFile* hwIni = iniFile_newFromMemory("", NULL);
David 'Digit' Turner622f1532011-02-01 17:48:37 +01001061 androidHwConfig_write(hw, hwIni);
David 'Digit' Turner26d41532011-03-01 15:03:07 +01001062
1063 if (filelock_create(coreHwIniPath) == NULL) {
1064 /* The AVD is already in use, we still support this as an
1065 * experimental feature. Use a temporary hardware-qemu.ini
1066 * file though to avoid overwriting the existing one. */
1067 TempFile* tempIni = tempfile_create();
1068 coreHwIniPath = tempfile_path(tempIni);
1069 }
1070
David 'Digit' Turner42074e52011-02-10 16:03:28 +01001071 if (iniFile_saveToFile(hwIni, coreHwIniPath) < 0) {
1072 derror("Could not write hardware.ini to %s: %s", coreHwIniPath, strerror(errno));
David 'Digit' Turner622f1532011-02-01 17:48:37 +01001073 exit(2);
1074 }
1075 args[n++] = "-android-hw";
David 'Digit' Turner42074e52011-02-10 16:03:28 +01001076 args[n++] = strdup(coreHwIniPath);
David 'Digit' Turner622f1532011-02-01 17:48:37 +01001077 }
1078
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001079 if(VERBOSE_CHECK(init)) {
1080 int i;
David 'Digit' Turner33361762011-01-19 22:11:03 +01001081 printf("QEMU options list:\n");
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001082 for(i = 0; i < n; i++) {
David 'Digit' Turner33361762011-01-19 22:11:03 +01001083 printf("emulator: argv[%02d] = \"%s\"\n", i, args[i]);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001084 }
David 'Digit' Turner33361762011-01-19 22:11:03 +01001085 /* Dump final command-line option to make debugging the core easier */
1086 printf("Concatenated QEMU options:\n");
1087 for (i = 0; i < n; i++) {
David 'Digit' Turner26722dd2011-02-24 16:40:20 +01001088 /* To make it easier to copy-paste the output to a command-line,
1089 * quote anything that contains spaces.
1090 */
1091 if (strchr(args[i], ' ') != NULL) {
1092 printf(" '%s'", args[i]);
1093 } else {
1094 printf(" %s", args[i]);
1095 }
David 'Digit' Turner33361762011-01-19 22:11:03 +01001096 }
1097 printf("\n");
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001098 }
David 'Digit' Turner74d7ace2011-02-02 13:21:03 +01001099
1100 /* Setup SDL UI just before calling the code */
1101 init_sdl_ui(skinConfig, skinPath, opts);
1102
David 'Digit' Turnerbdb6f2d2011-02-23 15:57:25 +01001103 if (attach_ui_to_core(opts) < 0) {
1104 derror("Can't attach to core!");
1105 exit(1);
1106 }
1107
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001108 return qemu_main(n, args);
1109}