Merge "GLES2Debugger: Make command exchange async to improve performance."
diff --git a/build/sdk.atree b/build/sdk.atree
index 1fb7dad..d098400 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -47,7 +47,6 @@
bin/dexdump platform-tools/dexdump
framework/dx.jar platform-tools/lib/dx.jar
-sdk/files/sdk_files_NOTICE.txt platform-tools/NOTICE.txt
development/sdk/plat_tools_source.properties platform-tools/source.properties
##############################################################################
@@ -55,10 +54,10 @@
##############################################################################
# version files for the SDK updater, from sdk.git
-development/sdk/platform_source.properties platforms/${PLATFORM_NAME}/source.properties
+development/sdk/platform_source.properties platforms/${PLATFORM_NAME}/source.properties
# copy build prop from out/.../sdk/
-sdk/sdk-build.prop platforms/${PLATFORM_NAME}/build.prop
+sdk/sdk-build.prop platforms/${PLATFORM_NAME}/build.prop
# the uper-jar file that apps link against. This is the public API
${OUT_DIR}/target/common/obj/PACKAGING/android_jar_intermediates/android.jar platforms/${PLATFORM_NAME}/android.jar
@@ -155,6 +154,7 @@
development/samples/AccessibilityService samples/${PLATFORM_NAME}/AccessibilityService
development/samples/AccelerometerPlay samples/${PLATFORM_NAME}/AccelerometerPlay
development/samples/ApiDemos samples/${PLATFORM_NAME}/ApiDemos
+${OUT_DIR}/target/common/obj/PACKAGING/android-support-v4_intermediates/android-support-v4.jar samples/${PLATFORM_NAME}/ApiDemos/libs/android-support-v4.jar
development/samples/BackupRestore samples/${PLATFORM_NAME}/BackupRestore
development/samples/BasicGLSurfaceView samples/${PLATFORM_NAME}/BasicGLSurfaceView
development/samples/BluetoothChat samples/${PLATFORM_NAME}/BluetoothChat
@@ -205,6 +205,7 @@
development/sdk/compatibility_source.properties extras/android/compatibility/source.properties
development/sdk/compatibility_README.txt extras/android/compatibility/README.txt
+sdk/files/sdk_files_NOTICE.txt extras/android/compatibility/NOTICE.txt
${OUT_DIR}/target/common/obj/PACKAGING/android-support-v4_intermediates/android-support-v4.jar extras/android/compatibility/v4/android-support-v4.jar
frameworks/support/v4 extras/android/compatibility/v4/src
development/samples/ApiDemos extras/android/compatibility/v4/samples/ApiDemos
diff --git a/build/tools/windows_sdk.mk b/build/tools/windows_sdk.mk
index b7cf78d..8421a91 100644
--- a/build/tools/windows_sdk.mk
+++ b/build/tools/windows_sdk.mk
@@ -73,7 +73,7 @@
winsdk-tools: $(WIN_BUILD_PREREQ)
$(call winsdk-banner,Build Windows Tools)
- $(hide) USE_MINGW=1 $(MAKE) PRODUCT-$(TARGET_PRODUCT)-$(strip $(WIN_TARGETS)) $(if $(hide),,showcommands)
+ $(hide) USE_MINGW=1 USE_CCACHE="" $(MAKE) PRODUCT-$(TARGET_PRODUCT)-$(strip $(WIN_TARGETS)) $(if $(hide),,showcommands)
$(WIN_SDK_ZIP): winsdk-tools sdk
$(call winsdk-banner,Build $(WIN_SDK_NAME))
diff --git a/ndk/platforms/android-3/include/stdint.h b/ndk/platforms/android-3/include/stdint.h
index 237baa2..e791475 100644
--- a/ndk/platforms/android-3/include/stdint.h
+++ b/ndk/platforms/android-3/include/stdint.h
@@ -41,11 +41,6 @@
# define __STDINT_MACROS
#endif
-/* the definitions of STDINT_LIMITS depend on those of STDINT_MACROS */
-#if defined __STDINT_LIMITS && !defined __STDINT_MACROS
-# define __STDINT_MACROS
-#endif
-
#if !defined __STRICT_ANSI__ || __STDC_VERSION__ >= 199901L
# define __STDC_INT64__
#endif
@@ -185,13 +180,14 @@
# define UINT_FAST64_MAX UINT64_MAX
#endif
+#define __INT64_C(c) c ## LL
+#define __UINT64_C(c) c ## ULL
+
#ifdef __STDINT_MACROS
-# define __INT64_C(c) c ## LL
# define INT64_C(c) __INT64_C(c)
# define INT_LEAST64_C(c) INT64_C(c)
# define INT_FAST64_C(c) INT64_C(c)
-# define __UINT64_C(c) c ## ULL
# define UINT64_C(c) __UINT64_C(c)
# define UINT_LEAST64_C(c) UINT64_C(c)
# define UINT_FAST64_C(c) UINT64_C(c)
@@ -211,14 +207,20 @@
typedef int intptr_t;
typedef unsigned int uintptr_t;
+#ifdef __STDINT_LIMITS
# define INTPTR_MIN INT32_MIN
# define INTPTR_MAX INT32_MAX
# define UINTPTR_MAX UINT32_MAX
+# define PTRDIFF_MIN INT32_MIN
+# define PTRDIFF_MAX INT32_MAX
+#endif
+
+#ifdef __STDINT_MACROS
# define INTPTR_C(c) INT32_C(c)
# define UINTPTR_C(c) UINT32_C(c)
# define PTRDIFF_C(c) INT32_C(c)
-# define PTRDIFF_MIN INT32_MIN
-# define PTRDIFF_MAX INT32_MAX
+#endif
+
/*
@@ -230,24 +232,32 @@
typedef uint64_t uintmax_t;
typedef int64_t intmax_t;
-#define INTMAX_MIN INT64_MIN
-#define INTMAX_MAX INT64_MAX
-#define UINTMAX_MAX UINT64_MAX
+#ifdef __STDINT_LIMITS
+# define INTMAX_MIN INT64_MIN
+# define INTMAX_MAX INT64_MAX
+# define UINTMAX_MAX UINT64_MAX
+#endif
-#define INTMAX_C(c) INT64_C(c)
-#define UINTMAX_C(c) UINT64_C(c)
+#ifdef __STDINT_MACROS
+# define INTMAX_C(c) INT64_C(c)
+# define UINTMAX_C(c) UINT64_C(c)
+#endif
#else /* !__STDC_INT64__ */
typedef uint32_t uintmax_t;
typedef int32_t intmax_t;
-#define INTMAX_MIN INT32_MIN
-#define INTMAX_MAX INT32_MAX
-#define UINTMAX_MAX UINT32_MAX
+#ifdef __STDINT_LIMITS
+# define INTMAX_MIN INT32_MIN
+# define INTMAX_MAX INT32_MAX
+# define UINTMAX_MAX UINT32_MAX
+#endif
-#define INTMAX_C(c) INT32_C(c)
-#define UINTMAX_C(c) UINT32_C(c)
+#ifdef __STDINT_MACROS
+# define INTMAX_C(c) INT32_C(c)
+# define UINTMAX_C(c) UINT32_C(c)
+#endif
#endif /* !__STDC_INT64__ */
diff --git a/ndk/platforms/android-3/include/unistd.h b/ndk/platforms/android-3/include/unistd.h
index 6d0515a..25fc334 100644
--- a/ndk/platforms/android-3/include/unistd.h
+++ b/ndk/platforms/android-3/include/unistd.h
@@ -126,7 +126,7 @@
extern ssize_t read(int, void *, size_t);
extern ssize_t write(int, const void *, size_t);
extern ssize_t pread(int, void *, size_t, off_t);
-extern ssize_t pwrite(int, void *, size_t, off_t);
+extern ssize_t pwrite(int, const void *, size_t, off_t);
extern int dup(int);
extern int dup2(int, int);
diff --git a/ndk/platforms/android-8/include/unistd.h b/ndk/platforms/android-8/include/unistd.h
index d64c971..863d56d 100644
--- a/ndk/platforms/android-8/include/unistd.h
+++ b/ndk/platforms/android-8/include/unistd.h
@@ -130,7 +130,7 @@
extern ssize_t read(int, void *, size_t);
extern ssize_t write(int, const void *, size_t);
extern ssize_t pread(int, void *, size_t, off_t);
-extern ssize_t pwrite(int, void *, size_t, off_t);
+extern ssize_t pwrite(int, const void *, size_t, off_t);
extern int dup(int);
extern int dup2(int, int);
diff --git a/ndk/platforms/android-9/include/pthread.h b/ndk/platforms/android-9/include/pthread.h
index 1e80b12..5e87043 100644
--- a/ndk/platforms/android-9/include/pthread.h
+++ b/ndk/platforms/android-9/include/pthread.h
@@ -235,7 +235,7 @@
void* reserved[4]; /* for future extensibility */
} pthread_rwlock_t;
-#define PTHREAD_RWLOCK_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, NULL, 0, 0 }
+#define PTHREAD_RWLOCK_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0, 0, 0, 0, { NULL, NULL, NULL, NULL } }
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
diff --git a/ndk/platforms/android-9/include/unistd.h b/ndk/platforms/android-9/include/unistd.h
index 29154a2..21154ad 100644
--- a/ndk/platforms/android-9/include/unistd.h
+++ b/ndk/platforms/android-9/include/unistd.h
@@ -133,7 +133,7 @@
extern ssize_t read(int, void *, size_t);
extern ssize_t write(int, const void *, size_t);
extern ssize_t pread(int, void *, size_t, off_t);
-extern ssize_t pwrite(int, void *, size_t, off_t);
+extern ssize_t pwrite(int, const void *, size_t, off_t);
extern int dup(int);
extern int dup2(int, int);
diff --git a/ndk/platforms/android-9/samples/native-activity/Android.mk b/ndk/platforms/android-9/samples/native-activity/Android.mk
index ea961ca..73b3d87 100644
--- a/ndk/platforms/android-9/samples/native-activity/Android.mk
+++ b/ndk/platforms/android-9/samples/native-activity/Android.mk
@@ -44,7 +44,7 @@
LOCAL_SHARED_LIBRARIES := liblog libandroid libEGL libGLESv1_CM
-LOCAL_PRELINK_MODULE := false
+
LOCAL_MODULE := libnative-activity
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index c3d66df..9e0e857 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -368,7 +368,7 @@
<!-- Fragment Support Samples -->
- <activity android:name=".app.FragmentAlertDialogSupport"
+ <activity android:name=".support.app.FragmentAlertDialogSupport"
android:label="@string/fragment_alert_dialog_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -376,7 +376,15 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentHideShowSupport"
+ <activity android:name=".support.app.FragmentArgumentsSupport"
+ android:label="@string/fragment_arguments_support">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".support.app.FragmentHideShowSupport"
android:label="@string/fragment_hide_show_support"
android:windowSoftInputMode="stateUnchanged">
<intent-filter>
@@ -385,7 +393,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentContextMenuSupport"
+ <activity android:name=".support.app.FragmentContextMenuSupport"
android:label="@string/fragment_context_menu_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -393,7 +401,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentDialogSupport"
+ <activity android:name=".support.app.FragmentDialogSupport"
android:label="@string/fragment_dialog_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -401,7 +409,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentDialogOrActivitySupport"
+ <activity android:name=".support.app.FragmentDialogOrActivitySupport"
android:label="@string/fragment_dialog_or_activity_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -409,7 +417,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentLayoutSupport"
+ <activity android:name=".support.app.FragmentLayoutSupport"
android:label="@string/fragment_layout_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -417,7 +425,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentListCursorLoaderSupport"
+ <activity android:name=".support.app.FragmentListCursorLoaderSupport"
android:label="@string/fragment_list_cursor_loader_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -425,7 +433,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentListArraySupport"
+ <activity android:name=".support.app.FragmentListArraySupport"
android:label="@string/fragment_list_array_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -433,9 +441,9 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentLayoutSupport$DetailsActivity" />
+ <activity android:name=".support.app.FragmentLayoutSupport$DetailsActivity" />
- <activity android:name=".app.FragmentMenuSupport"
+ <activity android:name=".support.app.FragmentMenuSupport"
android:label="@string/fragment_menu_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -443,7 +451,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentRetainInstanceSupport"
+ <activity android:name=".support.app.FragmentRetainInstanceSupport"
android:label="@string/fragment_retain_instance_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -451,7 +459,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentReceiveResultSupport"
+ <activity android:name=".support.app.FragmentReceiveResultSupport"
android:label="@string/fragment_receive_result_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -459,7 +467,7 @@
</intent-filter>
</activity>
- <activity android:name=".app.FragmentStackSupport"
+ <activity android:name=".support.app.FragmentStackSupport"
android:label="@string/fragment_stack_support">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -483,6 +491,18 @@
android:enabled="@bool/atLeastHoneycomb" />
<!-- END_INCLUDE(loader_throttle) -->
+ <!-- Fragment Support Samples -->
+
+ <activity android:name=".support.app.LoaderThrottleSupport"
+ android:label="@string/loader_throttle_support">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+ <provider android:name=".support.app.LoaderThrottleSupport$SimpleProvider"
+ android:authorities="com.example.android.apis.support.app.LoaderThrottle" />
+
<!-- Intent Samples -->
<activity android:name=".app.Intents" android:label="@string/activity_intents">
diff --git a/samples/ApiDemos/_index.html b/samples/ApiDemos/_index.html
index 399ef86..ffe5db5 100644
--- a/samples/ApiDemos/_index.html
+++ b/samples/ApiDemos/_index.html
@@ -37,7 +37,8 @@
" <li><a href='src/com/example/android/apis/media/index.html'>Media</a></li>"+
" <li><a href='src/com/example/android/apis/os/index.html'>OS</a></li>"+
" <li><a href='src/com/example/android/apis/text/index.html'>Text</a></li>"+
-" <li><a href='src/com/example/android/apis/view/index.html'>Views</a></li></ul>");
+" <li><a href='src/com/example/android/apis/view/index.html'>Views</a></li></ul>"+
+" <li><a href='src/com/example/android/apis/support/index.html'>Static Support Library</a></li>");
}
diff --git a/samples/ApiDemos/res/layout-land/fragment_arguments_support.xml b/samples/ApiDemos/res/layout-land/fragment_arguments_support.xml
new file mode 100644
index 0000000..9aa8daf
--- /dev/null
+++ b/samples/ApiDemos/res/layout-land/fragment_arguments_support.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Top-level content view for the simple fragment sample. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:padding="4dip"
+ android:gravity="center_horizontal"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:padding="4dip"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:gravity="top|center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/fragment_arguments_msg" />
+
+ <LinearLayout android:orientation="horizontal" android:padding="4dip"
+ android:layout_width="match_parent" android:layout_height="wrap_content">
+
+ <fragment class="com.example.android.apis.support.app.FragmentArgumentsSupport$MyFragment"
+ android:id="@+id/embedded"
+ android:layout_width="0px" android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:label="@string/fragment_arguments_embedded" />
+
+ <FrameLayout
+ android:id="@+id/created"
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ <fragment class="com.example.android.apis.support.app.FragmentArgumentsSupport$MyFragment"
+ android:id="@+id/embedded_land"
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:label="@string/fragment_arguments_embedded_land" />
+
+</LinearLayout>
diff --git a/samples/ApiDemos/res/layout-land/fragment_layout_support.xml b/samples/ApiDemos/res/layout-land/fragment_layout_support.xml
index 383e41c..de31341 100644
--- a/samples/ApiDemos/res/layout-land/fragment_layout_support.xml
+++ b/samples/ApiDemos/res/layout-land/fragment_layout_support.xml
@@ -22,7 +22,7 @@
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
- <fragment class="com.example.android.apis.app.FragmentLayoutSupport$TitlesFragment"
+ <fragment class="com.example.android.apis.support.app.FragmentLayoutSupport$TitlesFragment"
android:id="@+id/titles" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
diff --git a/samples/ApiDemos/res/layout/fragment_arguments_support.xml b/samples/ApiDemos/res/layout/fragment_arguments_support.xml
new file mode 100644
index 0000000..b218f82
--- /dev/null
+++ b/samples/ApiDemos/res/layout/fragment_arguments_support.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- Top-level content view for the simple fragment sample. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:padding="4dip"
+ android:gravity="center_horizontal"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:padding="4dip"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:gravity="top|center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/fragment_arguments_msg" />
+
+ <LinearLayout android:orientation="horizontal" android:padding="4dip"
+ android:layout_width="match_parent" android:layout_height="wrap_content">
+
+<!-- BEGIN_INCLUDE(from_attributes) -->
+ <fragment class="com.example.android.apis.support.app.FragmentArgumentsSupport$MyFragment"
+ android:id="@+id/embedded"
+ android:layout_width="0px" android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:label="@string/fragment_arguments_embedded" />
+<!-- END_INCLUDE(from_attributes) -->
+
+ <FrameLayout
+ android:id="@+id/created"
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/samples/ApiDemos/res/layout/fragment_hide_show_support.xml b/samples/ApiDemos/res/layout/fragment_hide_show_support.xml
index dc87ba3..e4245bd 100644
--- a/samples/ApiDemos/res/layout/fragment_hide_show_support.xml
+++ b/samples/ApiDemos/res/layout/fragment_hide_show_support.xml
@@ -34,7 +34,7 @@
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Hide" />
- <fragment android:name="com.example.android.apis.app.FragmentHideShowSupport$FirstFragment"
+ <fragment android:name="com.example.android.apis.support.app.FragmentHideShowSupport$FirstFragment"
android:id="@+id/fragment1" android:layout_weight="1"
android:layout_width="0px" android:layout_height="wrap_content" />
@@ -48,7 +48,7 @@
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Hide" />
- <fragment android:name="com.example.android.apis.app.FragmentHideShowSupport$SecondFragment"
+ <fragment android:name="com.example.android.apis.support.app.FragmentHideShowSupport$SecondFragment"
android:id="@+id/fragment2" android:layout_weight="1"
android:layout_width="0px" android:layout_height="wrap_content" />
diff --git a/samples/ApiDemos/res/layout/fragment_layout_support.xml b/samples/ApiDemos/res/layout/fragment_layout_support.xml
index 1d974dd..08c40bf 100644
--- a/samples/ApiDemos/res/layout/fragment_layout_support.xml
+++ b/samples/ApiDemos/res/layout/fragment_layout_support.xml
@@ -20,7 +20,7 @@
<!-- BEGIN_INCLUDE(layout) -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
- <fragment class="com.example.android.apis.app.FragmentLayoutSupport$TitlesFragment"
+ <fragment class="com.example.android.apis.support.app.FragmentLayoutSupport$TitlesFragment"
android:id="@+id/titles"
android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>
diff --git a/samples/ApiDemos/res/values-large/strings.xml b/samples/ApiDemos/res/values-hdpi/strings.xml
similarity index 92%
copy from samples/ApiDemos/res/values-large/strings.xml
copy to samples/ApiDemos/res/values-hdpi/strings.xml
index c9fb189..f2b46d4 100644
--- a/samples/ApiDemos/res/values-large/strings.xml
+++ b/samples/ApiDemos/res/values-hdpi/strings.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <string name="density_title">Density: Large</string>
+ <string name="density_title">Density: High</string>
</resources>
diff --git a/samples/ApiDemos/res/values-large-long/strings.xml b/samples/ApiDemos/res/values-large-long/strings.xml
deleted file mode 100644
index 7c392d8..0000000
--- a/samples/ApiDemos/res/values-large-long/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Large Long</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-large-notlong/strings.xml b/samples/ApiDemos/res/values-large-notlong/strings.xml
deleted file mode 100644
index 40328ea..0000000
--- a/samples/ApiDemos/res/values-large-notlong/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Large NotLong</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-large/strings.xml b/samples/ApiDemos/res/values-ldpi/strings.xml
similarity index 92%
copy from samples/ApiDemos/res/values-large/strings.xml
copy to samples/ApiDemos/res/values-ldpi/strings.xml
index c9fb189..ebbcc38 100644
--- a/samples/ApiDemos/res/values-large/strings.xml
+++ b/samples/ApiDemos/res/values-ldpi/strings.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <string name="density_title">Density: Large</string>
+ <string name="density_title">Density: Low</string>
</resources>
diff --git a/samples/ApiDemos/res/values-long/strings.xml b/samples/ApiDemos/res/values-long/strings.xml
deleted file mode 100644
index 5d15048..0000000
--- a/samples/ApiDemos/res/values-long/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Long</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-large/strings.xml b/samples/ApiDemos/res/values-mdpi/strings.xml
similarity index 92%
rename from samples/ApiDemos/res/values-large/strings.xml
rename to samples/ApiDemos/res/values-mdpi/strings.xml
index c9fb189..3034171 100644
--- a/samples/ApiDemos/res/values-large/strings.xml
+++ b/samples/ApiDemos/res/values-mdpi/strings.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <string name="density_title">Density: Large</string>
+ <string name="density_title">Density: Medium</string>
</resources>
diff --git a/samples/ApiDemos/res/values-normal-long/strings.xml b/samples/ApiDemos/res/values-normal-long/strings.xml
deleted file mode 100644
index 01a0487..0000000
--- a/samples/ApiDemos/res/values-normal-long/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Normal Long</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-normal-notlong/strings.xml b/samples/ApiDemos/res/values-normal-notlong/strings.xml
deleted file mode 100644
index bea3f8d..0000000
--- a/samples/ApiDemos/res/values-normal-notlong/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Normal NotLong</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-normal/strings.xml b/samples/ApiDemos/res/values-normal/strings.xml
deleted file mode 100644
index 16ff46f..0000000
--- a/samples/ApiDemos/res/values-normal/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Normal</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-notlong/strings.xml b/samples/ApiDemos/res/values-notlong/strings.xml
deleted file mode 100644
index a3e78db..0000000
--- a/samples/ApiDemos/res/values-notlong/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: NotLong</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-small-long/strings.xml b/samples/ApiDemos/res/values-small-long/strings.xml
deleted file mode 100644
index 8526f61..0000000
--- a/samples/ApiDemos/res/values-small-long/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Small Long</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-small-notlong/strings.xml b/samples/ApiDemos/res/values-small-notlong/strings.xml
deleted file mode 100644
index bc8a676..0000000
--- a/samples/ApiDemos/res/values-small-notlong/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Small NotLong</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-small/strings.xml b/samples/ApiDemos/res/values-small/strings.xml
deleted file mode 100644
index 56db730..0000000
--- a/samples/ApiDemos/res/values-small/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<resources>
- <string name="density_title">Density: Small</string>
-</resources>
diff --git a/samples/ApiDemos/res/values-large/strings.xml b/samples/ApiDemos/res/values-xhdpi/strings.xml
similarity index 91%
copy from samples/ApiDemos/res/values-large/strings.xml
copy to samples/ApiDemos/res/values-xhdpi/strings.xml
index c9fb189..5757044 100644
--- a/samples/ApiDemos/res/values-large/strings.xml
+++ b/samples/ApiDemos/res/values-xhdpi/strings.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <string name="density_title">Density: Large</string>
+ <string name="density_title">Density: Extra High</string>
</resources>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index 36b44c7..d86b194 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -152,32 +152,36 @@
<string name="fragment_stack">App/Fragment/Stack</string>
<string name="new_fragment">New fragment</string>
- <string name="fragment_alert_dialog_support">App/Fragment Support/Alert Dialog</string>
+ <string name="fragment_alert_dialog_support">Support/App/Fragment/Alert Dialog</string>
- <string name="fragment_hide_show_support">App/Fragment Support/Hide and Show</string>
+ <string name="fragment_arguments_support">Support/App/Fragment/Arguments</string>
- <string name="fragment_context_menu_support">App/Fragment Support/Context Menu</string>
+ <string name="fragment_hide_show_support">Support/App/Fragment/Hide and Show</string>
- <string name="fragment_dialog_support">App/Fragment Support/Dialog</string>
+ <string name="fragment_context_menu_support">Support/App/Fragment/Context Menu</string>
- <string name="fragment_dialog_or_activity_support">App/Fragment Support/Dialog or Activity</string>
+ <string name="fragment_dialog_support">Support/App/Fragment/Dialog</string>
- <string name="fragment_layout_support">App/Fragment Support/Layout</string>
+ <string name="fragment_dialog_or_activity_support">Support/App/Fragment/Dialog or Activity</string>
- <string name="fragment_list_array_support">App/Fragment Support/List Array</string>
+ <string name="fragment_layout_support">Support/App/Fragment/Layout</string>
- <string name="fragment_list_cursor_loader_support">App/Fragment Support/List Cursor Loader</string>
+ <string name="fragment_list_array_support">Support/App/Fragment/List Array</string>
- <string name="fragment_menu_support">App/Fragment Support/Menu</string>
+ <string name="fragment_list_cursor_loader_support">Support/App/Fragment/List Cursor Loader</string>
- <string name="fragment_retain_instance_support">App/Fragment Support/Retain Instance</string>
+ <string name="fragment_menu_support">Support/App/Fragment/Menu</string>
- <string name="fragment_receive_result_support">App/Fragment Support/Receive Result</string>
+ <string name="fragment_retain_instance_support">Support/App/Fragment/Retain Instance</string>
- <string name="fragment_stack_support">App/Fragment Support/Stack</string>
+ <string name="fragment_receive_result_support">Support/App/Fragment/Receive Result</string>
+
+ <string name="fragment_stack_support">Support/App/Fragment/Stack</string>
<string name="loader_throttle">App/Loader/Throttle</string>
+ <string name="loader_throttle_support">Support/Loader/Throttle</string>
+
<string name="activity_menu">App/Activity/Menu</string>
<string name="open_menu">Open menu</string>
<string name="close_menu">Close menu</string>
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/_index.html b/samples/ApiDemos/src/com/example/android/apis/app/_index.html
index 3615e26..fa69dee 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/_index.html
+++ b/samples/ApiDemos/src/com/example/android/apis/app/_index.html
@@ -92,6 +92,11 @@
<dd>Demonstrates how to use a DialogFragment to show and manage an
AlertDialog.</dd>
+ <dt><a href="FragmentArguments.html">Fragment Arguments</a></dt>
+ <dd>Demonstrates how a fragment can be initialized with arguments,
+ supplying them either as an argument Bundle at runtime or XML attributes
+ in a <fragment> tag.</dd>
+
<dt><a href="FragmentContextMenu.html">Fragment Context Menu</a></dt>
<dd>Demonstrates how to display and respond to a context menu that is
display from a fragment's view hierarchy.</dd>
diff --git a/samples/ApiDemos/src/com/example/android/apis/support/_index.html b/samples/ApiDemos/src/com/example/android/apis/support/_index.html
new file mode 100644
index 0000000..806a571
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/support/_index.html
@@ -0,0 +1,76 @@
+
+<p>This section includes samples showing the use of Android's
+static support library. This library contains code that you can
+build in to your application to access new features and common
+utilities while being able to run down to version 1.6 (API 4)
+of the platform.</p>
+<ul>
+ <li><a href="#Fragment">Fragment</a></li>
+ <li><a href="#LoaderManager">LoaderManager</a></li>
+</ul>
+
+
+<h3 id="Fragment">Fragment</h3>
+<dl>
+ <dt><a href="app/FragmentAlertDialogSupport.html">Fragment Alert Dialog</a></dt>
+ <dd>Demonstrates how to use a DialogFragment to show and manage an
+ AlertDialog.</dd>
+
+ <dt><a href="app/FragmentArgumentsSupport.html">Fragment Arguments</a></dt>
+ <dd>Demonstrates how a fragment can be initialized with arguments,
+ supplying them either as an argument Bundle at runtime or XML attributes
+ in a <fragment> tag.</dd>
+
+ <dt><a href="app/FragmentContextMenuSupport.html">Fragment Context Menu</a></dt>
+ <dd>Demonstrates how to display and respond to a context menu that is
+ display from a fragment's view hierarchy.</dd>
+
+ <dt><a href="app/FragmentDialogSupport.html">Fragment Dialog</a></dt>
+ <dd>Demonstrates use of DialogFragment to show various types of dialogs.</dd>
+
+ <dt><a href="app/FragmentDialogOrActivitySupport.html">Fragment Dialog or Activity</a></dt>
+ <dd>Demonstrates how the same Fragment implementation can be used to provide the UI
+ for either an Activity or Dialog.</dd>
+
+ <dt><a href="app/FragmentHideShowSupport.html">Fragment Hide Show</a></dt>
+ <dd>Demonstrates hiding and showing fragments.</dd>
+
+ <dt><a href="app/FragmentLayoutSupport.html">Fragment Layout</a></dt>
+ <dd>Demonstrates use of the <fragment> tag to embed a Fragment in
+ an Activity's content view layout, and making the layout change based on
+ configuration to achieve different UI flows.</dd>
+
+ <dt><a href="app/FragmentListArraySupport.html">Fragment List Array</a></dt>
+ <dd>Demonstrates use of ListFragment to show the contents of a simple ArrayAdapter.</dd>
+
+ <dt><a href="app/FragmentListCursorLoaderSupport.html">Fragment List Cursor Loader</a></dt>
+ <dd>Demonstrates use of LoaderManager to perform a query for a Cursor that
+ populates a ListFragment.</dd>
+
+ <dt><a href="app/FragmentMenuSupport.html">Fragment Menu</a></dt>
+ <dd>Demonstrates populating custom menu items from a Fragment.</dd>
+
+ <dt><a href="app/FragmentReceiveResultSupport.html">Fragment Receive Result</a></dt>
+ <dd>Demonstrates starting a new Activity from a Fragment, and receiving
+ a result back from it.</dd>
+
+ <dt><a href="app/FragmentRetainInstanceSupport.html">Fragment Retain Instance</a></dt>
+ <dd>Demonstrates a Fragment can be used to easily retain active state across
+ an Activity's configuration change.</dd>
+
+ <dt><a href="app/FragmentStackSupport.html">Fragment Stack</a></dt>
+ <dd>Demonstrates creating a stack of Fragment instances similar to the
+ traditional stack of activities.</dd>
+
+</dl>
+
+<h3 id="LoaderManager">LoaderManager</h3>
+<dl>
+ <dt><a href="app/LoaderThrottleSupport.html">Loader Throttle</a></dt>
+ <dd>Complete end-to-end demonstration of a simple content provider that
+ populates data in a list through a cursor loader. The UI allows the list
+ to be populated with a series of items, showing how AsyncTaskLoader's
+ throttling facility can be used to control how much a Loader is refreshed
+ in this case.</dd>
+</dl>
+
\ No newline at end of file
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialogSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentAlertDialogSupport.java
similarity index 98%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialogSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentAlertDialogSupport.java
index 84e0ef6..d7f6ae9 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialogSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentAlertDialogSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
diff --git a/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentArgumentsSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentArgumentsSupport.java
new file mode 100644
index 0000000..1ff7185
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentArgumentsSupport.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+package com.example.android.apis.support.app;
+
+import com.example.android.apis.R;
+
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+
+import android.app.Activity;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Demonstrates a fragment that can be configured through both Bundle arguments
+ * and layout attributes.
+ */
+public class FragmentArgumentsSupport extends FragmentActivity {
+//BEGIN_INCLUDE(create)
+ @Override protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fragment_arguments_support);
+
+ if (savedInstanceState == null) {
+ // First-time init; create fragment to embed in activity.
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ Fragment newFragment = MyFragment.newInstance("From Arguments");
+ ft.add(R.id.created, newFragment);
+ ft.commit();
+ }
+ }
+//END_INCLUDE(create)
+
+//BEGIN_INCLUDE(fragment)
+ public static class MyFragment extends Fragment {
+ CharSequence mLabel;
+
+ /**
+ * Create a new instance of MyFragment that will be initialized
+ * with the given arguments.
+ */
+ static MyFragment newInstance(CharSequence label) {
+ MyFragment f = new MyFragment();
+ Bundle b = new Bundle();
+ b.putCharSequence("label", label);
+ f.setArguments(b);
+ return f;
+ }
+
+ /**
+ * Parse attributes during inflation from a view hierarchy into the
+ * arguments we handle.
+ */
+ @Override public void onInflate(Activity activity, AttributeSet attrs,
+ Bundle savedInstanceState) {
+ super.onInflate(activity, attrs, savedInstanceState);
+
+ TypedArray a = activity.obtainStyledAttributes(attrs,
+ R.styleable.FragmentArguments);
+ mLabel = a.getText(R.styleable.FragmentArguments_android_label);
+ a.recycle();
+ }
+
+ /**
+ * During creation, if arguments have been supplied to the fragment
+ * then parse those out.
+ */
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle args = getArguments();
+ if (args != null) {
+ CharSequence label = args.getCharSequence("label");
+ if (label != null) {
+ mLabel = label;
+ }
+ }
+ }
+
+ /**
+ * Create the view for this fragment, using the arguments given to it.
+ */
+ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.hello_world, container, false);
+ View tv = v.findViewById(R.id.text);
+ ((TextView)tv).setText(mLabel != null ? mLabel : "(no label)");
+ tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
+ return v;
+ }
+ }
+//END_INCLUDE(fragment)
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentContextMenuSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentContextMenuSupport.java
similarity index 98%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentContextMenuSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentContextMenuSupport.java
index 3b4af5f..a934145 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentContextMenuSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentContextMenuSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivitySupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentDialogOrActivitySupport.java
similarity index 98%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivitySupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentDialogOrActivitySupport.java
index 20e4569..fe677aa 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivitySupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentDialogOrActivitySupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentDialogSupport.java
similarity index 98%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentDialogSupport.java
index 9b93d48..666151d 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentDialogSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentHideShowSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentHideShowSupport.java
similarity index 98%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentHideShowSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentHideShowSupport.java
index bd5161d..d347160 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentHideShowSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentHideShowSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayoutSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentLayoutSupport.java
similarity index 99%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentLayoutSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentLayoutSupport.java
index b6aa7ee..e1c63ae 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayoutSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentLayoutSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
import com.example.android.apis.Shakespeare;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentListArraySupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListArraySupport.java
similarity index 97%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentListArraySupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListArraySupport.java
index aa3df32..2bcc018 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentListArraySupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListArraySupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.Shakespeare;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoaderSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListCursorLoaderSupport.java
similarity index 99%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoaderSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListCursorLoaderSupport.java
index 35a2f35..98aa47f 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoaderSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentListCursorLoaderSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentMenuSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentMenuSupport.java
similarity index 98%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentMenuSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentMenuSupport.java
index 05d94ca..5ac309a 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentMenuSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentMenuSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentReceiveResultSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentReceiveResultSupport.java
similarity index 97%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentReceiveResultSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentReceiveResultSupport.java
index 3a3a34a..f430e69 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentReceiveResultSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentReceiveResultSupport.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
+import com.example.android.apis.app.SendResult;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstanceSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentRetainInstanceSupport.java
similarity index 99%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstanceSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentRetainInstanceSupport.java
index b83ca44..dbf0cfb 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentRetainInstanceSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentRetainInstanceSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentStackSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentStackSupport.java
similarity index 98%
rename from samples/ApiDemos/src/com/example/android/apis/app/FragmentStackSupport.java
rename to samples/ApiDemos/src/com/example/android/apis/support/app/FragmentStackSupport.java
index 9cc2e04..404721b 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentStackSupport.java
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/FragmentStackSupport.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.example.android.apis.support.app;
import com.example.android.apis.R;
diff --git a/samples/ApiDemos/src/com/example/android/apis/support/app/LoaderThrottleSupport.java b/samples/ApiDemos/src/com/example/android/apis/support/app/LoaderThrottleSupport.java
new file mode 100644
index 0000000..e8f4af5
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/support/app/LoaderThrottleSupport.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+package com.example.android.apis.support.app;
+
+//BEGIN_INCLUDE(complete)
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.ListFragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.support.v4.widget.SimpleCursorAdapter;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ListView;
+
+import java.util.HashMap;
+
+/**
+ * Demonstration of bottom to top implementation of a content provider holding
+ * structured data through displaying it in the UI, using throttling to reduce
+ * the number of queries done when its data changes.
+ */
+public class LoaderThrottleSupport extends FragmentActivity {
+ // Debugging.
+ static final String TAG = "LoaderThrottle";
+
+ /**
+ * The authority we use to get to our sample provider.
+ */
+ public static final String AUTHORITY = "com.example.android.apis.support.app.LoaderThrottle";
+
+ /**
+ * Definition of the contract for the main table of our provider.
+ */
+ public static final class MainTable implements BaseColumns {
+
+ // This class cannot be instantiated
+ private MainTable() {}
+
+ /**
+ * The table name offered by this provider
+ */
+ public static final String TABLE_NAME = "main";
+
+ /**
+ * The content:// style URL for this table
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/main");
+
+ /**
+ * The content URI base for a single row of data. Callers must
+ * append a numeric row id to this Uri to retrieve a row
+ */
+ public static final Uri CONTENT_ID_URI_BASE
+ = Uri.parse("content://" + AUTHORITY + "/main/");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI}.
+ */
+ public static final String CONTENT_TYPE
+ = "vnd.android.cursor.dir/vnd.example.api-demos-throttle";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} sub-directory of a single row.
+ */
+ public static final String CONTENT_ITEM_TYPE
+ = "vnd.android.cursor.item/vnd.example.api-demos-throttle";
+ /**
+ * The default sort order for this table
+ */
+ public static final String DEFAULT_SORT_ORDER = "data COLLATE LOCALIZED ASC";
+
+ /**
+ * Column name for the single column holding our data.
+ * <P>Type: TEXT</P>
+ */
+ public static final String COLUMN_NAME_DATA = "data";
+ }
+
+ /**
+ * This class helps open, create, and upgrade the database file.
+ */
+ static class DatabaseHelper extends SQLiteOpenHelper {
+
+ private static final String DATABASE_NAME = "loader_throttle.db";
+ private static final int DATABASE_VERSION = 2;
+
+ DatabaseHelper(Context context) {
+
+ // calls the super constructor, requesting the default cursor factory.
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ /**
+ *
+ * Creates the underlying database with table name and column names taken from the
+ * NotePad class.
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + MainTable.TABLE_NAME + " ("
+ + MainTable._ID + " INTEGER PRIMARY KEY,"
+ + MainTable.COLUMN_NAME_DATA + " TEXT"
+ + ");");
+ }
+
+ /**
+ *
+ * Demonstrates that the provider must consider what happens when the
+ * underlying datastore is changed. In this sample, the database is upgraded the database
+ * by destroying the existing data.
+ * A real application should upgrade the database in place.
+ */
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+
+ // Logs that the database is being upgraded
+ Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ + newVersion + ", which will destroy all old data");
+
+ // Kills the table and existing data
+ db.execSQL("DROP TABLE IF EXISTS notes");
+
+ // Recreates the database with a new version
+ onCreate(db);
+ }
+ }
+
+ /**
+ * A very simple implementation of a content provider.
+ */
+ public static class SimpleProvider extends ContentProvider {
+ // A projection map used to select columns from the database
+ private final HashMap<String, String> mNotesProjectionMap;
+ // Uri matcher to decode incoming URIs.
+ private final UriMatcher mUriMatcher;
+
+ // The incoming URI matches the main table URI pattern
+ private static final int MAIN = 1;
+ // The incoming URI matches the main table row ID URI pattern
+ private static final int MAIN_ID = 2;
+
+ // Handle to a new DatabaseHelper.
+ private DatabaseHelper mOpenHelper;
+
+ /**
+ * Global provider initialization.
+ */
+ public SimpleProvider() {
+ // Create and initialize URI matcher.
+ mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME, MAIN);
+ mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME + "/#", MAIN_ID);
+
+ // Create and initialize projection map for all columns. This is
+ // simply an identity mapping.
+ mNotesProjectionMap = new HashMap<String, String>();
+ mNotesProjectionMap.put(MainTable._ID, MainTable._ID);
+ mNotesProjectionMap.put(MainTable.COLUMN_NAME_DATA, MainTable.COLUMN_NAME_DATA);
+ }
+
+ /**
+ * Perform provider creation.
+ */
+ @Override
+ public boolean onCreate() {
+ mOpenHelper = new DatabaseHelper(getContext());
+ // Assumes that any failures will be reported by a thrown exception.
+ return true;
+ }
+
+ /**
+ * Handle incoming queries.
+ */
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+
+ // Constructs a new query builder and sets its table name
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(MainTable.TABLE_NAME);
+
+ switch (mUriMatcher.match(uri)) {
+ case MAIN:
+ // If the incoming URI is for main table.
+ qb.setProjectionMap(mNotesProjectionMap);
+ break;
+
+ case MAIN_ID:
+ // The incoming URI is for a single row.
+ qb.setProjectionMap(mNotesProjectionMap);
+ qb.appendWhere(MainTable._ID + "=?");
+ selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,
+ new String[] { uri.getLastPathSegment() });
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+
+
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = MainTable.DEFAULT_SORT_ORDER;
+ }
+
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+ Cursor c = qb.query(db, projection, selection, selectionArgs,
+ null /* no group */, null /* no filter */, sortOrder);
+
+ c.setNotificationUri(getContext().getContentResolver(), uri);
+ return c;
+ }
+
+ /**
+ * Return the MIME type for an known URI in the provider.
+ */
+ @Override
+ public String getType(Uri uri) {
+ switch (mUriMatcher.match(uri)) {
+ case MAIN:
+ return MainTable.CONTENT_TYPE;
+ case MAIN_ID:
+ return MainTable.CONTENT_ITEM_TYPE;
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ }
+
+ /**
+ * Handler inserting new data.
+ */
+ @Override
+ public Uri insert(Uri uri, ContentValues initialValues) {
+ if (mUriMatcher.match(uri) != MAIN) {
+ // Can only insert into to main URI.
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+
+ ContentValues values;
+
+ if (initialValues != null) {
+ values = new ContentValues(initialValues);
+ } else {
+ values = new ContentValues();
+ }
+
+ if (values.containsKey(MainTable.COLUMN_NAME_DATA) == false) {
+ values.put(MainTable.COLUMN_NAME_DATA, "");
+ }
+
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+ long rowId = db.insert(MainTable.TABLE_NAME, null, values);
+
+ // If the insert succeeded, the row ID exists.
+ if (rowId > 0) {
+ Uri noteUri = ContentUris.withAppendedId(MainTable.CONTENT_ID_URI_BASE, rowId);
+ getContext().getContentResolver().notifyChange(noteUri, null);
+ return noteUri;
+ }
+
+ throw new SQLException("Failed to insert row into " + uri);
+ }
+
+ /**
+ * Handle deleting data.
+ */
+ @Override
+ public int delete(Uri uri, String where, String[] whereArgs) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ String finalWhere;
+
+ int count;
+
+ switch (mUriMatcher.match(uri)) {
+ case MAIN:
+ // If URI is main table, delete uses incoming where clause and args.
+ count = db.delete(MainTable.TABLE_NAME, where, whereArgs);
+ break;
+
+ // If the incoming URI matches a single note ID, does the delete based on the
+ // incoming data, but modifies the where clause to restrict it to the
+ // particular note ID.
+ case MAIN_ID:
+ // If URI is for a particular row ID, delete is based on incoming
+ // data but modified to restrict to the given ID.
+ finalWhere = DatabaseUtils.concatenateWhere(
+ MainTable._ID + " = " + ContentUris.parseId(uri), where);
+ count = db.delete(MainTable.TABLE_NAME, finalWhere, whereArgs);
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+
+ getContext().getContentResolver().notifyChange(uri, null);
+
+ return count;
+ }
+
+ /**
+ * Handle updating data.
+ */
+ @Override
+ public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ int count;
+ String finalWhere;
+
+ switch (mUriMatcher.match(uri)) {
+ case MAIN:
+ // If URI is main table, update uses incoming where clause and args.
+ count = db.update(MainTable.TABLE_NAME, values, where, whereArgs);
+ break;
+
+ case MAIN_ID:
+ // If URI is for a particular row ID, update is based on incoming
+ // data but modified to restrict to the given ID.
+ finalWhere = DatabaseUtils.concatenateWhere(
+ MainTable._ID + " = " + ContentUris.parseId(uri), where);
+ count = db.update(MainTable.TABLE_NAME, values, finalWhere, whereArgs);
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+
+ getContext().getContentResolver().notifyChange(uri, null);
+
+ return count;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FragmentManager fm = getSupportFragmentManager();
+
+ // Create the list fragment and add it as our sole content.
+ if (fm.findFragmentById(android.R.id.content) == null) {
+ ThrottledLoaderListFragment list = new ThrottledLoaderListFragment();
+ fm.beginTransaction().add(android.R.id.content, list).commit();
+ }
+ }
+
+ public static class ThrottledLoaderListFragment extends ListFragment
+ implements LoaderManager.LoaderCallbacks<Cursor> {
+
+ // Menu identifiers
+ static final int POPULATE_ID = Menu.FIRST;
+ static final int CLEAR_ID = Menu.FIRST+1;
+
+ // This is the Adapter being used to display the list's data.
+ SimpleCursorAdapter mAdapter;
+
+ // If non-null, this is the current filter the user has provided.
+ String mCurFilter;
+
+ // Task we have running to populate the database.
+ AsyncTask<Void, Void, Void> mPopulatingTask;
+
+ @Override public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ setEmptyText("No data. Select 'Populate' to fill with data from Z to A at a rate of 4 per second.");
+ setHasOptionsMenu(true);
+
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new SimpleCursorAdapter(getActivity(),
+ android.R.layout.simple_list_item_1, null,
+ new String[] { MainTable.COLUMN_NAME_DATA },
+ new int[] { android.R.id.text1 }, 0);
+ setListAdapter(mAdapter);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ menu.add(Menu.NONE, POPULATE_ID, 0, "Populate")
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ menu.add(Menu.NONE, CLEAR_ID, 0, "Clear")
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ }
+
+ @Override public boolean onOptionsItemSelected(MenuItem item) {
+ final ContentResolver cr = getActivity().getContentResolver();
+
+ switch (item.getItemId()) {
+ case POPULATE_ID:
+ if (mPopulatingTask != null) {
+ mPopulatingTask.cancel(false);
+ }
+ mPopulatingTask = new AsyncTask<Void, Void, Void>() {
+ @Override protected Void doInBackground(Void... params) {
+ for (char c='Z'; c>='A'; c--) {
+ if (isCancelled()) {
+ break;
+ }
+ StringBuilder builder = new StringBuilder("Data ");
+ builder.append(c);
+ ContentValues values = new ContentValues();
+ values.put(MainTable.COLUMN_NAME_DATA, builder.toString());
+ cr.insert(MainTable.CONTENT_URI, values);
+ // Wait a bit between each insert.
+ try {
+ Thread.sleep(250);
+ } catch (InterruptedException e) {
+ }
+ }
+ return null;
+ }
+ };
+ mPopulatingTask.executeOnExecutor(
+ AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
+ return true;
+
+ case CLEAR_ID:
+ if (mPopulatingTask != null) {
+ mPopulatingTask.cancel(false);
+ mPopulatingTask = null;
+ }
+ AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+ @Override protected Void doInBackground(Void... params) {
+ cr.delete(MainTable.CONTENT_URI, null, null);
+ return null;
+ }
+ };
+ task.execute((Void[])null);
+ return true;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override public void onListItemClick(ListView l, View v, int position, long id) {
+ // Insert desired behavior here.
+ Log.i(TAG, "Item clicked: " + id);
+ }
+
+ // These are the rows that we will retrieve.
+ static final String[] PROJECTION = new String[] {
+ MainTable._ID,
+ MainTable.COLUMN_NAME_DATA,
+ };
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ CursorLoader cl = new CursorLoader(getActivity(), MainTable.CONTENT_URI,
+ PROJECTION, null, null, null);
+ cl.setUpdateThrottle(2000); // update at most every 2 seconds.
+ return cl;
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mAdapter.swapCursor(data);
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mAdapter.swapCursor(null);
+ }
+ }
+}
+//END_INCLUDE(complete)
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java b/samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java
index 8c0db32..8aea949 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GameControllerInput.java
@@ -33,6 +33,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.InputDevice.MotionRange;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
@@ -152,8 +153,24 @@
public InputDeviceState(InputDevice device) {
mDevice = device;
- mAxes = device.getMotionAxes();
- mAxisValues = new float[mAxes.length];
+
+ int numAxes = 0;
+ for (MotionRange range : device.getMotionRanges()) {
+ if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ numAxes += 1;
+ }
+ }
+
+ mAxes = new int[numAxes];
+ mAxisValues = new float[numAxes];
+ int i = 0;
+ for (MotionRange range : device.getMotionRanges()) {
+ if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ numAxes += 1;
+ }
+ mAxes[i++] = range.getAxis();
+ }
+
mKeys = new SparseIntArray();
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/GameView.java b/samples/ApiDemos/src/com/example/android/apis/view/GameView.java
index 1791029..9fe236c 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/GameView.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/GameView.java
@@ -264,7 +264,7 @@
private static float getCenteredAxis(MotionEvent event, InputDevice device,
int axis, int historyPos) {
- final InputDevice.MotionRange range = device.getMotionRange(axis);
+ final InputDevice.MotionRange range = device.getMotionRange(axis, event.getSource());
if (range != null) {
final float flat = range.getFlat();
final float value = historyPos < 0 ? event.getAxisValue(axis)
diff --git a/samples/BrowserPlugin/jni/Android.mk b/samples/BrowserPlugin/jni/Android.mk
index b153e37..f8cea4d 100644
--- a/samples/BrowserPlugin/jni/Android.mk
+++ b/samples/BrowserPlugin/jni/Android.mk
@@ -66,7 +66,7 @@
libskia
LOCAL_CFLAGS += -fvisibility=hidden
-LOCAL_PRELINK_MODULE:=false
+
LOCAL_MODULE:= libsampleplugin
diff --git a/samples/SimpleJNI/jni/Android.mk b/samples/SimpleJNI/jni/Android.mk
index 528196b..a704fcf 100644
--- a/samples/SimpleJNI/jni/Android.mk
+++ b/samples/SimpleJNI/jni/Android.mk
@@ -44,11 +44,4 @@
# No special compiler flags.
LOCAL_CFLAGS +=
-# Don't prelink this library. For more efficient code, you may want
-# to add this library to the prelink map and set this to true. However,
-# it's difficult to do this for applications that are not supplied as
-# part of a system image.
-
-LOCAL_PRELINK_MODULE := false
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/tools/emulator/opengl/host/tools/emugen/Android.mk b/tools/emulator/opengl/host/tools/emugen/Android.mk
new file mode 100644
index 0000000..fcd7b24
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/Android.mk
@@ -0,0 +1,10 @@
+
+LOCAL_PATH:=$(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := debug
+LOCAL_SRC_FILES := ApiGen.cpp EntryPoint.cpp main.cpp strUtils.cpp TypeFactory.cpp
+LOCAL_MODULE := emugen
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp b/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
new file mode 100644
index 0000000..9b7949d
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
@@ -0,0 +1,805 @@
+/*
+* Copyright (C) 2011 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 "ApiGen.h"
+#include "EntryPoint.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "strUtils.h"
+#include <errno.h>
+#include <sys/types.h>
+
+
+EntryPoint * ApiGen::findEntryByName(const std::string & name)
+{
+ EntryPoint * entry = NULL;
+
+ size_t n = this->size();
+ for (size_t i = 0; i < n; i++) {
+ if (at(i).name() == name) {
+ entry = &(at(i));
+ break;
+ }
+ }
+ return entry;
+}
+
+void ApiGen::printHeader(FILE *fp) const
+{
+ fprintf(fp, "// Generated Code - DO NOT EDIT !!\n");
+ fprintf(fp, "// generated by 'emugen'\n");
+}
+
+int ApiGen::genProcTypes(const std::string &filename, SideType side)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+ printHeader(fp);
+
+ fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", m_basename.c_str(), sideString(side));
+ fprintf(fp, "#define __%s_%s_proc_t_h\n", m_basename.c_str(), sideString(side));
+ fprintf(fp, "\n\n");
+ fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str());
+
+ for (size_t i = 0; i < size(); i++) {
+ EntryPoint *e = &at(i);
+
+ fprintf(fp, "typedef ");
+ e->retval().printType(fp);
+ fprintf(fp, " (* %s_%s_proc_t) (", e->name().c_str(), sideString(side));
+ if (side == CLIENT_SIDE) { fprintf(fp, "void * ctx"); }
+ if (e->customDecoder() && side == SERVER_SIDE) { fprintf(fp, "void *ctx"); }
+
+ VarsArray & evars = e->vars();
+ size_t n = evars.size();
+
+ for (size_t j = 0; j < n; j++) {
+ if (!evars[j].isVoid()) {
+ if (j != 0 || side == CLIENT_SIDE || (side == SERVER_SIDE && e->customDecoder())) fprintf(fp, ", ");
+ evars[j].printType(fp);
+ }
+ }
+ fprintf(fp, ");\n");
+ }
+ fprintf(fp, "\n\n#endif\n");
+ return 0;
+}
+
+int ApiGen::genContext(const std::string & filename, SideType side)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+ printHeader(fp);
+
+ fprintf(fp, "#ifndef __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
+ fprintf(fp, "#define __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
+
+ // fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str());
+ fprintf(fp, "\n#include \"%s_%s_proc.h\"\n", m_basename.c_str(), sideString(side));
+
+ StringVec & contextHeaders = side == CLIENT_SIDE ? m_clientContextHeaders : m_serverContextHeaders;
+ for (size_t i = 0; i < contextHeaders.size(); i++) {
+ fprintf(fp, "#include %s\n", contextHeaders[i].c_str());
+ }
+ fprintf(fp, "\n");
+
+ fprintf(fp, "\nstruct %s_%s_context_t {\n\n", m_basename.c_str(), sideString(side));
+ for (size_t i = 0; i < size(); i++) {
+ EntryPoint *e = &at(i);
+ fprintf(fp, "\t%s_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str());
+ }
+ // accessors
+ fprintf(fp, "\t//Accessors \n");
+
+ for (size_t i = 0; i < size(); i++) {
+ EntryPoint *e = &at(i);
+ const char *n = e->name().c_str();
+ const char *s = sideString(side);
+ fprintf(fp, "\tvirtual %s_%s_proc_t set_%s(%s_%s_proc_t f) { %s_%s_proc_t retval = %s; %s = f; return retval;}\n", n, s, n, n, s, n, s, n, n);
+ }
+
+ // virtual destructor
+ fprintf(fp, "\t virtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side));
+ // accessor
+ if (side == CLIENT_SIDE) {
+ fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n",
+ m_basename.c_str(), sideString(side));
+ fprintf(fp, "\tvoid setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n");
+ }
+
+ fprintf(fp, "};\n");
+
+ fprintf(fp, "\n#endif\n");
+ fclose(fp);
+ return 0;
+}
+
+int ApiGen::genClientEntryPoints(const std::string & filename)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return errno;
+ }
+
+ printHeader(fp);
+ fprintf(fp, "#include <stdio.h>\n");
+ fprintf(fp, "#include <stdlib.h>\n");
+ fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(CLIENT_SIDE));
+ fprintf(fp, "\n");
+
+ fprintf(fp, "extern \"C\" {\n");
+
+ for (size_t i = 0; i < size(); i++) {
+ fprintf(fp, "\t"); at(i).print(fp, false); fprintf(fp, ";\n");
+ }
+ fprintf(fp, "};\n\n");
+
+ fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n",
+ m_basename.c_str(), sideString(CLIENT_SIDE));
+
+ fprintf(fp,
+ "void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n\n",
+ m_basename.c_str(), sideString(CLIENT_SIDE));
+
+
+ for (size_t i = 0; i < size(); i++) {
+ EntryPoint *e = &at(i);
+ e->print(fp);
+ fprintf(fp, "{\n");
+ fprintf(fp, "\t %s_%s_context_t * ctx = getCurrentContext(); \n",
+ m_basename.c_str(), sideString(CLIENT_SIDE));
+
+ bool shouldReturn = !e->retval().isVoid();
+
+ fprintf(fp, "\t %sctx->%s(ctx",
+ shouldReturn ? "return " : "",
+ e->name().c_str());
+ size_t nvars = e->vars().size();
+
+ for (size_t j = 0; j < nvars; j++) {
+ if (!e->vars()[j].isVoid()) {
+ fprintf(fp, ", %s", e->vars()[j].name().c_str());
+ }
+ }
+ fprintf(fp, ");\n");
+ fprintf(fp, "}\n\n");
+ }
+ fclose(fp);
+ return 0;
+}
+
+
+int ApiGen::genOpcodes(const std::string &filename)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return errno;
+ }
+
+ printHeader(fp);
+ fprintf(fp, "#ifndef __GUARD_%s_opcodes_h_\n", m_basename.c_str());
+ fprintf(fp, "#define __GUARD_%s_opcodes_h_\n\n", m_basename.c_str());
+ for (size_t i = 0; i < size(); i++) {
+ fprintf(fp, "#define OP_%s \t\t\t\t\t%u\n", at(i).name().c_str(), (unsigned int)i + m_baseOpcode);
+ }
+ fprintf(fp, "#define OP_last \t\t\t\t\t%u\n", (unsigned int)size() + m_baseOpcode);
+ fprintf(fp,"\n\n#endif\n");
+ fclose(fp);
+ return 0;
+
+}
+int ApiGen::genAttributesTemplate(const std::string &filename )
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+
+ for (size_t i = 0; i < size(); i++) {
+ if (at(i).hasPointers()) {
+ fprintf(fp, "#");
+ at(i).print(fp);
+ fprintf(fp, "%s\n\n", at(i).name().c_str());
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
+int ApiGen::genEncoderHeader(const std::string &filename)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+
+ printHeader(fp);
+ std::string classname = m_basename + "_encoder_context_t";
+
+ fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
+ fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
+
+ fprintf(fp, "#include \"IOStream.h\"\n");
+ fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(CLIENT_SIDE));
+
+ for (size_t i = 0; i < m_encoderHeaders.size(); i++) {
+ fprintf(fp, "#include %s\n", m_encoderHeaders[i].c_str());
+ }
+ fprintf(fp, "\n");
+
+ fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
+ classname.c_str(), m_basename.c_str(), sideString(CLIENT_SIDE));
+ fprintf(fp, "\tIOStream *m_stream;\n\n");
+
+ fprintf(fp, "\t%s(IOStream *stream);\n\n", classname.c_str());
+ fprintf(fp, "\n};\n\n");
+
+ fprintf(fp,"extern \"C\" {\n");
+
+ for (size_t i = 0; i < size(); i++) {
+ fprintf(fp, "\t");
+ at(i).print(fp, false, "_enc", /* classname + "::" */"", "void *self");
+ fprintf(fp, ";\n");
+ }
+ fprintf(fp, "};\n");
+ fprintf(fp, "#endif");
+
+ fclose(fp);
+ return 0;
+}
+
+int ApiGen::genEncoderImpl(const std::string &filename)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+
+ printHeader(fp);
+ fprintf(fp, "\n\n#include <string.h>\n");
+ fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
+ fprintf(fp, "#include \"%s_enc.h\"\n\n\n", m_basename.c_str());
+ fprintf(fp, "#include <stdio.h>\n");
+ std::string classname = m_basename + "_encoder_context_t";
+ size_t n = size();
+
+ // unsupport printout
+ fprintf(fp, "static void enc_unsupported()\n{\n\tfprintf(stderr, \"Function is unsupported\\n\");\n}\n\n");
+
+ // entry points;
+ for (size_t i = 0; i < n; i++) {
+ EntryPoint *e = &at(i);
+
+ if (e->unsupported()) continue;
+
+
+ e->print(fp, true, "_enc", /* classname + "::" */"", "void *self");
+ fprintf(fp, "{\n");
+
+ fprintf(fp, "\n\t%s *ctx = (%s *)self;\n\n",
+ classname.c_str(),
+ classname.c_str());
+
+ // size calculation ;
+ fprintf(fp, "\t size_t packetSize = ");
+
+ VarsArray & evars = e->vars();
+ size_t nvars = evars.size();
+ size_t npointers = 0;
+ for (size_t j = 0; j < nvars; j++) {
+ fprintf(fp, "%s ", j == 0 ? "" : " +");
+ if (evars[j].isPointer()) {
+ npointers++;
+
+ if (evars[j].lenExpression() == "") {
+ fprintf(stderr, "%s: data len is undefined for '%s'\n",
+ e->name().c_str(), evars[j].name().c_str());
+ }
+
+ if (evars[j].nullAllowed()) {
+ fprintf(fp, "(%s != NULL ? %s : 0)",
+ evars[j].name().c_str(),
+ evars[j].lenExpression().c_str());
+ } else {
+ if (evars[j].pointerDir() == Var::POINTER_IN ||
+ evars[j].pointerDir() == Var::POINTER_INOUT) {
+ fprintf(fp, "%s", evars[j].lenExpression().c_str());
+ } else {
+ fprintf(fp, "0");
+ }
+ }
+ } else {
+ fprintf(fp, "%u", (unsigned int) evars[j].type()->bytes());
+ }
+ }
+ fprintf(fp, " %s 8 + %u * 4;\n", nvars != 0 ? "+" : "", (unsigned int) npointers);
+
+ // allocate buffer from the stream;
+ fprintf(fp, "\t unsigned char *ptr = ctx->m_stream->alloc(packetSize);\n\n");
+
+ // encode into the stream;
+ fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n", e->name().c_str());
+ fprintf(fp, "\t*(unsigned int *)(ptr) = (unsigned int) packetSize; ptr += 4;\n\n");
+
+ // out variables
+ for (size_t j = 0; j < nvars; j++) {
+ if (evars[j].isPointer()) {
+ // encode a pointer header
+ if (evars[j].nullAllowed()) {
+ fprintf(fp, "\t*(unsigned int *)(ptr) = (%s != NULL) ? %s : 0; ptr += 4; \n",
+ evars[j].name().c_str(), evars[j].lenExpression().c_str());
+ } else {
+ fprintf(fp, "\t*(unsigned int *)(ptr) = %s; ptr += 4; \n",
+ evars[j].lenExpression().c_str());
+ }
+
+ Var::PointerDir dir = evars[j].pointerDir();
+ if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
+ if (evars[j].nullAllowed()) {
+ fprintf(fp, "\tif (%s != NULL) ", evars[j].name().c_str());
+ } else {
+ fprintf(fp, "\t");
+ }
+
+ if (evars[j].packExpression().size() != 0) {
+ fprintf(fp, "%s;", evars[j].packExpression().c_str());
+ } else {
+ fprintf(fp, "memcpy(ptr, %s, %s);",
+ evars[j].name().c_str(),
+ evars[j].lenExpression().c_str());
+ }
+
+ fprintf(fp, "ptr += %s;\n", evars[j].lenExpression().c_str());
+ }
+ } else {
+ // encode a non pointer variable
+ if (!evars[j].isVoid()) {
+ fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
+ evars[j].type()->name().c_str(), evars[j].name().c_str(),
+ (uint) evars[j].type()->bytes());
+ }
+ }
+ }
+ // in variables;
+ for (size_t j = 0; j < nvars; j++) {
+ if (evars[j].isPointer()) {
+ Var::PointerDir dir = evars[j].pointerDir();
+ if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) {
+ if (evars[j].nullAllowed()) {
+ fprintf(fp, "\tif (%s != NULL) ctx->m_stream->readback(%s, %s);\n",
+ evars[j].name().c_str(),
+ evars[j].name().c_str(),
+ evars[j].lenExpression().c_str());
+ } else {
+ fprintf(fp, "\tctx->m_stream->readback(%s, %s);\n",
+ evars[j].name().c_str(),
+ evars[j].lenExpression().c_str());
+ }
+ }
+ }
+ }
+ // todo - return value for pointers
+ if (e->retval().isPointer()) {
+ fprintf(stderr, "WARNING: %s : return value of pointer is unsupported\n",
+ e->name().c_str());
+ fprintf(fp, "\t return NULL;\n");
+ } else if (e->retval().type()->name() != "void") {
+ fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str());
+ fprintf(fp, "\tctx->m_stream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
+ fprintf(fp, "\treturn retval;\n");
+ }
+ fprintf(fp, "}\n\n");
+ }
+
+ // constructor
+ fprintf(fp, "%s::%s(IOStream *stream)\n{\n", classname.c_str(), classname.c_str());
+ fprintf(fp, "\tm_stream = stream;\n\n");
+
+ for (size_t i = 0; i < n; i++) {
+ EntryPoint *e = &at(i);
+ if (e->unsupported()) {
+ fprintf(fp, "\tset_%s((%s_%s_proc_t)(enc_unsupported));\n", e->name().c_str(), e->name().c_str(), sideString(CLIENT_SIDE));
+ } else {
+ fprintf(fp, "\tset_%s(%s_enc);\n", e->name().c_str(), e->name().c_str());
+ }
+ /**
+ if (e->unsupsported()) {
+ fprintf(fp, "\tmemcpy((void *)(&%s), (const void *)(&enc_unsupported), sizeof(%s));\n",
+ e->name().c_str(),
+ e->name().c_str());
+ } else {
+ fprintf(fp, "\t%s = %s_enc;\n", e->name().c_str(), e->name().c_str());
+ }
+ **/
+ }
+ fprintf(fp, "}\n\n");
+
+ fclose(fp);
+ return 0;
+}
+
+
+int ApiGen::genDecoderHeader(const std::string &filename)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+
+ printHeader(fp);
+ std::string classname = m_basename + "_decoder_context_t";
+
+ fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
+ fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
+
+ fprintf(fp, "#include \"IOStream.h\" \n");
+ fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE));
+
+ for (size_t i = 0; i < m_decoderHeaders.size(); i++) {
+ fprintf(fp, "#include %s\n", m_decoderHeaders[i].c_str());
+ }
+ fprintf(fp, "\n");
+
+ fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
+ classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE));
+ fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream);\n");
+ fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n");
+ fprintf(fp, "\n};\n\n");
+ fprintf(fp, "#endif");
+
+ fclose(fp);
+ return 0;
+}
+
+int ApiGen::genDecoderImpl(const std::string &filename)
+{
+ FILE *fp = fopen(filename.c_str(), "wt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+
+ printHeader(fp);
+
+ std::string classname = m_basename + "_decoder_context_t";
+
+ size_t n = size();
+
+ fprintf(fp, "\n\n#include <string.h>\n");
+ fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
+ fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str());
+ fprintf(fp, "#include <stdio.h>\n");
+
+ // init function;
+ fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str());
+ fprintf(fp, "\tvoid *ptr;\n\n");
+ for (size_t i = 0; i < n; i++) {
+ EntryPoint *e = &at(i);
+ fprintf(fp, "\tptr = getProc(\"%s\", userData); set_%s((%s_%s_proc_t)ptr);\n",
+ e->name().c_str(),
+ e->name().c_str(),
+ e->name().c_str(),
+ sideString(SERVER_SIDE));
+
+ }
+ fprintf(fp, "\treturn 0;\n");
+ fprintf(fp, "}\n\n");
+
+ // decoder switch;
+ fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream)\n{\n", classname.c_str());
+ fprintf(fp,
+ " \n\
+\tsize_t pos = 0;\n\
+\tif (len < 8) return pos; \n\
+\tunsigned char *ptr = (unsigned char *)buf;\n\
+\tbool unknownOpcode = false; \n\
+\twhile ((len - pos >= 8) && !unknownOpcode) { \n\
+\t\tvoid *params[%u]; \n\
+\t\tint opcode = *(int *)ptr; \n\
+\t\tunsigned int packetLen = *(int *)(ptr + 4);\n\
+\t\tif (len - pos < packetLen) return pos; \n\
+\t\tswitch(opcode) {\n",
+ (uint) m_maxEntryPointsParams);
+
+ for (size_t f = 0; f < n; f++) {
+ enum Pass_t { PASS_TmpBuffAlloc = 0, PASS_MemAlloc, PASS_DebugPrint, PASS_FunctionCall, PASS_Epilog, PASS_LAST };
+ EntryPoint *e = &at(f);
+
+ // construct a printout string;
+ std::string printString = "";
+ for (size_t i = 0; i < e->vars().size(); i++) {
+ Var *v = &e->vars()[i];
+ if (!v->isVoid()) printString += (v->isPointer() ? "%p(%u)" : v->type()->printFormat()) + " ";
+ }
+ printString += "";
+ // TODO - add for return value;
+
+ fprintf(fp, "\t\t\tcase OP_%s:\n", e->name().c_str());
+ fprintf(fp, "\t\t\t{\n");
+
+ bool totalTmpBuffExist = false;
+ std::string totalTmpBuffOffset = "0";
+ std::string *tmpBufOffset = new std::string[e->vars().size()];
+
+ // construct retval type string
+ std::string retvalType;
+ if (!e->retval().isVoid()) {
+ retvalType = e->retval().type()->name();
+ if (e->retval().isPointer()) retvalType += "*";
+ }
+
+ for (int pass = PASS_TmpBuffAlloc; pass < PASS_LAST; pass++) {
+ if (pass == PASS_FunctionCall && !e->retval().isVoid() && !e->retval().isPointer()) {
+ fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(),
+ totalTmpBuffOffset.c_str());
+ }
+
+
+ if (pass == PASS_FunctionCall) {
+ fprintf(fp, "\t\t\tthis->%s(", e->name().c_str());
+ if (e->customDecoder()) {
+ fprintf(fp, "this"); // add a context to the call
+ }
+ } else if (pass == PASS_DebugPrint) {
+ fprintf(fp, "#ifdef DEBUG_PRINTOUT\n");
+ fprintf(fp, "\t\t\tfprintf(stderr,\"%s(%s)\\n\"", e->name().c_str(), printString.c_str());
+ if (e->vars().size() > 0 && !e->vars()[0].isVoid()) fprintf(fp, ",");
+ }
+
+ std::string varoffset = "8"; // skip the header
+ VarsArray & evars = e->vars();
+ // allocate memory for out pointers;
+ for (size_t j = 0; j < evars.size(); j++) {
+ Var *v = & evars[j];
+ if (!v->isVoid()) {
+ if ((pass == PASS_FunctionCall) && (j != 0 || e->customDecoder())) fprintf(fp, ", ");
+ if (pass == PASS_DebugPrint && j != 0) fprintf(fp, ", ");
+
+ if (!v->isPointer()) {
+ if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) {
+ fprintf(fp, "*(%s *)(ptr + %s)", v->type()->name().c_str(), varoffset.c_str());
+ }
+ varoffset += " + " + toString(v->type()->bytes());
+ } else {
+ if (v->pointerDir() == Var::POINTER_IN || v->pointerDir() == Var::POINTER_INOUT) {
+ if (pass == PASS_MemAlloc && v->pointerDir() == Var::POINTER_INOUT) {
+ fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n",
+ (uint) j, varoffset.c_str());
+ fprintf(fp, "unsigned char *tmpPtr%u = (ptr + %s + 4);\n",
+ (uint) j, varoffset.c_str());
+ }
+ if (pass == PASS_FunctionCall) {
+ fprintf(fp, "(%s *)(ptr + %s + 4)",
+ v->type()->name().c_str(), varoffset.c_str());
+ } else if (pass == PASS_DebugPrint) {
+ fprintf(fp, "(%s *)(ptr + %s + 4), *(unsigned int *)(ptr + %s)",
+ v->type()->name().c_str(), varoffset.c_str(),
+ varoffset.c_str());
+ }
+ varoffset += " + 4 + *(size_t *)(ptr +" + varoffset + ")";
+ } else { // in pointer;
+ if (pass == PASS_TmpBuffAlloc) {
+ fprintf(fp, "\t\t\tsize_t tmpPtr%uSize = (size_t)*(unsigned int *)(ptr + %s);\n",
+ (uint) j, varoffset.c_str());
+ if (!totalTmpBuffExist)
+ fprintf(fp, "\t\t\tsize_t totalTmpSize = tmpPtr%uSize;\n", (uint)j);
+ else
+ fprintf(fp, "\t\t\ttotalTmpSize += tmpPtr%uSize;\n", (uint)j);
+ tmpBufOffset[j] = totalTmpBuffOffset;
+ char tmpPtrName[16];
+ sprintf(tmpPtrName,"tmpPtr%u", (uint)j);
+ totalTmpBuffOffset += std::string(tmpPtrName);
+ totalTmpBuffExist = true;
+ } else if (pass == PASS_MemAlloc) {
+ fprintf(fp, "\t\t\tunsigned char *tmpPtr%u = &tmpBuf[%s];\n",
+ (uint)j, tmpBufOffset[j].c_str());
+ } else if (pass == PASS_FunctionCall) {
+ fprintf(fp, "(%s *)(tmpPtr%u)", v->type()->name().c_str(), (uint) j);
+ } else if (pass == PASS_DebugPrint) {
+ fprintf(fp, "(%s *)(tmpPtr%u), *(unsigned int *)(ptr + %s)",
+ v->type()->name().c_str(), (uint) j,
+ varoffset.c_str());
+ }
+ varoffset += " + 4";
+ }
+ }
+ }
+ }
+
+ if (pass == PASS_FunctionCall || pass == PASS_DebugPrint) fprintf(fp, ");\n");
+ if (pass == PASS_DebugPrint) fprintf(fp, "#endif\n");
+
+ if (pass == PASS_TmpBuffAlloc) {
+ if (!e->retval().isVoid() && !e->retval().isPointer()) {
+ if (!totalTmpBuffExist)
+ fprintf(fp, "\t\t\tsize_t totalTmpSize = sizeof(%s);\n", retvalType.c_str());
+ else
+ fprintf(fp, "\t\t\ttotalTmpSize += sizeof(%s);\n", retvalType.c_str());
+
+ totalTmpBuffExist = true;
+ }
+ if (totalTmpBuffExist) {
+ fprintf(fp, "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n");
+ }
+ }
+
+ if (pass == PASS_Epilog) {
+ // send back out pointers data as well as retval
+ if (totalTmpBuffExist) {
+ fprintf(fp, "\t\t\tstream->flush();\n");
+ }
+
+ fprintf(fp, "\t\t\tpos += *(int *)(ptr + 4);\n");
+ fprintf(fp, "\t\t\tptr += *(int *)(ptr + 4);\n");
+ }
+
+ } // pass;
+ fprintf(fp, "\t\t\t}\n");
+ fprintf(fp, "\t\t\tbreak;\n");
+
+ delete [] tmpBufOffset;
+ }
+ fprintf(fp, "\t\t\tdefault:\n");
+ fprintf(fp, "\t\t\t\tunknownOpcode = true;\n");
+ fprintf(fp, "\t\t} //switch\n");
+ fprintf(fp, "\t} // while\n");
+ fprintf(fp, "\treturn pos;\n");
+ fprintf(fp, "}\n");
+
+ fclose(fp);
+ return 0;
+}
+
+int ApiGen::readSpec(const std::string & filename)
+{
+ FILE *specfp = fopen(filename.c_str(), "rt");
+ if (specfp == NULL) {
+ return -1;
+ }
+
+ char line[1000];
+ unsigned int lc = 0;
+ while (fgets(line, sizeof(line), specfp) != NULL) {
+ lc++;
+ EntryPoint ref;
+ if (ref.parse(lc, std::string(line))) {
+ push_back(ref);
+ updateMaxEntryPointsParams(ref.vars().size());
+ }
+ }
+ fclose(specfp);
+ return 0;
+}
+
+int ApiGen::readAttributes(const std::string & attribFilename)
+{
+ enum { ST_NAME, ST_ATT } state;
+
+ FILE *fp = fopen(attribFilename.c_str(), "rt");
+ if (fp == NULL) {
+ perror(attribFilename.c_str());
+ return -1;
+ }
+ char buf[1000];
+
+ state = ST_NAME;
+ EntryPoint *currentEntry = NULL;
+ size_t lc = 0;
+ bool globalAttributes = false;
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ lc++;
+ std::string line(buf);
+ if (line.size() == 0) continue; // could that happen?
+
+ if (line.at(0) == '#') continue; // comment
+
+ size_t first = line.find_first_not_of(" \t\n");
+ if (state == ST_ATT && (first == std::string::npos || first == 0)) state = ST_NAME;
+
+ line = trim(line);
+ if (line.size() == 0 || line.at(0) == '#') continue;
+
+ switch(state) {
+ case ST_NAME:
+ if (line == "GLOBAL") {
+ globalAttributes = true;
+ } else {
+ globalAttributes = false;
+ currentEntry = findEntryByName(line);
+ if (currentEntry == NULL) {
+ fprintf(stderr, "WARNING: %u: attribute of non existant entry point %s\n", (unsigned int)lc, line.c_str());
+ }
+ }
+ state = ST_ATT;
+ break;
+ case ST_ATT:
+ if (globalAttributes) {
+ setGlobalAttribute(line, lc);
+ } else if (currentEntry != NULL) {
+ currentEntry->setAttribute(line, lc);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+
+int ApiGen::setGlobalAttribute(const std::string & line, size_t lc)
+{
+ size_t pos = 0;
+ size_t last;
+ std::string token = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+
+ if (token == "base_opcode") {
+ std::string str = getNextToken(line, pos, &last, WHITESPACE);
+ if (str.size() == 0) {
+ fprintf(stderr, "line %u: missing value for base_opcode\n", (uint) lc);
+ } else {
+ setBaseOpcode(atoi(str.c_str()));
+ }
+ } else if (token == "encoder_headers") {
+ std::string str = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+ while (str.size() != 0) {
+ encoderHeaders().push_back(str);
+ str = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+ }
+ } else if (token == "client_context_headers") {
+ std::string str = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+ while (str.size() != 0) {
+ clientContextHeaders().push_back(str);
+ str = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+ }
+ } else if (token == "server_context_headers") {
+ std::string str = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+ while (str.size() != 0) {
+ serverContextHeaders().push_back(str);
+ str = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+ }
+ } else if (token == "decoder_headers") {
+ std::string str = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+ while (str.size() != 0) {
+ decoderHeaders().push_back(str);
+ str = getNextToken(line, pos, &last, WHITESPACE);
+ pos = last;
+ }
+ }
+ else {
+ fprintf(stderr, "WARNING: %u : unknown global attribute %s\n", (unsigned int)lc, line.c_str());
+ }
+
+ return 0;
+}
+
diff --git a/tools/emulator/opengl/host/tools/emugen/ApiGen.h b/tools/emulator/opengl/host/tools/emugen/ApiGen.h
new file mode 100644
index 0000000..3c7fd27
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/ApiGen.h
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2011 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.
+*/
+#ifndef __API_GEN_H_
+#define __API_GEN_H_
+
+#include <vector>
+#include <string.h>
+#include "EntryPoint.h"
+
+
+class ApiGen : public std::vector<EntryPoint> {
+
+public:
+ typedef std::vector<std::string> StringVec;
+ typedef enum { CLIENT_SIDE, SERVER_SIDE } SideType;
+
+ ApiGen(const std::string & basename) :
+ m_basename(basename),
+ m_maxEntryPointsParams(0),
+ m_baseOpcode(0)
+ { }
+ virtual ~ApiGen() {}
+ int readSpec(const std::string & filename);
+ int readAttributes(const std::string & attribFilename);
+ size_t maxEntryPointsParams() { return m_maxEntryPointsParams; }
+ void updateMaxEntryPointsParams(size_t val) {
+ if (m_maxEntryPointsParams == 0 || val > m_maxEntryPointsParams) m_maxEntryPointsParams = val;
+ }
+ int baseOpcode() { return m_baseOpcode; }
+ void setBaseOpcode(int base) { m_baseOpcode = base; }
+
+ const char *sideString(SideType side) { return (side == CLIENT_SIDE) ? "client" : "server"; }
+
+ StringVec & clientContextHeaders() { return m_clientContextHeaders; }
+ StringVec & encoderHeaders() { return m_encoderHeaders; }
+ StringVec & serverContextHeaders() { return m_serverContextHeaders; }
+ StringVec & decoderHeaders() { return m_decoderHeaders; }
+
+ EntryPoint * findEntryByName(const std::string & name);
+ int genOpcodes(const std::string &filename);
+ int genAttributesTemplate(const std::string &filename);
+ int genProcTypes(const std::string &filename, SideType side);
+
+ int genContext(const std::string &filename, SideType side);
+ int genClientEntryPoints(const std::string &filename);
+
+ int genEncoderHeader(const std::string &filename);
+ int genEncoderImpl(const std::string &filename);
+
+ int genDecoderHeader(const std::string &filename);
+ int genDecoderImpl(const std::string &filename);
+
+protected:
+ virtual void printHeader(FILE *fp) const;
+ std::string m_basename;
+ StringVec m_clientContextHeaders;
+ StringVec m_encoderHeaders;
+ StringVec m_serverContextHeaders;
+ StringVec m_decoderHeaders;
+ size_t m_maxEntryPointsParams; // record the maximum number of parameters in the entry points;
+ int m_baseOpcode;
+ int setGlobalAttribute(const std::string & line, size_t lc);
+};
+
+#endif
diff --git a/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp b/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
new file mode 100644
index 0000000..d9b5499
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
@@ -0,0 +1,335 @@
+/*
+* Copyright (C) 2011 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 <stdio.h>
+#include "EntryPoint.h"
+#include <string>
+#include "TypeFactory.h"
+#include "strUtils.h"
+#include <sstream>
+
+
+EntryPoint::EntryPoint()
+{
+ reset();
+}
+
+EntryPoint::~EntryPoint()
+{
+}
+
+void EntryPoint::reset()
+{
+ m_unsupported = false;
+ m_customDecoder = false;
+ m_vars.empty();
+}
+
+bool parseTypeField(const std::string & f, std::string *vartype, bool *pointer_type, std::string *varname)
+{
+ size_t pos = 0, last;
+ bool done = false;
+
+
+ *vartype = "";
+ if (varname != NULL) *varname = "";
+ *pointer_type = false;
+
+ enum { ST_TYPE, ST_NAME, ST_END } state = ST_TYPE;
+
+ while(!done) {
+
+ std::string str = getNextToken(f, pos, &last, WHITESPACE);
+ if (str.size() == 0) break;
+
+ switch(state) {
+ case ST_TYPE:
+ if (str == "const") {
+ pos = last;
+ } else {
+ // must be a type name;
+ *vartype = str;
+ // do we have an astriks at the end of the name?
+ if (vartype->at(vartype->size() - 1) == '*') {
+ *pointer_type = true;
+ // remove the astriks
+ (*vartype)[vartype->size() - 1] = ' ';
+ *vartype = trim(*vartype);
+ }
+ state = ST_NAME;
+ pos = last;
+ }
+ break;
+ case ST_NAME:
+ if (str.size() == 0) {
+ done = true;
+ } else if (str == "*") {
+ *pointer_type = true;
+ // remove the leading astriks;
+ pos = last;
+ } else if (varname == NULL) {
+ done = true;
+ } else {
+ if (str[0] == '*') {
+ *pointer_type = true;
+ str[0] = ' ';
+ str = trim(str);
+ }
+ *varname = str;
+ done = true;
+ }
+ break;
+ case ST_END:
+ break;
+ }
+ }
+ return true;
+}
+
+// return true for valid line (need to get into the entry points list)
+bool EntryPoint::parse(unsigned int lc, const std::string & str)
+{
+ size_t pos, last;
+ std::string field;
+
+ reset();
+ std::string linestr = trim(str);
+
+ if (linestr.size() == 0) return false;
+ if (linestr.at(0) == '#') return false;
+
+ // skip PREFIX
+ field = getNextToken(linestr, 0, &last, "(");
+ pos = last + 1;
+ // return type
+ field = getNextToken(linestr, pos, &last, ",)");
+ std::string retTypeName;
+ bool pointer_type;
+ if (!parseTypeField(field, &retTypeName, &pointer_type, NULL)) {
+ fprintf(stderr, "line: %d: Parsing error in field <%s>\n", lc, field.c_str());
+ return false;
+ }
+ pos = last + 1;
+ const VarType *theType = TypeFactory::instance()->getVarTypeByName(retTypeName);
+ if (theType->name() == "UNKNOWN") {
+ fprintf(stderr, "UNKNOWN retval: %s\n", linestr.c_str());
+ }
+
+ m_retval.init(std::string(""), theType, pointer_type, std::string(""), Var::POINTER_OUT, std::string(""));
+
+ // function name
+ m_name = getNextToken(linestr, pos, &last, ",)");
+ pos = last + 1;
+
+ // parameters;
+ int nvars = 0;
+ while (pos < linestr.size() - 1) {
+ field = getNextToken(linestr, pos, &last, ",)");
+ std::string vartype, varname;
+ if (!parseTypeField(field, &vartype, &pointer_type, &varname)) {
+ fprintf(stderr, "line: %d: Parsing error in field <%s>\n", lc, field.c_str());
+ return false;
+ }
+ nvars++;
+ const VarType *v = TypeFactory::instance()->getVarTypeByName(vartype);
+ if (v->id() == 0) {
+ fprintf(stderr, "%d: Unknown type: %s\n", lc, vartype.c_str());
+ } else {
+ if (varname == "" &&
+ !(v->name() == "void" && !pointer_type)) {
+ std::ostringstream oss;
+ oss << "var" << nvars;
+ varname = oss.str();
+ }
+
+ m_vars.push_back(Var(varname, v, pointer_type, std::string(""), Var::POINTER_IN, ""));
+ }
+ pos = last + 1;
+ }
+ return true;
+}
+
+void EntryPoint::print(FILE *fp, bool newline,
+ const std::string & name_suffix,
+ const std::string & name_prefix,
+ const std::string & ctx_param ) const
+{
+ fprintf(fp, "%s%s %s%s%s(",
+ m_retval.type()->name().c_str(),
+ m_retval.isPointer() ? "*" : "",
+ name_prefix.c_str(),
+ m_name.c_str(),
+ name_suffix.c_str());
+
+ if (ctx_param != "") fprintf(fp, "%s ", ctx_param.c_str());
+
+ for (size_t i = 0; i < m_vars.size(); i++) {
+ if (m_vars[i].isVoid()) continue;
+ if (i != 0 || ctx_param != "") fprintf(fp, ", ");
+ fprintf(fp, "%s %s%s", m_vars[i].type()->name().c_str(),
+ m_vars[i].isPointer() ? "*" : "",
+ m_vars[i].name().c_str());
+ }
+ fprintf(fp, ")%s", newline? "\n" : "");
+}
+
+Var * EntryPoint::var(const std::string & name)
+{
+ Var *v = NULL;
+ for (size_t i = 0; i < m_vars.size(); i++) {
+ if (m_vars[i].name() == name) {
+ v = &m_vars[i];
+ break;
+ }
+ }
+ return v;
+}
+
+bool EntryPoint::hasPointers()
+{
+ bool pointers = false;
+ if (m_retval.isPointer()) pointers = true;
+ if (!pointers) {
+ for (size_t i = 0; i < m_vars.size(); i++) {
+ if (m_vars[i].isPointer()) {
+ pointers = true;
+ break;
+ }
+ }
+ }
+ return pointers;
+}
+
+int EntryPoint::setAttribute(const std::string &line, size_t lc)
+{
+ size_t pos = 0;
+ size_t last;
+ std::string token = getNextToken(line, 0, &last, WHITESPACE);
+
+ if (token == "len") {
+ pos = last;
+ std::string varname = getNextToken(line, pos, &last, WHITESPACE);
+
+ if (varname.size() == 0) {
+ fprintf(stderr, "ERROR: %u: Missing variable name in 'len' attribute\n", (unsigned int)lc);
+ return -1;
+ }
+ Var * v = var(varname);
+ if (v == NULL) {
+ fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
+ (unsigned int)lc, varname.c_str(), name().c_str());
+ return -2;
+ }
+ // set the size expression into var
+ pos = last;
+ v->setLenExpression(line.substr(pos));
+ } else if (token == "dir") {
+ pos = last;
+ std::string varname = getNextToken(line, pos, &last, WHITESPACE);
+ if (varname.size() == 0) {
+ fprintf(stderr, "ERROR: %u: Missing variable name in 'dir' attribute\n", (unsigned int)lc);
+ return -1;
+ }
+ Var * v = var(varname);
+ if (v == NULL) {
+ fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
+ (unsigned int)lc, varname.c_str(), name().c_str());
+ return -2;
+ }
+
+ pos = last;
+ std::string pointerDirStr = getNextToken(line, pos, &last, WHITESPACE);
+ if (pointerDirStr.size() == 0) {
+ fprintf(stderr, "ERROR: %u: missing pointer directions\n", (unsigned int)lc);
+ return -3;
+ }
+
+ if (pointerDirStr == "out") {
+ v->setPointerDir(Var::POINTER_OUT);
+ } else if (pointerDirStr == "inout") {
+ v->setPointerDir(Var::POINTER_INOUT);
+ } else if (pointerDirStr == "in") {
+ v->setPointerDir(Var::POINTER_IN);
+ } else {
+ fprintf(stderr, "ERROR: %u: unknow pointer direction %s\n", (unsigned int)lc, pointerDirStr.c_str());
+ }
+ } else if (token == "var_flag") {
+ pos = last;
+ std::string varname = getNextToken(line, pos, &last, WHITESPACE);
+ if (varname.size() == 0) {
+ fprintf(stderr, "ERROR: %u: Missing variable name in 'var_flag' attribute\n", (unsigned int)lc);
+ return -1;
+ }
+ Var * v = var(varname);
+ if (v == NULL) {
+ fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
+ (unsigned int)lc, varname.c_str(), name().c_str());
+ return -2;
+ }
+ pos = last;
+ std::string flag = getNextToken(line, pos, &last, WHITESPACE);
+ if (flag.size() == 0) {
+ fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
+ return -3;
+ }
+
+ if (flag == "nullAllowed") {
+ if (v->isPointer()) {
+ v->setNullAllowed(true);
+ } else {
+ fprintf(stderr, "WARNING: %u: setting nullAllowed for non-pointer variable %s\n",
+ (unsigned int) lc, v->name().c_str());
+ }
+ } else {
+ fprintf(stderr, "WARNING: %u: unknow flag %s\n", (unsigned int)lc, flag.c_str());
+ }
+ } else if (token == "custom_pack") {
+ pos = last;
+ std::string varname = getNextToken(line, pos, &last, WHITESPACE);
+
+ if (varname.size() == 0) {
+ fprintf(stderr, "ERROR: %u: Missing variable name in 'custom_pack' attribute\n", (unsigned int)lc);
+ return -1;
+ }
+ Var * v = var(varname);
+ if (v == NULL) {
+ fprintf(stderr, "ERROR: %u: variable %s is not a parameter of %s\n",
+ (unsigned int)lc, varname.c_str(), name().c_str());
+ return -2;
+ }
+ // set the size expression into var
+ pos = last;
+ v->setPackExpression(line.substr(pos));
+ } else if (token == "flag") {
+ pos = last;
+ std::string flag = getNextToken(line, pos, &last, WHITESPACE);
+ if (flag.size() == 0) {
+ fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
+ return -4;
+ }
+
+ if (flag == "unsupported") {
+ setUnsupported(true);
+ } else if (flag == "custom_decoder") {
+ setCustomDecoder(true);
+ } else {
+ fprintf(stderr, "WARNING: %u: unknown flag %s\n", (unsigned int)lc, flag.c_str());
+ }
+ } else {
+ fprintf(stderr, "WARNING: %u: unknown attribute %s\n", (unsigned int)lc, token.c_str());
+ }
+
+ return 0;
+}
diff --git a/tools/emulator/opengl/host/tools/emugen/EntryPoint.h b/tools/emulator/opengl/host/tools/emugen/EntryPoint.h
new file mode 100644
index 0000000..c417bda
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/EntryPoint.h
@@ -0,0 +1,64 @@
+/*
+* Copyright (C) 2011 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.
+*/
+#ifndef __EntryPoint__H__
+#define __EntryPoint__H__
+
+#include <string>
+#include <vector>
+#include <stdio.h>
+
+#include "Var.h"
+
+//---------------------------------------------------
+
+typedef std::vector<Var> VarsArray;
+
+class EntryPoint {
+public:
+ EntryPoint();
+ virtual ~EntryPoint();
+ bool parse(unsigned int lc, const std::string & str);
+ void reset(); // reset the class to empty;
+ void print(FILE *fp = stdout, bool newline = true,
+ const std::string & name_suffix = std::string(""),
+ const std::string & name_prefix = std::string(""),
+ const std::string & ctx_param = std::string("")) const;
+ const std::string & name() const { return m_name; }
+ VarsArray & vars() { return m_vars; }
+ Var & retval() { return m_retval; }
+ Var * var(const std::string & name);
+ bool hasPointers();
+ bool unsupported() const { return m_unsupported; }
+ void setUnsupported(bool state) { m_unsupported = state; }
+ bool customDecoder() { return m_customDecoder; }
+ void setCustomDecoder(bool state) { m_customDecoder = state; }
+ int setAttribute(const std::string &line, size_t lc);
+
+private:
+ enum { PR_RETVAL = 0, PR_NAME, PR_VARS, PR_DONE } prState;
+ std::string m_name;
+ Var m_retval;
+ VarsArray m_vars;
+ bool m_unsupported;
+ bool m_customDecoder;
+
+ void err(unsigned int lc, const char *msg) {
+ fprintf(stderr, "line %d: %s\n", lc, msg);
+ }
+};
+
+
+#endif
diff --git a/tools/emulator/opengl/host/tools/emugen/README b/tools/emulator/opengl/host/tools/emugen/README
new file mode 100644
index 0000000..18c3edb
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/README
@@ -0,0 +1,292 @@
+Introduction:
+
+The emugen tool is a tool to generate a wire protocol implementation
+based on provided API. The tool generates c++ encoder code that takes
+API calls and encodes them into the wire and decoder code that decodes
+the wire stream and calls server matching API.
+
+The following paragraphs includes the following:
+ * Wire Protocol Description
+ * Input files description & format
+ * Generated code description.
+
+
+Note: In this document, the caller is referred to as Encoder or Client
+and the callee is referred to as the Decoder or Server. These terms
+are used interchangeably by the context.
+
+
+
+Wire Protocol packet structure:
+
+A general Encoder->Decoder packet is structured as following:
+struct Packet {
+ unsigned int opcode;
+ unsigned int packet_len;
+ … parameter 1
+ … parameter 2
+};
+A general Decoder->Encoder reply is expected to be received in the
+context of the ‘call’ that triggered it, thus it includes the reply
+data only, with no context headers. In precise term terms, a reply
+packet will look like:
+
+struct {
+ ...// reply data
+};
+
+consider the following function call:
+int foo(int p1, short s1)
+will be encoded into :
+{
+ 101, // foo opcode
+ 14, // sizeof(opcode) + sizeof(packet_len) + sizeof(int) + sizeof(short)
+ p1, // 4 bytes
+ s1 // 4 bytes
+}
+
+Since ‘foo’ returns value, the caller is expected to read back the return packet from the server->client stream. The return value in this example is in thus the return packet is:
+{
+ int retval;
+}
+
+
+Pointer decoding:
+
+The wire protocol also allows exchanging of pointer data
+(arrays). Pointers are defined with directions:
+
+ in : Data is sent from the caller to the calle
+ out: Data is sent from the callee to the caller
+ in_out: data is sent from the caller and return in place.
+
+‘in’ and ‘in_out’ encoded with their len:
+{
+ unsinged int pointer_data_len;
+ unsigned char data[pointer_data_len];… // pointer data
+}
+
+‘out’ pointers are encoded by data length only:
+{
+unsigned int pointer_data_len;
+}
+
+‘out’ and ‘in_out’ pointer’s data is returned in the return
+packet. For example, consider the following call:
+
+int foo(int n, int *ptr); // assume that ‘data’ is in_out pointer which contains ‘n’ ints
+
+The caller packet will have the following form:
+{
+ 101, // foo opcode
+ xx, sizeof(opcode) + sizeof(datalen) + sizeof(int) + sizeof(unsigned int) + n * sizeof(int);
+ n, // the n parameter
+ n * sizeof(int), // size of the data in ptr
+ … // n* sizeof(int) bytes
+}
+
+The return packet is;
+{
+ …. // n * sizeof(int) bytes of data return in ptr
+ retval // sizeof(int) - the return value of the function;
+}
+
+Endianess
+
+The Wire protocol is designed to impose minimum overhead on the client
+side. Thus, the data endianness that is sent across the wire is
+determined by the ‘client’ side. It is up to the server side to
+determine the client endianess and marshal the packets as required.
+
+
+
+Emugen input files - protocol specification
+
+The protocol generated by emugen consists of two input files:
+
+1. basename.in - A sepcification of the protocol RPC procedures. This
+part of the specification is expected to be generated automatically
+from c/c++ header files or similar.
+
+‘basename’ is the basename for the protocol and will be used to prefix
+the files that are generated for this protocol. A line in the .in
+file has the following format:
+
+[prefix](retvalType, FuncName, <param type> [param name],...)
+where
+ retvalType - The function return value type
+ FuncName - function name
+ <param type> mandatory parameter type
+ [param name] - optional parameter name
+Examples:
+GL_ENTRY(void, glVertex1f, float v)
+XXX(int *, foo, int n, float, short)
+XXX(void, glFlush, void)
+
+Note: Empty lines in the file are ignored. A line starts with # is a comment
+
+2. basename.attrib - Attributes information of the API.
+This file includes additional flags, pointers datalen information and
+global attributes of the protocol. For uptodate format of the file,
+please refer to the specification file in the project source
+tree. The format of the .attrib file is described below.
+
+3. basename.types - Types information
+
+This files describes the types that are described by the API. A type
+is defined as follows:
+<type name> <size in bits> <print format string>
+where:
+<type name> is the name of the type as described in the API
+<size in bits> 0, 8, 16, 32 sizes are accepted
+<print format string> a string to format the value of the type, as acceted by printf(3)
+
+example:
+GLint 32 %d
+
+Encoder generated code files
+
+In order to generate the encoder files, one should run the ‘emugen’
+tool as follows:
+
+emugen -i <input directory> -E <encoder files output directory> <basename>
+where:
+ <input directory> containes the api specification files (basename.in + basename.attrib)
+ <encoder directory> - a directory name to generate the encoder output files
+ basename - The basename for the api.
+
+Assuming the basename is ‘api’, The following files are generated:
+
+api_opcodes.h - defines the protocol opcodes. The first opcode value
+is 0, unless defined otherwise in the .attrib file
+
+api_entry.cpp - defines entry points for the functions that are
+defined by the protocol. this File also includes a function call
+‘setContextAccessor(void *(*f)()). This function should be used to
+provide a callback function that is used by the functions to access
+the encoder context. For example, such callback could fetch the
+context from a Thread Local Storage (TLS) location.
+
+api_client_proc.h - type defintions for the protocol procedures.
+
+api_client_context.h - defines the client side dispatch table data
+structure that stores the encoding functions. This data structure also
+includes ‘accessors’ methods such that library user can override
+default entries for special case handling.
+
+api_enc.h - This header file defines the encoder data strcuture. The
+encoder data structure inherits its functionality from the
+‘client_context’ class above and adds encoding and streaming
+functionality.
+
+api_enc.cpp - Encoder implementation.
+
+5.1.2.2 Decoder generated files
+In order to generate the decoder files, one should run the ‘emugen’
+tool as follows:
+emugen -i <input directory> -D <decoder files output directory> basename
+where:
+ <input directory> containes the api specification files (basename.in + basename.attrib)
+ <decoder directory> - a directory name to generate the decoder output files
+ basename - The basename for the api.
+
+With resepct to the example above, Emugen will generate the following
+files:
+
+api_opcodes.h - Protocol opcodes
+
+api_server_proc.h - type definitions for the server side procedures
+
+api_server_context.h - dispatch table the encoder functions
+
+api_dec.h - Decoder header file
+
+api_dec.cpp - Decoder implementation. In addtion, this file includes
+an intiailization function that uses a user provided callback to
+initialize the API server implementation. An example for such
+initialization is loading a set of functions from a shared library
+module.
+
+.attrib file format description:
+
+The .attrib file is an input file to emugen and is used to provide
+ additional information that is required for the code generation.
+The file format is as follows:
+
+a line that starts with # is ignored (comment)
+a empty line just whitespace of (" " "\t" "\n") is ignored.
+
+The file is divided into 'sections', each describes a specific API
+function call. A section starts with the name of the function in
+column 0.
+
+A section that starts with the reserved word 'GLOBAL' provides global
+attributes.
+
+below are few sections examples:
+
+GLOBAL
+ encoder_headers string.h kuku.h
+
+glVertex3fv
+ len data (size)
+glTexImage2D
+ len pixels (pixels == NULL? 0 : (format_pixel_size(internalformat) * width * height * type_size(type)))
+
+
+Global section flags description:
+
+base_opcode
+ set the base opcode value for this api
+ format: base_opcode 100
+
+encoder_headers
+ a list of headers that will be included in the encoder header file
+ format: encoder_headers <stdio.h> "kuku.h"
+
+client_context_headers
+ a list of headers that will be included in the client context header file
+ format: client_context_headers <stdio.h> "kuku.h"
+
+decoder_headers
+ a list of headers that will be included in the decoder header file
+ format: decoder_headers <stdio.h> "kuku.h"
+
+server_context_headers
+ a list of headers that will be included in the server context header file
+ format: server_context_headers <stdio.h> "kuku.h"
+
+
+Entry point flags description:
+
+ len
+ desciption : provide an expression to calcualte an expression data len
+ format: len <var name> <c expression that calcluates the data len>
+
+custom_pack
+ description: provide an expression to pack data into the stream.
+ format: custom_pack <var name> <c++ expression that pack data from var into the stream>
+ The stream is represented by a (unsigned char *)ptr. The expression may also refer
+ to other function parameters. In addition, the expression may refer to 'void *self' which
+ is the encoding context as provided by the caller.
+
+ dir
+ description : set a pointer direction (in - for data that goes
+ to the codec, out from data that returns from the codec.
+ format: dir <varname> <[in | out | inout]>
+
+ var_flag
+ description : set variable flags
+ format: var_flag <varname> < nullAllowed | ... >
+
+ flag
+ description: set entry point flag;
+ format: flag < unsupported | ... >
+ supported flags are:
+ unsupported - The encoder side implementation is pointed to "unsuppored reporting function".
+ custom_decoder - The decoder is expected to be provided with
+ custom implementation. The call to the
+ deocder function includes a pointer to the
+ context
+
+
diff --git a/tools/emulator/opengl/host/tools/emugen/TypeFactory.cpp b/tools/emulator/opengl/host/tools/emugen/TypeFactory.cpp
new file mode 100644
index 0000000..709807e
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/TypeFactory.cpp
@@ -0,0 +1,135 @@
+/*
+* Copyright (C) 2011 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 "TypeFactory.h"
+#include "VarType.h"
+#include <string>
+#include <map>
+#include <stdio.h>
+#include <stdlib.h>
+#include "strUtils.h"
+
+
+TypeFactory * TypeFactory::m_instance = NULL;
+
+static Var0 g_var0;
+static Var8 g_var8;
+static Var16 g_var16;
+static Var32 g_var32;
+
+typedef std::map<std::string, VarType> TypeMap;
+static TypeMap g_varMap;
+static bool g_initialized = false;
+static int g_typeId = 0;
+
+
+static VarConverter * getVarConverter(int size)
+{
+ VarConverter *v = NULL;
+
+ switch(size) {
+ case 0: v = &g_var0; break;
+ case 8: v = &g_var8; break;
+ case 16: v = &g_var16; break;
+ case 32: v = &g_var32; break;
+ }
+ return v;
+}
+
+#define ADD_TYPE(name, size, printformat) \
+ g_varMap.insert(std::pair<std::string, VarType>(name, VarType(g_typeId++, name, &g_var##size,printformat)));
+
+void TypeFactory::initBaseTypes()
+{
+ g_initialized = true;
+ ADD_TYPE("UNKNOWN", 0, "0x%x");
+ ADD_TYPE("void", 0, "0x%x");
+ ADD_TYPE("char", 8, "%c");
+ ADD_TYPE("int", 32, "%d");
+ ADD_TYPE("float", 32, "%d");
+ ADD_TYPE("short", 16, "%d");
+}
+
+int TypeFactory::initFromFile(const std::string &filename)
+{
+ if (!g_initialized) {
+ initBaseTypes();
+ }
+
+ FILE *fp = fopen(filename.c_str(), "rt");
+ if (fp == NULL) {
+ perror(filename.c_str());
+ return -1;
+ }
+ char line[1000];
+ int lc = 0;
+ while(fgets(line, sizeof(line), fp) != NULL) {
+ lc++;
+ std::string str = trim(line);
+ if (str.size() == 0 || str.at(0) == '#') {
+ continue;
+ }
+ size_t pos = 0, last;
+ std::string name;
+ name = getNextToken(str, pos, &last, WHITESPACE);
+ if (name.size() == 0) {
+ fprintf(stderr, "Error: %d : missing type name\n", lc);
+ return -2;
+ }
+ pos = last + 1;
+ std::string size;
+ size = getNextToken(str, pos, &last, WHITESPACE);
+ if (size.size() == 0) {
+ fprintf(stderr, "Error: %d : missing type width\n", lc);
+ return -2;
+ }
+ pos = last + 1;
+ std::string printString;
+ printString = getNextToken(str, pos, &last, WHITESPACE);
+ if (printString.size() == 0) {
+ fprintf(stderr, "Error: %d : missing print-string\n", lc);
+ return -2;
+ }
+
+ VarConverter *v = getVarConverter(atoi(size.c_str()));
+ if (v == NULL) {
+ fprintf(stderr, "Error: %d : unknown var width: %d\n", lc, atoi(size.c_str()));
+ return -1;
+ }
+
+ if (getVarTypeByName(name)->id() != 0) {
+ fprintf(stderr,
+ "Warining: %d : type %s is already known, definition in line %d is taken\n",
+ lc, name.c_str(), lc);
+ }
+ g_varMap.insert(std::pair<std::string, VarType>(name, VarType(g_typeId++, name, v ,printString)));
+ }
+ g_initialized = true;
+ return 0;
+}
+
+
+const VarType * TypeFactory::getVarTypeByName(const std::string & type)
+{
+ if (!g_initialized) {
+ initBaseTypes();
+ }
+ TypeMap::iterator i = g_varMap.find(type);
+ if (i == g_varMap.end()) {
+ i = g_varMap.find("UNKNOWN");
+ }
+ return &(i->second);
+}
+
diff --git a/tools/emulator/opengl/host/tools/emugen/TypeFactory.h b/tools/emulator/opengl/host/tools/emugen/TypeFactory.h
new file mode 100644
index 0000000..deee2ca
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/TypeFactory.h
@@ -0,0 +1,37 @@
+/*
+* Copyright (C) 2011 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.
+*/
+#ifndef __TYPE__FACTORY__H__
+#define __TYPE__FACTORY__H__
+
+#include <string>
+#include "VarType.h"
+
+class TypeFactory {
+public:
+ static TypeFactory *instance() {
+ if (m_instance == NULL) {
+ m_instance = new TypeFactory;
+ }
+ return m_instance;
+ }
+ const VarType * getVarTypeByName(const std::string &type);
+ int initFromFile(const std::string &filename);
+private:
+ static TypeFactory *m_instance;
+ void initBaseTypes();
+ TypeFactory() {}
+};
+#endif
diff --git a/tools/emulator/opengl/host/tools/emugen/Var.h b/tools/emulator/opengl/host/tools/emugen/Var.h
new file mode 100644
index 0000000..ef9f7c2
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/Var.h
@@ -0,0 +1,94 @@
+/*
+* Copyright (C) 2011 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.
+*/
+#ifndef __VAR__H__
+#define __VAR__H__
+
+#include "VarType.h"
+#include <string>
+#include <stdio.h>
+
+class Var {
+public:
+ // pointer data direction - from the client point of view.
+ typedef enum { POINTER_OUT = 0x1, POINTER_IN = 0x2, POINTER_INOUT = 0x3 } PointerDir;
+ Var() :
+ m_name(""),
+ m_type(NULL),
+ m_pointer(false),
+ m_lenExpression(""),
+ m_pointerDir(POINTER_IN),
+ m_nullAllowed(false),
+ m_packExpression("")
+
+ {
+ }
+
+ Var(const std::string & name,
+ const VarType * vartype,
+ bool isPointer,
+ const std::string & lenExpression,
+ PointerDir dir,
+ const std::string &packExpression) :
+ m_name(name),
+ m_type(const_cast<VarType *>(vartype)),
+ m_pointer(isPointer),
+ m_lenExpression(lenExpression),
+ m_pointerDir(dir),
+ m_nullAllowed(false),
+ m_packExpression(packExpression)
+ {
+ }
+
+ void init(const std::string name, const VarType * vartype,
+ bool isPointer, std::string lenExpression,
+ PointerDir dir, std::string packExpression) {
+ m_name = name;
+ m_type = vartype;
+ m_pointer = isPointer;
+ m_lenExpression = lenExpression;
+ m_packExpression = packExpression;
+ m_pointerDir = dir;
+ m_nullAllowed = false;
+
+ }
+
+ const std::string & name() const { return m_name; }
+ const VarType * type() const { return m_type; }
+ bool isPointer() const { return m_pointer; }
+ bool isVoid() const { return ((m_type->bytes() == 0) && (m_pointer == false)); }
+ const std::string & lenExpression() const { return m_lenExpression; }
+ const std::string & packExpression() const { return(m_packExpression); }
+ void setLenExpression(const std::string & lenExpression) { m_lenExpression = lenExpression; }
+ void setPackExpression(const std::string & packExpression) { m_packExpression = packExpression; }
+ void setPointerDir(PointerDir dir) { m_pointerDir = dir; }
+ PointerDir pointerDir() { return m_pointerDir; }
+ void setNullAllowed(bool state) { m_nullAllowed = state; }
+ bool nullAllowed() const { return m_nullAllowed; }
+ void printType(FILE *fp) { fprintf(fp, "%s%s", m_type->name().c_str(), m_pointer ? "*" : ""); }
+ void printTypeName(FILE *fp) { printType(fp); fprintf(fp, " %s", m_name.c_str()); }
+
+private:
+ std::string m_name;
+ const VarType * m_type;
+ bool m_pointer; // is this variable a pointer;
+ std::string m_lenExpression; // an expression to calcualte a pointer data size
+ PointerDir m_pointerDir;
+ bool m_nullAllowed;
+ std::string m_packExpression; // an expression to pack data into the stream
+
+};
+
+#endif
diff --git a/tools/emulator/opengl/host/tools/emugen/VarType.h b/tools/emulator/opengl/host/tools/emugen/VarType.h
new file mode 100644
index 0000000..a0718bb
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/VarType.h
@@ -0,0 +1,76 @@
+/*
+* Copyright (C) 2011 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.
+*/
+#ifndef __VARTYPE__H__
+#define __VARTYPE__H__
+
+#include <string>
+
+class VarConverter {
+public:
+ VarConverter(size_t bytes) : m_bytes(bytes) {}
+ size_t bytes() const { return m_bytes; }
+private:
+ size_t m_bytes;
+};
+
+class Var8 : public VarConverter {
+public:
+ Var8() : VarConverter(1) {}
+};
+
+class Var16 : public VarConverter {
+public:
+ Var16() : VarConverter(2) {}
+};
+
+class Var32 : public VarConverter {
+public:
+ Var32() : VarConverter(4) {}
+};
+
+class Var0 : public VarConverter {
+public:
+ Var0() : VarConverter(0) {}
+};
+
+
+class VarType {
+public:
+ VarType() :
+ m_id(0), m_name("default_constructed"), m_converter(NULL), m_printFomrat("0x%x")
+ {
+ }
+
+ VarType(size_t id, const std::string & name, const VarConverter * converter, const std::string & printFormat ) :
+ m_id(id), m_name(name), m_converter(const_cast<VarConverter *>(converter)), m_printFomrat(printFormat)
+ {
+ }
+
+ ~VarType()
+ {
+ }
+ const std::string & name() const { return m_name; }
+ const std::string & printFormat() const { return m_printFomrat; }
+ size_t bytes() const { return m_converter->bytes(); }
+ size_t id() const { return m_id; }
+private:
+ size_t m_id;
+ std::string m_name;
+ VarConverter * m_converter;
+ std::string m_printFomrat;
+};
+
+#endif
diff --git a/tools/emulator/opengl/host/tools/emugen/errors.h b/tools/emulator/opengl/host/tools/emugen/errors.h
new file mode 100644
index 0000000..d09c292
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/errors.h
@@ -0,0 +1,24 @@
+/*
+* Copyright (C) 2011 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.
+*/
+#ifndef _ERRORS_H_
+#define _ERRORS_H_
+
+#define BAD_USAGE -1
+#define BAD_SPEC_FILE -2
+#define BAD_TYPES_FILE -3
+#define BAD_ATTRIBUTES_FILE -4
+
+#endif
diff --git a/tools/emulator/opengl/host/tools/emugen/main.cpp b/tools/emulator/opengl/host/tools/emugen/main.cpp
new file mode 100644
index 0000000..aefba0a
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/main.cpp
@@ -0,0 +1,147 @@
+/*
+* Copyright (C) 2011 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 <stdio.h>
+#include <stdlib.h>
+#include "errors.h"
+#include "EntryPoint.h"
+#include "strUtils.h"
+#include "ApiGen.h"
+#include "TypeFactory.h"
+
+const std::string SPEC_EXTENSION = std::string(".in");
+const std::string ATTRIB_EXTENSION = std::string(".attrib");
+const std::string TYPES_EXTENTION = std::string(".types");
+
+
+void usage(const char *filename)
+{
+ fprintf(stderr, "Usage: %s [options] <base name>\n", filename);
+ fprintf(stderr, "\t-h: This message\n");
+ fprintf(stderr, "\t-E <dir>: generate encoder into dir\n");
+ fprintf(stderr, "\t-D <dir>: generate decoder into dir\n");
+ fprintf(stderr, "\t-i: input dir, local directory by default\n");
+ fprintf(stderr, "\t-T : generate attribute template into the input directory\n\t\tno other files are generated\n");
+}
+
+int main(int argc, char *argv[])
+{
+ std::string encoderDir = "";
+ std::string decoderDir = "";
+ std::string inDir = ".";
+ bool generateAttributesTemplate = false;
+
+ int c;
+ while((c = getopt(argc, argv, "TE:D:i:h")) != -1) {
+ switch(c) {
+ case 'T':
+ generateAttributesTemplate = true;
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ case 'E':
+ encoderDir = std::string(optarg);
+ break;
+ case 'D':
+ decoderDir = std::string(optarg);
+ break;
+ case 'i':
+ inDir = std::string(optarg);
+ break;
+ case ':':
+ fprintf(stderr, "Missing argument !!\n");
+ // fall through
+ default:
+ usage(argv[0]);
+ exit(0);
+ }
+ }
+
+ if (optind >= argc) {
+ fprintf(stderr, "Usage: %s [options] <base name> \n", argv[0]);
+ return BAD_USAGE;
+ }
+
+ if (encoderDir.size() == 0 && decoderDir.size() == 0 && generateAttributesTemplate == false) {
+ fprintf(stderr, "No output specified - aborting\n");
+ return BAD_USAGE;
+ }
+
+ std::string baseName = std::string(argv[optind]);
+ ApiGen apiEntries(baseName);
+
+ // init types;
+ std::string typesFilename = inDir + "/" + baseName + TYPES_EXTENTION;
+
+ if (TypeFactory::instance()->initFromFile(typesFilename) < 0) {
+ fprintf(stderr, "missing or error reading types file: %s...ignored\n", typesFilename.c_str());
+ }
+
+ std::string filename = inDir + "/" + baseName + SPEC_EXTENSION;
+ if (apiEntries.readSpec(filename) < 0) {
+ perror(filename.c_str());
+ return BAD_SPEC_FILE;
+ }
+
+
+ if (generateAttributesTemplate) {
+ apiEntries.genAttributesTemplate(inDir + "/" + baseName + ATTRIB_EXTENSION);
+ exit(0);
+ }
+
+ std::string attribFileName = inDir + "/" + baseName + ATTRIB_EXTENSION;
+ if (apiEntries.readAttributes(attribFileName) < 0) {
+ perror(attribFileName.c_str());
+ fprintf(stderr, "failed to parse attributes\n");
+ exit(1);
+ }
+
+ if (encoderDir.size() != 0) {
+
+ apiEntries.genOpcodes(encoderDir + "/" + baseName + "_opcodes.h");
+ apiEntries.genContext(encoderDir + "/" + baseName + "_client_context.h", ApiGen::CLIENT_SIDE);
+ apiEntries.genProcTypes(encoderDir + "/" + baseName + "_client_proc.h", ApiGen::CLIENT_SIDE);
+
+ apiEntries.genClientEntryPoints(encoderDir + "/" + baseName + "_entry.cpp");
+ apiEntries.genEncoderHeader(encoderDir + "/" + baseName + "_enc.h");
+ apiEntries.genEncoderImpl(encoderDir + "/" + baseName + "_enc.cpp");
+ }
+
+ if (decoderDir.size() != 0) {
+ //apiEntries.genEntryPoints(decoderDir + "/" + baseName + "_entry.cpp", baseName);
+ apiEntries.genOpcodes(decoderDir + "/" + baseName + "_opcodes.h");
+ apiEntries.genProcTypes(decoderDir + "/" + baseName + "_server_proc.h", ApiGen::SERVER_SIDE);
+ apiEntries.genContext(decoderDir + "/" + baseName + "_server_context.h", ApiGen::SERVER_SIDE);
+ apiEntries.genDecoderHeader(decoderDir + "/" + baseName + "_dec.h");
+ apiEntries.genDecoderImpl(decoderDir + "/" + baseName + "_dec.cpp");
+ // generate the encoder type;
+
+ }
+#ifdef DEBUG_DUMP
+ int withPointers = 0;
+ printf("%d functions found\n", int(apiEntries.size()));
+ for (int i = 0; i < apiEntries.size(); i++) {
+ if (apiEntries[i].hasPointers()) {
+ withPointers++;
+ apiEntries[i].print();
+ }
+ }
+ fprintf(stdout, "%d entries has poitners\n", withPointers);
+#endif
+
+}
+
diff --git a/tools/emulator/opengl/host/tools/emugen/strUtils.cpp b/tools/emulator/opengl/host/tools/emugen/strUtils.cpp
new file mode 100644
index 0000000..357054b
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/strUtils.cpp
@@ -0,0 +1,49 @@
+/*
+* Copyright (C) 2011 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 "strUtils.h"
+
+using namespace std;
+
+
+std::string getNextToken(const std::string & str, size_t pos, size_t * last, const std::string & delim)
+{
+ if (str.size() == 0 || pos >= str.size()) return "";
+
+ pos = str.find_first_not_of(WHITESPACE, pos);
+ if (pos == std::string::npos) return "";
+
+ *last = str.find_first_of(delim, pos);
+ if (*last == std::string::npos) *last = str.size();
+ std::string retval = str.substr(pos, *last - pos);
+ retval = trim(retval);
+ return retval;
+}
+
+
+std::string trim(const string & str)
+{
+ string result;
+ string::size_type start = str.find_first_not_of(WHITESPACE, 0);
+ string::size_type end = str.find_last_not_of(WHITESPACE);
+ if (start == string::npos || end == string::npos) {
+ result = string("");
+ } else {
+ result = str.substr(start, end - start + 1);
+ }
+ return result;
+}
+
+
diff --git a/tools/emulator/opengl/host/tools/emugen/strUtils.h b/tools/emulator/opengl/host/tools/emugen/strUtils.h
new file mode 100644
index 0000000..3fa0908
--- /dev/null
+++ b/tools/emulator/opengl/host/tools/emugen/strUtils.h
@@ -0,0 +1,33 @@
+/*
+* Copyright (C) 2011 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.
+*/
+#ifndef STR_UTILS_H_
+#define STR_UTILS_H_
+
+#include <string>
+#include <sstream>
+
+#define WHITESPACE " \t\n"
+
+std::string trim(const std::string & str);
+std::string getNextToken(const std::string & str, size_t pos, size_t * last, const std::string & delim);
+template <class T> std::string inline toString(const T& t) {
+ std::stringstream ss;
+ ss << t;
+ return ss.str();
+
+}
+
+#endif
diff --git a/tools/emulator/system/gps/Android.mk b/tools/emulator/system/gps/Android.mk
new file mode 100644
index 0000000..41de596
--- /dev/null
+++ b/tools/emulator/system/gps/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2010 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.
+
+
+# We're moving the emulator-specific platform libs to
+# development.git/tools/emulator/. The following test is to ensure
+# smooth builds even if the tree contains both versions.
+#
+ifndef BUILD_EMULATOR_GPS_MODULE
+BUILD_EMULATOR_GPS_MODULE := true
+
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_PRODUCT),sim)
+# HAL module implemenation stored in
+# hw/<GPS_HARDWARE_MODULE_ID>.<ro.hardware>.so
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_CFLAGS += -DQEMU_HARDWARE
+LOCAL_SHARED_LIBRARIES := liblog libcutils libhardware
+LOCAL_SRC_FILES := gps_qemu.c
+LOCAL_MODULE := gps.goldfish
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_SHARED_LIBRARY)
+endif
+
+endif # BUILD_EMULATOR_GPS_MODULE
diff --git a/tools/emulator/system/gps/gps_qemu.c b/tools/emulator/system/gps/gps_qemu.c
new file mode 100644
index 0000000..a4699d3
--- /dev/null
+++ b/tools/emulator/system/gps/gps_qemu.c
@@ -0,0 +1,941 @@
+/*
+ * Copyright (C) 2010 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 implements a GPS hardware library for the Android emulator.
+ * the following code should be built as a shared library that will be
+ * placed into /system/lib/hw/gps.goldfish.so
+ *
+ * it will be loaded by the code in hardware/libhardware/hardware.c
+ * which is itself called from android_location_GpsLocationProvider.cpp
+ */
+
+
+#include <errno.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <math.h>
+#include <time.h>
+
+#define LOG_TAG "gps_qemu"
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+#include <hardware/gps.h>
+#include <hardware/qemud.h>
+
+/* the name of the qemud-controlled socket */
+#define QEMU_CHANNEL_NAME "gps"
+
+#define GPS_DEBUG 0
+
+#if GPS_DEBUG
+# define D(...) LOGD(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+/*****************************************************************/
+/*****************************************************************/
+/***** *****/
+/***** N M E A T O K E N I Z E R *****/
+/***** *****/
+/*****************************************************************/
+/*****************************************************************/
+
+typedef struct {
+ const char* p;
+ const char* end;
+} Token;
+
+#define MAX_NMEA_TOKENS 16
+
+typedef struct {
+ int count;
+ Token tokens[ MAX_NMEA_TOKENS ];
+} NmeaTokenizer;
+
+static int
+nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end )
+{
+ int count = 0;
+ char* q;
+
+ // the initial '$' is optional
+ if (p < end && p[0] == '$')
+ p += 1;
+
+ // remove trailing newline
+ if (end > p && end[-1] == '\n') {
+ end -= 1;
+ if (end > p && end[-1] == '\r')
+ end -= 1;
+ }
+
+ // get rid of checksum at the end of the sentecne
+ if (end >= p+3 && end[-3] == '*') {
+ end -= 3;
+ }
+
+ while (p < end) {
+ const char* q = p;
+
+ q = memchr(p, ',', end-p);
+ if (q == NULL)
+ q = end;
+
+ if (q > p) {
+ if (count < MAX_NMEA_TOKENS) {
+ t->tokens[count].p = p;
+ t->tokens[count].end = q;
+ count += 1;
+ }
+ }
+ if (q < end)
+ q += 1;
+
+ p = q;
+ }
+
+ t->count = count;
+ return count;
+}
+
+static Token
+nmea_tokenizer_get( NmeaTokenizer* t, int index )
+{
+ Token tok;
+ static const char* dummy = "";
+
+ if (index < 0 || index >= t->count) {
+ tok.p = tok.end = dummy;
+ } else
+ tok = t->tokens[index];
+
+ return tok;
+}
+
+
+static int
+str2int( const char* p, const char* end )
+{
+ int result = 0;
+ int len = end - p;
+
+ for ( ; len > 0; len--, p++ )
+ {
+ int c;
+
+ if (p >= end)
+ goto Fail;
+
+ c = *p - '0';
+ if ((unsigned)c >= 10)
+ goto Fail;
+
+ result = result*10 + c;
+ }
+ return result;
+
+Fail:
+ return -1;
+}
+
+static double
+str2float( const char* p, const char* end )
+{
+ int result = 0;
+ int len = end - p;
+ char temp[16];
+
+ if (len >= (int)sizeof(temp))
+ return 0.;
+
+ memcpy( temp, p, len );
+ temp[len] = 0;
+ return strtod( temp, NULL );
+}
+
+/*****************************************************************/
+/*****************************************************************/
+/***** *****/
+/***** N M E A P A R S E R *****/
+/***** *****/
+/*****************************************************************/
+/*****************************************************************/
+
+#define NMEA_MAX_SIZE 83
+
+typedef struct {
+ int pos;
+ int overflow;
+ int utc_year;
+ int utc_mon;
+ int utc_day;
+ int utc_diff;
+ GpsLocation fix;
+ gps_location_callback callback;
+ char in[ NMEA_MAX_SIZE+1 ];
+} NmeaReader;
+
+
+static void
+nmea_reader_update_utc_diff( NmeaReader* r )
+{
+ time_t now = time(NULL);
+ struct tm tm_local;
+ struct tm tm_utc;
+ long time_local, time_utc;
+
+ gmtime_r( &now, &tm_utc );
+ localtime_r( &now, &tm_local );
+
+ time_local = tm_local.tm_sec +
+ 60*(tm_local.tm_min +
+ 60*(tm_local.tm_hour +
+ 24*(tm_local.tm_yday +
+ 365*tm_local.tm_year)));
+
+ time_utc = tm_utc.tm_sec +
+ 60*(tm_utc.tm_min +
+ 60*(tm_utc.tm_hour +
+ 24*(tm_utc.tm_yday +
+ 365*tm_utc.tm_year)));
+
+ r->utc_diff = time_utc - time_local;
+}
+
+
+static void
+nmea_reader_init( NmeaReader* r )
+{
+ memset( r, 0, sizeof(*r) );
+
+ r->pos = 0;
+ r->overflow = 0;
+ r->utc_year = -1;
+ r->utc_mon = -1;
+ r->utc_day = -1;
+ r->callback = NULL;
+ r->fix.size = sizeof(r->fix);
+
+ nmea_reader_update_utc_diff( r );
+}
+
+
+static void
+nmea_reader_set_callback( NmeaReader* r, gps_location_callback cb )
+{
+ r->callback = cb;
+ if (cb != NULL && r->fix.flags != 0) {
+ D("%s: sending latest fix to new callback", __FUNCTION__);
+ r->callback( &r->fix );
+ r->fix.flags = 0;
+ }
+}
+
+
+static int
+nmea_reader_update_time( NmeaReader* r, Token tok )
+{
+ int hour, minute;
+ double seconds;
+ struct tm tm;
+ time_t fix_time;
+
+ if (tok.p + 6 > tok.end)
+ return -1;
+
+ if (r->utc_year < 0) {
+ // no date yet, get current one
+ time_t now = time(NULL);
+ gmtime_r( &now, &tm );
+ r->utc_year = tm.tm_year + 1900;
+ r->utc_mon = tm.tm_mon + 1;
+ r->utc_day = tm.tm_mday;
+ }
+
+ hour = str2int(tok.p, tok.p+2);
+ minute = str2int(tok.p+2, tok.p+4);
+ seconds = str2float(tok.p+4, tok.end);
+
+ tm.tm_hour = hour;
+ tm.tm_min = minute;
+ tm.tm_sec = (int) seconds;
+ tm.tm_year = r->utc_year - 1900;
+ tm.tm_mon = r->utc_mon - 1;
+ tm.tm_mday = r->utc_day;
+ tm.tm_isdst = -1;
+
+ fix_time = mktime( &tm ) + r->utc_diff;
+ r->fix.timestamp = (long long)fix_time * 1000;
+ return 0;
+}
+
+static int
+nmea_reader_update_date( NmeaReader* r, Token date, Token time )
+{
+ Token tok = date;
+ int day, mon, year;
+
+ if (tok.p + 6 != tok.end) {
+ D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
+ return -1;
+ }
+ day = str2int(tok.p, tok.p+2);
+ mon = str2int(tok.p+2, tok.p+4);
+ year = str2int(tok.p+4, tok.p+6) + 2000;
+
+ if ((day|mon|year) < 0) {
+ D("date not properly formatted: '%.*s'", tok.end-tok.p, tok.p);
+ return -1;
+ }
+
+ r->utc_year = year;
+ r->utc_mon = mon;
+ r->utc_day = day;
+
+ return nmea_reader_update_time( r, time );
+}
+
+
+static double
+convert_from_hhmm( Token tok )
+{
+ double val = str2float(tok.p, tok.end);
+ int degrees = (int)(floor(val) / 100);
+ double minutes = val - degrees*100.;
+ double dcoord = degrees + minutes / 60.0;
+ return dcoord;
+}
+
+
+static int
+nmea_reader_update_latlong( NmeaReader* r,
+ Token latitude,
+ char latitudeHemi,
+ Token longitude,
+ char longitudeHemi )
+{
+ double lat, lon;
+ Token tok;
+
+ tok = latitude;
+ if (tok.p + 6 > tok.end) {
+ D("latitude is too short: '%.*s'", tok.end-tok.p, tok.p);
+ return -1;
+ }
+ lat = convert_from_hhmm(tok);
+ if (latitudeHemi == 'S')
+ lat = -lat;
+
+ tok = longitude;
+ if (tok.p + 6 > tok.end) {
+ D("longitude is too short: '%.*s'", tok.end-tok.p, tok.p);
+ return -1;
+ }
+ lon = convert_from_hhmm(tok);
+ if (longitudeHemi == 'W')
+ lon = -lon;
+
+ r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG;
+ r->fix.latitude = lat;
+ r->fix.longitude = lon;
+ return 0;
+}
+
+
+static int
+nmea_reader_update_altitude( NmeaReader* r,
+ Token altitude,
+ Token units )
+{
+ double alt;
+ Token tok = altitude;
+
+ if (tok.p >= tok.end)
+ return -1;
+
+ r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE;
+ r->fix.altitude = str2float(tok.p, tok.end);
+ return 0;
+}
+
+
+static int
+nmea_reader_update_bearing( NmeaReader* r,
+ Token bearing )
+{
+ double alt;
+ Token tok = bearing;
+
+ if (tok.p >= tok.end)
+ return -1;
+
+ r->fix.flags |= GPS_LOCATION_HAS_BEARING;
+ r->fix.bearing = str2float(tok.p, tok.end);
+ return 0;
+}
+
+
+static int
+nmea_reader_update_speed( NmeaReader* r,
+ Token speed )
+{
+ double alt;
+ Token tok = speed;
+
+ if (tok.p >= tok.end)
+ return -1;
+
+ r->fix.flags |= GPS_LOCATION_HAS_SPEED;
+ r->fix.speed = str2float(tok.p, tok.end);
+ return 0;
+}
+
+
+static void
+nmea_reader_parse( NmeaReader* r )
+{
+ /* we received a complete sentence, now parse it to generate
+ * a new GPS fix...
+ */
+ NmeaTokenizer tzer[1];
+ Token tok;
+
+ D("Received: '%.*s'", r->pos, r->in);
+ if (r->pos < 9) {
+ D("Too short. discarded.");
+ return;
+ }
+
+ nmea_tokenizer_init(tzer, r->in, r->in + r->pos);
+#if GPS_DEBUG
+ {
+ int n;
+ D("Found %d tokens", tzer->count);
+ for (n = 0; n < tzer->count; n++) {
+ Token tok = nmea_tokenizer_get(tzer,n);
+ D("%2d: '%.*s'", n, tok.end-tok.p, tok.p);
+ }
+ }
+#endif
+
+ tok = nmea_tokenizer_get(tzer, 0);
+ if (tok.p + 5 > tok.end) {
+ D("sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p);
+ return;
+ }
+
+ // ignore first two characters.
+ tok.p += 2;
+ if ( !memcmp(tok.p, "GGA", 3) ) {
+ // GPS fix
+ Token tok_time = nmea_tokenizer_get(tzer,1);
+ Token tok_latitude = nmea_tokenizer_get(tzer,2);
+ Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3);
+ Token tok_longitude = nmea_tokenizer_get(tzer,4);
+ Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5);
+ Token tok_altitude = nmea_tokenizer_get(tzer,9);
+ Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10);
+
+ nmea_reader_update_time(r, tok_time);
+ nmea_reader_update_latlong(r, tok_latitude,
+ tok_latitudeHemi.p[0],
+ tok_longitude,
+ tok_longitudeHemi.p[0]);
+ nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits);
+
+ } else if ( !memcmp(tok.p, "GSA", 3) ) {
+ // do something ?
+ } else if ( !memcmp(tok.p, "RMC", 3) ) {
+ Token tok_time = nmea_tokenizer_get(tzer,1);
+ Token tok_fixStatus = nmea_tokenizer_get(tzer,2);
+ Token tok_latitude = nmea_tokenizer_get(tzer,3);
+ Token tok_latitudeHemi = nmea_tokenizer_get(tzer,4);
+ Token tok_longitude = nmea_tokenizer_get(tzer,5);
+ Token tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
+ Token tok_speed = nmea_tokenizer_get(tzer,7);
+ Token tok_bearing = nmea_tokenizer_get(tzer,8);
+ Token tok_date = nmea_tokenizer_get(tzer,9);
+
+ D("in RMC, fixStatus=%c", tok_fixStatus.p[0]);
+ if (tok_fixStatus.p[0] == 'A')
+ {
+ nmea_reader_update_date( r, tok_date, tok_time );
+
+ nmea_reader_update_latlong( r, tok_latitude,
+ tok_latitudeHemi.p[0],
+ tok_longitude,
+ tok_longitudeHemi.p[0] );
+
+ nmea_reader_update_bearing( r, tok_bearing );
+ nmea_reader_update_speed ( r, tok_speed );
+ }
+ } else {
+ tok.p -= 2;
+ D("unknown sentence '%.*s", tok.end-tok.p, tok.p);
+ }
+ if (r->fix.flags != 0) {
+#if GPS_DEBUG
+ char temp[256];
+ char* p = temp;
+ char* end = p + sizeof(temp);
+ struct tm utc;
+
+ p += snprintf( p, end-p, "sending fix" );
+ if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) {
+ p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude);
+ }
+ if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) {
+ p += snprintf(p, end-p, " altitude=%g", r->fix.altitude);
+ }
+ if (r->fix.flags & GPS_LOCATION_HAS_SPEED) {
+ p += snprintf(p, end-p, " speed=%g", r->fix.speed);
+ }
+ if (r->fix.flags & GPS_LOCATION_HAS_BEARING) {
+ p += snprintf(p, end-p, " bearing=%g", r->fix.bearing);
+ }
+ if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) {
+ p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy);
+ }
+ gmtime_r( (time_t*) &r->fix.timestamp, &utc );
+ p += snprintf(p, end-p, " time=%s", asctime( &utc ) );
+ D(temp);
+#endif
+ if (r->callback) {
+ r->callback( &r->fix );
+ r->fix.flags = 0;
+ }
+ else {
+ D("no callback, keeping data until needed !");
+ }
+ }
+}
+
+
+static void
+nmea_reader_addc( NmeaReader* r, int c )
+{
+ if (r->overflow) {
+ r->overflow = (c != '\n');
+ return;
+ }
+
+ if (r->pos >= (int) sizeof(r->in)-1 ) {
+ r->overflow = 1;
+ r->pos = 0;
+ return;
+ }
+
+ r->in[r->pos] = (char)c;
+ r->pos += 1;
+
+ if (c == '\n') {
+ nmea_reader_parse( r );
+ r->pos = 0;
+ }
+}
+
+
+/*****************************************************************/
+/*****************************************************************/
+/***** *****/
+/***** C O N N E C T I O N S T A T E *****/
+/***** *****/
+/*****************************************************************/
+/*****************************************************************/
+
+/* commands sent to the gps thread */
+enum {
+ CMD_QUIT = 0,
+ CMD_START = 1,
+ CMD_STOP = 2
+};
+
+
+/* this is the state of our connection to the qemu_gpsd daemon */
+typedef struct {
+ int init;
+ int fd;
+ GpsCallbacks callbacks;
+ pthread_t thread;
+ int control[2];
+} GpsState;
+
+static GpsState _gps_state[1];
+
+
+static void
+gps_state_done( GpsState* s )
+{
+ // tell the thread to quit, and wait for it
+ char cmd = CMD_QUIT;
+ void* dummy;
+ write( s->control[0], &cmd, 1 );
+ pthread_join(s->thread, &dummy);
+
+ // close the control socket pair
+ close( s->control[0] ); s->control[0] = -1;
+ close( s->control[1] ); s->control[1] = -1;
+
+ // close connection to the QEMU GPS daemon
+ close( s->fd ); s->fd = -1;
+ s->init = 0;
+}
+
+
+static void
+gps_state_start( GpsState* s )
+{
+ char cmd = CMD_START;
+ int ret;
+
+ do { ret=write( s->control[0], &cmd, 1 ); }
+ while (ret < 0 && errno == EINTR);
+
+ if (ret != 1)
+ D("%s: could not send CMD_START command: ret=%d: %s",
+ __FUNCTION__, ret, strerror(errno));
+}
+
+
+static void
+gps_state_stop( GpsState* s )
+{
+ char cmd = CMD_STOP;
+ int ret;
+
+ do { ret=write( s->control[0], &cmd, 1 ); }
+ while (ret < 0 && errno == EINTR);
+
+ if (ret != 1)
+ D("%s: could not send CMD_STOP command: ret=%d: %s",
+ __FUNCTION__, ret, strerror(errno));
+}
+
+
+static int
+epoll_register( int epoll_fd, int fd )
+{
+ struct epoll_event ev;
+ int ret, flags;
+
+ /* important: make the fd non-blocking */
+ flags = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ ev.events = EPOLLIN;
+ ev.data.fd = fd;
+ do {
+ ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
+ } while (ret < 0 && errno == EINTR);
+ return ret;
+}
+
+
+static int
+epoll_deregister( int epoll_fd, int fd )
+{
+ int ret;
+ do {
+ ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
+ } while (ret < 0 && errno == EINTR);
+ return ret;
+}
+
+/* this is the main thread, it waits for commands from gps_state_start/stop and,
+ * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences
+ * that must be parsed to be converted into GPS fixes sent to the framework
+ */
+static void
+gps_state_thread( void* arg )
+{
+ GpsState* state = (GpsState*) arg;
+ NmeaReader reader[1];
+ int epoll_fd = epoll_create(2);
+ int started = 0;
+ int gps_fd = state->fd;
+ int control_fd = state->control[1];
+
+ nmea_reader_init( reader );
+
+ // register control file descriptors for polling
+ epoll_register( epoll_fd, control_fd );
+ epoll_register( epoll_fd, gps_fd );
+
+ D("gps thread running");
+
+ // now loop
+ for (;;) {
+ struct epoll_event events[2];
+ int ne, nevents;
+
+ nevents = epoll_wait( epoll_fd, events, 2, -1 );
+ if (nevents < 0) {
+ if (errno != EINTR)
+ LOGE("epoll_wait() unexpected error: %s", strerror(errno));
+ continue;
+ }
+ D("gps thread received %d events", nevents);
+ for (ne = 0; ne < nevents; ne++) {
+ if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) {
+ LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?");
+ return;
+ }
+ if ((events[ne].events & EPOLLIN) != 0) {
+ int fd = events[ne].data.fd;
+
+ if (fd == control_fd)
+ {
+ char cmd = 255;
+ int ret;
+ D("gps control fd event");
+ do {
+ ret = read( fd, &cmd, 1 );
+ } while (ret < 0 && errno == EINTR);
+
+ if (cmd == CMD_QUIT) {
+ D("gps thread quitting on demand");
+ return;
+ }
+ else if (cmd == CMD_START) {
+ if (!started) {
+ D("gps thread starting location_cb=%p", state->callbacks.location_cb);
+ started = 1;
+ nmea_reader_set_callback( reader, state->callbacks.location_cb );
+ }
+ }
+ else if (cmd == CMD_STOP) {
+ if (started) {
+ D("gps thread stopping");
+ started = 0;
+ nmea_reader_set_callback( reader, NULL );
+ }
+ }
+ }
+ else if (fd == gps_fd)
+ {
+ char buff[32];
+ D("gps fd event");
+ for (;;) {
+ int nn, ret;
+
+ ret = read( fd, buff, sizeof(buff) );
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno != EWOULDBLOCK)
+ LOGE("error while reading from gps daemon socket: %s:", strerror(errno));
+ break;
+ }
+ D("received %d bytes: %.*s", ret, ret, buff);
+ for (nn = 0; nn < ret; nn++)
+ nmea_reader_addc( reader, buff[nn] );
+ }
+ D("gps fd event end");
+ }
+ else
+ {
+ LOGE("epoll_wait() returned unkown fd %d ?", fd);
+ }
+ }
+ }
+ }
+}
+
+
+static void
+gps_state_init( GpsState* state, GpsCallbacks* callbacks )
+{
+ state->init = 1;
+ state->control[0] = -1;
+ state->control[1] = -1;
+ state->fd = -1;
+
+ state->fd = qemud_channel_open(QEMU_CHANNEL_NAME);
+
+ if (state->fd < 0) {
+ D("no gps emulation detected");
+ return;
+ }
+
+ D("gps emulation will read from '%s' qemud channel", QEMU_CHANNEL_NAME );
+
+ if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) {
+ LOGE("could not create thread control socket pair: %s", strerror(errno));
+ goto Fail;
+ }
+
+ state->thread = callbacks->create_thread_cb( "gps_state_thread", gps_state_thread, state );
+
+ if ( !state->thread ) {
+ LOGE("could not create gps thread: %s", strerror(errno));
+ goto Fail;
+ }
+
+ state->callbacks = *callbacks;
+
+ D("gps state initialized");
+ return;
+
+Fail:
+ gps_state_done( state );
+}
+
+
+/*****************************************************************/
+/*****************************************************************/
+/***** *****/
+/***** I N T E R F A C E *****/
+/***** *****/
+/*****************************************************************/
+/*****************************************************************/
+
+
+static int
+qemu_gps_init(GpsCallbacks* callbacks)
+{
+ GpsState* s = _gps_state;
+
+ if (!s->init)
+ gps_state_init(s, callbacks);
+
+ if (s->fd < 0)
+ return -1;
+
+ return 0;
+}
+
+static void
+qemu_gps_cleanup(void)
+{
+ GpsState* s = _gps_state;
+
+ if (s->init)
+ gps_state_done(s);
+}
+
+
+static int
+qemu_gps_start()
+{
+ GpsState* s = _gps_state;
+
+ if (!s->init) {
+ D("%s: called with uninitialized state !!", __FUNCTION__);
+ return -1;
+ }
+
+ D("%s: called", __FUNCTION__);
+ gps_state_start(s);
+ return 0;
+}
+
+
+static int
+qemu_gps_stop()
+{
+ GpsState* s = _gps_state;
+
+ if (!s->init) {
+ D("%s: called with uninitialized state !!", __FUNCTION__);
+ return -1;
+ }
+
+ D("%s: called", __FUNCTION__);
+ gps_state_stop(s);
+ return 0;
+}
+
+
+static int
+qemu_gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty)
+{
+ return 0;
+}
+
+static int
+qemu_gps_inject_location(double latitude, double longitude, float accuracy)
+{
+ return 0;
+}
+
+static void
+qemu_gps_delete_aiding_data(GpsAidingData flags)
+{
+}
+
+static int qemu_gps_set_position_mode(GpsPositionMode mode, int fix_frequency)
+{
+ // FIXME - support fix_frequency
+ return 0;
+}
+
+static const void*
+qemu_gps_get_extension(const char* name)
+{
+ // no extensions supported
+ return NULL;
+}
+
+static const GpsInterface qemuGpsInterface = {
+ sizeof(GpsInterface),
+ qemu_gps_init,
+ qemu_gps_start,
+ qemu_gps_stop,
+ qemu_gps_cleanup,
+ qemu_gps_inject_time,
+ qemu_gps_inject_location,
+ qemu_gps_delete_aiding_data,
+ qemu_gps_set_position_mode,
+ qemu_gps_get_extension,
+};
+
+const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)
+{
+ return &qemuGpsInterface;
+}
+
+static int open_gps(const struct hw_module_t* module, char const* name,
+ struct hw_device_t** device)
+{
+ struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
+ memset(dev, 0, sizeof(*dev));
+
+ dev->common.tag = HARDWARE_DEVICE_TAG;
+ dev->common.version = 0;
+ dev->common.module = (struct hw_module_t*)module;
+// dev->common.close = (int (*)(struct hw_device_t*))close_lights;
+ dev->get_gps_interface = gps__get_gps_interface;
+
+ *device = (struct hw_device_t*)dev;
+ return 0;
+}
+
+
+static struct hw_module_methods_t gps_module_methods = {
+ .open = open_gps
+};
+
+const struct hw_module_t HAL_MODULE_INFO_SYM = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = GPS_HARDWARE_MODULE_ID,
+ .name = "Goldfish GPS Module",
+ .author = "The Android Open Source Project",
+ .methods = &gps_module_methods,
+};
diff --git a/tools/emulator/system/qemu-props/Android.mk b/tools/emulator/system/qemu-props/Android.mk
new file mode 100644
index 0000000..1bdbf68
--- /dev/null
+++ b/tools/emulator/system/qemu-props/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2009 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 build emulator-specific program tools
+# that should only run in the emulator.
+#
+
+# We're moving the emulator-specific platform libs to
+# development.git/tools/emulator/. The following test is to ensure
+# smooth builds even if the tree contains both versions.
+#
+ifndef BUILD_EMULATOR_QEMU_PROPS
+BUILD_EMULATOR_QEMU_PROPS := true
+
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_PRODUCT),sim)
+
+# The 'qemu-props' program is run from /system/etc/init.goldfish.rc
+# to setup various system properties sent by the emulator program.
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := qemu-props
+LOCAL_SRC_FILES := qemu-props.c
+LOCAL_SHARED_LIBRARIES := libcutils
+# we don't want this in 'user' builds which don't have
+# emulator-specific binaries.
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_EXECUTABLE)
+
+endif # TARGET_PRODUCT != sim
+
+endif # BUILD_EMULATOR_QEMU_PROPS
diff --git a/tools/emulator/system/qemu-props/qemu-props.c b/tools/emulator/system/qemu-props/qemu-props.c
new file mode 100644
index 0000000..3f086a1
--- /dev/null
+++ b/tools/emulator/system/qemu-props/qemu-props.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2009 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 program is used to read a set of system properties and their values
+ * from the emulator program and set them in the currently-running emulated
+ * system. It does so by connecting to the 'boot-properties' qemud service.
+ *
+ * This program should be run as root and called from
+ * /system/etc/init.goldfish.rc exclusively.
+ */
+
+#define LOG_TAG "qemu-props"
+
+#define DEBUG 1
+
+#if DEBUG
+# include <cutils/log.h>
+# define DD(...) LOGI(__VA_ARGS__)
+#else
+# define DD(...) ((void)0)
+#endif
+
+#include <cutils/properties.h>
+#include <unistd.h>
+#include <hardware/qemud.h>
+
+/* Name of the qemud service we want to connect to.
+ */
+#define QEMUD_SERVICE "boot-properties"
+
+#define MAX_TRIES 5
+
+int main(void)
+{
+ int qemud_fd, count = 0;
+
+ /* try to connect to the qemud service */
+ {
+ int tries = MAX_TRIES;
+
+ while (1) {
+ qemud_fd = qemud_channel_open( "boot-properties" );
+ if (qemud_fd >= 0)
+ break;
+
+ if (--tries <= 0) {
+ DD("Could not connect after too many tries. Aborting");
+ return 1;
+ }
+
+ DD("waiting 1s to wait for qemud.");
+ sleep(1);
+ }
+ }
+
+ DD("connected to '%s' qemud service.", QEMUD_SERVICE);
+
+ /* send the 'list' command to the service */
+ if (qemud_channel_send(qemud_fd, "list", -1) < 0) {
+ DD("could not send command to '%s' service", QEMUD_SERVICE);
+ return 1;
+ }
+
+ /* read each system property as a single line from the service,
+ * until exhaustion.
+ */
+ for (;;)
+ {
+#define BUFF_SIZE (PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 2)
+ DD("receiving..");
+ char* q;
+ char temp[BUFF_SIZE];
+ int len = qemud_channel_recv(qemud_fd, temp, sizeof temp - 1);
+
+ /* lone NUL-byte signals end of properties */
+ if (len < 0 || len > BUFF_SIZE-1 || temp[0] == '\0')
+ break;
+
+ temp[len] = '\0'; /* zero-terminate string */
+
+ DD("received: %.*s", len, temp);
+
+ /* separate propery name from value */
+ q = strchr(temp, '=');
+ if (q == NULL) {
+ DD("invalid format, ignored.");
+ continue;
+ }
+ *q++ = '\0';
+
+ if (property_set(temp, q) < 0) {
+ DD("could not set property '%s' to '%s'", temp, q);
+ } else {
+ count += 1;
+ }
+ }
+
+
+ /* finally, close the channel and exit */
+ close(qemud_fd);
+ DD("exiting (%d properties set).", count);
+ return 0;
+}
diff --git a/tools/emulator/system/qemud/Android.mk b/tools/emulator/system/qemud/Android.mk
new file mode 100644
index 0000000..5666a74
--- /dev/null
+++ b/tools/emulator/system/qemud/Android.mk
@@ -0,0 +1,25 @@
+# Copyright 2008 The Android Open Source Project
+
+# We're moving the emulator-specific platform libs to
+# development.git/tools/emulator/. The following test is to ensure
+# smooth builds even if the tree contains both versions.
+#
+ifndef BUILD_EMULATOR_QEMUD
+BUILD_EMULATOR_QEMUD := true
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ qemud.c
+
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+
+LOCAL_MODULE:= qemud
+LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_EXECUTABLE)
+
+endif # BUILD_EMULATOR_QEMUD
\ No newline at end of file
diff --git a/tools/emulator/system/qemud/qemud.c b/tools/emulator/system/qemud/qemud.c
new file mode 100644
index 0000000..e1c7b54
--- /dev/null
+++ b/tools/emulator/system/qemud/qemud.c
@@ -0,0 +1,1719 @@
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <termios.h>
+#include <cutils/sockets.h>
+
+/*
+ * the qemud daemon program is only used within Android as a bridge
+ * between the emulator program and the emulated system. it really works as
+ * a simple stream multiplexer that works as follows:
+ *
+ * - qemud is started by init following instructions in
+ * /system/etc/init.goldfish.rc (i.e. it is never started on real devices)
+ *
+ * - qemud communicates with the emulator program through a single serial
+ * port, whose name is passed through a kernel boot parameter
+ * (e.g. android.qemud=ttyS1)
+ *
+ * - qemud binds one unix local stream socket (/dev/socket/qemud, created
+ * by init through /system/etc/init.goldfish.rc).
+ *
+ *
+ * emulator <==serial==> qemud <---> /dev/socket/qemud <-+--> client1
+ * |
+ * +--> client2
+ *
+ * - the special channel index 0 is used by the emulator and qemud only.
+ * other channel numbers correspond to clients. More specifically,
+ * connection are created like this:
+ *
+ * * the client connects to /dev/socket/qemud
+ *
+ * * the client sends the service name through the socket, as
+ * <service-name>
+ *
+ * * qemud creates a "Client" object internally, assigns it an
+ * internal unique channel number > 0, then sends a connection
+ * initiation request to the emulator (i.e. through channel 0):
+ *
+ * connect:<id>:<name>
+ *
+ * where <name> is the service name, and <id> is a 2-hexchar
+ * number corresponding to the channel number.
+ *
+ * * in case of success, the emulator responds through channel 0
+ * with:
+ *
+ * ok:connect:<id>
+ *
+ * after this, all messages between the client and the emulator
+ * are passed in pass-through mode.
+ *
+ * * if the emulator refuses the service connection, it will
+ * send the following through channel 0:
+ *
+ * ko:connect:<id>:reason-for-failure
+ *
+ * * If the client closes the connection, qemud sends the following
+ * to the emulator:
+ *
+ * disconnect:<id>
+ *
+ * The same message is the opposite direction if the emulator
+ * chooses to close the connection.
+ *
+ * * any command sent through channel 0 to the emulator that is
+ * not properly recognized will be answered by:
+ *
+ * ko:unknown command
+ *
+ *
+ * Internally, the daemon maintains a "Client" object for each client
+ * connection (i.e. accepting socket connection).
+ */
+
+/* name of the single control socket used by the daemon */
+#define CONTROL_SOCKET_NAME "qemud"
+
+#define DEBUG 1
+#define T_ACTIVE 0 /* set to 1 to dump traffic */
+
+#if DEBUG
+# define LOG_TAG "qemud"
+# include <cutils/log.h>
+# define D(...) LOGD(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+# define T(...) ((void)0)
+#endif
+
+#if T_ACTIVE
+# define T(...) D(__VA_ARGS__)
+#else
+# define T(...) ((void)0)
+#endif
+
+/** UTILITIES
+ **/
+
+static void
+fatal( const char* fmt, ... )
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "PANIC: ");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n" );
+ va_end(args);
+ exit(1);
+}
+
+static void*
+xalloc( size_t sz )
+{
+ void* p;
+
+ if (sz == 0)
+ return NULL;
+
+ p = malloc(sz);
+ if (p == NULL)
+ fatal( "not enough memory" );
+
+ return p;
+}
+
+#define xnew(p) (p) = xalloc(sizeof(*(p)))
+
+static void*
+xalloc0( size_t sz )
+{
+ void* p = xalloc(sz);
+ memset( p, 0, sz );
+ return p;
+}
+
+#define xnew0(p) (p) = xalloc0(sizeof(*(p)))
+
+#define xfree(p) (free((p)), (p) = NULL)
+
+static void*
+xrealloc( void* block, size_t size )
+{
+ void* p = realloc( block, size );
+
+ if (p == NULL && size > 0)
+ fatal( "not enough memory" );
+
+ return p;
+}
+
+#define xrenew(p,count) (p) = xrealloc((p),sizeof(*(p))*(count))
+
+static int
+hex2int( const uint8_t* data, int len )
+{
+ int result = 0;
+ while (len > 0) {
+ int c = *data++;
+ unsigned d;
+
+ result <<= 4;
+ do {
+ d = (unsigned)(c - '0');
+ if (d < 10)
+ break;
+
+ d = (unsigned)(c - 'a');
+ if (d < 6) {
+ d += 10;
+ break;
+ }
+
+ d = (unsigned)(c - 'A');
+ if (d < 6) {
+ d += 10;
+ break;
+ }
+
+ return -1;
+ }
+ while (0);
+
+ result |= d;
+ len -= 1;
+ }
+ return result;
+}
+
+
+static void
+int2hex( int value, uint8_t* to, int width )
+{
+ int nn = 0;
+ static const char hexchars[16] = "0123456789abcdef";
+
+ for ( --width; width >= 0; width--, nn++ ) {
+ to[nn] = hexchars[(value >> (width*4)) & 15];
+ }
+}
+
+static int
+fd_read(int fd, void* to, int len)
+{
+ int ret;
+
+ do {
+ ret = read(fd, to, len);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+static int
+fd_write(int fd, const void* from, int len)
+{
+ int ret;
+
+ do {
+ ret = write(fd, from, len);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+static void
+fd_setnonblock(int fd)
+{
+ int ret, flags;
+
+ do {
+ flags = fcntl(fd, F_GETFD);
+ } while (flags < 0 && errno == EINTR);
+
+ if (flags < 0) {
+ fatal( "%s: could not get flags for fd %d: %s",
+ __FUNCTION__, fd, strerror(errno) );
+ }
+
+ do {
+ ret = fcntl(fd, F_SETFD, flags | O_NONBLOCK);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ fatal( "%s: could not set fd %d to non-blocking: %s",
+ __FUNCTION__, fd, strerror(errno) );
+ }
+}
+
+
+static int
+fd_accept(int fd)
+{
+ struct sockaddr from;
+ socklen_t fromlen = sizeof(from);
+ int ret;
+
+ do {
+ ret = accept(fd, &from, &fromlen);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+/** FD EVENT LOOP
+ **/
+
+/* A Looper object is used to monitor activity on one or more
+ * file descriptors (e.g sockets).
+ *
+ * - call looper_add() to register a function that will be
+ * called when events happen on the file descriptor.
+ *
+ * - call looper_enable() or looper_disable() to enable/disable
+ * the set of monitored events for a given file descriptor.
+ *
+ * - call looper_del() to unregister a file descriptor.
+ * this does *not* close the file descriptor.
+ *
+ * Note that you can only provide a single function to handle
+ * all events related to a given file descriptor.
+
+ * You can call looper_enable/_disable/_del within a function
+ * callback.
+ */
+
+/* the current implementation uses Linux's epoll facility
+ * the event mask we use are simply combinations of EPOLLIN
+ * EPOLLOUT, EPOLLHUP and EPOLLERR
+ */
+#include <sys/epoll.h>
+
+#define MAX_CHANNELS 16
+#define MAX_EVENTS (MAX_CHANNELS+1) /* each channel + the serial fd */
+
+/* the event handler function type, 'user' is a user-specific
+ * opaque pointer passed to looper_add().
+ */
+typedef void (*EventFunc)( void* user, int events );
+
+/* bit flags for the LoopHook structure.
+ *
+ * HOOK_PENDING means that an event happened on the
+ * corresponding file descriptor.
+ *
+ * HOOK_CLOSING is used to delay-close monitored
+ * file descriptors.
+ */
+enum {
+ HOOK_PENDING = (1 << 0),
+ HOOK_CLOSING = (1 << 1),
+};
+
+/* A LoopHook structure is used to monitor a given
+ * file descriptor and record its event handler.
+ */
+typedef struct {
+ int fd;
+ int wanted; /* events we are monitoring */
+ int events; /* events that occured */
+ int state; /* see HOOK_XXX constants */
+ void* ev_user; /* user-provided handler parameter */
+ EventFunc ev_func; /* event handler callback */
+} LoopHook;
+
+/* Looper is the main object modeling a looper object
+ */
+typedef struct {
+ int epoll_fd;
+ int num_fds;
+ int max_fds;
+ struct epoll_event* events;
+ LoopHook* hooks;
+} Looper;
+
+/* initialize a looper object */
+static void
+looper_init( Looper* l )
+{
+ l->epoll_fd = epoll_create(4);
+ l->num_fds = 0;
+ l->max_fds = 0;
+ l->events = NULL;
+ l->hooks = NULL;
+}
+
+/* finalize a looper object */
+static void
+looper_done( Looper* l )
+{
+ xfree(l->events);
+ xfree(l->hooks);
+ l->max_fds = 0;
+ l->num_fds = 0;
+
+ close(l->epoll_fd);
+ l->epoll_fd = -1;
+}
+
+/* return the LoopHook corresponding to a given
+ * monitored file descriptor, or NULL if not found
+ */
+static LoopHook*
+looper_find( Looper* l, int fd )
+{
+ LoopHook* hook = l->hooks;
+ LoopHook* end = hook + l->num_fds;
+
+ for ( ; hook < end; hook++ ) {
+ if (hook->fd == fd)
+ return hook;
+ }
+ return NULL;
+}
+
+/* grow the arrays in the looper object */
+static void
+looper_grow( Looper* l )
+{
+ int old_max = l->max_fds;
+ int new_max = old_max + (old_max >> 1) + 4;
+ int n;
+
+ xrenew( l->events, new_max );
+ xrenew( l->hooks, new_max );
+ l->max_fds = new_max;
+
+ /* now change the handles to all events */
+ for (n = 0; n < l->num_fds; n++) {
+ struct epoll_event ev;
+ LoopHook* hook = l->hooks + n;
+
+ ev.events = hook->wanted;
+ ev.data.ptr = hook;
+ epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, hook->fd, &ev );
+ }
+}
+
+/* register a file descriptor and its event handler.
+ * no event mask will be enabled
+ */
+static void
+looper_add( Looper* l, int fd, EventFunc func, void* user )
+{
+ struct epoll_event ev;
+ LoopHook* hook;
+
+ if (l->num_fds >= l->max_fds)
+ looper_grow(l);
+
+ hook = l->hooks + l->num_fds;
+
+ hook->fd = fd;
+ hook->ev_user = user;
+ hook->ev_func = func;
+ hook->state = 0;
+ hook->wanted = 0;
+ hook->events = 0;
+
+ fd_setnonblock(fd);
+
+ ev.events = 0;
+ ev.data.ptr = hook;
+ epoll_ctl( l->epoll_fd, EPOLL_CTL_ADD, fd, &ev );
+
+ l->num_fds += 1;
+}
+
+/* unregister a file descriptor and its event handler
+ */
+static void
+looper_del( Looper* l, int fd )
+{
+ LoopHook* hook = looper_find( l, fd );
+
+ if (!hook) {
+ D( "%s: invalid fd: %d", __FUNCTION__, fd );
+ return;
+ }
+ /* don't remove the hook yet */
+ hook->state |= HOOK_CLOSING;
+
+ epoll_ctl( l->epoll_fd, EPOLL_CTL_DEL, fd, NULL );
+}
+
+/* enable monitoring of certain events for a file
+ * descriptor. This adds 'events' to the current
+ * event mask
+ */
+static void
+looper_enable( Looper* l, int fd, int events )
+{
+ LoopHook* hook = looper_find( l, fd );
+
+ if (!hook) {
+ D("%s: invalid fd: %d", __FUNCTION__, fd );
+ return;
+ }
+
+ if (events & ~hook->wanted) {
+ struct epoll_event ev;
+
+ hook->wanted |= events;
+ ev.events = hook->wanted;
+ ev.data.ptr = hook;
+
+ epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, fd, &ev );
+ }
+}
+
+/* disable monitoring of certain events for a file
+ * descriptor. This ignores events that are not
+ * currently enabled.
+ */
+static void
+looper_disable( Looper* l, int fd, int events )
+{
+ LoopHook* hook = looper_find( l, fd );
+
+ if (!hook) {
+ D("%s: invalid fd: %d", __FUNCTION__, fd );
+ return;
+ }
+
+ if (events & hook->wanted) {
+ struct epoll_event ev;
+
+ hook->wanted &= ~events;
+ ev.events = hook->wanted;
+ ev.data.ptr = hook;
+
+ epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, fd, &ev );
+ }
+}
+
+/* wait until an event occurs on one of the registered file
+ * descriptors. Only returns in case of error !!
+ */
+static void
+looper_loop( Looper* l )
+{
+ for (;;) {
+ int n, count;
+
+ do {
+ count = epoll_wait( l->epoll_fd, l->events, l->num_fds, -1 );
+ } while (count < 0 && errno == EINTR);
+
+ if (count < 0) {
+ D("%s: error: %s", __FUNCTION__, strerror(errno) );
+ return;
+ }
+
+ if (count == 0) {
+ D("%s: huh ? epoll returned count=0", __FUNCTION__);
+ continue;
+ }
+
+ /* mark all pending hooks */
+ for (n = 0; n < count; n++) {
+ LoopHook* hook = l->events[n].data.ptr;
+ hook->state = HOOK_PENDING;
+ hook->events = l->events[n].events;
+ }
+
+ /* execute hook callbacks. this may change the 'hooks'
+ * and 'events' array, as well as l->num_fds, so be careful */
+ for (n = 0; n < l->num_fds; n++) {
+ LoopHook* hook = l->hooks + n;
+ if (hook->state & HOOK_PENDING) {
+ hook->state &= ~HOOK_PENDING;
+ hook->ev_func( hook->ev_user, hook->events );
+ }
+ }
+
+ /* now remove all the hooks that were closed by
+ * the callbacks */
+ for (n = 0; n < l->num_fds;) {
+ struct epoll_event ev;
+ LoopHook* hook = l->hooks + n;
+
+ if (!(hook->state & HOOK_CLOSING)) {
+ n++;
+ continue;
+ }
+
+ hook[0] = l->hooks[l->num_fds-1];
+ l->num_fds -= 1;
+ ev.events = hook->wanted;
+ ev.data.ptr = hook;
+ epoll_ctl( l->epoll_fd, EPOLL_CTL_MOD, hook->fd, &ev );
+ }
+ }
+}
+
+#if T_ACTIVE
+char*
+quote( const void* data, int len )
+{
+ const char* p = data;
+ const char* end = p + len;
+ int count = 0;
+ int phase = 0;
+ static char* buff = NULL;
+
+ for (phase = 0; phase < 2; phase++) {
+ if (phase != 0) {
+ xfree(buff);
+ buff = xalloc(count+1);
+ }
+ count = 0;
+ for (p = data; p < end; p++) {
+ int c = *p;
+
+ if (c == '\\') {
+ if (phase != 0) {
+ buff[count] = buff[count+1] = '\\';
+ }
+ count += 2;
+ continue;
+ }
+
+ if (c >= 32 && c < 127) {
+ if (phase != 0)
+ buff[count] = c;
+ count += 1;
+ continue;
+ }
+
+
+ if (c == '\t') {
+ if (phase != 0) {
+ memcpy(buff+count, "<TAB>", 5);
+ }
+ count += 5;
+ continue;
+ }
+ if (c == '\n') {
+ if (phase != 0) {
+ memcpy(buff+count, "<LN>", 4);
+ }
+ count += 4;
+ continue;
+ }
+ if (c == '\r') {
+ if (phase != 0) {
+ memcpy(buff+count, "<CR>", 4);
+ }
+ count += 4;
+ continue;
+ }
+
+ if (phase != 0) {
+ buff[count+0] = '\\';
+ buff[count+1] = 'x';
+ buff[count+2] = "0123456789abcdef"[(c >> 4) & 15];
+ buff[count+3] = "0123456789abcdef"[ (c) & 15];
+ }
+ count += 4;
+ }
+ }
+ buff[count] = 0;
+ return buff;
+}
+#endif /* T_ACTIVE */
+
+/** PACKETS
+ **
+ ** We need a way to buffer data before it can be sent to the
+ ** corresponding file descriptor. We use linked list of Packet
+ ** objects to do this.
+ **/
+
+typedef struct Packet Packet;
+
+#define MAX_PAYLOAD 4000
+
+struct Packet {
+ Packet* next;
+ int len;
+ int channel;
+ uint8_t data[ MAX_PAYLOAD ];
+};
+
+/* we expect to alloc/free a lot of packets during
+ * operations so use a single linked list of free packets
+ * to keep things speedy and simple.
+ */
+static Packet* _free_packets;
+
+/* Allocate a packet */
+static Packet*
+packet_alloc(void)
+{
+ Packet* p = _free_packets;
+ if (p != NULL) {
+ _free_packets = p->next;
+ } else {
+ xnew(p);
+ }
+ p->next = NULL;
+ p->len = 0;
+ p->channel = -1;
+ return p;
+}
+
+/* Release a packet. This takes the address of a packet
+ * pointer that will be set to NULL on exit (avoids
+ * referencing dangling pointers in case of bugs)
+ */
+static void
+packet_free( Packet* *ppacket )
+{
+ Packet* p = *ppacket;
+ if (p) {
+ p->next = _free_packets;
+ _free_packets = p;
+ *ppacket = NULL;
+ }
+}
+
+/** PACKET RECEIVER
+ **
+ ** Simple abstraction for something that can receive a packet
+ ** from a FDHandler (see below) or something else.
+ **
+ ** Send a packet to it with 'receiver_post'
+ **
+ ** Call 'receiver_close' to indicate that the corresponding
+ ** packet source was closed.
+ **/
+
+typedef void (*PostFunc) ( void* user, Packet* p );
+typedef void (*CloseFunc)( void* user );
+
+typedef struct {
+ PostFunc post;
+ CloseFunc close;
+ void* user;
+} Receiver;
+
+/* post a packet to a receiver. Note that this transfers
+ * ownership of the packet to the receiver.
+ */
+static __inline__ void
+receiver_post( Receiver* r, Packet* p )
+{
+ if (r->post)
+ r->post( r->user, p );
+ else
+ packet_free(&p);
+}
+
+/* tell a receiver the packet source was closed.
+ * this will also prevent further posting to the
+ * receiver.
+ */
+static __inline__ void
+receiver_close( Receiver* r )
+{
+ if (r->close) {
+ r->close( r->user );
+ r->close = NULL;
+ }
+ r->post = NULL;
+}
+
+
+/** FD HANDLERS
+ **
+ ** these are smart listeners that send incoming packets to a receiver
+ ** and can queue one or more outgoing packets and send them when
+ ** possible to the FD.
+ **
+ ** note that we support clean shutdown of file descriptors,
+ ** i.e. we try to send all outgoing packets before destroying
+ ** the FDHandler.
+ **/
+
+typedef struct FDHandler FDHandler;
+typedef struct FDHandlerList FDHandlerList;
+
+struct FDHandler {
+ int fd;
+ FDHandlerList* list;
+ char closing;
+ Receiver receiver[1];
+
+ /* queue of outgoing packets */
+ int out_pos;
+ Packet* out_first;
+ Packet** out_ptail;
+
+ FDHandler* next;
+ FDHandler** pref;
+
+};
+
+struct FDHandlerList {
+ /* the looper that manages the fds */
+ Looper* looper;
+
+ /* list of active FDHandler objects */
+ FDHandler* active;
+
+ /* list of closing FDHandler objects.
+ * these are waiting to push their
+ * queued packets to the fd before
+ * freeing themselves.
+ */
+ FDHandler* closing;
+
+};
+
+/* remove a FDHandler from its current list */
+static void
+fdhandler_remove( FDHandler* f )
+{
+ f->pref[0] = f->next;
+ if (f->next)
+ f->next->pref = f->pref;
+}
+
+/* add a FDHandler to a given list */
+static void
+fdhandler_prepend( FDHandler* f, FDHandler** list )
+{
+ f->next = list[0];
+ f->pref = list;
+ list[0] = f;
+ if (f->next)
+ f->next->pref = &f->next;
+}
+
+/* initialize a FDHandler list */
+static void
+fdhandler_list_init( FDHandlerList* list, Looper* looper )
+{
+ list->looper = looper;
+ list->active = NULL;
+ list->closing = NULL;
+}
+
+
+/* close a FDHandler (and free it). Note that this will not
+ * perform a graceful shutdown, i.e. all packets in the
+ * outgoing queue will be immediately free.
+ *
+ * this *will* notify the receiver that the file descriptor
+ * was closed.
+ *
+ * you should call fdhandler_shutdown() if you want to
+ * notify the FDHandler that its packet source is closed.
+ */
+static void
+fdhandler_close( FDHandler* f )
+{
+ /* notify receiver */
+ receiver_close(f->receiver);
+
+ /* remove the handler from its list */
+ fdhandler_remove(f);
+
+ /* get rid of outgoing packet queue */
+ if (f->out_first != NULL) {
+ Packet* p;
+ while ((p = f->out_first) != NULL) {
+ f->out_first = p->next;
+ packet_free(&p);
+ }
+ }
+
+ /* get rid of file descriptor */
+ if (f->fd >= 0) {
+ looper_del( f->list->looper, f->fd );
+ close(f->fd);
+ f->fd = -1;
+ }
+
+ f->list = NULL;
+ xfree(f);
+}
+
+/* Ask the FDHandler to cleanly shutdown the connection,
+ * i.e. send any pending outgoing packets then auto-free
+ * itself.
+ */
+static void
+fdhandler_shutdown( FDHandler* f )
+{
+ /* prevent later fdhandler_close() to
+ * call the receiver's close.
+ */
+ f->receiver->close = NULL;
+
+ if (f->out_first != NULL && !f->closing)
+ {
+ /* move the handler to the 'closing' list */
+ f->closing = 1;
+ fdhandler_remove(f);
+ fdhandler_prepend(f, &f->list->closing);
+ return;
+ }
+
+ fdhandler_close(f);
+}
+
+/* Enqueue a new packet that the FDHandler will
+ * send through its file descriptor.
+ */
+static void
+fdhandler_enqueue( FDHandler* f, Packet* p )
+{
+ Packet* first = f->out_first;
+
+ p->next = NULL;
+ f->out_ptail[0] = p;
+ f->out_ptail = &p->next;
+
+ if (first == NULL) {
+ f->out_pos = 0;
+ looper_enable( f->list->looper, f->fd, EPOLLOUT );
+ }
+}
+
+
+/* FDHandler file descriptor event callback for read/write ops */
+static void
+fdhandler_event( FDHandler* f, int events )
+{
+ int len;
+
+ /* in certain cases, it's possible to have both EPOLLIN and
+ * EPOLLHUP at the same time. This indicates that there is incoming
+ * data to read, but that the connection was nonetheless closed
+ * by the sender. Be sure to read the data before closing
+ * the receiver to avoid packet loss.
+ */
+
+ if (events & EPOLLIN) {
+ Packet* p = packet_alloc();
+ int len;
+
+ if ((len = fd_read(f->fd, p->data, MAX_PAYLOAD)) < 0) {
+ D("%s: can't recv: %s", __FUNCTION__, strerror(errno));
+ packet_free(&p);
+ } else if (len > 0) {
+ p->len = len;
+ p->channel = -101; /* special debug value, not used */
+ receiver_post( f->receiver, p );
+ }
+ }
+
+ if (events & (EPOLLHUP|EPOLLERR)) {
+ /* disconnection */
+ D("%s: disconnect on fd %d", __FUNCTION__, f->fd);
+ fdhandler_close(f);
+ return;
+ }
+
+ if (events & EPOLLOUT && f->out_first) {
+ Packet* p = f->out_first;
+ int avail, len;
+
+ avail = p->len - f->out_pos;
+ if ((len = fd_write(f->fd, p->data + f->out_pos, avail)) < 0) {
+ D("%s: can't send: %s", __FUNCTION__, strerror(errno));
+ } else {
+ f->out_pos += len;
+ if (f->out_pos >= p->len) {
+ f->out_pos = 0;
+ f->out_first = p->next;
+ packet_free(&p);
+ if (f->out_first == NULL) {
+ f->out_ptail = &f->out_first;
+ looper_disable( f->list->looper, f->fd, EPOLLOUT );
+ }
+ }
+ }
+ }
+}
+
+
+/* Create a new FDHandler that monitors read/writes */
+static FDHandler*
+fdhandler_new( int fd,
+ FDHandlerList* list,
+ Receiver* receiver )
+{
+ FDHandler* f = xalloc0(sizeof(*f));
+
+ f->fd = fd;
+ f->list = list;
+ f->receiver[0] = receiver[0];
+ f->out_first = NULL;
+ f->out_ptail = &f->out_first;
+ f->out_pos = 0;
+
+ fdhandler_prepend(f, &list->active);
+
+ looper_add( list->looper, fd, (EventFunc) fdhandler_event, f );
+ looper_enable( list->looper, fd, EPOLLIN );
+
+ return f;
+}
+
+
+/* event callback function to monitor accepts() on server sockets.
+ * the convention used here is that the receiver will receive a
+ * dummy packet with the new client socket in p->channel
+ */
+static void
+fdhandler_accept_event( FDHandler* f, int events )
+{
+ if (events & EPOLLIN) {
+ /* this is an accept - send a dummy packet to the receiver */
+ Packet* p = packet_alloc();
+
+ D("%s: accepting on fd %d", __FUNCTION__, f->fd);
+ p->data[0] = 1;
+ p->len = 1;
+ p->channel = fd_accept(f->fd);
+ if (p->channel < 0) {
+ D("%s: accept failed ?: %s", __FUNCTION__, strerror(errno));
+ packet_free(&p);
+ return;
+ }
+ receiver_post( f->receiver, p );
+ }
+
+ if (events & (EPOLLHUP|EPOLLERR)) {
+ /* disconnecting !! */
+ D("%s: closing accept fd %d", __FUNCTION__, f->fd);
+ fdhandler_close(f);
+ return;
+ }
+}
+
+
+/* Create a new FDHandler used to monitor new connections on a
+ * server socket. The receiver must expect the new connection
+ * fd in the 'channel' field of a dummy packet.
+ */
+static FDHandler*
+fdhandler_new_accept( int fd,
+ FDHandlerList* list,
+ Receiver* receiver )
+{
+ FDHandler* f = xalloc0(sizeof(*f));
+
+ f->fd = fd;
+ f->list = list;
+ f->receiver[0] = receiver[0];
+
+ fdhandler_prepend(f, &list->active);
+
+ looper_add( list->looper, fd, (EventFunc) fdhandler_accept_event, f );
+ looper_enable( list->looper, fd, EPOLLIN );
+ listen( fd, 5 );
+
+ return f;
+}
+
+/** SERIAL CONNECTION STATE
+ **
+ ** The following is used to handle the framing protocol
+ ** used on the serial port connection.
+ **/
+
+/* each packet is made of a 6 byte header followed by a payload
+ * the header looks like:
+ *
+ * offset size description
+ * 0 2 a 2-byte hex string for the channel number
+ * 4 4 a 4-char hex string for the size of the payload
+ * 6 n the payload itself
+ */
+#define HEADER_SIZE 6
+#define CHANNEL_OFFSET 0
+#define LENGTH_OFFSET 2
+#define CHANNEL_SIZE 2
+#define LENGTH_SIZE 4
+
+#define CHANNEL_CONTROL 0
+
+/* The Serial object receives data from the serial port,
+ * extracts the payload size and channel index, then sends
+ * the resulting messages as a packet to a generic receiver.
+ *
+ * You can also use serial_send to send a packet through
+ * the serial port.
+ */
+typedef struct Serial {
+ FDHandler* fdhandler; /* used to monitor serial port fd */
+ Receiver receiver[1]; /* send payload there */
+ int in_len; /* current bytes in input packet */
+ int in_datalen; /* payload size, or 0 when reading header */
+ int in_channel; /* extracted channel number */
+ Packet* in_packet; /* used to read incoming packets */
+} Serial;
+
+
+/* a callback called when the serial port's fd is closed */
+static void
+serial_fd_close( Serial* s )
+{
+ fatal("unexpected serial port close !!");
+}
+
+static void
+serial_dump( Packet* p, const char* funcname )
+{
+ T("%s: %03d bytes: '%s'",
+ funcname, p->len, quote(p->data, p->len));
+}
+
+/* a callback called when a packet arrives from the serial port's FDHandler.
+ *
+ * This will essentially parse the header, extract the channel number and
+ * the payload size and store them in 'in_datalen' and 'in_channel'.
+ *
+ * After that, the payload is sent to the receiver once completed.
+ */
+static void
+serial_fd_receive( Serial* s, Packet* p )
+{
+ int rpos = 0, rcount = p->len;
+ Packet* inp = s->in_packet;
+ int inpos = s->in_len;
+
+ serial_dump( p, __FUNCTION__ );
+
+ while (rpos < rcount)
+ {
+ int avail = rcount - rpos;
+
+ /* first, try to read the header */
+ if (s->in_datalen == 0) {
+ int wanted = HEADER_SIZE - inpos;
+ if (avail > wanted)
+ avail = wanted;
+
+ memcpy( inp->data + inpos, p->data + rpos, avail );
+ inpos += avail;
+ rpos += avail;
+
+ if (inpos == HEADER_SIZE) {
+ s->in_datalen = hex2int( inp->data + LENGTH_OFFSET, LENGTH_SIZE );
+ s->in_channel = hex2int( inp->data + CHANNEL_OFFSET, CHANNEL_SIZE );
+
+ if (s->in_datalen <= 0) {
+ D("ignoring %s packet from serial port",
+ s->in_datalen ? "empty" : "malformed");
+ s->in_datalen = 0;
+ }
+
+ //D("received %d bytes packet for channel %d", s->in_datalen, s->in_channel);
+ inpos = 0;
+ }
+ }
+ else /* then, populate the packet itself */
+ {
+ int wanted = s->in_datalen - inpos;
+
+ if (avail > wanted)
+ avail = wanted;
+
+ memcpy( inp->data + inpos, p->data + rpos, avail );
+ inpos += avail;
+ rpos += avail;
+
+ if (inpos == s->in_datalen) {
+ if (s->in_channel < 0) {
+ D("ignoring %d bytes addressed to channel %d",
+ inpos, s->in_channel);
+ } else {
+ inp->len = inpos;
+ inp->channel = s->in_channel;
+ receiver_post( s->receiver, inp );
+ s->in_packet = inp = packet_alloc();
+ }
+ s->in_datalen = 0;
+ inpos = 0;
+ }
+ }
+ }
+ s->in_len = inpos;
+ packet_free(&p);
+}
+
+
+/* send a packet to the serial port.
+ * this assumes that p->len and p->channel contain the payload's
+ * size and channel and will add the appropriate header.
+ */
+static void
+serial_send( Serial* s, Packet* p )
+{
+ Packet* h = packet_alloc();
+
+ //D("sending to serial %d bytes from channel %d: '%.*s'", p->len, p->channel, p->len, p->data);
+
+ /* insert a small header before this packet */
+ h->len = HEADER_SIZE;
+ int2hex( p->len, h->data + LENGTH_OFFSET, LENGTH_SIZE );
+ int2hex( p->channel, h->data + CHANNEL_OFFSET, CHANNEL_SIZE );
+
+ serial_dump( h, __FUNCTION__ );
+ serial_dump( p, __FUNCTION__ );
+
+ fdhandler_enqueue( s->fdhandler, h );
+ fdhandler_enqueue( s->fdhandler, p );
+}
+
+
+/* initialize serial reader */
+static void
+serial_init( Serial* s,
+ int fd,
+ FDHandlerList* list,
+ Receiver* receiver )
+{
+ Receiver recv;
+
+ recv.user = s;
+ recv.post = (PostFunc) serial_fd_receive;
+ recv.close = (CloseFunc) serial_fd_close;
+
+ s->receiver[0] = receiver[0];
+
+ s->fdhandler = fdhandler_new( fd, list, &recv );
+ s->in_len = 0;
+ s->in_datalen = 0;
+ s->in_channel = 0;
+ s->in_packet = packet_alloc();
+}
+
+
+/** CLIENTS
+ **/
+
+typedef struct Client Client;
+typedef struct Multiplexer Multiplexer;
+
+/* A Client object models a single qemud client socket
+ * connection in the emulated system.
+ *
+ * the client first sends the name of the system service
+ * it wants to contact (no framing), then waits for a 2
+ * byte answer from qemud.
+ *
+ * the answer is either "OK" or "KO" to indicate
+ * success or failure.
+ *
+ * In case of success, the client can send messages
+ * to the service.
+ *
+ * In case of failure, it can disconnect or try sending
+ * the name of another service.
+ */
+struct Client {
+ Client* next;
+ Client** pref;
+ int channel;
+ char registered;
+ FDHandler* fdhandler;
+ Multiplexer* multiplexer;
+};
+
+struct Multiplexer {
+ Client* clients;
+ int last_channel;
+ Serial serial[1];
+ Looper looper[1];
+ FDHandlerList fdhandlers[1];
+};
+
+
+static int multiplexer_open_channel( Multiplexer* mult, Packet* p );
+static void multiplexer_close_channel( Multiplexer* mult, int channel );
+static void multiplexer_serial_send( Multiplexer* mult, int channel, Packet* p );
+
+static void
+client_dump( Client* c, Packet* p, const char* funcname )
+{
+ T("%s: client %p (%d): %3d bytes: '%s'",
+ funcname, c, c->fdhandler->fd,
+ p->len, quote(p->data, p->len));
+}
+
+/* destroy a client */
+static void
+client_free( Client* c )
+{
+ /* remove from list */
+ c->pref[0] = c->next;
+ if (c->next)
+ c->next->pref = c->pref;
+
+ c->channel = -1;
+ c->registered = 0;
+
+ /* gently ask the FDHandler to shutdown to
+ * avoid losing queued outgoing packets */
+ if (c->fdhandler != NULL) {
+ fdhandler_shutdown(c->fdhandler);
+ c->fdhandler = NULL;
+ }
+
+ xfree(c);
+}
+
+
+/* a function called when a client socket receives data */
+static void
+client_fd_receive( Client* c, Packet* p )
+{
+ client_dump(c, p, __FUNCTION__);
+
+ if (c->registered) {
+ /* the client is registered, just send the
+ * data through the serial port
+ */
+ multiplexer_serial_send(c->multiplexer, c->channel, p);
+ return;
+ }
+
+ if (c->channel > 0) {
+ /* the client is waiting registration results.
+ * this should not happen because the client
+ * should wait for our 'ok' or 'ko'.
+ * close the connection.
+ */
+ D("%s: bad client sending data before end of registration",
+ __FUNCTION__);
+ BAD_CLIENT:
+ packet_free(&p);
+ client_free(c);
+ return;
+ }
+
+ /* the client hasn't registered a service yet,
+ * so this must be the name of a service, call
+ * the multiplexer to start registration for
+ * it.
+ */
+ D("%s: attempting registration for service '%.*s'",
+ __FUNCTION__, p->len, p->data);
+ c->channel = multiplexer_open_channel(c->multiplexer, p);
+ if (c->channel < 0) {
+ D("%s: service name too long", __FUNCTION__);
+ goto BAD_CLIENT;
+ }
+ D("%s: -> received channel id %d", __FUNCTION__, c->channel);
+ packet_free(&p);
+}
+
+
+/* a function called when the client socket is closed. */
+static void
+client_fd_close( Client* c )
+{
+ T("%s: client %p (%d)", __FUNCTION__, c, c->fdhandler->fd);
+
+ /* no need to shutdown the FDHandler */
+ c->fdhandler = NULL;
+
+ /* tell the emulator we're out */
+ if (c->channel > 0)
+ multiplexer_close_channel(c->multiplexer, c->channel);
+
+ /* free the client */
+ client_free(c);
+}
+
+/* a function called when the multiplexer received a registration
+ * response from the emulator for a given client.
+ */
+static void
+client_registration( Client* c, int registered )
+{
+ Packet* p = packet_alloc();
+
+ /* sends registration status to client */
+ if (!registered) {
+ D("%s: registration failed for client %d", __FUNCTION__, c->channel);
+ memcpy( p->data, "KO", 2 );
+ p->len = 2;
+ } else {
+ D("%s: registration succeeded for client %d", __FUNCTION__, c->channel);
+ memcpy( p->data, "OK", 2 );
+ p->len = 2;
+ }
+ client_dump(c, p, __FUNCTION__);
+ fdhandler_enqueue(c->fdhandler, p);
+
+ /* now save registration state
+ */
+ c->registered = registered;
+ if (!registered) {
+ /* allow the client to try registering another service */
+ c->channel = -1;
+ }
+}
+
+/* send data to a client */
+static void
+client_send( Client* c, Packet* p )
+{
+ client_dump(c, p, __FUNCTION__);
+ fdhandler_enqueue(c->fdhandler, p);
+}
+
+
+/* Create new client socket handler */
+static Client*
+client_new( Multiplexer* mult,
+ int fd,
+ FDHandlerList* pfdhandlers,
+ Client** pclients )
+{
+ Client* c;
+ Receiver recv;
+
+ xnew(c);
+
+ c->multiplexer = mult;
+ c->next = NULL;
+ c->pref = &c->next;
+ c->channel = -1;
+ c->registered = 0;
+
+ recv.user = c;
+ recv.post = (PostFunc) client_fd_receive;
+ recv.close = (CloseFunc) client_fd_close;
+
+ c->fdhandler = fdhandler_new( fd, pfdhandlers, &recv );
+
+ /* add to client list */
+ c->next = *pclients;
+ c->pref = pclients;
+ *pclients = c;
+ if (c->next)
+ c->next->pref = &c->next;
+
+ return c;
+}
+
+/** GLOBAL MULTIPLEXER
+ **/
+
+/* find a client by its channel */
+static Client*
+multiplexer_find_client( Multiplexer* mult, int channel )
+{
+ Client* c = mult->clients;
+
+ for ( ; c != NULL; c = c->next ) {
+ if (c->channel == channel)
+ return c;
+ }
+ return NULL;
+}
+
+/* handle control messages coming from the serial port
+ * on CONTROL_CHANNEL.
+ */
+static void
+multiplexer_handle_control( Multiplexer* mult, Packet* p )
+{
+ /* connection registration success */
+ if (p->len == 13 && !memcmp(p->data, "ok:connect:", 11)) {
+ int channel = hex2int(p->data+11, 2);
+ Client* client = multiplexer_find_client(mult, channel);
+
+ /* note that 'client' can be NULL if the corresponding
+ * socket was closed before the emulator response arrived.
+ */
+ if (client != NULL) {
+ client_registration(client, 1);
+ } else {
+ D("%s: NULL client: '%.*s'", __FUNCTION__, p->len, p->data+11);
+ }
+ goto EXIT;
+ }
+
+ /* connection registration failure */
+ if (p->len == 13 && !memcmp(p->data, "ko:connect:",11)) {
+ int channel = hex2int(p->data+11, 2);
+ Client* client = multiplexer_find_client(mult, channel);
+
+ if (client != NULL)
+ client_registration(client, 0);
+
+ goto EXIT;
+ }
+
+ /* emulator-induced client disconnection */
+ if (p->len == 13 && !memcmp(p->data, "disconnect:",11)) {
+ int channel = hex2int(p->data+11, 2);
+ Client* client = multiplexer_find_client(mult, channel);
+
+ if (client != NULL)
+ client_free(client);
+
+ goto EXIT;
+ }
+
+ /* A message that begins with "X00" is a probe sent by
+ * the emulator used to detect which version of qemud it runs
+ * against (in order to detect 1.0/1.1 system images. Just
+ * silently ignore it there instead of printing an error
+ * message.
+ */
+ if (p->len >= 3 && !memcmp(p->data,"X00",3)) {
+ goto EXIT;
+ }
+
+ D("%s: unknown control message (%d bytes): '%.*s'",
+ __FUNCTION__, p->len, p->len, p->data);
+
+EXIT:
+ packet_free(&p);
+}
+
+/* a function called when an incoming packet comes from the serial port */
+static void
+multiplexer_serial_receive( Multiplexer* mult, Packet* p )
+{
+ Client* client;
+
+ T("%s: channel=%d '%.*s'", __FUNCTION__, p->channel, p->len, p->data);
+
+ if (p->channel == CHANNEL_CONTROL) {
+ multiplexer_handle_control(mult, p);
+ return;
+ }
+
+ client = multiplexer_find_client(mult, p->channel);
+ if (client != NULL) {
+ client_send(client, p);
+ return;
+ }
+
+ D("%s: discarding packet for unknown channel %d", __FUNCTION__, p->channel);
+ packet_free(&p);
+}
+
+/* a function called when the serial reader closes */
+static void
+multiplexer_serial_close( Multiplexer* mult )
+{
+ fatal("unexpected close of serial reader");
+}
+
+/* a function called to send a packet to the serial port */
+static void
+multiplexer_serial_send( Multiplexer* mult, int channel, Packet* p )
+{
+ p->channel = channel;
+ serial_send( mult->serial, p );
+}
+
+
+
+/* a function used by a client to allocate a new channel id and
+ * ask the emulator to open it. 'service' must be a packet containing
+ * the name of the service in its payload.
+ *
+ * returns -1 if the service name is too long.
+ *
+ * notice that client_registration() will be called later when
+ * the answer arrives.
+ */
+static int
+multiplexer_open_channel( Multiplexer* mult, Packet* service )
+{
+ Packet* p = packet_alloc();
+ int len, channel;
+
+ /* find a free channel number, assume we don't have many
+ * clients here. */
+ {
+ Client* c;
+ TRY_AGAIN:
+ channel = (++mult->last_channel) & 0xff;
+
+ for (c = mult->clients; c != NULL; c = c->next)
+ if (c->channel == channel)
+ goto TRY_AGAIN;
+ }
+
+ len = snprintf((char*)p->data, sizeof p->data, "connect:%.*s:%02x", service->len, service->data, channel);
+ if (len >= (int)sizeof(p->data)) {
+ D("%s: weird, service name too long (%d > %d)", __FUNCTION__, len, sizeof(p->data));
+ packet_free(&p);
+ return -1;
+ }
+ p->channel = CHANNEL_CONTROL;
+ p->len = len;
+
+ serial_send(mult->serial, p);
+ return channel;
+}
+
+/* used to tell the emulator a channel was closed by a client */
+static void
+multiplexer_close_channel( Multiplexer* mult, int channel )
+{
+ Packet* p = packet_alloc();
+ int len = snprintf((char*)p->data, sizeof(p->data), "disconnect:%02x", channel);
+
+ if (len > (int)sizeof(p->data)) {
+ /* should not happen */
+ return;
+ }
+
+ p->channel = CHANNEL_CONTROL;
+ p->len = len;
+
+ serial_send(mult->serial, p);
+}
+
+/* this function is used when a new connection happens on the control
+ * socket.
+ */
+static void
+multiplexer_control_accept( Multiplexer* m, Packet* p )
+{
+ /* the file descriptor for the new socket connection is
+ * in p->channel. See fdhandler_accept_event() */
+ int fd = p->channel;
+ Client* client = client_new( m, fd, m->fdhandlers, &m->clients );
+
+ D("created client %p listening on fd %d", client, fd);
+
+ /* free dummy packet */
+ packet_free(&p);
+}
+
+static void
+multiplexer_control_close( Multiplexer* m )
+{
+ fatal("unexpected multiplexer control close");
+}
+
+static void
+multiplexer_init( Multiplexer* m, const char* serial_dev )
+{
+ int fd, control_fd;
+ Receiver recv;
+
+ /* initialize looper and fdhandlers list */
+ looper_init( m->looper );
+ fdhandler_list_init( m->fdhandlers, m->looper );
+
+ /* open the serial port */
+ do {
+ fd = open(serial_dev, O_RDWR);
+ } while (fd < 0 && errno == EINTR);
+
+ if (fd < 0) {
+ fatal( "%s: could not open '%s': %s", __FUNCTION__, serial_dev,
+ strerror(errno) );
+ }
+ // disable echo on serial lines
+ if ( !memcmp( serial_dev, "/dev/ttyS", 9 ) ) {
+ struct termios ios;
+ tcgetattr( fd, &ios );
+ ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */
+ tcsetattr( fd, TCSANOW, &ios );
+ }
+
+ /* initialize the serial reader/writer */
+ recv.user = m;
+ recv.post = (PostFunc) multiplexer_serial_receive;
+ recv.close = (CloseFunc) multiplexer_serial_close;
+
+ serial_init( m->serial, fd, m->fdhandlers, &recv );
+
+ /* open the qemud control socket */
+ recv.user = m;
+ recv.post = (PostFunc) multiplexer_control_accept;
+ recv.close = (CloseFunc) multiplexer_control_close;
+
+ fd = android_get_control_socket(CONTROL_SOCKET_NAME);
+ if (fd < 0) {
+ fatal("couldn't get fd for control socket '%s'", CONTROL_SOCKET_NAME);
+ }
+
+ fdhandler_new_accept( fd, m->fdhandlers, &recv );
+
+ /* initialize clients list */
+ m->clients = NULL;
+}
+
+/** MAIN LOOP
+ **/
+
+static Multiplexer _multiplexer[1];
+
+int main( void )
+{
+ Multiplexer* m = _multiplexer;
+
+ /* extract the name of our serial device from the kernel
+ * boot options that are stored in /proc/cmdline
+ */
+#define KERNEL_OPTION "android.qemud="
+
+ {
+ char buff[1024];
+ int fd, len;
+ char* p;
+ char* q;
+
+ fd = open( "/proc/cmdline", O_RDONLY );
+ if (fd < 0) {
+ D("%s: can't open /proc/cmdline !!: %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+
+ len = fd_read( fd, buff, sizeof(buff)-1 );
+ close(fd);
+ if (len < 0) {
+ D("%s: can't read /proc/cmdline: %s", __FUNCTION__,
+ strerror(errno));
+ exit(1);
+ }
+ buff[len] = 0;
+
+ p = strstr( buff, KERNEL_OPTION );
+ if (p == NULL) {
+ D("%s: can't find '%s' in /proc/cmdline",
+ __FUNCTION__, KERNEL_OPTION );
+ exit(1);
+ }
+
+ p += sizeof(KERNEL_OPTION)-1; /* skip option */
+ q = p;
+ while ( *q && *q != ' ' && *q != '\t' )
+ q += 1;
+
+ snprintf( buff, sizeof(buff), "/dev/%.*s", q-p, p );
+
+ multiplexer_init( m, buff );
+ }
+
+ D( "entering main loop");
+ looper_loop( m->looper );
+ D( "unexpected termination !!" );
+ return 0;
+}
diff --git a/tools/emulator/system/sensors/Android.mk b/tools/emulator/system/sensors/Android.mk
new file mode 100644
index 0000000..b6dcaf6
--- /dev/null
+++ b/tools/emulator/system/sensors/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2009 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.
+
+
+# We're moving the emulator-specific platform libs to
+# development.git/tools/emulator/. The following test is to ensure
+# smooth builds even if the tree contains both versions.
+#
+ifndef BUILD_EMULATOR_SENSORS_MODULE
+BUILD_EMULATOR_SENSORS_MODULE := true
+
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_PRODUCT),sim)
+# HAL module implemenation stored in
+# hw/<SENSORS_HARDWARE_MODULE_ID>.<ro.hardware>.so
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_SRC_FILES := sensors_qemu.c
+LOCAL_MODULE := sensors.goldfish
+LOCAL_MODULE_TAGS := debug
+include $(BUILD_SHARED_LIBRARY)
+endif
+
+endif # BUILD_EMULATOR_SENSORS_MODULE
diff --git a/tools/emulator/system/sensors/sensors_qemu.c b/tools/emulator/system/sensors/sensors_qemu.c
new file mode 100644
index 0000000..9a776c7
--- /dev/null
+++ b/tools/emulator/system/sensors/sensors_qemu.c
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) 2009 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 implements a sensors hardware library for the Android emulator.
+ * the following code should be built as a shared library that will be
+ * placed into /system/lib/hw/sensors.goldfish.so
+ *
+ * it will be loaded by the code in hardware/libhardware/hardware.c
+ * which is itself called from com_android_server_SensorService.cpp
+ */
+
+
+/* we connect with the emulator through the "sensors" qemud service
+ */
+#define SENSORS_SERVICE_NAME "sensors"
+
+#define LOG_TAG "QemuSensors"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <cutils/log.h>
+#include <cutils/native_handle.h>
+#include <cutils/sockets.h>
+#include <hardware/sensors.h>
+
+#if 0
+#define D(...) LOGD(__VA_ARGS__)
+#else
+#define D(...) ((void)0)
+#endif
+
+#define E(...) LOGE(__VA_ARGS__)
+
+#include <hardware/qemud.h>
+
+/** SENSOR IDS AND NAMES
+ **/
+
+#define MAX_NUM_SENSORS 5
+
+#define SUPPORTED_SENSORS ((1<<MAX_NUM_SENSORS)-1)
+
+#define ID_BASE SENSORS_HANDLE_BASE
+#define ID_ACCELERATION (ID_BASE+0)
+#define ID_MAGNETIC_FIELD (ID_BASE+1)
+#define ID_ORIENTATION (ID_BASE+2)
+#define ID_TEMPERATURE (ID_BASE+3)
+#define ID_PROXIMITY (ID_BASE+4)
+
+#define SENSORS_ACCELERATION (1 << ID_ACCELERATION)
+#define SENSORS_MAGNETIC_FIELD (1 << ID_MAGNETIC_FIELD)
+#define SENSORS_ORIENTATION (1 << ID_ORIENTATION)
+#define SENSORS_TEMPERATURE (1 << ID_TEMPERATURE)
+#define SENSORS_PROXIMITY (1 << ID_PROXIMITY)
+
+#define ID_CHECK(x) ((unsigned)((x)-ID_BASE) < MAX_NUM_SENSORS)
+
+#define SENSORS_LIST \
+ SENSOR_(ACCELERATION,"acceleration") \
+ SENSOR_(MAGNETIC_FIELD,"magnetic-field") \
+ SENSOR_(ORIENTATION,"orientation") \
+ SENSOR_(TEMPERATURE,"temperature") \
+ SENSOR_(PROXIMITY,"proximity") \
+
+static const struct {
+ const char* name;
+ int id; } _sensorIds[MAX_NUM_SENSORS] =
+{
+#define SENSOR_(x,y) { y, ID_##x },
+ SENSORS_LIST
+#undef SENSOR_
+};
+
+static const char*
+_sensorIdToName( int id )
+{
+ int nn;
+ for (nn = 0; nn < MAX_NUM_SENSORS; nn++)
+ if (id == _sensorIds[nn].id)
+ return _sensorIds[nn].name;
+ return "<UNKNOWN>";
+}
+
+static int
+_sensorIdFromName( const char* name )
+{
+ int nn;
+
+ if (name == NULL)
+ return -1;
+
+ for (nn = 0; nn < MAX_NUM_SENSORS; nn++)
+ if (!strcmp(name, _sensorIds[nn].name))
+ return _sensorIds[nn].id;
+
+ return -1;
+}
+
+/** SENSORS POLL DEVICE
+ **
+ ** This one is used to read sensor data from the hardware.
+ ** We implement this by simply reading the data from the
+ ** emulator through the QEMUD channel.
+ **/
+
+typedef struct SensorPoll {
+ struct sensors_poll_device_t device;
+ sensors_event_t sensors[MAX_NUM_SENSORS];
+ int events_fd;
+ uint32_t pendingSensors;
+ int64_t timeStart;
+ int64_t timeOffset;
+ int fd;
+ uint32_t active_sensors;
+} SensorPoll;
+
+/* this must return a file descriptor that will be used to read
+ * the sensors data (it is passed to data__data_open() below
+ */
+static native_handle_t*
+control__open_data_source(struct sensors_poll_device_t *dev)
+{
+ SensorPoll* ctl = (void*)dev;
+ native_handle_t* handle;
+
+ if (ctl->fd < 0) {
+ ctl->fd = qemud_channel_open(SENSORS_SERVICE_NAME);
+ }
+ D("%s: fd=%d", __FUNCTION__, ctl->fd);
+ handle = native_handle_create(1, 0);
+ handle->data[0] = dup(ctl->fd);
+ return handle;
+}
+
+static int
+control__activate(struct sensors_poll_device_t *dev,
+ int handle,
+ int enabled)
+{
+ SensorPoll* ctl = (void*)dev;
+ uint32_t mask, sensors, active, new_sensors, changed;
+ char command[128];
+ int ret;
+
+ D("%s: handle=%s (%d) fd=%d enabled=%d", __FUNCTION__,
+ _sensorIdToName(handle), handle, ctl->fd, enabled);
+
+ if (!ID_CHECK(handle)) {
+ E("%s: bad handle ID", __FUNCTION__);
+ return -1;
+ }
+
+ mask = (1<<handle);
+ sensors = enabled ? mask : 0;
+
+ active = ctl->active_sensors;
+ new_sensors = (active & ~mask) | (sensors & mask);
+ changed = active ^ new_sensors;
+
+ if (!changed)
+ return 0;
+
+ snprintf(command, sizeof command, "set:%s:%d",
+ _sensorIdToName(handle), enabled != 0);
+
+ if (ctl->fd < 0) {
+ ctl->fd = qemud_channel_open(SENSORS_SERVICE_NAME);
+ }
+
+ ret = qemud_channel_send(ctl->fd, command, -1);
+ if (ret < 0) {
+ E("%s: when sending command errno=%d: %s", __FUNCTION__, errno, strerror(errno));
+ return -1;
+ }
+ ctl->active_sensors = new_sensors;
+
+ return 0;
+}
+
+static int
+control__set_delay(struct sensors_poll_device_t *dev, int32_t ms)
+{
+ SensorPoll* ctl = (void*)dev;
+ char command[128];
+
+ D("%s: dev=%p delay-ms=%d", __FUNCTION__, dev, ms);
+
+ snprintf(command, sizeof command, "set-delay:%d", ms);
+
+ return qemud_channel_send(ctl->fd, command, -1);
+}
+
+static int
+control__close(struct hw_device_t *dev)
+{
+ SensorPoll* ctl = (void*)dev;
+ close(ctl->fd);
+ free(ctl);
+ return 0;
+}
+
+/* return the current time in nanoseconds */
+static int64_t
+data__now_ns(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ return (int64_t)ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
+
+static int
+data__data_open(struct sensors_poll_device_t *dev, native_handle_t* handle)
+{
+ SensorPoll* data = (void*)dev;
+ int i;
+ D("%s: dev=%p fd=%d", __FUNCTION__, dev, handle->data[0]);
+ memset(&data->sensors, 0, sizeof(data->sensors));
+
+ for (i=0 ; i<MAX_NUM_SENSORS ; i++) {
+ data->sensors[i].acceleration.status = SENSOR_STATUS_ACCURACY_HIGH;
+ }
+ data->pendingSensors = 0;
+ data->timeStart = 0;
+ data->timeOffset = 0;
+
+ data->events_fd = dup(handle->data[0]);
+ D("%s: dev=%p fd=%d (was %d)", __FUNCTION__, dev, data->events_fd, handle->data[0]);
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ return 0;
+}
+
+static int
+data__data_close(struct sensors_poll_device_t *dev)
+{
+ SensorPoll* data = (void*)dev;
+ D("%s: dev=%p", __FUNCTION__, dev);
+ if (data->events_fd >= 0) {
+ close(data->events_fd);
+ data->events_fd = -1;
+ }
+ return 0;
+}
+
+static int
+pick_sensor(SensorPoll* data,
+ sensors_event_t* values)
+{
+ uint32_t mask = SUPPORTED_SENSORS;
+ while (mask) {
+ uint32_t i = 31 - __builtin_clz(mask);
+ mask &= ~(1<<i);
+ if (data->pendingSensors & (1<<i)) {
+ data->pendingSensors &= ~(1<<i);
+ *values = data->sensors[i];
+ values->sensor = i;
+ values->version = sizeof(*values);
+
+ D("%s: %d [%f, %f, %f]", __FUNCTION__,
+ i,
+ values->data[0],
+ values->data[1],
+ values->data[2]);
+ return i;
+ }
+ }
+ LOGE("No sensor to return!!! pendingSensors=%08x", data->pendingSensors);
+ // we may end-up in a busy loop, slow things down, just in case.
+ usleep(100000);
+ return -EINVAL;
+}
+
+static int
+data__poll(struct sensors_poll_device_t *dev, sensors_event_t* values)
+{
+ SensorPoll* data = (void*)dev;
+ int fd = data->events_fd;
+
+ D("%s: data=%p", __FUNCTION__, dev);
+
+ // there are pending sensors, returns them now...
+ if (data->pendingSensors) {
+ return pick_sensor(data, values);
+ }
+
+ // wait until we get a complete event for an enabled sensor
+ uint32_t new_sensors = 0;
+
+ while (1) {
+ /* read the next event */
+ char buff[256];
+ int len = qemud_channel_recv(data->events_fd, buff, sizeof buff-1);
+ float params[3];
+ int64_t event_time;
+
+ if (len < 0) {
+ E("%s: len=%d, errno=%d: %s", __FUNCTION__, len, errno, strerror(errno));
+ return -errno;
+ }
+
+ buff[len] = 0;
+
+ /* "wake" is sent from the emulator to exit this loop. */
+ if (!strcmp((const char*)data, "wake")) {
+ return 0x7FFFFFFF;
+ }
+
+ /* "acceleration:<x>:<y>:<z>" corresponds to an acceleration event */
+ if (sscanf(buff, "acceleration:%g:%g:%g", params+0, params+1, params+2) == 3) {
+ new_sensors |= SENSORS_ACCELERATION;
+ data->sensors[ID_ACCELERATION].acceleration.x = params[0];
+ data->sensors[ID_ACCELERATION].acceleration.y = params[1];
+ data->sensors[ID_ACCELERATION].acceleration.z = params[2];
+ continue;
+ }
+
+ /* "orientation:<azimuth>:<pitch>:<roll>" is sent when orientation changes */
+ if (sscanf(buff, "orientation:%g:%g:%g", params+0, params+1, params+2) == 3) {
+ new_sensors |= SENSORS_ORIENTATION;
+ data->sensors[ID_ORIENTATION].orientation.azimuth = params[0];
+ data->sensors[ID_ORIENTATION].orientation.pitch = params[1];
+ data->sensors[ID_ORIENTATION].orientation.roll = params[2];
+ continue;
+ }
+
+ /* "magnetic:<x>:<y>:<z>" is sent for the params of the magnetic field */
+ if (sscanf(buff, "magnetic:%g:%g:%g", params+0, params+1, params+2) == 3) {
+ new_sensors |= SENSORS_MAGNETIC_FIELD;
+ data->sensors[ID_MAGNETIC_FIELD].magnetic.x = params[0];
+ data->sensors[ID_MAGNETIC_FIELD].magnetic.y = params[1];
+ data->sensors[ID_MAGNETIC_FIELD].magnetic.z = params[2];
+ continue;
+ }
+
+ /* "temperature:<celsius>" */
+ if (sscanf(buff, "temperature:%g", params+0) == 2) {
+ new_sensors |= SENSORS_TEMPERATURE;
+ data->sensors[ID_TEMPERATURE].temperature = params[0];
+ continue;
+ }
+
+ /* "proximity:<value>" */
+ if (sscanf(buff, "proximity:%g", params+0) == 1) {
+ new_sensors |= SENSORS_PROXIMITY;
+ data->sensors[ID_PROXIMITY].distance = params[0];
+ continue;
+ }
+
+ /* "sync:<time>" is sent after a series of sensor events.
+ * where 'time' is expressed in micro-seconds and corresponds
+ * to the VM time when the real poll occured.
+ */
+ if (sscanf(buff, "sync:%lld", &event_time) == 1) {
+ if (new_sensors) {
+ data->pendingSensors = new_sensors;
+ int64_t t = event_time * 1000LL; /* convert to nano-seconds */
+
+ /* use the time at the first sync: as the base for later
+ * time values */
+ if (data->timeStart == 0) {
+ data->timeStart = data__now_ns();
+ data->timeOffset = data->timeStart - t;
+ }
+ t += data->timeOffset;
+
+ while (new_sensors) {
+ uint32_t i = 31 - __builtin_clz(new_sensors);
+ new_sensors &= ~(1<<i);
+ data->sensors[i].timestamp = t;
+ }
+ return pick_sensor(data, values);
+ } else {
+ D("huh ? sync without any sensor data ?");
+ }
+ continue;
+ }
+ D("huh ? unsupported command");
+ }
+ return -1;
+}
+
+static int
+data__close(struct hw_device_t *dev)
+{
+ SensorPoll* data = (SensorPoll*)dev;
+ if (data) {
+ if (data->events_fd >= 0) {
+ //LOGD("(device close) about to close fd=%d", data->events_fd);
+ close(data->events_fd);
+ }
+ free(data);
+ }
+ return 0;
+}
+
+/** SENSORS POLL DEVICE FUNCTIONS **/
+
+static int poll__close(struct hw_device_t* dev)
+{
+ SensorPoll* ctl = (void*)dev;
+ close(ctl->fd);
+ if (ctl->fd >= 0) {
+ close(ctl->fd);
+ }
+ if (ctl->events_fd >= 0) {
+ close(ctl->events_fd);
+ }
+ free(ctl);
+ return 0;
+}
+
+static int poll__poll(struct sensors_poll_device_t *dev,
+ sensors_event_t* data, int count)
+{
+ SensorPoll* datadev = (void*)dev;
+ int ret;
+ int i;
+ D("%s: dev=%p data=%p count=%d ", __FUNCTION__, dev, data, count);
+
+ for (i = 0; i < count; i++) {
+ ret = data__poll(dev, data);
+ data++;
+ if (ret > MAX_NUM_SENSORS || ret < 0) {
+ return i;
+ }
+ if (!datadev->pendingSensors) {
+ return i + 1;
+ }
+ }
+ return count;
+}
+
+static int poll__activate(struct sensors_poll_device_t *dev,
+ int handle, int enabled)
+{
+ int ret;
+ native_handle_t* hdl;
+ SensorPoll* ctl = (void*)dev;
+ D("%s: dev=%p handle=%x enable=%d ", __FUNCTION__, dev, handle, enabled);
+ if (ctl->fd < 0) {
+ D("%s: OPEN CTRL and DATA ", __FUNCTION__);
+ hdl = control__open_data_source(dev);
+ ret = data__data_open(dev,hdl);
+ }
+ ret = control__activate(dev, handle, enabled);
+ return ret;
+}
+
+static int poll__setDelay(struct sensors_poll_device_t *dev,
+ int handle, int64_t ns)
+{
+ // TODO
+ return 0;
+}
+
+/** MODULE REGISTRATION SUPPORT
+ **
+ ** This is required so that hardware/libhardware/hardware.c
+ ** will dlopen() this library appropriately.
+ **/
+
+/*
+ * the following is the list of all supported sensors.
+ * this table is used to build sSensorList declared below
+ * according to which hardware sensors are reported as
+ * available from the emulator (see get_sensors_list below)
+ *
+ * note: numerical values for maxRange/resolution/power were
+ * taken from the reference AK8976A implementation
+ */
+static const struct sensor_t sSensorListInit[] = {
+ { .name = "Goldfish 3-axis Accelerometer",
+ .vendor = "The Android Open Source Project",
+ .version = 1,
+ .handle = ID_ACCELERATION,
+ .type = SENSOR_TYPE_ACCELEROMETER,
+ .maxRange = 2.8f,
+ .resolution = 1.0f/4032.0f,
+ .power = 3.0f,
+ .reserved = {}
+ },
+
+ { .name = "Goldfish 3-axis Magnetic field sensor",
+ .vendor = "The Android Open Source Project",
+ .version = 1,
+ .handle = ID_MAGNETIC_FIELD,
+ .type = SENSOR_TYPE_MAGNETIC_FIELD,
+ .maxRange = 2000.0f,
+ .resolution = 1.0f,
+ .power = 6.7f,
+ .reserved = {}
+ },
+
+ { .name = "Goldfish Orientation sensor",
+ .vendor = "The Android Open Source Project",
+ .version = 1,
+ .handle = ID_ORIENTATION,
+ .type = SENSOR_TYPE_ORIENTATION,
+ .maxRange = 360.0f,
+ .resolution = 1.0f,
+ .power = 9.7f,
+ .reserved = {}
+ },
+
+ { .name = "Goldfish Temperature sensor",
+ .vendor = "The Android Open Source Project",
+ .version = 1,
+ .handle = ID_TEMPERATURE,
+ .type = SENSOR_TYPE_TEMPERATURE,
+ .maxRange = 80.0f,
+ .resolution = 1.0f,
+ .power = 0.0f,
+ .reserved = {}
+ },
+
+ { .name = "Goldfish Proximity sensor",
+ .vendor = "The Android Open Source Project",
+ .version = 1,
+ .handle = ID_PROXIMITY,
+ .type = SENSOR_TYPE_PROXIMITY,
+ .maxRange = 1.0f,
+ .resolution = 1.0f,
+ .power = 20.0f,
+ .reserved = {}
+ },
+};
+
+static struct sensor_t sSensorList[MAX_NUM_SENSORS];
+
+static int sensors__get_sensors_list(struct sensors_module_t* module,
+ struct sensor_t const** list)
+{
+ int fd = qemud_channel_open(SENSORS_SERVICE_NAME);
+ char buffer[12];
+ int mask, nn, count;
+
+ int ret;
+ if (fd < 0) {
+ E("%s: no qemud connection", __FUNCTION__);
+ return 0;
+ }
+ ret = qemud_channel_send(fd, "list-sensors", -1);
+ if (ret < 0) {
+ E("%s: could not query sensor list: %s", __FUNCTION__,
+ strerror(errno));
+ close(fd);
+ return 0;
+ }
+ ret = qemud_channel_recv(fd, buffer, sizeof buffer-1);
+ if (ret < 0) {
+ E("%s: could not receive sensor list: %s", __FUNCTION__,
+ strerror(errno));
+ close(fd);
+ return 0;
+ }
+ buffer[ret] = 0;
+ close(fd);
+
+ /* the result is a integer used as a mask for available sensors */
+ mask = atoi(buffer);
+ count = 0;
+ for (nn = 0; nn < MAX_NUM_SENSORS; nn++) {
+ if (((1 << nn) & mask) == 0)
+ continue;
+
+ sSensorList[count++] = sSensorListInit[nn];
+ }
+ D("%s: returned %d sensors (mask=%d)", __FUNCTION__, count, mask);
+ *list = sSensorList;
+ return count;
+}
+
+
+static int
+open_sensors(const struct hw_module_t* module,
+ const char* name,
+ struct hw_device_t* *device)
+{
+ int status = -EINVAL;
+
+ D("%s: name=%s", __FUNCTION__, name);
+
+ if (!strcmp(name, SENSORS_HARDWARE_POLL)) {
+ SensorPoll *dev = malloc(sizeof(*dev));
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->device.common.tag = HARDWARE_DEVICE_TAG;
+ dev->device.common.version = 0;
+ dev->device.common.module = (struct hw_module_t*) module;
+ dev->device.common.close = poll__close;
+ dev->device.poll = poll__poll;
+ dev->device.activate = poll__activate;
+ dev->device.setDelay = poll__setDelay;
+ dev->events_fd = -1;
+ dev->fd = -1;
+
+ *device = &dev->device.common;
+ status = 0;
+ }
+ return status;
+}
+
+
+static struct hw_module_methods_t sensors_module_methods = {
+ .open = open_sensors
+};
+
+const struct sensors_module_t HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = SENSORS_HARDWARE_MODULE_ID,
+ .name = "Goldfish SENSORS Module",
+ .author = "The Android Open Source Project",
+ .methods = &sensors_module_methods,
+ },
+ .get_sensors_list = sensors__get_sensors_list
+};