am 3e132a1a: Fix sdk source prop to include proper layoutlib api level.

* commit '3e132a1a2602019ccccc4d965cb7d3aded1c988d':
  Fix sdk source prop to include proper layoutlib api level.
diff --git a/apps/Fallback/res/values-af/strings.xml b/apps/Fallback/res/values-af/strings.xml
deleted file mode 100644
index 8cf16a7..0000000
--- a/apps/Fallback/res/values-af/strings.xml
+++ /dev/null
@@ -1,22 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle" msgid="161410001913116606">"Terugval"</string>
-    <string name="title" msgid="8156274565006125136">"Nie-ondersteunde handeling"</string>
-    <string name="error" msgid="6539615832923362301">"Hierdie handeling word tans nie ondersteun nie."</string>
-</resources>
diff --git a/apps/Fallback/res/values-am/strings.xml b/apps/Fallback/res/values-am/strings.xml
deleted file mode 100644
index 0c89122..0000000
--- a/apps/Fallback/res/values-am/strings.xml
+++ /dev/null
@@ -1,22 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle" msgid="161410001913116606">"Fallback"</string>
-    <string name="title" msgid="8156274565006125136">"የማይደገፍ ድርጊት"</string>
-    <string name="error" msgid="6539615832923362301">"ያድርጊት በአሁኑ ጊዜ የማይደገፍ ነው።"</string>
-</resources>
diff --git a/apps/Fallback/res/values-iw/strings.xml b/apps/Fallback/res/values-iw/strings.xml
index cbf35ef..671919b 100644
--- a/apps/Fallback/res/values-iw/strings.xml
+++ b/apps/Fallback/res/values-iw/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle" msgid="161410001913116606">"חלופי"</string>
+    <string name="appTitle" msgid="161410001913116606">"החזרה"</string>
     <string name="title" msgid="8156274565006125136">"פעולה לא נתמכת"</string>
     <string name="error" msgid="6539615832923362301">"הפעולה אינה נתמכת בשלב זה."</string>
 </resources>
diff --git a/apps/Fallback/res/values-ms/strings.xml b/apps/Fallback/res/values-ms/strings.xml
deleted file mode 100644
index 930fe79..0000000
--- a/apps/Fallback/res/values-ms/strings.xml
+++ /dev/null
@@ -1,22 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle" msgid="161410001913116606">"Jatuh balik"</string>
-    <string name="title" msgid="8156274565006125136">"Tindakan tidak disokong"</string>
-    <string name="error" msgid="6539615832923362301">"Tindakan tidak disokong pada masa ini."</string>
-</resources>
diff --git a/apps/Fallback/res/values-sw/strings.xml b/apps/Fallback/res/values-sw/strings.xml
deleted file mode 100644
index f322c20..0000000
--- a/apps/Fallback/res/values-sw/strings.xml
+++ /dev/null
@@ -1,22 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle" msgid="161410001913116606">"Rudi nyuma"</string>
-    <string name="title" msgid="8156274565006125136">"Kitendo kinachohimiliwa"</string>
-    <string name="error" msgid="6539615832923362301">"Kitendo hakijahimiliwa ipasavyo"</string>
-</resources>
diff --git a/apps/Fallback/res/values-zu/strings.xml b/apps/Fallback/res/values-zu/strings.xml
deleted file mode 100644
index 2c85d28..0000000
--- a/apps/Fallback/res/values-zu/strings.xml
+++ /dev/null
@@ -1,22 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="appTitle" msgid="161410001913116606">"Buyela emuva"</string>
-    <string name="title" msgid="8156274565006125136">"Isenzo esingasekelwe"</string>
-    <string name="error" msgid="6539615832923362301">"Leso senzo okwamanje asisekelwe."</string>
-</resources>
diff --git a/samples/ApiDemos/res/layout/layout_animations_by_default.xml b/samples/ApiDemos/res/layout/layout_animations_by_default.xml
index a5062bb..6236088 100644
--- a/samples/ApiDemos/res/layout/layout_animations_by_default.xml
+++ b/samples/ApiDemos/res/layout/layout_animations_by_default.xml
@@ -24,18 +24,11 @@
         android:text="Add Button"
         android:id="@+id/addNewButton"
         />
-    <LinearLayout
-        android:orientation="horizontal"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:id="@+id/horizontalContainer"
-        android:animateLayoutChanges="true"
-        />
-    <LinearLayout
-        android:orientation="vertical"
+    <GridLayout
+        android:columnCount="4"
         android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:id="@+id/verticalContainer"
+        android:layout_height="wrap_content"
+        android:id="@+id/gridContainer"
         android:animateLayoutChanges="true"
         />
 </LinearLayout>
diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.java b/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.java
index 67b9b51..8898b55 100644
--- a/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.java
+++ b/samples/ApiDemos/src/com/example/android/apis/animation/LayoutAnimationsByDefault.java
@@ -21,11 +21,11 @@
 import com.example.android.apis.R;
 
 import android.view.View;
-import android.view.ViewGroup;
 
 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.Button;
+import android.widget.GridLayout;
 
 /**
  * This application demonstrates how to use the animateLayoutChanges tag in XML to automate
@@ -35,36 +35,25 @@
 
     private int numButtons = 1;
 
-    /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.layout_animations_by_default);
 
-        final ViewGroup horizontalContainer = (ViewGroup) findViewById(R.id.horizontalContainer);
-        final ViewGroup verticalContainer = (ViewGroup) findViewById(R.id.verticalContainer);
+        final GridLayout gridContainer = (GridLayout) findViewById(R.id.gridContainer);
 
         Button addButton = (Button) findViewById(R.id.addNewButton);
         addButton.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 Button newButton = new Button(LayoutAnimationsByDefault.this);
-                newButton.setText("Click To Remove " + (numButtons++));
+                newButton.setText(String.valueOf(numButtons++));
                 newButton.setOnClickListener(new View.OnClickListener() {
                     public void onClick(View v) {
-                        horizontalContainer.removeView(v);
+                        gridContainer.removeView(v);
                     }
                 });
-                horizontalContainer.addView(newButton, Math.min(1, horizontalContainer.getChildCount()));
-
-                newButton = new Button(LayoutAnimationsByDefault.this);
-                newButton.setText("Click To Remove " + (numButtons++));
-                newButton.setOnClickListener(new View.OnClickListener() {
-                    public void onClick(View v) {
-                        verticalContainer.removeView(v);
-                    }
-                });
-                verticalContainer.addView(newButton, Math.min(1, verticalContainer.getChildCount()));
+                gridContainer.addView(newButton, Math.min(1, gridContainer.getChildCount()));
             }
         });
     }
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java b/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java
index b98a5b5..b0a1f96 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/LabelView.java
@@ -84,7 +84,8 @@
     private final void initLabelView() {
         mTextPaint = new Paint();
         mTextPaint.setAntiAlias(true);
-        mTextPaint.setTextSize(16);
+        // Must manually scale the desired text size to match screen density
+        mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
         mTextPaint.setColor(0xFF000000);
         setPadding(3, 3, 3, 3);
     }
@@ -104,6 +105,7 @@
      * @param size Font size
      */
     public void setTextSize(int size) {
+        // This text size has been pre-scaled by the getDimensionPixelOffset method
         mTextPaint.setTextSize(size);
         requestLayout();
         invalidate();
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java
index 3ee63e5..464b601 100644
--- a/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/view/OverscanActivity.java
@@ -99,9 +99,8 @@
     }
     private class OverscanState implements State {
         public void apply() {
-            display("FULLSCREEN + LOW_PROFILE + HIDE_NAVIGATION");
-            mImage.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
-                                       | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+            display("FULLSCREEN + HIDE_NAVIGATION");
+            mImage.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
         }
         public State next() {
             return new NormalState();
diff --git a/samples/HoneycombGallery/AndroidManifest.xml b/samples/HoneycombGallery/AndroidManifest.xml
index 704a1c6..2a99856 100644
--- a/samples/HoneycombGallery/AndroidManifest.xml
+++ b/samples/HoneycombGallery/AndroidManifest.xml
@@ -16,9 +16,9 @@
  -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.android.hcgallery" android:versionCode="2"
-    android:versionName="1.2">
+    android:versionName="1.5">
 
-    <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="13" />
+    <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="14" />
 
     <!-- Declares that the app supports devices w/o touch, such as a mouse-driven device
          or a device that provides only a d-pad for navigation -->
diff --git a/samples/HoneycombGallery/res/values-port/dimens.xml b/samples/HoneycombGallery/res/values-large-port/dimens.xml
similarity index 100%
rename from samples/HoneycombGallery/res/values-port/dimens.xml
rename to samples/HoneycombGallery/res/values-large-port/dimens.xml
diff --git a/samples/SampleSyncAdapter/AndroidManifest.xml b/samples/SampleSyncAdapter/AndroidManifest.xml
index 25e9e99..285abfb 100644
--- a/samples/SampleSyncAdapter/AndroidManifest.xml
+++ b/samples/SampleSyncAdapter/AndroidManifest.xml
@@ -46,7 +46,7 @@
     <uses-permission
         android:name="android.permission.WRITE_SYNC_SETTINGS" />
 
-    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="11"/>
+    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14"/>
 
     <application
         android:icon="@drawable/icon"
@@ -100,27 +100,8 @@
         </activity>
 
         <activity
-            android:name=".editor.ContactEditorActivity"
-            android:theme="@style/ContactEditTheme"
-            android:windowSoftInputMode="adjustResize">
-            <intent-filter>
-                <action
-                    android:name="android.intent.action.INSERT" />
-                <data
-                    android:mimeType="vnd.android.cursor.item/contact" />
-            </intent-filter>
-
-            <!--
-                Note that the editor gets a raw contact URI, but is expected to call
-                setResult with the corresponding aggregate contact URI, not raw contact
-                URI.
-            -->
-            <intent-filter>
-                <action
-                    android:name="android.intent.action.EDIT" />
-                <data
-                    android:mimeType="vnd.android.cursor.item/raw_contact" />
-            </intent-filter>
+            android:name=".activites.InviteContactActivity"
+            android:theme="@android:style/Theme.Dialog">
             <!--
                 We use the INVITE intent to add a raw contact to an existing contact.
                 It always comes with a lookup URI.
@@ -132,5 +113,50 @@
                     android:mimeType="vnd.android.cursor.item/contact" />
             </intent-filter>
         </activity>
+
+        <activity
+            android:name=".activities.ViewGroupActivity"
+            android:theme="@android:style/Theme.Dialog">
+            <!--
+                We use the VIEW intent to view a group in our app.
+                It always comes with a lookup URI.
+            -->
+            <intent-filter>
+                <action
+                    android:name="android.intent.action.VIEW" />
+                <data
+                    android:mimeType="vnd.android.cursor.item/group" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".activities.ViewStreamItemActivity"
+            android:theme="@android:style/Theme.Dialog">
+            <!--
+                We use the VIEW intent to view a stream item in our app.
+                It always comes with a lookup URI.
+            -->
+            <intent-filter>
+                <action
+                    android:name="android.intent.action.VIEW" />
+                <data
+                    android:mimeType="vnd.android.cursor.item/stream_item" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".activities.ViewStreamItemPhotoActivity"
+            android:theme="@android:style/Theme.Dialog">
+            <!--
+                We use the VIEW intent to view a stream item photo in our app.
+                It always comes with a lookup URI.
+            -->
+            <intent-filter>
+                <action
+                    android:name="android.intent.action.VIEW" />
+                <data
+                    android:mimeType="vnd.android.cursor.item/stream_item_photo" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/samples/SampleSyncAdapter/res/drawable/border.xml b/samples/SampleSyncAdapter/res/drawable/border.xml
deleted file mode 100644
index ab71f2c..0000000
--- a/samples/SampleSyncAdapter/res/drawable/border.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-  <solid android:color="@color/EditPanelBackgroundColor" />
-  <stroke android:width="2dip" android:color="@color/EditPanelBorderColor" />
-  <padding android:left="5dip" android:top="5dip" android:right="5dip" android:bottom="5dip" />
-</shape>
\ No newline at end of file
diff --git a/samples/SampleSyncAdapter/res/drawable/done_menu_icon.png b/samples/SampleSyncAdapter/res/drawable/done_menu_icon.png
deleted file mode 100644
index 3468bbd..0000000
--- a/samples/SampleSyncAdapter/res/drawable/done_menu_icon.png
+++ /dev/null
Binary files differ
diff --git a/samples/SampleSyncAdapter/res/layout-xlarge/editor.xml b/samples/SampleSyncAdapter/res/layout-xlarge/editor.xml
deleted file mode 100644
index 3b7d97b..0000000
--- a/samples/SampleSyncAdapter/res/layout-xlarge/editor.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!--
-/**
- * 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.
- */
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:gravity="center_horizontal">
-    <LinearLayout
-      xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="600dip"
-      android:layout_height="match_parent"
-      android:orientation="vertical"
-      android:background="@drawable/border">
-      <include layout="@layout/editor_header" />
-      <ScrollView
-          android:layout_width="match_parent"
-          android:layout_height="0dip"
-          android:paddingTop="20dip"
-          android:paddingRight="20dip"
-          android:paddingBottom="20dip"
-          android:paddingLeft="20dip"
-          android:layout_weight="1">
-          <include layout="@layout/editor_fields" />
-      </ScrollView>
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml b/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml
deleted file mode 100644
index 648e6f9..0000000
--- a/samples/SampleSyncAdapter/res/layout-xlarge/editor_header.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-
-<!-- Account info header -->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="64dip"
-    android:layout_width="match_parent"
-    android:background="?android:attr/selectableItemBackground">
-
-    <ImageView
-        android:id="@+id/header_account_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="7dip"
-        android:layout_marginRight="7dip"
-        android:layout_centerVertical="true"
-        android:layout_alignParentRight="true"
-        android:src="@drawable/icon" />
-
-    <TextView
-        android:id="@+id/header_account_type"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_toLeftOf="@+id/header_account_icon"
-        android:layout_alignTop="@id/header_account_icon"
-        android:layout_marginTop="-4dip"
-        android:textSize="24sp"
-        android:textColor="?android:attr/textColorPrimary"
-        android:singleLine="true"
-        android:text="@string/header_account_type" />
-
-    <TextView
-        android:id="@+id/header_account_name"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_toLeftOf="@+id/header_account_icon"
-        android:layout_alignBottom="@+id/header_account_icon"
-        android:layout_marginBottom="2dip"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorPrimary"
-        android:singleLine="true" />
-
-</RelativeLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/editor_fields.xml b/samples/SampleSyncAdapter/res/layout/editor_fields.xml
deleted file mode 100644
index 31d0128..0000000
--- a/samples/SampleSyncAdapter/res/layout/editor_fields.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!--
-/**
- * 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.
- */
--->
-
-<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent"
-  android:stretchColumns="2">
-
-  <TableRow>
-    <TextView
-      android:layout_column="1"
-      android:textStyle="bold"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:text="@string/label_name"
-      android:padding="3dip" />
-    <EditText
-      android:layout_column="2"
-      android:id="@+id/editor_name"
-      android:singleLine="true"
-      android:inputType="textPersonName"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:minWidth="250dip"
-      android:scrollHorizontally="true"
-      android:capitalize="none"
-      android:textSize="@dimen/contact_name_text_size"
-      android:gravity="fill_horizontal"
-      android:autoText="false" />
-  </TableRow>
-  <TableRow>
-    <TextView
-      android:layout_column="1"
-      android:textStyle="bold"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:text="@string/label_phone_home"
-      android:padding="3dip" />
-    <EditText
-      android:id="@+id/editor_phone_home"
-      android:singleLine="true"
-      android:inputType="phone"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:minWidth="250dip"
-      android:scrollHorizontally="true"
-      android:capitalize="none"
-      android:gravity="fill_horizontal"
-      android:autoText="false" />
-  </TableRow>
-  <TableRow>
-    <TextView
-      android:layout_column="1"
-      android:textStyle="bold"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:text="@string/label_phone_mobile"
-      android:padding="3dip" />
-    <EditText
-      android:id="@+id/editor_phone_mobile"
-      android:singleLine="true"
-      android:inputType="phone"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:minWidth="250dip"
-      android:scrollHorizontally="true"
-      android:capitalize="none"
-      android:gravity="fill_horizontal"
-      android:autoText="false" />
-  </TableRow>
-  <TableRow>
-    <TextView
-      android:layout_column="1"
-      android:textStyle="bold"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:text="@string/label_phone_work"
-      android:padding="3dip" />
-    <EditText
-      android:id="@+id/editor_phone_work"
-      android:singleLine="true"
-      android:phoneNumber="true"
-      android:autoText="true"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:minWidth="250dip"
-      android:scrollHorizontally="true"
-      android:capitalize="none"
-      android:gravity="fill_horizontal" />
-  </TableRow>
-  <TableRow>
-    <TextView
-      android:layout_column="1"
-      android:textStyle="bold"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:text="@string/label_email"
-      android:padding="3dip" />
-    <EditText
-      android:id="@+id/editor_email"
-      android:singleLine="true"
-      android:inputType="textEmailAddress"
-      android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:minWidth="250dip"
-      android:scrollHorizontally="true"
-      android:capitalize="none"
-      android:gravity="fill_horizontal"
-      android:autoText="false" />
-  </TableRow>
-</TableLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/editor.xml b/samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml
similarity index 61%
rename from samples/SampleSyncAdapter/res/layout/editor.xml
rename to samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml
index a0c36d2..1e09d5b 100644
--- a/samples/SampleSyncAdapter/res/layout/editor.xml
+++ b/samples/SampleSyncAdapter/res/layout/invite_contact_activity.xml
@@ -18,19 +18,15 @@
 -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <ScrollView
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:paddingTop="20dip"
-        android:paddingRight="20dip"
-        android:paddingBottom="20dip"
-        android:paddingLeft="20dip"
-        android:layout_weight="1">
-        <include layout="@layout/editor_fields" />
-    </ScrollView>
-
-</LinearLayout>
\ No newline at end of file
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <TextView
+        android:text="@string/invite_contact_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+    <TextView
+        android:id="@+id/invite_contact_uri"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/login_activity.xml b/samples/SampleSyncAdapter/res/layout/login_activity.xml
index 7408ffe..6c93f90 100644
--- a/samples/SampleSyncAdapter/res/layout/login_activity.xml
+++ b/samples/SampleSyncAdapter/res/layout/login_activity.xml
@@ -55,7 +55,8 @@
                 android:scrollHorizontally="true"
                 android:capitalize="none"
                 android:autoText="false"
-                android:inputType="textEmailAddress" />
+                android:inputType="textEmailAddress"
+                android:text="user" />
             <TextView
                 android:id="@+id/username_fixed"
                 android:textAppearance="?android:attr/textAppearanceSmall"
@@ -81,7 +82,8 @@
                 android:capitalize="none"
                 android:autoText="false"
                 android:password="true"
-                android:inputType="textPassword" />
+                android:inputType="textPassword"
+                android:text="test" />
             <TextView
                 android:id="@+id/message_bottom"
                 android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/samples/SampleSyncAdapter/res/layout/view_group_activity.xml b/samples/SampleSyncAdapter/res/layout/view_group_activity.xml
new file mode 100644
index 0000000..45212d9
--- /dev/null
+++ b/samples/SampleSyncAdapter/res/layout/view_group_activity.xml
@@ -0,0 +1,32 @@
+<?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.
+ */
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <TextView
+        android:text="@string/view_group_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+    <TextView
+        android:id="@+id/view_group_uri"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml b/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml
new file mode 100644
index 0000000..a04d07f
--- /dev/null
+++ b/samples/SampleSyncAdapter/res/layout/view_stream_item_activity.xml
@@ -0,0 +1,32 @@
+<?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.
+ */
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <TextView
+        android:text="@string/view_stream_item_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+    <TextView
+        android:id="@+id/view_stream_item_uri"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml b/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml
new file mode 100644
index 0000000..ddc09d0
--- /dev/null
+++ b/samples/SampleSyncAdapter/res/layout/view_stream_item_photo_activity.xml
@@ -0,0 +1,32 @@
+<?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.
+ */
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <TextView
+        android:text="@string/view_stream_item_photo_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+    <TextView
+        android:id="@+id/view_stream_item_photo_uri"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SampleSyncAdapter/res/menu/edit.xml b/samples/SampleSyncAdapter/res/menu/edit.xml
deleted file mode 100644
index 1227584..0000000
--- a/samples/SampleSyncAdapter/res/menu/edit.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/menu_done"
-        android:alphabeticShortcut="\n"
-        android:icon="@drawable/done_menu_icon"
-        android:title="@string/menu_done"
-        android:showAsAction="always|withText" />
-
-    <item
-        android:id="@+id/menu_cancel"
-        android:alphabeticShortcut="q"
-        android:title="@string/menu_cancel"
-        android:showAsAction="always|withText" />
-</menu>
diff --git a/samples/SampleSyncAdapter/res/values/strings.xml b/samples/SampleSyncAdapter/res/values/strings.xml
index 7ac95f5..22fe14e 100644
--- a/samples/SampleSyncAdapter/res/values/strings.xml
+++ b/samples/SampleSyncAdapter/res/values/strings.xml
@@ -109,4 +109,34 @@
     <!-- The label of the button to add contact to this contact provider  -->
     <string name="invite_action_label">Add to Sample SyncAdaper</string>
 
+    <!-- The description for the invite contact flow -->
+    <string name="invite_contact_description">Congratulations! The user wants to add the contact
+        to the amazing Sample SyncAdapter social network. If this was a real app, it should now
+        make best efforts to add the contact to this network. This would probably involve
+        looking up the person on the network, inviting if he is not there already and syncing
+        the new contact down.
+
+        Ideally, when the user gets back to the People app, the new contact should already
+        be there, enriching the original contact.
+
+        This is the information we got to lookup the contact:</string>
+
+    <!-- The label of the button to view a group -->
+    <string name="view_group_action_label">Show sample group details</string>
+
+    <!-- The description for the view group button -->
+    <string name="view_group_description">This would now show the details of the group.
+
+        This is the group uri:</string>
+
+    <!-- The description for the view stream item -->
+    <string name="view_stream_item_description">This would now show the details of the stream item.
+
+        This is the uri of the stream item:</string>
+
+    <!-- The description for the view stream item photo -->
+    <string name="view_stream_item_photo_description">This would now show the details of the stream item photo.
+
+        This is the uri of the photo:</string>
+
 </resources>
\ No newline at end of file
diff --git a/samples/SampleSyncAdapter/res/values/styles.xml b/samples/SampleSyncAdapter/res/values/styles.xml
deleted file mode 100644
index 074613e..0000000
--- a/samples/SampleSyncAdapter/res/values/styles.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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.
--->
-
-<resources>
-    <!--
-        These styles will only be used in Honeycomb and later because
-        Android doesn't support third-party contact editing in pre-
-        Honeycomb versions.
-    -->
-    <color name="EditPanelBackgroundColor">#ffffff</color>
-    <color name="EditPanelBorderColor">#cccccc</color>
-    <style name="ContactEditTheme" parent="android:Theme.Holo.Light">
-    </style>
-</resources>
diff --git a/samples/SampleSyncAdapter/res/xml-v11/contacts.xml b/samples/SampleSyncAdapter/res/xml-v11/contacts.xml
index 62dfa80..48cf503 100644
--- a/samples/SampleSyncAdapter/res/xml-v11/contacts.xml
+++ b/samples/SampleSyncAdapter/res/xml-v11/contacts.xml
@@ -19,8 +19,6 @@
 
 <ContactsAccountType
     xmlns:android="http://schemas.android.com/apk/res/android"
-    editContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
-    createContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
 >
 
     <ContactsDataKind
diff --git a/samples/SampleSyncAdapter/res/xml-v14/contacts.xml b/samples/SampleSyncAdapter/res/xml-v14/contacts.xml
index b900c72..48e079a 100644
--- a/samples/SampleSyncAdapter/res/xml-v14/contacts.xml
+++ b/samples/SampleSyncAdapter/res/xml-v14/contacts.xml
@@ -17,18 +17,15 @@
  */
 -->
 
-<!-- This sample doesn't currently support groups or stream items. viewGroupActivity and
-     viewStreamItemActivity and viewStreamItemPhotoActivity or just here for reference -->
 <ContactsAccountType
     xmlns:android="http://schemas.android.com/apk/res/android"
-    editContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
-    createContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
-    inviteContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
+    inviteContactActivity="com.example.android.samplesync.activities.InviteContactActivity"
     inviteContactActionLabel="@string/invite_action_label"
     viewContactNotifyService="com.example.android.samplesync.notifier.NotifierService"
-    viewGroupActivity="com.example.android.samplesync.viewer.ViewGroupActivity"
-    viewStreamItemActivity="com.example.android.samplesync.viewer.ViewStreamItemActivity"
-    viewStreamItemPhotoActivity="com.example.android.samplesync.viewer.ViewStreamItemPhotoActivity"
+    viewGroupActivity="com.example.android.samplesync.activities.ViewGroupActivity"
+    viewGroupActionLabel="@string/view_group_action_label"
+    viewStreamItemActivity="com.example.android.samplesync.activities.ViewStreamItemActivity"
+    viewStreamItemPhotoActivity="com.example.android.samplesync.activities.ViewStreamItemPhotoActivity"
 >
 
     <ContactsDataKind
diff --git a/samples/SampleSyncAdapter/res/xml/contacts.xml b/samples/SampleSyncAdapter/res/xml/contacts.xml
index b46257d..06ecf6a 100644
--- a/samples/SampleSyncAdapter/res/xml/contacts.xml
+++ b/samples/SampleSyncAdapter/res/xml/contacts.xml
@@ -19,8 +19,6 @@
 
 <ContactsSource
     xmlns:android="http://schemas.android.com/apk/res/android"
-    editContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
-    createContactActivity="com.example.android.samplesync.editor.ContactEditorActivity"
 >
 
     <ContactsDataKind
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/InviteContactActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/InviteContactActivity.java
new file mode 100644
index 0000000..1923fc2
--- /dev/null
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/InviteContactActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.samplesync.activities;
+
+import com.example.android.samplesync.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Activity to handle the invite-intent. In a real app, this would look up the user on the network
+ * and either connect ("add as friend", "follow") or invite them to the network
+ */
+public class InviteContactActivity extends Activity {
+    private static final String TAG = "InviteContactActivity";
+
+    private TextView mUriTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.invite_contact_activity);
+
+        mUriTextView = (TextView) findViewById(R.id.invite_contact_uri);
+        mUriTextView.setText(getIntent().getDataString());
+    }
+}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewGroupActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewGroupActivity.java
new file mode 100644
index 0000000..1b32784
--- /dev/null
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewGroupActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.samplesync.activities;
+
+import com.example.android.samplesync.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Activity to handle the view-group action. In a real app, this would show a rich view of the
+ * group, like members, updates etc.
+ */
+public class ViewGroupActivity extends Activity {
+    private static final String TAG = "ViewGroupActivity";
+
+    private TextView mUriTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.view_group_activity);
+
+        mUriTextView = (TextView) findViewById(R.id.view_group_uri);
+        mUriTextView.setText(getIntent().getDataString());
+    }
+}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemActivity.java
new file mode 100644
index 0000000..6d54f31
--- /dev/null
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.samplesync.activities;
+
+import com.example.android.samplesync.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Activity to handle view a stream-item. In a real app, this would show a rich view of the
+ * item.
+ */
+public class ViewStreamItemActivity extends Activity {
+    private static final String TAG = "ViewStreamItemActivity";
+
+    private TextView mUriTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.view_stream_item_activity);
+
+        mUriTextView = (TextView) findViewById(R.id.view_stream_item_uri);
+        mUriTextView.setText(getIntent().getDataString());
+    }
+}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemPhotoActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemPhotoActivity.java
new file mode 100644
index 0000000..962bc70
--- /dev/null
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/activities/ViewStreamItemPhotoActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.samplesync.activities;
+
+import com.example.android.samplesync.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+/**
+ * Activity to view a stream-item-photo. In a real app, this would show a fullscreen view of the
+ * photo, potentially with ways to interact with it
+ */
+public class ViewStreamItemPhotoActivity extends Activity {
+    private static final String TAG = "ViewStreamItemPhotoActivity";
+
+    private TextView mUriTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.view_stream_item_photo_activity);
+
+        mUriTextView = (TextView) findViewById(R.id.view_stream_item_photo_uri);
+        mUriTextView.setText(getIntent().getDataString());
+    }
+}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/AuthenticatorActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/AuthenticatorActivity.java
index 2a3c0fc..5649f6d 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/AuthenticatorActivity.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/authenticator/AuthenticatorActivity.java
@@ -110,7 +110,7 @@
         mMessage = (TextView) findViewById(R.id.message);
         mUsernameEdit = (EditText) findViewById(R.id.username_edit);
         mPasswordEdit = (EditText) findViewById(R.id.password_edit);
-        mUsernameEdit.setText(mUsername);
+        if (!TextUtils.isEmpty(mUsername)) mUsernameEdit.setText(mUsername);
         mMessage.setText(getMessage());
     }
 
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/client/NetworkUtilities.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/client/NetworkUtilities.java
index bebcd72..c2ebd19 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/client/NetworkUtilities.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/client/NetworkUtilities.java
@@ -16,16 +16,6 @@
 
 package com.example.android.samplesync.client;
 
-import android.accounts.Account;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Handler;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.example.android.samplesync.authenticator.AuthenticatorActivity;
-
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
@@ -41,9 +31,15 @@
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 import org.apache.http.util.EntityUtils;
-import org.json.JSONObject;
 import org.json.JSONArray;
 import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.accounts.Account;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.text.TextUtils;
+import android.util.Log;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
@@ -54,11 +50,8 @@
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
-import java.util.TimeZone;
 
 /**
  * Provides utility methods for communicating with the server.
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/editor/ContactEditorActivity.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/editor/ContactEditorActivity.java
deleted file mode 100644
index efda0cc..0000000
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/editor/ContactEditorActivity.java
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.samplesync.editor;
-
-import com.example.android.samplesync.Constants;
-import com.example.android.samplesync.R;
-import com.example.android.samplesync.client.RawContact;
-import com.example.android.samplesync.platform.BatchOperation;
-import com.example.android.samplesync.platform.ContactManager;
-import com.example.android.samplesync.platform.ContactManager.ContactQuery;
-import com.example.android.samplesync.platform.ContactManager.EditorQuery;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.widget.EditText;
-import android.widget.TextView;
-
-/**
- * Implements a sample editor for a contact that belongs to a remote contact service.
- * The editor can be invoked for an existing SampleSyncAdapter contact, or it can
- * be used to create a brand new SampleSyncAdapter contact. We look at the Intent
- * object to figure out whether this is a "new" or "edit" operation.
- */
-public class ContactEditorActivity extends Activity {
-    private static final String TAG = "SampleSyncAdapter";
-
-    // Keep track of whether we're inserting a new contact or editing an
-    // existing contact.
-    private boolean mIsInsert;
-
-    // The name of the external account we're syncing this contact to.
-    private String mAccountName;
-
-    // For existing contacts, this is the URI to the contact data.
-    private Uri mRawContactUri;
-
-    // The raw clientId for this contact
-    private long mRawContactId;
-
-    // Make sure we only attempt to save the contact once if the
-    // user presses the "done" button multiple times...
-    private boolean mSaveInProgress = false;
-
-    // Keep track of the controls used to edit contact values, so we can get/set
-    // those values easily.
-    private EditText mNameEditText;
-    private EditText mHomePhoneEditText;
-    private EditText mMobilePhoneEditText;
-    private EditText mWorkPhoneEditText;
-    private EditText mEmailEditText;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.editor);
-
-        mNameEditText = (EditText)findViewById(R.id.editor_name);
-        mHomePhoneEditText = (EditText)findViewById(R.id.editor_phone_home);
-        mMobilePhoneEditText = (EditText)findViewById(R.id.editor_phone_mobile);
-        mWorkPhoneEditText = (EditText)findViewById(R.id.editor_phone_work);
-        mEmailEditText = (EditText)findViewById(R.id.editor_email);
-
-        // Figure out whether we're creating a new contact (ACTION_INSERT), editing
-        // an existing contact, or adding a new one to existing contact (INVITE_CONTACT).
-        Intent intent = getIntent();
-        String action = intent.getAction();
-        if (Intent.ACTION_INSERT.equals(action)) {
-            // We're inserting a new contact, so save off the external account name
-            // which should have been added to the intent we were passed.
-            mIsInsert = true;
-            String accountName = intent.getStringExtra(RawContacts.ACCOUNT_NAME);
-            if (accountName == null) {
-                Log.e(TAG, "Account name is required");
-                finish();
-            }
-            setAccountName(accountName);
-        } else if (ContactsContract.Intents.INVITE_CONTACT.equals(action)) {
-            // Adding to an existing contact.
-            mIsInsert = true;
-            // Use the first account found.
-            Account[] myAccounts = AccountManager.get(this).getAccountsByType(
-                    Constants.ACCOUNT_TYPE);
-            if (myAccounts.length == 0) {
-                Log.e(TAG, "Account not configured");
-                finish();
-            }
-            setAccountName(myAccounts[0].name);
-
-            Uri lookupUri = intent.getData();
-            if (lookupUri == null) {
-                Log.e(TAG, "Contact lookup URI is required");
-                finish();
-            }
-            startLoadContactEntity(lookupUri);
-        } else {
-            // We're editing an existing contact. Load in the data from the contact
-            // so that the user can edit it.
-            mIsInsert = false;
-            mRawContactUri = intent.getData();
-            if (mRawContactUri == null) {
-                Log.e(TAG, "Raw contact URI is required");
-                finish();
-            }
-            startLoadRawContactEntity();
-        }
-    }
-
-    @Override
-    public void onBackPressed() {
-        // This method will have been called if the user presses the "Back" button
-        // in the ActionBar.  We treat that the same way as the "Done" button in
-        // the ActionBar.
-        save();
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        // This method gets called so that we can place items in the main Options menu -
-        // for example, the ActionBar items.  We add our menus from the res/menu/edit.xml
-        // file.
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.edit, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home:
-            case R.id.menu_done:
-                // The user pressed the "Home" button or our "Done" button - both
-                // in the ActionBar.  In both cases, we want to save the contact
-                // and exit.
-                save();
-                return true;
-            case R.id.menu_cancel:
-                // The user pressed the Cancel menu item in the ActionBar.
-                // Close the editor without saving any changes.
-                finish();
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * Create an AsyncTask to load the contact from the Contacts data provider
-     */
-    private void startLoadRawContactEntity() {
-        Uri uri = Uri.withAppendedPath(mRawContactUri, RawContacts.Entity.CONTENT_DIRECTORY);
-        new LoadRawContactTask().execute(uri);
-    }
-
-    /**
-     * Called by the LoadRawContactTask when the contact information has been
-     * successfully loaded from the Contacts data provider.
-     */
-    public void onRawContactEntityLoaded(Cursor cursor) {
-        if (cursor.moveToFirst()) {
-            String mimetype = cursor.getString(EditorQuery.COLUMN_MIMETYPE);
-            if (StructuredName.CONTENT_ITEM_TYPE.equals(mimetype)) {
-                setAccountName(cursor.getString(EditorQuery.COLUMN_ACCOUNT_NAME));
-                mRawContactId = cursor.getLong(EditorQuery.COLUMN_RAW_CONTACT_ID);
-                mNameEditText.setText(cursor.getString(EditorQuery.COLUMN_FULL_NAME));
-            } else if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
-                final int type = cursor.getInt(EditorQuery.COLUMN_PHONE_TYPE);
-                if (type == Phone.TYPE_HOME) {
-                    mHomePhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
-                } else if (type == Phone.TYPE_MOBILE) {
-                    mMobilePhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
-                } else if (type == Phone.TYPE_WORK) {
-                    mWorkPhoneEditText.setText(cursor.getString(EditorQuery.COLUMN_PHONE_NUMBER));
-                }
-            } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
-                mEmailEditText.setText(cursor.getString(EditorQuery.COLUMN_DATA1));
-            }
-        }
-    }
-
-    /**
-     * Create an AsyncTask to load the contact from the Contacts data provider
-     */
-    private void startLoadContactEntity(Uri lookupUri) {
-        new LoadContactTask().execute(lookupUri);
-    }
-
-    /**
-     * Called by the LoadContactTask when the contact information has been
-     * successfully loaded from the Contacts data provider.
-     */
-    public void onContactEntityLoaded(Cursor cursor) {
-        if (cursor.moveToFirst()) {
-            mNameEditText.setText(cursor.getString(ContactQuery.COLUMN_DISPLAY_NAME));
-        }
-    }
-
-    /**
-     * Save the updated contact data. We actually take two different actions
-     * depending on whether we are creating a new contact or editing an
-     * existing contact.
-     */
-    public void save() {
-        // If we're already saving this contact, don't kick-off yet
-        // another save - the user probably just pressed the "Done"
-        // button multiple times...
-        if (mSaveInProgress) {
-            return;
-        }
-
-        mSaveInProgress = true;
-        if (mIsInsert) {
-            saveNewContact();
-        } else {
-            saveChanges();
-        }
-    }
-
-    /**
-     * Save off the external contacts provider account name. We show the account name
-     * in the header section of the edit panel, and we also need it later when we
-     * save off a brand new contact.
-     */
-    private void setAccountName(String accountName) {
-        mAccountName = accountName;
-        Log.i(TAG, "account=" + mAccountName);
-        if (accountName != null) {
-            TextView accountNameLabel = (TextView)findViewById(R.id.header_account_name);
-            if (accountNameLabel != null) {
-                accountNameLabel.setText(accountName);
-            }
-        }
-    }
-
-    /**
-     * Save a new contact using the Contacts content provider. The actual insertion
-     * is performed in an AsyncTask.
-     */
-    @SuppressWarnings("unchecked")
-    private void saveNewContact() {
-        new InsertContactTask().execute(buildRawContact());
-    }
-
-    /**
-     * Save changes to an existing contact.  The actual update is performed in
-     * an AsyncTask.
-     */
-    @SuppressWarnings("unchecked")
-    private void saveChanges() {
-        new UpdateContactTask().execute(buildRawContact());
-    }
-
-    /**
-     * Build a RawContact object from the data in the user-editable form
-     * @return a new RawContact object representing the edited user
-     */
-    private RawContact buildRawContact() {
-        return RawContact.create(mNameEditText.getText().toString(),
-                null,
-                null,
-                mMobilePhoneEditText.getText().toString(),
-                mWorkPhoneEditText.getText().toString(),
-                mHomePhoneEditText.getText().toString(),
-                mEmailEditText.getText().toString(),
-                null,
-                false,
-                mRawContactId,
-                -1);
-    }
-
-    /**
-     * Called after a contact is saved - both for edited contacts and new contacts.
-     * We set the final result of the activity to be "ok", and then close the activity
-     * by calling finish().
-     */
-    public void onContactSaved(Uri result) {
-        if (result != null) {
-            Intent intent = new Intent();
-            intent.setData(result);
-            setResult(RESULT_OK, intent);
-            finish();
-        }
-        mSaveInProgress = false;
-    }
-
-    /**
-     * Represents an asynchronous task used to load a contact from
-     * the Contacts content provider.
-     *
-     */
-    public class LoadRawContactTask extends AsyncTask<Uri, Void, Cursor> {
-
-        @Override
-        protected Cursor doInBackground(Uri... params) {
-            // Our background task is to load the contact from the Contacts provider
-            return getContentResolver().query(params[0], EditorQuery.PROJECTION, null, null, null);
-        }
-
-        @Override
-        protected void onPostExecute(Cursor cursor) {
-            if (cursor == null) return;
-            // After we've successfully loaded the contact, call back into
-            // the ContactEditorActivity so we can update the UI
-            try {
-                onRawContactEntityLoaded(cursor);
-            } finally {
-                cursor.close();
-            }
-        }
-    }
-
-    /**
-     * Represents an asynchronous task used to save a new contact
-     * into the contacts database.
-     */
-    public class InsertContactTask extends AsyncTask<RawContact, Void, Uri> {
-
-        @Override
-        protected Uri doInBackground(RawContact... params) {
-            try {
-                final RawContact rawContact = params[0];
-                final Context context = getApplicationContext();
-                final ContentResolver resolver = getContentResolver();
-                final BatchOperation batchOperation = new BatchOperation(context, resolver);
-                ContactManager.addContact(context, mAccountName, rawContact, false, batchOperation);
-                Uri rawContactUri = batchOperation.execute();
-
-                // Convert the raw contact URI to a contact URI
-                if (rawContactUri != null) {
-                    return RawContacts.getContactLookupUri(resolver, rawContactUri);
-                } else {
-                    Log.e(TAG, "Could not save new contact");
-                    return null;
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "An error occurred while saving new contact", e);
-            }
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Uri result) {
-            // Tell the UI that the contact has been successfully saved
-            onContactSaved(result);
-        }
-    }
-
-
-    /**
-     * Represents an asynchronous task used to save an updated contact
-     * into the contacts database.
-     */
-    public class UpdateContactTask extends AsyncTask<RawContact, Void, Uri> {
-
-        @Override
-        protected Uri doInBackground(RawContact... params) {
-            try {
-                final RawContact rawContact = params[0];
-                final Context context = getApplicationContext();
-                final ContentResolver resolver = getContentResolver();
-                final BatchOperation batchOperation = new BatchOperation(context, resolver);
-                ContactManager.updateContact(context, resolver, rawContact, false, false, false,
-                        false, rawContact.getRawContactId(), batchOperation);
-                batchOperation.execute();
-
-                // Convert the raw contact URI to a contact URI
-                return RawContacts.getContactLookupUri(resolver, mRawContactUri);
-            } catch (Exception e) {
-                Log.e(TAG, "Could not save changes", e);
-            }
-            return null;
-        }
-
-        @Override
-        protected void onPostExecute(Uri result) {
-            // Tell the UI that the contact has been successfully saved
-            onContactSaved(result);
-        }
-    }
-
-    /**
-     * Loads contact information by a lookup URI.
-     */
-    public class LoadContactTask extends AsyncTask<Uri, Void, Cursor> {
-
-        @Override
-        protected Cursor doInBackground(Uri... params) {
-            return getContentResolver().query(params[0], ContactQuery.PROJECTION, null, null, null);
-        }
-
-        @Override
-        protected void onPostExecute(Cursor cursor) {
-            if (cursor == null) return;
-            try {
-                onContactEntityLoaded(cursor);
-            } finally {
-                cursor.close();
-            }
-        }
-    }
-}
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java
index 035c976..6b2dfb1 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.java
@@ -34,6 +34,7 @@
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.Settings;
 import android.provider.ContactsContract.StatusUpdates;
@@ -54,6 +55,41 @@
 
     private static final String TAG = "ContactManager";
 
+    public static final String SAMPLE_GROUP_NAME = "Sample Group";
+
+    public static long ensureSampleGroupExists(Context context, Account account) {
+        final ContentResolver resolver = context.getContentResolver();
+
+        // Lookup the sample group
+        long groupId = 0;
+        final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
+                Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=? AND " +
+                Groups.TITLE + "=?",
+                new String[] { account.name, account.type, SAMPLE_GROUP_NAME }, null);
+        if (cursor != null) {
+            try {
+                if (cursor.moveToFirst()) {
+                    groupId = cursor.getLong(0);
+                }
+            } finally {
+                cursor.close();
+            }
+        }
+
+        if (groupId == 0) {
+            // Sample group doesn't exist yet, so create it
+            final ContentValues contentValues = new ContentValues();
+            contentValues.put(Groups.ACCOUNT_NAME, account.name);
+            contentValues.put(Groups.ACCOUNT_TYPE, account.type);
+            contentValues.put(Groups.TITLE, SAMPLE_GROUP_NAME);
+            contentValues.put(Groups.GROUP_IS_READ_ONLY, true);
+
+            final Uri newGroupUri = resolver.insert(Groups.CONTENT_URI, contentValues);
+            groupId = ContentUris.parseId(newGroupUri);
+        }
+        return groupId;
+    }
+
     /**
      * Take a list of updated contacts and apply those changes to the
      * contacts database. Typically this list of contacts would have been
@@ -67,7 +103,7 @@
      * sync request.
      */
     public static synchronized long updateContacts(Context context, String account,
-            List<RawContact> rawContacts, long lastSyncMarker) {
+            List<RawContact> rawContacts, long groupId, long lastSyncMarker) {
 
         long currentSyncMarker = lastSyncMarker;
         final ContentResolver resolver = context.getContentResolver();
@@ -112,7 +148,7 @@
                 Log.d(TAG, "In addContact");
                 if (!rawContact.isDeleted()) {
                     newUsers.add(rawContact);
-                    addContact(context, account, rawContact, true, batchOperation);
+                    addContact(context, account, rawContact, groupId, true, batchOperation);
                 }
             }
             // A sync adapter should batch operations on multiple contacts,
@@ -235,12 +271,13 @@
      * @param context the Authenticator Activity context
      * @param accountName the account the contact belongs to
      * @param rawContact the sample SyncAdapter User object
+     * @param groupId the id of the sample group
      * @param inSync is the add part of a client-server sync?
      * @param batchOperation allow us to batch together multiple operations
      *        into a single provider call
      */
     public static void addContact(Context context, String accountName, RawContact rawContact,
-        boolean inSync, BatchOperation batchOperation) {
+            long groupId, boolean inSync, BatchOperation batchOperation) {
 
         // Put the data in the contacts provider
         final ContactOperations contactOp = ContactOperations.createNewContact(
@@ -252,6 +289,7 @@
                 .addPhone(rawContact.getCellPhone(), Phone.TYPE_MOBILE)
                 .addPhone(rawContact.getHomePhone(), Phone.TYPE_HOME)
                 .addPhone(rawContact.getOfficePhone(), Phone.TYPE_WORK)
+                .addGroupMembership(groupId)
                 .addAvatar(rawContact.getAvatarUrl());
 
         // If we have a serverId, then go ahead and create our status profile.
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java
index cb8e97b..1445e55 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactOperations.java
@@ -25,6 +25,7 @@
 import android.net.Uri;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
@@ -185,6 +186,20 @@
         return this;
     }
 
+    /**
+     * Adds a group membership
+     *
+     * @param id The id of the group to assign
+     * @return instance of ContactOperations
+     */
+    public ContactOperations addGroupMembership(long groupId) {
+        mValues.clear();
+        mValues.put(GroupMembership.GROUP_ROW_ID, groupId);
+        mValues.put(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
+        addInsertOp();
+        return this;
+    }
+
     public ContactOperations addAvatar(String avatarUrl) {
         if (avatarUrl != null) {
             byte[] avatarBuffer = NetworkUtilities.downloadAvatar(avatarUrl);
diff --git a/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java b/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java
index 0ca8dee..0f570cd 100644
--- a/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java
+++ b/samples/SampleSyncAdapter/src/com/example/android/samplesync/syncadapter/SyncAdapter.java
@@ -15,6 +15,15 @@
  */
 package com.example.android.samplesync.syncadapter;
 
+import com.example.android.samplesync.Constants;
+import com.example.android.samplesync.client.NetworkUtilities;
+import com.example.android.samplesync.client.RawContact;
+import com.example.android.samplesync.platform.ContactManager;
+
+import org.apache.http.ParseException;
+import org.apache.http.auth.AuthenticationException;
+import org.json.JSONException;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AuthenticatorException;
@@ -27,18 +36,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.example.android.samplesync.Constants;
-import com.example.android.samplesync.client.NetworkUtilities;
-import com.example.android.samplesync.client.RawContact;
-import com.example.android.samplesync.platform.ContactManager;
-
-import org.apache.http.ParseException;
-import org.apache.http.auth.AuthenticationException;
-import org.json.JSONException;
-
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 
 /**
@@ -91,6 +89,9 @@
             final String authtoken = mAccountManager.blockingGetAuthToken(account,
                     Constants.AUTHTOKEN_TYPE, NOTIFY_AUTH_FAILURE);
 
+            // Make sure that the sample group exists
+            final long groupId = ContactManager.ensureSampleGroupExists(mContext, account);
+
             // Find the local 'dirty' contacts that we need to tell the server about...
             // Find the local users that need to be sync'd to the server...
             dirtyContacts = ContactManager.getDirtyContacts(mContext, account);
@@ -106,6 +107,7 @@
             long newSyncState = ContactManager.updateContacts(mContext,
                     account.name,
                     updatedContacts,
+                    groupId,
                     lastSyncMarker);
 
             // This is a demo of how you can update IM-style status messages
diff --git a/tools/emulator/opengl/Android.mk b/tools/emulator/opengl/Android.mk
index 8147395..2c1cb64 100644
--- a/tools/emulator/opengl/Android.mk
+++ b/tools/emulator/opengl/Android.mk
@@ -9,11 +9,6 @@
 #
 ifeq (true,$(BUILD_EMULATOR_OPENGL))
 
-# By default, NEVER build the gralloc.goldfish support library because
-# the code has not been ported to Honeycomb / IceCreamSandwich yet and
-# will fail to build properly.
-BUILD_EMULATOR_OPENGL_DRIVER ?= true
-
 # Top-level for all modules
 EMUGL_PATH := $(call my-dir)
 
@@ -71,13 +66,8 @@
 include $(EMUGL_PATH)/system/GLESv1/Android.mk
 include $(EMUGL_PATH)/system/GLESv2/Android.mk
 
-ifeq (false,$(BUILD_EMULATOR_OPENGL_DRIVER))
-  include $(EMUGL_PATH)/tests/ut_rendercontrol_enc/Android.mk
-  include $(EMUGL_PATH)/tests/gles_android_wrapper/Android.mk
-else
-  include $(EMUGL_PATH)/system/gralloc/Android.mk
-  include $(EMUGL_PATH)/system/egl/Android.mk
-endif
+include $(EMUGL_PATH)/system/gralloc/Android.mk
+include $(EMUGL_PATH)/system/egl/Android.mk
 
 # Host static libraries
 include $(EMUGL_PATH)/host/libs/GLESv1_dec/Android.mk
@@ -95,12 +85,7 @@
 # Host executables
 include $(EMUGL_PATH)/host/renderer/Android.mk
 
-# Host unit-test for the renderer. this one uses its own small
-# EGL host wrapper.
-include $(EMUGL_PATH)/tests/event_injector/Android.mk
-include $(EMUGL_PATH)/tests/EGL_host_wrapper/Android.mk
-include $(EMUGL_PATH)/tests/emulator_test_renderer/Android.mk
-include $(EMUGL_PATH)/tests/ut_renderer/Android.mk
+# Host unit-test for the renderer.
 
 include $(EMUGL_PATH)/tests/translator_tests/MacCommon/Android.mk
 include $(EMUGL_PATH)/tests/translator_tests/GLES_CM/Android.mk
diff --git a/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h b/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
index 41d8023..445ec17 100644
--- a/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
+++ b/tools/emulator/opengl/host/include/libOpenglRender/IOStream.h
@@ -34,6 +34,7 @@
     virtual int commitBuffer(size_t size) = 0;
     virtual const unsigned char *readFully( void *buf, size_t len) = 0;
     virtual const unsigned char *read( void *buf, size_t *inout_len) = 0;
+    virtual int writeFully(const void* buf, size_t len) = 0;
 
     virtual ~IOStream() {
 
@@ -82,6 +83,7 @@
         return readFully(buf, len);
     }
 
+
 private:
     unsigned char *m_buf;
     size_t m_bufsize;
diff --git a/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp b/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
index 9e95604..4265301 100644
--- a/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
+++ b/tools/emulator/opengl/host/tools/emugen/ApiGen.cpp
@@ -21,6 +21,15 @@
 #include <errno.h>
 #include <sys/types.h>
 
+/* Define this to 1 to enable support for the 'isLarge' variable flag
+ * that instructs the encoder to send large data buffers by a direct
+ * write through the pipe (i.e. without copying it into a temporary
+ * buffer. This has definite performance benefits when using a QEMU Pipe.
+ *
+ * Set to 0 otherwise.
+ */
+#define WITH_LARGE_SUPPORT  1
+
 EntryPoint * ApiGen::findEntryByName(const std::string & name)
 {
     EntryPoint * entry = NULL;
@@ -338,6 +347,104 @@
     return 0;
 }
 
+// Format the byte length expression for a given variable into a user-provided buffer
+// If the variable type is not a pointer, this is simply its size as a decimal constant
+// If the variable is a pointer, this will be an expression provided by the .attrib file
+// through the 'len' attribute.
+//
+// Returns 1 if the variable is a pointer, 0 otherwise
+//
+static int getVarEncodingSizeExpression(Var&  var, EntryPoint* e, char* buff, size_t bufflen)
+{
+    int ret = 0;
+    if (!var.isPointer()) {
+        snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes());
+    } else {
+        ret = 1;
+        const char* lenExpr = var.lenExpression().c_str();
+        const char* varname = var.name().c_str();
+        if (e != NULL && lenExpr[0] == '\0') {
+            fprintf(stderr, "%s: data len is undefined for '%s'\n",
+                    e->name().c_str(), varname);
+        }
+        if (var.nullAllowed()) {
+            snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr);
+        } else {
+            snprintf(buff, bufflen, "%s", lenExpr);
+        }
+    }
+    return ret;
+}
+
+static int writeVarEncodingSize(Var& var, FILE* fp)
+{
+    int ret = 0;
+    if (!var.isPointer()) {
+        fprintf(fp, "%u", (unsigned int) var.type()->bytes());
+    } else {
+        ret = 1;
+        fprintf(fp, "__size_%s", var.name().c_str());
+    }
+    return ret;
+}
+
+
+
+static void writeVarEncodingExpression(Var& var, FILE* fp)
+{
+    const char* varname = var.name().c_str();
+
+    if (var.isPointer()) {
+        // encode a pointer header
+        fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname);
+
+        Var::PointerDir dir = var.pointerDir();
+        if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
+            if (var.nullAllowed()) {
+                fprintf(fp, "\tif (%s != NULL) ", varname);
+            } else {
+                fprintf(fp, "\t");
+            }
+
+            if (var.packExpression().size() != 0) {
+                fprintf(fp, "%s;", var.packExpression().c_str());
+            } else {
+                fprintf(fp, "memcpy(ptr, %s, __size_%s);",
+                        varname, varname);
+            }
+
+            fprintf(fp, "ptr += __size_%s;\n", varname);
+        }
+    } else {
+        // encode a non pointer variable
+        if (!var.isVoid()) {
+            fprintf(fp, "\t*(%s *) (ptr) = %s; ptr += %u;\n",
+                    var.type()->name().c_str(), varname,
+                    (uint) var.type()->bytes());
+        }
+    }
+}
+
+#if WITH_LARGE_SUPPORT
+static void writeVarLargeEncodingExpression(Var& var, FILE* fp)
+{
+    const char* varname = var.name().c_str();
+
+    fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname);
+    if (var.nullAllowed()) {
+        fprintf(fp, "\tif (%s != NULL) ", varname);
+    } else {
+        fprintf(fp, "\t");
+    }
+    if (var.writeExpression() != "") {
+        fprintf(fp, "%s", var.writeExpression().c_str());
+    } else {
+        fprintf(fp, "stream->writeFully(%s, __size_%s)", varname, varname);
+    }
+    fprintf(fp, ";\n");
+}
+#endif /* WITH_LARGE_SUPPORT */
+
 int ApiGen::genEncoderImpl(const std::string &filename)
 {
     FILE *fp = fopen(filename.c_str(), "wt");
@@ -368,46 +475,139 @@
         fprintf(fp, "{\n");
 
 //      fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str());
-        fprintf(fp, "\n\t%s *ctx = (%s *)self;\n\n",
+        fprintf(fp, "\n\t%s *ctx = (%s *)self;\n",
                 classname.c_str(),
                 classname.c_str());
-
-        // size calculation ;
-        fprintf(fp, "\t size_t packetSize = ");
-
+        fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n\n");
         VarsArray & evars = e->vars();
+        size_t  maxvars = evars.size();
+        size_t  j;
+
+        char    buff[256];
+
+        // Define the __size_XXX variables that contain the size of data
+        // associated with pointers.
+        for (j = 0; j < maxvars; j++) {
+            Var& var = evars[j];
+
+            if (!var.isPointer())
+                continue;
+
+            const char* varname = var.name().c_str();
+            fprintf(fp, "\tconst unsigned int __size_%s = ", varname);
+
+            getVarEncodingSizeExpression(var, e, buff, sizeof(buff));
+            fprintf(fp, "%s;\n", buff);
+        }
+
+#if WITH_LARGE_SUPPORT
+        // We need to take care of 'isLarge' variable in a special way
+        // Anything before an isLarge variable can be packed into a single
+        // buffer, which is then commited. Each isLarge variable is a pointer
+        // to data that can be written to directly through the pipe, which
+        // will be instant when using a QEMU pipe
+
+        size_t  nvars   = 0;
+        size_t  npointers = 0;
+
+        // First, compute the total size, 8 bytes for the opcode + payload size
+        fprintf(fp, "\t unsigned char *ptr;\n");
+        fprintf(fp, "\t const size_t packetSize = 8");
+
+        for (j = 0; j < maxvars; j++) {
+            fprintf(fp, " + ");
+            npointers += writeVarEncodingSize(evars[j], fp);
+        }
+        if (npointers > 0) {
+            fprintf(fp, " + %u*4", npointers);
+        }
+        fprintf(fp, ";\n");
+
+        // We need to divide the packet into fragments. Each fragment contains
+        // either copied arguments to a temporary buffer, or direct writes for
+        // large variables.
+        //
+        // The first fragment must also contain the opcode+payload_size
+        //
+        nvars = 0;
+        while (nvars < maxvars || maxvars == 0) {
+
+            // Skip over non-large fields
+            for (j = nvars; j < maxvars; j++) {
+                if (evars[j].isLarge())
+                    break;
+            }
+
+            // Write a fragment if needed.
+            if (nvars == 0 || j > nvars) {
+                const char* plus = "";
+
+                if (nvars == 0 && j == maxvars) {
+                    // Simple shortcut for the common case where we don't have large variables;
+                    fprintf(fp, "\tptr = stream->alloc(packetSize);\n");
+
+                } else {
+                    // allocate buffer from the stream until the first large variable
+                    fprintf(fp, "\tptr = stream->alloc(");
+                    plus = "";
+
+                    if (nvars == 0) {
+                        fprintf(fp,"8"); plus = " + ";
+                    }
+                    if (j > nvars) {
+                        npointers = 0;
+                        for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
+                            fprintf(fp, "%s", plus); plus = " + ";
+                            npointers += writeVarEncodingSize(evars[j], fp);
+                        }
+                        if (npointers > 0) {
+                            fprintf(fp, "%s%u*4", plus, npointers); plus = " + ";
+                        }
+                    }
+                    fprintf(fp,");\n");
+                }
+
+                // encode packet header if needed.
+                if (nvars == 0) {
+                    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");
+                }
+
+                if (maxvars == 0)
+                    break;
+
+                // encode non-large fields in this fragment
+                for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
+                    writeVarEncodingExpression(evars[j],fp);
+                }
+
+                // Ensure the fragment is commited if it is followed by a large variable
+                if (j < maxvars) {
+                    fprintf(fp, "\tstream->flush();\n");
+                }
+            }
+
+            // If we have one or more large variables, write them directly.
+            // As size + data
+            for ( ; j < maxvars && evars[j].isLarge(); j++) {
+                writeVarLargeEncodingExpression(evars[j], fp);
+            }
+
+            nvars = j;
+        }
+
+#else /* !WITH_LARGE_SUPPORT */
         size_t nvars = evars.size();
         size_t npointers = 0;
+        fprintf(fp, "\t const size_t packetSize = 8");
         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());
-            }
+            npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff));
+            fprintf(fp, " + %s", buff);
         }
-        fprintf(fp, " %s 8 + %u * 4;\n", nvars != 0 ? "+" : "", (unsigned int) npointers);
+        fprintf(fp, " + %u * 4;\n", (unsigned int) npointers);
 
         // allocate buffer from the stream;
-        fprintf(fp, "\t unsigned char *ptr = ctx->m_stream->alloc(packetSize);\n\n");
+        fprintf(fp, "\t unsigned char *ptr = stream->alloc(packetSize);\n\n");
 
         // encode into the stream;
         fprintf(fp, "\t*(unsigned int *)(ptr) = OP_%s; ptr += 4;\n",  e->name().c_str());
@@ -415,62 +615,23 @@
 
         // 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());
-                    }
-
-                    if (evars[j].nullAllowed()) {
-                        fprintf(fp, "ptr += %s == NULL ? 0 : %s; \n", evars[j].name().c_str(), evars[j].lenExpression().c_str());
-                    } else {
-                        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());
-                }
-            }
+            writeVarEncodingExpression(evars[j], fp);
         }
+#endif /* !WITH_LARGE_SUPPORT */
+
         // 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) {
+                    const char* varname = evars[j].name().c_str();
                     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());
+                        fprintf(fp, "\tif (%s != NULL) ",varname);
                     } else {
-                        fprintf(fp, "\tctx->m_stream->readback(%s, %s);\n",
-                                evars[j].name().c_str(),
-                                evars[j].lenExpression().c_str());
+                        fprintf(fp, "\t");
                     }
+                    fprintf(fp, "stream->readback(%s, __size_%s);\n",
+                            varname, varname);
                 }
             }
         }
@@ -482,7 +643,7 @@
             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, "\tstream->readback(&retval, %u);\n",(uint) e->retval().type()->bytes());
             fprintf(fp, "\treturn retval;\n");
         }
         fprintf(fp, "}\n\n");
diff --git a/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp b/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
index 413b56a..43b904b 100644
--- a/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
+++ b/tools/emulator/opengl/host/tools/emugen/EntryPoint.cpp
@@ -119,7 +119,7 @@
         fprintf(stderr, "UNKNOWN retval: %s\n", linestr.c_str());
     }
 
-    m_retval.init(std::string(""), theType, std::string(""), Var::POINTER_OUT, std::string(""));
+    m_retval.init(std::string(""), theType, std::string(""), Var::POINTER_OUT, std::string(""), std::string(""));
 
     // function name
     m_name = getNextToken(linestr, pos, &last, ",)");
@@ -146,7 +146,7 @@
                 varname = oss.str();
             }
 
-            m_vars.push_back(Var(varname, v, std::string(""), Var::POINTER_IN, ""));
+            m_vars.push_back(Var(varname, v, std::string(""), Var::POINTER_IN, "", ""));
         }
         pos = last + 1;
     }
@@ -286,22 +286,36 @@
                     (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());
+        int count = 0;
+        for (;;) {
+            pos = last;
+            std::string flag = getNextToken(line, pos, &last, WHITESPACE);
+            if (flag.size() == 0) {
+                if (count == 0) {
+                    fprintf(stderr, "ERROR: %u: missing flag\n", (unsigned int) lc);
+                    return -3;
+                }
+                break;
             }
-        } else {
-            fprintf(stderr, "WARNING: %u: unknow flag %s\n", (unsigned int)lc, flag.c_str());
+            count++;
+
+            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 if (flag == "isLarge") {
+                if (v->isPointer()) {
+                    v->setIsLarge(true);
+                } else {
+                    fprintf(stderr, "WARNING: %u: setting isLarge flag for a 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;
@@ -320,6 +334,23 @@
         // set the size expression into var
         pos = last;
         v->setPackExpression(line.substr(pos));
+    } else if (token == "custom_write") {
+        pos = last;
+        std::string varname = getNextToken(line, pos, &last, WHITESPACE);
+
+        if (varname.size() == 0) {
+            fprintf(stderr, "ERROR: %u: Missing variable name in 'custom_write' 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->setWriteExpression(line.substr(pos));
     } else if (token == "flag") {
         pos = last;
         std::string flag = getNextToken(line, pos, &last, WHITESPACE);
diff --git a/tools/emulator/opengl/host/tools/emugen/README b/tools/emulator/opengl/host/tools/emugen/README
index 4d2c28d..5df11a3 100644
--- a/tools/emulator/opengl/host/tools/emugen/README
+++ b/tools/emulator/opengl/host/tools/emugen/README
@@ -313,7 +313,10 @@
 
  var_flag 
  	 description : set variable flags
- 	 format: var_flag <varname> < nullAllowed | ... >
+ 	 format: var_flag <varname> < nullAllowed | isLarge | ... >
+
+        nullAllowed -> for pointer variables, indicates that NULL is a valid value
+        isLarge     -> for pointer variables, indicates that the data should be sent without an intermediate copy
 
  flag
 	description: set entry point flag; 
diff --git a/tools/emulator/opengl/host/tools/emugen/Var.h b/tools/emulator/opengl/host/tools/emugen/Var.h
index c9735c7..322c66a 100644
--- a/tools/emulator/opengl/host/tools/emugen/Var.h
+++ b/tools/emulator/opengl/host/tools/emugen/Var.h
@@ -30,7 +30,9 @@
         m_lenExpression(""),
         m_pointerDir(POINTER_IN),
         m_nullAllowed(false),
+        m_isLarge(false),
         m_packExpression(""),
+        m_writeExpression(""),
         m_paramCheckExpression("")
 
     {
@@ -40,26 +42,33 @@
         const VarType * vartype,
         const std::string & lenExpression,
         PointerDir dir,
-        const std::string &packExpression) :
+        const std::string &packExpression,
+        const std::string &writeExpression) :
         m_name(name),
         m_type(const_cast<VarType *>(vartype)),
         m_lenExpression(lenExpression),
         m_pointerDir(dir),
         m_nullAllowed(false),
+        m_isLarge(false),
         m_packExpression(packExpression),
-		m_paramCheckExpression("")	
+        m_writeExpression(writeExpression),
+	m_paramCheckExpression("")
     {
     }
 
     void init(const std::string name, const VarType * vartype,
               std::string lenExpression,
-              PointerDir dir, std::string packExpression) {
+              PointerDir dir,
+              std::string packExpression,
+              std::string writeExpression) {
         m_name = name;
         m_type = vartype;
         m_lenExpression = lenExpression;
         m_packExpression = packExpression;
+        m_writeExpression = writeExpression;
         m_pointerDir = dir;
         m_nullAllowed = false;
+        m_isLarge = false;
 
     }
 
@@ -69,14 +78,18 @@
     bool isVoid() const { return ((m_type->bytes() == 0) && (!m_type->isPointer())); }
     const std::string & lenExpression() const { return m_lenExpression; }
     const std::string & packExpression() const { return(m_packExpression); }
+    const std::string & writeExpression() const { return(m_writeExpression); }
     const std::string & paramCheckExpression() const { return m_paramCheckExpression; }
     void setLenExpression(const std::string & lenExpression) { m_lenExpression = lenExpression; }
     void setPackExpression(const std::string & packExpression) { m_packExpression = packExpression; }
+    void setWriteExpression(const std::string & writeExpression) { m_writeExpression = writeExpression; }
     void setParamCheckExpression(const std::string & paramCheckExpression) { m_paramCheckExpression = paramCheckExpression; }
     void setPointerDir(PointerDir dir) { m_pointerDir = dir; }
     PointerDir pointerDir() { return m_pointerDir; }
     void setNullAllowed(bool state) { m_nullAllowed = state; }
+    void setIsLarge(bool state) { m_isLarge = state; }
     bool nullAllowed() const { return m_nullAllowed; }
+    bool isLarge() const { return m_isLarge; }
     void printType(FILE *fp) { fprintf(fp, "%s", m_type->name().c_str()); }
     void printTypeName(FILE *fp) { printType(fp); fprintf(fp, " %s", m_name.c_str()); }
 
@@ -87,7 +100,9 @@
     std::string m_lenExpression; // an expression to calcualte a pointer data size
     PointerDir m_pointerDir;
     bool m_nullAllowed;
+    bool m_isLarge;
     std::string m_packExpression; // an expression to pack data into the stream
+    std::string m_writeExpression; // an expression to write data into the stream
     std::string m_paramCheckExpression; //an expression to check parameter value
 
 };
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp
index 244cfbb..ddc56d0 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.cpp
@@ -85,13 +85,18 @@
 
 int SocketStream::commitBuffer(size_t size)
 {
+    return writeFully(m_buf, size);
+}
+
+int SocketStream::writeFully(const void* buffer, size_t size)
+{
     if (!valid()) return -1;
 
     size_t res = size;
     int retval = 0;
 
     while (res > 0) {
-        ssize_t stat = ::send(m_sock, (const char *)(m_buf) + (size - res), res, 0);
+        ssize_t stat = ::send(m_sock, (const char *)buffer + (size - res), res, 0);
         if (stat < 0) {
             if (errno != EINTR) {
                 retval =  stat;
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h
index c54dea7..3a501b4 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/SocketStream.h
@@ -37,6 +37,7 @@
 
     bool valid() { return m_sock >= 0; }
     virtual int recv(void *buf, size_t len);
+    virtual int writeFully(const void *buf, size_t len);
 
 protected:
     int            m_sock;
@@ -44,7 +45,6 @@
     unsigned char *m_buf;
 
     SocketStream(int sock, size_t bufSize);
-    int writeFully(const void *buf, size_t len);
 };
 
 #endif /* __SOCKET_STREAM_H */
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.cpp b/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.cpp
index b0702a7..ae70598 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.cpp
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.cpp
@@ -16,6 +16,7 @@
 #include "glUtils.h"
 #include <string.h>
 #include "ErrorLog.h"
+#include <IOStream.h>
 
 size_t glSizeof(GLenum type)
 {
@@ -344,6 +345,25 @@
     }
 }
 
+void glUtilsWritePackPointerData(void* _stream, unsigned char *src,
+                                 int size, GLenum type, unsigned int stride,
+                                 unsigned int datalen)
+{
+    IOStream* stream = reinterpret_cast<IOStream*>(_stream);
+
+    unsigned int  vsize = size * glSizeof(type);
+    if (stride == 0) stride = vsize;
+
+    if (stride == vsize) {
+        stream->writeFully(src, datalen);
+    } else {
+        for (unsigned int i = 0; i < datalen; i += vsize) {
+            stream->writeFully(src, (size_t)vsize);
+            src += stride;
+        }
+    }
+}
+
 int glUtilsPixelBitSize(GLenum format, GLenum type)
 {
     int components = 0;
diff --git a/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.h b/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.h
index c66c568..f8857f1 100644
--- a/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.h
+++ b/tools/emulator/opengl/shared/OpenglCodecCommon/glUtils.h
@@ -51,6 +51,9 @@
     void   glUtilsPackPointerData(unsigned char *dst, unsigned char *str,
                            int size, GLenum type, unsigned int stride,
                            unsigned int datalen);
+    void glUtilsWritePackPointerData(void* stream, unsigned char *src,
+                                    int size, GLenum type, unsigned int stride,
+                                    unsigned int datalen);
     int glUtilsPixelBitSize(GLenum format, GLenum type);
     void   glUtilsPackStrings(char *ptr, char **strings, GLint *length, GLsizei count);
     int glUtilsCalcShaderSourceLen(char **strings, GLint *length, GLsizei count);
diff --git a/tools/emulator/opengl/system/GLESv1_enc/GLEncoder.cpp b/tools/emulator/opengl/system/GLESv1_enc/GLEncoder.cpp
index 6d12fe3..43391f0 100644
--- a/tools/emulator/opengl/system/GLESv1_enc/GLEncoder.cpp
+++ b/tools/emulator/opengl/system/GLESv1_enc/GLEncoder.cpp
@@ -544,7 +544,7 @@
     m_glBufferData_enc = set_glBufferData(s_glBufferData);
     m_glBufferSubData_enc = set_glBufferSubData(s_glBufferSubData);
     m_glDeleteBuffers_enc = set_glDeleteBuffers(s_glDeleteBuffers);
-    
+
     m_glEnableClientState_enc = set_glEnableClientState(s_glEnableClientState);
     m_glDisableClientState_enc = set_glDisableClientState(s_glDisableClientState);
     m_glIsEnabled_enc = set_glIsEnabled(s_glIsEnabled);
@@ -558,7 +558,7 @@
 
 GLEncoder::~GLEncoder()
 {
-    delete m_compressedTextureFormats;
+    delete [] m_compressedTextureFormats;
 }
 
 size_t GLEncoder::pixelDataSize(GLsizei width, GLsizei height, GLenum format, GLenum type, int pack)
diff --git a/tools/emulator/opengl/system/GLESv1_enc/gl.attrib b/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
index fed0f7a..9b84f89 100644
--- a/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
+++ b/tools/emulator/opengl/system/GLESv1_enc/gl.attrib
@@ -240,7 +240,7 @@
 glTexImage2D
 	dir pixels in
 	len pixels pixelDataSize(self, width, height, format, type, 0)
-	var_flag pixels nullAllowed
+	var_flag pixels nullAllowed isLarge
 
 #void glTexParameteriv(GLenum target, GLenum pname, GLint *params)
 glTexParameteriv
@@ -253,6 +253,7 @@
 #void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
 glTexSubImage2D
 	len pixels pixelDataSize(self, width, height, format, type, 0)
+        var_flag pixels isLarge
 
 #void glVertexPointer(GLint size, GLenum type, GLsizei stride, GLvoid *pointer)
 # we treat the pointer as an offset to a VBO
diff --git a/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib b/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
index 538c453..7fe9a66 100644
--- a/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
+++ b/tools/emulator/opengl/system/GLESv2_enc/gl2.attrib
@@ -9,20 +9,22 @@
 #void glBufferData(GLenum target, GLsizeiptr size, GLvoid *data, GLenum usage)
 glBufferData
 	len data size
-	var_flag data nullAllowed
+	var_flag data nullAllowed isLarge
 
 #void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data)
 glBufferSubData
 	len data size
+        var_flag data isLarge
 
 #void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid *data)
 glCompressedTexImage2D
 	len data imageSize
-    var_flag data nullAllowed
+    var_flag data nullAllowed isLarge
 
 #void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, GLvoid *data)
 glCompressedTexSubImage2D
 	len data imageSize
+        var_flag data isLarge
 
 #void glDeleteBuffers(GLsizei n, GLuint *buffers)
 glDeleteBuffers
@@ -243,7 +245,7 @@
 glTexImage2D
 	dir pixels in
 	len pixels pixelDataSize(self, width, height, format, type, 0)
-	var_flag pixels nullAllowed
+	var_flag pixels nullAllowed isLarge
 
 #void glTexParameterfv(GLenum target, GLenum pname, GLfloat *params)
 glTexParameterfv
@@ -255,6 +257,7 @@
 #void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
 glTexSubImage2D
 	len pixels pixelDataSize(self, width, height, format, type, 0)
+        var_flag pixels isLarge
 	
 #void glUniform1fv(GLint location, GLsizei count, GLfloat *v)
 glUniform1fv
@@ -333,20 +336,23 @@
 
 #void glTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, GLvoid *pixels)
 glTexImage3DOES
-	len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)	
-	var_flag pixels nullAllowed
+	len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)
+	var_flag pixels nullAllowed isLarge
 
 #void glTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *pixels)
 glTexSubImage3DOES
 	len pixels pixelDataSize3D(self, width, height, depth, format, type, 0)	
+        var_flag pixels isLarge
 
 #void glCompressedTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, GLvoid *data)
 glCompressedTexImage3DOES
 	len data imageSize
+        var_flag data isLarge
 
 #void glCompressedTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, GLvoid *data)
 glCompressedTexSubImage3DOES
 	len data imageSize
+        var_flag data isLarge
 
 #void glDeleteVertexArraysOES(GLsizei n, GLuint *arrays)
 glDeleteVertexArraysOES
diff --git a/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h b/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
index db36286..57ee399 100644
--- a/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
+++ b/tools/emulator/opengl/system/OpenglSystemCommon/QemuPipeStream.h
@@ -39,8 +39,7 @@
     bool valid() { return m_sock >= 0; }
     int recv(void *buf, size_t len);
 
-private:
-    int writeFully(const void *buf, size_t len);
+    virtual int writeFully(const void *buf, size_t len);
 
 private:
     int m_sock;
diff --git a/tools/emulator/opengl/system/egl/ClientAPIExts.cpp b/tools/emulator/opengl/system/egl/ClientAPIExts.cpp
index 888c231..5e81afe 100644
--- a/tools/emulator/opengl/system/egl/ClientAPIExts.cpp
+++ b/tools/emulator/opengl/system/egl/ClientAPIExts.cpp
@@ -129,7 +129,7 @@
 #define API_ENTRY_RET(rtype,fname,params,args) \
     API_ENTRY(fname,params,args)
 
-static struct _client_ext_funcs {
+static const struct _client_ext_funcs {
     const char *fname;
     void* proc;
 } s_client_ext_funcs[] = {
diff --git a/tools/emulator/opengl/system/egl/ClientAPIExts.in b/tools/emulator/opengl/system/egl/ClientAPIExts.in
index c3162eb..5850701 100644
--- a/tools/emulator/opengl/system/egl/ClientAPIExts.in
+++ b/tools/emulator/opengl/system/egl/ClientAPIExts.in
@@ -2,7 +2,7 @@
 // Each extension function should have one of the following
 // macro definitions:
 //    API_ENTRY(funcname, paramlist, arglist)
-//  -or- (in case funciton has return value)
+//  -or- (if the function has a return value)
 //    API_ENTRY_RET(return_type,funcname, paramlist, arglist)
 //
 API_ENTRY(glEGLImageTargetTexture2DOES,
diff --git a/tools/emulator/opengl/system/egl/eglDisplay.cpp b/tools/emulator/opengl/system/egl/eglDisplay.cpp
index 78025b1..7beeb8e 100644
--- a/tools/emulator/opengl/system/egl/eglDisplay.cpp
+++ b/tools/emulator/opengl/system/egl/eglDisplay.cpp
@@ -19,17 +19,17 @@
 
 static const int systemEGLVersionMajor = 1;
 static const int systemEGLVersionMinor = 4;
-static const char *systemEGLVendor = "Google Android emulator";
+static const char systemEGLVendor[] = "Google Android emulator";
 
 // list of extensions supported by this EGL implementation
 //  NOTE that each extension name should be suffixed with space
-static const char *systemStaticEGLExtensions =
+static const char systemStaticEGLExtensions[] =
             "EGL_ANDROID_image_native_buffer ";
 
 // list of extensions supported by this EGL implementation only if supported
 // on the host implementation.
 //  NOTE that each extension name should be suffixed with space
-static const char *systemDynamicEGLExtensions =
+static const char systemDynamicEGLExtensions[] =
             "EGL_KHR_image_base "
             "EGL_KHR_gl_texture_2d_image ";
 
@@ -70,6 +70,11 @@
     pthread_mutex_init(&m_lock, NULL);
 }
 
+eglDisplay::~eglDisplay()
+{
+    pthread_mutex_destroy(&m_lock);
+}
+
 bool eglDisplay::initialize(EGLClient_eglInterface *eglIface)
 {
     pthread_mutex_lock(&m_lock);
diff --git a/tools/emulator/opengl/system/egl/eglDisplay.h b/tools/emulator/opengl/system/egl/eglDisplay.h
index 934c699..9d979d9 100644
--- a/tools/emulator/opengl/system/egl/eglDisplay.h
+++ b/tools/emulator/opengl/system/egl/eglDisplay.h
@@ -33,6 +33,7 @@
 {
 public:
     eglDisplay();
+    ~eglDisplay();
 
     bool initialize(EGLClient_eglInterface *eglIface);
     void terminate();
diff --git a/tools/emulator/opengl/system/egl/egl_ftable.h b/tools/emulator/opengl/system/egl/egl_ftable.h
index ee40585..2c9d19a 100644
--- a/tools/emulator/opengl/system/egl/egl_ftable.h
+++ b/tools/emulator/opengl/system/egl/egl_ftable.h
@@ -13,7 +13,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-static struct _egl_funcs_by_name {
+static const struct _egl_funcs_by_name {
     const char *name;
     void *proc;
 } egl_funcs_by_name[] = {
@@ -63,4 +63,4 @@
     {"eglSetSwapRectangleANDROID", (void *)eglSetSwapRectangleANDROID}
 };
 
-static int egl_num_funcs = sizeof(egl_funcs_by_name) / sizeof(struct _egl_funcs_by_name);
+static const int egl_num_funcs = sizeof(egl_funcs_by_name) / sizeof(struct _egl_funcs_by_name);
diff --git a/tools/emulator/opengl/system/gralloc/gralloc.cpp b/tools/emulator/opengl/system/gralloc/gralloc.cpp
index 9c3df97..b27eaa3 100644
--- a/tools/emulator/opengl/system/gralloc/gralloc.cpp
+++ b/tools/emulator/opengl/system/gralloc/gralloc.cpp
@@ -31,6 +31,21 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
+/* Set to 1 or 2 to enable debug traces */
+#define DEBUG  0
+
+#if DEBUG >= 1
+#  define D(...)   LOGD(__VA_ARGS__)
+#else
+#  define D(...)   ((void)0)
+#endif
+
+#if DEBUG >= 2
+#  define DD(...)  LOGD(__VA_ARGS__)
+#else
+#  define DD(...)  ((void)0)
+#endif
+
 #define DBG_FUNC DBG("%s\n", __FUNCTION__)
 //
 // our private gralloc module structure
@@ -115,7 +130,7 @@
                          int w, int h, int format, int usage,
                          buffer_handle_t* pHandle, int* pStride)
 {
-    LOGD("gralloc_alloc w=%d h=%d usage=0x%x\n", w, h, usage);
+    D("gralloc_alloc w=%d h=%d usage=0x%x\n", w, h, usage);
 
     gralloc_device_t *grdev = (gralloc_device_t *)dev;
     if (!grdev || !pHandle || !pStride)
@@ -182,7 +197,7 @@
         *pStride = bpr / bpp;
     }
 
-    LOGD("gralloc_alloc ashmem_size=%d, tid %d\n", ashmem_size, gettid());
+    D("gralloc_alloc ashmem_size=%d, tid %d\n", ashmem_size, gettid());
 
     //
     // Allocate space in ashmem if needed
@@ -194,7 +209,7 @@
 
         fd = ashmem_create_region("gralloc-buffer", ashmem_size);
         if (fd < 0) {
-            LOGE("gralloc_alloc failed to create ashmem region err=%d\n", errno);
+            LOGE("gralloc_alloc failed to create ashmem region: %s\n", strerror(errno));
             return -errno;
         }
     }
@@ -224,7 +239,7 @@
         DEFINE_HOST_CONNECTION;
         if (hostCon && rcEnc) {
             cb->hostHandle = rcEnc->rcCreateColorBuffer(rcEnc, w, h, glFormat);
-            LOGD("Created host ColorBuffer 0x%x\n", cb->hostHandle);
+            D("Created host ColorBuffer 0x%x\n", cb->hostHandle);
         }
 
         if (!cb->hostHandle) {
@@ -264,7 +279,7 @@
 
     if (cb->hostHandle != 0) {
         DEFINE_AND_VALIDATE_HOST_CONNECTION;
-
+        D("Destroying host ColorBuffer 0x%x\n", cb->hostHandle);
         rcEnc->rcDestroyColorBuffer(rcEnc, cb->hostHandle);
     }
 
@@ -370,7 +385,7 @@
     DEFINE_AND_VALIDATE_HOST_CONNECTION;
 
     // send request to host
-    // XXX - should be implemented
+    // TODO: XXX - should be implemented
     //rcEnc->rc_XXX
 
     return 0;
@@ -399,9 +414,7 @@
 {
     fb_device_t *fbdev = (fb_device_t *)dev;
 
-    if (fbdev) {
-        delete fbdev;
-    }
+    delete fbdev;
 
     return 0;
 }
@@ -418,11 +431,12 @@
         return sFallback->registerBuffer(sFallback, handle);
     }
 
+    D("gralloc_register_buffer(%p) called", handle);
 
     private_module_t *gr = (private_module_t *)module;
     cb_handle_t *cb = (cb_handle_t *)handle;
     if (!gr || !cb_handle_t::validate(cb)) {
-        ERR("gralloc_register_buffer: invalid buffer");
+        ERR("gralloc_register_buffer(%p): invalid buffer", cb);
         return -EINVAL;
     }
 
@@ -434,7 +448,7 @@
         void *vaddr;
         int err = map_buffer(cb, &vaddr);
         if (err) {
-            ERR("gralloc_register_buffer: map failed");
+            ERR("gralloc_register_buffer(%p): map failed: %s", cb, strerror(-err));
             return -err;
         }
         cb->mappedPid = getpid();
@@ -453,7 +467,7 @@
     private_module_t *gr = (private_module_t *)module;
     cb_handle_t *cb = (cb_handle_t *)handle;
     if (!gr || !cb_handle_t::validate(cb)) {
-        ERR("gralloc_unregister_buffer: invalid buffer");
+        ERR("gralloc_unregister_buffer(%p): invalid buffer", cb);
         return -EINVAL;
     }
 
@@ -465,13 +479,15 @@
         void *vaddr;
         int err = munmap((void *)cb->ashmemBase, cb->ashmemSize);
         if (err) {
-            ERR("gralloc_unregister_buffer: unmap failed");
+            ERR("gralloc_unregister_buffer(%p): unmap failed", cb);
             return -EINVAL;
         }
         cb->ashmemBase = NULL;
         cb->mappedPid = 0;
     }
 
+    D("gralloc_unregister_buffer(%p) done\n", cb);
+
     return 0;
 }
 
@@ -640,7 +656,7 @@
 {
     int status = -EINVAL;
 
-    LOGD("gralloc_device_open %s\n", name);
+    D("gralloc_device_open %s\n", name);
 
     pthread_once( &sFallbackOnce, fallback_init );
     if (sFallback != NULL) {
@@ -689,16 +705,21 @@
         //
         // Query the host for Framebuffer attributes
         //
-        LOGD("gralloc: query Frabuffer attribs\n");
+        D("gralloc: query Frabuffer attribs\n");
         EGLint width = rcEnc->rcGetFBParam(rcEnc, FB_WIDTH);
-        LOGD("gralloc: width=%d\n", width);
+        D("gralloc: width=%d\n", width);
         EGLint height = rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT);
-        LOGD("gralloc: height=%d\n", height);
+        D("gralloc: height=%d\n", height);
         EGLint xdpi = rcEnc->rcGetFBParam(rcEnc, FB_XDPI);
+        D("gralloc: xdpi=%d\n", xdpi);
         EGLint ydpi = rcEnc->rcGetFBParam(rcEnc, FB_YDPI);
+        D("gralloc: ydpi=%d\n", ydpi);
         EGLint fps = rcEnc->rcGetFBParam(rcEnc, FB_FPS);
+        D("gralloc: fps=%d\n", fps);
         EGLint min_si = rcEnc->rcGetFBParam(rcEnc, FB_MIN_SWAP_INTERVAL);
+        D("gralloc: min_swap=%d\n", min_si);
         EGLint max_si = rcEnc->rcGetFBParam(rcEnc, FB_MAX_SWAP_INTERVAL);
+        D("gralloc: max_swap=%d\n", max_si);
 
         //
         // Allocate memory for the framebuffer device
diff --git a/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib b/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
index c51ae0e..8b9972f 100644
--- a/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
+++ b/tools/emulator/opengl/system/renderControl_enc/renderControl.attrib
@@ -26,10 +26,10 @@
 
 rcChooseConfig
     dir attribs in
-	len attribs attribs_size
-	dir configs out
+    len attribs attribs_size
+    dir configs out
     var_flag configs nullAllowed
-	len configs configs_size*sizeof(uint32_t)
+    len configs configs_size*sizeof(uint32_t)
 
 rcReadColorBuffer
     dir pixels out
@@ -38,3 +38,4 @@
 rcUpdateColorBuffer
     dir pixels in
     len pixels (((glUtilsPixelBitSize(format, type) * width) >> 3) * height)
+    var_flag pixels isLarge
diff --git a/tools/emulator/system/camera/Android.mk b/tools/emulator/system/camera/Android.mk
index 47f425a..2e5c19c 100755
--- a/tools/emulator/system/camera/Android.mk
+++ b/tools/emulator/system/camera/Android.mk
@@ -28,6 +28,16 @@
     libcamera_client \
     libui \
 
+# JPEG conversion libraries and includes.
+LOCAL_SHARED_LIBRARIES += \
+	libjpeg \
+	libskia \
+	libandroid_runtime \
+
+LOCAL_C_INCLUDES += external/jpeg \
+					external/skia/include/core/ \
+					frameworks/base/core/jni/android/graphics
+
 LOCAL_SRC_FILES := \
 	EmulatedCameraHal.cpp \
     EmulatedCameraFactory.cpp \
@@ -40,7 +50,8 @@
 	Converters.cpp \
 	PreviewWindow.cpp \
 	CallbackNotifier.cpp \
-	QemuClient.cpp
+	QemuClient.cpp \
+	JpegCompressor.cpp
 
 ifeq ($(TARGET_PRODUCT),vbox_x86)
 LOCAL_MODULE := camera.vbox_x86
diff --git a/tools/emulator/system/camera/CallbackNotifier.cpp b/tools/emulator/system/camera/CallbackNotifier.cpp
index 188bf3a..a877b40 100755
--- a/tools/emulator/system/camera/CallbackNotifier.cpp
+++ b/tools/emulator/system/camera/CallbackNotifier.cpp
@@ -25,6 +25,7 @@
 #include <media/stagefright/MetadataBufferType.h>
 #include "EmulatedCameraDevice.h"
 #include "CallbackNotifier.h"
+#include "JpegCompressor.h"
 
 namespace android {
 
@@ -92,7 +93,9 @@
       mLastFrameTimestamp(0),
       mFrameRefreshFreq(0),
       mMessageEnabler(0),
-      mVideoRecEnabled(false)
+      mJpegQuality(90),
+      mVideoRecEnabled(false),
+      mTakingPicture(false)
 {
 }
 
@@ -194,7 +197,9 @@
     mCBOpaque = NULL;
     mLastFrameTimestamp = 0;
     mFrameRefreshFreq = 0;
+    mJpegQuality = 90;
     mVideoRecEnabled = false;
+    mTakingPicture = false;
 }
 
 void CallbackNotifier::onNextFrameAvailable(const void* frame,
@@ -213,6 +218,51 @@
             LOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
         }
     }
+
+    if (mTakingPicture) {
+        /* This happens just once. */
+        mTakingPicture = false;
+        /* The sequence of callbacks during picture taking is:
+         *  - CAMERA_MSG_SHUTTER
+         *  - CAMERA_MSG_RAW_IMAGE_NOTIFY
+         *  - CAMERA_MSG_COMPRESSED_IMAGE
+         */
+        if (isMessageEnabled(CAMERA_MSG_SHUTTER)) {
+            mNotifyCB(CAMERA_MSG_SHUTTER, 0, 0, mCBOpaque);
+        }
+        if (isMessageEnabled(CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
+            mNotifyCB(CAMERA_MSG_RAW_IMAGE_NOTIFY, 0, 0, mCBOpaque);
+        }
+        if (isMessageEnabled(CAMERA_MSG_COMPRESSED_IMAGE)) {
+            /* Compress the frame to JPEG. Note that when taking pictures, we
+             * have requested camera device to provide us with NV21 frames. */
+            NV21JpegCompressor compressor;
+            status_t res =
+                compressor.compressRawImage(frame, camera_dev->getFrameWidth(),
+                                            camera_dev->getFrameHeight(),
+                                            mJpegQuality);
+            if (res == NO_ERROR) {
+                camera_memory_t* jpeg_buff =
+                    mGetMemoryCB(-1, compressor.getCompressedSize(), 1, NULL);
+                if (NULL != jpeg_buff && NULL != jpeg_buff->data) {
+                    compressor.getCompressedImage(jpeg_buff->data);
+                    mDataCB(CAMERA_MSG_COMPRESSED_IMAGE, jpeg_buff, 0, NULL, mCBOpaque);
+                    jpeg_buff->release(jpeg_buff);
+                } else {
+                    LOGE("%s: Memory failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+                }
+            } else {
+                LOGE("%s: Compression failure in CAMERA_MSG_VIDEO_FRAME", __FUNCTION__);
+            }
+        }
+    }
+}
+
+void CallbackNotifier::onCameraDeviceError(int err)
+{
+    if (isMessageEnabled(CAMERA_MSG_ERROR) && mNotifyCB != NULL) {
+        mNotifyCB(CAMERA_MSG_ERROR, err, 0, mCBOpaque);
+    }
 }
 
 /****************************************************************************
diff --git a/tools/emulator/system/camera/CallbackNotifier.h b/tools/emulator/system/camera/CallbackNotifier.h
index 0a595ef..63301d2 100755
--- a/tools/emulator/system/camera/CallbackNotifier.h
+++ b/tools/emulator/system/camera/CallbackNotifier.h
@@ -114,7 +114,7 @@
      */
     inline int isMessageEnabled(uint msg_type)
     {
-        return mMessageEnabler & ~msg_type;
+        return mMessageEnabler & msg_type;
     }
 
     /* Checks id video recording is enabled.
@@ -159,6 +159,27 @@
                               nsecs_t timestamp,
                               EmulatedCameraDevice* camera_dev);
 
+    /* Entry point for notifications that occur in camera device.
+     * Param:
+     *  err - CAMERA_ERROR_XXX error code.
+     */
+    void onCameraDeviceError(int err);
+
+    /* Sets, or resets taking picture state.
+     * This state control whether or not to notify the framework about compressed
+     * image, shutter, and other picture related events.
+     */
+    void setTakingPicture(bool taking)
+    {
+        mTakingPicture = taking;
+    }
+
+    /* Sets JPEG quality used to compress frame during picture taking. */
+    void setJpegQuality(int jpeg_quality)
+    {
+        mJpegQuality = jpeg_quality;
+    }
+
     /****************************************************************************
      * Private API
      ***************************************************************************/
@@ -197,8 +218,14 @@
     /* Message enabler. */
     uint32_t                        mMessageEnabler;
 
+    /* JPEG quality used to compress frame during picture taking. */
+    int                             mJpegQuality;
+
     /* Video recording status. */
     bool                            mVideoRecEnabled;
+
+    /* Picture taking status. */
+    bool                            mTakingPicture;
 };
 
 }; /* namespace android */
diff --git a/tools/emulator/system/camera/Converters.cpp b/tools/emulator/system/camera/Converters.cpp
index 797d652..f63f67f 100755
--- a/tools/emulator/system/camera/Converters.cpp
+++ b/tools/emulator/system/camera/Converters.cpp
@@ -25,24 +25,25 @@
 
 namespace android {
 
-void YV12ToRGB565(const void* yv12, void* rgb, int width, int height)
+static void _YUV420SToRGB565(const uint8_t* Y,
+                             const uint8_t* U,
+                             const uint8_t* V,
+                             int dUV,
+                             uint16_t* rgb,
+                             int width,
+                             int height)
 {
-    const int pix_total = width * height;
-    uint16_t* rgb_buf = reinterpret_cast<uint16_t*>(rgb);
-    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
-    const uint8_t* U_pos = Y + pix_total;
-    const uint8_t* V_pos = U_pos + pix_total / 4;
-    const uint8_t* U = U_pos;
-    const uint8_t* V = V_pos;
+    const uint8_t* U_pos = U;
+    const uint8_t* V_pos = V;
 
     for (int y = 0; y < height; y++) {
-        for (int x = 0; x < width; x += 2) {
-            const uint8_t nU = *U; U++;
-            const uint8_t nV = *V; V++;
-            *rgb_buf = YUVToRGB565(*Y, nU, nV);
-            Y++; rgb_buf++;
-            *rgb_buf = YUVToRGB565(*Y, nU, nV);
-            Y++; rgb_buf++;
+        for (int x = 0; x < width; x += 2, U += dUV, V += dUV) {
+            const uint8_t nU = *U;
+            const uint8_t nV = *V;
+            *rgb = YUVToRGB565(*Y, nU, nV);
+            Y++; rgb++;
+            *rgb = YUVToRGB565(*Y, nU, nV);
+            Y++; rgb++;
         }
         if (y & 0x1) {
             U_pos = U;
@@ -54,24 +55,25 @@
     }
 }
 
-void YV12ToRGB32(const void* yv12, void* rgb, int width, int height)
+static void _YUV420SToRGB32(const uint8_t* Y,
+                            const uint8_t* U,
+                            const uint8_t* V,
+                            int dUV,
+                            uint32_t* rgb,
+                            int width,
+                            int height)
 {
-    const int pix_total = width * height;
-    uint32_t* rgb_buf = reinterpret_cast<uint32_t*>(rgb);
-    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
-    const uint8_t* U_pos = Y + pix_total;
-    const uint8_t* V_pos = U_pos + pix_total / 4;
-    const uint8_t* U = U_pos;
-    const uint8_t* V = V_pos;
+    const uint8_t* U_pos = U;
+    const uint8_t* V_pos = V;
 
     for (int y = 0; y < height; y++) {
-        for (int x = 0; x < width; x += 2) {
-            const uint8_t nU = *U; U++;
-            const uint8_t nV = *V; V++;
-            *rgb_buf = YUVToRGB32(*Y, nU, nV);
-            Y++; rgb_buf++;
-            *rgb_buf = YUVToRGB32(*Y, nU, nV);
-            Y++; rgb_buf++;
+        for (int x = 0; x < width; x += 2, U += dUV, V += dUV) {
+            const uint8_t nU = *U;
+            const uint8_t nV = *V;
+            *rgb = YUVToRGB32(*Y, nU, nV);
+            Y++; rgb++;
+            *rgb = YUVToRGB32(*Y, nU, nV);
+            Y++; rgb++;
         }
         if (y & 0x1) {
             U_pos = U;
@@ -83,4 +85,89 @@
     }
 }
 
+void YV12ToRGB565(const void* yv12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
+    const uint8_t* U = Y + pix_total;
+    const uint8_t* V = U + pix_total / 4;
+    _YUV420SToRGB565(Y, U, V, 1, reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void YV12ToRGB32(const void* yv12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yv12);
+    const uint8_t* V = Y + pix_total;
+    const uint8_t* U = V + pix_total / 4;
+    _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+void YU12ToRGB32(const void* yu12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* Y = reinterpret_cast<const uint8_t*>(yu12);
+    const uint8_t* U = Y + pix_total;
+    const uint8_t* V = U + pix_total / 4;
+    _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+/* Common converter for YUV 4:2:0 interleaved to RGB565.
+ * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
+ */
+static void _NVXXToRGB565(const uint8_t* Y,
+                          const uint8_t* U,
+                          const uint8_t* V,
+                          uint16_t* rgb,
+                          int width,
+                          int height)
+{
+    _YUV420SToRGB565(Y, U, V, 2, rgb, width, height);
+}
+
+/* Common converter for YUV 4:2:0 interleaved to RGB32.
+ * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
+ */
+static void _NVXXToRGB32(const uint8_t* Y,
+                         const uint8_t* U,
+                         const uint8_t* V,
+                         uint32_t* rgb,
+                         int width,
+                         int height)
+{
+    _YUV420SToRGB32(Y, U, V, 2, rgb, width, height);
+}
+
+void NV12ToRGB565(const void* nv12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv12);
+    _NVXXToRGB565(y, y + pix_total, y + pix_total + 1,
+                  reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void NV12ToRGB32(const void* nv12, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv12);
+    _NVXXToRGB32(y, y + pix_total, y + pix_total + 1,
+                 reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
+void NV21ToRGB565(const void* nv21, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv21);
+    _NVXXToRGB565(y, y + pix_total + 1, y + pix_total,
+                  reinterpret_cast<uint16_t*>(rgb), width, height);
+}
+
+void NV21ToRGB32(const void* nv21, void* rgb, int width, int height)
+{
+    const int pix_total = width * height;
+    const uint8_t* y = reinterpret_cast<const uint8_t*>(nv21);
+    _NVXXToRGB32(y, y + pix_total + 1, y + pix_total,
+                 reinterpret_cast<uint32_t*>(rgb), width, height);
+}
+
 }; /* namespace android */
diff --git a/tools/emulator/system/camera/Converters.h b/tools/emulator/system/camera/Converters.h
index 5cdcea2..13e2a85 100755
--- a/tools/emulator/system/camera/Converters.h
+++ b/tools/emulator/system/camera/Converters.h
@@ -17,7 +17,7 @@
 #ifndef HW_EMULATOR_CAMERA_CONVERTERS_H
 #define HW_EMULATOR_CAMERA_CONVERTERS_H
 
-#include <sys/endian.h>
+#include <endian.h>
 
 #ifndef __BYTE_ORDER
 #error "could not determine byte order"
@@ -269,6 +269,46 @@
  */
 void YV12ToRGB32(const void* yv12, void* rgb, int width, int height);
 
+/* Converts an YU12 framebuffer to RGB32 framebuffer.
+ * Param:
+ *  yu12 - YU12 framebuffer.
+ *  rgb - RGB32 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void YU12ToRGB32(const void* yu12, void* rgb, int width, int height);
+
+/* Converts an NV12 framebuffer to RGB565 framebuffer.
+ * Param:
+ *  nv12 - NV12 framebuffer.
+ *  rgb - RGB565 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void NV12ToRGB565(const void* nv12, void* rgb, int width, int height);
+
+/* Converts an NV12 framebuffer to RGB32 framebuffer.
+ * Param:
+ *  nv12 - NV12 framebuffer.
+ *  rgb - RGB32 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void NV12ToRGB32(const void* nv12, void* rgb, int width, int height);
+
+/* Converts an NV21 framebuffer to RGB565 framebuffer.
+ * Param:
+ *  nv21 - NV21 framebuffer.
+ *  rgb - RGB565 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void NV21ToRGB565(const void* nv21, void* rgb, int width, int height);
+
+/* Converts an NV21 framebuffer to RGB32 framebuffer.
+ * Param:
+ *  nv21 - NV21 framebuffer.
+ *  rgb - RGB32 framebuffer.
+ *  width, height - Dimensions for both framebuffers.
+ */
+void NV21ToRGB32(const void* nv21, void* rgb, int width, int height);
+
 }; /* namespace android */
 
 #endif  /* HW_EMULATOR_CAMERA_CONVERTERS_H */
diff --git a/tools/emulator/system/camera/EmulatedCamera.cpp b/tools/emulator/system/camera/EmulatedCamera.cpp
index 12b9792..9cabe75 100755
--- a/tools/emulator/system/camera/EmulatedCamera.cpp
+++ b/tools/emulator/system/camera/EmulatedCamera.cpp
@@ -92,11 +92,6 @@
      * Fake required parameters.
      */
 
-    /* Only RGBX are supported by the framework for preview window in the emulator! */
-    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS, CameraParameters::PIXEL_FORMAT_RGBA8888);
-    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES, "60,50,25,15,10");
-    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, "(10,60)");
-    mParameters.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, "10,60");
     mParameters.set(CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES, "320x240,0x0");
     mParameters.set(CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION, "6");
     mParameters.set(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION, "-6");
@@ -109,8 +104,13 @@
     mParameters.set(CameraParameters::KEY_VERTICAL_VIEW_ANGLE, "42.5");
     mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, "90");
 
-    /* Only RGB formats are supported by preview window in emulator. */
-    mParameters.setPreviewFormat(CameraParameters::PIXEL_FORMAT_RGBA8888);
+    /* Preview format settings used here are related to panoramic view only. It's
+     * not related to the preview window that works only with RGB frames, which
+     * is explicitly stated when set_buffers_geometry is called on the preview
+     * window object. */
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS,
+                    CameraParameters::PIXEL_FORMAT_YUV420SP);
+    mParameters.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP);
 
     /* We don't relay on the actual frame rates supported by the camera device,
      * since we will emulate them through timeouts in the emulated camera device
@@ -125,8 +125,8 @@
     mParameters.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT,
                     CameraParameters::PIXEL_FORMAT_YUV420P);
     mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS,
-                    CameraParameters::PIXEL_FORMAT_YUV420P);
-    mParameters.setPictureFormat(CameraParameters::PIXEL_FORMAT_YUV420P);
+                    CameraParameters::PIXEL_FORMAT_JPEG);
+    mParameters.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG);
 
     /*
      * Not supported features
@@ -149,6 +149,12 @@
     mCallbackNotifier.onNextFrameAvailable(frame, timestamp, camera_dev);
 }
 
+void EmulatedCamera::onCameraDeviceError(int err)
+{
+    /* Errors are reported through the callback notifier */
+    mCallbackNotifier.onCameraDeviceError(err);
+}
+
 /****************************************************************************
  * Camera API implementation.
  ***************************************************************************/
@@ -302,22 +308,77 @@
 {
     LOGV("%s", __FUNCTION__);
 
+    status_t res;
+    int width, height;
+    uint32_t org_fmt;
+
+    /* Collect frame info for the picture. */
+    mParameters.getPictureSize(&width, &height);
+    const char* pix_fmt = mParameters.getPictureFormat();
+    if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
+        org_fmt = V4L2_PIX_FMT_YUV420;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
+        org_fmt = V4L2_PIX_FMT_RGB32;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
+        org_fmt = V4L2_PIX_FMT_NV21;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_JPEG) == 0) {
+        /* We only have JPEG converted for NV21 format. */
+        org_fmt = V4L2_PIX_FMT_NV21;
+    } else {
+        LOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
+        return EINVAL;
+    }
+    /* Get JPEG quality. */
+    int jpeg_quality = mParameters.getInt(CameraParameters::KEY_JPEG_QUALITY);
+    if (jpeg_quality <= 0) {
+        jpeg_quality = 90;  /* Fall back to default. */
+    }
+
     /*
-     * Before taking picture, pause the camera (pause worker thread), and pause
-     * the preview.
+     * Make sure preview is not running, and device is stopped before taking
+     * picture.
      */
 
+    const bool preview_on = mPreviewWindow.isPreviewEnabled();
+    if (preview_on) {
+        doStopPreview();
+    }
+
+    /* Camera device should have been stopped when the shutter message has been
+     * enabled. */
+    EmulatedCameraDevice* const camera_dev = getCameraDevice();
+    if (camera_dev->isStarted()) {
+        LOGW("%s: Camera device is started", __FUNCTION__);
+        camera_dev->stopDeliveringFrames();
+        camera_dev->stopDevice();
+    }
+
     /*
      * Take the picture now.
      */
 
-    /*
-     * After picture has been taken, resume the preview, and the camera (if any
-     * has been paused.
-     */
+    /* Start camera device for the picture frame. */
+    LOGD("Starting camera for picture: %.4s(%s)[%dx%d]",
+         reinterpret_cast<const char*>(&org_fmt), pix_fmt, width, height);
+    res = camera_dev->startDevice(width, height, org_fmt);
+    if (res != NO_ERROR) {
+        if (preview_on) {
+            doStartPreview();
+        }
+        return res;
+    }
 
-
-    return NO_ERROR;
+    /* Deliver one frame only. */
+    mCallbackNotifier.setJpegQuality(jpeg_quality);
+    mCallbackNotifier.setTakingPicture(true);
+    res = camera_dev->startDeliveringFrames(true);
+    if (res != NO_ERROR) {
+        mCallbackNotifier.setTakingPicture(false);
+        if (preview_on) {
+            doStartPreview();
+        }
+    }
+    return res;
 }
 
 status_t EmulatedCamera::cancelPicture()
@@ -454,17 +515,70 @@
 {
     LOGV("%s", __FUNCTION__);
 
-    status_t res = mPreviewWindow.startPreview();
+    EmulatedCameraDevice* camera_dev = getCameraDevice();
+    if (camera_dev->isStarted()) {
+        camera_dev->stopDeliveringFrames();
+        camera_dev->stopDevice();
+    }
 
-    /* Start the camera. */
-    if (res == NO_ERROR && !getCameraDevice()->isCapturing()) {
-        res = startCamera();
+    status_t res = mPreviewWindow.startPreview();
+    if (res != NO_ERROR) {
+        return res;
+    }
+
+    /* Make sure camera device is connected. */
+    if (!camera_dev->isConnected()) {
+        res = camera_dev->connectDevice();
         if (res != NO_ERROR) {
-            /* If camera didn't start, disable the preview window. */
             mPreviewWindow.stopPreview();
+            return res;
         }
     }
 
+    int width, height;
+    /* Lets see what should we use for frame width, and height. */
+    if (mParameters.get(CameraParameters::KEY_VIDEO_SIZE) != NULL) {
+        mParameters.getVideoSize(&width, &height);
+    } else {
+        mParameters.getPreviewSize(&width, &height);
+    }
+    /* Lets see what should we use for the frame pixel format. */
+    const char* pix_fmt =
+        mParameters.get(CameraParameters::KEY_VIDEO_FRAME_FORMAT);
+    if (pix_fmt == NULL) {
+        pix_fmt = mParameters.getPreviewFormat();
+    }
+    if (pix_fmt == NULL) {
+        LOGE("%s: Unable to obtain video format", __FUNCTION__);
+        mPreviewWindow.stopPreview();
+        return EINVAL;
+    }
+    uint32_t org_fmt;
+    if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
+        org_fmt = V4L2_PIX_FMT_YUV420;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
+        org_fmt = V4L2_PIX_FMT_RGB32;
+    } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
+        org_fmt = V4L2_PIX_FMT_NV21;
+    } else {
+        LOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
+        mPreviewWindow.stopPreview();
+        return EINVAL;
+    }
+    LOGD("Starting camera: %dx%d -> %.4s(%s)",
+         width, height, reinterpret_cast<const char*>(&org_fmt), pix_fmt);
+    res = camera_dev->startDevice(width, height, org_fmt);
+    if (res != NO_ERROR) {
+        mPreviewWindow.stopPreview();
+        return res;
+    }
+
+    res = camera_dev->startDeliveringFrames(false);
+    if (res != NO_ERROR) {
+        camera_dev->stopDevice();
+        mPreviewWindow.stopPreview();
+    }
+
     return res;
 }
 
@@ -473,89 +587,22 @@
     LOGV("%s", __FUNCTION__);
 
     status_t res = NO_ERROR;
-    /* Stop the camera. */
-    if (getCameraDevice()->isCapturing()) {
-        res = stopCamera();
-    }
+    if (mPreviewWindow.isPreviewEnabled()) {
+        /* Stop the camera. */
+        if (getCameraDevice()->isStarted()) {
+            getCameraDevice()->stopDeliveringFrames();
+            res = getCameraDevice()->stopDevice();
+        }
 
-    if (res == NO_ERROR) {
-        /* Disable preview as well. */
-        mPreviewWindow.stopPreview();
+        if (res == NO_ERROR) {
+            /* Disable preview as well. */
+            mPreviewWindow.stopPreview();
+        }
     }
 
     return NO_ERROR;
 }
 
-status_t EmulatedCamera::startCamera()
-{
-    LOGV("%s", __FUNCTION__);
-
-    status_t res = EINVAL;
-    EmulatedCameraDevice* camera_dev = getCameraDevice();
-    if (camera_dev != NULL) {
-        if (!camera_dev->isConnected()) {
-            res = camera_dev->connectDevice();
-            if (res != NO_ERROR) {
-                return res;
-            }
-        }
-        if (!camera_dev->isCapturing()) {
-            int width, height;
-            /* Lets see what should we use for frame width, and height. */
-            if (mParameters.get(CameraParameters::KEY_VIDEO_SIZE) != NULL) {
-                mParameters.getVideoSize(&width, &height);
-            } else {
-                mParameters.getPreviewSize(&width, &height);
-            }
-            /* Lets see what should we use for the frame pixel format. */
-            const char* pix_fmt =
-                mParameters.get(CameraParameters::KEY_VIDEO_FRAME_FORMAT);
-            if (pix_fmt == NULL) {
-                pix_fmt = mParameters.getPreviewFormat();
-            }
-            if (pix_fmt == NULL) {
-                LOGE("%s: Unable to obtain video format", __FUNCTION__);
-                return EINVAL;
-            }
-            uint32_t org_fmt;
-            if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
-                org_fmt = V4L2_PIX_FMT_YVU420;
-            } else if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) {
-                org_fmt = V4L2_PIX_FMT_RGB32;
-            } else {
-                LOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt);
-                return EINVAL;
-            }
-            LOGD("Starting camera: %dx%d -> %s", width, height, pix_fmt);
-            res = camera_dev->startCapturing(width, height, org_fmt);
-            if (res != NO_ERROR) {
-                return res;
-            }
-        }
-    }
-
-    return res;
-}
-
-status_t EmulatedCamera::stopCamera()
-{
-    LOGV("%s", __FUNCTION__);
-
-    status_t res = NO_ERROR;
-    EmulatedCameraDevice* const camera_dev = getCameraDevice();
-    if (camera_dev != NULL) {
-        if (camera_dev->isCapturing()) {
-            res = camera_dev->stopCapturing();
-            if (res != NO_ERROR) {
-                return res;
-            }
-        }
-    }
-
-    return res;
-}
-
-
 /****************************************************************************
  * Private API.
  ***************************************************************************/
@@ -573,8 +620,9 @@
     /* Stop and disconnect the camera device. */
     EmulatedCameraDevice* const camera_dev = getCameraDevice();
     if (camera_dev != NULL) {
-        if (camera_dev->isCapturing()) {
-            res = camera_dev->stopCapturing();
+        if (camera_dev->isStarted()) {
+            camera_dev->stopDeliveringFrames();
+            res = camera_dev->stopDevice();
             if (res != NO_ERROR) {
                 return -res;
             }
diff --git a/tools/emulator/system/camera/EmulatedCamera.h b/tools/emulator/system/camera/EmulatedCamera.h
index 77d16c9..8afdd83 100755
--- a/tools/emulator/system/camera/EmulatedCamera.h
+++ b/tools/emulator/system/camera/EmulatedCamera.h
@@ -94,6 +94,12 @@
                                       nsecs_t timestamp,
                                       EmulatedCameraDevice* camera_dev);
 
+    /* Entry point for notifications that occur in camera device.
+     * Param:
+     *  err - CAMERA_ERROR_XXX error code.
+     */
+    virtual void onCameraDeviceError(int err);
+
     /****************************************************************************
      * Camera API implementation
      ***************************************************************************/
@@ -290,18 +296,6 @@
      */
     virtual status_t doStopPreview();
 
-    /* Starts capturing frames
-     * Return:
-     *  NO_ERROR on success, or an appropriate error status on failure.
-     */
-    virtual status_t startCamera();
-
-    /* Stops capturing frames.
-     * Return:
-     *  NO_ERROR on success, or an appropriate error status on failure.
-     */
-    virtual status_t stopCamera();
-
     /****************************************************************************
      * Private API.
      ***************************************************************************/
diff --git a/tools/emulator/system/camera/EmulatedCameraDevice.cpp b/tools/emulator/system/camera/EmulatedCameraDevice.cpp
index fb9c1da..e09bead 100755
--- a/tools/emulator/system/camera/EmulatedCameraDevice.cpp
+++ b/tools/emulator/system/camera/EmulatedCameraDevice.cpp
@@ -72,16 +72,82 @@
     return NO_ERROR;
 }
 
-status_t EmulatedCameraDevice::startCapturing(int width,
-                                              int height,
-                                              uint32_t pix_fmt)
+status_t EmulatedCameraDevice::startDeliveringFrames(bool one_burst)
 {
     LOGV("%s", __FUNCTION__);
 
-    Mutex::Autolock locker(&mObjectLock);
+    if (!isStarted()) {
+        LOGE("%s: Device is not started", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* Frames will be delivered from the thread routine. */
+    const status_t res = startWorkerThread(one_burst);
+    LOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__);
+    return res;
+}
+
+status_t EmulatedCameraDevice::stopDeliveringFrames()
+{
+    LOGV("%s", __FUNCTION__);
+
+    if (!isStarted()) {
+        LOGW("%s: Device is not started", __FUNCTION__);
+        return NO_ERROR;
+    }
+
+    const status_t res = stopWorkerThread();
+    LOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__);
+    return res;
+}
+
+status_t EmulatedCameraDevice::getCurrentPreviewFrame(void* buffer)
+{
+    if (!isStarted()) {
+        LOGE("%s: Device is not started", __FUNCTION__);
+        return EINVAL;
+    }
+    if (mCurrentFrame == NULL || buffer == NULL) {
+        LOGE("%s: No framebuffer", __FUNCTION__);
+        return EINVAL;
+    }
+
+    /* In emulation the framebuffer is never RGB. */
+    switch (mPixelFormat) {
+        case V4L2_PIX_FMT_YVU420:
+            YV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+            return NO_ERROR;
+        case V4L2_PIX_FMT_YUV420:
+            YU12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+            return NO_ERROR;
+        case V4L2_PIX_FMT_NV21:
+            NV21ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+            return NO_ERROR;
+        case V4L2_PIX_FMT_NV12:
+            NV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
+            return NO_ERROR;
+
+        default:
+            LOGE("%s: Unknown pixel format %.4s",
+                 __FUNCTION__, reinterpret_cast<const char*>(&mPixelFormat));
+            return EINVAL;
+    }
+}
+
+/****************************************************************************
+ * Emulated camera device private API
+ ***************************************************************************/
+
+status_t EmulatedCameraDevice::commonStartDevice(int width,
+                                                 int height,
+                                                 uint32_t pix_fmt)
+{
     /* Validate pixel format, and calculate framebuffer size at the same time. */
     switch (pix_fmt) {
         case V4L2_PIX_FMT_YVU420:
+        case V4L2_PIX_FMT_YUV420:
+        case V4L2_PIX_FMT_NV21:
+        case V4L2_PIX_FMT_NV12:
             mFrameBufferSize = (width * height * 12) / 8;
             break;
 
@@ -103,78 +169,20 @@
         LOGE("%s: Unable to allocate framebuffer", __FUNCTION__);
         return ENOMEM;
     }
-    /* Calculate U/V panes inside the framebuffer. */
-    mFrameU = mCurrentFrame + mTotalPixels;
-    mFrameV = mFrameU + mTotalPixels / 4;
-
-    /* Start the camera. */
-    const status_t res = startDevice();
-    if (res == NO_ERROR) {
-        LOGD("Camera device is started:\n"
-             "      Framebuffer dimensions: %dx%d.\n"
-             "      Pixel format: %.4s",
-             mFrameWidth, mFrameHeight,
-             reinterpret_cast<const char*>(&mPixelFormat));
-    } else {
-        delete[] mCurrentFrame;
-        mCurrentFrame = NULL;
-    }
-
-    return res;
-}
-
-status_t EmulatedCameraDevice::stopCapturing()
-{
-    LOGV("%s", __FUNCTION__);
-
-    Mutex::Autolock locker(&mObjectLock);
-    /* Stop the camera. */
-    const status_t res = stopDevice();
-    if (res == NO_ERROR) {
-        /* Release resources allocated for capturing. */
-        if (mCurrentFrame != NULL) {
-            delete[] mCurrentFrame;
-            mCurrentFrame = NULL;
-        }
-    }
-
-    return res;
-}
-
-status_t EmulatedCameraDevice::getCurrentFrame(void* buffer)
-{
-    Mutex::Autolock locker(&mObjectLock);
-
-    if (!isCapturing() || mCurrentFrame == NULL) {
-        LOGE("%s is called on a device that is not in the capturing state",
-            __FUNCTION__);
-        return EINVAL;
-    }
-
-    memcpy(buffer, mCurrentFrame, mFrameBufferSize);
-
+    LOGV("%s: Allocated %p %d bytes for %d pixels in %.4s[%dx%d] frame",
+         __FUNCTION__, mCurrentFrame, mFrameBufferSize, mTotalPixels,
+         reinterpret_cast<const char*>(&mPixelFormat), mFrameWidth, mFrameHeight);
     return NO_ERROR;
 }
 
-status_t EmulatedCameraDevice::getCurrentPreviewFrame(void* buffer)
+void EmulatedCameraDevice::commonStopDevice()
 {
-    Mutex::Autolock locker(&mObjectLock);
+    mFrameWidth = mFrameHeight = mTotalPixels = 0;
+    mPixelFormat = 0;
 
-    if (!isCapturing() || mCurrentFrame == NULL) {
-        LOGE("%s is called on a device that is not in the capturing state",
-            __FUNCTION__);
-        return EINVAL;
-    }
-
-    /* In emulation the framebuffer is never RGB. */
-    switch (mPixelFormat) {
-        case V4L2_PIX_FMT_YVU420:
-            YV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight);
-            return NO_ERROR;
-
-        default:
-            LOGE("%s: Unknown pixel format %d", __FUNCTION__, mPixelFormat);
-            return EINVAL;
+    if (mCurrentFrame != NULL) {
+        delete[] mCurrentFrame;
+        mCurrentFrame = NULL;
     }
 }
 
@@ -182,7 +190,7 @@
  * Worker thread management.
  ***************************************************************************/
 
-status_t EmulatedCameraDevice::startWorkerThread()
+status_t EmulatedCameraDevice::startWorkerThread(bool one_burst)
 {
     LOGV("%s", __FUNCTION__);
 
@@ -191,11 +199,9 @@
         return EINVAL;
     }
 
-    const status_t ret = getWorkerThread()->startThread();
-    LOGE_IF(ret != NO_ERROR, "%s: Unable to start worker thread: %d -> %s",
-            __FUNCTION__, ret, strerror(ret));
-
-    return ret;
+    const status_t res = getWorkerThread()->startThread(one_burst);
+    LOGE_IF(res != NO_ERROR, "%s: Unable to start worker thread", __FUNCTION__);
+    return res;
 }
 
 status_t EmulatedCameraDevice::stopWorkerThread()
@@ -207,14 +213,15 @@
         return EINVAL;
     }
 
-    getWorkerThread()->stopThread();
-
-    return NO_ERROR;
+    const status_t res = getWorkerThread()->stopThread();
+    LOGE_IF(res != NO_ERROR, "%s: Unable to stop worker thread", __FUNCTION__);
+    return res;
 }
 
 bool EmulatedCameraDevice::inWorkerThread()
 {
-    /* This will end the thread loop, and will terminate the thread. */
+    /* This will end the thread loop, and will terminate the thread. Derived
+     * classes must override this method. */
     return false;
 }
 
@@ -268,10 +275,10 @@
                 LOGV("Emulated camera device's worker thread has been stopped.");
             } else {
                 LOGE("%s: requestExitAndWait failed: %d -> %s",
-                     __FUNCTION__, res, strerror(res));
+                     __FUNCTION__, res, strerror(-res));
             }
         } else {
-            LOGE("%s: Unable to send THREAD_STOP: %d -> %s",
+            LOGE("%s: Unable to send THREAD_STOP message: %d -> %s",
                  __FUNCTION__, errno, strerror(errno));
             res = errno ? errno : EINVAL;
         }
diff --git a/tools/emulator/system/camera/EmulatedCameraDevice.h b/tools/emulator/system/camera/EmulatedCameraDevice.h
index b73b863..88e2dd2 100755
--- a/tools/emulator/system/camera/EmulatedCameraDevice.h
+++ b/tools/emulator/system/camera/EmulatedCameraDevice.h
@@ -69,28 +69,30 @@
      *  NO_ERROR on success, or an appropriate error status. If this method is
      *  called for already disconnected, or uninitialized instance of this class,
      *  a successful status must be returned from this method. If this method is
-     *  called for an instance that is in "capturing" state, this method must
+     *  called for an instance that is in the "started" state, this method must
      *  return a failure.
      */
     virtual status_t disconnectDevice() = 0;
 
-protected:
-    /* Starts capturing frames from the camera device.
-     *
-     * Typically, this method initializes the camera device with the settings
-     * requested by the framework through the camera HAL, and starts a worker
-     * thread that will listen to the physical device for available frames. When
-     * new frame becomes available, it will be cached in current_framebuffer_,
-     * and the containing emulated camera object will be notified via call to
-     * its onNextFrameAvailable method. This method must be called on a
-     * connected instance of this class. If it is called on a disconnected
-     * instance, this method must return a failure.
+    /* Starts the camera device.
+     * This method tells the camera device to start capturing frames of the given
+     * dimensions for the given pixel format. Note that this method doesn't start
+     * the delivery of the captured frames to the emulated camera. Call
+     * startDeliveringFrames method to start delivering frames. This method must
+     * be called on a connected instance of this class. If it is called on a
+     * disconnected instance, this method must return a failure.
+     * Param:
+     *  width, height - Frame dimensions to use when capturing video frames.
+     *  pix_fmt - Pixel format to use when capturing video frames.
      * Return:
      *  NO_ERROR on success, or an appropriate error status.
      */
-    virtual status_t startDevice() = 0;
+    virtual status_t startDevice(int width, int height, uint32_t pix_fmt) = 0;
 
-    /* Stops capturing frames from the camera device.
+    /* Stops the camera device.
+     * This method tells the camera device to stop capturing frames. Note that
+     * this method doesn't stop delivering frames to the emulated camera. Always
+     * call stopDeliveringFrames prior to calling this method.
      * Return:
      *  NO_ERROR on success, or an appropriate error status. If this method is
      *  called for an object that is not capturing frames, or is disconnected,
@@ -114,79 +116,95 @@
      */
     virtual status_t Initialize();
 
-    /* Starts capturing frames from the camera device.
-     *
-     * Typically, this method caches desired frame parameters, and calls
-     * startDevice method to start capturing video frames from the camera
-     * device. This method must be called on a connected instance of this class.
-     * If it is called on a disconnected instance, this method must return a
-     * failure.
+    /* Starts delivering frames captured from the camera device.
+     * This method will start the worker thread that would be pulling frames from
+     * the camera device, and will deliver the pulled frames back to the emulated
+     * camera via onNextFrameAvailable callback. This method must be called on a
+     * connected instance of this class with a started camera device. If it is
+     * called on a disconnected instance, or camera device has not been started,
+     * this method must return a failure.
+     * Param:
+     *  one_burst - Controls how many frames should be delivered. If this
+     *      parameter is 'true', only one captured frame will be delivered to the
+     *      emulated camera. If this parameter is 'false', frames will keep
+     *      coming until stopDeliveringFrames method is called. Typically, this
+     *      parameter is set to 'true' only in order to obtain a single frame
+     *      that will be used as a "picture" in takePicture method of the
+     *      emulated camera.
      * Return:
      *  NO_ERROR on success, or an appropriate error status.
      */
-    virtual status_t startCapturing(int width, int height, uint32_t pix_fmt);
+    virtual status_t startDeliveringFrames(bool one_burst);
 
-    /* Stops capturing frames from the camera device.
-     *
-     * Typically, this method calls stopDevice method of this class, and
-     * uninitializes frame properties, saved in StartCapturing method of this
-     * class.
-     * This method must be called on a connected instance of this class. If it
-     * is called on a disconnected instance, this method must return a failure.
+    /* Stops delivering frames captured from the camera device.
+     * This method will stop the worker thread started by startDeliveringFrames.
      * Return:
      *  NO_ERROR on success, or an appropriate error status.
      */
-    virtual status_t stopCapturing();
+    virtual status_t stopDeliveringFrames();
 
-    /* Gets current fame into provided buffer.
-     * Typically, this method is called by the emulated camera (HAL) in response
-     * to a callback from the emulated camera device that gets invoked when new
-     * captured frame is available.
-     * This method must be called on an instance that is capturing frames from
-     * the physical device. If this method is called on an instance that is not
-     * capturing frames from the physical device, it must return a failure.
+    /* Gets current framebuffer, converted into preview frame format.
+     * This method must be called on a connected instance of this class with a
+     * started camera device. If it is called on a disconnected instance, or
+     * camera device has not been started, this method must return a failure.
+     * Note that this method should be called only after at least one frame has
+     * been captured and delivered. Otherwise it will return garbage in the
+     * preview frame buffer. Typically, this method shuld be called from
+     * onNextFrameAvailable callback.
      * Param:
-     *  buffer - A buffer where to return the frame. Note that the buffer must be
-     *      large enough to contain the entire frame, as defined by frame's width,
-     *      height, and pixel format that are current for the camera device.
-     */
-    virtual status_t getCurrentFrame(void* buffer);
-
-    /* Gets current preview fame into provided buffer.
-     * Param:
-     *  buffer - A buffer where to return the preview frame. Note that the buffer
-     *      must be large enough to contain the entire preview frame, as defined
-     *      by frame's width, height, and preview pixel format. Note also, that
-     *      due to the the limitations of the camera framework in emulator, the
-     *      preview frame is always formatted with RGBA8888.
+     *  buffer - Buffer, large enough to contain the entire preview frame.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
      */
     virtual status_t getCurrentPreviewFrame(void* buffer);
 
-    /* Gets width of the frame obtained from the physical device. */
+    /* Gets width of the frame obtained from the physical device.
+     * Return:
+     *  Width of the frame obtained from the physical device. Note that value
+     *  returned from this method is valid only in case if camera device has been
+     *  started.
+     */
     inline int getFrameWidth() const
     {
+        LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
         return mFrameWidth;
     }
 
-    /* Gets height of the frame obtained from the physical device. */
+    /* Gets height of the frame obtained from the physical device.
+     * Return:
+     *  Height of the frame obtained from the physical device. Note that value
+     *  returned from this method is valid only in case if camera device has been
+     *  started.
+     */
     inline int getFrameHeight() const
     {
+        LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
         return mFrameHeight;
     }
 
-    /* Gets byte size of the current frame buffer. */
+    /* Gets byte size of the current frame buffer.
+     * Return:
+     *  Byte size of the frame buffer. Note that value returned from this method
+     *  is valid only in case if camera device has been started.
+     */
     inline size_t getFrameBufferSize() const
     {
+        LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
         return mFrameBufferSize;
     }
 
-    /* Gets number of pixels in the current frame buffer. */
+    /* Gets number of pixels in the current frame buffer.
+     * Return:
+     *  Number of pixels in the frame buffer. Note that value returned from this
+     *  method is valid only in case if camera device has been started.
+     */
     inline int getPixelNum() const
     {
+        LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
         return mTotalPixels;
     }
 
-    /* Gets pixel format of the frame that physical device streams.
+    /* Gets pixel format of the frame that camera device streams to this class.
      * Throughout camera framework, there are three different forms of pixel
      * format representation:
      *  - Original format, as reported by the actual camera device. Values for
@@ -198,17 +216,17 @@
      * pixel format in the original form. And that's the pixel format
      * representation that will be returned from this method. HAL components will
      * need to translate value returned from this method to the appropriate form.
-     * This method must be called only on connected instance of this class, since
-     * it's applicable only when physical device is ready to stream frames. If
-     * this method is called on an instance that is not connected, it must return
-     * a failure.
+     * This method must be called only on started instance of this class, since
+     * it's applicable only when camera device is ready to stream frames.
      * Param:
      *  pix_fmt - Upon success contains the original pixel format.
      * Return:
-     *  Current framebuffer's pixel format.
+     *  Current framebuffer's pixel format. Note that value returned from this
+     *  method is valid only in case if camera device has been started.
      */
     inline uint32_t getOriginalPixelFormat() const
     {
+        LOGE_IF(!isStarted(), "%s: Device is not started", __FUNCTION__);
         return mPixelFormat;
     }
 
@@ -222,16 +240,32 @@
         return mWorkerThread.get() != NULL && mState != ECDS_CONSTRUCTED;
     }
     inline bool isConnected() const {
-        /* Instance is connected when it is initialized and its status is either
-         * "connected", or "capturing". */
-        return isInitialized() &&
-               (mState == ECDS_CONNECTED || mState == ECDS_CAPTURING);
+        /* Instance is connected when its status is either"connected", or
+         * "started". */
+        return mState == ECDS_CONNECTED || mState == ECDS_STARTED;
     }
-    inline bool isCapturing() const {
-        return isInitialized() && mState == ECDS_CAPTURING;
+    inline bool isStarted() const {
+        return mState == ECDS_STARTED;
     }
 
     /****************************************************************************
+     * Emulated camera device private API
+     ***************************************************************************/
+protected:
+    /* Performs common validation and calculation of startDevice parameters.
+     * Param:
+     *  width, height, pix_fmt - Parameters passed to the startDevice method.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     */
+    virtual status_t commonStartDevice(int width, int height, uint32_t pix_fmt);
+
+    /* Performs common cleanup on stopDevice.
+     * This method will undo what commonStartDevice had done.
+     */
+    virtual void commonStopDevice();
+
+    /****************************************************************************
      * Worker thread management.
      * Typicaly when emulated camera device starts capturing frames from the
      * actual device, it does that in a worker thread created in StartCapturing,
@@ -242,15 +276,22 @@
 
 protected:
     /* Starts the worker thread.
-     * Typically, worker thread is started from StartCamera method of this
-     * class.
+     * Typically, worker thread is started from startDeliveringFrames method of
+     * this class.
+     * Param:
+     *  one_burst - Controls how many times thread loop should run. If this
+     *      parameter is 'true', thread routine will run only once If this
+     *      parameter is 'false', thread routine will run until stopWorkerThread
+     *      method is called. See startDeliveringFrames for more info.
      * Return:
      *  NO_ERROR on success, or an appropriate error status.
      */
-    virtual status_t startWorkerThread();
+    virtual status_t startWorkerThread(bool one_burst);
 
     /* Stops the worker thread.
      * Note that this method will always wait for the worker thread to terminate.
+     * Typically, worker thread is started from stopDeliveringFrames method of
+     * this class.
      * Return:
      *  NO_ERROR on success, or an appropriate error status.
      */
@@ -260,7 +301,7 @@
      * In the default implementation of the worker thread routine we simply
      * return 'false' forcing the thread loop to exit, and the thread to
      * terminate. Derived class should override that method to provide there the
-     * actual frame capturing functionality.
+     * actual frame delivery.
      * Return:
      *  true To continue thread loop (this method will be called again), or false
      *  to exit the thread loop and to terminate the thread.
@@ -298,9 +339,19 @@
                 }
             }
 
-            /* Starts the thread */
-            inline status_t startThread()
+            /* Starts the thread
+             * Param:
+             *  one_burst - Controls how many times thread loop should run. If
+             *      this parameter is 'true', thread routine will run only once
+             *      If this parameter is 'false', thread routine will run until
+             *      stopThread method is called. See startWorkerThread for more
+             *      info.
+             * Return:
+             *  NO_ERROR on success, or an appropriate error status.
+             */
+            inline status_t startThread(bool one_burst)
             {
+                mOneBurst = one_burst;
                 return run(NULL, ANDROID_PRIORITY_URGENT_DISPLAY, 0);
             }
 
@@ -343,10 +394,15 @@
 
         private:
             /* Implements abstract method of the base Thread class. */
-            inline bool threadLoop()
+            bool threadLoop()
             {
                 /* Simply dispatch the call to the containing camera device. */
-                return mCameraDevice->inWorkerThread();
+                if (mCameraDevice->inWorkerThread()) {
+                    /* Respect "one burst" parameter (see startThread). */
+                    return !mOneBurst;
+                } else {
+                    return false;
+                }
             }
 
             /* Containing camera device object. */
@@ -358,6 +414,10 @@
             /* FD that thread uses to receive control messages. */
             int                     mControlFD;
 
+            /* Controls number of times the thread loop runs.
+             * See startThread for more information. */
+            bool                    mOneBurst;
+
             /* Enumerates control messages that can be sent into the thread. */
             enum ControlMessage {
                 /* Stop the thread. */
@@ -391,12 +451,6 @@
     /* Framebuffer containing the current frame. */
     uint8_t*                    mCurrentFrame;
 
-    /* U panel inside the framebuffer. */
-    uint8_t*                    mFrameU;
-
-    /* V panel inside the framebuffer. */
-    uint8_t*                    mFrameV;
-
     /*
      * Framebuffer properties.
      */
@@ -426,8 +480,8 @@
         ECDS_INITIALIZED,
         /* Object has been connected to the physical device. */
         ECDS_CONNECTED,
-        /* Frames are being captured. */
-        ECDS_CAPTURING,
+        /* Camera device has been started. */
+        ECDS_STARTED,
     };
 
     /* Object state. */
diff --git a/tools/emulator/system/camera/EmulatedCameraFactory.cpp b/tools/emulator/system/camera/EmulatedCameraFactory.cpp
index d6ba9b4..5c5c5de 100755
--- a/tools/emulator/system/camera/EmulatedCameraFactory.cpp
+++ b/tools/emulator/system/camera/EmulatedCameraFactory.cpp
@@ -22,6 +22,7 @@
 #define LOG_NDEBUG 0
 #define LOG_TAG "EmulatedCamera_Factory"
 #include <cutils/log.h>
+#include <cutils/properties.h>
 #include "EmulatedQemuCamera.h"
 #include "EmulatedFakeCamera.h"
 #include "EmulatedCameraFactory.h"
@@ -43,9 +44,8 @@
           mConstructedOK(false)
 
 {
-    /* If qemu camera emulation is on, try to connect to the factory service in
-     * the emulator. */
-    if (isQemuCameraEmulationOn() && mQemuClient.connectClient(NULL) == NO_ERROR) {
+    /* Connect to the factory service in the emulator, and create Qemu cameras. */
+    if (mQemuClient.connectClient(NULL) == NO_ERROR) {
         /* Connection has succeeded. Create emulated cameras for each camera
          * device, reported by the service. */
         createQemuCameras();
@@ -82,6 +82,8 @@
             mFakeCameraID = -1;
             LOGE("%s: Unable to instantiate fake camera class", __FUNCTION__);
         }
+    } else {
+        LOGD("Fake camera emulation is disabled.");
     }
 
     LOGV("%d cameras are being emulated. Fake camera ID is %d",
@@ -121,7 +123,7 @@
         return -EINVAL;
     }
 
-    if (camera_id >= getEmulatedCameraNum()) {
+    if (camera_id < 0 || camera_id >= getEmulatedCameraNum()) {
         LOGE("%s: Camera id %d is out of bounds (%d)",
              __FUNCTION__, camera_id, getEmulatedCameraNum());
         return -EINVAL;
@@ -139,7 +141,7 @@
         return -EINVAL;
     }
 
-    if (camera_id >= getEmulatedCameraNum()) {
+    if (camera_id < 0 || camera_id >= getEmulatedCameraNum()) {
         LOGE("%s: Camera id %d is out of bounds (%d)",
              __FUNCTION__, camera_id, getEmulatedCameraNum());
         return -EINVAL;
@@ -197,6 +199,8 @@
 static const char lListNameToken[]    = "name=";
 /* Frame dimensions token. */
 static const char lListDimsToken[]    = "framedims=";
+/* Facing direction token. */
+static const char lListDirToken[]     = "dir=";
 
 void EmulatedCameraFactory::createQemuCameras()
 {
@@ -252,13 +256,15 @@
             next_entry++;   // Start of the next entry.
         }
 
-        /* Find 'name', and 'framedims' tokens that are required here. */
+        /* Find 'name', 'framedims', and 'dir' tokens that are required here. */
         char* name_start = strstr(cur_entry, lListNameToken);
         char* dim_start = strstr(cur_entry, lListDimsToken);
-        if (name_start != NULL && dim_start != NULL) {
+        char* dir_start = strstr(cur_entry, lListDirToken);
+        if (name_start != NULL && dim_start != NULL && dir_start != NULL) {
             /* Advance to the token values. */
             name_start += strlen(lListNameToken);
             dim_start += strlen(lListDimsToken);
+            dir_start += strlen(lListDirToken);
 
             /* Terminate token values with zero. */
             char* s = strchr(name_start, ' ');
@@ -269,12 +275,16 @@
             if (s != NULL) {
                 *s = '\0';
             }
+            s = strchr(dir_start, ' ');
+            if (s != NULL) {
+                *s = '\0';
+            }
 
             /* Create and initialize qemu camera. */
             EmulatedQemuCamera* qemu_cam =
                 new EmulatedQemuCamera(index, &HAL_MODULE_INFO_SYM.common);
             if (NULL != qemu_cam) {
-                res = qemu_cam->Initialize(name_start, dim_start);
+                res = qemu_cam->Initialize(name_start, dim_start, dir_start);
                 if (res == NO_ERROR) {
                     mEmulatedCameras[index] = qemu_cam;
                     index++;
@@ -295,16 +305,17 @@
     mEmulatedCameraNum = index;
 }
 
-bool EmulatedCameraFactory::isQemuCameraEmulationOn()
-{
-    /* TODO: Have a boot property that controls that! */
-    return true;
-}
-
 bool EmulatedCameraFactory::isFakeCameraEmulationOn()
 {
-    /* TODO: Have a boot property that controls that! */
-    return true;
+    /* Defined by 'qemu.sf.fake_camera' boot property: If property is there
+     * and contains 'off', fake camera emulation is disabled. */
+    char prop[PROPERTY_VALUE_MAX];
+    if (property_get("qemu.sf.fake_camera", prop, NULL) <= 0 ||
+        strcmp(prop, "off")) {
+        return true;
+    } else {
+        return false;
+    }
 }
 
 /********************************************************************************
diff --git a/tools/emulator/system/camera/EmulatedCameraFactory.h b/tools/emulator/system/camera/EmulatedCameraFactory.h
index 1e40d82..19745a3 100755
--- a/tools/emulator/system/camera/EmulatedCameraFactory.h
+++ b/tools/emulator/system/camera/EmulatedCameraFactory.h
@@ -94,11 +94,6 @@
      ***************************************************************************/
 
 public:
-    /* Gets fake camera facing. */
-    int getFakeCameraFacing() {
-        /* TODO: Have a boot property that controls that. */
-        return CAMERA_FACING_BACK;
-    }
 
     /* Gets fake camera orientation. */
     int getFakeCameraOrientation() {
@@ -106,12 +101,6 @@
         return 90;
     }
 
-    /* Gets qemu camera facing. */
-    int getQemuCameraFacing() {
-        /* TODO: Have a boot property that controls that. */
-        return CAMERA_FACING_FRONT;
-    }
-
     /* Gets qemu camera orientation. */
     int getQemuCameraOrientation() {
         /* TODO: Have a boot property that controls that. */
@@ -142,9 +131,6 @@
      */
     void createQemuCameras();
 
-    /* Checks if qemu camera emulation is on. */
-    bool isQemuCameraEmulationOn();
-
     /* Checks if fake camera emulation is on. */
     bool isFakeCameraEmulationOn();
 
diff --git a/tools/emulator/system/camera/EmulatedFakeCamera.cpp b/tools/emulator/system/camera/EmulatedFakeCamera.cpp
index 84828cf..d82fd78 100755
--- a/tools/emulator/system/camera/EmulatedFakeCamera.cpp
+++ b/tools/emulator/system/camera/EmulatedFakeCamera.cpp
@@ -22,6 +22,7 @@
 #define LOG_NDEBUG 0
 #define LOG_TAG "EmulatedCamera_FakeCamera"
 #include <cutils/log.h>
+#include <cutils/properties.h>
 #include "EmulatedFakeCamera.h"
 #include "EmulatedCameraFactory.h"
 
@@ -48,11 +49,13 @@
         return res;
     }
 
-    const char* facing = EmulatedCamera::FACING_BACK;
-    if (gEmulatedCameraFactory.getFakeCameraOrientation() == CAMERA_FACING_FRONT) {
-        facing = EmulatedCamera::FACING_FRONT;
-    }
+    /* Fake camera facing is defined by the qemu.sf.fake_camera boot property. */
+    char prop[PROPERTY_VALUE_MAX];
+    property_get("qemu.sf.fake_camera", prop, EmulatedCamera::FACING_BACK);
+    const char* facing = prop;
+
     mParameters.set(EmulatedCamera::FACING_KEY, facing);
+    LOGD("%s: Fake camera is facing %s", __FUNCTION__, facing);
 
     mParameters.set(EmulatedCamera::ORIENTATION_KEY,
                     gEmulatedCameraFactory.getFakeCameraOrientation());
diff --git a/tools/emulator/system/camera/EmulatedFakeCamera.h b/tools/emulator/system/camera/EmulatedFakeCamera.h
index f8a8099..3debe9e 100755
--- a/tools/emulator/system/camera/EmulatedFakeCamera.h
+++ b/tools/emulator/system/camera/EmulatedFakeCamera.h
@@ -45,11 +45,7 @@
      ***************************************************************************/
 
 public:
-    /* Initializes EmulatedFakeCamera instance.
-     * The contained EmulatedFakeCameraDevice will be initialized in this method.
-     * Return:
-     *  NO_ERROR on success, or an appropriate error statsu on failure.
-     */
+    /* Initializes EmulatedFakeCamera instance. */
      status_t Initialize();
 
     /****************************************************************************
diff --git a/tools/emulator/system/camera/EmulatedFakeCameraDevice.cpp b/tools/emulator/system/camera/EmulatedFakeCameraDevice.cpp
index a6c97c6..53a5b1b 100755
--- a/tools/emulator/system/camera/EmulatedFakeCameraDevice.cpp
+++ b/tools/emulator/system/camera/EmulatedFakeCameraDevice.cpp
@@ -34,9 +34,15 @@
       mRedYUV(kRed8),
       mGreenYUV(kGreen8),
       mBlueYUV(kBlue8),
+      mLastRedrawn(0),
       mCheckX(0),
       mCheckY(0),
       mCcounter(0)
+#if EFCD_ROTATE_FRAME
+      , mLastRotatedAt(0),
+        mCurrentFrameType(0),
+        mCurrentColor(&mWhiteYUV)
+#endif  // EFCD_ROTATE_FRAME
 {
 }
 
@@ -62,6 +68,7 @@
         return NO_ERROR;
     }
 
+    /* There is no device to connect to. */
     mState = ECDS_CONNECTED;
 
     return NO_ERROR;
@@ -76,57 +83,99 @@
         LOGW("%s: Fake camera device is already disconnected.", __FUNCTION__);
         return NO_ERROR;
     }
-    if (isCapturing()) {
-        LOGE("%s: Cannot disconnect while in the capturing state.", __FUNCTION__);
+    if (isStarted()) {
+        LOGE("%s: Cannot disconnect from the started device.", __FUNCTION__);
         return EINVAL;
     }
 
+    /* There is no device to disconnect from. */
     mState = ECDS_INITIALIZED;
 
     return NO_ERROR;
 }
 
-status_t EmulatedFakeCameraDevice::startDevice()
+status_t EmulatedFakeCameraDevice::startDevice(int width,
+                                               int height,
+                                               uint32_t pix_fmt)
 {
     LOGV("%s", __FUNCTION__);
 
+    Mutex::Autolock locker(&mObjectLock);
     if (!isConnected()) {
         LOGE("%s: Fake camera device is not connected.", __FUNCTION__);
         return EINVAL;
     }
-    if (isCapturing()) {
-        LOGW("%s: Fake camera device is already capturing.", __FUNCTION__);
-        return NO_ERROR;
+    if (isStarted()) {
+        LOGE("%s: Fake camera device is already started.", __FUNCTION__);
+        return EINVAL;
     }
 
-    /* Used in calculating U/V position when drawing the square. */
-    mHalfWidth = mFrameWidth / 2;
+    /* Initialize the base class. */
+    const status_t res =
+        EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt);
+    if (res == NO_ERROR) {
+        /* Calculate U/V panes inside the framebuffer. */
+        switch (mPixelFormat) {
+            case V4L2_PIX_FMT_YVU420:
+                mFrameV = mCurrentFrame + mTotalPixels;
+                mFrameU = mFrameU + mTotalPixels / 4;
+                mUVStep = 1;
+                mUVTotalNum = mTotalPixels / 4;
+                break;
 
-    /* Just start the worker thread: there is no real device to deal with. */
-    const status_t ret = startWorkerThread();
-    if (ret == NO_ERROR) {
-        mState = ECDS_CAPTURING;
+            case V4L2_PIX_FMT_YUV420:
+                mFrameU = mCurrentFrame + mTotalPixels;
+                mFrameV = mFrameU + mTotalPixels / 4;
+                mUVStep = 1;
+                mUVTotalNum = mTotalPixels / 4;
+                break;
+
+            case V4L2_PIX_FMT_NV21:
+                /* Interleaved UV pane, V first. */
+                mFrameV = mCurrentFrame + mTotalPixels;
+                mFrameU = mFrameV + 1;
+                mUVStep = 2;
+                mUVTotalNum = mTotalPixels / 4;
+                break;
+
+            case V4L2_PIX_FMT_NV12:
+                /* Interleaved UV pane, U first. */
+                mFrameU = mCurrentFrame + mTotalPixels;
+                mFrameV = mFrameU + 1;
+                mUVStep = 2;
+                mUVTotalNum = mTotalPixels / 4;
+                break;
+
+            default:
+                LOGE("%s: Unknown pixel format %.4s", __FUNCTION__,
+                     reinterpret_cast<const char*>(&mPixelFormat));
+                return EINVAL;
+        }
+        /* Number of items in a single row inside U/V panes. */
+        mUVInRow = (width / 2) * mUVStep;
+        mState = ECDS_STARTED;
+    } else {
+        LOGE("%s: commonStartDevice failed", __FUNCTION__);
     }
 
-    return ret;
+    return res;
 }
 
 status_t EmulatedFakeCameraDevice::stopDevice()
 {
     LOGV("%s", __FUNCTION__);
 
-    if (!isCapturing()) {
-        LOGW("%s: Fake camera device is not capturing.", __FUNCTION__);
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isStarted()) {
+        LOGW("%s: Fake camera device is not started.", __FUNCTION__);
         return NO_ERROR;
     }
 
-    /* Just stop the worker thread: there is no real device to deal with. */
-    const status_t ret = stopWorkerThread();
-    if (ret == NO_ERROR) {
-        mState = ECDS_CONNECTED;
-    }
+    mFrameU = mFrameV = NULL;
+    EmulatedCameraDevice::commonStopDevice();
+    mState = ECDS_CONNECTED;
 
-    return ret;
+    return NO_ERROR;
 }
 
 /****************************************************************************
@@ -144,23 +193,31 @@
     }
 
     /* Lets see if we need to generate a new frame. */
-    if ((systemTime(SYSTEM_TIME_MONOTONIC) - mCurFrameTimestamp) >= mRedrawAfter) {
+    if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRedrawn) >= mRedrawAfter) {
         /*
          * Time to generate a new frame.
          */
 
+#if EFCD_ROTATE_FRAME
+        const int frame_type = rotateFrame();
+        switch (frame_type) {
+            case 0:
+                drawCheckerboard();
+                break;
+            case 1:
+                drawStripes();
+                break;
+            case 2:
+                drawSolid(mCurrentColor);
+                break;
+        }
+#else
         /* Draw the checker board. */
         drawCheckerboard();
 
-        /* Run the square. */
-        int x = ((mCcounter * 3) & 255);
-        if(x > 128) x = 255 - x;
-        int y = ((mCcounter * 5) & 255);
-        if(y > 128) y = 255 - y;
-        const int size = mFrameWidth / 10;
-        drawSquare(x * size / 32, y * size / 32, (size * 5) >> 1,
-                   (mCcounter & 0x100) ? &mRedYUV : &mGreenYUV);
-        mCcounter++;
+#endif  // EFCD_ROTATE_FRAME
+
+        mLastRedrawn = systemTime(SYSTEM_TIME_MONOTONIC);
     }
 
     /* Timestamp the current frame, and notify the camera HAL about new frame. */
@@ -202,7 +259,7 @@
                 mWhiteYUV.get(Y, U, V);
             }
             Y[1] = *Y;
-            Y += 2; U++; V++;
+            Y += 2; U += mUVStep; V += mUVStep;
             countx += 2;
             if(countx >= size) {
                 countx = 0;
@@ -223,6 +280,16 @@
     }
     mCheckX += 3;
     mCheckY++;
+
+    /* Run the square. */
+    int sqx = ((mCcounter * 3) & 255);
+    if(sqx > 128) sqx = 255 - sqx;
+    int sqy = ((mCcounter * 5) & 255);
+    if(sqy > 128) sqy = 255 - sqy;
+    const int sqsize = mFrameWidth / 10;
+    drawSquare(sqx * sqsize / 32, sqy * sqsize / 32, (sqsize * 5) >> 1,
+               (mCcounter & 0x100) ? &mRedYUV : &mGreenYUV);
+    mCcounter++;
 }
 
 void EmulatedFakeCameraDevice::drawSquare(int x,
@@ -230,24 +297,115 @@
                                           int size,
                                           const YUVPixel* color)
 {
-    const int half_x = x / 2;
-    const int square_xstop = min(mFrameWidth, x+size);
-    const int square_ystop = min(mFrameHeight, y+size);
+    const int square_xstop = min(mFrameWidth, x + size);
+    const int square_ystop = min(mFrameHeight, y + size);
     uint8_t* Y_pos = mCurrentFrame + y * mFrameWidth + x;
 
     // Draw the square.
     for (; y < square_ystop; y++) {
-        const int iUV = (y / 2) * mHalfWidth + half_x;
+        const int iUV = (y / 2) * mUVInRow + (x / 2) * mUVStep;
         uint8_t* sqU = mFrameU + iUV;
         uint8_t* sqV = mFrameV + iUV;
         uint8_t* sqY = Y_pos;
         for (int i = x; i < square_xstop; i += 2) {
             color->get(sqY, sqU, sqV);
             sqY[1] = *sqY;
-            sqY += 2; sqU++; sqV++;
+            sqY += 2; sqU += mUVStep; sqV += mUVStep;
         }
         Y_pos += mFrameWidth;
     }
 }
 
+#if EFCD_ROTATE_FRAME
+
+void EmulatedFakeCameraDevice::drawSolid(YUVPixel* color)
+{
+    /* All Ys are the same. */
+    memset(mCurrentFrame, color->Y, mTotalPixels);
+
+    /* Fill U, and V panes. */
+    uint8_t* U = mFrameU;
+    uint8_t* V = mFrameV;
+    for (int k = 0; k < mUVTotalNum; k++, U += mUVStep, V += mUVStep) {
+        *U = color->U;
+        *V = color->V;
+    }
+}
+
+void EmulatedFakeCameraDevice::drawStripes()
+{
+    /* Divide frame into 4 stripes. */
+    const int change_color_at = mFrameHeight / 4;
+    const int each_in_row = mUVInRow / mUVStep;
+    uint8_t* pY = mCurrentFrame;
+    for (int y = 0; y < mFrameHeight; y++, pY += mFrameWidth) {
+        /* Select the color. */
+        YUVPixel* color;
+        const int color_index = y / change_color_at;
+        if (color_index == 0) {
+            /* White stripe on top. */
+            color = &mWhiteYUV;
+        } else if (color_index == 1) {
+            /* Then the red stripe. */
+            color = &mRedYUV;
+        } else if (color_index == 2) {
+            /* Then the green stripe. */
+            color = &mGreenYUV;
+        } else {
+            /* And the blue stripe at the bottom. */
+            color = &mBlueYUV;
+        }
+
+        /* All Ys at the row are the same. */
+        memset(pY, color->Y, mFrameWidth);
+
+        /* Offset of the current row inside U/V panes. */
+        const int uv_off = (y / 2) * mUVInRow;
+        /* Fill U, and V panes. */
+        uint8_t* U = mFrameU + uv_off;
+        uint8_t* V = mFrameV + uv_off;
+        for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) {
+            *U = color->U;
+            *V = color->V;
+        }
+    }
+}
+
+int EmulatedFakeCameraDevice::rotateFrame()
+{
+    if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRotatedAt) >= mRotateFreq) {
+        mLastRotatedAt = systemTime(SYSTEM_TIME_MONOTONIC);
+        mCurrentFrameType++;
+        if (mCurrentFrameType > 2) {
+            mCurrentFrameType = 0;
+        }
+        if (mCurrentFrameType == 2) {
+            LOGD("********** Rotated to the SOLID COLOR frame **********");
+            /* Solid color: lets rotate color too. */
+            if (mCurrentColor == &mWhiteYUV) {
+                LOGD("----- Painting a solid RED frame -----");
+                mCurrentColor = &mRedYUV;
+            } else if (mCurrentColor == &mRedYUV) {
+                LOGD("----- Painting a solid GREEN frame -----");
+                mCurrentColor = &mGreenYUV;
+            } else if (mCurrentColor == &mGreenYUV) {
+                LOGD("----- Painting a solid BLUE frame -----");
+                mCurrentColor = &mBlueYUV;
+            } else {
+                /* Back to white. */
+                LOGD("----- Painting a solid WHITE frame -----");
+                mCurrentColor = &mWhiteYUV;
+            }
+        } else if (mCurrentFrameType == 0) {
+            LOGD("********** Rotated to the CHECKERBOARD frame **********");
+        } else {
+            LOGD("********** Rotated to the STRIPED frame **********");
+        }
+    }
+
+    return mCurrentFrameType;
+}
+
+#endif  // EFCD_ROTATE_FRAME
+
 }; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedFakeCameraDevice.h b/tools/emulator/system/camera/EmulatedFakeCameraDevice.h
index c9f13ea..f54127e 100755
--- a/tools/emulator/system/camera/EmulatedFakeCameraDevice.h
+++ b/tools/emulator/system/camera/EmulatedFakeCameraDevice.h
@@ -25,6 +25,15 @@
 #include "Converters.h"
 #include "EmulatedCameraDevice.h"
 
+/* This is used for debugging format / conversion issues. If EFCD_ROTATE_FRAME is
+ * set to 0, the frame content will be always the "checkerboard". Otherwise, if
+ * EFCD_ROTATE_FRAME is set to a non-zero value, the frame content will "rotate"
+ * from a "checkerboard" frame to a "white/red/green/blue stripes" frame, to a
+ * "white/red/green/blue" frame. Frame content rotation helps finding bugs in
+ * format conversions.
+ */
+#define EFCD_ROTATE_FRAME   1
+
 namespace android {
 
 class EmulatedFakeCamera;
@@ -62,19 +71,15 @@
      */
     status_t disconnectDevice();
 
-protected:
-    /* Starts capturing frames from the camera device.
-     * Since there is no real device to control, this method simply starts the
-     * worker thread, and changes the state.
-     */
-    status_t startDevice();
+    /* Starts the camera device. */
+    status_t startDevice(int width, int height, uint32_t pix_fmt);
 
-    /* Stops capturing frames from the camera device.
-     * Since there is no real device to control, this method simply stops the
-     * worker thread, and changes the state.
-     */
+    /* Stops the camera device. */
     status_t stopDevice();
 
+    /* Gets current preview fame into provided buffer. */
+    status_t getPreviewFrame(void* buffer);
+
     /***************************************************************************
      * Worker thread management overrides.
      * See declarations of these methods in EmulatedCameraDevice class for
@@ -83,8 +88,8 @@
 
 protected:
     /* Implementation of the worker thread routine.
-     * This method simply sleeps for a period of time defined by FPS property of
-     * the fake camera (simulating frame frequency), and then calls emulated
+     * This method simply sleeps for a period of time defined by the FPS property
+     * of the fake camera (simulating frame frequency), and then calls emulated
      * camera's onNextFrameAvailable method.
      */
     bool inWorkerThread();
@@ -105,6 +110,12 @@
      */
     void drawSquare(int x, int y, int size, const YUVPixel* color);
 
+#if EFCD_ROTATE_FRAME
+    void drawSolid(YUVPixel* color);
+    void drawStripes();
+    int rotateFrame();
+#endif  // EFCD_ROTATE_FRAME
+
     /****************************************************************************
      * Fake camera device data members
      ***************************************************************************/
@@ -120,14 +131,37 @@
     YUVPixel    mGreenYUV;
     YUVPixel    mBlueYUV;
 
+    /* Last time the frame has been redrawn. */
+    nsecs_t     mLastRedrawn;
+
     /*
-     * Drawing related stuff
+     * Precalculated values related to U/V panes.
+     */
+
+    /* U pane inside the framebuffer. */
+    uint8_t*    mFrameU;
+
+    /* V pane inside the framebuffer. */
+    uint8_t*    mFrameV;
+
+    /* Defines byte distance between adjacent U, and V values. */
+    int         mUVStep;
+
+    /* Defines number of Us and Vs in a row inside the U/V panes.
+     * Note that if U/V panes are interleaved, this value reflects the total
+     * number of both, Us and Vs in a single row in the interleaved UV pane. */
+    int         mUVInRow;
+
+    /* Total number of each, U, and V elements in the framebuffer. */
+    int         mUVTotalNum;
+
+    /*
+     * Checkerboard drawing related stuff
      */
 
     int         mCheckX;
     int         mCheckY;
     int         mCcounter;
-    int         mHalfWidth;
 
     /* Emulated FPS (frames per second).
      * We will emulate 50 FPS. */
@@ -136,6 +170,25 @@
     /* Defines time (in nanoseconds) between redrawing the checker board.
      * We will redraw the checker board every 15 milliseconds. */
     static const nsecs_t    mRedrawAfter = 15000000LL;
+
+#if EFCD_ROTATE_FRAME
+    /* Frame rotation frequency in nanosec (currently - 3 sec) */
+    static const nsecs_t    mRotateFreq = 3000000000LL;
+
+    /* Last time the frame has rotated. */
+    nsecs_t     mLastRotatedAt;
+
+    /* Type of the frame to display in the current rotation:
+     *  0 - Checkerboard.
+     *  1 - White/Red/Green/Blue horisontal stripes
+     *  2 - Solid color. */
+    int         mCurrentFrameType;
+
+    /* Color to use to paint the solid color frame. Colors will rotate between
+     * white, red, gree, and blue each time rotation comes to the solid color
+     * frame. */
+    YUVPixel*   mCurrentColor;
+#endif  // EFCD_ROTATE_FRAME
 };
 
 }; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedQemuCamera.cpp b/tools/emulator/system/camera/EmulatedQemuCamera.cpp
index 5c98ae6..611b6b5 100755
--- a/tools/emulator/system/camera/EmulatedQemuCamera.cpp
+++ b/tools/emulator/system/camera/EmulatedQemuCamera.cpp
@@ -42,8 +42,11 @@
  ***************************************************************************/
 
 status_t EmulatedQemuCamera::Initialize(const char* device_name,
-                                        const char* frame_dims)
+                                        const char* frame_dims,
+                                        const char* facing_dir)
 {
+    LOGV("%s:\n   Name=%s\n   Facing '%s'\n   Dimensions=%s",
+         __FUNCTION__, device_name, facing_dir, frame_dims);
     /* Save dimensions. */
     mFrameDims = frame_dims;
 
@@ -63,11 +66,7 @@
      * Set customizable parameters.
      */
 
-    const char* facing = EmulatedCamera::FACING_FRONT;
-    if (gEmulatedCameraFactory.getQemuCameraOrientation() == CAMERA_FACING_BACK) {
-        facing = EmulatedCamera::FACING_BACK;
-    }
-    mParameters.set(EmulatedCamera::FACING_KEY, facing);
+    mParameters.set(EmulatedCamera::FACING_KEY, facing_dir);
     mParameters.set(EmulatedCamera::ORIENTATION_KEY,
                     gEmulatedCameraFactory.getQemuCameraOrientation());
     mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, frame_dims);
@@ -84,7 +83,7 @@
     if (c == NULL) {
         strncpy(first_dim, frame_dims, sizeof(first_dim));
         first_dim[sizeof(first_dim)-1] = '\0';
-    } else if ((c - frame_dims) < sizeof(first_dim)) {
+    } else if (static_cast<size_t>(c - frame_dims) < sizeof(first_dim)) {
         memcpy(first_dim, frame_dims, c - frame_dims);
         first_dim[c - frame_dims] = '\0';
     } else {
diff --git a/tools/emulator/system/camera/EmulatedQemuCamera.h b/tools/emulator/system/camera/EmulatedQemuCamera.h
index f00076b..1b826c7 100755
--- a/tools/emulator/system/camera/EmulatedQemuCamera.h
+++ b/tools/emulator/system/camera/EmulatedQemuCamera.h
@@ -42,12 +42,10 @@
      **************************************************************************/
 
 public:
-    /* Initializes EmulatedQemuCamera instance.
-     * The contained EmulatedQemuCameraDevice will be initialized in this method.
-     * Return:
-     *  NO_ERROR on success, or an appropriate error status.
-     */
-     status_t Initialize(const char* device_name, const char* frame_dims);
+    /* Initializes EmulatedQemuCamera instance. */
+     status_t Initialize(const char* device_name,
+                         const char* frame_dims,
+                         const char* facing_dir);
 
     /***************************************************************************
      * EmulatedCamera abstract API implementation.
diff --git a/tools/emulator/system/camera/EmulatedQemuCameraDevice.cpp b/tools/emulator/system/camera/EmulatedQemuCameraDevice.cpp
index 1342b95..57dbc98 100755
--- a/tools/emulator/system/camera/EmulatedQemuCameraDevice.cpp
+++ b/tools/emulator/system/camera/EmulatedQemuCameraDevice.cpp
@@ -82,16 +82,20 @@
         return EINVAL;
     }
     if (isConnected()) {
-        LOGW("%s: Qemu camera device is already connected.", __FUNCTION__);
+        LOGW("%s: Qemu camera device '%s' is already connected.",
+             __FUNCTION__, (const char*)mDeviceName);
         return NO_ERROR;
     }
 
+    /* Connect to the camera device via emulator. */
     const status_t res = mQemuClient.queryConnect();
     if (res == NO_ERROR) {
-        LOGV("%s: Connected", __FUNCTION__);
+        LOGV("%s: Connected to device '%s'",
+             __FUNCTION__, (const char*)mDeviceName);
         mState = ECDS_CONNECTED;
     } else {
-        LOGE("%s: Connection failed", __FUNCTION__);
+        LOGE("%s: Connection to device '%s' failed",
+             __FUNCTION__, (const char*)mDeviceName);
     }
 
     return res;
@@ -103,62 +107,76 @@
 
     Mutex::Autolock locker(&mObjectLock);
     if (!isConnected()) {
-        LOGW("%s: Qemu camera device is already disconnected.", __FUNCTION__);
+        LOGW("%s: Qemu camera device '%s' is already disconnected.",
+             __FUNCTION__, (const char*)mDeviceName);
         return NO_ERROR;
     }
-    if (isCapturing()) {
-        LOGE("%s: Cannot disconnect while in the capturing state.", __FUNCTION__);
+    if (isStarted()) {
+        LOGE("%s: Cannot disconnect from the started device '%s.",
+             __FUNCTION__, (const char*)mDeviceName);
         return EINVAL;
     }
 
+    /* Disconnect from the camera device via emulator. */
     const status_t res = mQemuClient.queryDisconnect();
     if (res == NO_ERROR) {
-        LOGV("%s: Disonnected", __FUNCTION__);
+        LOGV("%s: Disonnected from device '%s'",
+             __FUNCTION__, (const char*)mDeviceName);
         mState = ECDS_INITIALIZED;
     } else {
-        LOGE("%s: Disconnection failed", __FUNCTION__);
+        LOGE("%s: Disconnection from device '%s' failed",
+             __FUNCTION__, (const char*)mDeviceName);
     }
 
     return res;
 }
 
-status_t EmulatedQemuCameraDevice::startDevice()
+status_t EmulatedQemuCameraDevice::startDevice(int width,
+                                               int height,
+                                               uint32_t pix_fmt)
 {
     LOGV("%s", __FUNCTION__);
 
+    Mutex::Autolock locker(&mObjectLock);
     if (!isConnected()) {
-        LOGE("%s: Qemu camera device is not connected.", __FUNCTION__);
+        LOGE("%s: Qemu camera device '%s' is not connected.",
+             __FUNCTION__, (const char*)mDeviceName);
         return EINVAL;
     }
-    if (isCapturing()) {
-        LOGW("%s: Qemu camera device is already capturing.", __FUNCTION__);
+    if (isStarted()) {
+        LOGW("%s: Qemu camera device '%s' is already started.",
+             __FUNCTION__, (const char*)mDeviceName);
         return NO_ERROR;
     }
 
+    status_t res = EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt);
+    if (res != NO_ERROR) {
+        LOGE("%s: commonStartDevice failed", __FUNCTION__);
+        return res;
+    }
+
     /* Allocate preview frame buffer. */
     /* TODO: Watch out for preview format changes! At this point we implement
      * RGB32 only.*/
-    mPreviewFrame = new uint16_t[mTotalPixels * 4];
+    mPreviewFrame = new uint32_t[mTotalPixels];
     if (mPreviewFrame == NULL) {
         LOGE("%s: Unable to allocate %d bytes for preview frame",
-             __FUNCTION__, mTotalPixels * 4);
+             __FUNCTION__, mTotalPixels);
         return ENOMEM;
     }
-    memset(mPreviewFrame, 0, mTotalPixels * 4);
 
     /* Start the actual camera device. */
-    status_t res =
-        mQemuClient.queryStart(mPixelFormat, mFrameWidth, mFrameHeight);
+    res = mQemuClient.queryStart(mPixelFormat, mFrameWidth, mFrameHeight);
     if (res == NO_ERROR) {
-        /* Start the worker thread. */
-        res = startWorkerThread();
-        if (res == NO_ERROR) {
-            mState = ECDS_CAPTURING;
-        } else {
-            mQemuClient.queryStop();
-        }
+        LOGV("%s: Qemu camera device '%s' is started for %.4s[%dx%d] frames",
+             __FUNCTION__, (const char*)mDeviceName,
+             reinterpret_cast<const char*>(&mPixelFormat),
+             mFrameWidth, mFrameHeight);
+        mState = ECDS_STARTED;
     } else {
-        LOGE("%s: Start failed", __FUNCTION__);
+        LOGE("%s: Unable to start device '%s' for %.4s[%dx%d] frames",
+             __FUNCTION__, (const char*)mDeviceName,
+             reinterpret_cast<const char*>(&pix_fmt), width, height);
     }
 
     return res;
@@ -168,28 +186,27 @@
 {
     LOGV("%s", __FUNCTION__);
 
-    if (!isCapturing()) {
-        LOGW("%s: Qemu camera device is not capturing.", __FUNCTION__);
+    Mutex::Autolock locker(&mObjectLock);
+    if (!isStarted()) {
+        LOGW("%s: Qemu camera device '%s' is not started.",
+             __FUNCTION__, (const char*)mDeviceName);
         return NO_ERROR;
     }
 
-    /* Stop the worker thread first. */
-    status_t res = stopWorkerThread();
+    /* Stop the actual camera device. */
+    status_t res = mQemuClient.queryStop();
     if (res == NO_ERROR) {
-        /* Stop the actual camera device. */
-        res = mQemuClient.queryStop();
-        if (res == NO_ERROR) {
-            if (mPreviewFrame == NULL) {
-                delete[] mPreviewFrame;
-                mPreviewFrame = NULL;
-            }
-            mState = ECDS_CONNECTED;
-            LOGV("%s: Stopped", __FUNCTION__);
-        } else {
-            LOGE("%s: Stop failed", __FUNCTION__);
+        if (mPreviewFrame == NULL) {
+            delete[] mPreviewFrame;
+            mPreviewFrame = NULL;
         }
+        EmulatedCameraDevice::commonStopDevice();
+        mState = ECDS_CONNECTED;
+        LOGV("%s: Qemu camera device '%s' is stopped",
+             __FUNCTION__, (const char*)mDeviceName);
     } else {
-        LOGE("%s: Unable to stop worker thread", __FUNCTION__);
+        LOGE("%s: Unable to stop device '%s'",
+             __FUNCTION__, (const char*)mDeviceName);
     }
 
     return res;
@@ -232,12 +249,13 @@
         /* Timestamp the current frame, and notify the camera HAL. */
         mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         mCameraHAL->onNextFrameAvailable(mCurrentFrame, mCurFrameTimestamp, this);
+        return true;
     } else {
         LOGE("%s: Unable to get current video frame: %s",
              __FUNCTION__, strerror(query_res));
+        mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED);
+        return false;
     }
-
-    return true;
 }
 
 }; /* namespace android */
diff --git a/tools/emulator/system/camera/EmulatedQemuCameraDevice.h b/tools/emulator/system/camera/EmulatedQemuCameraDevice.h
index 2030869..8ef562b 100755
--- a/tools/emulator/system/camera/EmulatedQemuCameraDevice.h
+++ b/tools/emulator/system/camera/EmulatedQemuCameraDevice.h
@@ -67,15 +67,16 @@
     /* Disconnects from the camera device. */
     status_t disconnectDevice();
 
-protected:
     /* Starts capturing frames from the camera device. */
-    status_t startDevice();
+    status_t startDevice(int width, int height, uint32_t pix_fmt);
 
     /* Stops capturing frames from the camera device. */
     status_t stopDevice();
 
     /***************************************************************************
      * EmulatedCameraDevice virtual overrides
+     * See declarations of these methods in EmulatedCameraDevice class for
+     * information on each of these methods.
      **************************************************************************/
 
 public:
@@ -108,7 +109,7 @@
     String8             mDeviceName;
 
     /* Current preview framebuffer. */
-    uint16_t*           mPreviewFrame;
+    uint32_t*           mPreviewFrame;
 
     /* Emulated FPS (frames per second).
      * We will emulate 50 FPS. */
diff --git a/tools/emulator/system/camera/JpegCompressor.cpp b/tools/emulator/system/camera/JpegCompressor.cpp
new file mode 100644
index 0000000..0e538a1
--- /dev/null
+++ b/tools/emulator/system/camera/JpegCompressor.cpp
@@ -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.
+ */
+
+/*
+ * Contains implementation of a class NV21JpegCompressor that encapsulates a
+ * converter between NV21, and JPEG formats.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "EmulatedCamera_JPEG"
+#include <cutils/log.h>
+#include "JpegCompressor.h"
+
+namespace android {
+
+NV21JpegCompressor::NV21JpegCompressor()
+    : Yuv420SpToJpegEncoder(mStrides)
+{
+}
+
+NV21JpegCompressor::~NV21JpegCompressor()
+{
+}
+
+/****************************************************************************
+ * Public API
+ ***************************************************************************/
+
+status_t NV21JpegCompressor::compressRawImage(const void* image,
+                                              int width,
+                                              int height,
+                                              int quality)
+{
+    LOGV("%s: %p[%dx%d]", __FUNCTION__, image, width, height);
+    void* pY = const_cast<void*>(image);
+    int offsets[2];
+    offsets[0] = 0;
+    offsets[1] = width * height;
+    mStrides[0] = width;
+    mStrides[1] = width;
+    if (encode(&mStream, pY, width, height, offsets, quality)) {
+        LOGV("%s: Compressed JPEG: %d[%dx%d] -> %d bytes",
+             __FUNCTION__, (width * height * 12) / 8, width, height, mStream.getOffset());
+        return NO_ERROR;
+    } else {
+        LOGE("%s: JPEG compression failed", __FUNCTION__);
+        return errno ? errno : EINVAL;
+    }
+}
+
+}; /* namespace android */
diff --git a/tools/emulator/system/camera/JpegCompressor.h b/tools/emulator/system/camera/JpegCompressor.h
new file mode 100644
index 0000000..1f97ae4
--- /dev/null
+++ b/tools/emulator/system/camera/JpegCompressor.h
@@ -0,0 +1,97 @@
+/*
+ * 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 HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H
+#define HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H
+
+/*
+ * Contains declaration of a class NV21JpegCompressor that encapsulates a
+ * converter between YV21, and JPEG formats.
+ */
+
+#include <YuvToJpegEncoder.h>
+
+namespace android {
+
+/* Encapsulates a converter between YV12, and JPEG formats.
+ */
+class NV21JpegCompressor : protected Yuv420SpToJpegEncoder
+{
+public:
+    /* Constructs JpegCompressor instance. */
+    NV21JpegCompressor();
+    /* Destructs JpegCompressor instance. */
+    ~NV21JpegCompressor();
+
+    /****************************************************************************
+     * Public API
+     ***************************************************************************/
+
+public:
+    /* Compresses raw NV21 image into a JPEG.
+     * The compressed image will be saved in mStream member of this class. Use
+     * getCompressedSize method to obtain buffer size of the compressed image,
+     * and getCompressedImage to copy out the compressed image.
+     * Param:
+     *  image - Raw NV21 image.
+     *  width, height - Image dimensions.
+     *  quality - JPEG quality.
+     * Return:
+     *  NO_ERROR on success, or an appropriate error status.
+     *
+     */
+    status_t compressRawImage(const void* image,
+                              int width,
+                              int height,
+                              int quality);
+
+    /* Get size of the compressed JPEG buffer.
+     * This method must be called only after a successful completion of
+     * compressRawImage call.
+     * Return:
+     *  Size of the compressed JPEG buffer.
+     */
+    size_t getCompressedSize() const
+    {
+        return mStream.getOffset();
+    }
+
+    /* Copies out compressed JPEG buffer.
+     * This method must be called only after a successful completion of
+     * compressRawImage call.
+     * Param:
+     *  buff - Buffer where to copy the JPEG. Must be large enough to contain the
+     *      entire image.
+     */
+    void getCompressedImage(void* buff) const
+    {
+        mStream.copyTo(buff);
+    }
+
+    /****************************************************************************
+     * Class data
+     ***************************************************************************/
+
+protected:
+    /* Memory stream where converted JPEG is saved. */
+    SkDynamicMemoryWStream  mStream;
+    /* Strides for Y (the first element), and UV (the second one) panes. */
+    int                     mStrides[2];
+};
+
+}; /* namespace android */
+
+#endif  /* HW_EMULATOR_CAMERA_JPEG_COMPRESSOR_H */
diff --git a/tools/emulator/system/camera/PreviewWindow.cpp b/tools/emulator/system/camera/PreviewWindow.cpp
index c96a807..fb708d5 100755
--- a/tools/emulator/system/camera/PreviewWindow.cpp
+++ b/tools/emulator/system/camera/PreviewWindow.cpp
@@ -121,9 +121,9 @@
              __FUNCTION__, mPreviewWindow, mPreviewFrameWidth,
              mPreviewFrameHeight);
         res = mPreviewWindow->set_buffers_geometry(mPreviewWindow,
-                                                    mPreviewFrameWidth,
-                                                    mPreviewFrameHeight,
-                                                    HAL_PIXEL_FORMAT_RGBA_8888);
+                                                   mPreviewFrameWidth,
+                                                   mPreviewFrameHeight,
+                                                   HAL_PIXEL_FORMAT_RGBA_8888);
         if (res != NO_ERROR) {
             LOGE("%s: Error in set_buffers_geometry %d -> %s",
                  __FUNCTION__, -res, strerror(-res));
@@ -180,6 +180,10 @@
     grbuffer_mapper.unlock(*buffer);
 }
 
+/***************************************************************************
+ * Private API
+ **************************************************************************/
+
 bool PreviewWindow::adjustPreviewDimensions(EmulatedCameraDevice* camera_dev)
 {
     /* Match the cached frame dimensions against the actual ones. */
diff --git a/tools/emulator/system/camera/QemuClient.cpp b/tools/emulator/system/camera/QemuClient.cpp
index 49307bb..fd49585 100755
--- a/tools/emulator/system/camera/QemuClient.cpp
+++ b/tools/emulator/system/camera/QemuClient.cpp
@@ -19,7 +19,7 @@
  * services in the emulator via qemu pipe.
  */
 
-#define LOG_NDEBUG 0
+#define LOG_NDEBUG 1
 #define LOG_TAG "EmulatedCamera_QemuClient"
 #include <cutils/log.h>
 #include "EmulatedCamera.h"
@@ -40,7 +40,7 @@
 
 QemuQuery::QemuQuery()
     : mQuery(mQueryPrealloc),
-      mQueryStatus(NO_ERROR),
+      mQueryDeliveryStatus(NO_ERROR),
       mReplyBuffer(NULL),
       mReplyData(NULL),
       mReplySize(0),
@@ -52,26 +52,26 @@
 
 QemuQuery::QemuQuery(const char* query_string)
     : mQuery(mQueryPrealloc),
-      mQueryStatus(NO_ERROR),
+      mQueryDeliveryStatus(NO_ERROR),
       mReplyBuffer(NULL),
       mReplyData(NULL),
       mReplySize(0),
       mReplyDataSize(0),
       mReplyStatus(0)
 {
-    mQueryStatus = QemuQuery::createQuery(query_string, NULL);
+    mQueryDeliveryStatus = QemuQuery::createQuery(query_string, NULL);
 }
 
 QemuQuery::QemuQuery(const char* query_name, const char* query_param)
     : mQuery(mQueryPrealloc),
-      mQueryStatus(NO_ERROR),
+      mQueryDeliveryStatus(NO_ERROR),
       mReplyBuffer(NULL),
       mReplyData(NULL),
       mReplySize(0),
       mReplyDataSize(0),
       mReplyStatus(0)
 {
-    mQueryStatus = QemuQuery::createQuery(query_name, query_param);
+    mQueryDeliveryStatus = QemuQuery::createQuery(query_name, query_param);
 }
 
 QemuQuery::~QemuQuery()
@@ -88,6 +88,7 @@
     if (name == NULL || *name == '\0') {
         LOGE("%s: NULL or an empty string is passed as query name.",
              __FUNCTION__);
+        mQueryDeliveryStatus = EINVAL;
         return EINVAL;
     }
 
@@ -101,7 +102,7 @@
         if (mQuery == NULL) {
             LOGE("%s: Unable to allocate %d bytes for query buffer",
                  __FUNCTION__, required);
-            mQueryStatus = ENOMEM;
+            mQueryDeliveryStatus = ENOMEM;
             return ENOMEM;
         }
     }
@@ -119,9 +120,9 @@
 status_t QemuQuery::completeQuery(status_t status)
 {
     /* Save query completion status. */
-    mQueryStatus = status;
-    if (mQueryStatus != NO_ERROR) {
-        return mQueryStatus;
+    mQueryDeliveryStatus = status;
+    if (mQueryDeliveryStatus != NO_ERROR) {
+        return mQueryDeliveryStatus;
     }
 
     /* Make sure reply buffer contains at least 'ok', or 'ko'.
@@ -131,7 +132,7 @@
      * zero-terminated, and the terminator will be inculded in the reply. */
     if (mReplyBuffer == NULL || mReplySize < 3) {
         LOGE("%s: Invalid reply to the query", __FUNCTION__);
-        mQueryStatus = EINVAL;
+        mQueryDeliveryStatus = EINVAL;
         return EINVAL;
     }
 
@@ -142,7 +143,7 @@
         mReplyStatus = 0;
     } else {
         LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
-        mQueryStatus = EINVAL;
+        mQueryDeliveryStatus = EINVAL;
         return EINVAL;
     }
 
@@ -152,7 +153,7 @@
          * with a ':' */
         if (mReplyBuffer[2] != ':') {
             LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
-            mQueryStatus = EINVAL;
+            mQueryDeliveryStatus = EINVAL;
             return EINVAL;
         }
         mReplyData = mReplyBuffer + 3;
@@ -162,7 +163,7 @@
          * zero-terminator. */
         if (mReplyBuffer[2] != '\0') {
             LOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
-            mQueryStatus = EINVAL;
+            mQueryDeliveryStatus = EINVAL;
             return EINVAL;
         }
     }
@@ -176,14 +177,13 @@
         delete[] mQuery;
     }
     mQuery = mQueryPrealloc;
-    mQueryStatus = NO_ERROR;
+    mQueryDeliveryStatus = NO_ERROR;
     if (mReplyBuffer != NULL) {
         free(mReplyBuffer);
         mReplyBuffer = NULL;
     }
     mReplyData = NULL;
-    mReplySize = 0;
-    mReplyDataSize = 0;
+    mReplySize = mReplyDataSize = 0;
     mReplyStatus = 0;
 }
 
@@ -270,9 +270,9 @@
     if (written == data_size) {
         return NO_ERROR;
     } else {
-        LOGE("%s: Error sending data via qemu pipe: %s",
+        LOGE("%s: Error sending data via qemu pipe: '%s'",
              __FUNCTION__, strerror(errno));
-        return errno != NO_ERROR ? errno : EIO;
+        return errno ? errno : EIO;
     }
 }
 
@@ -331,9 +331,9 @@
 status_t QemuClient::doQuery(QemuQuery* query)
 {
     /* Make sure that query has been successfuly constructed. */
-    if (query->mQueryStatus != NO_ERROR) {
+    if (query->mQueryDeliveryStatus != NO_ERROR) {
         LOGE("%s: Query is invalid", __FUNCTION__);
-        return query->mQueryStatus;
+        return query->mQueryDeliveryStatus;
     }
 
     LOGQ("Send query '%s'", query->mQuery);
@@ -357,7 +357,11 @@
     }
 
     /* Complete the query, and return its completion handling status. */
-    return query->completeQuery(res);
+    const status_t res1 = query->completeQuery(res);
+    LOGE_IF(res1 != NO_ERROR && res1 != res,
+            "%s: Error %d in query '%s' completion",
+            __FUNCTION__, res1, query->mQuery);
+    return res1;
 }
 
 /****************************************************************************
@@ -385,8 +389,9 @@
     LOGV("%s", __FUNCTION__);
 
     QemuQuery query(mQueryList);
-    doQuery(&query);
-    if (!query.isQuerySucceeded()) {
+    if (doQuery(&query) || !query.isQuerySucceeded()) {
+        LOGE("%s: List cameras query failed: %s", __FUNCTION__,
+             query.mReplyData ? query.mReplyData : "No error message");
         return query.getCompletionStatus();
     }
 
@@ -445,9 +450,9 @@
     QemuQuery query(mQueryConnect);
     doQuery(&query);
     const status_t res = query.getCompletionStatus();
-    LOGE_IF(res != NO_ERROR, "%s failed: %s",
+    LOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
             __FUNCTION__, query.mReplyData ? query.mReplyData :
-                                              "No error message");
+                                             "No error message");
     return res;
 }
 
@@ -458,9 +463,9 @@
     QemuQuery query(mQueryDisconnect);
     doQuery(&query);
     const status_t res = query.getCompletionStatus();
-    LOGE_IF(res != NO_ERROR, "%s failed: %s",
+    LOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
             __FUNCTION__, query.mReplyData ? query.mReplyData :
-                                              "No error message");
+                                             "No error message");
     return res;
 }
 
@@ -476,9 +481,9 @@
     QemuQuery query(query_str);
     doQuery(&query);
     const status_t res = query.getCompletionStatus();
-    LOGE_IF(res != NO_ERROR, "%s failed: %s",
+    LOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
             __FUNCTION__, query.mReplyData ? query.mReplyData :
-                                              "No error message");
+                                             "No error message");
     return res;
 }
 
@@ -489,9 +494,9 @@
     QemuQuery query(mQueryStop);
     doQuery(&query);
     const status_t res = query.getCompletionStatus();
-    LOGE_IF(res != NO_ERROR, "%s failed: %s",
+    LOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
             __FUNCTION__, query.mReplyData ? query.mReplyData :
-                                              "No error message");
+                                             "No error message");
     return res;
 }
 
@@ -500,6 +505,8 @@
                                       size_t vframe_size,
                                       size_t pframe_size)
 {
+    LOGV("%s", __FUNCTION__);
+
     char query_str[256];
     snprintf(query_str, sizeof(query_str), "%s video=%d preview=%d",
              mQueryFrame, (vframe && vframe_size) ? vframe_size : 0,
@@ -507,39 +514,41 @@
     QemuQuery query(query_str);
     doQuery(&query);
     const status_t res = query.getCompletionStatus();
-    LOGE_IF(res != NO_ERROR, "%s failed: %s",
-            __FUNCTION__, query.mReplyData ? query.mReplyData :
+    if( res != NO_ERROR) {
+        LOGE("%s: Query failed: %s",
+             __FUNCTION__, query.mReplyData ? query.mReplyData :
                                               "No error message");
-    if (res == NO_ERROR) {
-        /* Copy requested frames. */
-        size_t cur_offset = 0;
-        const uint8_t* frame = reinterpret_cast<const uint8_t*>(query.mReplyData);
-        /* Video frame is always first. */
-        if (vframe != NULL && vframe_size != 0) {
-            /* Make sure that video frame is in. */
-            if ((query.mReplyDataSize - cur_offset) >= vframe_size) {
-                memcpy(vframe, frame, vframe_size);
-                cur_offset += vframe_size;
-            } else {
-                LOGE("%s: Reply (%d bytes) is to small to contain video frame (%d bytes)",
-                     __FUNCTION__, query.mReplyDataSize - cur_offset, vframe_size);
-                return EINVAL;
-            }
+        return res;
+    }
+
+    /* Copy requested frames. */
+    size_t cur_offset = 0;
+    const uint8_t* frame = reinterpret_cast<const uint8_t*>(query.mReplyData);
+    /* Video frame is always first. */
+    if (vframe != NULL && vframe_size != 0) {
+        /* Make sure that video frame is in. */
+        if ((query.mReplyDataSize - cur_offset) >= vframe_size) {
+            memcpy(vframe, frame, vframe_size);
+            cur_offset += vframe_size;
+        } else {
+            LOGE("%s: Reply %d bytes is to small to contain %d bytes video frame",
+                 __FUNCTION__, query.mReplyDataSize - cur_offset, vframe_size);
+            return EINVAL;
         }
-        if (pframe != NULL && pframe_size != 0) {
-            /* Make sure that preview frame is in. */
-            if ((query.mReplyDataSize - cur_offset) >= pframe_size) {
-                memcpy(pframe, frame + cur_offset, pframe_size);
-                cur_offset += pframe_size;
-            } else {
-                LOGE("%s: Reply (%d bytes) is to small to contain preview frame (%d bytes)",
-                     __FUNCTION__, query.mReplyDataSize - cur_offset, pframe_size);
-                return EINVAL;
-            }
+    }
+    if (pframe != NULL && pframe_size != 0) {
+        /* Make sure that preview frame is in. */
+        if ((query.mReplyDataSize - cur_offset) >= pframe_size) {
+            memcpy(pframe, frame + cur_offset, pframe_size);
+            cur_offset += pframe_size;
+        } else {
+            LOGE("%s: Reply %d bytes is to small to contain %d bytes preview frame",
+                 __FUNCTION__, query.mReplyDataSize - cur_offset, pframe_size);
+            return EINVAL;
         }
     }
 
-    return res;
+    return NO_ERROR;
 }
 
 }; /* namespace android */
diff --git a/tools/emulator/system/camera/QemuClient.h b/tools/emulator/system/camera/QemuClient.h
index 9614a4d..c0b8e61 100755
--- a/tools/emulator/system/camera/QemuClient.h
+++ b/tools/emulator/system/camera/QemuClient.h
@@ -57,7 +57,9 @@
  *  - '=' are allowed only to divide parameter names from parameter values.
  *
  * Emulator replies to each query in two chunks:
- * - 4 bytes encoding the payload size
+ * - 8 bytes encoding the payload size as a string containing hexadecimal
+ *   representation of the payload size value. This is done in order to simplify
+ *   dealing with different endianness on the host, and on the guest.
  * - Payload, whose size is defined by the first chunk.
  *
  * Every payload always begins with two characters, encoding the result of the
@@ -66,7 +68,9 @@
  *  - 'ko' Encoding a failure.
  * After that payload may have optional data. If payload has more data following
  * the query result, there is a ':' character separating them. If payload carries
- * only the result, it always ends with a zero-terminator.
+ * only the result, it always ends with a zero-terminator. So, payload 'ok'/'ko'
+ * prefix is always 3 bytes long: it either includes a zero-terminator, if there
+ * is no data, or a ':' separator.
  */
 class QemuQuery {
 public:
@@ -81,7 +85,7 @@
      */
     explicit QemuQuery(const char* query_string);
 
-    /* Constructs and initializes QemuQuery instance for a query.
+    /* Constructs and initializes QemuQuery instance for a query with parameters.
      * Param:
      *  query_name - Query name.
      *  query_param - Query parameters. Can be NULL.
@@ -96,7 +100,8 @@
      ***************************************************************************/
 
     /* Creates new query.
-     * This method will reset this instance prior to creating a new query.
+     * Note: this method will reset this instance prior to creating a new query
+     * in order to discard possible "leftovers" from the previous query.
      * Param:
      *  query_name - Query name.
      *  query_param - Query parameters. Can be NULL.
@@ -108,19 +113,19 @@
     /* Completes the query after a reply from the emulator.
      * This method will parse the reply buffer, and calculate the final query
      * status, which depends not only on the transport success / failure, but
-     * also on 'ok' / 'ko' in the query reply.
+     * also on 'ok' / 'ko' in the reply buffer.
      * Param:
      *  status - Query delivery status. This status doesn't necessarily reflects
-     *  the final query status (which is defined by 'ok'/'ko' in the reply buffer).
-     *  This status simply states whether or not the query has been sent, and a
-     *  reply has been received successfuly. However, if status indicates a
-     *  failure, the entire query has failed. If status indicates a success, the
-     *  reply will be checked here to calculate the final query status.
+     *      the final query status (which is defined by 'ok'/'ko' prefix in the
+     *      reply buffer). This status simply states whether or not the query has
+     *      been sent, and a reply has been received successfuly. However, if
+     *      this status indicates a failure, it means that the entire query has
+     *      failed.
      * Return:
      *  NO_ERROR on success, or an appropriate error status on failure. Note that
      *  status returned here just signals whether or not the method has succeeded.
-     *  Use isQuerySucceeded() / getCompletionStatus() methods to check the final
-     *  query status.
+     *  Use isQuerySucceeded() / getCompletionStatus() methods of this class to
+     *  check the final query status.
      */
     status_t completeQuery(status_t status);
 
@@ -132,19 +137,26 @@
      * class has been executed.
      */
     inline bool isQuerySucceeded() const {
-        return mQueryStatus == NO_ERROR && mReplyStatus != 0;
+        return mQueryDeliveryStatus == NO_ERROR && mReplyStatus != 0;
     }
 
     /* Gets final completion status of the query.
      * Note that this method must be called after completeQuery() method of this
      * class has been executed.
-     *  NO_ERROR on success, or an appropriate error status on failure.
+     * Return:
+     *  NO_ERROR if query has succeeded, or an appropriate error status on query
+     *  failure.
      */
     inline status_t getCompletionStatus() const {
-        if (isQuerySucceeded()) {
-            return NO_ERROR;
+        if (mQueryDeliveryStatus == NO_ERROR) {
+            if (mReplyStatus) {
+                return NO_ERROR;
+            } else {
+                return EINVAL;
+            }
+        } else {
+            return mQueryDeliveryStatus;
         }
-        return (mQueryStatus != NO_ERROR) ? mQueryStatus : EINVAL;
     }
 
     /****************************************************************************
@@ -154,8 +166,8 @@
 public:
     /* Query string. */
     char*       mQuery;
-    /* Query status. */
-    status_t    mQueryStatus;
+    /* Query delivery status. */
+    status_t    mQueryDeliveryStatus;
     /* Reply buffer */
     char*       mReplyBuffer;
     /* Reply data (past 'ok'/'ko'). If NULL, there were no data in reply. */
@@ -208,7 +220,7 @@
      *      the 'factory' service, while connection with parameters means
      *      connection to an 'emulated camera' service, where camera is identified
      *      by one of the connection parameters. So, passing NULL, or an empty
-     *      string to this method will establish connection with a 'factory'
+     *      string to this method will establish a connection with the 'factory'
      *      service, while not empty string passed here will establish connection
      *      with an 'emulated camera' service. Parameters defining the emulated
      *      camera must be formatted as such:
@@ -216,10 +228,10 @@
      *          "name=<device name> [inp_channel=<input channel #>]",
      *
      *      where 'device name' is a required parameter defining name of the
-     *      camera device, 'input channel' is an optional parameter (positive
-     *      integer), defining input channel to use on the camera device. Note
-     *      that device name passed here must have been previously obtained from
-     *      the factory service.
+     *      camera device, and 'input channel' is an optional parameter (positive
+     *      integer), defining the input channel to use on the camera device.
+     *      Note that device name passed here must have been previously obtained
+     *      from the factory service using 'list' query.
      * Return:
      *  NO_ERROR on success, or an appropriate error status.
      */
@@ -259,11 +271,11 @@
      * Return:
      *  NO_ERROR on success, or an appropriate error status on failure. Note that
      *  status returned here is not the final query status. Use isQuerySucceeded(),
-     *  or getCompletionStatus() method on the query to see if it has succeeded.
-     *  However, if this method returns a failure, it means that the query has
-     *  failed, and there is no guarantee that its data members are properly
-     *  initialized (except for the 'mQueryStatus', which is always in the
-     *  proper state).
+     *  or getCompletionStatus() method on the query object to see if it has
+     *  succeeded. However, if this method returns a failure, it means that the
+     *  query has failed, and there is no guarantee that its data members are
+     *  properly initialized (except for the 'mQueryDeliveryStatus', which is
+     *  always in the proper state).
      */
     virtual status_t doQuery(QemuQuery* query);
 
@@ -300,25 +312,26 @@
 public:
     /* Lists camera devices connected to the host.
      * Param:
-     *  list - Upon success contains list of cameras connected to the host. The
+     *  list - Upon success contains a list of cameras connected to the host. The
      *      list returned here is represented as a string, containing multiple
-     *      lines, separated with '\n', where each line represents a camera. Each
+     *      lines separated with '\n', where each line represents a camera. Each
      *      camera line is formatted as such:
      *
      *          "name=<device name> channel=<num> pix=<num> framedims=<dimensions>\n"
      *
      *      Where:
-     *      - 'name' is the name of camera device attached to the host. This name
-     *        must be used for subsequent connection to the 'emulated camera'
+     *      - 'name' is the name of the camera device attached to the host. This
+     *        name must be used for subsequent connection to the 'emulated camera'
      *        service for that camera.
      *      - 'channel' - input channel number (positive int) to use to communicate
      *        with the camera.
-     *      - 'pix' - pixel format (a "fourcc" int), chosen for the video frames.
+     *      - 'pix' - pixel format (a "fourcc" uint), chosen for the video frames
+     *        by the camera service.
      *      - 'framedims' contains a list of frame dimensions supported by the
-     *        camera. Each etry in the list is in form '<width>x<height>', where
-     *        'width' and 'height' are numeric values for width and height of a
-     *        supported frame dimension. Entries in this list are separated with
-     *        ','.
+     *        camera for the chosen pixel format. Each etry in the list is in form
+     *        '<width>x<height>', where 'width' and 'height' are numeric values
+     *        for width and height of a supported frame dimension. Entries in
+     *        this list are separated with ',' with no spaces between the entries.
      * Return:
      *  NO_ERROR on success, or an appropriate error status on failure.
      */
diff --git a/tools/emulator/system/camera/media_profiles.xml b/tools/emulator/system/camera/media_profiles.xml
new file mode 100644
index 0000000..ae1ce88
--- /dev/null
+++ b/tools/emulator/system/camera/media_profiles.xml
@@ -0,0 +1,400 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<!DOCTYPE MediaSettings [
+<!ELEMENT MediaSettings (CamcorderProfiles,
+                         EncoderOutputFileFormat+,
+                         VideoEncoderCap+,
+                         AudioEncoderCap+,
+                         VideoDecoderCap,
+                         AudioDecoderCap)>
+<!ELEMENT CamcorderProfiles (EncoderProfile+, ImageEncoding+, ImageDecoding, Camera)>
+<!ELEMENT EncoderProfile (Video, Audio)>
+<!ATTLIST EncoderProfile quality (high|low) #REQUIRED>
+<!ATTLIST EncoderProfile fileFormat (mp4|3gp) #REQUIRED>
+<!ATTLIST EncoderProfile duration (30|60) #REQUIRED>
+<!ATTLIST EncoderProfile cameraId (0|1) #REQUIRED>
+<!ELEMENT Video EMPTY>
+<!ATTLIST Video codec (h264|h263|m4v) #REQUIRED>
+<!ATTLIST Video bitRate CDATA #REQUIRED>
+<!ATTLIST Video width CDATA #REQUIRED>
+<!ATTLIST Video height CDATA #REQUIRED>
+<!ATTLIST Video frameRate CDATA #REQUIRED>
+<!ELEMENT Audio EMPTY>
+<!ATTLIST Audio codec (amrnb|amrwb|aac) #REQUIRED>
+<!ATTLIST Audio bitRate CDATA #REQUIRED>
+<!ATTLIST Audio sampleRate CDATA #REQUIRED>
+<!ATTLIST Audio channels (1|2) #REQUIRED>
+<!ELEMENT ImageEncoding EMPTY>
+<!ATTLIST ImageEncoding quality (90|80|70|60|50|40) #REQUIRED>
+<!ELEMENT ImageDecoding EMPTY>
+<!ATTLIST ImageDecoding memCap CDATA #REQUIRED>
+<!ELEMENT Camera EMPTY>
+<!ELEMENT EncoderOutputFileFormat EMPTY>
+<!ATTLIST EncoderOutputFileFormat name (mp4|3gp) #REQUIRED>
+<!ELEMENT VideoEncoderCap EMPTY>
+<!ATTLIST VideoEncoderCap name (h264|h263|m4v|wmv) #REQUIRED>
+<!ATTLIST VideoEncoderCap enabled (true|false) #REQUIRED>
+<!ATTLIST VideoEncoderCap minBitRate CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap maxBitRate CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap minFrameWidth CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap maxFrameWidth CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap minFrameHeight CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap maxFrameHeight CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap minFrameRate CDATA #REQUIRED>
+<!ATTLIST VideoEncoderCap maxFrameRate CDATA #REQUIRED>
+<!ELEMENT AudioEncoderCap EMPTY>
+<!ATTLIST AudioEncoderCap name (amrnb|amrwb|aac|wma) #REQUIRED>
+<!ATTLIST AudioEncoderCap enabled (true|false) #REQUIRED>
+<!ATTLIST AudioEncoderCap minBitRate CDATA #REQUIRED>
+<!ATTLIST AudioEncoderCap maxBitRate CDATA #REQUIRED>
+<!ATTLIST AudioEncoderCap minSampleRate CDATA #REQUIRED>
+<!ATTLIST AudioEncoderCap maxSampleRate CDATA #REQUIRED>
+<!ATTLIST AudioEncoderCap minChannels (1|2) #REQUIRED>
+<!ATTLIST AudioEncoderCap maxChannels (1|2) #REQUIRED>
+<!ELEMENT VideoDecoderCap EMPTY>
+<!ATTLIST VideoDecoderCap name (wmv) #REQUIRED>
+<!ATTLIST VideoDecoderCap enabled (true|false) #REQUIRED>
+<!ELEMENT AudioDecoderCap EMPTY>
+<!ATTLIST AudioDecoderCap name (wma) #REQUIRED>
+<!ATTLIST AudioDecoderCap enabled (true|false) #REQUIRED>
+<!ELEMENT VideoEditorCap EMPTY>
+<!ATTLIST VideoEditorCap maxInputFrameWidth CDATA #REQUIRED>
+<!ATTLIST VideoEditorCap maxInputFrameHeight CDATA #REQUIRED>
+<!ATTLIST VideoEditorCap maxOutputFrameWidth CDATA #REQUIRED>
+<!ATTLIST VideoEditorCap maxOutputFrameHeight CDATA #REQUIRED>
+<!ELEMENT ExportVideoProfile EMPTY>
+<!ATTLIST ExportVideoProfile name (h264|h263|m4v) #REQUIRED>
+<!ATTLIST ExportVideoProfile profile CDATA #REQUIRED>
+<!ATTLIST ExportVideoProfile level CDATA #REQUIRED>
+]>
+<!--
+     This file is used to declare the multimedia profiles and capabilities
+     on an android-powered device.
+-->
+<MediaSettings>
+    <!-- Each camcorder profile defines a set of predefined configuration parameters -->
+    <CamcorderProfiles cameraId="0">
+
+        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+            <Video codec="m4v"
+                   bitRate="128000"
+                   width="320"
+                   height="240"
+                   frameRate="15" />
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+            <Video codec="h264"
+                   bitRate="192000"
+                   width="176"
+                   height="144"
+                   frameRate="30" />
+            <!-- audio setting is ignored -->
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <ImageEncoding quality="95" />
+        <ImageEncoding quality="80" />
+        <ImageEncoding quality="70" />
+        <ImageDecoding memCap="20000000" />
+
+    </CamcorderProfiles>
+
+    <CamcorderProfiles cameraId="1">
+
+        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+            <Video codec="m4v"
+                   bitRate="128000"
+                   width="320"
+                   height="240"
+                   frameRate="15" />
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+            <Video codec="h264"
+                   bitRate="192000"
+                   width="176"
+                   height="144"
+                   frameRate="30" />
+            <!-- audio setting is ignored -->
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <ImageEncoding quality="95" />
+        <ImageEncoding quality="80" />
+        <ImageEncoding quality="70" />
+        <ImageDecoding memCap="20000000" />
+
+    </CamcorderProfiles>
+
+    <CamcorderProfiles cameraId="2">
+
+        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+            <Video codec="m4v"
+                   bitRate="128000"
+                   width="320"
+                   height="240"
+                   frameRate="15" />
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+            <Video codec="h264"
+                   bitRate="192000"
+                   width="176"
+                   height="144"
+                   frameRate="30" />
+            <!-- audio setting is ignored -->
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <ImageEncoding quality="95" />
+        <ImageEncoding quality="80" />
+        <ImageEncoding quality="70" />
+        <ImageDecoding memCap="20000000" />
+
+    </CamcorderProfiles>
+
+    <CamcorderProfiles cameraId="3">
+
+        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+            <Video codec="m4v"
+                   bitRate="128000"
+                   width="320"
+                   height="240"
+                   frameRate="15" />
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+            <Video codec="h264"
+                   bitRate="192000"
+                   width="176"
+                   height="144"
+                   frameRate="30" />
+            <!-- audio setting is ignored -->
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <ImageEncoding quality="95" />
+        <ImageEncoding quality="80" />
+        <ImageEncoding quality="70" />
+        <ImageDecoding memCap="20000000" />
+
+    </CamcorderProfiles>
+
+    <CamcorderProfiles cameraId="4">
+
+        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+            <Video codec="m4v"
+                   bitRate="128000"
+                   width="320"
+                   height="240"
+                   frameRate="15" />
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+            <Video codec="h264"
+                   bitRate="192000"
+                   width="176"
+                   height="144"
+                   frameRate="30" />
+            <!-- audio setting is ignored -->
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <ImageEncoding quality="95" />
+        <ImageEncoding quality="80" />
+        <ImageEncoding quality="70" />
+        <ImageDecoding memCap="20000000" />
+
+    </CamcorderProfiles>
+
+    <CamcorderProfiles cameraId="5">
+
+        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+            <Video codec="m4v"
+                   bitRate="128000"
+                   width="320"
+                   height="240"
+                   frameRate="15" />
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+            <Video codec="h264"
+                   bitRate="192000"
+                   width="176"
+                   height="144"
+                   frameRate="30" />
+            <!-- audio setting is ignored -->
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <ImageEncoding quality="95" />
+        <ImageEncoding quality="80" />
+        <ImageEncoding quality="70" />
+        <ImageDecoding memCap="20000000" />
+
+    </CamcorderProfiles>
+
+    <CamcorderProfiles cameraId="6">
+
+        <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
+            <Video codec="m4v"
+                   bitRate="128000"
+                   width="320"
+                   height="240"
+                   frameRate="15" />
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
+            <Video codec="h264"
+                   bitRate="192000"
+                   width="176"
+                   height="144"
+                   frameRate="30" />
+            <!-- audio setting is ignored -->
+            <Audio codec="amrnb"
+                   bitRate="12200"
+                   sampleRate="8000"
+                   channels="1" />
+        </EncoderProfile>
+
+        <ImageEncoding quality="95" />
+        <ImageEncoding quality="80" />
+        <ImageEncoding quality="70" />
+        <ImageDecoding memCap="20000000" />
+
+    </CamcorderProfiles>
+
+    <EncoderOutputFileFormat name="3gp" />
+    <EncoderOutputFileFormat name="mp4" />
+
+    <!--
+         If a codec is not enabled, it is invisible to the applications
+         In other words, the applications won't be able to use the codec
+         or query the capabilities of the codec at all if it is disabled
+    -->
+    <VideoEncoderCap name="h264" enabled="true"
+        minBitRate="64000" maxBitRate="192000"
+        minFrameWidth="176" maxFrameWidth="320"
+        minFrameHeight="144" maxFrameHeight="240"
+        minFrameRate="15" maxFrameRate="30" />
+
+    <VideoEncoderCap name="h263" enabled="true"
+        minBitRate="64000" maxBitRate="192000"
+        minFrameWidth="176" maxFrameWidth="320"
+        minFrameHeight="144" maxFrameHeight="240"
+        minFrameRate="15" maxFrameRate="30" />
+
+    <VideoEncoderCap name="m4v" enabled="true"
+        minBitRate="64000" maxBitRate="192000"
+        minFrameWidth="176" maxFrameWidth="320"
+        minFrameHeight="144" maxFrameHeight="240"
+        minFrameRate="15" maxFrameRate="30" />
+
+    <AudioEncoderCap name="aac" enabled="true"
+        minBitRate="8000" maxBitRate="96000"
+        minSampleRate="8000" maxSampleRate="48000"
+        minChannels="1" maxChannels="1" />
+
+    <AudioEncoderCap name="amrwb" enabled="true"
+        minBitRate="6600" maxBitRate="23050"
+        minSampleRate="16000" maxSampleRate="16000"
+        minChannels="1" maxChannels="1" />
+
+    <AudioEncoderCap name="amrnb" enabled="true"
+        minBitRate="5525" maxBitRate="12200"
+        minSampleRate="8000" maxSampleRate="8000"
+        minChannels="1" maxChannels="1" />
+
+    <!--
+        FIXME:
+        We do not check decoder capabilities at present
+        At present, we only check whether windows media is visible
+        for TEST applications. For other applications, we do
+        not perform any checks at all.
+    -->
+    <VideoDecoderCap name="wmv" enabled="false"/>
+    <AudioDecoderCap name="wma" enabled="false"/>
+    <VideoEditorCap  maxInputFrameWidth="320"
+        maxInputFrameHeight="240" maxOutputFrameWidth="320"
+        maxOutputFrameHeight="240"/>
+    <!--
+        The VideoEditor Export codec profile and level values
+        correspond to the values in OMX_Video.h.
+        E.g. for h264, profile value 1 means OMX_VIDEO_AVCProfileBaseline
+        and  level 4096 means OMX_VIDEO_AVCLevel41.
+        Please note that the values are in decimal.
+        These values are for video encoder.
+    -->
+    <!--
+      Codec = h.264, Baseline profile, level 4.1
+    -->
+    <ExportVideoProfile name="h264" profile= "1" level="512"/>
+    <!--
+      Codec = h.263, Baseline profile, level 0
+    -->
+    <ExportVideoProfile name="h263" profile= "1" level="1"/>
+    <!--
+      Codec = mpeg4, Simple profile, level 3
+    -->
+    <ExportVideoProfile name="m4v" profile= "1" level="16"/>
+</MediaSettings>