From df7881f07f53b041dc0568be8528e9dbb74994cc Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 17 Dec 2008 18:04:49 -0800 Subject: Code drop from //branches/cupcake/...@124589 --- android/build/binary.make | 29 + android/build/clear_vars.make | 30 + android/build/definitions.make | 88 ++ android/build/getdir.make | 19 + android/build/host_executable.make | 36 + android/build/host_static_library.make | 35 + android/build/mkdeps.sh | 48 + android/config/check-alsa.c | 107 +++ android/config/check-esd.c | 67 ++ android/globals.h | 33 + android/tools/gen-hw-config.py | 141 +++ android/utils/debug.h | 18 + android/utils/dirscanner.c | 203 +++++ android/utils/dirscanner.h | 25 + android/utils/ini.c | 366 ++++++++ android/utils/ini.h | 121 +++ android/vm/hardware-properties.ini | 158 ++++ android/vm/hw-config-defs.h | 165 ++++ android/vm/hw-config.c | 39 + android/vm/hw-config.h | 46 + android/vm/info.c | 1534 ++++++++++++++++++++++++++++++++ android/vm/info.h | 161 ++++ 22 files changed, 3469 insertions(+) create mode 100644 android/build/binary.make create mode 100644 android/build/clear_vars.make create mode 100644 android/build/definitions.make create mode 100644 android/build/getdir.make create mode 100644 android/build/host_executable.make create mode 100644 android/build/host_static_library.make create mode 100755 android/build/mkdeps.sh create mode 100644 android/config/check-alsa.c create mode 100644 android/config/check-esd.c create mode 100644 android/globals.h create mode 100755 android/tools/gen-hw-config.py create mode 100644 android/utils/debug.h create mode 100644 android/utils/dirscanner.c create mode 100644 android/utils/dirscanner.h create mode 100644 android/utils/ini.c create mode 100644 android/utils/ini.h create mode 100644 android/vm/hardware-properties.ini create mode 100644 android/vm/hw-config-defs.h create mode 100644 android/vm/hw-config.c create mode 100644 android/vm/hw-config.h create mode 100644 android/vm/info.c create mode 100644 android/vm/info.h (limited to 'android') diff --git a/android/build/binary.make b/android/build/binary.make new file mode 100644 index 0000000..379ef9a --- /dev/null +++ b/android/build/binary.make @@ -0,0 +1,29 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# definitions shared by host_executable.make and host_static_library.make +# + +# the directory where we're going to place our object files +LOCAL_OBJS_DIR := $(call intermediates-dir-for,EXECUTABLES,$(LOCAL_MODULE)) +LOCAL_OBJECTS := +LOCAL_CC ?= $(CC) +LOCAL_C_SOURCES := $(filter %.c,$(LOCAL_SRC_FILES)) + +$(foreach src,$(LOCAL_C_SOURCES), \ + $(eval $(call compile-c-source,$(src))) \ +) + +CLEAN_OBJS_DIRS += $(LOCAL_OBJS_DIR) diff --git a/android/build/clear_vars.make b/android/build/clear_vars.make new file mode 100644 index 0000000..a9289b0 --- /dev/null +++ b/android/build/clear_vars.make @@ -0,0 +1,30 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# called multiple times to clear variables used to define a given 'module' +# +LOCAL_NO_DEFAULT_COMPILER_FLAGS:= +LOCAL_CC := +LOCAL_CXX := +LOCAL_CFLAGS := +LOCAL_LDFLAGS := +LOCAL_LDLIBS := +LOCAL_SRC_FILES := +LOCAL_MODULE := +LOCAL_MODULE_PATH:= +LOCAL_STATIC_LIBRARIES := +LOCAL_BUILT_MODULE := +LOCAL_PREBUILT_OBJ_FILES := + diff --git a/android/build/definitions.make b/android/build/definitions.make new file mode 100644 index 0000000..c4183ea --- /dev/null +++ b/android/build/definitions.make @@ -0,0 +1,88 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# shared definitions +ifeq ($(strip $(SHOW)),) +define pretty +@echo $1 +endef +hide := @ +else +define pretty +endef +hide := +endif + +define my-dir +. +endef + +# return the directory containing the intermediate files for a given +# kind of executable +# $1 = type (EXECUTABLES or STATIC_LIBRARIES) +# $2 = module name +# $3 = ignored +# +define intermediates-dir-for +$(OBJS_DIR)/intermediates/$(2) +endef + +# Generate the full path of a given static library +define library-path +$(OBJS_DIR)/$(1).a +endef + +define executable-path +$(OBJS_DIR)/$(1)$(EXE) +endef + +# Compile a C source file +# +define compile-c-source +SRC:=$(1) +OBJ:=$$(LOCAL_OBJS_DIR)/$$(SRC:%.c=%.o) +LOCAL_OBJECTS += $$(OBJ) +$$(OBJ): PRIVATE_CFLAGS := $$(CFLAGS) $$(LOCAL_CFLAGS) -I$$(LOCAL_PATH) -I$$(OBJS_DIR) +$$(OBJ): PRIVATE_CC := $$(LOCAL_CC) +$$(OBJ): PRIVATE_OBJ := $$(OBJ) +$$(OBJ): PRIVATE_MODULE := $$(LOCAL_MODULE) +$$(OBJ): PRIVATE_SRC := $$(SRC_PATH)/$$(SRC) +$$(OBJ): PRIVATE_SRC0 := $$(SRC) +$$(OBJ): $$(SRC_PATH)/$$(SRC) + @mkdir -p $$(dir $$(PRIVATE_OBJ)) + @echo "Compile: $$(PRIVATE_MODULE) <= $$(PRIVATE_SRC0)" + $(hide) $$(PRIVATE_CC) $$(PRIVATE_CFLAGS) -c -o $$(PRIVATE_OBJ) -MMD -MP -MF $$(PRIVATE_OBJ).d.tmp $$(PRIVATE_SRC) + $(hide) $$(SRC_PATH)/android/build/mkdeps.sh $$(PRIVATE_OBJ) $$(PRIVATE_OBJ).d.tmp $$(PRIVATE_OBJ).d +endef + +# for now, we only use prebuilt SDL libraries, so copy them +define copy-prebuilt-lib +_SRC := $(1) +_SRC1 := $$(notdir $$(_SRC)) +_DST := $$(OBJS_DIR)/$$(_SRC1) +LIBRARIES += $$(_DST) +$$(_DST): PRIVATE_DST := $$(_DST) +$$(_DST): PRIVATE_SRC := $$(_SRC) +$$(_DST): $$(_SRC) + @mkdir -p $$(dir $$(PRIVATE_DST)) + @echo "Prebuilt: $$(PRIVATE_DST)" + $(hide) cp -f $$(PRIVATE_SRC) $$(PRIVATE_DST) +endef + +define create-dir +$(1): + mkdir -p $(1) +endef + diff --git a/android/build/getdir.make b/android/build/getdir.make new file mode 100644 index 0000000..a4dadd3 --- /dev/null +++ b/android/build/getdir.make @@ -0,0 +1,19 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# used to return in 'dir' the name of the current operating system +# we really get the value from the configuration script +# +dir := $(HOST_OS) diff --git a/android/build/host_executable.make b/android/build/host_executable.make new file mode 100644 index 0000000..a9b51d8 --- /dev/null +++ b/android/build/host_executable.make @@ -0,0 +1,36 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# first, call a library containing all object files +include $(BUILD_HOST_STATIC_LIBRARY) + +# now, link the executable with it +LOCAL_BUILT_LIBRARY := $(LOCAL_BUILT_MODULE) +LOCAL_BUILT_MODULE := $(call executable-path,$(LOCAL_MODULE)) + +LOCAL_LDLIBS := $(foreach lib,$(LOCAL_STATIC_LIBRARIES),$(call library-path,$(lib))) $(LOCAL_LDLIBS) + +$(LOCAL_BUILT_MODULE): PRIVATE_LDFLAGS := $(LDFLAGS) $(LOCAL_LDFLAGS) +$(LOCAL_BUILT_MODULE): PRIVATE_LDLIBS := $(LOCAL_LDLIBS) +$(LOCAL_BUILT_MODULE): PRIVATE_LIBRARY := $(LOCAL_BUILT_LIBRARY) + +$(LOCAL_BUILT_MODULE): $(LOCAL_BUILT_LIBRARY) + @ mkdir -p $(dir $@) + @ echo "Executable: $@" + $(hide) $(LD) $(PRIVATE_LDFLAGS) -o $@ $(PRIVATE_LIBRARY) $(PRIVATE_LDLIBS) + +EXECUTABLES += $(LOCAL_BUILT_MODULE) +$(LOCAL_BUILT_MODULE): $(foreach lib,$(LOCAL_STATIC_LIBRARIES),$(call library-path,$(lib))) + diff --git a/android/build/host_static_library.make b/android/build/host_static_library.make new file mode 100644 index 0000000..3de5a99 --- /dev/null +++ b/android/build/host_static_library.make @@ -0,0 +1,35 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# build a host executable, the name of the final executable should be +# put in LOCAL_BUILT_MODULE for use by the caller +# + +#$(info STATIC_LIBRARY SRCS=$(LOCAL_SRC_FILES)) +LOCAL_BUILT_MODULE := $(call library-path,$(LOCAL_MODULE)) +LOCAL_CC ?= $(CC) +include $(BUILD_SYSTEM)/binary.make + +LOCAL_AR ?= $(AR) +ARFLAGS := crs + +$(LOCAL_BUILT_MODULE): PRIVATE_AR := $(LOCAL_AR) +$(LOCAL_BUILT_MODULE): PRIVATE_OBJECTS := $(LOCAL_OBJECTS) +$(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS) + @mkdir -p $(dir $@) + @echo "Library: $@" + $(hide) $(PRIVATE_AR) $(ARFLAGS) $@ $(PRIVATE_OBJECTS) + +LIBRARIES += $(LOCAL_BUILT_MODULE) diff --git a/android/build/mkdeps.sh b/android/build/mkdeps.sh new file mode 100755 index 0000000..2d25ca3 --- /dev/null +++ b/android/build/mkdeps.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This script is used to transform the dependency files generated by GCC +# For example, a typical .d file will have a line like: +# +# source.o: /full/path/to/source.c other.h headers.h +# ... +# +# the script is used to replace 'source.o' to a full path, as in +# +# objs/intermediates/emulator/source.o: /full/path/to/source.c other.h headers.h +# +# parameters +# +# $1: object file (full path) +# $2: source dependency file to modify (erased on success) +# $3: target source dependency file +# + +# quote the object path. we change a single '.' into +# a '\.' since this will be parsed by sed. +# +OBJECT=`echo $1 | sed -e s/\\\\./\\\\\\\\./g` +#echo OBJECT=$OBJECT + +OBJ_NAME=`basename $OBJECT` +#echo OBJ_NAME=$OBJ_NAME + +# we replace $OBJ_NAME with $OBJECT +# +cat $2 | sed -e s%$OBJ_NAME%$OBJECT%g > $3 && rm -f $2 + + + diff --git a/android/config/check-alsa.c b/android/config/check-alsa.c new file mode 100644 index 0000000..4ab2945 --- /dev/null +++ b/android/config/check-alsa.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#define D(...) fprintf(stderr,__VA_ARGS__) +#define STRINGIFY(x) _STRINGIFY(x) +#define _STRINGIFY(x) #x + +#define DYN_SYMBOLS \ + DYN_FUNCTION(size_t,snd_pcm_sw_params_sizeof,(void)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_current,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) \ + DYN_FUNCTION(int,snd_pcm_sw_params_set_start_threshold,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val)) \ + DYN_FUNCTION(int,snd_pcm_sw_params,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)) \ + DYN_FUNCTION(int,snd_pcm_sw_params_current,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)) \ + DYN_FUNCTION(size_t,snd_pcm_hw_params_sizeof,(void)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_any,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_set_access,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_set_format,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_set_rate_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_set_channels_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_set_buffer_time_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) \ + DYN_FUNCTION(int,snd_pcm_hw_params,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_get_buffer_size,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \ + DYN_FUNCTION(int,snd_pcm_prepare,(snd_pcm_t *pcm)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_get_period_size,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_get_period_size_min,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_set_period_size,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int dir)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_get_buffer_size_min,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_set_buffer_size,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val)) \ + DYN_FUNCTION(int,snd_pcm_hw_params_set_period_time_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) \ + DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_avail_update,(snd_pcm_t *pcm)) \ + DYN_FUNCTION(int,snd_pcm_drop,(snd_pcm_t *pcm)) \ + DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_writei,(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)) \ + DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_readi,(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)) \ + DYN_FUNCTION(snd_pcm_state_t,snd_pcm_state,(snd_pcm_t *pcm)) \ + DYN_FUNCTION(const char*,snd_strerror,(int errnum)) \ + DYN_FUNCTION(int,snd_pcm_open,(snd_pcm_t **pcm, const char *name,snd_pcm_stream_t stream, int mode)) \ + DYN_FUNCTION(int,snd_pcm_close,(snd_pcm_t *pcm)) \ + + + +/* define pointers to library functions we're going to use */ +#define DYN_FUNCTION(ret,name,sig) \ + static ret (*func_ ## name)sig; + +DYN_SYMBOLS + +#undef DYN_FUNCTION + +#define func_snd_pcm_hw_params_alloca(ptr) \ + do { assert(ptr); *ptr = (snd_pcm_hw_params_t *) alloca(func_snd_pcm_hw_params_sizeof()); memset(*ptr, 0, func_snd_pcm_hw_params_sizeof()); } while (0) + +#define func_snd_pcm_sw_params_alloca(ptr) \ + do { assert(ptr); *ptr = (snd_pcm_sw_params_t *) alloca(func_snd_pcm_sw_params_sizeof()); memset(*ptr, 0, func_snd_pcm_sw_params_sizeof()); } while (0) + +static void* alsa_lib; + +int main(void) +{ + int result = 1; + + alsa_lib = dlopen( "libasound.so", RTLD_NOW ); + if (alsa_lib == NULL) + alsa_lib = dlopen( "libasound.so.2", RTLD_NOW ); + + if (alsa_lib == NULL) { + D("could not find libasound on this system\n"); + return 1; + } + +#undef DYN_FUNCTION +#define DYN_FUNCTION(ret,name,sig) \ + do { \ + (func_ ##name) = dlsym( alsa_lib, STRINGIFY(name) ); \ + if ((func_##name) == NULL) { \ + D("could not find %s in libasound\n", STRINGIFY(name)); \ + goto Fail; \ + } \ + } while (0); + + DYN_SYMBOLS + + result = 0; + goto Exit; + +Fail: + D("failed to open library\n"); + +Exit: + dlclose(alsa_lib); + return result; +} diff --git a/android/config/check-esd.c b/android/config/check-esd.c new file mode 100644 index 0000000..a8eb11b --- /dev/null +++ b/android/config/check-esd.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* this file is used to test that we can use libesd with lazy dynamic linking */ + +#include +#include +#include + +#define D(...) fprintf(stderr,__VA_ARGS__) +#define STRINGIFY(x) _STRINGIFY(x) +#define _STRINGIFY(x) #x + +#define ESD_SYMBOLS \ + ESD_FUNCTION(int,esd_play_stream,(esd_format_t,int,const char*,const char*)) \ + ESD_FUNCTION(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \ + ESD_FUNCTION(int,esd_open_sound,( const char *host )) \ + ESD_FUNCTION(int,esd_close,(int)) \ + +/* define pointers to library functions we're going to use */ +#define ESD_FUNCTION(ret,name,sig) \ + static ret (*func_ ## name)sig; + +ESD_SYMBOLS + +#undef ESD_FUNCTION +static void* esd_lib; + +int main( void ) +{ + int fd; + + esd_lib = dlopen( "libesd.so", RTLD_NOW ); + if (esd_lib == NULL) + esd_lib = dlopen( "libesd.so.0", RTLD_NOW ); + + if (esd_lib == NULL) { + D("could not find libesd on this system"); + return 1; + } + +#undef ESD_FUNCTION +#define ESD_FUNCTION(ret,name,sig) \ + do { \ + (func_ ##name) = dlsym( esd_lib, STRINGIFY(name) ); \ + if ((func_##name) == NULL) { \ + D("could not find %s in libesd\n", STRINGIFY(name)); \ + return 1; \ + } \ + } while (0); + + ESD_SYMBOLS + + return 0; +} diff --git a/android/globals.h b/android/globals.h new file mode 100644 index 0000000..625c84a --- /dev/null +++ b/android/globals.h @@ -0,0 +1,33 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#ifndef _ANDROID_GLOBALS_H +#define _ANDROID_GLOBALS_H + +#include "android/vm/info.h" +#include "android/vm/hw-config.h" + +/* this structure is setup when loading the virtual machine + * after that, you can read the 'flags' field to determine + * wether a data or cache wipe has been in effect. + */ +extern AvmInfoParams android_vmParams[1]; + +/* a pointer to the android virtual machine information + * object, which can be queried for the paths of various + * image files or the skin + */ +extern AvmInfo* android_vmInfo; + +/* the hardware configuration for this specific virtual machine */ +extern AndroidHwConfig android_hw[1]; + +#endif /* _ANDROID_GLOBALS_H */ diff --git a/android/tools/gen-hw-config.py b/android/tools/gen-hw-config.py new file mode 100755 index 0000000..ae3a8de --- /dev/null +++ b/android/tools/gen-hw-config.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# +# This software is licensed under the terms of the GNU General Public +# License version 2, as published by the Free Software Foundation, and +# may be copied, distributed, and modified under those terms. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# this script is used to generate 'android/vm/hw-config.h' by +# parsing 'android/vm/hardware-properties.ini' +# +# +import sys, os, string, re + +# location of source file, relative to current program directory +relativeSourcePath = "../vm/hardware-properties.ini" + +# location of target file, relative to current program directory +relativeTargetPath = "../vm/hw-config-defs.h" + +def quoteStringForC(str): + """quote a string so it can be used in C""" + return '\\"'.join('"'+p+'"' for p in str.split('"')) + +# a dictionary that maps item types as they appear in the .ini +# file into macro names in the generated C header +# +typesToMacros = { + 'integer': 'HWCFG_INT', + 'string': 'HWCFG_STRING', + 'boolean': 'HWCFG_BOOL', + 'diskSize': 'HWCFG_DISKSIZE', + 'double': 'HWCFG_DOUBLE' + } + +# the list of macro names +macroNames = typesToMacros.values() + +# target program header +targetHeader = """\ +/* this file is automatically generated from 'hardware-properties.ini' + * DO NOT EDIT IT. To re-generate it, use android/tools/gen-hw-config.py' + */""" + +# locate source and target +programDir = os.path.dirname(sys.argv[0]) +sourceFile = os.path.normpath(os.path.join(programDir,relativeSourcePath)) +targetFile = os.path.normpath(os.path.join(programDir,relativeTargetPath)) + +# parse the source file and record items +# I would love to use Python's ConfigParser, but it doesn't +# support files without sections, or multiply defined items +# +items = [] +lastItem = None + +class Item: + def __init__(self,name): + self.name = name + self.type = type + self.default = None + self.abstract = "" + self.description = "" + + def add(self,key,val): + if key == 'type': + self.type = val + elif key == 'default': + self.default = val + elif key == 'abstract': + self.abstract = val + elif key == 'description': + self.description = val + +for line in open(sourceFile): + line = line.strip() + # ignore empty lines and comments + if len(line) == 0 or line[0] in ";#": + continue + key, value = line.split('=') + + key = key.strip() + value = value.strip() + + if key == 'name': + if lastItem: items.append(lastItem) + lastItem = Item(value) + else: + lastItem.add(key, value) + +if lastItem: + items.append(lastItem) + + +print targetHeader + +# write guards to prevent bad compiles +for m in macroNames: + print """\ +#ifndef %(macro)s +#error %(macro)s not defined +#endif""" % { 'macro':m } +print "" + +for item in items: + if item.type == None: + sys.stderr.write("ignoring config item with no type '%s'\n" % item.name) + continue + + if not typesToMacros.has_key(item.type): + sys.stderr.write("ignoring config item with unknown type '%s': '%s'\n" % \ + (item.type, item.name)) + continue + + if item.default == None: + sys.stderr.write("ignoring config item with no default '%s' */" % item.name) + continue + + # convert dots into underscores + varMacro = typesToMacros[item.type] + varNameStr = quoteStringForC(item.name) + varName = item.name.replace(".","_") + varDefault = item.default + varAbstract = quoteStringForC(item.abstract) + varDesc = quoteStringForC(item.description) + + if item.type in [ 'string', 'boolean', 'diskSize' ]: + # quote default value for strings + varDefault = quoteStringForC(varDefault) + + print "%s(\n %s,\n %s,\n %s,\n %s,\n %s)\n" % \ + (varMacro,varName,varNameStr,varDefault,varAbstract,varDesc) + + +for m in macroNames: + print "#undef %s" % m + +print "/* end of auto-generated file */" diff --git a/android/utils/debug.h b/android/utils/debug.h new file mode 100644 index 0000000..28b93ee --- /dev/null +++ b/android/utils/debug.h @@ -0,0 +1,18 @@ +/* Copyright (C) 2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#ifndef _ANDROID_UTILS_DEBUG_H +#define _ANDROID_UTILS_DEBUG_H + +/* we'll move android_debug.h here later */ +#include "android_debug.h" + +#endif /* _ANDROID_UTILS_DEBUG_H */ diff --git a/android/utils/dirscanner.c b/android/utils/dirscanner.c new file mode 100644 index 0000000..9f44d2d --- /dev/null +++ b/android/utils/dirscanner.c @@ -0,0 +1,203 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "android/utils/dirscanner.h" +#include "android_utils.h" +#include "vl.h" +#include + +#define DIRSCANNER_BASE \ + char root[PATH_MAX]; \ + int rootLen; \ + char full[PATH_MAX]; \ + + +#if _WIN32 + +#include + +struct DirScanner { + DIRSCANNER_BASE + intptr_t findIndex1; + struct _finddata_t findData; +}; + +/* note: findIndex1 contains the find index + 1 + * so a value of 0 means 'invalid' + */ + +static int +_dirScannerInit( DirScanner* s ) +{ + char* p = s->root + s->rootLen; + char* end = s->root + sizeof s->root; + int ret; + + /* create file spec by appending \* to root */ + p = bufprint(p, end, "\\*"); + if (p >= end) + return -1; + + ret = _findfirst(s->root, &s->findData) + 1; + + s->findIndex1 = ret+1; + return ret; +} + +static void +_dirScanner_done( DirScanner* s ) +{ + if (s->findIndex1 > 0) { + _findclose(s->findIndex1-1); + s->findIndex1 = 0; + } +} + +const char* +dirScanner_next( DirScanner* s ) +{ + char* ret = NULL; + + if (!s || s->findIndex1 <= 0) + return NULL; + + while (ret == NULL) { + ret = s->findData.name; + + /* ignore special directories */ + if (!strcmp(ret, ".") || !strcmp(ret, "..")) { + ret = NULL; + } + /* find next one */ + if (_findnext(s->findIndex1-1, &s->findData) < 0) { + _dirScanner_done(s); + break; + } + } + return ret; +} + +#else /* !_WIN32 */ + +#include +struct DirScanner { + DIRSCANNER_BASE + DIR* dir; + struct dirent* entry; +}; + +static int +_dirScannerInit( DirScanner* s ) +{ + s->dir = opendir(s->root); + + if (s->dir == NULL) + return -1; + + s->entry = NULL; + return 0; +} + +static void +_dirScanner_done( DirScanner* s ) +{ + if (s->dir) { + closedir(s->dir); + s->dir = NULL; + } +} + +const char* +dirScanner_next( DirScanner* s ) +{ + char* ret = NULL; + + if (!s || s->dir == NULL) + return NULL; + + for (;;) + { + /* read new entry if needed */ + s->entry = readdir(s->dir); + if (s->entry == NULL) { + _dirScanner_done(s); + break; + } + + /* ignore special directories */ + ret = s->entry->d_name; + + if (!strcmp(ret,".") || !strcmp(ret,"..")) { + ret = NULL; + continue; + } + break; + } + return ret; +} + +#endif /* !_WIN32 */ + +DirScanner* +dirScanner_new ( const char* rootPath ) +{ + DirScanner* s = qemu_mallocz(sizeof *s); + char* p = s->root; + char* end = p + sizeof s->root; + + p = bufprint(p, end, "%s", rootPath); + if (p >= end) + goto FAIL; + + s->rootLen = (p - s->root); + + if (_dirScannerInit(s) < 0) + goto FAIL; + + return s; + +FAIL: + dirScanner_free(s); + return NULL; +} + + +void +dirScanner_free( DirScanner* s ) +{ + if (!s) + return; + + _dirScanner_done(s); + qemu_free(s); +} + + +const char* +dirScanner_nextFull( DirScanner* s ) +{ + const char* name = dirScanner_next(s); + char* p; + char* end; + + if (name == NULL) + return NULL; + + p = s->full; + end = p + sizeof s->full; + + p = bufprint(p, end, "%.*s/%s", s->rootLen, s->root, name); + if (p >= end) { + /* ignore if the full name is too long */ + return dirScanner_nextFull(s); + } + return s->full; +} diff --git a/android/utils/dirscanner.h b/android/utils/dirscanner.h new file mode 100644 index 0000000..9486cfc --- /dev/null +++ b/android/utils/dirscanner.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#ifndef _ANDROID_UTILS_DIR_H +#define _ANDROID_UTILS_DIR_H + +/* simple utility to parse directories for files */ +/* needed because Unix and Windows don't use the same stuff */ + +typedef struct DirScanner DirScanner; + +DirScanner* dirScanner_new ( const char* rootPath ); +void dirScanner_free( DirScanner* s ); +const char* dirScanner_next( DirScanner* s ); +const char* dirScanner_nextFull( DirScanner* s ); + +#endif /* _ANDROID_UTILS_DIR_H */ diff --git a/android/utils/ini.c b/android/utils/ini.c new file mode 100644 index 0000000..d99ecdd --- /dev/null +++ b/android/utils/ini.c @@ -0,0 +1,366 @@ +/* Copyright (C) 2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "android/utils/ini.h" +#include +#include +#include +#include +#include +#include "android_debug.h" +#include "osdep.h" + +/* W() is used to print warnings, D() to print debugging info */ +#define W(...) dwarning(__VA_ARGS__) +#define D(...) VERBOSE_PRINT(vm_config,__VA_ARGS__) + +/* a simple .ini file parser and container for Android + * no sections support. see android/utils/ini.h for + * more details on the supported file format. + */ +typedef struct { + char* key; + char* value; +} IniPair; + +struct IniFile { + int numPairs; + int maxPairs; + IniPair* pairs; +}; + +void +iniFile_free( IniFile* i ) +{ + int nn; + for (nn = 0; nn < i->numPairs; nn++) { + free(i->pairs[nn].key); + i->pairs[nn].key = NULL; + i->pairs[nn].value = NULL; + } + free(i->pairs); + free(i); +} + +static IniFile* +iniFile_alloc( void ) +{ + IniFile* i = calloc(1, sizeof(*i)); + return i; +} + +static void +iniFile_addPair( IniFile* i, const char* key, int keyLen, + const char* value, int valueLen ) +{ + IniPair* pair; + + if (i->numPairs >= i->maxPairs) { + int oldMax = i->maxPairs; + int newMax = oldMax + (oldMax >> 1) + 4; + IniPair* newPairs = realloc(i->pairs, newMax*sizeof(newPairs[0])); + + i->pairs = newPairs; + i->maxPairs = newMax; + } + + pair = i->pairs + i->numPairs; + + pair->key = malloc(keyLen + valueLen + 2); + memcpy(pair->key, key, keyLen); + pair->key[keyLen] = 0; + + pair->value = pair->key + keyLen + 1; + memcpy(pair->value, value, valueLen); + pair->value[valueLen] = 0; + + i->numPairs += 1; +} + +const char* +iniFile_getValue( IniFile* i, const char* key ) +{ + if (i && key) { + int nn; + + for (nn = 0; nn < i->numPairs; nn++) { + if (!strcmp(i->pairs[nn].key,key)) + return i->pairs[nn].value; + } + } + return NULL; +} + +int +iniFile_getPairCount( IniFile* i ) +{ + return i ? i->numPairs : 0; +} + +void +iniFile_getPair( IniFile* i, + int index, + const char* *pKey, + const char* *pValue ) +{ + const char* key = NULL; + const char* value = NULL; + + if (i && index >= 0 && index < i->numPairs) { + key = i->pairs[index].key; + value = i->pairs[index].value; + } + *pKey = key; + *pValue = value; +} + +/* NOTE: we avoid using functions to avoid locale-specific + * behaviour that can be the source of strange bugs. + */ + +static const char* +skipSpaces( const char* p ) +{ + while (*p == ' ' || *p == '\t') + p ++; + return p; +} + +static const char* +skipToEOL( const char* p ) +{ + while (*p && (*p != '\n' && *p != '\r')) + p ++; + + if (*p) { + p ++; + if (p[-1] == '\r' && p[0] == '\n') + p ++; + } + return p; +} + +static int +isKeyStartChar( int c ) +{ + return ((unsigned)(c-'a') < 26 || + (unsigned)(c-'A') < 26 || + c == '_'); +} + +static int +isKeyChar( int c ) +{ + return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.'); +} + +IniFile* +iniFile_newFromMemory( const char* text, const char* fileName ) +{ + const char* p = text; + IniFile* ini = iniFile_alloc(); + int lineno = 0; + + if (!fileName) + fileName = ""; + + D("%s: parsing as .ini file", fileName); + + while (*p) { + const char* key; + int keyLen; + const char* value; + int valueLen; + + lineno += 1; + + /* skip leading whitespace */ + p = skipSpaces(p); + + /* skip comments and empty lines */ + if (*p == 0 || *p == ';' || *p == '#' || *p == '\n' || *p == '\r') { + p = skipToEOL(p); + continue; + } + + /* check the key name */ + key = p++; + if (!isKeyStartChar(*key)) { + p = skipToEOL(p); + W("%s:%d: key name doesn't start with valid character. line ignored", + fileName, lineno); + continue; + } + + while (isKeyChar(*p)) + p++; + + keyLen = p - key; + p = skipSpaces(p); + + /* check the equal */ + if (*p != '=') { + W("%s:%d: missing expected assignment operator (=). line ignored", + fileName, lineno); + p = skipToEOL(p); + continue; + } + p += 1; + + /* skip spaces before the value */ + p = skipSpaces(p); + value = p; + + /* find the value */ + while (*p && (*p != '\n' && *p != '\r')) + p += 1; + + /* remove trailing spaces */ + while (p > value && (p[-1] == ' ' || p[-1] == '\t')) + p --; + + valueLen = p - value; + + iniFile_addPair(ini, key, keyLen, value, valueLen); + D("%s:%d: KEY='%.*s' VALUE='%.*s'", fileName, lineno, + keyLen, key, valueLen, value); + + p = skipToEOL(p); + } + + return ini; +} + +IniFile* +iniFile_newFromFile( const char* filepath ) +{ + FILE* fp = fopen(filepath, "rt"); + char* text; + long size; + IniFile* ini = NULL; + + if (fp == NULL) { + W("could not open .ini file: %s: %s", + filepath, strerror(errno)); + return NULL; + } + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + /* avoid reading a very large file that was passed by mistake + * this threshold is quite liberal. + */ +#define MAX_INI_FILE_SIZE 655360 + + if (size < 0 || size > MAX_INI_FILE_SIZE) { + W("hardware configuration file '%s' too large (%ld bytes)", + filepath, size); + goto EXIT; + } + + /* read the file, add a sentinel at the end of it */ + text = malloc(size+1); + fread(text, 1, size, fp); + text[size] = 0; + + ini = iniFile_newFromMemory(text, filepath); + free(text); + +EXIT: + fclose(fp); + return ini; +} + +char* +iniFile_getString( IniFile* f, const char* key ) +{ + const char* val = iniFile_getValue(f, key); + + if (!val) + return NULL; + + return qemu_strdup(val); +} + +int +iniFile_getInteger( IniFile* f, const char* key, int defaultValue ) +{ + const char* valueStr = iniFile_getValue(f, key); + int value = defaultValue; + + if (valueStr != NULL) { + char* end; + long l = strtol(valueStr, &end, 10); + if (end != NULL && end[0] == 0 && (int)l == l) + value = l; + } + return value; +} + +double +iniFile_getDouble( IniFile* f, const char* key, double defaultValue ) +{ + const char* valueStr = iniFile_getValue(f, key); + double value = defaultValue; + + if (valueStr != NULL) { + char* end; + double d = strtod(valueStr, &end); + if (end != NULL && end[0] == 0) + value = d; + } + return value; +} + +int +iniFile_getBoolean( IniFile* f, const char* key, const char* defaultValue ) +{ + const char* value = iniFile_getValue(f, key); + + if (!value) + value = defaultValue; + + if (!strcmp(value,"1") || + !strcmp(value,"yes") || + !strcmp(value,"YES") || + !strcmp(value,"true") || + !strcmp(value,"TRUE")) + { + return 1; + } + else + return 0; +} + +int64_t +iniFile_getDiskSize( IniFile* f, const char* key, const char* defaultValue ) +{ + const char* valStr = iniFile_getValue(f, key); + int64_t value = 0; + + if (!valStr) + valStr = defaultValue; + + if (valStr != NULL) { + char* end; + + value = strtoll(valStr, &end, 10); + if (*end == 'k' || *end == 'K') + value *= 1024ULL; + else if (*end == 'm' || *end == 'M') + value *= 1024*1024ULL; + else if (*end == 'g' || *end == 'G') + value *= 1024*1024*1024ULL; + } + return value; +} diff --git a/android/utils/ini.h b/android/utils/ini.h new file mode 100644 index 0000000..41b369b --- /dev/null +++ b/android/utils/ini.h @@ -0,0 +1,121 @@ +/* Copyright (C) 2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#ifndef _ANDROID_UTILS_INI_H +#define _ANDROID_UTILS_INI_H + +#include + +/* the emulator supports a simple .ini file format for its configuration + * files. Here's the BNF for it: + * + * file := * + * line := | | + * comment := (';'|'#') * + * assignment := * * '=' * * + * keyName := * + * keyNameStartChar := [A-Za-z_] + * keyNameChar := [A-Za-z0-9_.] + * valueString := * + * space := ' ' | '\t' + * LF := '\r\n' | '\n' | '\r' + * noLF := [^] + * + * Or, in English: + * + * - no support for sections + * - empty lines are ignored, as well as lines beginning with ';' or '#' + * - lines must be of the form: " = " + * - key names must start with a letter or an underscore + * - other key name characters can be letters, digits, underscores or dots + * + * - leading and trailing space are allowed and ignored before/after the key name + * and before/after the value + * + * - there is no restriction on the value, except that it can't contain + * leading/trailing space/tab characters or newline/charfeed characters + * + * - empty values are possible, and will be stored as an empty string. + * - any badly formatted line is discarded (and will print a warning) + * + */ + +/* an opaque structure used to model an .ini configuration file */ +typedef struct IniFile IniFile; + +/* creates a new IniFile object from a config file loaded in memory. + * 'fileName' is only used when writing a warning to stderr in case + * of badly formed output + */ +IniFile* iniFile_newFromMemory( const char* text, const char* fileName ); + +/* creates a new IniFile object from a file path, + * returns NULL if the file cannot be opened. + */ +IniFile* iniFile_newFromFile( const char* filePath); + +/* free an IniFile object */ +void iniFile_free( IniFile* f ); + +/* returns the number of (key.value) pairs in an IniFile */ +int iniFile_getPairCount( IniFile* f ); + +/* return a specific (key,value) pair from an IniFile. + * if the index is not correct, both '*pKey' and '*pValue' will be + * set to NULL. + * + * you should probably use iniFile_getValue() and its variants instead + */ +void iniFile_getPair( IniFile* f, + int index, + const char* *pKey, + const char* *pValue ); + +/* returns the value of a given key from an IniFile. + * NULL if the key is not assigned in the corresponding configuration file + */ +const char* iniFile_getValue( IniFile* f, const char* key ); + +/* returns a copy of the value of a given key, or NULL + */ +char* iniFile_getString( IniFile* f, const char* key ); + +/* returns an integer value, or a default in case the value string is + * missing or badly formatted + */ +int iniFile_getInteger( IniFile* f, const char* key, int defaultValue ); + +/* returns a double value, or a default in case the value string is + * missing or badly formatted + */ +double iniFile_getDouble( IniFile* f, const char* key, double defaultValue ); + +/* returns a copy of a given key's value, if any, or NULL if it is missing + * caller must call free() to release it */ +char* iniFile_getString( IniFile* f, const char* key ); + +/* parses a key value as a boolean. Accepted values are "1", "0", "yes", "YES", + * "no" and "NO". Returns either 1 or 0. + * note that the default value must be provided as a string too + */ +int iniFile_getBoolean( IniFile* f, const char* key, const char* defaultValue ); + +/* parses a key value as a disk size. this means it can be an integer followed + * by a suffix that can be one of "mMkKgG" which correspond to KiB, MiB and GiB + * multipliers. + * + * NOTE: we consider that 1K = 1024, not 1000. + */ +int64_t iniFile_getDiskSize( IniFile* f, const char* key, const char* defaultValue ); + +/* */ + +#endif /* _ANDROID_UTILS_INI_H */ diff --git a/android/vm/hardware-properties.ini b/android/vm/hardware-properties.ini new file mode 100644 index 0000000..3589e45 --- /dev/null +++ b/android/vm/hardware-properties.ini @@ -0,0 +1,158 @@ +# This file describes the properties of a given virtual device configuration file. +# +# Note: Most top-level properties are boolean that control whether a feature is +# present or not. Sub-features that depend on it are ignored if their +# parent is set to 'false' or 'no' +# +# This file is parsed by 'android/tools/gen-hw-config.py' to generate +# 'android/vm/hw-config-defs.h'. The latter is a special header containing +# macro statements that is used several times: +# +# - once to define the fields of the AndroidHwConfig structure +# (see android/vm/hw-config.h) +# +# - once to implement the hardware configuration loader +# (see android/vm/hw-config.h) +# +# Hopefully, this file should also be read by a virtual device creation +# tool/wizard to provide a nice user interface (hence the presence of +# the 'abstract' and 'description' keys which are not currently used) +# +# +# NOTE: if you remove items from this file, be sure that you do not break +# the emulator build. +# + +# Ram size +name = hw.ramSize +type = integer +default = 96 +abstract = Device ram size +description = The amount of physical RAM on the device, in megabytes. + +# Touch screen support +name = hw.touchScreen +type = boolean +default = yes +abstract = Touch-screen support +description = Whether there is a touch screen or not on the device. + +# Trackball support +name = hw.trackBall +type = boolean +default = yes +abstract = Track-ball support +description = Whether there is a trackball on the device. + +# Keyboard support (qwerty/azerty) +name = hw.keyboard +type = boolean +default = yes +abstract = Keyboard support +description = Whether the device has a QWERTY keyboard. + +# DPad keys +name = hw.dPad +type = boolean +default = yes +abstract = DPad support +description = Whether the device has DPad keys + +# GSM Modem support +name = hw.gsmModem +type = boolean +default = yes +abstract = GSM modem support +description = Whether there is a GSM modem in the device. + +# Wifi support +name = hw.wifi +type = boolean +default = no +abstract = Wifi support +description = Whether the device has a Wifi chipset. + +# Bluetooth support +name = hw.bluetooth +type = boolean +default = no +abstract = Bluetooth support +description = Whether the device has a Bluetooth chipset. + +# Camera support +name = hw.camera +type = boolean +default = no +abstract = Camera support +description = Whether the device has a camera. + +name = hw.camera.maxHorizontalPixels +type = integer +default = 640 +abstract = Maximum horizontal camera pixels + +name = hw.camera.maxVerticalPixels +type = integer +default = 480 +abstract = Maximum vertical camera pixels + +# GPS support +name = hw.gps +type = boolean +default = no +abstract = GPS support +description = Whether there is a GPS in the device. + +# Accelerometer +name = hw.accelerometer +type = boolean +default = no +abstract = Accelerometer support +description = Whether there is an accelerometer in the device. + +# Battery +name = hw.battery +type = boolean +default = yes +abstract = Battery support +description = Whether the device can run on a battery. + +# Audio input +name = hw.audioInput +type = boolean +default = yes +abstract = Audio recording support +description = Whether the device can record audio + +# Audio output +name = hw.audioOutput +type = boolean +default = yes +abstract = Audio playback support +description = Whether the device can play audio + +# Compass +name = hw.compass +type = boolean +default = no +abstract = Compass support +description = Whether there is a compass in the device. + +# SDCard support +name = hw.sdCard +type = boolean +default = yes +abstract = SD Card support +description = Whether the device supports insertion/removal of virtual SD Cards. + +# Cache partition +name = disk.cachePartition +type = boolean +default = yes +abstract = Cache partition support +description = Whether we use a /cache partition on the device. + +name = disk.cachePartition.size +type = diskSize +abstract = Cache partition size +default = 66MB diff --git a/android/vm/hw-config-defs.h b/android/vm/hw-config-defs.h new file mode 100644 index 0000000..5c3b9ab --- /dev/null +++ b/android/vm/hw-config-defs.h @@ -0,0 +1,165 @@ +/* this file is automatically generated from 'hardware-properties.ini' + * DO NOT EDIT IT. To re-generate it, use android/tools/gen-hw-config.py' + */ +#ifndef HWCFG_INT +#error HWCFG_INT not defined +#endif +#ifndef HWCFG_BOOL +#error HWCFG_BOOL not defined +#endif +#ifndef HWCFG_DISKSIZE +#error HWCFG_DISKSIZE not defined +#endif +#ifndef HWCFG_STRING +#error HWCFG_STRING not defined +#endif +#ifndef HWCFG_DOUBLE +#error HWCFG_DOUBLE not defined +#endif + +HWCFG_INT( + hw_ramSize, + "hw.ramSize", + 96, + "Device ram size", + "The amount of physical RAM on the device, in megabytes.") + +HWCFG_BOOL( + hw_touchScreen, + "hw.touchScreen", + "yes", + "Touch-screen support", + "Whether there is a touch screen or not on the device.") + +HWCFG_BOOL( + hw_trackBall, + "hw.trackBall", + "yes", + "Track-ball support", + "Whether there is a trackball on the device.") + +HWCFG_BOOL( + hw_keyboard, + "hw.keyboard", + "yes", + "Keyboard support", + "Whether the device has a QWERTY keyboard.") + +HWCFG_BOOL( + hw_dPad, + "hw.dPad", + "yes", + "DPad support", + "Whether the device has DPad keys") + +HWCFG_BOOL( + hw_gsmModem, + "hw.gsmModem", + "yes", + "GSM modem support", + "Whether there is a GSM modem in the device.") + +HWCFG_BOOL( + hw_wifi, + "hw.wifi", + "no", + "Wifi support", + "Whether the device has a Wifi chipset.") + +HWCFG_BOOL( + hw_bluetooth, + "hw.bluetooth", + "no", + "Bluetooth support", + "Whether the device has a Bluetooth chipset.") + +HWCFG_BOOL( + hw_camera, + "hw.camera", + "no", + "Camera support", + "Whether the device has a camera.") + +HWCFG_INT( + hw_camera_maxHorizontalPixels, + "hw.camera.maxHorizontalPixels", + 640, + "Maximum horizontal camera pixels", + "") + +HWCFG_INT( + hw_camera_maxVerticalPixels, + "hw.camera.maxVerticalPixels", + 480, + "Maximum vertical camera pixels", + "") + +HWCFG_BOOL( + hw_gps, + "hw.gps", + "no", + "GPS support", + "Whether there is a GPS in the device.") + +HWCFG_BOOL( + hw_accelerometer, + "hw.accelerometer", + "no", + "Accelerometer support", + "Whether there is an accelerometer in the device.") + +HWCFG_BOOL( + hw_battery, + "hw.battery", + "yes", + "Battery support", + "Whether the device can run on a battery.") + +HWCFG_BOOL( + hw_audioInput, + "hw.audioInput", + "yes", + "Audio recording support", + "Whether the device can record audio") + +HWCFG_BOOL( + hw_audioOutput, + "hw.audioOutput", + "yes", + "Audio playback support", + "Whether the device can play audio") + +HWCFG_BOOL( + hw_compass, + "hw.compass", + "no", + "Compass support", + "Whether there is a compass in the device.") + +HWCFG_BOOL( + hw_sdCard, + "hw.sdCard", + "yes", + "SD Card support", + "Whether the device supports insertion/removal of virtual SD Cards.") + +HWCFG_BOOL( + disk_cachePartition, + "disk.cachePartition", + "yes", + "Cache partition support", + "Whether we use a /cache partition on the device.") + +HWCFG_DISKSIZE( + disk_cachePartition_size, + "disk.cachePartition.size", + "66MB", + "Cache partition size", + "") + +#undef HWCFG_INT +#undef HWCFG_BOOL +#undef HWCFG_DISKSIZE +#undef HWCFG_STRING +#undef HWCFG_DOUBLE +/* end of auto-generated file */ diff --git a/android/vm/hw-config.c b/android/vm/hw-config.c new file mode 100644 index 0000000..7018d32 --- /dev/null +++ b/android/vm/hw-config.c @@ -0,0 +1,39 @@ +/* Copyright (C) 2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "android/vm/hw-config.h" +#include "android/utils/ini.h" +#include +#include + + +/* the global variable containing the hardware config for this device */ +AndroidHwConfig android_hw[1]; + +int +androidHwConfig_read( AndroidHwConfig* config, + IniFile* ini ) +{ + if (ini == NULL) + return -1; + + /* use the magic of macros to implement the hardware configuration loaded */ + +#define HWCFG_BOOL(n,s,d,a,t) config->n = iniFile_getBoolean(ini, s, d); +#define HWCFG_INT(n,s,d,a,t) config->n = iniFile_getInteger(ini, s, d); +#define HWCFG_STRING(n,s,d,a,t) config->n = iniFile_getString(ini, s, d); +#define HWCFG_DOUBLE(n,s,d,a,t) config->n = iniFile_getDouble(ini, s, d); +#define HWCFG_DISKSIZE(n,s,d,a,t) config->n = iniFile_getDiskSize(ini, s, d); + +#include "android/vm/hw-config-defs.h" + + return 0; +} diff --git a/android/vm/hw-config.h b/android/vm/hw-config.h new file mode 100644 index 0000000..0ab231d --- /dev/null +++ b/android/vm/hw-config.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#ifndef _ANDROID_VM_HW_CONFIG_H +#define _ANDROID_VM_HW_CONFIG_H + +#include +#include "android/utils/ini.h" + +typedef char hw_bool_t; +typedef int hw_int_t; +typedef int64_t hw_disksize_t; +typedef char* hw_string_t; +typedef double hw_double_t; + +/* these macros are used to define the fields of AndroidHwConfig + * declared below + */ +#define HWCFG_BOOL(n,s,d,a,t) hw_bool_t n; +#define HWCFG_INT(n,s,d,a,t) hw_int_t n; +#define HWCFG_STRING(n,s,d,a,t) hw_string_t n; +#define HWCFG_DOUBLE(n,s,d,a,t) hw_double_t n; +#define HWCFG_DISKSIZE(n,s,d,a,t) hw_disksize_t n; + +typedef struct { +#include "android/vm/hw-config-defs.h" +} AndroidHwConfig; + +/* reads a hardware configuration file from disk. + * returns -1 if the file could not be read, or 0 in case of success. + * + * note that default values are written to hwConfig if the configuration + * file doesn't have the corresponding hardware properties. + */ +int androidHwConfig_read( AndroidHwConfig* hwConfig, + IniFile* configFile ); + +#endif /* _ANDROID_VM_HW_CONFIG_H */ diff --git a/android/vm/info.c b/android/vm/info.c new file mode 100644 index 0000000..e3f04ff --- /dev/null +++ b/android/vm/info.c @@ -0,0 +1,1534 @@ +/* Copyright (C) 2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "android/vm/info.h" +#include "android_utils.h" +#include "android/utils/debug.h" +#include "android/utils/dirscanner.h" +#include "osdep.h" +#include +#include +#include +#include + +/* global variables - see android/globals.h */ +AvmInfoParams android_vmParams[1]; +AvmInfo* android_vmInfo; + +/* for debugging */ +#define D(...) VERBOSE_PRINT(init,__VA_ARGS__) +#define DD(...) VERBOSE_PRINT(vm_config,__VA_ARGS__) + +/* technical note on how all of this is supposed to work: + * + * we assume the following SDK layout: + * + * SDK/ + * tools/ + * emulator[.exe] + * libs/ + * hardware-properties.ini + * ... + * + * platforms/ + * / + * build.prop + * images/ + * + * skins/ + * default/ --> default skin + * layout + * + * / --> another skin + * layout + * + * / --> skin alias to + * alias- + * + * / + * build.prop + * images/ + * + * + * add-ons/ + * / + * manifest.ini + * images/ + * + * + * / + * manifest.ini + * + * hardware.ini + * skins/ + * default/ + * layout + * + * / + * layout + * + * + * + * we define a 'platform' as a directory that provides a complete + * set of disk/kernel images, some skins, as well as a build.prop + * file. + * + * we define an 'addon' as a directory that provides additionnal + * or replacement files related to a given existing platform. + * each add-on provides at the minimum a 'manifest.ini' file + * that describes it (see below). + * + * important notes: + * + * - the build.prop file of a given platform directory contains + * a line that reads 'ro.build.version.sdk=' where + * is an integer corresponding to the corresponding + * official API version number as defined by Android. + * + * each platform provided with the SDK must have a unique + * version number. + * + * - the manifest.ini of a given addon must contain lines + * that include: + * + * name= + * vendor= + * api= + * + * where is used to identify the platform the add-on + * refers to. Note that the platform's directory name is + * irrelevant to the matching algorithm. + * + * each addon available must have a unique + * :: triplet + * + * - an add-on can provide a hardware.ini file. If present, this + * is used to force the hardware setting of any virtual machine + * built from the add-on. + * + * - the file in SDK/tools/lib/hardware-properties.ini declares which + * hardware properties are supported by the emulator binary. + * these can appear in the config.ini file of a given virtual + * machine, or the hardware.ini of a given add-on. + * + * normally, a virtual machine corresponds to: + * + * - a root configuration file, placed in ~/.android/vm/.ini + * where is the name of the virtual machine. + * + * - a "content" directory, which contains disk images for the + * virtual machine (e.g. at a minimum, the userdata.img file) + * plus some configuration information. + * + * - the root config file must have at least two lines like: + * + * path= + * target= + * + * the 'path' value must point to the location of + * the virtual machine's content directory. By default, this + * should be ~/.android/vm//, though the user should be + * able to choose an alternative path at creation time. + * + * the 'target' value can be one of: + * + * android- + * :: + * + * the first form is used to refer to a given platform. + * the second form is used to refer to a unique add-on. + * in both forms, must be an integer that + * matches one of the available platforms. + * + * :: must match the triplet of one + * of the available add-ons + * + * if the target value is incorrect, or if the content path + * is invalid, the emulator will abort with an error. + * + * - the content directory shall contain a 'config.ini' that + * contains hardware properties for the virtual machine + * (as defined by SDK/tools/lib/hardware-properties.ini), as + * well as additional lines like: + * + * sdcard= + * skin= + * options= + * + * + * Finally, to find the skin to be used with a given virtual + * machine, the following logic is used: + * + * - if no skin name has been manually specified on + * the command line, or in the config.ini file, + * look in $CONTENT/skin/layout and use it if available. + * + * - otherwise, set SKINNAME to 'default' if not manually + * specified, and look for $ADDON/skins/$SKINNAME/layout + * and use it if available + * + * - otherwise, look for $PLATFORM/skins/$SKINNAME/layout + * and use it if available. + * + * - otherwise, look for $PLATFORM/skins/$SKINNAME/alias-. + * if a file exist by that name, look at $PLATFORM/skins//layout + * and use it if available. Aliases are not recursives :-) + */ + +/* now, things get a little bit more complicated when working + * within the Android build system. In this mode, which can be + * detected by looking at the definition of the ANDROID_PRODUCT_OUT + * environment variable, we're going to simply pick the image files + * from the out directory, or from $BUILDROOT/prebuilt + */ + +/* the name of the $SDKROOT subdirectory that contains all platforms */ +#define PLATFORMS_SUBDIR "platforms" + +/* the name of the $SDKROOT subdirectory that contains add-ons */ +#define ADDONS_SUBDIR "add-ons" + +/* this is the subdirectory of $HOME/.android where all + * root configuration files (and default content directories) + * are located. + */ +#define ANDROID_VM_DIR "vm" + +/* certain disk image files are mounted read/write by the emulator + * to ensure that several emulators referencing the same files + * do not corrupt these files, we need to lock them and respond + * to collision depending on the image type. + * + * the enumeration below is used to record information about + * each image file path. + * + * READONLY means that the file will be mounted read-only + * and this doesn't need to be locked. must be first in list + * + * MUSTLOCK means that the file should be locked before + * being mounted by the emulator + * + * TEMPORARY means that the file has been copied to a + * temporary image, which can be mounted read/write + * but doesn't require locking. + */ +typedef enum { + IMAGE_STATE_READONLY, /* unlocked */ + IMAGE_STATE_MUSTLOCK, /* must be locked */ + IMAGE_STATE_LOCKED, /* locked */ + IMAGE_STATE_LOCKED_EMPTY, /* locked and empty */ + IMAGE_STATE_TEMPORARY, /* copied to temp file (no lock needed) */ +} AvmImageState; + +struct AvmInfo { + /* for the Android build system case */ + char inAndroidBuild; + char* androidOut; + char* androidBuildRoot; + + /* for the normal virtual machine case */ + char* machineName; + char* sdkRootPath; + int platformVersion; + char* platformPath; + char* addonTarget; + char* addonPath; + char* contentPath; + IniFile* rootIni; /* root .ini file */ + IniFile* configIni; /* virtual machine's config.ini */ + + /* for both */ + char* skinName; /* skin name */ + char* skinDirPath; /* skin directory */ + + /* image files */ + char* imagePath [ AVM_IMAGE_MAX ]; + char imageState[ AVM_IMAGE_MAX ]; +}; + + +void +avmInfo_free( AvmInfo* i ) +{ + if (i) { + int nn; + + for (nn = 0; nn < AVM_IMAGE_MAX; nn++) + qemu_free(i->imagePath[nn]); + + qemu_free(i->skinName); + qemu_free(i->skinDirPath); + + if (i->configIni) { + iniFile_free(i->configIni); + i->configIni = NULL; + } + + if (i->rootIni) { + iniFile_free(i->rootIni); + i->rootIni = NULL; + } + + qemu_free(i->contentPath); + qemu_free(i->sdkRootPath); + + if (i->inAndroidBuild) { + qemu_free(i->androidOut); + qemu_free(i->androidBuildRoot); + } else { + qemu_free(i->platformPath); + qemu_free(i->addonTarget); + qemu_free(i->addonPath); + } + + qemu_free(i->machineName); + qemu_free(i); + } +} + +/* list of default file names for each supported image file type */ +static const char* const _imageFileNames[ AVM_IMAGE_MAX ] = { +#define _AVM_IMG(x,y,z) y, + AVM_IMAGE_LIST +#undef _AVM_IMG +}; + +/* list of short text description for each supported image file type */ +static const char* const _imageFileText[ AVM_IMAGE_MAX ] = { +#define _AVM_IMG(x,y,z) z, + AVM_IMAGE_LIST +#undef _AVM_IMG +}; + +/*************************************************************** + *************************************************************** + ***** + ***** NORMAL VIRTUAL MACHINE SUPPORT + ***** + *****/ + +/* compute path to the root SDK directory + * assume we are in $SDKROOT/tools/emulator[.exe] + */ +static int +_getSdkRoot( AvmInfo* i ) +{ + char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); + + (void) bufprint_app_dir(temp, end); + + i->sdkRootPath = path_parent(temp, 1); + if (i->sdkRootPath == NULL) { + derror("can't find root of SDK directory"); + return -1; + } + D("found SDK root at %s", i->sdkRootPath); + return 0; +} + +/* returns the full path of the platform subdirectory + * corresponding to a given API version + */ +static char* +_findPlatformByVersion( const char* sdkRoot, int version ) +{ + char temp[PATH_MAX], *p=temp, *end=p+sizeof temp; + char* subdir = NULL; + DirScanner* scanner; + + DD("> %s(%s,%d)", __FUNCTION__, sdkRoot, version); + p = bufprint(temp, end, "%s/%s", sdkRoot, PLATFORMS_SUBDIR); + if (p >= end) { + DD("! path too long"); + return NULL; + } + + scanner = dirScanner_new(temp); + if (scanner == NULL) { + DD("! cannot scan path %s: %s", temp, strerror(errno)); + return NULL; + } + + for (;;) { + IniFile* ini; + int apiVersion; + + subdir = (char*) dirScanner_nextFull(scanner); + if (subdir == NULL) + break; + + /* look for a file named "build.prop */ + p = bufprint(temp, end, "%s/build.prop", subdir); + if (p >= end) + continue; + + if (!path_exists(temp)) { + DD("! no file at %s", temp); + continue; + } + + ini = iniFile_newFromFile(temp); + if (ini == NULL) + continue; + + apiVersion = iniFile_getInteger(ini, "ro.build.version.sdk", -1); + iniFile_free(ini); + + DD("! found %s (version %d)", temp, apiVersion); + + if (apiVersion == version) { + /* Bingo */ + subdir = qemu_strdup(subdir); + break; + } + } + + if (!subdir) { + DD("< didn't found anything"); + } + + dirScanner_free(scanner); + return subdir; +} + +/* returns the full path of the addon corresponding to a given target, + * or NULL if not found. on success, *pversion will contain the SDK + * version number + */ +static char* +_findAddonByTarget( const char* sdkRoot, const char* target, int *pversion ) +{ + char* targetCopy = qemu_strdup(target); + char* targetVendor = NULL; + char* targetName = NULL; + int targetVersion = -1; + + char temp[PATH_MAX]; + char* p; + char* end; + DirScanner* scanner; + char* subdir; + + DD("> %s(%s,%s)", __FUNCTION__, sdkRoot, target); + + /* extract triplet from target string */ + targetVendor = targetCopy; + + p = strchr(targetVendor, ':'); + if (p == NULL) { + DD("< missing first column separator"); + goto FAIL; + } + *p = 0; + targetName = p + 1; + p = strchr(targetName, ':'); + if (p == NULL) { + DD("< missing second column separator"); + goto FAIL; + } + *p++ = 0; + + targetVersion = atoi(p); + + if (targetVersion == 0) { + DD("< invalid version number"); + goto FAIL; + } + /* now scan addons directory */ + p = temp; + end = p + sizeof temp; + + p = bufprint(p, end, "%s/%s", sdkRoot, ADDONS_SUBDIR); + if (p >= end) { + DD("< add-on path too long"); + goto FAIL; + } + scanner = dirScanner_new(temp); + if (scanner == NULL) { + DD("< cannot scan add-on path %s: %s", temp, strerror(errno)); + goto FAIL; + } + for (;;) { + IniFile* ini; + const char* vendor; + const char* name; + int version; + int matches; + + subdir = (char*) dirScanner_nextFull(scanner); + if (subdir == NULL) + break; + + /* try to open the manifest.ini file */ + p = bufprint(temp, end, "%s/manifest.ini", subdir); + if (p >= end) + continue; + + ini = iniFile_newFromFile(temp); + if (ini == NULL) + continue; + + DD("! scanning manifest.ini in %s", temp); + + /* find the vendor, name and version */ + vendor = iniFile_getValue(ini, "vendor"); + name = iniFile_getValue(ini, "name"); + version = iniFile_getInteger(ini, "api", -1); + + matches = 0; + + matches += (version == targetVersion); + matches += (vendor && !strcmp(vendor, targetVendor)); + matches += (name && !strcmp(name, targetName)); + + DD("! matches=%d vendor=[%s] name=[%s] version=%d", + matches, + vendor ? vendor : "", + name ? name : "", + version); + + iniFile_free(ini); + + if (matches == 3) { + /* bingo */ + *pversion = version; + subdir = qemu_strdup(subdir); + break; + } + } + + dirScanner_free(scanner); + + DD("< returning %s", subdir ? subdir : ""); + return subdir; + +FAIL: + qemu_free(targetCopy); + return NULL; +} + +static int +_checkAvmName( const char* name ) +{ + int len = strlen(name); + int len2 = strspn(name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789_.-"); + return (len == len2); +} + +/* parse the root config .ini file. it is located in + * ~/.android/vm/.ini or Windows equivalent + */ +static int +_getRootIni( AvmInfo* i ) +{ + char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); + + p = bufprint_config_path(temp, end); + p = bufprint(p, end, "/" ANDROID_VM_DIR "/%s.ini", i->machineName); + if (p >= end) { + derror("machine name too long"); + return -1; + } + + i->rootIni = iniFile_newFromFile(temp); + if (i->rootIni == NULL) { + derror("unknown virtual machine name: '%s'", i->machineName); + return -1; + } + D("root virtual machine file at %s", temp); + return 0; +} + +/* the .ini variable name that points to the content directory + * in a root AVM ini file. This is required */ +# define ROOT_PATH_KEY "path" +# define ROOT_TARGET_KEY "target" + +/* retrieve the content path and target from the root .ini file */ +static int +_getTarget( AvmInfo* i ) +{ + i->contentPath = iniFile_getString(i->rootIni, ROOT_PATH_KEY); + i->addonTarget = iniFile_getString(i->rootIni, ROOT_TARGET_KEY); + + iniFile_free(i->rootIni); + i->rootIni = NULL; + + if (i->contentPath == NULL) { + derror("bad config: %s", + "virtual machine file lacks a "ROOT_PATH_KEY" entry"); + return -1; + } + + if (i->addonTarget == NULL) { + derror("bad config: %s", + "virtual machine file lacks a "ROOT_TARGET_KEY" entry"); + return -1; + } + + D("virtual machine content at %s", i->contentPath); + D("virtual machine target is %s", i->addonTarget); + + if (!strncmp(i->addonTarget, "android-", 8)) { /* target is platform */ + char* end; + const char* versionString = i->addonTarget+8; + int version = (int) strtol(versionString, &end, 10); + if (*end != 0 || version <= 0) { + derror("bad config: invalid platform version: '%s'", versionString); + return -1; + } + i->platformVersion = version; + i->platformPath = _findPlatformByVersion(i->sdkRootPath, + version); + if (i->platformPath == NULL) { + derror("bad config: unknown platform version: '%d'", version); + return -1; + } + } + else /* target is add-on */ + { + i->addonPath = _findAddonByTarget(i->sdkRootPath, i->addonTarget, + &i->platformVersion); + if (i->addonPath == NULL) { + derror("bad config: %s", + "unknown add-on target: '%s'", i->addonTarget); + return -1; + } + + i->platformPath = _findPlatformByVersion(i->sdkRootPath, + i->platformVersion); + if (i->platformPath == NULL) { + derror("bad config: %s", + "unknown add-on platform version: '%d'", i->platformVersion); + return -1; + } + D("virtual machine add-on path: %s", i->addonPath); + } + D("virtual machine platform path: %s", i->platformPath); + D("virtual machine platform version %d", i->platformVersion); + return 0; +} + + +/* find and parse the config.ini file from the content directory */ +static int +_getConfigIni(AvmInfo* i) +{ + char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); + + p = bufprint(p, end, "%s/config.ini", i->contentPath); + if (p >= end) { + derror("can't access virtual machine content directory"); + return -1; + } + +#if 1 /* XXX: TODO: remove this in the future */ + /* for now, allow a non-existing config.ini */ + if (!path_exists(temp)) { + D("virtual machine has no config file - no problem"); + return 0; + } +#endif + + i->configIni = iniFile_newFromFile(temp); + if (i->configIni == NULL) { + derror("bad config: %s", + "virtual machine directory lacks config.ini"); + return -1; + } + D("virtual machine config file: %s", temp); + return 0; +} + +/*************************************************************** + *************************************************************** + ***** + ***** KERNEL/DISK IMAGE LOADER + ***** + *****/ + +/* a structure used to handle the loading of + * kernel/disk images. + */ +typedef struct { + AvmInfo* info; + AvmInfoParams* params; + AvmImageType id; + const char* imageFile; + const char* imageText; + char** pPath; + char* pState; + char temp[PATH_MAX]; +} ImageLoader; + +static void +imageLoader_init( ImageLoader* l, AvmInfo* info, AvmInfoParams* params ) +{ + memset(l, 0, sizeof(*l)); + l->info = info; + l->params = params; +} + +/* set the type of the image to load */ +static void +imageLoader_set( ImageLoader* l, AvmImageType id ) +{ + l->id = id; + l->imageFile = _imageFileNames[id]; + l->imageText = _imageFileText[id]; + l->pPath = &l->info->imagePath[id]; + l->pState = &l->info->imageState[id]; + + l->pState[0] = IMAGE_STATE_READONLY; +} + +/* change the image path */ +static char* +imageLoader_setPath( ImageLoader* l, const char* path ) +{ + path = path ? qemu_strdup(path) : NULL; + + qemu_free(l->pPath[0]); + l->pPath[0] = (char*) path; + + return (char*) path; +} + +static char* +imageLoader_extractPath( ImageLoader* l ) +{ + char* result = l->pPath[0]; + l->pPath[0] = NULL; + return result; +} + +/* flags used when loading images */ +enum { + IMAGE_REQUIRED = (1<<0), /* image is required */ + IMAGE_SEARCH_SDK = (1<<1), /* search image in SDK */ + IMAGE_EMPTY_IF_MISSING = (1<<2), /* create empty file if missing */ + IMAGE_DONT_LOCK = (1<<4), /* don't try to lock image */ + IMAGE_IGNORE_IF_LOCKED = (1<<5), /* ignore file if it's locked */ +}; + +#define IMAGE_OPTIONAL 0 + +/* find an image from the SDK add-on and/or platform + * directories. returns the full path or NULL if + * the file could not be found. + * + * note: this stores the result in the image's path as well + */ +static char* +imageLoader_lookupSdk( ImageLoader* l ) +{ + AvmInfo* i = l->info; + char* temp = l->temp, *p = temp, *end = p + sizeof(l->temp); + + do { + /* try the add-on directory, if any */ + if (i->addonPath != NULL) { + DD("searching %s in add-on directory: %s", + l->imageFile, i->addonPath); + + p = bufprint(temp, end, "%s/images/%s", + i->addonPath, l->imageFile); + + if (p < end && path_exists(temp)) + break; + } + + /* or try the platform directory */ + DD("searching %s in platform directory: %s", + l->imageFile, i->platformPath); + + p = bufprint(temp, end, "%s/images/%s", + i->platformPath, l->imageFile); + if (p < end && path_exists(temp)) + break; + + DD("could not find %s in SDK", l->imageFile); + return NULL; + + } while (0); + + l->pState[0] = IMAGE_STATE_READONLY; + + return imageLoader_setPath(l, temp); +} + +/* search for a file in the content directory. + * returns NULL if the file cannot be found. + * + * note that this formats l->temp with the file's path + * allowing you to retrieve it if the function returns NULL + */ +static char* +imageLoader_lookupContent( ImageLoader* l ) +{ + AvmInfo* i = l->info; + char* temp = l->temp, *p = temp, *end = p + sizeof(l->temp); + + DD("searching %s in content directory", l->imageFile); + p = bufprint(temp, end, "%s/%s", i->contentPath, l->imageFile); + if (p >= end) { + derror("content directory path too long"); + exit(2); + } + if (!path_exists(temp)) { + DD("not found %s in content directory", l->imageFile); + return NULL; + } + + /* assume content image files must be locked */ + l->pState[0] = IMAGE_STATE_MUSTLOCK; + + return imageLoader_setPath(l, temp); +} + +/* lock a file image depending on its state and user flags + * note that this clears l->pPath[0] if the lock could not + * be acquired and that IMAGE_IGNORE_IF_LOCKED is used. + */ +static void +imageLoader_lock( ImageLoader* l, unsigned flags ) +{ + const char* path = l->pPath[0]; + + if (flags & IMAGE_DONT_LOCK) + return; + + if (l->pState[0] != IMAGE_STATE_MUSTLOCK) + return; + + D("locking %s image at %s", l->imageText, path); + + if (filelock_create(path) != NULL) { + /* succesful lock */ + l->pState[0] = IMAGE_STATE_LOCKED; + return; + } + + if (flags & IMAGE_IGNORE_IF_LOCKED) { + dwarning("ignoring locked %s image at %s", l->imageText, path); + imageLoader_setPath(l, NULL); + return; + } + + derror("the %s image is used by another emulator. aborting", + l->imageText); + exit(2); +} + +/* make a file image empty, this may require locking */ +static void +imageLoader_empty( ImageLoader* l, unsigned flags ) +{ + const char* path; + + imageLoader_lock(l, flags); + + path = l->pPath[0]; + if (path == NULL) /* failed to lock, caller will handle it */ + return; + + if (make_empty_file(path) < 0) { + derror("could not create %s image at %s: %s", + l->imageText, path, strerror(errno)); + exit(2); + } + l->pState[0] = IMAGE_STATE_LOCKED_EMPTY; +} + + +/* copy image file from a given source + * assumes locking is needed. + */ +static void +imageLoader_copyFrom( ImageLoader* l, const char* srcPath ) +{ + const char* dstPath = NULL; + + /* find destination file */ + if (l->params) { + dstPath = l->params->forcePaths[l->id]; + } + if (!dstPath) { + imageLoader_lookupContent(l); + dstPath = l->temp; + } + + /* lock destination */ + imageLoader_setPath(l, dstPath); + l->pState[0] = IMAGE_STATE_MUSTLOCK; + imageLoader_lock(l, 0); + + /* make the copy */ + if (copy_file(dstPath, srcPath) < 0) { + derror("can't initialize %s image from SDK: %s: %s", + l->imageText, dstPath, strerror(errno)); + exit(2); + } +} + +/* this will load and eventually lock and image file, depending + * on the flags being used. on exit, this function udpates + * l->pState[0] and l->pPath[0] + * + * returns the path to the file. Note that it returns NULL + * only if the file was optional and could not be found. + * + * if the file is required and missing, the function aborts + * the program. + */ +static char* +imageLoader_load( ImageLoader* l, + unsigned flags ) +{ + const char* path = NULL; + + DD("looking for %s image (%s)", l->imageText, l->imageFile); + + /* first, check user-provided path */ + path = l->params->forcePaths[l->id]; + if (path != NULL) { + imageLoader_setPath(l, path); + if (path_exists(path)) + goto EXIT; + + D("user-provided %s image does not exist: %s", + l->imageText, path); + + /* if the file is required, abort */ + if (flags & IMAGE_REQUIRED) { + derror("user-provided %s image at %s doesn't exist", + l->imageText, path); + exit(2); + } + } + else { + const char* contentFile; + + /* second, look in the content directory */ + path = imageLoader_lookupContent(l); + if (path) goto EXIT; + + contentFile = qemu_strdup(l->temp); + + /* it's not there */ + if (flags & IMAGE_SEARCH_SDK) { + /* third, look in the SDK directory */ + path = imageLoader_lookupSdk(l); + if (path) { + qemu_free((char*)contentFile); + goto EXIT; + } + } + DD("found no %s image (%s)", l->imageText, l->imageFile); + + /* if the file is required, abort */ + if (flags & IMAGE_REQUIRED) { + derror("could not find required %s image (%s)", + l->imageText, l->imageFile); + exit(2); + } + + path = imageLoader_setPath(l, contentFile); + qemu_free((char*)contentFile); + } + + /* otherwise, do we need to create it ? */ + if (flags & IMAGE_EMPTY_IF_MISSING) { + imageLoader_empty(l, flags); + return l->pPath[0]; + } + return NULL; + +EXIT: + imageLoader_lock(l, flags); + return l->pPath[0]; +} + + + +/* find the correct path of all image files we're going to need + * and lock the files that need it. + */ +static int +_getImagePaths(AvmInfo* i, AvmInfoParams* params ) +{ + int wipeData = (params->flags & AVMINFO_WIPE_DATA) != 0; + int wipeCache = (params->flags & AVMINFO_WIPE_CACHE) != 0; + int noCache = (params->flags & AVMINFO_NO_CACHE) != 0; + int noSdCard = (params->flags & AVMINFO_NO_SDCARD) != 0; + + ImageLoader l[1]; + + imageLoader_init(l, i, params); + + /* pick up the kernel and ramdisk image files - these don't + * need a specific handling. + */ + imageLoader_set ( l, AVM_IMAGE_KERNEL ); + imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK ); + + imageLoader_set ( l, AVM_IMAGE_RAMDISK ); + imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK ); + + /* the system image + * + * if there is one in the content directory just lock + * and use it. + */ + imageLoader_set ( l, AVM_IMAGE_SYSTEM ); + imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK ); + + /* the data partition - this one is special because if it + * is missing, we need to copy the initial image file into it. + * + * first, try to see if it is in the content directory + * (or the user-provided path) + */ + imageLoader_set( l, AVM_IMAGE_USERDATA ); + if ( !imageLoader_load( l, IMAGE_OPTIONAL | + IMAGE_EMPTY_IF_MISSING | + IMAGE_DONT_LOCK ) ) + { + /* it's not, we're going to initialize it. simply + * forcing a data wipe should be enough */ + D("initializing new data partition image: %s", l->pPath[0]); + wipeData = 1; + } + + if (wipeData) { + /* find SDK source file */ + const char* srcPath; + + if (imageLoader_lookupSdk(l)) { + derror("can't locate initial %s image in SDK", + l->imageText); + exit(2); + } + srcPath = imageLoader_extractPath(l); + + imageLoader_copyFrom( l, srcPath ); + qemu_free((char*) srcPath); + } + else + { + /* lock the data partition image */ + l->pState[0] = IMAGE_STATE_MUSTLOCK; + imageLoader_lock( l, 0 ); + } + + /* the cache partition: unless the user doesn't want one, + * we're going to create it in the content directory + */ + if (!noCache) { + imageLoader_set (l, AVM_IMAGE_CACHE); + imageLoader_load(l, IMAGE_OPTIONAL | + IMAGE_EMPTY_IF_MISSING ); + + if (wipeCache) { + if (make_empty_file(l->pPath[0]) < 0) { + derror("cannot wipe %s image at %s: %s", + l->imageText, l->pPath[0], + strerror(errno)); + exit(2); + } + } + } + + /* the SD Card image. unless the user doesn't want to, we're + * going to mount it if available. Note that if the image is + * already used, we must ignore it. + */ + if (!noSdCard) { + imageLoader_set (l, AVM_IMAGE_SDCARD); + imageLoader_load(l, IMAGE_OPTIONAL | + IMAGE_IGNORE_IF_LOCKED); + + /* if the file was not found, ignore it */ + if (l->pPath[0] && !path_exists(l->pPath[0])) + { + D("ignoring non-existing %s at %s: %s", + l->imageText, l->pPath[0], strerror(errno)); + + /* if the user provided the SD Card path by hand, + * warn him. */ + if (params->forcePaths[AVM_IMAGE_SDCARD] != NULL) + dwarning("ignoring non-existing SD Card image"); + + imageLoader_setPath(l, NULL); + } + } + + return 0; +} + +/* check that there is a skin named 'skinName' listed from 'skinDirRoot' + * this returns 1 on success, 0 on failure + * on success, the 'temp' buffer will get the path of the real skin + * directory (after alias expansion, if needed) + */ +static int +_checkSkinDir( char* temp, char* end, const char* skinDirRoot, const char* skinName ) +{ + DirScanner* scanner; + char *p, *q; + + p = bufprint(temp, end, "%s/skins/%s", + skinDirRoot, skinName); + + if (p >= end || !path_exists(temp)) + return -1; + + /* first, is this a normal skin directory ? */ + q = bufprint(q, end, "/layout"); + if (q < end && path_exists(temp)) { + /* yes */ + *p = 0; + return 1; + } + + /* second, is it an alias to another skin ? */ + *p = 0; + scanner = dirScanner_new(temp); + if (scanner != NULL) { + for (;;) { + const char* file = dirScanner_next(scanner); + + if (file == NULL) + break; + + if (strncmp(file, "alias-", 6) || file[6] == 0) + continue; + + p = bufprint(temp, end, "%s/skins/%s", + skinDirRoot, file+6); + + q = bufprint(p, end, "/layout"); + if (q < end && path_exists(temp)) { + /* yes, it's an alias */ + *p = 0; + return 1; + } + } + dirScanner_free(scanner); + } + return 0; +} + +static int +_getSkin( AvmInfo* i, AvmInfoParams* params ) +{ + char* skinName; + char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); + char explicitSkin = 1; + + /* determine the skin name, the default is "default" + * unless specified by the caller or in config.ini + */ + if (params->skinName) { + skinName = qemu_strdup(params->skinName); + } else { + skinName = iniFile_getString( i->configIni, "skin" ); + if (skinName == NULL) { + skinName = qemu_strdup("default"); + explicitSkin = 0; + } + } + + i->skinName = skinName; + + /* now try to find the skin directory for that name - + * first try the content directory */ + do { + /* if there is a single 'skin' directory in + * the content directory, assume that's what the + * user wants, unless an explicit name was given + */ + if (!explicitSkin) { + p = bufprint(temp, end, "%s/skin/layout", i->contentPath); + if (p < end && path_exists(temp)) { + /* use this one - cheat a little */ + qemu_free(i->skinName); + i->skinName = qemu_strdup("skin"); + i->skinDirPath = qemu_strdup(i->contentPath); + return 0; + } + } + + /* look in content directory */ + if (_checkSkinDir(temp, end, i->contentPath, skinName)) + break; + + /* look in the add-on directory, if any */ + if (i->addonPath && + _checkSkinDir(temp, end, i->addonPath, skinName)) + break; + + /* look in the platforms directory */ + if (_checkSkinDir(temp, end, i->platformPath, skinName)) + break; + + /* didn't find it */ + if (explicitSkin) + dwarning("could not find directory for skin '%s'", skinName); + + return -1; + + } while (0); + + i->skinDirPath = qemu_strdup(temp); + return 0; +} + + +AvmInfo* +avmInfo_new( const char* name, AvmInfoParams* params ) +{ + AvmInfo* i; + + if (name == NULL) + return NULL; + + if (!_checkAvmName(name)) { + derror("virtual machine name contains invalid characters"); + exit(1); + } + + i = qemu_mallocz(sizeof *i); + i->machineName = qemu_strdup(name); + + if ( _getSdkRoot(i) < 0 || + _getRootIni(i) < 0 || + _getTarget(i) < 0 || + _getConfigIni(i) < 0 ) + goto FAIL; + + if ( _getImagePaths(i, params) < 0 || + _getSkin (i, params) < 0 ) + goto FAIL; + + return i; + +FAIL: + avmInfo_free(i); + return NULL; +} + +/*************************************************************** + *************************************************************** + ***** + ***** ANDROID BUILD SUPPORT + ***** + ***** The code below corresponds to the case where we're + ***** starting the emulator inside the Android build + ***** system. The main differences are that: + ***** + ***** - the $ANDROID_PRODUCT_OUT directory is used as the + ***** content file. + ***** + ***** - built images must not be modified by the emulator, + ***** so system.img must be copied to a temporary file + ***** and userdata.img must be copied to userdata-qemu.img + ***** if the latter doesn't exist. + ***** + ***** - the kernel and default skin directory are taken from + ***** prebuilt + ***** + ***** - there is no root .ini file, or any config.ini in + ***** the content directory, no SDK platform version + ***** and no add-on to consider. + *****/ + +/* used to fake a config.ini located in the content directory */ +static int +_getBuildConfigIni( AvmInfo* i ) +{ + /* a blank file is ok at the moment */ + i->configIni = iniFile_newFromMemory( "", 0 ); + return 0; +} + +static int +_getBuildImagePaths( AvmInfo* i, AvmInfoParams* params ) +{ + int wipeData = (params->flags & AVMINFO_WIPE_DATA) != 0; + int noCache = (params->flags & AVMINFO_NO_CACHE) != 0; + int noSdCard = (params->flags & AVMINFO_NO_SDCARD) != 0; + + char temp[PATH_MAX], *p=temp, *end=p+sizeof temp; + char* srcData; + ImageLoader l[1]; + + imageLoader_init(l, i, params); + + /** load the kernel image + **/ + + /* if it is not in the out directory, get it from prebuilt + */ + imageLoader_set ( l, AVM_IMAGE_KERNEL ); + + if ( !imageLoader_load( l, IMAGE_OPTIONAL | + IMAGE_DONT_LOCK ) ) + { +#define PREBUILT_KERNEL_PATH "prebuilt/android-arm/kernel/kernel-qemu" + p = bufprint(temp, end, "%s/%s", i->androidBuildRoot, + PREBUILT_KERNEL_PATH); + if (p >= end || !path_exists(temp)) { + derror("bad workspace: cannot find prebuilt kernel in: %s", temp); + exit(1); + } + imageLoader_setPath(l, temp); + } + + /** load the data partition. note that we use userdata-qemu.img + ** since we don't want to modify userdata.img at all + **/ + imageLoader_set ( l, AVM_IMAGE_USERDATA ); + imageLoader_load( l, IMAGE_OPTIONAL | IMAGE_DONT_LOCK ); + + /* get the path of the source file, and check that it actually exists + * if the user didn't provide an explicit data file + */ + srcData = imageLoader_extractPath(l); + if (srcData == NULL && params->forcePaths[AVM_IMAGE_USERDATA] == NULL) { + derror("There is no %s image in your build directory. Please make a full build", + l->imageText, l->imageFile); + exit(2); + } + + /* get the path of the target file */ + l->imageFile = "userdata-qemu.img"; + imageLoader_load( l, IMAGE_OPTIONAL | + IMAGE_EMPTY_IF_MISSING | + IMAGE_IGNORE_IF_LOCKED ); + + /* force a data wipe if we just created the image */ + if (l->pState[0] == IMAGE_STATE_LOCKED_EMPTY) + wipeData = 1; + + /* if the image was already locked, create a temp file + * then force a data wipe. + */ + if (l->pPath[0] == NULL) { + TempFile* temp = tempfile_create(); + imageLoader_setPath(l, tempfile_path(temp)); + dwarning( "Another emulator is running. user data changes will *NOT* be saved"); + wipeData = 1; + } + + /* in the case of a data wipe, copy userdata.img into + * the destination */ + if (wipeData) { + if (srcData == NULL || !path_exists(srcData)) { + derror("There is no %s image in your build directory. Please make a full build", + l->imageText, _imageFileNames[l->id]); + exit(2); + } + if (copy_file( l->pPath[0], srcData ) < 0) { + derror("could not initialize %s image from %s: %s", + l->imageText, temp, strerror(errno)); + exit(2); + } + } + + qemu_free(srcData); + + /** load the ramdisk image + **/ + imageLoader_set ( l, AVM_IMAGE_RAMDISK ); + imageLoader_load( l, IMAGE_REQUIRED | + IMAGE_DONT_LOCK ); + + /** load the system image. read-only. the caller must + ** take care of checking the state + **/ + imageLoader_set ( l, AVM_IMAGE_SYSTEM ); + imageLoader_load( l, IMAGE_REQUIRED | IMAGE_DONT_LOCK ); + + /* force the system image to read-only status */ + l->pState[0] = IMAGE_STATE_READONLY; + + /** cache partition handling + **/ + if (!noCache) { + imageLoader_set (l, AVM_IMAGE_CACHE); + + /* if the user provided one cache image, lock & use it */ + if ( params->forcePaths[l->id] != NULL ) { + imageLoader_load(l, IMAGE_REQUIRED | + IMAGE_IGNORE_IF_LOCKED); + } + } + + /** SD Card image + **/ + if (!noSdCard) { + imageLoader_set (l, AVM_IMAGE_SDCARD); + imageLoader_load(l, IMAGE_OPTIONAL | IMAGE_IGNORE_IF_LOCKED); + } + + return 0; +} + +static int +_getBuildSkin( AvmInfo* i, AvmInfoParams* params ) +{ + /* the (current) default skin name for our build system */ + const char* skinName = params->skinName; + const char* skinDir = params->skinRootPath; + char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); + char* q; + + if (!skinName) { + /* the (current) default skin name for the build system */ + skinName = "HVGA"; + DD("selecting default skin name '%s'", skinName); + } + + i->skinName = qemu_strdup(skinName); + + if (!skinDir) { + +#define PREBUILT_SKINS_DIR "development/emulator/skins" + + /* the (current) default skin directory */ + p = bufprint( temp, end, "%s/%s", + i->androidBuildRoot, PREBUILT_SKINS_DIR ); + } else { + p = bufprint( temp, end, "%s", skinDir ); + } + + q = bufprint(p, end, "/%s/layout", skinName); + if (q >= end || !path_exists(temp)) { + if (skinDir) + dwarning("could not find valid skin '%s' in %s:\n", + skinName, skinDir); + else + DD("could not find directory for skin '%s'", + skinName); + return -1; + } + *p = 0; + DD("found skin path: %s", temp); + i->skinDirPath = qemu_strdup(temp); + + return 0; +} + +AvmInfo* +avmInfo_newForAndroidBuild( const char* androidBuildRoot, + const char* androidOut, + AvmInfoParams* params ) +{ + AvmInfo* i; + + i = qemu_mallocz(sizeof *i); + + i->inAndroidBuild = 1; + i->androidBuildRoot = qemu_strdup(androidBuildRoot); + i->androidOut = qemu_strdup(androidOut); + i->contentPath = qemu_strdup(androidOut); + + /* TODO: find a way to provide better information from the build files */ + i->machineName = qemu_strdup(""); + + if (_getBuildConfigIni(i) < 0 || + _getBuildImagePaths(i, params) < 0 ) + goto FAIL; + + /* we don't need to fail if there is no valid skin */ + _getBuildSkin(i, params); + + return i; + +FAIL: + avmInfo_free(i); + return NULL; +} + +const char* +avmInfo_getName( AvmInfo* i ) +{ + return i ? i->machineName : NULL; +} + +const char* +avmInfo_getImageFile( AvmInfo* i, AvmImageType imageType ) +{ + if (i == NULL || (unsigned)imageType >= AVM_IMAGE_MAX) + return NULL; + + return i->imagePath[imageType]; +} + +int +avmInfo_isImageReadOnly( AvmInfo* i, AvmImageType imageType ) +{ + if (i == NULL || (unsigned)imageType >= AVM_IMAGE_MAX) + return 1; + + return (i->imageState[imageType] == IMAGE_STATE_READONLY); +} + +const char* +avmInfo_getSkinName( AvmInfo* i ) +{ + return i->skinName; +} + +const char* +avmInfo_getSkinDir ( AvmInfo* i ) +{ + return i->skinDirPath; +} + +int +avmInfo_getHwConfig( AvmInfo* i, AndroidHwConfig* hw ) +{ + IniFile* ini = i->configIni; + int ret; + + if (ini == NULL) + ini = iniFile_newFromMemory("", 0); + + ret = androidHwConfig_read(hw, ini); + + if (ini != i->configIni) + iniFile_free(ini); + + return ret; +} + + +char* +avmInfo_getTracePath( AvmInfo* i, const char* traceName ) +{ + char tmp[MAX_PATH], *p=tmp, *end=p + sizeof(tmp); + + if (i == NULL || traceName == NULL || traceName[0] == 0) + return NULL; + + if (i->inAndroidBuild) { + p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s", + i->androidOut, traceName ); + } else { + p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s", + i->contentPath, traceName ); + } + return qemu_strdup(tmp); +} diff --git a/android/vm/info.h b/android/vm/info.h new file mode 100644 index 0000000..cdad34f --- /dev/null +++ b/android/vm/info.h @@ -0,0 +1,161 @@ +/* Copyright (C) 2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#ifndef ANDROID_VM_INFO_H +#define ANDROID_VM_INFO_H + +#include "android/utils/ini.h" +#include "android/vm/hw-config.h" + +/* An Android Virtual Machine (AVM for short) corresponds to a + * directory containing all kernel/disk images for a given virtual + * device, as well as information about its hardware capabilities, + * SDK version number, skin, etc... + * + * Each AVM has a human-readable name and is backed by a root + * configuration file and a content directory. For example, an + * AVM named 'foo' will correspond to the following: + * + * - a root configuration file named ~/.android/vm/foo.ini + * describing where the AVM's content can be found + * + * - a content directory like ~/.android/vm/foo/ containing all + * disk image and configuration files for the virtual device. + * + * the 'foo.ini' file should contain at least one line of the form: + * + * rootPath= + * + * it may also contain other lines that cache stuff found in the + * content directory, like hardware properties or SDK version number. + * + * it is possible to move the content directory by updating the foo.ini + * file to point to the new location. This can be interesting when your + * $HOME directory is located on a network share or in a roaming profile + * (Windows), given that the content directory of a single virtual device + * can easily use more than 100MB of data. + * + */ + +/* a macro used to define the list of disk images managed by the + * implementation. This macro will be expanded several times with + * varying definitions of _AVM_IMG + */ +#define AVM_IMAGE_LIST \ + _AVM_IMG(KERNEL,"kernel-qemu","kernel") \ + _AVM_IMG(RAMDISK,"ramdisk.img","ramdisk") \ + _AVM_IMG(SYSTEM,"system.img","system") \ + _AVM_IMG(USERDATA,"userdata.img","user data") \ + _AVM_IMG(CACHE,"cache.img","cache") \ + _AVM_IMG(SDCARD,"sdcard.img","SD Card") \ + +/* define the enumared values corresponding to each AVM image type + * examples are: AVM_IMAGE_KERNEL, AVM_IMAGE_SYSTEM, etc.. + */ +#define _AVM_IMG(x,y,z) AVM_IMAGE_##x , +typedef enum { + AVM_IMAGE_LIST + AVM_IMAGE_MAX /* do not remove */ +} AvmImageType; +#undef _AVM_IMG + +/* AvmInfo is an opaque structure used to model the information + * corresponding to a given AVM instance + */ +typedef struct AvmInfo AvmInfo; + +/* various flags used when creating an AvmInfo object */ +typedef enum { + /* use to force a data wipe */ + AVMINFO_WIPE_DATA = (1 << 0), + /* use to ignore the cache partition */ + AVMINFO_NO_CACHE = (1 << 1), + /* use to wipe cache partition, ignored if NO_CACHE is set */ + AVMINFO_WIPE_CACHE = (1 << 2), + /* use to ignore ignore SDCard image (default or provided) */ + AVMINFO_NO_SDCARD = (1 << 3), +} AvmFlags; + +typedef struct { + unsigned flags; + const char* skinName; + const char* skinRootPath; + const char* forcePaths[AVM_IMAGE_MAX]; +} AvmInfoParams; + +/* Creates a new AvmInfo object from a name. Returns NULL if name is NULL + * or contains characters that are not part of the following list: + * letters, digits, underscores, dashes and periods + */ +AvmInfo* avmInfo_new( const char* name, AvmInfoParams* params ); + +/* A special function used to setup an AvmInfo for use when starting + * the emulator from the Android build system. In this specific instance + * we're going to create temporary files to hold all writable image + * files, and activate all hardware features by default + * + * 'androidBuildRoot' must be the absolute path to the root of the + * Android build system (i.e. the 'android' directory) + * + * 'androidOut' must be the target-specific out directory where + * disk images will be looked for. + */ +AvmInfo* avmInfo_newForAndroidBuild( const char* androidBuildRoot, + const char* androidOut, + AvmInfoParams* params ); + +/* Frees an AvmInfo object and the corresponding strings that may be + * returned by its getXXX() methods + */ +void avmInfo_free( AvmInfo* i ); + +/* Return the name of the Android Virtual Machine + */ +const char* avmInfo_getName( AvmInfo* i ); + +/* Try to find the path of a given image file, returns NULL + * if the corresponding file could not be found. the string + * belongs to the AvmInfo object. + */ +const char* avmInfo_getImageFile( AvmInfo* i, AvmImageType imageType ); + +/* Returns 1 if the corresponding image file is read-only + */ +int avmInfo_isImageReadOnly( AvmInfo* i, AvmImageType imageType ); + +/* lock an image file if it is writable. returns 0 on success, or -1 + * otherwise. note that if the file is read-only, it doesn't need to + * be locked and the function will return success. + */ +int avmInfo_lockImageFile( AvmInfo* i, AvmImageType imageType, int abortOnError); + +/* Manually set the path of a given image file. */ +void avmInfo_setImageFile( AvmInfo* i, AvmImageType imageType, const char* imagePath ); + +/* Returns the path of the skin directory */ +/* the string belongs to the AvmInfo object */ +const char* avmInfo_getSkinPath( AvmInfo* i ); + +/* Returns the name of the virtual machine's skin */ +const char* avmInfo_getSkinName( AvmInfo* i ); + +/* Returns the root skin directory for this machine */ +const char* avmInfo_getSkinDir ( AvmInfo* i ); + +/* Reads the AVM's hardware configuration into 'hw'. returns -1 on error, 0 otherwise */ +int avmInfo_getHwConfig( AvmInfo* i, AndroidHwConfig* hw ); + +/* Returns a *copy* of the path used to store trace 'foo'. result must be freed by caller */ +char* avmInfo_getTracePath( AvmInfo* i, const char* traceName ); + +/* */ + +#endif /* ANDROID_VM_INFO_H */ -- cgit v1.1