merge from open-source master
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..e3959d6
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := user
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := Bluetooth
+
+LOCAL_JAVA_LIBRARIES := javax.obex
+
+LOCAL_CERTIFICATE := shared
+
+include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..89c24f2
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.bluetooth" >
+    <!-- Allows access to the Bluetooth Share Manager -->
+    <permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE"
+        android:label="@string/permlab_bluetoothShareManager"
+        android:description="@string/permdesc_bluetoothShareManager"
+        android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <application android:icon="@drawable/bt_share"
+        android:label="@string/app_name">
+        <uses-library android:name="javax.obex" />
+        <provider android:name=".opp.BluetoothOppProvider"
+            android:authorities="com.android.bluetooth.opp">
+            <path-permission
+                    android:path="/btopp"
+                    android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />
+        </provider>
+        <service android:name=".opp.BluetoothOppService"
+            android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />
+        <receiver android:name=".opp.BluetoothOppReceiver">
+            <intent-filter>
+                <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+        <activity android:name=".opp.BluetoothOppLauncherActivity"
+            android:theme="@android:style/Theme.Dialog" android:label="@string/bt_share_picker_label">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="image/*" />
+                <data android:mimeType="video/*" />
+                <data android:mimeType="audio/*" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND_MULTIPLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="image/*" />
+                <data android:mimeType="video/*" />
+                <data android:mimeType="x-mixmedia/*" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.btopp.intent.action.OPEN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.item/vnd.android.btopp" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".opp.BluetoothOppBtEnableActivity"
+                  android:excludeFromRecents="true"
+                  android:theme="@*android:style/Theme.Dialog.Alert">
+        </activity>
+        <activity android:name=".opp.BluetoothOppBtErrorActivity"
+                  android:excludeFromRecents="true"
+                  android:theme="@*android:style/Theme.Dialog.Alert">
+        </activity>
+        <activity android:name=".opp.BluetoothOppBtEnablingActivity"
+                  android:excludeFromRecents="true"
+                  android:theme="@*android:style/Theme.Dialog.Alert">
+        </activity>
+        <activity android:name=".opp.BluetoothOppIncomingFileConfirmActivity"
+                  android:excludeFromRecents="true"
+                  android:theme="@*android:style/Theme.Dialog.Alert">
+        </activity>
+        <activity android:name=".opp.BluetoothOppTransferActivity"
+                  android:excludeFromRecents="true"
+                  android:theme="@*android:style/Theme.Dialog.Alert">
+        </activity>
+        <activity android:name=".opp.BluetoothOppLiveFolder"
+            android:icon="@drawable/ic_launcher_folder_bluetooth"
+            android:label="@string/btopp_live_folder">
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".pbap.BluetoothPbapActivity"
+            android:excludeFromRecents="true"
+            android:theme="@*android:style/Theme.Dialog.Alert">
+            <intent-filter>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <service android:name=".pbap.BluetoothPbapService" >
+        </service>
+        <receiver android:name=".pbap.BluetoothPbapReceiver">
+            <intent-filter>
+                <action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/AndroidManifest_test.xml b/AndroidManifest_test.xml
new file mode 100644
index 0000000..b6d6568
--- /dev/null
+++ b/AndroidManifest_test.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.bluetooth" >
+    <!-- Allows access to the Bluetooth Share Manager -->
+    <permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE"
+        android:label="@string/permlab_bluetoothShareManager"
+        android:description="@string/permdesc_bluetoothShareManager"
+        android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <application android:icon="@drawable/bt_share"
+        android:label="@string/app_name">
+        <uses-library android:name="javax.obex" />
+        <activity android:name=".opp.TestActivity" android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <provider android:name=".opp.BluetoothOppProvider"
+            android:authorities="com.android.bluetooth.opp">
+            <path-permission
+                    android:path="/btopp"
+                    android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />
+        </provider>
+        <service android:name=".opp.BluetoothOppService"
+            android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />
+        <receiver android:name=".opp.BluetoothOppReceiver">
+            <intent-filter>
+                <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+        <activity android:name=".opp.BluetoothOppLauncherActivity"
+            android:theme="@android:style/Theme.Dialog" android:label="@string/bt_share_picker_label">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="image/*" />
+                <data android:mimeType="video/*" />
+                <data android:mimeType="audio/*" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND_MULTIPLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="image/*" />
+                <data android:mimeType="video/*" />
+                <data android:mimeType="x-mixmedia/*" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.btopp.intent.action.OPEN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.item/vnd.android.btopp" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".opp.BluetoothOppBtEnableActivity">
+        </activity>
+        <activity android:name=".opp.BluetoothOppBtErrorActivity">
+        </activity>
+        <activity android:name=".opp.BluetoothOppBtEnablingActivity">
+            android:theme="@android:style/Theme.Dialog"> </activity>
+        <activity android:name=".opp.BluetoothOppIncomingFileConfirmActivity">
+        </activity>
+        <activity android:name=".opp.BluetoothOppTransferActivity">
+        </activity>
+        <activity android:name=".opp.BluetoothOppLiveFolder"
+            android:label="@string/btopp_live_folder">
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".pbap.BluetoothPbapActivity"
+            android:label=" "
+            android:theme="@*android:style/Theme.Dialog.Alert">
+            <intent-filter>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <service android:name=".pbap.BluetoothPbapService" >
+            <action android:name="android.bluetooth.IBluetoothPbap"/>
+        </service>
+        <receiver android:name=".pbap.BluetoothPbapReceiver">
+            <intent-filter>
+                <action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/res/drawable-hdpi/bt_incomming_file_notification.png b/res/drawable-hdpi/bt_incomming_file_notification.png
new file mode 100755
index 0000000..2df6960
--- /dev/null
+++ b/res/drawable-hdpi/bt_incomming_file_notification.png
Binary files differ
diff --git a/res/drawable-hdpi/bt_share.png b/res/drawable-hdpi/bt_share.png
new file mode 100644
index 0000000..bf35e9c
--- /dev/null
+++ b/res/drawable-hdpi/bt_share.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_folder_bluetooth.png b/res/drawable-hdpi/ic_launcher_folder_bluetooth.png
new file mode 100644
index 0000000..78c56b7
--- /dev/null
+++ b/res/drawable-hdpi/ic_launcher_folder_bluetooth.png
Binary files differ
diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..bf35e9c
--- /dev/null
+++ b/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/res/drawable-mdpi/bt_incomming_file_notification.png b/res/drawable-mdpi/bt_incomming_file_notification.png
new file mode 100644
index 0000000..8d3f555
--- /dev/null
+++ b/res/drawable-mdpi/bt_incomming_file_notification.png
Binary files differ
diff --git a/res/drawable-mdpi/bt_share.png b/res/drawable-mdpi/bt_share.png
new file mode 100644
index 0000000..7f9b9ce
--- /dev/null
+++ b/res/drawable-mdpi/bt_share.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_bluetooth.png b/res/drawable-mdpi/ic_launcher_folder_bluetooth.png
new file mode 100644
index 0000000..c22315d
--- /dev/null
+++ b/res/drawable-mdpi/ic_launcher_folder_bluetooth.png
Binary files differ
diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..7f9b9ce
--- /dev/null
+++ b/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/res/layout/access.xml b/res/layout/access.xml
new file mode 100644
index 0000000..5905b2c
--- /dev/null
+++ b/res/layout/access.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="fill_parent"
+    android:layout_width="fill_parent">
+
+    <LinearLayout
+        android:layout_height="fill_parent"
+        android:layout_width="fill_parent"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/message"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            android:gravity="center_horizontal"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+        <CheckBox android:id="@+id/alwaysallowed"
+            style="?android:attr/textAppearanceMedium"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dip"
+            android:text="@string/alwaysallowed" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/auth.xml b/res/layout/auth.xml
new file mode 100644
index 0000000..e5c079a
--- /dev/null
+++ b/res/layout/auth.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="fill_parent"
+    android:layout_width="fill_parent">
+
+    <LinearLayout
+    android:layout_height="fill_parent"
+    android:layout_width="fill_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:gravity="center_horizontal"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <EditText
+        android:id="@+id/text"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dip"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:singleLine="true" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/bt_enabling_progress.xml b/res/layout/bt_enabling_progress.xml
new file mode 100644
index 0000000..fa09d4e
--- /dev/null
+++ b/res/layout/bt_enabling_progress.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="fill_parent"
+    android:layout_width="fill_parent">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
+
+        <ProgressBar android:id="@+android:id/progress"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+        <TextView
+            android:id="@+id/progress_info"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            android:layout_gravity="center_vertical"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+     </LinearLayout>
+
+ </ScrollView>
diff --git a/res/layout/confirm_dialog.xml b/res/layout/confirm_dialog.xml
new file mode 100644
index 0000000..20c3b27
--- /dev/null
+++ b/res/layout/confirm_dialog.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="fill_parent"
+    android:layout_width="fill_parent">
+
+    <LinearLayout
+        android:layout_height="fill_parent"
+        android:layout_width="fill_parent"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/content"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            android:gravity="center_horizontal"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </LinearLayout>
+
+</ScrollView>
+
diff --git a/res/layout/file_transfer.xml b/res/layout/file_transfer.xml
new file mode 100644
index 0000000..55a75ad
--- /dev/null
+++ b/res/layout/file_transfer.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="fill_parent"
+    android:layout_width="fill_parent">
+
+    <LinearLayout
+        android:layout_height="fill_parent"
+        android:layout_width="fill_parent"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/line1_view"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/download_line1" />
+
+        <TextView
+            android:id="@+id/line2_view"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/download_line2" />
+
+        <TextView
+            android:id="@+id/line3_view"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/download_line3" />
+
+        <TextView
+            android:id="@+id/line4_view"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:text="@string/download_line4" />
+
+        <TextView
+            android:id="@+id/line5_view"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:text="@string/download_line5" />
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            >
+
+            <TextView
+                android:id="@+id/progress_percent"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_marginLeft="20dip"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:gravity="left" />
+
+            <ProgressBar android:id="@+id/progress_transfer"
+                style="?android:attr/progressBarStyleHorizontal"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="10dip"
+                android:layout_marginRight="20dip"
+                android:layout_width="0dip"
+                android:layout_weight="1"
+                android:max="100"
+                android:progress="100" />
+        </LinearLayout>
+
+    </LinearLayout>
+
+ </ScrollView>
\ No newline at end of file
diff --git a/res/layout/main.xml b/res/layout/main.xml
new file mode 100644
index 0000000..b8e4b03
--- /dev/null
+++ b/res/layout/main.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="@string/hello"
+    />
+
+</LinearLayout>
diff --git a/res/layout/status_bar_ongoing_event_progress_bar.xml b/res/layout/status_bar_ongoing_event_progress_bar.xml
new file mode 100644
index 0000000..6af8792
--- /dev/null
+++ b/res/layout/status_bar_ongoing_event_progress_bar.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical"
+    android:background="@android:drawable/status_bar_item_app_background"
+    >
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:orientation="horizontal"
+        >
+        
+        <LinearLayout
+            android:layout_width="40dp"
+            android:layout_height="fill_parent"
+            android:orientation="vertical"
+            android:paddingTop="8dp"
+            android:focusable="true"
+            android:clickable="true"
+            >
+            <com.android.server.status.AnimatedImageView 
+                android:id="@+id/appIcon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:src="@android:drawable/sym_def_app_icon"
+                />
+            <TextView android:id="@+id/progress_text" 
+                android:layout_width="wrap_content" 
+                android:layout_height="wrap_content"
+                android:textColor="#ff000000"
+                android:singleLine="true"
+                android:textSize="14sp"
+                android:layout_gravity="center_horizontal"
+                />
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:orientation="vertical"
+            android:focusable="true"
+            android:clickable="true"
+            >
+            <LinearLayout
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:focusable="true"
+                android:clickable="true"
+                android:layout_alignParentTop="true"
+                android:paddingTop="10dp"
+                >
+                <TextView android:id="@+id/description"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textColor="#ff000000"
+                    android:singleLine="true"
+                    android:textSize="14sp"
+                    android:paddingLeft="5dp"
+                    />
+            </LinearLayout>
+            <ProgressBar android:id="@+id/progress_bar"
+                style="?android:attr/progressBarStyleHorizontal"
+                android:layout_width="fill_parent" 
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:paddingBottom="8dp"
+                android:paddingRight="25dp"
+                />
+        </RelativeLayout>
+    </LinearLayout>
+        
+    <com.android.server.status.AnimatedImageView 
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:src="@android:drawable/divider_horizontal_bright"
+        />
+
+</LinearLayout>
+
diff --git a/res/layout/testactivity_main.xml b/res/layout/testactivity_main.xml
new file mode 100644
index 0000000..bf9fa11
--- /dev/null
+++ b/res/layout/testactivity_main.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content">
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="left">
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="3dip"
+            android:text="@string/insert_record" />
+        <EditText android:id="@+id/insert_text"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="left">
+        <EditText android:id="@+id/media_text"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content" />
+        <EditText android:id="@+id/address_text"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content" />
+        <Button android:id="@+id/insert_record"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="3dip"
+            android:text="@string/ok_button" />
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="3dip"
+            android:text="@string/delete_record" />
+        <EditText android:id="@+id/delete_text"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content" />
+        <Button android:id="@+id/delete_record"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="3dip"
+            android:text="@string/ok_button" />
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="3dip"
+            android:text="@string/update_record" />
+        <EditText android:id="@+id/update_text"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content" />
+        <Button android:id="@+id/update_record"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="3dip"
+            android:text="@string/ok_button" />
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="3dip"
+            android:text="@string/ack_record" />
+        <EditText android:id="@+id/ack_text"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="wrap_content" />
+        <Button android:id="@+id/ack_record"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="3dip"
+            android:text="@string/ok_button" />
+    </LinearLayout>
+
+    <Button android:id="@+id/deleteAll_record"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/deleteAll_record"></Button>
+
+    <Button android:id="@+id/start_server"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/start_server"></Button>
+
+    <Button android:id="@+id/notify_server"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/notify_server"></Button>
+</LinearLayout>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
new file mode 100644
index 0000000..a8b800b
--- /dev/null
+++ b/res/values-cs/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Získat přístup ke správci stahování."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Umožňuje aplikaci přístup ke Správci sdílení Bluetooth a jeho využívání k přenosu souborů."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Neznámé zařízení"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Režim V letadle"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Režim V letadle nedovoluje používat rozhraní Bluetooth."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Chcete-li používat služby Bluetooth, musíte Bluetooth nejprve zapnout."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Zapnout Bluetooth?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Zrušit"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Zapnout"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Přenos souborů"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"Zařízení „%1$s“ se vám pokouší odeslat soubor %2$s (%3$s). "\n\n" Chcete soubor přijmout?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Odmítnout"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Přijmout"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"Ok"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Časový limit pro přijetí souboru ze zařízení „%1$s“ vypršel"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Sdílení Bluetooth: Příchozí soubor"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Potvrďte prosím, zda tento soubor chcete přijmout."</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Potvrďte prosím příjem příchozího souboru z jiného zařízení"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Sdílení Bluetooth: Příjem souboru %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Sdílení Bluetooth: Soubor %1$s byl přijat"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"Dokončeno 100%"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Sdílení Bluetooth: Soubor %1$s nebyl přijat"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Sdílení Bluetooth: Odesílání souboru %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Sdílení Bluetooth: Odesílání souboru"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Sdílení Bluetooth: Soubor %1$s byl odeslán"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"Dokončeno 100 %"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Sdílení Bluetooth: Soubor %1$s nebyl odeslán"</string>
+    <string name="download_title" msgid="3353228219772092586">"Přenos souborů"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Z: „%1$s“"</string>
+    <string name="download_line2" msgid="65085079456902842">"Soubor: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Velikost souboru: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Přijímání souboru..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Zastavit"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Skrýt"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Soubor nebyl přijat"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Soubor: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Příčina selhání: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Soubor byl přijat"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Otevřít"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Do: „%1$s“"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Typ souboru: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Odesílání souboru..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Soubor byl odeslán"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Soubor nebyl odeslán do zařízení „%1$s“."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Soubor: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Zkuste to znovu"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Zavřít"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Neznámý soubor"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Žádná aplikace nedokáže s tímto typem souboru pracovat. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Soubor neexistuje"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Soubor neexistuje! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Čekejte prosím..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Zapínání rozhraní Bluetooth..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Proběhne přijímání souboru. Průběh můžete sledovat na panelu Oznámení."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Soubor nebude přijat."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Přijímání souboru ze zařízení „%1$s“ bylo zastaveno"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Odesílání souboru do zařízení „%1$s“"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Odesílání %1$s souborů zařízení „%2$s“"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Odesílání souboru do zařízení „%1$s“ bylo zastaveno"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Odesílání %1$s souborů do zařízení „%2$s“ bylo zastaveno"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Na kartě SD není pro přijetí souboru ze zařízení „%1$s“ dost místa."</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Požadované místo v paměti: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"Zařízení „%1$s“ se vám pokusilo odeslat několik souborů (celkem %2$s)."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Toto zařízení může přijímat soubory pouze jednotlivě. Požádejte odesílatele, aby odeslal zbývající soubory (celkem %1$s) jednotlivě."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"Zařízení „%1$s“ je nedostupné."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Spárování se zařízením „%1$s“ není možné."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Spojení bylo přerušeno."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"Zařízení „%1$s“ nepodporuje přenos souborů."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"Zařízení „%1$s“ nemůže %2$s přijmout"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"Zařízení „%1$s“ odmítlo soubor přijmout."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Soubor nelze odeslat."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Soubor nelze přijmout."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Paměťová karta byla uzamčena. Chcete-li ukládat soubory, je potřeba kartu odemknout."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Probíhá přenos souboru do jiného zařízení. Čekejte prosím..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Rozhraní Bluetooth je zaneprázdněno. Zkuste vypnout jinou funkci Bluetooth a akci opakovat."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Přenos souborů ještě nebyl zahájen"</string>
+    <string name="status_running" msgid="2695810336448055064">"Probíhá přenos souborů"</string>
+    <string name="status_success" msgid="2268261336330060663">"Přenos souborů byl úspěšně dokončen"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Telefon není s tímto typem obsahu kompatibilní"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Tento přenos byl cílovým zařízením zakázán"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Tento přenos byl uživatelem zrušen"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problém s úložištěm"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Žádná karta SD není dostupná. Chcete-li přenášené soubory uložit, vložte kartu SD."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Selhání připojení"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Požadavek není možné správně zpracovat"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Neznámá chyba"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth – přijaté soubory"</string>
+</resources>
diff --git a/res/values-cs/strings_pbap.xml b/res/values-cs/strings_pbap.xml
new file mode 100644
index 0000000..aac3bce
--- /dev/null
+++ b/res/values-cs/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"Zařízení %1$s žádá o přístup k vašim kontaktům a historii hovorů. Povolit zařízení %2$s přístup?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Přístup do telefonního seznamu"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Zadejte klíč relace pro zařízení %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Je požadován klíč relace Bluetooth"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Časový limit pro přijetí spojení se zařízením „%1$s“ vypršel"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Časový limit zadání klíče relace pro %1$s vypršel"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Přenos Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Nepodařilo se aktivovat sdílení adresáře kontaktů se zařízením"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"Zařízení %1$s bylo připojeno k vašemu telefonu."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"Zařízení %1$s bylo od vašeho telefonu odpojeno."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Požadavek přístupu do telefonního seznamu"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Požadavek PBAP"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Povolit zařízení %1$s přístup do telefonního seznamu"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Požadavek ověření Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Klíč relace"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Zadejte klíč relace pro zařízení %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Vždy povoleno?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Sada handsfree do auta"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Neznámý název"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Mé jméno"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-cs/test_strings.xml b/res/values-cs/test_strings.xml
new file mode 100644
index 0000000..94ab3c4
--- /dev/null
+++ b/res/values-cs/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Sdílení Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Vložit záznam"</string>
+    <string name="update_record" msgid="2480425402384910635">"Potvrdit záznam"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Záznam ACK"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Smazat všechny záznamy"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Smazat záznam"</string>
+    <string name="start_server" msgid="9034821924409165795">"Spustit server TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Upozornit server TCP"</string>
+</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
new file mode 100644
index 0000000..54fa758
--- /dev/null
+++ b/res/values-da/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Få adgang til downloadadministrator."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Giver programmer adgang til BluetoothShare-administratoren og til at bruge det til overførsel af filer."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Ukendt enhed"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Flytilstand"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Du kan ikke bruge Bluetooth, når telefonen er i flytilstand."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Du skal aktivere Bluetooth, før du kan bruge Bluetooth-tjenester."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Vil du slå Bluetooth til nu?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Annuller"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Slå til"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Filoverførsel"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\" vil sende %2$s (%3$s) til dig. "\n\n" Vil du modtage filen?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Afvis"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepter"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Der var timeout ved modtagelse af indgående fil fra \"%1$s\""</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Deling med Bluetooth: indgående fil"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Bekræft, at du vil modtage denne fil"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Der var en indgående fil fra en anden tjeneste. Bekræft, at du vil modtage denne fil"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Deling med Bluetooth: Modtager %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Deling med Bluetooth: Modtog %1$s"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100 % fuldført"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Deling med Bluetooth: Filen %1$s blev ikke modtaget"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Deling med Bluetooth: Sender %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Deling med Bluetooth: Sender fil"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Deling med Bluetooth: Sendt %1$s"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100 % fuldført"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Deling med Bluetooth: Filen %1$s blev ikke sendt"</string>
+    <string name="download_title" msgid="3353228219772092586">"Filoverførsel"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Fra: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Fil: % 1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Filstørrelse: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Modtager fil…"</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Stop"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Skjul"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Filen blev ikke modtaget"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Fil: % 1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Årsag til fejl: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Filen blev modtaget"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Åbn"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Til: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Filtype: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Sender fil…"</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Filen er sendt"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Filen blev ikke sendt til \"%1$s\"."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Fil: % 1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Prøv igen"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Luk"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Ukendt fil"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Der blev ikke fundet noget program til denne filtype. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Filen findes ikke."</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Filen eksisterer ikke! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Vent ..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Aktiverer Bluetooth ..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Filen modtages. Se status i meddelelsespanelet."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Filen modtages ikke."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Stoppede modtagelse af fil fra \"%1$s\""</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Sender fil til \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Sender %1$s filer til \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Stoppede afsendelse af fil til \"%1$s\""</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Stoppede afsendelse af %1$s filer til \"%2$s\""</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Der var ikke nok plads på SD-kortet til at gemme filen fra \"%1$s\""</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Nødvendig plads: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" forsøgte at sende %2$s filer til dig."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Dog kan denne enhed kun modtage én fil ad gangen. Bed afsenderen om at sende de resterende %1$s filer enkeltvis."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\" er ikke tilgængelig."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Kan ikke parre med \"%1$s\"."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Forbindelsen blev afbrudt."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\" understøtter ikke filoverførsler."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" kan ikke acceptere %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" afviste at modtage filen."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Filen kunne ikke sendes."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Filen kunne ikke modtages."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Låst hukommelseskort. Du skal låse hukommelseskortet op for at gemme filerne."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Der overføres en fil til en anden enhed. Vent et øjeblik……"</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth er optaget lige nu. Deaktiver en anden Bluetooth- funktion, og prøv igen."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Filoverførslen er ikke påbegyndt"</string>
+    <string name="status_running" msgid="2695810336448055064">"Filoverførslen er i gang"</string>
+    <string name="status_success" msgid="2268261336330060663">"Filoverførslen blev gennemført"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Telefonen kan ikke håndtere denne type indhold"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Denne overførsel er forbudt for den modtagende enhed"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Overførslen blev annulleret af brugeren"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Lagringsproblem"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Intet SD-kort. Indsæt et SD-kort for at gemme overførte filer."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Forbindelsesfejl"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Anmodningen kan ikke håndteres korrekt"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Ukendt fejl"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth modtaget"</string>
+</resources>
diff --git a/res/values-da/strings_pbap.xml b/res/values-da/strings_pbap.xml
new file mode 100644
index 0000000..d36f475
--- /dev/null
+++ b/res/values-da/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s vil gerne have adgang til dine kontakter og opkaldsoversigt. Vil du give %2$s adgang?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Adgang til telefonbog"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Indtast sessionsnøgle til %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetooth-sessionsnøgle kræves"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Der var timeout ved forbindelsen med %1$s"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Der var timeout i indgangssessionnøgle med %1$s"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Bluetooth-overførsel"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Det mislykkedes at etablere deling af telefonbog med"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s oprettede forbindelse til din telefon."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s afbrudte forbindelsen til din telefon."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Anmodning om adgang til telefonbog"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAP-anmodning"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Tillad, at %1$s får adgang"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Anmodning om Obex-godkendelse"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Sessionstast"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Indtast sessionsnøgle til %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Altid tilladt?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Bilsæt"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Ukendt navn"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Mit navn"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-da/test_strings.xml b/res/values-da/test_strings.xml
new file mode 100644
index 0000000..014d07a
--- /dev/null
+++ b/res/values-da/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Deling med Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Indsæt post"</string>
+    <string name="update_record" msgid="2480425402384910635">"Bekræft post"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Ack-post"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Slet alle poster"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Slet post"</string>
+    <string name="start_server" msgid="9034821924409165795">"Start TCP-server"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Meddel TCP-server"</string>
+</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
new file mode 100644
index 0000000..8a30931
--- /dev/null
+++ b/res/values-de/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Auf Download-Manager zugreifen"</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Die Anwendung kann auf den Bluetooth-Weiterleitungs-Manager zugreifen und diesen für die Übertragung von Dateien verwenden."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Unbekanntes Gerät"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Flugmodus"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Wenn sich das Telefon im Flugmodus befindet, können Sie Bluetooth nicht verwenden."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Damit Sie Bluetooth-Dienste nutzen können, müssen Sie Bluetooth zuerst aktivieren."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Bluetooth jetzt aktivieren?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Abbrechen"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Aktivieren"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Dateiübertragung"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\" möchte Ihnen %2$s (%3$s) senden. "\n\n" Datei annehmen?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Ablehnen"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Akzeptieren"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Die Zeit zum Empfang der eingehenden Datei von \"%1$s\" ist abgelaufen."</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-Weiterleitung: Eingehende Datei"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Bestätigen Sie, dass Sie diese Datei empfangen möchten."</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Ein anderes Gerät versucht, eine Datei zu übertragen. Bestätigen Sie, dass Sie diese Datei empfangen möchten."</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Bluetooth-Weiterleitung: empfange %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Bluetooth-Weiterleitung: %1$s empfangen"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"Zu 100 % abgeschlossen"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Bluetooth-Weiterleitung: Die Datei %1$s wurde nicht empfangen."</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Bluetooth-Weiterleitung: %1$s gesendet"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Bluetooth-Weiterleitung: Datei wird gesendet."</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Bluetooth-Weiterleitung: %1$s gesendet"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"Zu 100 % abgeschlossen"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Bluetooth-Weiterleitung: Die Datei %1$s wurde nicht gesendet."</string>
+    <string name="download_title" msgid="3353228219772092586">"Dateiübertragung"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Von: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Datei: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Dateigröße: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Datei wird heruntergeladen..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Anhalten"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Ausblenden"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Datei nicht empfangen"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Datei: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Grund für den Fehler: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Datei empfangen"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Öffnen"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"An: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Dateityp: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Datei wird gesendet..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Die Datei wurde gesendet."</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Die Datei wurde nicht an \"%1$s\" gesendet."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Datei: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Wiederholen"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Schließen"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Unbekannte Datei"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Dieser Dateityp kann von keiner Anwendung verarbeitet werden. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Die Datei ist nicht vorhanden."</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Die Datei ist nicht vorhanden! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Bitte warten..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Bluetooth wird aktiviert..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Die Datei wird empfangen. Überprüfen Sie den Fortschritt im Benachrichtigungsfenster."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Die Datei wird nicht empfangen."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Der Empfang der Datei von \"%1$s\" wurde angehalten."</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Die Datei wird an \"%1$s\" gesendet."</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"%1$s Dateien werden an \"%2$s\" gesendet."</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Die Übertragung der Datei auf \"%1$s\" wurde abgebrochen."</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Übertragung von %1$s Dateien an \"%2$s\" wurde angehalten."</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Die SD-Karte verfügt über zu wenig Speicher für die Datei von \"%1$s\"."</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Erforderlicher Speicherplatz: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" hat versucht, Ihnen %2$s Dateien zu senden."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Dieses Gerät kann jedoch nur jeweils eine Datei empfangen. Bitten Sie die andere Partei, die übrigen %1$s Dateien einzeln zu senden."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\" ist nicht verfügbar."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Kein Pairing mit \"%1$s\" möglich"</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Die Verbindung wurde unterbrochen."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\" unterstützt keine Dateiübertragungen."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" kann %2$s nicht annehmen."</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" hat die Datei abgelehnt."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Die Datei konnte nicht gesendet werden."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Die Datei konnte nicht empfangen werden."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Gesperrte Speicherkarte. Entsperren Sie die Speicherkarte, um Dateien zu speichern."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Eine große Datei wird derzeit an ein anderes Gerät übertragen. Bitte warten..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth ist derzeit nicht verfügbar. Deaktivieren Sie eine andere Bluetooth-Funktion und versuchen Sie es erneut."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Die Dateiübertragung wurde noch nicht gestartet."</string>
+    <string name="status_running" msgid="2695810336448055064">"Dateiübertragung läuft"</string>
+    <string name="status_success" msgid="2268261336330060663">"Die Dateiübertragung wurde erfolgreich abgeschlossen."</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Das Telefon kann diesen Content-Typ nicht verarbeiten."</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Diese Übertragung wird durch das Zielgerät verhindert."</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Diese Übertragung wurde vom Nutzer abgebrochen."</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Speicherproblem"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Keine SD-Karte. Legen Sie eine SD-Karte ein, um die übertragenen Dateien zu speichern."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Verbindungsfehler"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Die Anfrage kann nicht richtig verarbeitet werden."</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Unbekannter Fehler"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth empfangen"</string>
+</resources>
diff --git a/res/values-de/strings_pbap.xml b/res/values-de/strings_pbap.xml
new file mode 100644
index 0000000..9938596
--- /dev/null
+++ b/res/values-de/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth-PBAP"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s möchte auf Ihre Kontakte und Ihr Anrufprotokoll zugreifen. Möchten Sie %2$s den Zugriff gestatten?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Zugriff auf Telefonbuch"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Sitzungsschlüssel für %1$s eingeben"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetooth-Sitzungsschlüssel erforderlich"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Die Zeit zum Verbindungsaufbau mit %1$s ist abgelaufen."</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Die Zeit zur Eingabe des Sitzungsschlüssels bei %1$s ist abgelaufen."</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Bluetooth-Übertragung"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Keine Telefonbuch-Weitergabe möglich an"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s ist mit Ihrem Telefon verbunden."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s hat die Verbindung mit Ihrem Telefon getrennt."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Telefonbuch-Zugriffsanfrage"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAP-Anfrage"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Telefonbuch-Zugriff durch %1$s erlauben"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"OBEX-Authentifizierungsanfrage"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Sitzungsschlüssel"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Sitzungsschlüssel für %1$s eingeben"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Immer erlaubt?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Carkit"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Unbekannter Name"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Mein Name"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-de/test_strings.xml b/res/values-de/test_strings.xml
new file mode 100644
index 0000000..a871648
--- /dev/null
+++ b/res/values-de/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hallo Welt, Testaktivität"</string>
+    <string name="app_name" msgid="1203877025577761792">"Bluetooth-Weiterleitung"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Aufnahme einfügen"</string>
+    <string name="update_record" msgid="2480425402384910635">"Aufnahme bestätigen"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Aufnahme bestätigen"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Gesamte Aufnahme löschen"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Aufnahme löschen"</string>
+    <string name="start_server" msgid="9034821924409165795">"TCP-Server starten"</string>
+    <string name="notify_server" msgid="4369106744022969655">"TCP-Server benachrichtigen"</string>
+</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
new file mode 100644
index 0000000..c42f845
--- /dev/null
+++ b/res/values-el/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Πρόσβαση στο πρόγραμμα διαχείρισης λήψεων."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Επιτρέπει στην εφαρμογή να αποκτά πρόσβαση στο πρόγραμμα διαχείρισης BluetoothShare και να το χρησιμοποιεί για τη μεταφορά αρχείων."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Άγνωστη συσκευή"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Λειτουργία πτήσης"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Δεν μπορείτε να χρησιμοποιήσετε το Bluetooth όταν το τηλέφωνο βρίσκεται σε λειτουργία πτήσης."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Για να χρησιμοποιήσετε υπηρεσίες Bluetooth, θα πρέπει πρώτα να ενεργοποιήσετε τη λειτουργία Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Να γίνει τώρα ενεργοποίηση του Bluetooth;"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Άκυρο"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Ενεργοποίηση"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Μεταφορά αρχείου"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"Ο χρήστης της συσκευής \"%1$s\" θέλει να σας στείλει το %2$s (%3$s). "\n\n" Αποδοχή του αρχείου;"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Απόρριψη"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Αποδοχή"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"ΟΚ"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Υπήρχε χρονικό όριο για την αποδοχή του εισερχόμενου αρχείου από τη συσκευή \"%1$s\""</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Κοινή χρήση μέσω Bluetooth: Εισερχόμενο αρχείο"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Επιβεβαιώστε ότι θέλετε να λάβετε αυτό το αρχείο"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Υπάρχει ένα εισερχόμενο αρχείο από μια άλλη συσκευή, επιβεβαιώστε ότι θέλετε να λάβετε αυτό το αρχείο"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Κοινή χρήση μέσω Bluetooth: Γίνεται λήψη %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Κοινή χρήση μέσω Bluetooth: Ελήφθη %1$s"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"Ολοκληρώθηκε το 100%"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Κοινή χρήση μέσω Bluetooth: Το αρχείο %1$s δεν ελήφθη"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Κοινή χρήση μέσω Bluetooth: Αποστολή %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Κοινή χρήση μέσω Bluetooth: Αποστολή αρχείου"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Κοινή χρήση μέσω Bluetooth: Εστάλη %1$s"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"Ολοκληρώθηκε το 100%"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Κοινή χρήση μέσω Bluetooth: Το αρχείο %1$s δεν εστάλη"</string>
+    <string name="download_title" msgid="3353228219772092586">"Μεταφορά αρχείου"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Από: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Αρχείο: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Μέγεθος αρχείου: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Λήψη αρχείου..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Διακοπή"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Απόκρυψη"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Δεν ελήφθη το αρχείο"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Αρχείο: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Αιτία αποτυχίας: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"ΟΚ"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Το αρχείο ελήφθη"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Άνοιγμα"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Προς: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Τύπος αρχείου: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Αποστολή αρχείου..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Το αρχείο εστάλη"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"ΟΚ"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Το αρχείο δεν εστάλη στη συσκευή \"%1$s\"."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Αρχείο: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Προσπαθήστε ξανά"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Κλείσιμο"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"ΟΚ"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Άγνωστο αρχείο"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Δεν υπάρχει εφαρμογή για το χειρισμό αυτού του τύπου αρχείου. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Το αρχείο δεν υπάρχει"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Το αρχείο δεν υπάρχει! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Περιμένετε..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Ενεργοποίηση του Bluetooth…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Θα γίνει λήψη του αρχείου. Ελέγξτε την πρόοδο στο πλαίσιο \"Ειδοποιήσεις\"."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Δεν θα γίνει λήψη του αρχείου."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Διακόπηκε η λήψη του αρχείου από τη συσκευή \"%1$s\""</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Γίνεται αποστολή του αρχείου στη συσκευή \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Αποστολή %1$s αρχείων στη συσκευή \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Διακόπηκε η αποστολή του αρχείου στη συσκευή \"%1$s\""</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Διακόπηκε η αποστολή %1$s αρχείων στη συσκευή \"%2$s\""</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Δεν υπάρχει αρκετός χώρος στην κάρτα SD για την αποθήκευση του αρχείου από τη συσκευή \"%1$s\""</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Απαιτούμενος χώρος: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"Ο χρήστης της συσκευής \"%1$s\" επιχείρησε να σας στείλει %2$s αρχεία."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Ωστόσο, αυτή η συσκευή μπορεί να λαμβάνει μόνο ένα αρχείο τη φορά. Ζητήστε από τον άλλο χρήστη να αποστείλει ξεχωριστά τα υπολειπόμενα %1$s αρχεία."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"Η συσκευή \"%1$s\" δεν είναι διαθέσιμη."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Δεν είναι δυνατή η δημιουργία ζεύξης με τη συσκευή \"%1$s\"."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Η σύνδεση διακόπηκε."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"Η συσκευή \"%1$s\" δεν υποστηρίζει μεταφορές αρχείων."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"Δεν είναι δυνατή η αποδοχή του %2$s από τη συσκευή \"%1$s\""</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"Ο χρήστης της συσκευής \"%1$s\" απέρριψε την αποδοχή του αρχείου."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Δεν ήταν δυνατή η αποστολή του αρχείου."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Δεν ήταν δυνατή η λήψη του αρχείου."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Κλειδωμένη κάρτα μνήμης, Θα πρέπει να ξεκλειδώσετε την κάρτα μνήμης για να αποθηκεύσετε τα αρχεία."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Η μεταφορά ενός αρχείου σε μια άλλη συσκευή βρίσκεται σε εξέλιξη. Περιμένετε..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Το Bluetooth είναι απασχολημένο αυτήν τη στιγμή. Προσπαθήστε να απενεργοποιήσετε κάποια άλλη λειτουργία Bluetooth και προσπαθήστε ξανά."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Δεν έχει ξεκινήσει ακόμα η μεταφορά αρχείου"</string>
+    <string name="status_running" msgid="2695810336448055064">"Η μεταφορά του αρχείου βρίσκεται σε εξέλιξη"</string>
+    <string name="status_success" msgid="2268261336330060663">"Η μεταφορά του αρχείου ολοκληρώθηκε με επιτυχία"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Το τηλέφωνο δεν μπορεί να χειριστεί αυτόν τον τύπο περιεχομένου"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Δεν επιτρέπεται αυτή η μεταφορά από τη συσκευή προορισμού"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Αυτή η μεταφορά ακυρώθηκε από το χρήστη"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Πρόβλημα χώρου αποθήκευσης"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Δεν υπάρχει κάρτα SD card. Εισαγάγετε μια κάρτα SD για να αποθηκεύετε τα μεταφερόμενα αρχεία."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Αποτυχία σύνδεσης"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Δεν μπορεί να γίνει σωστός χειρισμός του αιτήματος"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Άγνωστο σφάλμα"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Ελήφθη μέσω Bluetooth"</string>
+</resources>
diff --git a/res/values-el/strings_pbap.xml b/res/values-el/strings_pbap.xml
new file mode 100644
index 0000000..93c879a
--- /dev/null
+++ b/res/values-el/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"Η συσκευή %1$s θέλει να αποκτήσει πρόσβαση στις επαφές και το ιστορικό κλήσεών σας. Να επιτραπεί η πρόσβαση στη συσκευή %2$s;"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Πρόσβαση στον τηλεφωνικό κατάλογο"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Πληκτρολογήστε το κλειδί της περιόδου σύνδεσης για το %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Απαιτείται κλειδί περιόδου σύνδεσης Bluetooth"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Υπήρχε χρονικό όριο για την αποδοχή της σύνδεσης με τη συσκευή %1$s"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Το χρονικό όριο του κλειδιού περιόδου σύνδεσης εισόδου με το %1$s έληξε"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Μεταφορά μέσω Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Δεν ήταν δυνατή η επιτυχής δημιουργία σύνδεσης κοινής χρήσης τηλεφωνικού καταλόγου με"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"Η συσκευή %1$s συνδέθηκε στο τηλέφωνό σας."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"Η συσκευή %1$s αποσυνδέθηκε από το τηλέφωνό σας."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Αίτημα πρόσβασης στον τηλεφωνικό κατάλογο"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Αίτημα PBAP"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Να επιτρέπεται η πρόσβαση στον τηλεφωνικό κατάλογο στη συσκευή %1$s"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Αίτημα ελέγχου ταυτότητας Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Κλειδί περιόδου σύνδεσης"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Πληκτρολογήστε το κλειδί της περιόδου σύνδεσης για το %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Να επιτρέπεται πάντα;"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Κιτ αυτοκινήτου"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Άγνωστο όνομα"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Το όνομα μου"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-el/test_strings.xml b/res/values-el/test_strings.xml
new file mode 100644
index 0000000..72f34d4
--- /dev/null
+++ b/res/values-el/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Κοινή χρήση μέσω Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Εισαγωγή αρχείου"</string>
+    <string name="update_record" msgid="2480425402384910635">"Επιβεβαίωση αρχείου"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Αρχείο ack"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Διαγραφή όλων των αρχείων"</string>
+    <string name="ok_button" msgid="6519033415223065454">"ΟΚ"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Διαγραφή αρχείου"</string>
+    <string name="start_server" msgid="9034821924409165795">"Εκκίνηση διακομιστή TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Να ειδοποιείται ο διακομιστής TCP"</string>
+</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..cea61f4
--- /dev/null
+++ b/res/values-es-rUS/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Accede al administrador de descarga."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Permite a la aplicación acceder al administrador de BluetoothShare y usarlo para transferir archivos."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Dispositivo desconocido"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Modo de avión"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"No puedes usar el Bluetooth cuando el teléfono está en modo de avión."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Para acceder a nuestros servicios de Bluetooth, primero debes activar el Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"¿Deseas activar el Bluetooth ahora?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Cancelar"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Activar"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transferencia de archivo"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\"\" desea enviarte %2$s (%3$s)."\n\n" ¿Aceptas el archivo?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Rechazar"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceptar"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"Aceptar"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Hubo tiempo muerto al aceptar el archivo entrante desde \"%1$s\"."</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Compartir por Bluetooth: Archivo entrante"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Confirma si deseas recibir este archivo."</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Hay un archivo entrante de otro dispositivo, confirma si deseas recibir este archivo."</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Compartir por Bluetooth: Recibiendo %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Compartir por Bluetooth: Recibió %1$s"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"Completado el 100%"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Compartir por Bluetooth: El archivo %1$s no fue recibido."</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Compartir por Bluetooth: Enviando %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Compartir por Bluetooth: Enviando archivo"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Compartir por Bluetooth: Envió %1$s"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"Completado el 100%"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Compartir por Bluetooth: El archivo %1$s no ha sido enviado."</string>
+    <string name="download_title" msgid="3353228219772092586">"Transferencia de archivo"</string>
+    <string name="download_line1" msgid="3470164761579278582">"De: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Archivo: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Tamaño del archivo: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Recibiendo archivo..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Detener"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Ocultar"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Archivo no recibido"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Archivo: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Motivo de la falla: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"Aceptar"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Archivo recibido"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Abrir"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Para: \"%1$s\"\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Tipo de archivo: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Enviando archivo..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Archivo enviado"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"Aceptar"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"No se envió el archivo a \"%1$s\"."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Archivo: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Vuelve a intentarlo"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Cerrar"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"Aceptar"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Archivo desconocido"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"No hay ninguna aplicación para administrar este tipo de archivo. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"El archivo no existe."</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"El archivo no existe "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Por favor, espera..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Activando Bluetooth…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"El archivo será recibido. Verifica el progreso en el panel de notificaciones."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"El archivo no se recibirá."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Se detuvo la recepción del archivo de \"%1$s\"."</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Enviando archivo a \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Enviando %1$s archivos a \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Se detuvo el envío del archivo a \"%1$s\"\""</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Se detuvo el envío de %1$s archivos a \"%2$s\"."</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"No hay espacio suficiente en la tarjeta SD para guardar el archivo de \"% \"%1$s\"."</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Espacio necesario: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" intentó enviarte %2$s archivos."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"No obstante, este dispositivo sólo puede recibir un archivo a la vez. Pide a la otra persona que envíe los otros %1$s archivos individualmente."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\"\" no está disponible."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"No se puede vincular con \"%1$s\"."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Se interrumpió la conexión."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\"\" no es compatible con las trasferencias de archivos."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" no puede aceptar %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" rechazó el archivo."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"No se ha podido enviar el archivo."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"No se ha podido recibir el archivo."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Tarjeta de memoria bloqueada. Debes desbloquear la tarjeta de memoria para guardar archivos."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Se está transfiriendo un archivo a otro dispositivo. Por favor, espera..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth está actualmente ocupado. Considera desactivar otra función de Bluetooth y vuelve a intentarlo."</string>
+    <string name="status_pending" msgid="7446884326084324082">"La transferencia del archivo aún no ha comenzado."</string>
+    <string name="status_running" msgid="2695810336448055064">"Se está realizando la transferencia del archivo."</string>
+    <string name="status_success" msgid="2268261336330060663">"La transferencia del archivo finalizó con éxito."</string>
+    <string name="status_not_accept" msgid="149046562468006301">"El teléfono no puede administrar este tipo de contenido."</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"El dispositivo de destino prohíbe esta transferencia."</string>
+    <string name="status_canceled" msgid="5514756906439790976">"El usuario canceló esta transferencia."</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problema con el espacio de almacenamiento"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"No se ha encontrado una tarjeta SD. Inserta una tarjeta SD para guardar los archivos transferidos."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Falla de la conexión"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"No se pudo administrar la solicitud correctamente."</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Error desconocido"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth recibido"</string>
+</resources>
diff --git a/res/values-es-rUS/strings_pbap.xml b/res/values-es-rUS/strings_pbap.xml
new file mode 100644
index 0000000..392a281
--- /dev/null
+++ b/res/values-es-rUS/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"PBAP de Bluetooth"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"A %1$s le gustaría acceder a tus contactos e historial de llamadas. ¿Otorgas acceso a %2$s?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Acceso a la agenda telefónica"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Escribe la clave de la sesión para %1$s."</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Se requiere la clave de la sesión de Bluetooth."</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Hubo tiempo muerto al aceptar la conexión con %1$s."</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Hubo tiempo muerto al ingresar la clave de la sesión con %1$s"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Transferencia por Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"No se ha podido compartir con éxito la agenda telefónica con"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s se conectó a tu teléfono."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s se desconectó de tu teléfono."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Solicitud de acceso a la agenda telefónica"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Solicitud de PBAP (Perfil de Acceso a la Agenda Telefónica)"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Permitir acceso a la agenda telefónica a través de %1$s"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Solicitud de autenticación de Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Clave de la sesión"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Escribe la clave de la sesión para %1$s."</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"¿Permitir siempre?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Kit de automóvil"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Nombre desconocido"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Mi nombre"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-es-rUS/test_strings.xml b/res/values-es-rUS/test_strings.xml
new file mode 100644
index 0000000..c2d4abe
--- /dev/null
+++ b/res/values-es-rUS/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hola mundo, actividad de prueba"</string>
+    <string name="app_name" msgid="1203877025577761792">"Compartir por Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Insertar grabación"</string>
+    <string name="update_record" msgid="2480425402384910635">"Confirmar grabación"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Confirmación de la grabación"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Eliminar todas las grabaciones"</string>
+    <string name="ok_button" msgid="6519033415223065454">"Aceptar"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Eliminar grabación"</string>
+    <string name="start_server" msgid="9034821924409165795">"Iniciar el servidor TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Notificar al servidor TCP"</string>
+</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
new file mode 100644
index 0000000..6dd8b41
--- /dev/null
+++ b/res/values-es/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Acceso al administrador de descargas"</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Permite que la aplicación acceda al administrador BluetoothShare para utilizarlo y transferir los archivos."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Dispositivo desconocido"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Modo avión"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"No puedes utilizar la función Bluetooth cuando el teléfono está en modo avión."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Para utilizar los servicios de Bluetooth, primero debes activar la función Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"¿Quieres activar la función Bluetooth ahora?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Cancelar"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Activar"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transferencia de archivos"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\" quiere enviarte %2$s (%3$s). "\n\n" ¿Aceptas el archivo?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Rechazar"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceptar"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"Aceptar"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Se ha agotado el tiempo para aceptar el archivo entrante de \"%1$s\"."</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Compartir con Bluetooth: archivo entrante"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Confirma que quieres recibir este archivo."</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Hay un archivo entrante de otro dispositivo. Confirma que quieres recibirlo."</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Compartir con Bluetooth: recibiendo %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Compartir con Bluetooth: %1$s recibido"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100% completado"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Compartir con Bluetooth: el archivo %1$s no se ha recibido."</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Compartir con Bluetooth: enviando %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Compartir con Bluetooth: enviando archivo"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Compartir con Bluetooth: %1$s enviado"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100% completado"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Compartir con Bluetooth: el archivo %1$s no se ha enviado."</string>
+    <string name="download_title" msgid="3353228219772092586">"Transferencia de archivos"</string>
+    <string name="download_line1" msgid="3470164761579278582">"De: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Archivo: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Tamaño de archivo: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Recibiendo archivo…"</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Detener"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Ocultar"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Archivo no recibido"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Archivo: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Motivo del error: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"Aceptar"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Archivo recibido"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Abrir"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Para: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Tipo de archivo: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Enviando archivo…"</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Archivo enviado"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"Aceptar"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"No se ha enviado el archivo a \"%1$s\"."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Archivo: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Volver a intentarlo"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Cerrar"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"Aceptar"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Archivo desconocido"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Ninguna aplicación puede procesar este tipo de archivo. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"El archivo no existe."</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"El archivo no existe. "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Por favor, espera..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Activando Bluetooth..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Se recibirá el archivo. Comprueba el progreso en el panel de notificaciones."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"No se recibirá el archivo."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Se ha detenido la recepción del archivo de \"%1$s\"."</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Enviando archivo a \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Enviando %1$s archivos a \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Se ha detenido el envío del archivo a \"%1$s\"."</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Se ha detenido el envío de %1$s archivos a \"%2$s\"."</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"No hay suficiente espacio en la tarjeta SD para guardar el archivo de \"%1$s\"."</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Espacio necesario: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" ha intentado enviarte %2$s archivos."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Sin embargo, este dispositivo solo puede recibir los archivos de uno en uno. Pide a la otra parte que envíe los %1$s archivos restantes de forma individual."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\" no está disponible."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"No se puede realizar la sincronización con \"%1$s\"."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Se ha interrumpido la conexión."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\" no admite transferencias de archivos."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" no puede aceptar %2$s."</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" ha rechazado aceptar el archivo."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"No se ha podido enviar el archivo."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"No se ha podido recibir el archivo."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"La tarjeta de memoria está bloqueada. Debes desbloquearla para guardar los archivos."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Se está transfiriendo un archivo a un dispositivo diferente. Por favor, espera…"</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth está ocupado en este momento. Desactiva otra función de Bluetooth y vuelve a intentarlo."</string>
+    <string name="status_pending" msgid="7446884326084324082">"No se ha iniciado aún la transferencia de archivos."</string>
+    <string name="status_running" msgid="2695810336448055064">"La transferencia de archivos está en curso."</string>
+    <string name="status_success" msgid="2268261336330060663">"La transferencia de archivos se ha completado correctamente."</string>
+    <string name="status_not_accept" msgid="149046562468006301">"El teléfono no puede procesar este tipo de contenido."</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"El dispositivo de destino no permite esta transferencia."</string>
+    <string name="status_canceled" msgid="5514756906439790976">"El usuario ha cancelado esta transferencia."</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problema de almacenamiento"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"No se detecta ninguna tarjeta SD. Inserta una tarjeta SD y guarda los archivos transferidos."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Error de conexión"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"No se ha podido procesar la solicitud correctamente."</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Error desconocido"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth recibido"</string>
+</resources>
diff --git a/res/values-es/strings_pbap.xml b/res/values-es/strings_pbap.xml
new file mode 100644
index 0000000..cd62bdf
--- /dev/null
+++ b/res/values-es/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Pbap de Bluetooth"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s quiere acceder a tus contactos y a tu historial de llamadas. ¿Quieres permitir el acceso a %2$s?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Acceso a la agenda telefónica"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Introducir clave de sesión para %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Se necesita la clave de sesión de Bluetooth."</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Se ha agotado el tiempo para aceptar la conexión con %1$s."</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Se ha agotado el tiempo para introducir la clave de sesión con %1$s."</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Transferencia de Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"No se ha podido establecer correctamente el uso compartido de la agenda telefónica con"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s se ha conectado al teléfono."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s se ha desconectado del teléfono."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Solicitud de acceso a la agenda telefónica"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Solicitud de PBAP"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Permitir a %1$s acceder a la agenda telefónica"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Solicitud de autenticación de Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Clave de sesión"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Introducir clave de sesión para %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"¿Permitir siempre?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Carkit"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Nombre desconocido"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Mi nombre"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-es/test_strings.xml b/res/values-es/test_strings.xml
new file mode 100644
index 0000000..18fb0aa
--- /dev/null
+++ b/res/values-es/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Compartir con Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Insertar grabación"</string>
+    <string name="update_record" msgid="2480425402384910635">"Confirmar grabación"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Confirmar grabación"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Eliminar todas las grabaciones"</string>
+    <string name="ok_button" msgid="6519033415223065454">"Aceptar"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Eliminar grabación"</string>
+    <string name="start_server" msgid="9034821924409165795">"Iniciar servidor TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Informar al servidor TCP"</string>
+</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
new file mode 100644
index 0000000..7f9f46d
--- /dev/null
+++ b/res/values-fr/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Accéder au gestionnaire de téléchargement"</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Permet à l\'application d\'accéder au gestionnaire BluetoothShare et de l\'utiliser pour le transfert de fichiers."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Périphérique inconnu"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Mode Avion"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Impossible d\'utiliser le mode Bluetooth lorsque le téléphone est en mode Avion."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Vous devez activer la fonction Bluetooth pour pouvoir utiliser les services Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Activer Bluetooth maintenant ?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Annuler"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Activer"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transfert de fichier"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\" souhaite vous envoyer %2$s (%3$s). "\n\n" Accepter le fichier ?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Refuser"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepter"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Délai d\'attente dépassé pour l\'acceptation du fichier entrant de \"%1$s\""</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Partage Bluetooth : réception de fichier"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Veuillez confirmer que vous acceptez ce fichier."</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Réception en cours d\'un fichier provenant d\'un autre périphérique. Veuillez confirmer que vous souhaitez recevoir ce fichier."</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Partage Bluetooth : réception de %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Partage Bluetooth : %1$s reçu"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100 % effectués"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Partage Bluetooth : fichier %1$s non reçu"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Partage Bluetooth : envoi de %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Partage Bluetooth : envoi de fichier"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Partage Bluetooth : %1$s envoyé"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100 % effectués"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Partage Bluetooth : fichier %1$s non envoyé"</string>
+    <string name="download_title" msgid="3353228219772092586">"Transfert de fichier"</string>
+    <string name="download_line1" msgid="3470164761579278582">"De : \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Fichier : %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Taille du fichier : %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Réception de fichier en cours…"</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Arrêter"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Masquer"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Fichier non reçu"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Fichier : %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Motif de l\'échec : %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Fichier reçu"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Ouvrir"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"À : \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Type de fichier : %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Envoi de fichier en cours..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Fichier envoyé"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Le fichier n\'a pas été envoyé à \"%1$s\"."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Fichier : %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Réessayer"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Fermer"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Fichier inconnu"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Aucune application ne prend en charge de type de fichier. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Fichier inexistant"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Ce fichier n\'existe pas. "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Veuillez patienter..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Activation Bluetooth..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"La réception du fichier va commencer. La progression va s\'afficher dans le panneau de notification."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Le fichier ne sera pas reçu."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Réception de fichier de \"%1$s\" interrompue"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Envoi du fichier à \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Envoi de %1$s fichiers à \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Envoi du fichier à \"%1$s\" interrompu"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Envoi de %$1s fichiers à \"%2$s\" interrompu"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Espace insuffisant sur la carte SD pour l\'enregistrement du fichier de \"%1$s\""</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Espace nécessaire : %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" a tenté de vous envoyer %2$s fichiers."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Ce périphérique ne peut recevoir qu\'un seul fichier à la fois. Demandez à l\'autre partie d\'envoyer les %1$s fichiers restants individuellement."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\" n\'est pas disponible."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Association à \"%1$s\" impossible."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"La connexion a été interrompue."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\" ne prend pas en charge le transfert de fichiers."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" ne peut pas accepter %2$s."</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" a refusé le fichier."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Impossible d\'envoyer le fichier."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Réception du fichier impossible."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Carte mémoire verrouillée. Vous devez la déverrouiller pour enregistrer les fichiers."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Un fichier est transféré vers un autre périphérique. Veuillez patienter…"</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth est actuellement occupé. Désactivez une autre fonctionnalité Bluetooth et réessayez."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Le transfert de fichier n\'a pas encore commencé."</string>
+    <string name="status_running" msgid="2695810336448055064">"Le transfert de fichier est en cours."</string>
+    <string name="status_success" msgid="2268261336330060663">"Le transfert de fichiers s\'est déroulé correctement."</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Le téléphone ne prend pas en charge ce type de contenu."</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Ce transfert n\'est pas autorisé par le périphérique cible."</string>
+    <string name="status_canceled" msgid="5514756906439790976">"L\'utilisateur a annulé ce transfert."</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problème de mémoire"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Carte SD absente. Insérez une carte SD pour enregistrer les fichiers transférés."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Échec de connexion"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"La demande n\'a pas pu être traitée correctement."</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Erreur inconnue"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth reçu"</string>
+</resources>
diff --git a/res/values-fr/strings_pbap.xml b/res/values-fr/strings_pbap.xml
new file mode 100644
index 0000000..1175bc9
--- /dev/null
+++ b/res/values-fr/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Profil d\'accès au répertoire téléphonique Bluetooth"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s souhaite accéder à vos contacts et à l\'historique de vos appels. Autoriser l\'accès à %2$s ?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Accès au répertoire téléphonique"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Entrer la clé de session de %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Clé de session Bluetooth requise"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Délai d\'attente dépassé pour l\'acceptation de connexion à \"%1$s\""</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Délai d\'attente dépassé pour la saisie de la clé de session avec \"%1$s\""</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Transfert par Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Impossible de partager le répertoire téléphonique avec"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s est connecté à votre téléphone."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s est déconnecté de votre téléphone."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Demande d\'accès au répertoire téléphonique"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Demande de profil d\'accès au répertoire téléphonique"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Autoriser %1$s à accéder au répertoire téléphonique"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Demande d\'authentification Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Clé de session"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Entrer la clé de session de %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Toujours autorisé ?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Carkit"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Nom inconnu"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Mon nom"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-fr/test_strings.xml b/res/values-fr/test_strings.xml
new file mode 100644
index 0000000..06c9f9b
--- /dev/null
+++ b/res/values-fr/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Bonjour à tous, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Partage Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Insérer l\'enregistrement"</string>
+    <string name="update_record" msgid="2480425402384910635">"Confirmer l\'enregistrement"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Enregistrement accusé de réception"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Supprimer tout l\'enregistrement"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Supprimer l\'enregistrement"</string>
+    <string name="start_server" msgid="9034821924409165795">"Démarrer le serveur TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Avertir le serveur TCP"</string>
+</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
new file mode 100644
index 0000000..d49a866
--- /dev/null
+++ b/res/values-it/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Accedere al gestore dei download."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Consente l\'accesso dell\'applicazione al gestore BluetoothShare e il suo utilizzo per trasferire i file."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Dispositivo sconosciuto"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Modalità aereo"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Non puoi utilizzare Bluetooth quando il telefono è in modalità aereo."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Per utilizzare i servizi Bluetooth, prima è necessario attivare Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Attivare Bluetooth adesso?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Annulla"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Attiva"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Trasferimento file"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\" vuole inviarti %2$s (%3$s). "\n\n" Accetti il file?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Rifiuta"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accetta"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Timeout dell\'accettazione del file in arrivo da \"%1$s\""</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Condivisione Bluetooth: file in arrivo"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Conferma che desideri ricevere questo file"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"File in arrivo da un altro dispositivo, conferma che vuoi ricevere il file"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Condivisione Bluetooth: ricezione %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Condivisione Bluetooth: file %1$s ricevuto"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100% completato"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Condivisione Bluetooth: file %1$s non ricevuto"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Condivisione Bluetooth: invio %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Condivisione Bluetooth: invio file"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Condivisione Bluetooth: file %1$s inviato"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100% completato"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Condivisione Bluetooth: file %1$s non inviato"</string>
+    <string name="download_title" msgid="3353228219772092586">"Trasferimento file"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Da: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"File: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Dimensioni file: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Ricezione file in corso..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Interrompi"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Nascondi"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"File non ricevuto"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"File: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Motivo dell\'errore: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"File ricevuto"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Apri"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Su: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Tipo di file: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Invio file in corso..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"File inviato"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Impossibile inviare il file a \"%1$s."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"File: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Riprova"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Chiudi"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"File sconosciuto"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Nessuna applicazione è in grado di gestire questo tipo di file. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Il file non esiste"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Il file non esiste! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Attendi..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Attivazione Bluetooth in corso…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Il file verrà ricevuto. Controlla l\'avanzamento nel pannello Notifiche."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Impossibile ricevere il file."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Interruzione della ricezione del file da \"%1$s\""</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Invio del file a \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Invio di %1$s file a \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Invio del file a \"%1$s\" interrotto"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Invio di \"%1$s\" file a \"%2$s\" interrotto"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Spazio sulla scheda SD insufficiente per salvare il file da \"%1$s\""</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Spazio necessario: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" ha tentato di inviarti %2$s file."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Tuttavia questo dispositivo può ricevere solo un file per volta. Chiedi all\'altra parte di inviare i %1$s file rimanenti singolarmente."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\" non è disponibile."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Impossibile accoppiare con \"%1$s\"."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"La connessione è stata interrotta."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\" non supporta il trasferimento dei file."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" non può accettare %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" non ha accettato il file."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Invio del file non riuscito."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Ricezione del file non riuscita."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Scheda di memoria bloccata. Devi sbloccare la scheda di memoria per salvare i file."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Un file è in corso di trasferimento su un altro dispositivo. Attendi…"</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth è attualmente occupato. Ti consigliamo di disattivare un\'altra istanza Bluetooth e riprovare."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Trasferimento file non ancora iniziato"</string>
+    <string name="status_running" msgid="2695810336448055064">"Trasferimento file in corso"</string>
+    <string name="status_success" msgid="2268261336330060663">"Il trasferimento del file è stato completato"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Il telefono non può gestire questo tipo di contenuti"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Il dispositivo di destinazione non consente questo trasferimento"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Questo trasferimento è stato annullato dall\'utente"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problema di memorizzazione"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Nessuna scheda SD. Inserisci una scheda SD per salvare i file trasferiti."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Errore di connessione"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"La richiesta non può essere gestita correttamente"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Errore sconosciuto"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Ricezione Bluetooth avvenuta"</string>
+</resources>
diff --git a/res/values-it/strings_pbap.xml b/res/values-it/strings_pbap.xml
new file mode 100644
index 0000000..2e8f2c6
--- /dev/null
+++ b/res/values-it/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"PBAP Bluetooth"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s vorrebbe accedere ai tuoi contatti e alla tua cronologia chiamate. Consentire l\'accesso a %2$s?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Accesso rubrica"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Digita la chiave di sessione per %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Chiave sessione Bluetooth necessaria"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Timeout dell\'accettazione della connessione con %1$s"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Timeout dell\'ingresso della chiave di sessione con %1$s"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Trasferimento Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Impossibile stabilire la condivisione della rubrica con"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s si è collegato."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s si è scollegato."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Richiesta di accesso alla rubrica"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Richiesta PBAP"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Consenti a %1$s di accedere alla rubrica"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Richiesta di autenticazione Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Chiave sessione"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Digita la chiave di sessione per %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Sempre consentito?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Kit auto"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Nome sconosciuto"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Il mio nome"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-it/test_strings.xml b/res/values-it/test_strings.xml
new file mode 100644
index 0000000..a5d6761
--- /dev/null
+++ b/res/values-it/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Condivisione Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Inserisci record"</string>
+    <string name="update_record" msgid="2480425402384910635">"Conferma record"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Record ACK"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Elimina tutti i record"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Elimina record"</string>
+    <string name="start_server" msgid="9034821924409165795">"Avvia server TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Invia notifica al server TCP"</string>
+</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
new file mode 100644
index 0000000..b196b32
--- /dev/null
+++ b/res/values-ja/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"ダウンロードマネージャーにアクセスします。"</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"BluetoothShareマネージャーへのアクセスとそれを利用したファイル転送をアプリケーションに許可します。"</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"不明な携帯端末"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"機内モード"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"携帯が機内モードのときはBluetoothを使用できません。"</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Bluetoothサービスを利用するには、まずBluetoothをONにしてください。"</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Bluetoothを今すぐONにしますか?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"キャンセル"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"ONにする"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"ファイル転送"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"「%1$s」が%2$s(%3$s)を送信しようとしています。"\n\n"ファイルを受信しますか?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"拒否"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"承諾"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"「%1$s」からの着信ファイルの受信がタイムアウトになりました"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth共有: ファイル着信"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"このファイルを受信してもよろしいですか"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"別の携帯端末からファイルが着信しました。このファイルを受信してもよろしいですか"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Bluetooth共有: %1$sを受信中"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Bluetooth共有: %1$sを受信済み"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100%完了"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Bluetooth共有: ファイル%1$sの受信に失敗"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Bluetooth共有: %1$sを送信中"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Bluetooth共有: ファイルを送信中"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Bluetooth共有: %1$sを送信済み"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100%完了"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Bluetooth共有: ファイル%1$sの送信に失敗"</string>
+    <string name="download_title" msgid="3353228219772092586">"ファイル転送"</string>
+    <string name="download_line1" msgid="3470164761579278582">"From: 「%1$s」"</string>
+    <string name="download_line2" msgid="65085079456902842">"ファイル: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"ファイルサイズ: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"ファイルを受信中..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"停止"</string>
+    <string name="download_ok" msgid="5000360731674466039">"非表示"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"ファイルを受信していません"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"ファイル: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"失敗の理由: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"ファイルを受信しました"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"開く"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"To: 「%1$s」"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"ファイル形式: %1$s(%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"ファイルを送信中..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"ファイルを送信しました"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"ファイルは「%1$s」に送信されませんでした。"</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"ファイル: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"再試行"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"閉じる"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"不明なファイル"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"このファイル形式を扱うアプリケーションがありません。"\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"ファイルが存在しません"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"そのファイルは存在しません。"\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"お待ちください..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"BluetoothをONにしています..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"ファイルを受信します。進行状況は[通知]パネルでご確認ください。"</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"ファイルは受信されません。"</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"「%1$s」からのファイルの受信を停止しました"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"「%1$s」にファイルを送信中"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"%1$s個のファイルを「%2$s」に送信中"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"「%1$s」へのファイルの送信を停止しました"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"%1$s個のファイルの「%2$s」への送信を停止しました"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"「%1$s」からのファイルを保存する十分な空き領域がSDカードにありません"</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"空き領域が必要: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"「%1$s」が%2$s個のファイルを送信しようとしました。"</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"ただしこの携帯端末で一度に受信できるファイルは1つだけです。送信元に、残りの%1$s個のファイルを個々に送信するように依頼してください。"</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"「%1$s」は利用できません。"</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"「%1$s」をペアとして設定できません。"</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"接続が中断されました。"</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"「%1$s」はファイル転送に対応していません。"</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"「%1$s」は%2$sを承諾できません"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"「%1$s」がファイルの受信を拒否しました。"</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"ファイルを送信できませんでした。"</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"ファイルを受信できませんでした。"</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"メモリーカードがロックされています。ファイルを保存するには、メモリカードのロックの解除が必要です。"</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"別の携帯端末にファイルを転送中です。お待ちください..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetoothは現在使用中です。他のBluetooth機能をOFFにしてもう一度やり直すことをおすすめします。"</string>
+    <string name="status_pending" msgid="7446884326084324082">"ファイル転送はまだ開始されていません"</string>
+    <string name="status_running" msgid="2695810336448055064">"ファイルを転送中です"</string>
+    <string name="status_success" msgid="2268261336330060663">"ファイル転送が正常に完了しました"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"携帯ではこのタイプのコンテンツを処理できません"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"この転送は転送先の携帯端末で禁止されています"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"この転送はユーザーによりキャンセルされました"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"ストレージの問題"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"SDカードが見つかりません。転送ファイルを保存するSDカードを挿入してください。"</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"接続失敗"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"リクエストを正しく処理できません"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"不明なエラー"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetoothを受信しました"</string>
+</resources>
diff --git a/res/values-ja/strings_pbap.xml b/res/values-ja/strings_pbap.xml
new file mode 100644
index 0000000..2bb4245
--- /dev/null
+++ b/res/values-ja/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth PBAP"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$sが連絡先と通話履歴にアクセスしようとしています。%2$sにアクセスを許可しますか?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"電話帳アクセス"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"%1$sのセッションキーを入力"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetoothセッションキーが必要です"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"%1$sとの接続の承諾がタイムアウトになりました"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"%1$sでのセッションキーの入力がタイムアウトになりました"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Bluetooth転送"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"電話帳共有を確立できませんでした:"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$sが携帯に接続されました。"</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$sが携帯から切断されました。"</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"電話帳アクセスリクエスト"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAPリクエスト"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"%1$sの電話帳アクセスを許可します"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"OBEX認証リクエスト"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"セッションキー"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"%1$sのセッションキーを入力してください"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"常に許可しますか?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"カーキット"</string>
+    <string name="unknownName" msgid="2841414754740600042">"不明な名前"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"名前"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-ja/test_strings.xml b/res/values-ja/test_strings.xml
new file mode 100644
index 0000000..613b6f4
--- /dev/null
+++ b/res/values-ja/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Bluetooth共有"</string>
+    <string name="insert_record" msgid="1450997173838378132">"記録を挿入"</string>
+    <string name="update_record" msgid="2480425402384910635">"レコードを確認"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Ackレコード"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"レコードをすべて削除"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"レコードを削除"</string>
+    <string name="start_server" msgid="9034821924409165795">"TCPサーバーを起動"</string>
+    <string name="notify_server" msgid="4369106744022969655">"TCPサーバーに通知"</string>
+</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
new file mode 100644
index 0000000..750bd78
--- /dev/null
+++ b/res/values-ko/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"다운로드 관리자에 액세스합니다."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"응용프로그램에서 BluetoothShare 관리자에 액세스하고 이를 사용하여 파일을 전송할 수 있습니다."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"알 수 없는 장치"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"비행 모드"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"휴대전화가 비행 모드인 경우에는 Bluetooth를 사용할 수 없습니다."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Bluetooth 서비스를 사용하려면 먼저 Bluetooth를 켜야 합니다."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"지금 Bluetooth를 사용하시겠습니까?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"취소"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"사용"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"파일 전송"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\'%1$s\'이(가) %2$s(%3$s)을(를) 보내려고 합니다. "\n\n" 파일을 수락하시겠습니까?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"거부"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"수락"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"확인"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"\'%1$s\'에서 수신 중인 파일 수락 제한 시간이 초과되었습니다."</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth 공유: 수신 파일"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"이 파일을 수신할 것인지 확인하세요."</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"다른 장치에서 수신 중인 파일이 있습니다. 이 파일을 수신할 것인지 확인하세요."</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Bluetooth 공유: %1$s을(를) 수신하는 중"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Bluetooth 공유: %1$s을(를) 수신함"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100% 완료"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Bluetooth 공유: %1$s 파일이 수신되지 않음"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Bluetooth 공유: %1$s을(를) 보내는 중"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Bluetooth 공유: 파일을 보내는 중"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Bluetooth 공유: %1$s을(를) 보냄"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100% 완료"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Bluetooth 공유: %1$s 파일을 보내지 못함"</string>
+    <string name="download_title" msgid="3353228219772092586">"파일 전송"</string>
+    <string name="download_line1" msgid="3470164761579278582">"위치: \'%1$s\'"</string>
+    <string name="download_line2" msgid="65085079456902842">"파일: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"파일 크기: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"파일을 수신하는 중..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"중지"</string>
+    <string name="download_ok" msgid="5000360731674466039">"숨기기"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"파일이 수신되지 않았습니다."</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"파일: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"실패 이유: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"확인"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"파일을 수신했습니다."</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"열기"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"대상: \'%1$s\'"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"파일 형식: %1$s(%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"파일을 보내는 중..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"파일을 보냈습니다."</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"확인"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"\'%1$s\'에 파일을 보내지 못했습니다."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"파일: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"다시 시도"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"닫기"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"확인"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"알 수 없는 파일"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"이 파일 형식을 처리할 응용 프로그램이 없습니다."\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"파일이 없습니다."</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"파일이 없습니다."\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"잠시 기다려 주세요."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Bluetooth 사용 설정 중…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"파일이 수신됩니다. 알림 패널에서 진행률을 확인하세요."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"파일이 수신되지 않습니다."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"\'%1$s\'에서 파일 수신이 중지되었습니다."</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"\'%1$s\'에 파일을 보내는 중"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"\'%2$s\'에 %1$s 파일을 보내는 중"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"\'%1$s\'에 파일 보내기가 중지되었습니다."</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"\'%2$s\'에 %1$s 파일 보내기를 중지했습니다."</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"\'%1$s\'에서 수신한 파일을 저장할 SD 카드의 공간이 부족합니다."</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"필요한 공간: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\'%1$s\'이(가) %2$s개의 파일을 보내려고 했습니다."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"이 장치는 한 번에 파일 하나만 수신할 수 있습니다. 남은 %1$s개의 파일을 개별적으로 보내도록 상대방에게 요청하세요."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\'%1$s\'을(를) 사용할 수 없습니다."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"\'%1$s\'와(과) 페어링할 수 없습니다."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"연결이 중단되었습니다."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\'%1$s\'은(는) 파일 전송을 지원하지 않습니다."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\'%1$s\'이(가) %2$s을(를) 수락할 수 없습니다."</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\'%1$s\'이(가) 파일 수락을 거부했습니다."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"파일을 보내지 못했습니다."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"파일을 수신할 수 없습니다."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"메모리 카드가 잠겨 있습니다. 파일을 저장하려면 메모리 카드의 잠금을 해제해야 합니다."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"다른 장치로 전송 중인 파일이 있습니다. 잠시 기다려 주세요."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth가 사용 중입니다. 다른 Bluetooth 기능을 끄고 다시 시도해 보세요."</string>
+    <string name="status_pending" msgid="7446884326084324082">"파일 전송을 시작하지 않았습니다."</string>
+    <string name="status_running" msgid="2695810336448055064">"파일 전송 진행 중"</string>
+    <string name="status_success" msgid="2268261336330060663">"파일 전송이 완료되었습니다."</string>
+    <string name="status_not_accept" msgid="149046562468006301">"휴대전화에서 이 형식의 콘텐츠를 처리할 수 없습니다."</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"대상 장치에서 전송을 금지했습니다."</string>
+    <string name="status_canceled" msgid="5514756906439790976">"사용자가 전송을 취소했습니다."</string>
+    <string name="status_file_error" msgid="6125632529575521755">"저장용량 문제"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"SD 카드가 없습니다. 전송된 파일을 저장할 SD 카드를 삽입하세요."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"연결 실패"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"요청을 제대로 처리할 수 없습니다."</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"알 수 없는 오류"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth가 수신되었습니다."</string>
+</resources>
diff --git a/res/values-ko/strings_pbap.xml b/res/values-ko/strings_pbap.xml
new file mode 100644
index 0000000..133fed6
--- /dev/null
+++ b/res/values-ko/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s이(가) 전화번호부와 모든 통화 기록에 액세스하려고 합니다. %2$s에게 액세스 권한을 제공하시겠습니까?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"전화번호부 액세스"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"%1$s의 세션 키 입력"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetooth 세션 키 요청"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"%1$s 연결 수락 제한 시간이 초과되었습니다."</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"%1$s에 세션 키 입력 제한 시간이 초과되었습니다."</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Bluetooth 전송"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"전화번호부 공유를 설정할 수 없습니다. 대상:"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s이(가) 사용자의 휴대전화에 연결되었습니다."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s와(과) 사용자의 휴대전화 연결이 끊어졌습니다."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"전화번호부 액세스 요청"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAP 요청"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"%1$s(으)로 전화번호부 액세스 허용"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Obex 인증 요청"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"세션 키"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"%1$s의 세션 키 입력"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"항상 허용하시겠습니까?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Carkit"</string>
+    <string name="unknownName" msgid="2841414754740600042">"알 수 없는 이름"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"내 이름"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-ko/test_strings.xml b/res/values-ko/test_strings.xml
new file mode 100644
index 0000000..356c86a
--- /dev/null
+++ b/res/values-ko/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"안녕하세요. TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Bluetooth 공유"</string>
+    <string name="insert_record" msgid="1450997173838378132">"기록 삽입"</string>
+    <string name="update_record" msgid="2480425402384910635">"기록 확인"</string>
+    <string name="ack_record" msgid="6716152390978472184">"기록 인식"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"모든 기록 삭제"</string>
+    <string name="ok_button" msgid="6519033415223065454">"확인"</string>
+    <string name="delete_record" msgid="4645040331967533724">"기록 삭제"</string>
+    <string name="start_server" msgid="9034821924409165795">"TCP 서버 시작"</string>
+    <string name="notify_server" msgid="4369106744022969655">"TCP 서버 알림"</string>
+</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
new file mode 100644
index 0000000..a06a588
--- /dev/null
+++ b/res/values-nb/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Åpne nedlastingsbehandling."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Gir programmet tilgang til BluetoothShare-administratoren, og tillatelse til å bruke det til filoverføring"</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Ukjent enhet"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Flymodus"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Du kan ikke bruke Bluetooth når telefonen er i flymodus."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Du må slå på Bluetooth for å bruke Bluetooth-tjenestene."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Vil du slå på Bluetooth nå?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Avbryt"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Slå på"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Filoverføring"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"«%1$s» vil sende deg %2$s (%3$s). "\n\n" Godta filen?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Avslå"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Godta"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Det·tok·for·lang·tid·å·godta·innkommende·fil·fra·«%1$s»"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-deling: Innkommende fil"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Bekreft at du ønsker å motta denne filen"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"En fil er på vei inn fra en annen enhet. Bekreft at du vil motta denne filen"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Bluetooth-deling: Mottar %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Bluetooth-deling: %1$s er mottatt"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100 % fullført"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Bluetooth-deling: Filen %1$s er ikke mottatt"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Bluetooth-deling: Sender %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Bluetooth-deling: Sender fil"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Bluetooth-deling: Sendt %1$s"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100 % fullført"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Bluetooth-deling: Filen %1$s ble ikke sendt"</string>
+    <string name="download_title" msgid="3353228219772092586">"Filoverføring"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Fra «%1$s»"</string>
+    <string name="download_line2" msgid="65085079456902842">"Fil: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Filstørrelse: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Mottar fil ..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Stopp"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Skjul"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Filen ble ikke mottatt"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Fil: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Årsak til feilen: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Fil mottatt"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Åpne"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Til: «%1$s»"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Filtype: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Sender filen ..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Fil sendt"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Filen ble ikke sendt til «%1$s»."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Fil: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Prøv igjen"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Lukk"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Ukjent fil"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Det foreligger ikke noe program som kan behandle denne filtypen. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Filen fins ikke"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Filen fins ikke! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Vent litt ..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Slår på Bluetooth ..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Filen vil bli mottatt. Du kan se fremdriften i varselpanelet."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Filen vil ikke bli mottatt."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Stoppet mottak av fil fra «%1$s»"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Sender fil til «%1$s»"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Sender %1$s filer til «%2$s»"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Stoppet sendingen av fil til «%1$s»"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Stoppet sending av %1$s filer til «%2$s»"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Det er ikke nok plass på SD-kortet til å lagre filen fra «%1$s»"</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Nødvendig plass: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"«%1$s» forsøkte å sende deg %2$s filer."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Denne enheten kan imidlertid kun motta én fil om gangen. Be den andre parten om å sende de resterende %1$s-filene separat."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"«%1$s» er utilgjengelig."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Kan ikke kobles·til «%1$s»."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Forbindelsen ble avbrutt."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"«%1$s» håndterer ikke filoverføringer."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"«%1$s» kan ikke godta %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"«%1$s» avviste filen."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Sending av filen mislyktes."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Filen kunne ikke mottas."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Låst minnekort. Du må låse opp minnekortet for å lagre filene."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"En fil overføres til en annen enhet, vent litt ..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth er opptatt for øyeblikket. Du kan vurdere å slå av en annen Bluetooth-funksjon og prøve igjen."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Filoverføringen har ikke startet ennå"</string>
+    <string name="status_running" msgid="2695810336448055064">"Filoverføring pågår"</string>
+    <string name="status_success" msgid="2268261336330060663">"Filoverføringen er gjennomført"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Telefonen kan ikke behandle denne typen innhold"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Filoverføringen er forbudt av mottakende·enhet"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Denne overføringen ble avbrutt av brukeren"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problem med lagring"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"SD-kort mangler. Sett inn et SD.kort for å lagre overførte filer."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Tilkoblingsfeil"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Forespørselen kan ikke behandles korrekt"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Ukjent feil"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth mottatt"</string>
+</resources>
diff --git a/res/values-nb/strings_pbap.xml b/res/values-nb/strings_pbap.xml
new file mode 100644
index 0000000..142728a
--- /dev/null
+++ b/res/values-nb/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s ber om å få tilgang til kontaktene og ringeloggen. Vil du gi %2$s tilgang?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Tilgang til telefonbok"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Skriv inn øktsnøkkel for %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetooth-øktsnøkkel kreves"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Det·tok·for·lang·tid·å·godta·forbindelsen·med·«%1$s»"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Det·tok·for·lang·tid·å·legge·inn·øktsnøkkel·med·%1$s"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Bluetooth-overføring"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Fikk·ikke·opprettet·telefonbokdeling·med"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s er koblet til telefonen."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s er koblet fra telefonen."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Forespørsel om tilgang til telefonboka"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAP-forespørsel"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Gi·%1$s·tilgang·til·telefonboka"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Autentiseringsforespørsel fra Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Øktsnøkkel"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Skriv inn øktsnøkkel for %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Alltid tillatt?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Bilsett"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Ukjent navn"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Navnet mitt"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-nb/test_strings.xml b/res/values-nb/test_strings.xml
new file mode 100644
index 0000000..99f2205
--- /dev/null
+++ b/res/values-nb/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hallo verden, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Bluetooth-deling"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Sett inn oppføring"</string>
+    <string name="update_record" msgid="2480425402384910635">"Bekreft oppføring"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Ack-oppføring"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Slett hele oppføringen"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Slett oppføring"</string>
+    <string name="start_server" msgid="9034821924409165795">"Start TPC-tjener"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Varsle TCP-tjener"</string>
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
new file mode 100644
index 0000000..9d139b9
--- /dev/null
+++ b/res/values-nl/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Downloadbeheer weergeven."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Hiermee krijgt de toepassing toegang tot de beheerfunctie voor delen via Bluetooth om deze functie te gebruiken voor het overdragen van bestanden."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Onbekend apparaat"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Vliegmodus"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"U kunt Bluetooth niet gebruiken als de vliegtuigmodus van de telefoon actief is."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Als u Bluetooth-services wilt gebruiken, moet u eerst Bluetooth inschakelen."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Bluetooth nu inschakelen?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Annuleren"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Inschakelen"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Bestandsoverdracht"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\'%1$s\' wil %2$s (%3$s) naar u verzenden. "\n\n" Wilt u het bestand accepteren?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Weigeren"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Accepteren"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Er is een time-out opgetreden voor het accepteren van het inkomende bestand van \'%1$s\'"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Delen via Bluetooth: inkomend bestand"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Bevestig dat u dit bestand wilt ontvangen"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Er is een inkomend bestand van een ander apparaat beschikbaar. Als u dit bestand wilt ontvangen, moet u dit bevestigen."</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Delen via Bluetooth: %1$s ontvangen"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Delen via Bluetooth: %1$s ontvangen"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100% voltooid"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Delen via Bluetooth: bestand %1$s niet ontvangen"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Delen via Bluetooth: %1$s verzenden"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Delen via Bluetooth: bestand verzenden"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Delen via Bluetooth: %1$s verzonden"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100% voltooid"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Delen via Bluetooth: bestand %1$s niet verzonden"</string>
+    <string name="download_title" msgid="3353228219772092586">"Bestandsoverdracht"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Van: \'%1$s\'"</string>
+    <string name="download_line2" msgid="65085079456902842">"Bestand: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Bestandsgrootte: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Bestand ontvangen…"</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Stoppen"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Verbergen"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Bestand niet ontvangen"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Bestand: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Oorzaak van fout: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Bestand ontvangen"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Openen"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Aan: \'%1$s\'"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Bestandstype: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Bestand verzenden…"</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Bestand verzonden"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Het bestand is niet verzonden naar \'%1$s\'."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Bestand: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Opnieuw proberen"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Sluiten"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Onbekend bestand"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Er is geen toepassing beschikbaar waarmee dit bestandstype kan worden verwerkt. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Bestand bestaat niet"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Het bestand bestaat niet. "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Een ogenblik geduld..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Bluetooth inschakelen…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Het bestand wordt ontvangen. U kunt de voortgang controleren in het venster \'Meldingen\'."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Het bestand wordt niet ontvangen."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Ontvangen van bestand van \'%1$s\' is beëindigd"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Bestand verzenden naar \'%1$s\'"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"%1$s bestanden verzenden naar \'%2$s\'"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Verzenden van bestand naar \'%1$s\' is beëindigd"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Verzenden van %1$s bestanden naar \'%2$s is beëindigd"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Er is onvoldoende ruimte op de SD-kaart beschikbaar om het bestand van \'%1$s\' op te slaan"</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Benodigde ruimte: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\'%1$s\' heeft geprobeerd om %2$s bestanden naar u te verzenden."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Dit apparaat kan slechts één bestand tegelijk ontvangen. Vraag de andere partij om de resterende %1$s bestanden afzonderlijk te verzenden."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\'%1$s\' is niet beschikbaar."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Kan niet koppelen met \'%1$s\'."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"De verbinding is onderbroken."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\'%1$s\' ondersteunt geen overdracht van bestanden."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\'%1$s\' kan %2$s niet accepteren"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\'%1$s\' heeft het bestand geweigerd."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Het bestand kan niet worden verzonden."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Het bestand kan niet worden opgehaald."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Vergrendelde geheugenkaart. U moet de geheugenkaart ontgrendelen als u bestanden wilt opslaan."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Er wordt een bestand overgedragen naar een ander apparaat. Een ogenblik geduld..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth is momenteel bezet. Schakel een andere Bluetooth-functie uit en probeer het nogmaals."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Bestandsoverdracht nog niet gestart"</string>
+    <string name="status_running" msgid="2695810336448055064">"Bestandsoverdracht wordt uitgevoerd"</string>
+    <string name="status_success" msgid="2268261336330060663">"De bestandsoverdracht is voltooid"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"De telefoon kan dit type inhoud niet verwerken"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Deze overdracht wordt niet toegestaan door het doelapparaat"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Deze overdracht is geannuleerd door de gebruiker"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Probleem met opslagruimte"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Geen SD-kaart. Plaats een SD-kaart om overgedragen bestanden op te slaan."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Verbindingsfout"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Het verzoek kan niet correct worden verwerkt"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Onbekende fout"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Ontvangen via Bluetooth"</string>
+</resources>
diff --git a/res/values-nl/strings_pbap.xml b/res/values-nl/strings_pbap.xml
new file mode 100644
index 0000000..50d9671
--- /dev/null
+++ b/res/values-nl/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth PBAP"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s wil toegang tot uw contacten en oproepgeschiedenis. Toegang verlenen aan %2$s?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Toegang tot telefoonboek"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Typ de sessiesleutel voor %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetooth-sessiesleutel is vereist"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Er is een time-out opgetreden voor het accepteren van de verbinding met %1$s"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Er is een time-out opgetreden tijdens het invoeren van de sessiesleutel met %1$s"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Bluetooth-overdracht"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Kan delen van telefoonboek niet inschakelen met"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s is verbonden met uw telefoon."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s heeft de verbinding met uw telefoon verbroken."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Verzoek om toegang tot telefoonboek"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAP-verzoek"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Toegang tot telefoonboek toestaan aan %1$s"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"OBEX-verificatieverzoek"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Sessiesleutel"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Typ de sessiesleutel voor %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Altijd toestaan?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Carkit"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Onbekende naam"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Mijn naam"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-nl/test_strings.xml b/res/values-nl/test_strings.xml
new file mode 100644
index 0000000..e3e61e7
--- /dev/null
+++ b/res/values-nl/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hallo wereld, testactiviteit"</string>
+    <string name="app_name" msgid="1203877025577761792">"Delen via Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Record invoegen"</string>
+    <string name="update_record" msgid="2480425402384910635">"Record bevestigen"</string>
+    <string name="ack_record" msgid="6716152390978472184">"ACK-record"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Alle records verwijderen"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Record verwijderen"</string>
+    <string name="start_server" msgid="9034821924409165795">"TCP-server starten"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Melden aan TCP-server"</string>
+</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
new file mode 100644
index 0000000..158f151
--- /dev/null
+++ b/res/values-pl/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Dostęp do menedżera pobierania."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Zezwala aplikacji na dostęp do menedżera BluetoothShare i używanie go do przesyłania plików."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Nieznane urządzenie"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Tryb samolotowy"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Nie można używać funkcji Bluetooth, gdy telefon jest w trybie samolotowym."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Aby korzystać z usług Bluetooth, trzeba najpierw włączyć moduł Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Czy włączyć teraz moduł Bluetooth?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Anuluj"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Włącz"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Przesyłanie pliku"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"Urządzenie „%1$s” chce wysłać do Ciebie plik %2$s (%3$s). "\n\n" Czy zaakceptować ten plik?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Odrzuć"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Akceptuj"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Nastąpiło przekroczenie limitu czasu na akceptację przychodzącego pliku z urządzenia „%1$s”"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Udostępnianie Bluetooth: plik przychodzący"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Potwierdź, że chcesz odebrać ten plik"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Przysłano plik z innego urządzenia. Potwierdź, że chcesz go odebrać."</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Udostępnianie Bluetooth: odbieranie %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Udostępnianie Bluetooth: odebrano %1$s"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"Ukończono w 100%"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Udostępnianie Bluetooth: nie odebrano pliku %1$s"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Udostępnianie Bluetooth: wysyłanie %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Udostępnianie Bluetooth: wysyłanie pliku"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Udostępnianie Bluetooth: wysłano %1$s"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"Ukończono w 100%"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Udostępnianie Bluetooth: plik %1$s nie został wysłany"</string>
+    <string name="download_title" msgid="3353228219772092586">"Przesyłanie pliku"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Od: „%1$s”"</string>
+    <string name="download_line2" msgid="65085079456902842">"Plik: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Rozmiar pliku: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Odbieranie pliku…"</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Zatrzymaj"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Ukryj"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Plik nie został odebrany"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Plik: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Przyczyna niepowodzenia: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Odebrano plik"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Otwórz"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Do: „%1$s”"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Typ pliku: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Wysyłanie pliku…"</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Plik został wysłany"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Plik nie został wysłany do urządzenia „%1$s”."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Plik: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Spróbuj ponownie"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Zamknij"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Nieznany plik"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Brak aplikacji do obsłużenia pliku tego typu. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Plik nie istnieje"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Plik nie istnieje! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Czekaj…"</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Włączanie modułu Bluetooth…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Plik zostanie odebrany. Sprawdzaj postęp na panelu powiadomień."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Plik nie zostanie odebrany."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Zatrzymano odbiór pliku z urządzenia „%1$s”"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Wysyłanie pliku do urządzenia „%1$s”"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Wysyłanie %1$s plików do urządzenia „%2$s”"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Zatrzymano wysyłanie pliku do urządzenia „%1$s”"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Zatrzymano wysyłanie %1$s plików do urządzenia „%2$s”"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Brak wystarczającej ilości miejsca na karcie SD do zapisania pliku z urządzenia „%1$s”"</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Wymagane miejsce: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"Urządzenie „%1$s” podjęło próbę wysłania do Ciebie %2$s plików."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Jednak to urządzenie może odebrać tylko jeden plik w danym momencie. Poproś drugą stronę o wysłanie pozostałych %1$s plików pojedynczo."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"Urządzenie „%1$s” jest niedostępne."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Nie można powiązać z urządzeniem „%1$s”."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Połączenie zostało przerwane."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"Urządzenie „%1$s” nie obsługuje przesyłania plików."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"Urządzenie „%1$s” nie akceptuje %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"Urządzenie „%1$s” odmówiło przyjęcia pliku."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Nie można wysłać pliku."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Nie można odebrać pliku."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Zablokowana karta pamięci. Aby zapisać pliki, trzeba odblokować kartę pamięci."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Trwa przesyłanie pliku do innego urządzenia. Czekaj…"</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Moduł Bluetooth jest obecnie zajęty. Wyłącz inną funkcję Bluetooth i spróbuj ponownie."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Przesyłanie pliku nie zostało jeszcze rozpoczęte"</string>
+    <string name="status_running" msgid="2695810336448055064">"Trwa przesyłanie pliku"</string>
+    <string name="status_success" msgid="2268261336330060663">"Przesyłanie pliku zostało pomyślnie ukończone"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Telefon nie może obsłużyć tego typu zawartości"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Urządzenie docelowe zabroniło tego przesyłania"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"To przesyłanie zostało anulowane przez użytkownika"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problem z zapisem"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Brak karty SD. Włóż kartę SD, aby zapisać przesłane pliki."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Błąd połączenia"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Nie można poprawnie obsłużyć żądania"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Nieznany błąd"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Odebrane przez Bluetooth"</string>
+</resources>
diff --git a/res/values-pl/strings_pbap.xml b/res/values-pl/strings_pbap.xml
new file mode 100644
index 0000000..2b49315
--- /dev/null
+++ b/res/values-pl/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Profil PBAP Bluetooth"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"Urządzenie %1$s chce uzyskać dostęp do Twoich kontaktów i historii połączeń. Czy zezwolić %2$s na dostęp?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Dostęp do książki telefonicznej"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Wpisz klucz sesji dla %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Wymagany klucz sesji Bluetooth"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Nastąpiło przekroczenie limitu czasu na akceptację połączenia z %1$s"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Nastąpiło przekroczenie limitu czasu na wprowadzenie klucza sesji z %1$s"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Przesyłanie Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Nie można pomyślnie nawiązać połączenia do udostępniania książki telefonicznej z"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"Urządzenie %1$s nawiązało połączenie z Twoim telefonem."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"Urządzenie %1$s rozłączyło się z Twoim telefonem."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Żądanie dostępu do książki telefonicznej"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Żądanie PBAP"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Zezwól na dostęp do książki telefonicznej przez %1$s"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Żądanie uwierzytelniania Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Klucz sesji"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Wpisz klucz sesji dla %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Zawsze zezwalać?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Zestaw samochodowy"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Nieznana nazwa"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Moja nazwa"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-pl/test_strings.xml b/res/values-pl/test_strings.xml
new file mode 100644
index 0000000..2f07892
--- /dev/null
+++ b/res/values-pl/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Witaj świecie, działanie testowe"</string>
+    <string name="app_name" msgid="1203877025577761792">"Udostępnianie Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Wstaw rekord"</string>
+    <string name="update_record" msgid="2480425402384910635">"Potwierdź rekord"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Rekord Ack"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Usuń wszystkie rekordy"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Usuń rekord"</string>
+    <string name="start_server" msgid="9034821924409165795">"Uruchom serwer TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Powiadom serwer TCP"</string>
+</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..be7c79b
--- /dev/null
+++ b/res/values-pt-rPT/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Aceder ao gestor de transferências."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Permite à aplicação aceder ao gestor BluetoothShare e utilizá-lo para transferir ficheiros."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Dispositivo desconhecido"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Modo de avião"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Não pode utilizar o Bluetooth com o telefone em modo de avião."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Para utilizar os serviços Bluetooth, tem de activar primeiro o Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Activar o Bluetooth agora?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Cancelar"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Activar"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transferência do ficheiro"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\" quer enviar-lhe %2$s (%3$s). "\n\n" Aceita o ficheiro?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Recusar"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceitar"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Foi excedido o tempo para aceitar o ficheiro recebido de \"%1$s\""</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Partilha Bluetooth: ficheiro recebido"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Confirme se quer receber este ficheiro"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Está a receber um ficheiro vindo de outro dispositivo, confirme se pretende recebê-lo"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Partilha Bluetooth: a receber %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Partilha Bluetooth: %1$s recebido"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100% concluído"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Partilha Bluetooth: ficheiro %1$s não recebido"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Partilha Bluetooth: a enviar %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Partilha Bluetooth: a enviar ficheiro"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Partilha Bluetooth: %1$s enviado"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100% concluído"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Partilha Bluetooth: ficheiro %1$s não enviado"</string>
+    <string name="download_title" msgid="3353228219772092586">"Transferência do ficheiro"</string>
+    <string name="download_line1" msgid="3470164761579278582">"De: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Ficheiro: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Tamanho do ficheiro: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"A receber ficheiro..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Parar"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Ocultar"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Ficheiro não recebido"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Ficheiro: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Causa da falha: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Ficheiro recebido"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Abrir"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Para: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Tipo de ficheiro: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"A enviar ficheiro..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Ficheiro enviado"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"O ficheiro não foi enviado para \"%1$s\"."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Ficheiro: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Tente novamente"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Fechar"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Ficheiro desconhecido"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Não existe nenhuma aplicação para executar este tipo de ficheiro. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"O ficheiro não existe"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"O ficheiro não existe! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Aguarde..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"A activar Bluetooth…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"O ficheiro será recebido. Consulte o progresso no painel Notificações."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"O ficheiro não será recebido."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"A recepção do ficheiro de \"%1$s\" foi parada"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"A enviar o ficheiro para \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"A enviar %1$s ficheiros para \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"O envio do ficheiro para \"%1$s\" foi parado"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"O envio de %1$s ficheiros para \"%2$s\" foi parado"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Não existe espaço suficiente no cartão SD para guardar o ficheiro de \"%1$s\""</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Espaço necessário: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" tentou enviar-lhe %2$s ficheiros."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Este dispositivo só pode, no entanto, receber um ficheiro de cada vez. Peça à outra pessoa que envie individualmente os restantes %1$s ficheiros."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\" não está disponível."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Não é possível emparelhar com \"%1$s\"."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"A ligação foi interrompida."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\" não suporta transferências de ficheiros."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" não pode aceitar %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" recusou aceitar o ficheiro."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Não foi possível enviar o ficheiro."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Não foi possível receber o ficheiro."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Cartão de memória bloqueado. Para guardar os ficheiros terá de desbloquear o cartão de memória."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Está a ser transferido um ficheiro para outro dispositivo. Aguarde..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"O Bluetooth está actualmente ocupado. Recomendamos que desactive outra funcionalidade do Bluetooth e tente novamente."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Ainda não foi iniciada a transferência do ficheiro"</string>
+    <string name="status_running" msgid="2695810336448055064">"Está a decorrer a transferência do ficheiro"</string>
+    <string name="status_success" msgid="2268261336330060663">"A transferência do ficheiro foi concluída com êxito"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"O telefone não consegue executar este tipo de conteúdo"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Esta transferência está proibida pelo dispositivo de destino"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Esta transferência foi cancelada pelo utilizador"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problema de armazenamento"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Sem cartão SD. Insira um cartão SD para guardar os ficheiros transferidos."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Falha na ligação"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Não é possível processar o pedido correctamente"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Erro desconhecido"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth recebido"</string>
+</resources>
diff --git a/res/values-pt-rPT/strings_pbap.xml b/res/values-pt-rPT/strings_pbap.xml
new file mode 100644
index 0000000..ddf1bf1
--- /dev/null
+++ b/res/values-pt-rPT/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"PBAP Bluetooth"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s gostaria de aceder aos seus contactos e histórico de chamadas. Conceder acesso a %2$s?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Acesso à agenda telefónica"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Escreva a chave de sessão para %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Necessária chave de sessão Bluetooth"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Foi excedido o tempo para aceitar a ligação com %1$s"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Foi excedido o tempo para introduzir a chave de sessão com %1$s"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Transferência Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Falha ao estabelecer a partilha da agenda telefónica com"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s ligado ao seu telefone."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s desligado do seu telefone."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Pedido de acesso à agenda telefónica"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Pedido PBAP"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Permitir a %1$s o acesso à agenda telefónica"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Pedido de autenticação OBEX"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Chave de sessão"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Escreva a chave de sessão para %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Sempre permitido?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Kit para carro"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Nome desconhecido"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"O meu nome"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-pt-rPT/test_strings.xml b/res/values-pt-rPT/test_strings.xml
new file mode 100644
index 0000000..a860acd
--- /dev/null
+++ b/res/values-pt-rPT/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Olá mundo! Isto é um teste"</string>
+    <string name="app_name" msgid="1203877025577761792">"Bluetooth Share"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Inserir registo"</string>
+    <string name="update_record" msgid="2480425402384910635">"Confirmar registo"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Registo de confirmação"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Eliminar todo o registo"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Eliminar registo"</string>
+    <string name="start_server" msgid="9034821924409165795">"Iniciar servidor TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Notificar servidor TCP"</string>
+</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
new file mode 100644
index 0000000..4add8f6
--- /dev/null
+++ b/res/values-pt/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Acessar o gerenciador de download."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Permite que o aplicativo acesse e use o gerenciador BluetoothShare para transferir arquivos."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Dispositivo desconhecido"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Modo para avião"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Você não pode usar Bluetooth quando o telefone estiver no modo para avião."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Para usar os serviços de Bluetooth, é necessário ativar o Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Ativar Bluetooth agora?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Cancelar"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Ativar"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transferência de arquivo"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\" deseja enviar %2$s (%3$s) para você. "\n\n" Aceitar o arquivo?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Recusar"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Aceitar"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"Ok"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Tempo limite esgotado para aceitar o arquivo recebido de \"%1$s\""</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Compartilhamento Bluetooth: arquivo recebido"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Confirme que deseja receber esse arquivo"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Chegou um arquivo de outro dispositivo. Confirme se deseja receber esse arquivo"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Compartilhamento Bluetooth: recebendo %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Compartilhamento Bluetooth: %1$s recebido"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100% concluído"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Compartilhamento Bluetooth: o arquivo %1$s não foi recebido"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Compartilhamento Bluetooth: enviando %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Compartilhamento Bluetooth: enviando arquivo"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Compartilhamento Bluetooth: %1$s enviado"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100% concluído"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Compartilhamento Bluetooth: o arquivo %1$s não foi enviado"</string>
+    <string name="download_title" msgid="3353228219772092586">"Transferência de arquivo"</string>
+    <string name="download_line1" msgid="3470164761579278582">"De: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Arquivo: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Tamanho do arquivo: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Recebendo arquivo…"</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Parar"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Ocultar"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Arquivo não recebido"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Arquivo: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Motivo da falha: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Arquivo recebido"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Abrir"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Para: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Tipo de arquivo: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Enviando arquivo…"</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Arquivo enviado"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"O arquivo não foi enviado para \"%1$s\"."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Arquivo: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Tentar novamente"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Fechar"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Arquivo desconhecido"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Nenhum aplicativo suporta este tipo de arquivo. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"O arquivo não existe"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"O arquivo não existe! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Aguarde..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Ativando bluetooth…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"O arquivo será recebido. Verifique o andamento no painel de Notificações."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"O arquivo não será recebido."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Parou de receber o arquivo de \"%1$s\""</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Enviando arquivo para \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Enviando %1$s arquivos para \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Transferência de arquivo para \"%1$s\" interrompida"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Transferência de arquivos de %1$s para \"%2$s\" interrompida"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Não há espaço suficiente no cartão SD para salvar o arquivo de \"%1$s\""</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Espaço necessário: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" tentou enviar %2$s arquivos."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"No entanto, este aparelho pode receber apenas um arquivo por vez. Peça para a outra pessoa enviar os %1$s arquivos restantes um a um."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\" não está disponível."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Não é possível emparelhar com \"%1$s\"."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"A conexão foi interrompida."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\" não suporta transferências de arquivo."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" não pode aceitar %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" recusou o arquivo."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Não foi possível enviar o arquivo."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Não foi possível receber o arquivo."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Cartão de memória bloqueado. É necessário desbloquear o cartão de memória para salvar os arquivos."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Há um arquivo sendo transferido para um dispositivo diferente. Aguarde…"</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth está ocupado no momento. Desative outro recurso Bluetooth e tente novamente."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Transferência de arquivo não iniciada"</string>
+    <string name="status_running" msgid="2695810336448055064">"Transferência de arquivo em andamento"</string>
+    <string name="status_success" msgid="2268261336330060663">"A transferência do arquivo foi concluída com êxito"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"O telefone não suporta esse tipo de conteúdo"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Essa transferência foi proibida pelo aparelho de destino"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Esta transferência foi cancelada pelo usuário"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Problema de armazenamento"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Não há cartão SD. Insira um cartão SD para salvar os arquivos transferidos."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Falha na conexão"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"A solicitação não pode ser tratada corretamente"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Erro desconhecido"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth recebido"</string>
+</resources>
diff --git a/res/values-pt/strings_pbap.xml b/res/values-pt/strings_pbap.xml
new file mode 100644
index 0000000..020e9f8
--- /dev/null
+++ b/res/values-pt/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Pbap Bluetooth"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s gostaria de acessar os seus contatos e histórico de chamadas. Conceder acesso a %2$s?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Acesso à Agenda"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Digite a chave de sessão para %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"É necessário fornecer a chave de sessão Bluetooth"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Tempo limite esgotado para aceitar a conexão com %1$s"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Tempo limite esgotado para inserir a chave de sessão com %1$s"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Transferência por Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Não foi possível estabelecer compartilhamento de agenda com"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s se conectou ao seu telefone."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s se desconectou do seu telefone."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Solicitação de acesso à Agenda"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Solicitação de PBAP"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Permitir acesso à Agenda por %1$s"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Solicitação de autenticação Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Chave de sessão"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Digite a chave de sessão para %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Sempre permitir?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Kit para carro"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Nome desconhecido"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Meu nome"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-pt/test_strings.xml b/res/values-pt/test_strings.xml
new file mode 100644
index 0000000..5463548
--- /dev/null
+++ b/res/values-pt/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, atividade de teste"</string>
+    <string name="app_name" msgid="1203877025577761792">"Compartilhamento Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Inserir registro"</string>
+    <string name="update_record" msgid="2480425402384910635">"Confirmar registro"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Confirmar registro"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Excluir todos os registros"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Excluir registro"</string>
+    <string name="start_server" msgid="9034821924409165795">"Iniciar servidor TCP"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Notificar servidor TCP"</string>
+</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
new file mode 100644
index 0000000..1806dbd
--- /dev/null
+++ b/res/values-ru/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Доступ к диспетчеру загрузки."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Позволяет приложению получать доступ к диспетчеру передачи файлов по каналу Bluetooth и использовать его для передачи файлов."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Неизвестное устройство"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Режим полета"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Невозможно использовать функцию Bluetooth в режиме полета."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Чтобы использовать службы Bluetooth, включите адаптер Bluetooth."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Включить Bluetooth?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Отмена"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Включить"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Передача файла"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\"отправляет вам %2$s (%3$s). "\n\n" Принять файл?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Отклонить"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Принять"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"ОК"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Истекло время ожидания при получении файла с устройства \"%1$s\"."</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Передача по Bluetooth: входящий файл"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Подтвердите прием этого файла."</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Входящий файл с другого устройства. Подтвердите получение этого файла."</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Передача по Bluetooth: получение %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Передача по Bluetooth: получено %1$s"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100% завершено."</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Передача по Bluetooth: файл %1$s не получен"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Передача по Bluetooth: отправка %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Передача по Bluetooth: отправка файла"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Передача по Bluetooth: отправлено %1$s"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100% завершено."</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Передача по Bluetooth: файл %1$s не отправлен"</string>
+    <string name="download_title" msgid="3353228219772092586">"Передача файла"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Источник: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Файл: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Размер файла: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Получение файла..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Остановить"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Скрыть"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Файл не получен."</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Файл: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Причина ошибки: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"ОК"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Файл получен."</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Открыть"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Назначение: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Тип файла: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Отправка файла..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Файл отправлен"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"ОК"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Файл не был отправлен на устройство %1$s."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Файл: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Повторить"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Закрыть"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"ОК"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Неизвестный файл"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Отсутствует приложение для открытия этого файла. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Файл не существует."</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Файл не существует! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Подождите..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Включение Bluetooth…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Этот файл будет получен. Ход выполнения отображается на панели уведомлений."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Этот файл не будет получен."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Получение файла с устройства \"%1$s\" прервано."</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Отправка файла на \"%1$s\""</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Отправка файлов (%1$s) на устройство \"%2$s\""</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Отправка файла на \"%1$s\" остановлена"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Отправка файлов (%1$s) на устройство \"%2$s\" остановлена."</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Недостаточно места на SD-карте для сохранения файла, полученного от \"%1$s\"."</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Необходимо свободного места: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"Попытка отправить вам файлы (%2$s) с устройства \"%1$s\"."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Однако это устройство одновременно может принимать только один файл. Попросите передающую сторону отправить оставшиеся файлы (%1$s) по отдельности."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"Устройство \"%1$s\" недоступно."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Не удается установить соединение с \"%1$s\"."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Соединение прервано."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"Устройство \"%1$s\" не поддерживает передачу файлов."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\" не может принять %2$s."</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"Устройство \"%1$s\" отклонило прием файла."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Не удается отправить файл."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Не удается получить файл."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Карта памяти заблокирована. Чтобы сохранить файлы, необходимо разблокировать карту памяти."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Файл передается на другое устройство. Подождите..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Канал Bluetooth занят. Отключите другую функцию Bluetooth и повторите попытку."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Передача файла еще не началась."</string>
+    <string name="status_running" msgid="2695810336448055064">"Выполняется передача файла."</string>
+    <string name="status_success" msgid="2268261336330060663">"Передача файла успешно завершена."</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Телефон не может обработать этот тип содержания."</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Передача запрещена на принимающем устройстве"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Передача отменена пользователем."</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Ошибка хранилища"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Отсутствует SD-карта. Вставьте SD-карту для сохранения переданных файлов."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Ошибка подключения"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Невозможно правильно обработать запрос."</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Неизвестная ошибка"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Получено по Bluetooth"</string>
+</resources>
diff --git a/res/values-ru/strings_pbap.xml b/res/values-ru/strings_pbap.xml
new file mode 100644
index 0000000..873fd95
--- /dev/null
+++ b/res/values-ru/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"Устройство %1$s пытается получить доступ к вашим контактам и истории вызовов. Предоставить доступ для %2$s?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Доступ к телефонной книге"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Введите ключ сеанса для %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Необходим ключ сеанса Bluetooth."</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Истекло время ожидания при установке подключения к %1$s."</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Истекло время ожидания ввода ключа сеанса для связи с %1$s."</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Передача по Bluetooth"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Невозможно обменяться телефонной книгой с устройством"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"Устройство %1$s подключено к вашему телефону."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"Устройство %1$s отключено от вашего телефона."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Запрос на доступ к телефонной книге"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"Запрос PBAP"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Разрешить устройству %1$s доступ к телефонной книге"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Запрос на аутентификацию Obex"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Ключ сеанса"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Введите ключ сеанса для %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Всегда разрешать?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Автомобильный комплект"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Неизвестное имя"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Мое название"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-ru/test_strings.xml b/res/values-ru/test_strings.xml
new file mode 100644
index 0000000..3e6196a
--- /dev/null
+++ b/res/values-ru/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello, World! (проверка связи)"</string>
+    <string name="app_name" msgid="1203877025577761792">"Передача по Bluetooth"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Вставить запись"</string>
+    <string name="update_record" msgid="2480425402384910635">"Подтвердить запись"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Запись подтверждения"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Удалить все записи"</string>
+    <string name="ok_button" msgid="6519033415223065454">"ОК"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Удалить запись"</string>
+    <string name="start_server" msgid="9034821924409165795">"Запустить TCP-сервер"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Уведомить TCP-сервер"</string>
+</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
new file mode 100644
index 0000000..665c890
--- /dev/null
+++ b/res/values-sv/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Åtkomst till hämtningshanterare."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Tillåter att programmet använder BluetoothShare-hanteraren till att överföra filer."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Okänd enhet"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Flygplansläge"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Det går inte att använda Bluetooth när telefonen är i flygplansläge."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Du måste aktivera Bluetooth för att kunna använda Bluetooth-tjänster."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Vill du aktivera Bluetooth nu?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"Avbryt"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Aktivera"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Filöverföring"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"%1$s vill skicka %2$s (%3$s) till dig. "\n\n" Vill du ta emot filen?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Avvisa"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Godkänn"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"OK"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"Tidsgränsen för godkännande av inkommande fil från %1$s har överskridits"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth-delning: inkommande fil"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Bekräfta att du vill ta emot filen"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"En fil är på väg från en annan enhet. Bekräfta att du vill ta emot filen"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Bluetooth-delning: tar emot %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Bluetooth-delning: %1$s har tagits emot"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"100 % slutfört"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Bluetooth-delning: filen %1$s har inte tagits emot"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Bluetooth-delning: skickar %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Bluetooth-delning: skickar fil"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Bluetooth-delning: %1$s har skickats"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"100 % slutfört"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Bluetooth-delning: filen %1$s har inte skickats"</string>
+    <string name="download_title" msgid="3353228219772092586">"Filöverföring"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Från: %1$s"</string>
+    <string name="download_line2" msgid="65085079456902842">"Fil: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Filstorlek: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Tar emot fil ..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Stopp"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Dölj"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Filen har inte tagits emot"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Fil: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Felorsak: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"OK"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Filen har tagits emot"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Öppna"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Till: %1$s"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Filtyp: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Skickar fil ..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Filen har skickats"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Filen skickades inte till %1$s."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Fil: %1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Försök igen"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Stäng"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Okänd fil"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Det finns inget program som kan hantera filtypen. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Filen finns inte"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Filen finns inte! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Vänta …"</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Aktiverar Bluetooth …"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Filen tas emot. Du kan se förloppet på aviseringspanelen."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Filen tas inte emot."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"Slutade ta emot fil från %1$s"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Skickar fil till %1$s"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"Skickar %1$s filer till %2$s"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"Filöverföringen till %1$s har avbrutits"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"Slutade skicka %1$s filer till %2$s"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"Det finns för lite utrymme på SD-kortet för att spara filen från %1$s"</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Utrymmesbehov: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"%1$s försökte skicka dig %2$s filer."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Den här enheten kan dock bara ta emot en fil i taget. Be den andra parten att skicka återstående 1$s filer var och en för sig."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"%1$s är inte tillgänglig."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"Det går inte att parkoppla med %1$s."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Anslutningen avbröts."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"%1$s stöder inte filöverföring."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"%1$s kan inte ta emot %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"%1$s har avvisat filen."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Det gick inte att skicka filen."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Det gick inte att ta emot filen."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Minneskortet är låst. Du måste låsa upp minneskortet för att kunna spara filer."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"En fil överförs till en annan enhet. Vänta ..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth-anslutningen är upptagen. Du kan stänga av en annan Bluetooth-funktion och försöka igen."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Filöverföringen har inte börjat"</string>
+    <string name="status_running" msgid="2695810336448055064">"Filöverföring pågår"</string>
+    <string name="status_success" msgid="2268261336330060663">"Filöverföringen har slutförts"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Telefonen kan inte hantera den här typen av innehåll"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Målenheten tillåter inte filöverföringen"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Överföringen har avbrutits av användaren"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Lagringsproblem"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"Det finns inget SD-kort. Sätt i ett SD-kort om du vill spara överförda filer."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Anslutningsfel"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"Begäran kan inte hanteras korrekt"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Okänt fel"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth har tagits emot"</string>
+</resources>
diff --git a/res/values-sv/strings_pbap.xml b/res/values-sv/strings_pbap.xml
new file mode 100644
index 0000000..4cbb64a
--- /dev/null
+++ b/res/values-sv/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth-PBAP"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s vill ha åtkomst till dina kontakter och din samtalshistorik. Vill du ge %2$s åtkomst?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Telefonboksåtkomst"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"Ange sessionsnyckeln för %1$s"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"En Bluetooth-sessionsnyckel krävs"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"Tidsgränsen för godkännande av anslutning till %1$s har överskridits"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"Tiden för inmatning av sessionsnyckel med %1$s har gått ut"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Bluetooth-överföring"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Det gick inte att upprätta telefonboksdelning med"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s är ansluten till telefonen."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s har kopplats från telefonen."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Begäran om åtkomst till telefonboken"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAB-begäran"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Tillåt telefonboksåtkomst av %1$s"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Obex-autentiseringsbegäran"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Sessionsnyckel"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"Ange sessionsnyckeln för %1$s"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Vill du alltid tillåta?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Bilsats"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Okänt namn"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Mitt namn"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-sv/test_strings.xml b/res/values-sv/test_strings.xml
new file mode 100644
index 0000000..2bfcdbe
--- /dev/null
+++ b/res/values-sv/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Bluetooth-delning"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Infoga post"</string>
+    <string name="update_record" msgid="2480425402384910635">"Bekräfta post"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Bekräftelsepost"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Ta bort alla poster"</string>
+    <string name="ok_button" msgid="6519033415223065454">"OK"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Ta bort post"</string>
+    <string name="start_server" msgid="9034821924409165795">"Starta TCP-server"</string>
+    <string name="notify_server" msgid="4369106744022969655">"Avisera TCP-server"</string>
+</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
new file mode 100644
index 0000000..64b55bf
--- /dev/null
+++ b/res/values-tr/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"İndirme yöneticisine erişin."</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"Uygulamaya BluetoothShare yöneticisine erişme ve bunu dosyaları aktarmak için kullanma izni verir."</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"Bilinmeyen cihaz"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Uçak modu"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"Telefon uçak modundayken Bluetooth\'u kullanamazsınız."</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"Bluetooth"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Bluetooth hizmetlerini kullanmak için öncelikle Bluetooth\'u açmanız gerekir."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Bluetooth\'u şimdi açmak istiyor musunuz?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"İptal"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Aç"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Dosya aktarımı"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"\"%1$s\", size %2$s (%3$s) dosyasını göndermek istiyor. "\n\n" Dosyayı kabul ediyor musunuz?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Reddet"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"Kabul Et"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"Tamam"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"\"%1$s\" kaynağından gelen dosyayı kabul etme süresi doldu"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth paylaşımı: Gelen dosya"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"Lütfen bu dosyayı almak istediğinizi onaylayın"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"Başka cihazdan gelen bir dosya var, lütfen bu dosyayı almak istediğinizi onaylayın"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Bluetooth paylaşımı: %1$s alınıyor"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Bluetooth paylaşımı: %1$s alındı"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"%100 tamamlandı"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Bluetooth paylaşımı: %1$s dosyası alınamadı"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Bluetooth paylaşımı: 1$s gönderiliyor"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Bluetooth paylaşımı: Dosya gönderiliyor"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Bluetooth paylaşımı: %1$s gönderildi"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"%100 tamamlandı"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Bluetooth paylaşımı: Dosya %1$s gönderilemedi"</string>
+    <string name="download_title" msgid="3353228219772092586">"Dosya aktarımı"</string>
+    <string name="download_line1" msgid="3470164761579278582">"Gönderen: \"%1$s\""</string>
+    <string name="download_line2" msgid="65085079456902842">"Dosya: %1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"Dosya boyutu: %1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"Dosya alınıyor..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Durdur"</string>
+    <string name="download_ok" msgid="5000360731674466039">"Gizle"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"Dosya alınamadı"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"Dosya: %1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"Hata nedeni: %1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"Tamam"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"Dosya alındı"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"Aç"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"Hedef: \"%1$s\""</string>
+    <string name="upload_line3" msgid="6702013202133020437">"Dosya Türü: %1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"Dosya gönderiliyor..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"Dosya gönderildi"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"Tamam"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"Dosya, \"%1$s\" hedefine gönderilemedi."</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"Dosya: 1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Tekrar dene"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"Kapat"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"Tamam"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"Bilinmeyen dosya"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"Bu dosya türünü işleyecek hiçbir uygulama yok. "\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"Dosya mevcut değil"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"Dosya mevcut değil! "\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"Lütfen bekleyin..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"Bluetooth açılıyor..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Dosya alınacak. İlerlemeyi Bildirimler panelinden izleyebilirsiniz."</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"Dosya alınmayacak."</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"\"%1$s\" kaynağından dosya alımı durduruldu"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"Dosya \"%1$s\" hedefine gönderiliyor"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"%1$s dosyaları \"%2$s\" alıcısına gönderiliyor"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"\"%1$s\" hedefine dosya gönderme işlemi durduruldu"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"%1$s dosyalarının \"%2$s\" hedefine gönderilmesi durduruldu"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"SD kartta, \"%1$s\" kaynağından gelen dosyayı kaydedecek kadar alan yok"</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"Gereken alan: %1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"\"%1$s\" size %2$s dosyalarını gönderme girişiminde bulundu."</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"Ancak, bu cihaz bir seferde yalnızca bir dosya alabilir. Karşı taraftan kalan %1$s dosyayı teker teker göndermesini isteyin."</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"\"%1$s\" kullanılamıyor."</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"\"%1$s\" ile eşleşemiyor."</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"Bağlantı kesildi."</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"\"%1$s\", dosya aktarımlarını desteklemiyor."</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"\"%1$s\", şunu kabul edemez: %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"\"%1$s\" dosyayı kabul etmeyi reddetti."</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"Dosya gönderilemedi."</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"Dosya alınamadı."</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"Bellek kartı kilitli. Dosyaları kaydetmek için bellek kartının kilidini açmanız gerekiyor."</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"Farklı bir cihaza aktarılmakta olan bir dosya var. Lütfen bekleyin..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"Bluetooth şu anda meşgul. Başka bir Bluetooth özelliğini kapatmayı ve tekrar denemeyi tercih edebilirsiniz."</string>
+    <string name="status_pending" msgid="7446884326084324082">"Dosya aktarımı henüz başlatılmadı"</string>
+    <string name="status_running" msgid="2695810336448055064">"Dosya aktarımı devam ediyor"</string>
+    <string name="status_success" msgid="2268261336330060663">"Dosya aktarımı başarıyla tamamlandı"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"Telefon, bu tür içeriği işleyemiyor"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"Bu aktarım hedef cihaz tarafından yasaklandı"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"Bu aktarım kullanıcı tarafından iptal edildi"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"Depolama sorunu"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"SD kart yok. Aktarılan dosyaları kaydetmek için lütfen bir SD kart takın."</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"Bağlantı hatası"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"İstek düzgün bir şekilde işlenemiyor"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"Bilinmeyen hata"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth alındı"</string>
+</resources>
diff --git a/res/values-tr/strings_pbap.xml b/res/values-tr/strings_pbap.xml
new file mode 100644
index 0000000..289e1a1
--- /dev/null
+++ b/res/values-tr/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s kişilerinize ve çağrı geçmişinize erişmek istiyor. Şuna erişim izni verilsin mi?: %2$s"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"Telefon Rehberine Erişim"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"%1$s için oturum anahtarını yazın"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"Bluetooth oturum anahtarı gerekiyor"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"%1$s ile bağlantı yapılmasını kabul etme süresi doldu"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"%1$s ile oturum anahtarını girme süresi doldu"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"Bluetooth aktarımı"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"Şununla telefon rehberi paylaşımı başarıyla gerçekleştirilemedi:"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s telefonunuza bağlandı."</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s ile telefonunuzun bağlantısı kesildi."</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"Telefon rehberine erişim isteği"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAP isteği"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"Telefon rehberine %1$s tarafından erişilmesine izin ver"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Obex kimlik doğrulama isteği"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"Oturum Anahtarı"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"%1$s için oturum anahtarını yazın"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"Her zaman izin verilsin mi?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Araç Kiti"</string>
+    <string name="unknownName" msgid="2841414754740600042">"Bilinmeyen ad"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"Adım"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-tr/test_strings.xml b/res/values-tr/test_strings.xml
new file mode 100644
index 0000000..ae946f0
--- /dev/null
+++ b/res/values-tr/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"Bluetooth Paylaşımı"</string>
+    <string name="insert_record" msgid="1450997173838378132">"Kayıt ekle"</string>
+    <string name="update_record" msgid="2480425402384910635">"Kaydı onayla"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Onay kaydı"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"Tüm kayıtları silin"</string>
+    <string name="ok_button" msgid="6519033415223065454">"Tamam"</string>
+    <string name="delete_record" msgid="4645040331967533724">"Kaydı sil"</string>
+    <string name="start_server" msgid="9034821924409165795">"TCP sunucusunu başlat"</string>
+    <string name="notify_server" msgid="4369106744022969655">"TCP sunucusuna bildir"</string>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..1e6c25f
--- /dev/null
+++ b/res/values-zh-rCN/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"访问下载管理器。"</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"允许应用程序访问 Bluetooth Share 管理器,并使用该管理器传输文件。"</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"蓝牙"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"未知设备"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"飞行模式"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"手机处于飞行模式时,您不能使用蓝牙。"</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"蓝牙"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"若要使用蓝牙服务,您必须先打开蓝牙。"</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"立即打开蓝牙?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"取消"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"打开"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"文件传输"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"“%1$s”想给您发送 %2$s (%3$s)。"\n\n" 是否接受该文件?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"拒绝"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"接受"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"确定"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"接受来自“%1$s”的文件时超时"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Bluetooth Share:传入文件"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"请确认您要接收该文件"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"从其他设备收到一个文件,请确认是否要接收该文件"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"Bluetooth Share:正在接收 %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"Bluetooth Share:已接收 %1$s"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"已完成 100%"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"Bluetooth Share:未收到文件 %1$s"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"Bluetooth Share:正在发送 %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"Bluetooth Share:正在发送文件"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"Bluetooth Share:已发送 %1$s"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"已完成 100%"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"Bluetooth Share:未发送文件 %1$s"</string>
+    <string name="download_title" msgid="3353228219772092586">"文件传输"</string>
+    <string name="download_line1" msgid="3470164761579278582">"发送者:“%1$s”"</string>
+    <string name="download_line2" msgid="65085079456902842">"文件:%1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"文件大小:%1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"正在接收文件..."</string>
+    <string name="download_cancel" msgid="9177305996747500768">"停止"</string>
+    <string name="download_ok" msgid="5000360731674466039">"隐藏"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"未接收到文件"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"文件:%1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"失败原因:%1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"确定"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"已接收文件"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"打开"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"接收者:“%1$s”"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"文件类型:%1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"正在发送文件..."</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"已发送文件"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"确定"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"未将该文件发送至“%1$s”。"</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"文件:%1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"请重试"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"关闭"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"确定"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"未知文件"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"没有可处理此类文件的应用程序。"\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"文件不存在"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"该文件不存在!"\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"请稍候..."</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"正在打开蓝牙..."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"系统将要接收该文件。请在“通知”面板中检查进度。"</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"系统不会接收该文件。"</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"已停止接收来自“%1$s”的文件"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"正向“%1$s”发送文件"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"正在向“%2$s”发送 %1$s 个文件"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"已停止向“%1$s”发送文件"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"已停止向“%2$s”发送 %1$s 个文件"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"SD 卡存储空间不足,无法保存来自“%1$s”的文件"</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"所需空间:%1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"“%1$s”曾尝试向您发送 %2$s 个文件。"</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"但是,该设备一次只能接收一个文件。请让对方逐个发送其余的 %1$s 个文件。"</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"“%1$s”不可用。"</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"无法与“%1$s”配对。"</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"连接中断。"</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"“%1$s”不支持文件传输。"</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"“%1$s”无法接收 %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"“%1$s”拒绝接受该文件。"</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"无法发送该文件。"</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"系统不会接收该文件。"</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"内存卡已锁定。您必须将内存卡解锁,才可保存文件。"</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"系统正向其他设备传输一个文件。请稍候..."</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"蓝牙正忙。请关闭其他蓝牙功能后重试。"</string>
+    <string name="status_pending" msgid="7446884326084324082">"尚未开始传输文件"</string>
+    <string name="status_running" msgid="2695810336448055064">"正在传输文件"</string>
+    <string name="status_success" msgid="2268261336330060663">"已成功完成文件传输"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"该手机无法处理此类内容"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"目标设备禁止该传输"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"用户已取消该传输"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"存储问题"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"无 SD 卡。请插入 SD 卡保存传输的文件。"</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"连接失败"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"无法正确处理请求"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"未知错误"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"Bluetooth received"</string>
+</resources>
diff --git a/res/values-zh-rCN/strings_pbap.xml b/res/values-zh-rCN/strings_pbap.xml
new file mode 100644
index 0000000..e9903b2
--- /dev/null
+++ b/res/values-zh-rCN/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"Bluetooth Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s 想访问您的通讯录和通话记录。是否允许访问 %2$s?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"访问通讯录"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"键入 %1$s 的会话密钥"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"需要提供蓝牙会话密钥"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"接受与 %1$s 的连接时超时"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"输入带有 %1$s 的会话密钥时超时"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"蓝牙传输"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"无法成功分享通讯录"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s 已连接到您的手机。"</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s 已与您的手机断开连接。"</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"访问通讯录请求"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAP 请求"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"允许 %1$s 访问通讯录"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"OBEX 身份验证请求"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"会话密钥"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"键入 %1$s 的会话密钥"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"始终允许吗?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"Carkit"</string>
+    <string name="unknownName" msgid="2841414754740600042">"未知名称"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"我的名字"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-zh-rCN/test_strings.xml b/res/values-zh-rCN/test_strings.xml
new file mode 100644
index 0000000..883fafe
--- /dev/null
+++ b/res/values-zh-rCN/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"世界,你好,测试活动"</string>
+    <string name="app_name" msgid="1203877025577761792">"Bluetooth Share"</string>
+    <string name="insert_record" msgid="1450997173838378132">"插入记录"</string>
+    <string name="update_record" msgid="2480425402384910635">"确认记录"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Ack 记录"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"删除所有记录"</string>
+    <string name="ok_button" msgid="6519033415223065454">"确定"</string>
+    <string name="delete_record" msgid="4645040331967533724">"删除记录"</string>
+    <string name="start_server" msgid="9034821924409165795">"启动 TCP 服务器"</string>
+    <string name="notify_server" msgid="4369106744022969655">"通知 TCP 服务器"</string>
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..012c125
--- /dev/null
+++ b/res/values-zh-rTW/strings.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"存取下載管理員。"</string>
+    <string name="permdesc_bluetoothShareManager" msgid="8739116224907566462">"允許應用程式存取 BluetoothShare 管理員並使用它傳輸檔案。"</string>
+    <string name="bt_share_picker_label" msgid="6268100924487046932">"藍牙"</string>
+    <string name="unknown_device" msgid="9221903979877041009">"未知的裝置"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"飛行模式"</string>
+    <string name="airplane_error_msg" msgid="8031842956782631624">"手機處於飛行模式時,無法使用藍牙功能。"</string>
+    <string name="bt_enable_title" msgid="2371250311764527124">"藍牙"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"如果要使用藍牙服務,請先開啟藍牙功能。"</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"要立即開啟藍牙功能嗎?"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"取消"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"開啟"</string>
+    <string name="incoming_file_confirm_title" msgid="8139874248612182627">"檔案傳輸"</string>
+    <string name="incoming_file_confirm_content" msgid="3243341858335056258">"「%1$s」想傳送給您 %2$s (%3$s)。"\n\n"要接收檔案嗎?"</string>
+    <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"拒絕"</string>
+    <string name="incoming_file_confirm_ok" msgid="281462442932231475">"接受"</string>
+    <string name="incoming_file_confirm_timeout_ok" msgid="860272542827665475">"確定"</string>
+    <string name="incoming_file_confirm_timeout_content" msgid="4795632076456456334">"從「%1$s」接受外來檔案時發生作業逾時"</string>
+    <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"藍牙分享:外來檔案"</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="85804056450169291">"請確認您想接收這個檔案"</string>
+    <string name="incoming_file_toast_msg" msgid="5763638110029586276">"有來自其他裝置的外來檔案,請確認您想接收這個檔案"</string>
+    <string name="notification_receiving" msgid="3938472603351415139">"藍牙分享:正在接收 %1$s"</string>
+    <string name="notification_received" msgid="7594846429703561009">"藍牙分享:已接收 %1$s"</string>
+    <string name="notification_received_complete" msgid="8126908549148704724">"已接收所有檔案"</string>
+    <string name="notification_received_fail" msgid="2017422835194149214">"藍牙分享:尚未收到 %1$s 檔案"</string>
+    <string name="notification_sending" msgid="3118149961355869912">"藍牙分享:正在傳送 %1$s"</string>
+    <string name="notification_sending_multi" msgid="8217177886303103438">"藍牙分享:傳送檔案"</string>
+    <string name="notification_sent" msgid="6345048674000520702">"藍牙分享:已傳送 %1$s"</string>
+    <string name="notification_sent_complete" msgid="302943281067557969">"已傳送所有檔案"</string>
+    <string name="notification_sent_fail" msgid="6632995333466325127">"藍牙分享:尚未傳送 %1$s 檔案"</string>
+    <string name="download_title" msgid="3353228219772092586">"檔案傳輸"</string>
+    <string name="download_line1" msgid="3470164761579278582">"來自:「%1$s」"</string>
+    <string name="download_line2" msgid="65085079456902842">"檔案:%1$s"</string>
+    <string name="download_line3" msgid="7381726201104929958">"檔案大小:%1$s"</string>
+    <!-- no translation found for download_line4 (8535996869722666525) -->
+    <skip />
+    <string name="download_line5" msgid="3069560415845295386">"正在接收檔案…"</string>
+    <string name="download_cancel" msgid="9177305996747500768">"停止"</string>
+    <string name="download_ok" msgid="5000360731674466039">"隱藏"</string>
+    <string name="download_fail_line1" msgid="3846450148862894552">"未接收檔案"</string>
+    <string name="download_fail_line2" msgid="3138506111127456201">"檔案:%1$s"</string>
+    <string name="download_fail_line3" msgid="7362197851195915506">"失敗原因:%1$s"</string>
+    <string name="download_fail_ok" msgid="1521733664438320300">"確定"</string>
+    <string name="download_succ_line5" msgid="4509944688281573595">"已接收檔案"</string>
+    <string name="download_succ_ok" msgid="7053688246357050216">"開啟"</string>
+    <string name="upload_line1" msgid="1813850771254840346">"傳送到:「%1$s」"</string>
+    <string name="upload_line3" msgid="6702013202133020437">"檔案類型:%1$s (%2$s)"</string>
+    <string name="upload_line5" msgid="7759322537674229752">"正在傳送檔案…"</string>
+    <string name="upload_succ_line5" msgid="5687317197463383601">"檔案已傳送"</string>
+    <string name="upload_succ_ok" msgid="7705428476405478828">"確定"</string>
+    <string name="upload_fail_line1" msgid="604517433515376787">"檔案並未傳送至「%1$s」。"</string>
+    <string name="upload_fail_line1_2" msgid="5519612777343102736">"檔案:%1$s"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"再試一次"</string>
+    <string name="upload_fail_cancel" msgid="9118496285835687125">"關閉"</string>
+    <string name="bt_error_btn_ok" msgid="5965151173011534240">"確定"</string>
+    <string name="unknown_file" msgid="6092727753965095366">"未知的檔案"</string>
+    <string name="unknown_file_desc" msgid="5845231383612478643">"沒有可處理這種檔案類型的應用程式。"\n</string>
+    <string name="not_exist_file" msgid="2245958259960700777">"檔案不存在"</string>
+    <string name="not_exist_file_desc" msgid="2392808771821464983">"檔案不存在!"\n</string>
+    <string name="enabling_progress_title" msgid="436157952334723406">"請稍候…"</string>
+    <string name="enabling_progress_content" msgid="1291028079299309898">"正在開啟藍牙…"</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"即將接收檔案,請在通知面板中查看進度。"</string>
+    <string name="bt_toast_2" msgid="6357386241778417651">"這個檔案將不會被接收。"</string>
+    <string name="bt_toast_3" msgid="369942279238724592">"已停止從「%1$s」接收檔案"</string>
+    <string name="bt_toast_4" msgid="6320052202214451994">"傳送檔案至「%1$s」"</string>
+    <string name="bt_toast_5" msgid="3481598201604043629">"正在傳送 %1$s 個檔案至「%2$s」"</string>
+    <string name="bt_toast_6" msgid="4157809141200418519">"已停止傳送檔案至「%1$s」"</string>
+    <string name="bt_toast_7" msgid="5720877534395506413">"已停止傳送 %1$s 個檔案至「%2$s」"</string>
+    <string name="bt_sm_2_1" msgid="6625813581585502551">"SD 卡上沒有足夠的空間可儲存來自「%1$s」的檔案"</string>
+    <string name="bt_sm_2_2" msgid="2697023602119073395">"所需儲存空間:%1$s"</string>
+    <string name="bt_sm_3_1" msgid="1658443268226611198">"「%1$s」嘗試傳送給您 %2$s 個檔案。"</string>
+    <string name="bt_sm_3_2" msgid="1234428166130208088">"不過,此裝置一次僅能接收一個檔案。請要求其他人個別傳送其餘的 %1$s 個檔案。"</string>
+    <string name="bt_tf_1" msgid="3566606437748689505">"無法使用「%1$s」。"</string>
+    <string name="bt_tf_2" msgid="7165797096754450809">"無法與「%1$s」配對。"</string>
+    <string name="bt_tf_3" msgid="6899456362281219699">"連線中斷。"</string>
+    <string name="bt_tf_4" msgid="9083752106925339573">"「%1$s」不支援檔案傳輸。"</string>
+    <string name="bt_tf_5" msgid="2971780974433771152">"「%1$s」無法接受 %2$s"</string>
+    <string name="bt_tf_6" msgid="6882241272515770488">"「%1$s」已拒絕接收此檔案。"</string>
+    <string name="bt_tf_7" msgid="2160754176051707109">"無法傳送檔案。"</string>
+    <string name="bt_tf_8" msgid="8284960065910226862">"無法擷取檔案。"</string>
+    <string name="bt_tf_10" msgid="8612767267070143708">"記憶卡已鎖定,您必須將記憶卡解鎖才能儲存檔案。"</string>
+    <string name="bt_tf_12" msgid="1976542340333268388">"目前有一個檔案被傳輸到不同的裝置,請稍候…"</string>
+    <string name="bt_tf_13" msgid="8604795873559730409">"藍牙裝置目前忙碌中,建議您關閉其他藍牙功能並再試一次。"</string>
+    <string name="status_pending" msgid="7446884326084324082">"尚未開始傳輸檔案"</string>
+    <string name="status_running" msgid="2695810336448055064">"正在進行檔案傳輸"</string>
+    <string name="status_success" msgid="2268261336330060663">"已完成檔案傳輸"</string>
+    <string name="status_not_accept" msgid="149046562468006301">"您的手機無法處理這類型的內容"</string>
+    <string name="status_forbidden" msgid="8499236547788725258">"目標裝置禁止此傳輸"</string>
+    <string name="status_canceled" msgid="5514756906439790976">"使用者已取消傳輸作業"</string>
+    <string name="status_file_error" msgid="6125632529575521755">"儲存空間問題"</string>
+    <string name="status_no_sd_card" msgid="5760944071743325592">"沒有 SD 卡,請插入 SD 卡來儲存傳輸的檔案。"</string>
+    <string name="status_connection_error" msgid="7695388111375688873">"連線失敗"</string>
+    <string name="status_protocol_error" msgid="8898902951085043597">"無法正確處理要求"</string>
+    <string name="status_unknown_error" msgid="7441602228720350817">"未知的錯誤"</string>
+    <string name="btopp_live_folder" msgid="7967791481444474554">"已透過藍牙接收"</string>
+</resources>
diff --git a/res/values-zh-rTW/strings_pbap.xml b/res/values-zh-rTW/strings_pbap.xml
new file mode 100644
index 0000000..e1f4da0
--- /dev/null
+++ b/res/values-zh-rTW/strings_pbap.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name" msgid="6916507166414341324">"藍牙 Pbap"</string>
+    <string name="pbap_acceptance_dialog_title" msgid="4606773169019619760">"%1$s 想存取您的通訊錄和通話記錄。要授與 %2$s 存取權嗎?"</string>
+    <string name="pbap_acceptance_dialog_header" msgid="5456473921987026974">"電話簿存取"</string>
+    <string name="pbap_session_key_dialog_title" msgid="3580996574333882561">"輸入 %1$s 的對稱金鑰"</string>
+    <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"必須有藍牙對稱金鑰"</string>
+    <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"接受與 %1$s 的連線時發生作業逾時"</string>
+    <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"%1$s 輸入對稱金鑰時發生作業逾時"</string>
+    <string name="bluetooth_transfer_header" msgid="5088290435827887179">"藍牙傳輸"</string>
+    <string name="bluetooth_transfer_text" msgid="2283895365101625419">"無法建立與以下使用者分享的電話簿"</string>
+    <string name="toast_connected" msgid="5683199843935518834">"%1$s 已連線至您的手機。"</string>
+    <string name="toast_disconnected" msgid="78819084478319889">"%1$s 已中斷手機連線。"</string>
+    <string name="pbap_notif_ticker" msgid="1653350586489072739">"電話簿存取要求"</string>
+    <string name="pbap_notif_title" msgid="2967094612841799440">"PBAP 要求"</string>
+    <string name="pbap_notif_message" msgid="6046731677513301195">"允許 %1$s 存取電話簿"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"Obex 驗證要求"</string>
+    <string name="auth_notif_title" msgid="7599854855681573258">"對稱金鑰"</string>
+    <string name="auth_notif_message" msgid="6667218116427605038">"輸入 %1$s 的對稱金鑰"</string>
+    <string name="alwaysallowed" msgid="1912976993660130849">"永遠允許?"</string>
+    <string name="defaultname" msgid="4821590500649090078">"車用套件"</string>
+    <string name="unknownName" msgid="2841414754740600042">"未知的名稱"</string>
+    <string name="localPhoneName" msgid="2349001318925409159">"我的名稱"</string>
+    <string name="defaultnumber" msgid="8520116145890867338">"000000"</string>
+</resources>
diff --git a/res/values-zh-rTW/test_strings.xml b/res/values-zh-rTW/test_strings.xml
new file mode 100644
index 0000000..2e48039
--- /dev/null
+++ b/res/values-zh-rTW/test_strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hello" msgid="1740533743008967039">"Hello World, TestActivity"</string>
+    <string name="app_name" msgid="1203877025577761792">"藍牙分享"</string>
+    <string name="insert_record" msgid="1450997173838378132">"插入記錄"</string>
+    <string name="update_record" msgid="2480425402384910635">"確認記錄"</string>
+    <string name="ack_record" msgid="6716152390978472184">"Ack 記錄"</string>
+    <string name="deleteAll_record" msgid="4383349788485210582">"刪除所有記錄"</string>
+    <string name="ok_button" msgid="6519033415223065454">"確定"</string>
+    <string name="delete_record" msgid="4645040331967533724">"刪除記錄"</string>
+    <string name="start_server" msgid="9034821924409165795">"啟動 TCP 伺服器"</string>
+    <string name="notify_server" msgid="4369106744022969655">"通知 TCP 伺服器"</string>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..1c8c498
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!--
+        This is the short description of a permission associated with the
+        Bluetooth Share Manager. It is displayed as part of the description of
+        any application that was granted that permission. This specific
+        permission controls access to the Bluetooth Share Manager by
+        applications that initiate Bluetooth share.
+    -->
+    <string name="permlab_bluetoothShareManager">Access download manager.</string>
+    <!--
+        This is the long description of a permission associated with the
+        Android Download Manager. It is displayed as part of the description
+        of any application that was granted that permission. This specific
+        permission controls access to the Download Manager by applications
+        that initiate downloads.
+    -->
+    <string name="permdesc_bluetoothShareManager">Allows the application to access the
+        BluetoothShare manager and to use it to transfer files. </string>
+
+    <!-- string showed on "Share picutre via" dialog -->
+    <string name="bt_share_picker_label">Bluetooth</string>
+
+    <!-- string for "unknown device" -->
+    <string name="unknown_device">Unknown device</string>
+
+    <!-- string for "the title of airplane mode error" -->
+    <string name="airplane_error_title">Airplane mode</string>
+    <!-- string for "error message in airplane mode" -->
+    <string name="airplane_error_msg">You can\'t use Bluetooth when the phone is in airplane mode.</string>
+
+    <!-- Activate Bluetooth Confirmation Dialog -->
+    <!--Title -->
+    <string name="bt_enable_title">Bluetooth</string>
+    <!--Line 1 -->
+    <string name="bt_enable_line1">To use Bluetooth services, you must first turn on Bluetooth.</string>
+    <!--Line 2 -->
+    <string name="bt_enable_line2">Turn on Bluetooth now?</string>
+    <!-- Label for a cancel button. -->
+    <string name="bt_enable_cancel">Cancel</string>
+    <!-- Label for a confirm button.-->
+    <string name="bt_enable_ok">Turn on</string>
+
+    <!-- Bluetooth File Transfer Acceptance Dialog -->
+    <!--Title -->
+    <string name="incoming_file_confirm_title">File transfer</string>
+    <!--content -->
+    <string name="incoming_file_confirm_content">\u0022%1$s\u0022 wants to send you %2$s (%3$s). \n\n Accept the file? </string>
+    <!-- Label for a cancel button. -->
+    <string name="incoming_file_confirm_cancel">Decline</string>
+    <!-- Label for a confirm button.-->
+    <string name="incoming_file_confirm_ok">Accept</string>
+    <!-- Label for timeout OK button.-->
+    <string name="incoming_file_confirm_timeout_ok">Ok</string>
+    <!-- content for timeout-->
+    <string name="incoming_file_confirm_timeout_content">There was time out to accept incoming file from \u0022%1$s\u0022</string>
+
+    <!-- Bluetooth File Transfer Acceptance Notification item -->
+    <string name="incoming_file_confirm_Notification_title">Bluetooth share: Incoming file</string>
+    <string name="incoming_file_confirm_Notification_caption">Please confirm you want to receive this file</string>
+    <string name="incoming_file_toast_msg">There is an incoming file from another device,
+        please confirm that you want to receive this file</string>
+
+    <!-- Inbound File Transfer Progress Notification item -->
+    <!-- label for the notification item of receiving file -->
+    <string name="notification_receiving">Bluetooth share: Receiving %1$s</string>
+    <!-- label for the notification item of received file -->
+    <string name="notification_received">Bluetooth share: Received %1$s</string>
+    <!-- label for the notification item of received file -status -->
+    <string name="notification_received_complete">100% complete</string>
+    <!-- label for the notification item of failed receiving file -->
+    <string name="notification_received_fail">Bluetooth share: File %1$s not received</string>
+
+    <!-- Outbound File Transfer Progress Notification item -->
+    <!-- label for the notification item of sending file -->
+    <string name="notification_sending">Bluetooth share: Sending %1$s</string>
+    <!-- label for the notification item of sending files -->
+    <string name="notification_sending_multi">Bluetooth share: Sending file</string>
+    <!-- label for the notification item of sent file -->
+    <string name="notification_sent">Bluetooth share: Sent %1$s</string>
+    <!-- label for the notification item of sent file -status -->
+    <string name="notification_sent_complete">100% complete</string>
+    <!-- label for the notification item of failed sending file -->
+    <string name="notification_sent_fail">Bluetooth share: File %1$s not sent</string>
+
+    <!-- Bluetooth Download Progress Dialog -->
+    <!--Title -->
+    <string name="download_title">File transfer</string>
+    <!--Line 1 -->
+    <string name="download_line1">From: \u0022%1$s\u0022</string>
+    <!--Line 2 -->
+    <string name="download_line2">File: %1$s</string>
+    <!--Line 3 -->
+    <string name="download_line3">File size: %1$s</string>
+    <!--Line 4 -->
+    <string name="download_line4"></string>
+    <string name="download_line5">Receiving file\u2026</string>
+    <!-- Label for a cancel button. -->
+    <string name="download_cancel">Stop</string>
+    <!-- Label for a hide button.-->
+    <string name="download_ok">Hide</string>
+
+    <!-- Bluetooth failed Download  Dialog -->
+    <!--Line 1  -->
+    <string name="download_fail_line1">File not received</string>
+    <!--Line 2  -->
+    <string name="download_fail_line2">File: %1$s</string>
+    <!--Line 3  -->
+    <string name="download_fail_line3">Failure reason: %1$s</string>
+    <!-- Label for ok button.-->
+    <string name="download_fail_ok">OK</string>
+
+    <!-- Bluetooth Successful Download  Dialog -->
+    <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+    <string name="download_succ_line5">File received</string>
+    <!-- Label for a OK button.-->
+    <string name="download_succ_ok">Open</string>
+
+    <!-- Bluetooth Upload Progress Dialog -->
+    <string name="upload_line1">To: \u0022%1$s\u0022</string>
+    <string name="upload_line3">File Type: %1$s (%2$s)</string>
+    <string name="upload_line5">Sending file\u2026</string>
+
+    <!-- Bluetooth Successful Upload Progress Dialog -->
+    <!--Line 4 -->
+    <string name="upload_succ_line5">File sent</string>
+    <!-- Label for a confirm button.-->
+    <string name="upload_succ_ok">OK</string>
+
+    <!-- Bluetooth Failed Upload File Transfer Dialog -->
+    <string name="upload_fail_line1">The file was not sent to \u0022%1$s\u0022.</string>
+    <string name="upload_fail_line1_2">File: %1$s</string>
+    <!-- Label for a try again button.-->
+    <string name="upload_fail_ok">Try again</string>
+    <!-- Label for a cancel button.-->
+    <string name="upload_fail_cancel">Close</string>
+
+    <!-- Bluetooth error dialog -->
+    <string name="bt_error_btn_ok">OK</string>
+    <string name="unknown_file">Unknown file</string>
+    <string name="unknown_file_desc">There is no application to handle this file type. \n</string>
+    <string name="not_exist_file">File does not exist</string>
+    <string name="not_exist_file_desc">The file does not exist! \n</string>
+
+    <!-- Bluetooth  Enabling progress dialog -->
+    <string name="enabling_progress_title">Please wait\u2026</string>
+    <string name="enabling_progress_content">Turning on bluetooth\u2026</string>
+
+    <!-- Bluetooth Toast Message -->
+    <string name="bt_toast_1">The file will be received. Check progress in the Notifications panel.</string>
+    <string name="bt_toast_2">The file will not be received.</string>
+    <string name="bt_toast_3">Stopped receiving file from \u0022%1$s\u0022</string>
+    <string name="bt_toast_4">Sending file to \u0022%1$s\u0022</string>
+    <string name="bt_toast_5">Sending %1$s files to \u0022%2$s\u0022</string>
+    <string name="bt_toast_6">Stopped sending file to \u0022%1$s\u0022</string>
+    <string name="bt_toast_7">Stopped sending %1$s files to \u0022%2$s\u0022</string>
+
+    <!-- Bluetooth System Messages -->
+    <string name="bt_sm_2_1">There is not enough space on the SD card to save the file from \u0022%1$s\u0022</string>
+    <string name="bt_sm_2_2">Space needed: %1$s</string>
+    <string name="bt_sm_3_1">\u0022%1$s\u0022 attempted to send you %2$s files. </string>
+    <string name="bt_sm_3_2">However, this device can only receive one file at a time. Ask the other party to send the remaining %1$s files individually.</string>
+
+    <!-- Bluetooth Transfer Failure Messages -->
+    <string name="bt_tf_1">\u0022%1$s\u0022 is unavailable.</string>
+    <string name="bt_tf_2">Cannot pair with \u0022%1$s\u0022.</string>
+    <string name="bt_tf_3">The connection was interrupted.</string>
+    <string name="bt_tf_4">\u0022%1$s\u0022 does not support file transfers.</string>
+    <string name="bt_tf_5">\u0022%1$s\u0022 cannot accept %2$s</string>
+    <string name="bt_tf_6">\u0022%1$s\u0022 declined to accept the file.</string>
+    <string name="bt_tf_7">The file could not be sent.</string>
+    <string name="bt_tf_8">The file could not be received.</string>
+    <string name="bt_tf_10">Locked memory card. You must unlock the memory card to save files.</string>
+    <string name="bt_tf_12">There is a file being transferred to a different device. Please wait\u2026</string>
+    <string name="bt_tf_13">Bluetooth is currently busy. Consider turning off another Bluetooth feature and trying again.</string>
+
+    <!-- Bluetooth Transfer Failure Reason -->
+    <string name="status_pending">File transfer not started yet</string>
+    <string name="status_running">File transfer is ongoing</string>
+    <string name="status_success">The file transfer has completed successfully</string>
+    <string name="status_not_accept">The phone can\'t handle this type of content</string>
+    <string name="status_forbidden">This transfer is forbidden by the target device</string>
+    <string name="status_canceled">This transfer was canceled by the user</string>
+    <string name="status_file_error">Storage issue</string>
+    <string name="status_no_sd_card">No SD card. Insert an SD card to save transferred files.</string>
+    <string name="status_connection_error">Connection failure</string>
+    <string name="status_protocol_error">The request can not be handled correctly</string>
+    <string name="status_unknown_error">Unknown error</string>
+
+    <!-- Bluetooth OPP Live Folder -->
+    <string name="btopp_live_folder">Bluetooth received</string>
+
+</resources>
diff --git a/res/values/strings_pbap.xml b/res/values/strings_pbap.xml
new file mode 100644
index 0000000..9c8419a
--- /dev/null
+++ b/res/values/strings_pbap.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="pbap_app_name">Bluetooth Pbap</string>
+    <string name="pbap_acceptance_dialog_title">%1$s would like to access your contacts and call history. Give access to %2$s?</string>
+    <string name="pbap_acceptance_dialog_header">Phonebook Access</string>
+    <string name="pbap_session_key_dialog_title">Type session key for %1$s</string>
+    <string name="pbap_session_key_dialog_header">Bluetooth session key required</string>
+    <string name="pbap_acceptance_timeout_message">There was time out to accept connection with %1$s</string>
+    <string name="pbap_authentication_timeout_message">There was time out to input session key with %1$s</string>
+    <string name="bluetooth_transfer_header">Bluetooth transfer</string>
+    <string name="bluetooth_transfer_text">Could not successfully establish phone book sharing with</string>
+    <string name="toast_connected">%1$s connected to your phone. </string>
+    <string name="toast_disconnected">%1$s disconnected from your phone. </string>
+    <string name="pbap_notif_ticker">Phonebook access request</string>
+    <!-- Notification title when a Bluetooth device wants to pair with us -->
+    <string name="pbap_notif_title">PBAP request</string>
+    <!-- Notification message when a Bluetooth device wants to pair with us -->
+    <string name="pbap_notif_message">Allow phonebook access by %1$s</string>
+    <string name="auth_notif_ticker">Obex authentication request</string>
+    <!-- Notification title when a Bluetooth device wants to pair with us -->
+    <string name="auth_notif_title">Session Key</string>
+    <!-- Notification message when a Bluetooth device wants to pair with us -->
+    <string name="auth_notif_message">Type session key for %1$s</string>
+    <string name="alwaysallowed">Always allowed?</string>
+    <string name="defaultname">Carkit</string>
+    <string name="unknownName">Unknown name</string>
+    <string name="localPhoneName">My name</string>
+    <string name="defaultnumber">000000</string>
+</resources>
diff --git a/res/values/test_strings.xml b/res/values/test_strings.xml
new file mode 100644
index 0000000..545f4c1
--- /dev/null
+++ b/res/values/test_strings.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="hello">Hello World, TestActivity</string>
+    <string name="app_name">Bluetooth Share</string>
+    <string name="insert_record">Insert record</string>
+    <string name="update_record">Confirm record</string>
+    <string name="ack_record">Ack record</string>
+    <string name="deleteAll_record">Delete all record</string>
+    <string name="ok_button">OK</string>
+    <string name="delete_record">Delete record</string>
+    <string name="start_server">Start TCP server</string>
+    <string name="notify_server">Notify TCP server</string>
+</resources>
diff --git a/src/com/android/bluetooth/opp/BluetoothOppBatch.java b/src/com/android/bluetooth/opp/BluetoothOppBatch.java
new file mode 100644
index 0000000..5913b4c
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppBatch.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.Log;
+
+import com.google.android.collect.Lists;
+
+/**
+ * This class stores information about a batch of OPP shares that should be
+ * transferred in one session.
+ */
+/*There are a few cases: 1. create a batch for a single file to send
+ * 2. create a batch for multiple files to send
+ * 3. add additional file(s) to existing batch to send
+ * 4. create a batch for receive single file
+ * 5. add additional file to existing batch to receive (this only happens as the server
+ * session notify more files to receive)
+ * 6. Cancel sending a single file
+ * 7. Cancel sending a file from multiple files (implies cancel the transfer, rest of
+ * the unsent files are also canceled)
+ * 8. Cancel receiving a single file
+ * 9. Cancel receiving a file (implies cancel the transfer, no additional files will be received)
+ */
+
+public class BluetoothOppBatch {
+    private static final String TAG = "BtOppBatch";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    public int mId;
+    public int mStatus;
+
+    public final long mTimestamp;
+    public final int mDirection;
+    public final BluetoothDevice mDestination;
+
+    private BluetoothOppBatchListener mListener;
+
+    private final ArrayList<BluetoothOppShareInfo> mShares;
+    private final Context mContext;
+
+    /**
+     * An interface for notifying when BluetoothOppTransferBatch is changed
+     */
+    public interface BluetoothOppBatchListener {
+        /**
+         * Called to notify when a share is added into the batch
+         * @param id , BluetoothOppShareInfo.id
+         */
+        public void onShareAdded(int id);
+
+        /**
+         * Called to notify when a share is deleted from the batch
+         * @param id , BluetoothOppShareInfo.id
+         */
+        public void onShareDeleted(int id);
+
+        /**
+         * Called to notify when the batch is canceled
+         */
+        public void onBatchCanceled();
+    }
+
+    /**
+     * A batch is always created with at least one ShareInfo
+     * @param context, Context
+     * @param info, BluetoothOppShareInfo
+     */
+    public BluetoothOppBatch(Context context, BluetoothOppShareInfo info) {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        mContext = context;
+        mShares = Lists.newArrayList();
+        mTimestamp = info.mTimestamp;
+        mDirection = info.mDirection;
+        mDestination = adapter.getRemoteDevice(info.mDestination);
+        mStatus = Constants.BATCH_STATUS_PENDING;
+        mShares.add(info);
+
+        if (V) Log.v(TAG, "New Batch created for info " + info.mId);
+    }
+
+    /**
+     * Add one share into the batch.
+     */
+    /* There are 2 cases: Service scans the databases and it's multiple send
+     * Service receives database update and know additional file should be received
+     */
+
+    public void addShare(BluetoothOppShareInfo info) {
+        mShares.add(info);
+        if (mListener != null) {
+            mListener.onShareAdded(info.mId);
+        }
+    }
+
+    /**
+     * Delete one share from the batch. Not used now.
+     */
+    /*It should only be called under requirement that cancel one single share, but not to
+     * cancel the whole batch. Currently we assume "cancel" is to cancel whole batch.
+     */
+    public void deleteShare(BluetoothOppShareInfo info) {
+        if (info.mStatus == BluetoothShare.STATUS_RUNNING) {
+            info.mStatus = BluetoothShare.STATUS_CANCELED;
+            if (info.mDirection == BluetoothShare.DIRECTION_INBOUND && info.mFilename != null) {
+                new File(info.mFilename).delete();
+            }
+        }
+
+        if (mListener != null) {
+            mListener.onShareDeleted(info.mId);
+        }
+    }
+
+    /**
+     * Cancel the whole batch.
+     */
+    /* 1) If the batch is running, stop the transfer
+     * 2) Go through mShares list and mark all incomplete share as CANCELED status
+     * 3) update ContentProvider for these canceled transfer
+     */
+    public void cancelBatch() {
+        if (V) Log.v(TAG, "batch " + this.mId + " is canceled");
+
+        if (mListener != null) {
+            mListener.onBatchCanceled();
+        }
+        //TODO investigate if below code is redundant
+        for (int i = mShares.size() - 1; i >= 0; i--) {
+            BluetoothOppShareInfo info = mShares.get(i);
+
+            if (info.mStatus < 200) {
+                if (info.mDirection == BluetoothShare.DIRECTION_INBOUND && info.mFilename != null) {
+                    new File(info.mFilename).delete();
+                }
+                if (V) Log.v(TAG, "Cancel batch for info " + info.mId);
+
+                Constants.updateShareStatus(mContext, info.mId, BluetoothShare.STATUS_CANCELED);
+            }
+        }
+        mShares.clear();
+    }
+
+    /** check if a specific share is in this batch */
+    public boolean hasShare(BluetoothOppShareInfo info) {
+        return mShares.contains(info);
+    }
+
+    /** if this batch is empty */
+    public boolean isEmpty() {
+        return (mShares.size() == 0);
+    }
+
+    /**
+     * Get the running status of the batch
+     * @return
+     */
+
+    /** register a listener for the batch change */
+    public void registerListern(BluetoothOppBatchListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Get the first pending ShareInfo of the batch
+     * @return BluetoothOppShareInfo, for the first pending share, or null if
+     *         none exists
+     */
+    public BluetoothOppShareInfo getPendingShare() {
+        for (int i = 0; i < mShares.size(); i++) {
+            BluetoothOppShareInfo share = mShares.get(i);
+            if (share.mStatus == BluetoothShare.STATUS_PENDING) {
+                return share;
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppBtEnableActivity.java b/src/com/android/bluetooth/opp/BluetoothOppBtEnableActivity.java
new file mode 100644
index 0000000..029c4f8
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppBtEnableActivity.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+/**
+ * This class is designed to show BT enable confirmation dialog;
+ */
+public class BluetoothOppBtEnableActivity extends AlertActivity implements
+        DialogInterface.OnClickListener {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Set up the "dialog"
+        final AlertController.AlertParams p = mAlertParams;
+        p.mIconId = android.R.drawable.ic_dialog_alert;
+        p.mTitle = getString(R.string.bt_enable_title);
+        p.mView = createView();
+        p.mPositiveButtonText = getString(R.string.bt_enable_ok);
+        p.mPositiveButtonListener = this;
+        p.mNegativeButtonText = getString(R.string.bt_enable_cancel);
+        p.mNegativeButtonListener = this;
+        setupAlert();
+    }
+
+    private View createView() {
+        View view = getLayoutInflater().inflate(R.layout.confirm_dialog, null);
+        TextView contentView = (TextView)view.findViewById(R.id.content);
+        contentView.setText(getString(R.string.bt_enable_line1) + "\n\n"
+                + getString(R.string.bt_enable_line2) + "\n");
+
+        return view;
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                BluetoothOppManager mOppManager = BluetoothOppManager.getInstance(this);
+                mOppManager.enableBluetooth(); // this is an asyn call
+                mOppManager.mSendingFlag = true;
+
+                Toast.makeText(this, getString(R.string.enabling_progress_content),
+                        Toast.LENGTH_SHORT).show();
+
+                Intent in = new Intent(this, BluetoothOppBtEnablingActivity.class);
+                in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                this.startActivity(in);
+
+                finish();
+                break;
+
+            case DialogInterface.BUTTON_NEGATIVE:
+                finish();
+                break;
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppBtEnablingActivity.java b/src/com/android/bluetooth/opp/BluetoothOppBtEnablingActivity.java
new file mode 100644
index 0000000..d1b0d52
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppBtEnablingActivity.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * This class is designed to show BT enabling progress.
+ */
+public class BluetoothOppBtEnablingActivity extends AlertActivity {
+    private static final String TAG = "BluetoothOppEnablingActivity";
+
+    private static final boolean D = Constants.DEBUG;
+
+    private static final boolean V = Constants.VERBOSE;
+
+    private static final int BT_ENABLING_TIMEOUT = 0;
+
+    private static final int BT_ENABLING_TIMEOUT_VALUE = 20000;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+        registerReceiver(mBluetoothReceiver, filter);
+
+        // Set up the "dialog"
+        final AlertController.AlertParams p = mAlertParams;
+        p.mIconId = android.R.drawable.ic_dialog_info;
+        p.mTitle = getString(R.string.enabling_progress_title);
+        p.mView = createView();
+        setupAlert();
+
+        // Add timeout for enabling progress
+        mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(BT_ENABLING_TIMEOUT),
+                BT_ENABLING_TIMEOUT_VALUE);
+    }
+
+    private View createView() {
+        View view = getLayoutInflater().inflate(R.layout.bt_enabling_progress, null);
+        TextView contentView = (TextView)view.findViewById(R.id.progress_info);
+        contentView.setText(getString(R.string.enabling_progress_content));
+
+        return view;
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            if (D) Log.d(TAG, "onKeyDown() called; Key: back key");
+            mTimeoutHandler.removeMessages(BT_ENABLING_TIMEOUT);
+            cancelSendingProgress();
+        }
+        return true;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mBluetoothReceiver);
+    }
+
+    private final Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case BT_ENABLING_TIMEOUT:
+                    if (V) Log.v(TAG, "Received BT_ENABLING_TIMEOUT msg.");
+                    cancelSendingProgress();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (V) Log.v(TAG, "Received intent: " + action) ;
+            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+                switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
+                    case BluetoothAdapter.STATE_ON:
+                        mTimeoutHandler.removeMessages(BT_ENABLING_TIMEOUT);
+                        finish();
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    };
+
+    private void cancelSendingProgress() {
+        BluetoothOppManager mOppManager = BluetoothOppManager.getInstance(this);
+        if (mOppManager.mSendingFlag) {
+            mOppManager.mSendingFlag = false;
+        }
+        finish();
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppBtErrorActivity.java b/src/com/android/bluetooth/opp/BluetoothOppBtErrorActivity.java
new file mode 100644
index 0000000..9fa6e12
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppBtErrorActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+/**
+ * This class is designed to show BT error messages;
+ */
+public class BluetoothOppBtErrorActivity extends AlertActivity implements
+        DialogInterface.OnClickListener {
+
+    private String mErrorContent;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+        String mErrorTitle = intent.getStringExtra("title");
+        mErrorContent = intent.getStringExtra("content");
+
+        // Set up the "dialog"
+        final AlertController.AlertParams p = mAlertParams;
+        p.mIconId = android.R.drawable.ic_dialog_alert;
+        p.mTitle = mErrorTitle;
+        p.mView = createView();
+        p.mPositiveButtonText = getString(R.string.bt_error_btn_ok);
+        p.mPositiveButtonListener = this;
+        setupAlert();
+    }
+
+    private View createView() {
+        View view = getLayoutInflater().inflate(R.layout.confirm_dialog, null);
+        TextView contentView = (TextView)view.findViewById(R.id.content);
+        contentView.setText(mErrorContent);
+        return view;
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                break;
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java b/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java
new file mode 100644
index 0000000..fdc332c
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppIncomingFileConfirmActivity.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.text.format.Formatter;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+/**
+ * This class is designed to ask user to confirm if accept incoming file;
+ */
+public class BluetoothOppIncomingFileConfirmActivity extends AlertActivity implements
+        DialogInterface.OnClickListener {
+    private static final String TAG = "BluetoothIncomingFileConfirmActivity";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    private static final int DISMISS_TIMEOUT_DIALOG = 0;
+
+    private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000;
+
+    private static final String PREFERENCE_USER_TIMEOUT = "user_timeout";
+
+    private BluetoothOppTransferInfo mTransInfo;
+
+    private Uri mUri;
+
+    private ContentValues mUpdateValues;
+
+    private TextView mContentView;
+
+    private boolean mTimeout = false;
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothShare.USER_CONFIRMATION_TIMEOUT_ACTION.equals(intent.getAction())) {
+                return;
+            }
+            onTimeout();
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+        mUri = intent.getData();
+        mTransInfo = new BluetoothOppTransferInfo();
+        mTransInfo = BluetoothOppUtility.queryRecord(this, mUri);
+        if (mTransInfo == null) {
+            if (V) Log.e(TAG, "Error: Can not get data from db");
+            finish();
+            return;
+        }
+
+        // Set up the "dialog"
+        final AlertController.AlertParams p = mAlertParams;
+        p.mIconId = android.R.drawable.ic_dialog_info;
+        p.mTitle = getString(R.string.incoming_file_confirm_title);
+        p.mView = createView();
+        p.mPositiveButtonText = getString(R.string.incoming_file_confirm_ok);
+        p.mPositiveButtonListener = this;
+        p.mNegativeButtonText = getString(R.string.incoming_file_confirm_cancel);
+        p.mNegativeButtonListener = this;
+        setupAlert();
+        if (V) Log.v(TAG, "mTimeout: " + mTimeout);
+        if (mTimeout) {
+            onTimeout();
+        }
+
+        if (V) Log.v(TAG, "BluetoothIncomingFileConfirmActivity: Got uri:" + mUri);
+
+        registerReceiver(mReceiver, new IntentFilter(
+                BluetoothShare.USER_CONFIRMATION_TIMEOUT_ACTION));
+    }
+
+    private View createView() {
+        View view = getLayoutInflater().inflate(R.layout.confirm_dialog, null);
+
+        mContentView = (TextView)view.findViewById(R.id.content);
+
+        String text = getString(R.string.incoming_file_confirm_content, mTransInfo.mDeviceName,
+                mTransInfo.mFileName, Formatter.formatFileSize(this, mTransInfo.mTotalBytes));
+
+        mContentView.setText(text);
+
+        return view;
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                if (!mTimeout) {
+                    // Update database
+                    mUpdateValues = new ContentValues();
+                    mUpdateValues.put(BluetoothShare.USER_CONFIRMATION,
+                            BluetoothShare.USER_CONFIRMATION_CONFIRMED);
+                    this.getContentResolver().update(mUri, mUpdateValues, null, null);
+
+                    Toast.makeText(this, getString(R.string.bt_toast_1), Toast.LENGTH_SHORT).show();
+                }
+                break;
+
+            case DialogInterface.BUTTON_NEGATIVE:
+                // Update database
+                mUpdateValues = new ContentValues();
+                mUpdateValues.put(BluetoothShare.USER_CONFIRMATION,
+                        BluetoothShare.USER_CONFIRMATION_DENIED);
+                this.getContentResolver().update(mUri, mUpdateValues, null, null);
+                break;
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            if (D) Log.d(TAG, "onKeyDown() called; Key: back key");
+            mUpdateValues = new ContentValues();
+            mUpdateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
+            this.getContentResolver().update(mUri, mUpdateValues, null, null);
+
+            Toast.makeText(this, getString(R.string.bt_toast_2), Toast.LENGTH_SHORT).show();
+            finish();
+        }
+        return true;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mTimeout = savedInstanceState.getBoolean(PREFERENCE_USER_TIMEOUT);
+        if (V) Log.v(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout);
+        if (mTimeout) {
+            onTimeout();
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (V) Log.v(TAG, "onSaveInstanceState() mTimeout: " + mTimeout);
+        outState.putBoolean(PREFERENCE_USER_TIMEOUT, mTimeout);
+    }
+
+    private void onTimeout() {
+        mTimeout = true;
+        mContentView.setText(getString(R.string.incoming_file_confirm_timeout_content,
+                mTransInfo.mDeviceName));
+        mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+        mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+                getString(R.string.incoming_file_confirm_timeout_ok));
+
+        mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(DISMISS_TIMEOUT_DIALOG),
+                DISMISS_TIMEOUT_DIALOG_VALUE);
+    }
+
+    private final Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DISMISS_TIMEOUT_DIALOG:
+                    if (V) Log.v(TAG, "Received DISMISS_TIMEOUT_DIALOG msg.");
+                    finish();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
new file mode 100644
index 0000000..d0f9ff3
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import java.util.ArrayList;
+import android.app.Activity;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDevicePicker;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.provider.Settings;
+
+/**
+ * This class is designed to act as the entry point of handling the share intent
+ * via BT from other APPs. and also make "Bluetooth" available in sharing method
+ * selection dialog.
+ */
+public class BluetoothOppLauncherActivity extends Activity {
+    private static final String TAG = "BluetoothLauncherActivity";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+        String action = intent.getAction();
+
+        if (action.equals(Intent.ACTION_SEND) || action.equals(Intent.ACTION_SEND_MULTIPLE)) {
+            /*
+             * Other application is trying to share a file via Bluetooth,
+             * probably Pictures, videos, or vCards. The Intent should contain
+             * an EXTRA_STREAM with the data to attach.
+             */
+            if (action.equals(Intent.ACTION_SEND)) {
+                // TODO(Moto): handle type == null case
+                String type = intent.getType();
+                Uri stream = (Uri)intent.getParcelableExtra(Intent.EXTRA_STREAM);
+                if (stream != null && type != null) {
+                    if (V) Log.v(TAG, "Get ACTION_SEND intent: Uri = " + stream + "; mimetype = "
+                                + type);
+                    // Save type/stream, will be used when adding transfer
+                    // session to DB.
+                    BluetoothOppManager.getInstance(this).saveSendingFileInfo(type,
+                            stream.toString());
+                } else {
+                    Log.e(TAG, "type is null; or sending file URI is null");
+                    finish();
+                    return;
+                }
+            } else if (action.equals(Intent.ACTION_SEND_MULTIPLE)) {
+                ArrayList<Uri> uris = new ArrayList<Uri>();
+                String mimeType = intent.getType();
+                uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+                if (mimeType != null && uris != null) {
+                    if (V) Log.v(TAG, "Get ACTION_SHARE_MULTIPLE intent: uris " + uris + "\n Type= "
+                                + mimeType);
+                    BluetoothOppManager.getInstance(this).saveSendingFileInfo(mimeType, uris);
+                } else {
+                    Log.e(TAG, "type is null; or sending files URIs are null");
+                    finish();
+                    return;
+                }
+            }
+
+            if (isAirplaneModeOn()) {
+                Intent in = new Intent(this, BluetoothOppBtErrorActivity.class);
+                in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                in.putExtra("title", this.getString(R.string.airplane_error_title));
+                in.putExtra("content", this.getString(R.string.airplane_error_msg));
+                this.startActivity(in);
+
+                finish();
+                return;
+            }
+
+            // TODO(Moto): In the future, we may send intent to DevicePickerActivity
+            // directly,
+            // and let DevicePickerActivity to handle Bluetooth Enable.
+            if (!BluetoothOppManager.getInstance(this).isEnabled()) {
+                if (V) Log.v(TAG, "Prepare Enable BT!! ");
+                Intent in = new Intent(this, BluetoothOppBtEnableActivity.class);
+                in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                this.startActivity(in);
+            } else {
+                if (V) Log.v(TAG, "BT already enabled!! ");
+                Intent in1 = new Intent(BluetoothDevicePicker.ACTION_LAUNCH);
+                in1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                in1.putExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
+                in1.putExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
+                        BluetoothDevicePicker.FILTER_TYPE_TRANSFER);
+                in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE,
+                        Constants.THIS_PACKAGE_NAME);
+                in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS,
+                        BluetoothOppReceiver.class.getName());
+
+                this.startActivity(in1);
+            }
+        } else if (action.equals(Constants.ACTION_OPEN)) {
+            Uri uri = getIntent().getData();
+            if (V) Log.v(TAG, "Get ACTION_OPEN intent: Uri = " + uri);
+
+            Intent intent1 = new Intent();
+            intent1.setAction(action);
+            intent1.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+            intent1.setData(uri);
+            this.sendBroadcast(intent1);
+        }
+        finish();
+    }
+
+    /* Returns true if airplane mode is currently on */
+    private final boolean isAirplaneModeOn() {
+        return Settings.System.getInt(this.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
+                0) == 1;
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppLiveFolder.java b/src/com/android/bluetooth/opp/BluetoothOppLiveFolder.java
new file mode 100644
index 0000000..bb69ad9
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppLiveFolder.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.LiveFolders;
+
+// TODO: To make livefolder work, process acore need BluetoothOppProvider permission
+// Or disable provider permission. Need Google's comments
+public class BluetoothOppLiveFolder extends Activity {
+    public static final Uri CONTENT_URI = Uri
+            .parse("content://com.android.bluetooth.opp/live_folders/received");
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final Intent intent = getIntent();
+        final String action = intent.getAction();
+
+        if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
+            setResult(RESULT_OK, createLiveFolder(this, CONTENT_URI, this
+                    .getString(R.string.btopp_live_folder), R.drawable.bt_share));
+        } else {
+            setResult(RESULT_CANCELED);
+        }
+
+        finish();
+    }
+
+    private static Intent createLiveFolder(Context context, Uri uri, String name, int icon) {
+        final Intent intent = new Intent();
+
+        intent.setData(uri);
+        intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT, new Intent(
+                Constants.ACTION_OPEN, BluetoothShare.CONTENT_URI));
+        intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
+        intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON, Intent.ShortcutIconResource
+                .fromContext(context, icon));
+        intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, LiveFolders.DISPLAY_MODE_LIST);
+
+        return intent;
+    }
+
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppManager.java b/src/com/android/bluetooth/opp/BluetoothOppManager.java
new file mode 100644
index 0000000..75e9fd9
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppManager.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * This class provides a simplified interface on top of other Bluetooth service
+ * layer components; Also it handles some Opp application level variables. It's
+ * a singleton got from BluetoothOppManager.getInstance(context);
+ */
+public class BluetoothOppManager {
+    private static final String TAG = "BluetoothOppManager";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    private static BluetoothOppManager INSTANCE;
+
+    /** Used when obtaining a reference to the singleton instance. */
+    private static Object INSTANCE_LOCK = new Object();
+
+    private boolean mInitialized;
+
+    private Context mContext;
+
+    private BluetoothAdapter mAdapter;
+
+    private String mMimeTypeOfSendigFile;
+
+    private String mUriOfSendingFile;
+
+    private String mMimeTypeOfSendigFiles;
+
+    private ArrayList<Uri> mUrisOfSendingFiles;
+
+    private boolean mCanStartTransfer = false;
+
+    private static final String OPP_PREFERENCE_FILE = "OPPMGR";
+
+    private static final String SENDING_FLAG = "SENDINGFLAG";
+
+    private static final String MIME_TYPE = "MIMETYPE";
+
+    private static final String FILE_URI = "FILE_URI";
+
+    private static final String MIME_TYPE_MULTIPLE = "MIMETYPE_MULTIPLE";
+
+    private static final String FILE_URIS = "FILE_URIS";
+
+    private static final String MULTIPLE_FLAG = "MULTIPLE_FLAG";
+
+    private static final String ARRAYLIST_ITEM_SEPERATOR = "!";
+
+    // used to judge if need continue sending process after received a
+    // ENABLED_ACTION
+    public boolean mSendingFlag;
+
+    public boolean mMultipleFlag;
+
+    public int mfileNumInBatch;
+
+    /**
+     * Get singleton instance.
+     */
+    public static BluetoothOppManager getInstance(Context context) {
+        synchronized (INSTANCE_LOCK) {
+            if (INSTANCE == null) {
+                INSTANCE = new BluetoothOppManager();
+            }
+            INSTANCE.init(context);
+
+            return INSTANCE;
+        }
+    }
+
+    /**
+     * init
+     */
+    private boolean init(Context context) {
+        if (mInitialized)
+            return true;
+        mInitialized = true;
+
+        // This will be around as long as this process is
+        mContext = context.getApplicationContext();
+
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mAdapter == null) {
+            if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not started! ");
+        }
+
+        // Restore data from preference
+        restoreApplicationData();
+
+        return true;
+    }
+
+    /**
+     * Restore data from preference
+     */
+    private void restoreApplicationData() {
+        SharedPreferences settings = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0);
+
+        mSendingFlag = settings.getBoolean(SENDING_FLAG, false);
+        mMimeTypeOfSendigFile = settings.getString(MIME_TYPE, null);
+        mUriOfSendingFile = settings.getString(FILE_URI, null);
+        mMimeTypeOfSendigFiles = settings.getString(MIME_TYPE_MULTIPLE, null);
+        mMultipleFlag = settings.getBoolean(MULTIPLE_FLAG, false);
+
+        if (V) Log.v(TAG, "restoreApplicationData! " + mSendingFlag + mMultipleFlag
+                    + mMimeTypeOfSendigFile + mUriOfSendingFile);
+
+        String strUris = settings.getString(FILE_URIS, null);
+        // TODO(Moto): restore mUrisOfSendingFiles from strUris.
+    }
+
+    /**
+     * Save application data to preference, need restore these data later
+     */
+    private void onDestroy() {
+        SharedPreferences.Editor editor = mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0)
+                .edit();
+        editor.putBoolean(SENDING_FLAG, mSendingFlag).commit();
+        editor.putString(MIME_TYPE, mMimeTypeOfSendigFile).commit();
+        editor.putString(FILE_URI, mUriOfSendingFile).commit();
+        editor.putString(MIME_TYPE_MULTIPLE, mMimeTypeOfSendigFiles).commit();
+        editor.putBoolean(MULTIPLE_FLAG, mMultipleFlag).commit();
+        String strUris;
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0, count = mUrisOfSendingFiles.size(); i < count; i++) {
+            Uri uriContent = mUrisOfSendingFiles.get(i);
+            sb.append(uriContent);
+            sb.append(ARRAYLIST_ITEM_SEPERATOR);
+        }
+        strUris = sb.toString();
+        editor.putString(FILE_URIS, strUris).commit();
+        if (V) Log.v(TAG, "finalize is called and application data saved by SharedPreference! ");
+    }
+
+    /**
+     * Save data to preference when this class is destroyed by system due to
+     * memory lack
+     */
+    protected void finalize() throws Throwable {
+        try {
+            onDestroy();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public void saveSendingFileInfo(String mimeType, String uri) {
+        mMultipleFlag = false;
+        mMimeTypeOfSendigFile = mimeType;
+        mUriOfSendingFile = uri;
+        mCanStartTransfer = true;
+    }
+
+    public void saveSendingFileInfo(String mimeType, ArrayList<Uri> uris) {
+        mMultipleFlag = true;
+        mMimeTypeOfSendigFiles = mimeType;
+        mUrisOfSendingFiles = uris;
+        mCanStartTransfer = true;
+    }
+    /**
+     * Get the current status of Bluetooth hardware.
+     * @return true if Bluetooth enabled, false otherwise.
+     */
+    public boolean isEnabled() {
+        if (mAdapter != null) {
+            return mAdapter.isEnabled();
+        } else {
+            if (V) Log.v(TAG, "BLUETOOTH_SERVICE is not available! ");
+            return false;
+        }
+    }
+
+    /**
+     * Enable Bluetooth hardware.
+     */
+    public void enableBluetooth() {
+        if (mAdapter != null) {
+            mAdapter.enable();
+        }
+    }
+
+    /**
+     * Disable Bluetooth hardware.
+     */
+    public void disableBluetooth() {
+        if (mAdapter != null) {
+            mAdapter.disable();
+        }
+    }
+
+    /**
+     * Get device name per bluetooth address.
+     */
+    public String getDeviceName(BluetoothDevice device) {
+        String deviceName;
+
+        deviceName = BluetoothOppPreference.getInstance(mContext).getName(device);
+
+        if (deviceName == null && mAdapter != null) {
+            deviceName = device.getName();
+        }
+
+        if (deviceName == null) {
+            deviceName = mContext.getString(R.string.unknown_device);
+        }
+
+        return deviceName;
+    }
+
+    /**
+     * insert sending sessions to db, only used by Opp application.
+     */
+    public void startTransfer(BluetoothDevice device) {
+        if (device == null) {
+            Log.e(TAG, "Target bt device is null!");
+            return;
+        }
+
+        if (!mCanStartTransfer) {
+            if (V) Log.v(TAG, "No transfer info restored: fileType&fileName");
+            return;
+        }
+
+        if (mMultipleFlag == true) {
+            int count = mUrisOfSendingFiles.size();
+            mfileNumInBatch = count;
+
+            Long ts = System.currentTimeMillis();
+            for (int i = 0; i < count; i++) {
+                Uri fileUri = mUrisOfSendingFiles.get(i);
+                ContentResolver contentResolver = mContext.getContentResolver();
+                String contentType = contentResolver.getType(fileUri);
+                if (V) Log.v(TAG, "Got mimetype: " + contentType + "  Got uri: " + fileUri);
+
+                ContentValues values = new ContentValues();
+                values.put(BluetoothShare.URI, fileUri.toString());
+                values.put(BluetoothShare.MIMETYPE, contentType);
+                values.put(BluetoothShare.DESTINATION, device.getAddress());
+                values.put(BluetoothShare.TIMESTAMP, ts);
+
+                final Uri contentUri = mContext.getContentResolver().insert(
+                        BluetoothShare.CONTENT_URI, values);
+                if (V) Log.v(TAG, "Insert contentUri: " + contentUri + "  to device: "
+                            + getDeviceName(device));
+            }
+        } else {
+            ContentValues values = new ContentValues();
+            values.put(BluetoothShare.URI, mUriOfSendingFile);
+            values.put(BluetoothShare.MIMETYPE, mMimeTypeOfSendigFile);
+            values.put(BluetoothShare.DESTINATION, device.getAddress());
+
+            final Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI,
+                    values);
+            if (V) Log.v(TAG, "Insert contentUri: " + contentUri + "  to device: "
+                        + getDeviceName(device));
+        }
+
+        // reset vars
+        mMimeTypeOfSendigFile = null;
+        mUriOfSendingFile = null;
+        mUrisOfSendingFiles = null;
+        mMultipleFlag = false;
+        mCanStartTransfer = false;
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppNotification.java b/src/com/android/bluetooth/opp/BluetoothOppNotification.java
new file mode 100644
index 0000000..345ec89
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppNotification.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.content.Context;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.RemoteViews;
+import android.os.Process;
+import java.util.HashMap;
+
+/**
+ * This class handles the updating of the Notification Manager for the cases
+ * where there is an ongoing transfer, incoming transfer need confirm and
+ * complete (successful or failed) transfer.
+ */
+class BluetoothOppNotification {
+    private static final String TAG = "BluetoothOppNotification";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    static final String status = "(" + BluetoothShare.STATUS + " == '192'" + ")";
+
+    static final String visible = "(" + BluetoothShare.VISIBILITY + " IS NULL OR "
+            + BluetoothShare.VISIBILITY + " == '" + BluetoothShare.VISIBILITY_VISIBLE + "'" + ")";
+
+    static final String confirm = "(" + BluetoothShare.USER_CONFIRMATION + " == '"
+            + BluetoothShare.USER_CONFIRMATION_CONFIRMED + "' OR "
+            + BluetoothShare.USER_CONFIRMATION + " == '"
+            + BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED + "'" + ")";
+
+    static final String WHERE_RUNNING = status + " AND " + visible + " AND " + confirm;
+
+    static final String WHERE_COMPLETED = BluetoothShare.STATUS + " >= '200' AND " + visible;
+
+    static final String WHERE_CONFIRM_PENDING = BluetoothShare.USER_CONFIRMATION + " == '"
+            + BluetoothShare.USER_CONFIRMATION_PENDING + "'" + " AND " + visible;
+
+    public NotificationManager mNotificationMgr;
+
+    private Context mContext;
+
+    private HashMap<String, NotificationItem> mNotifications;
+
+    private NotificationUpdateThread mUpdateNotificationThread;
+
+    private boolean mPendingUpdate = false;
+
+    private boolean mFinised = false;
+
+    /**
+     * This inner class is used to describe some properties for one transfer.
+     */
+    static class NotificationItem {
+        int id; // This first field _id in db;
+
+        int direction; // to indicate sending or receiving
+
+        int totalCurrent = 0; // current transfer bytes
+
+        int totalTotal = 0; // total bytes for current transfer
+
+        String description; // the text above progress bar
+    }
+
+    /**
+     * Constructor
+     *
+     * @param ctx The context to use to obtain access to the Notification
+     *            Service
+     */
+    BluetoothOppNotification(Context ctx) {
+        mContext = ctx;
+        mNotificationMgr = (NotificationManager)mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        mNotifications = new HashMap<String, NotificationItem>();
+    }
+
+    public void finishNotification() {
+        synchronized (this) {
+            mFinised = true;
+        }
+    }
+
+    /**
+     * Update the notification ui.
+     */
+    public void updateNotification() {
+        synchronized (this) {
+            mPendingUpdate = true;
+            if (mUpdateNotificationThread == null) {
+                mUpdateNotificationThread = new NotificationUpdateThread();
+                mUpdateNotificationThread.start();
+                mFinised = false;
+            }
+        }
+    }
+
+    private class NotificationUpdateThread extends Thread {
+
+        public NotificationUpdateThread() {
+            super("Notification Update Thread");
+        }
+
+        @Override
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            for (;;) {
+                synchronized (this) {
+                    if (mUpdateNotificationThread != this) {
+                        throw new IllegalStateException(
+                                "multiple UpdateThreads in BluetoothOppNotification");
+                    }
+                    if (!mPendingUpdate && mFinised) {
+                        mUpdateNotificationThread = null;
+                        return;
+                    }
+                    mPendingUpdate = false;
+                }
+                updateActiveNotification();
+                updateCompletedNotification();
+                updateIncomingFileConfirmNotification();
+            }
+        }
+    }
+
+    private void updateActiveNotification() {
+        // Active transfers
+        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
+                WHERE_RUNNING, null, BluetoothShare._ID);
+        if (cursor == null) {
+            return;
+        }
+
+        // Collate the notifications
+        mNotifications.clear();
+        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+            int timeStamp = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
+            int dir = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION));
+            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
+            int total = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES));
+            int current = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES));
+
+            String fileName = cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare._DATA));
+            if (fileName == null) {
+                fileName = cursor.getString(cursor
+                        .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
+            }
+            if (fileName == null) {
+                fileName = mContext.getString(R.string.unknown_file);
+            }
+
+            String batchID = Long.toString(timeStamp);
+
+            // sending objects in one batch has same timeStamp
+            if (mNotifications.containsKey(batchID)) {
+                // NOTE: currently no such case
+                // Batch sending case
+            } else {
+                NotificationItem item = new NotificationItem();
+                item.id = id;
+                item.direction = dir;
+                if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
+                    item.description = mContext.getString(R.string.notification_sending, fileName);
+                } else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
+                    item.description = mContext
+                            .getString(R.string.notification_receiving, fileName);
+                } else {
+                    if (V) Log.v(TAG, "mDirection ERROR!");
+                }
+                item.totalCurrent = current;
+                item.totalTotal = total;
+
+                mNotifications.put(batchID, item);
+
+                if (V) Log.v(TAG, "ID=" + item.id + "; batchID=" + batchID + "; totoalCurrent"
+                            + item.totalCurrent + "; totalTotal=" + item.totalTotal);
+            }
+        }
+        cursor.close();
+
+        // Add the notifications
+        for (NotificationItem item : mNotifications.values()) {
+            // Build the RemoteView object
+            RemoteViews expandedView = new RemoteViews(Constants.THIS_PACKAGE_NAME,
+                    R.layout.status_bar_ongoing_event_progress_bar);
+
+            expandedView.setTextViewText(R.id.description, item.description);
+
+            expandedView.setProgressBar(R.id.progress_bar, item.totalTotal, item.totalCurrent,
+                    item.totalTotal == -1);
+
+            expandedView.setTextViewText(R.id.progress_text, BluetoothOppUtility
+                    .formatProgressText(item.totalTotal, item.totalCurrent));
+
+            // Build the notification object
+            Notification n = new Notification();
+            if (item.direction == BluetoothShare.DIRECTION_OUTBOUND) {
+                n.icon = android.R.drawable.stat_sys_upload;
+                expandedView.setImageViewResource(R.id.appIcon, android.R.drawable.stat_sys_upload);
+            } else if (item.direction == BluetoothShare.DIRECTION_INBOUND) {
+                n.icon = android.R.drawable.stat_sys_download;
+                expandedView.setImageViewResource(R.id.appIcon,
+                        android.R.drawable.stat_sys_download);
+            } else {
+                if (V) Log.v(TAG, "mDirection ERROR!");
+            }
+
+            n.flags |= Notification.FLAG_ONGOING_EVENT;
+            n.contentView = expandedView;
+
+            Intent intent = new Intent(Constants.ACTION_LIST);
+            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+            intent.setData(Uri.parse(BluetoothShare.CONTENT_URI + "/" + item.id));
+
+            n.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+            mNotificationMgr.notify(item.id, n);
+        }
+    }
+
+    private void updateCompletedNotification() {
+        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
+                WHERE_COMPLETED, null, BluetoothShare._ID);
+        if (cursor == null) {
+            return;
+        }
+
+        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+            // Add the notifications
+            long timeStamp = cursor.getLong(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
+            int dir = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION));
+            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
+            int status = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.STATUS));
+
+            String fileName = cursor.getString(cursor
+                    .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
+            if (fileName == null) {
+                fileName = mContext.getString(R.string.unknown_file);
+            }
+
+            String title;
+            String caption;
+            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
+
+            Notification n = new Notification();
+            if (BluetoothShare.isStatusError(status)) {
+                if (dir == BluetoothShare.DIRECTION_OUTBOUND) {
+                    title = mContext.getString(R.string.notification_sent_fail, fileName);
+                } else {
+                    title = mContext.getString(R.string.notification_received_fail, fileName);
+                }
+                caption = mContext.getString(R.string.download_fail_line3, BluetoothOppUtility
+                        .getStatusDescription(mContext, status));
+                n.icon = android.R.drawable.stat_notify_error;
+            } else {
+                if (dir == BluetoothShare.DIRECTION_OUTBOUND) {
+                    title = mContext.getString(R.string.notification_sent, fileName);
+                    n.icon = android.R.drawable.stat_sys_upload_done;
+                } else {
+                    title = mContext.getString(R.string.notification_received, fileName);
+                    n.icon = android.R.drawable.stat_sys_download_done;
+                }
+                caption = mContext.getString(R.string.notification_sent_complete);
+            }
+            Intent intent = new Intent(Constants.ACTION_OPEN);
+            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+            intent.setData(contentUri);
+
+            n.when = timeStamp;
+            n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
+                    intent, 0));
+
+            intent = new Intent(Constants.ACTION_HIDE);
+            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+            intent.setData(contentUri);
+            n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+
+            mNotificationMgr.notify(id, n);
+        }
+        cursor.close();
+    }
+
+    private void updateIncomingFileConfirmNotification() {
+        Cursor cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null,
+                WHERE_CONFIRM_PENDING, null, BluetoothShare._ID);
+
+        if (cursor == null) {
+            return;
+        }
+
+        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+            String title = mContext.getString(R.string.incoming_file_confirm_Notification_title);
+            String caption = mContext
+                    .getString(R.string.incoming_file_confirm_Notification_caption);
+            int id = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
+            long timeStamp = cursor.getLong(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
+            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
+
+            Notification n = new Notification();
+            n.icon = R.drawable.bt_incomming_file_notification;
+            n.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
+            n.defaults = Notification.DEFAULT_SOUND;
+            n.tickerText = title;
+            Intent intent = new Intent(Constants.ACTION_INCOMING_FILE_CONFIRM);
+            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+            intent.setData(contentUri);
+
+            n.when = timeStamp;
+            n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
+                    intent, 0));
+
+            intent = new Intent(Constants.ACTION_HIDE);
+            intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+            intent.setData(contentUri);
+            n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+
+            mNotificationMgr.notify(id, n);
+        }
+        cursor.close();
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
new file mode 100644
index 0000000..0b240f1
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import javax.obex.ClientOperation;
+import javax.obex.ClientSession;
+import javax.obex.HeaderSet;
+import javax.obex.ObexTransport;
+import javax.obex.ResponseCodes;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Thread;
+
+/**
+ * This class runs as an OBEX client
+ */
+public class BluetoothOppObexClientSession implements BluetoothOppObexSession {
+
+    private static final String TAG = "BtOpp ObexClient";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    private ClientThread mThread;
+
+    private ObexTransport mTransport;
+
+    private Context mContext;
+
+    private volatile boolean mInterrupted;
+
+    private volatile boolean mWaitingForRemote;
+
+    private Handler mCallback;
+
+    public BluetoothOppObexClientSession(Context context, ObexTransport transport) {
+        if (transport == null) {
+            throw new NullPointerException("transport is null");
+        }
+        mContext = context;
+        mTransport = transport;
+    }
+
+    public void start(Handler handler) {
+        if (D) Log.d(TAG, "Start!");
+        mCallback = handler;
+        mThread = new ClientThread(mContext, mTransport);
+        mThread.start();
+    }
+
+    public void stop() {
+        if (D) Log.d(TAG, "Stop!");
+        if (mThread != null) {
+            mInterrupted = true;
+            try {
+                mThread.interrupt();
+                if (V) Log.v(TAG, "waiting for thread to terminate");
+                mThread.join();
+                mThread = null;
+            } catch (InterruptedException e) {
+                if (V) Log.v(TAG, "Interrupted waiting for thread to join");
+            }
+        }
+        mCallback = null;
+    }
+
+    public void addShare(BluetoothOppShareInfo share) {
+        mThread.addShare(share);
+    }
+
+    private class ClientThread extends Thread {
+
+        private static final int sSleepTime = 500;
+
+        private Context mContext1;
+
+        private BluetoothOppShareInfo mInfo;
+
+        private volatile boolean waitingForShare;
+
+        private ObexTransport mTransport1;
+
+        private ClientSession mCs;
+
+        private WakeLock wakeLock;
+
+        private BluetoothOppSendFileInfo mFileInfo = null;
+
+        private boolean mConnected = false;
+
+        public ClientThread(Context context, ObexTransport transport) {
+            super("BtOpp ClientThread");
+            mContext1 = context;
+            mTransport1 = transport;
+            waitingForShare = true;
+            mWaitingForRemote = false;
+
+            PowerManager pm = (PowerManager)mContext1.getSystemService(Context.POWER_SERVICE);
+            wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+        }
+
+        public void addShare(BluetoothOppShareInfo info) {
+            mInfo = info;
+            mFileInfo = processShareInfo();
+            waitingForShare = false;
+        }
+
+        @Override
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+            if (V) Log.v(TAG, "acquire partial WakeLock");
+            wakeLock.acquire();
+
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e1) {
+                if (V) Log.v(TAG, "Client thread was interrupted (1), exiting");
+                mInterrupted = true;
+            }
+            if (!mInterrupted) {
+                connect();
+            }
+
+            while (!mInterrupted) {
+                if (!waitingForShare) {
+                    doSend();
+                } else {
+                    try {
+                        if (D) Log.d(TAG, "Client thread waiting for next share, sleep for "
+                                    + sSleepTime);
+                        Thread.sleep(sSleepTime);
+                    } catch (InterruptedException e) {
+
+                    }
+                }
+            }
+            disconnect();
+
+            if (wakeLock.isHeld()) {
+                if (V) Log.v(TAG, "release partial WakeLock");
+                wakeLock.release();
+            }
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothOppObexSession.MSG_SESSION_COMPLETE;
+            msg.obj = mInfo;
+            msg.sendToTarget();
+
+        }
+
+        private void disconnect() {
+            try {
+                if (mCs != null) {
+                    mCs.disconnect(null);
+                }
+                mCs = null;
+                if (D) Log.d(TAG, "OBEX session disconnected");
+            } catch (IOException e) {
+                Log.w(TAG, "OBEX session disconnect error" + e);
+            }
+            try {
+                if (mCs != null) {
+                    if (D) Log.d(TAG, "OBEX session close mCs");
+                    mCs.close();
+                    if (D) Log.d(TAG, "OBEX session closed");
+                    }
+            } catch (IOException e) {
+                Log.w(TAG, "OBEX session close error" + e);
+            }
+            if (mTransport1 != null) {
+                try {
+                    mTransport1.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "mTransport.close error");
+                }
+
+            }
+        }
+
+        private void connect() {
+            if (D) Log.d(TAG, "Create ClientSession with transport " + mTransport1.toString());
+            try {
+                mCs = new ClientSession(mTransport1);
+                mConnected = true;
+            } catch (IOException e1) {
+                Log.e(TAG, "OBEX session create error");
+            }
+            if (mConnected) {
+                mConnected = false;
+                HeaderSet hs = new HeaderSet();
+                synchronized (this) {
+                    mWaitingForRemote = true;
+                }
+                try {
+                    mCs.connect(hs);
+                    if (D) Log.d(TAG, "OBEX session created");
+                    mConnected = true;
+                } catch (IOException e) {
+                    Log.e(TAG, "OBEX session connect error");
+                }
+            }
+            synchronized (this) {
+                mWaitingForRemote = false;
+            }
+        }
+
+        private void doSend() {
+
+            int status = BluetoothShare.STATUS_SUCCESS;
+
+            /* connection is established too fast to get first mInfo */
+            while (mFileInfo == null) {
+                try {
+                    Thread.sleep(50);
+                } catch (InterruptedException e) {
+                    status = BluetoothShare.STATUS_CANCELED;
+                }
+            }
+            if (!mConnected) {
+                // Obex connection error
+                status = BluetoothShare.STATUS_CONNECTION_ERROR;
+            }
+            if (status == BluetoothShare.STATUS_SUCCESS) {
+                /* do real send */
+                if (mFileInfo.mFileName != null) {
+                    status = sendFile(mFileInfo);
+                } else {
+                    /* this is invalid request */
+                    status = mFileInfo.mStatus;
+                }
+                waitingForShare = true;
+            } else {
+                Constants.updateShareStatus(mContext1, mInfo.mId, status);
+            }
+
+            if (status == BluetoothShare.STATUS_SUCCESS) {
+                Message msg = Message.obtain(mCallback);
+                msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE;
+                msg.obj = mInfo;
+                msg.sendToTarget();
+            } else {
+                Message msg = Message.obtain(mCallback);
+                msg.what = BluetoothOppObexSession.MSG_SESSION_ERROR;
+                mInfo.mStatus = status;
+                msg.obj = mInfo;
+                msg.sendToTarget();
+            }
+        }
+
+        /*
+         * Validate this ShareInfo
+         */
+        private BluetoothOppSendFileInfo processShareInfo() {
+            if (V) Log.v(TAG, "Client thread processShareInfo() " + mInfo.mId);
+
+            BluetoothOppSendFileInfo fileInfo = BluetoothOppSendFileInfo.generateFileInfo(
+                    mContext1, mInfo.mUri, mInfo.mMimetype, mInfo.mDestination);
+            if (fileInfo.mFileName == null || fileInfo.mLength == 0) {
+                if (V) Log.v(TAG, "BluetoothOppSendFileInfo get invalid file");
+                    Constants.updateShareStatus(mContext1, mInfo.mId, fileInfo.mStatus);
+
+            } else {
+                if (V) {
+                    Log.v(TAG, "Generate BluetoothOppSendFileInfo:");
+                    Log.v(TAG, "filename  :" + fileInfo.mFileName);
+                    Log.v(TAG, "length    :" + fileInfo.mLength);
+                    Log.v(TAG, "mimetype  :" + fileInfo.mMimetype);
+                }
+
+                ContentValues updateValues = new ContentValues();
+                Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + mInfo.mId);
+
+                updateValues.put(BluetoothShare.FILENAME_HINT, fileInfo.mFileName);
+                updateValues.put(BluetoothShare.TOTAL_BYTES, fileInfo.mLength);
+                updateValues.put(BluetoothShare.MIMETYPE, fileInfo.mMimetype);
+
+                mContext1.getContentResolver().update(contentUri, updateValues, null, null);
+
+            }
+            return fileInfo;
+        }
+
+        private int sendFile(BluetoothOppSendFileInfo fileInfo) {
+            boolean error = false;
+            int responseCode = -1;
+            int status = BluetoothShare.STATUS_SUCCESS;
+            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + mInfo.mId);
+            ContentValues updateValues;
+            HeaderSet request;
+            request = new HeaderSet();
+            request.setHeader(HeaderSet.NAME, fileInfo.mFileName);
+            request.setHeader(HeaderSet.TYPE, fileInfo.mMimetype);
+
+            applyRemoteDeviceQuirks(request, fileInfo);
+
+            Constants.updateShareStatus(mContext1, mInfo.mId, BluetoothShare.STATUS_RUNNING);
+
+            request.setHeader(HeaderSet.LENGTH, fileInfo.mLength);
+            ClientOperation putOperation = null;
+            OutputStream outputStream = null;
+            InputStream inputStream = null;
+            try {
+                synchronized (this) {
+                    mWaitingForRemote = true;
+                }
+                try {
+                    if (V) Log.v(TAG, "put headerset for " + fileInfo.mFileName);
+                    putOperation = (ClientOperation)mCs.put(request);
+                } catch (IOException e) {
+                    status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
+                    Constants.updateShareStatus(mContext1, mInfo.mId, status);
+
+                    Log.e(TAG, "Error when put HeaderSet ");
+                    error = true;
+                }
+                synchronized (this) {
+                    mWaitingForRemote = false;
+                }
+
+                if (!error) {
+                    try {
+                        if (V) Log.v(TAG, "openOutputStream " + fileInfo.mFileName);
+                        outputStream = putOperation.openOutputStream();
+                        inputStream = putOperation.openInputStream();
+                    } catch (IOException e) {
+                        status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
+                        Constants.updateShareStatus(mContext1, mInfo.mId, status);
+                        Log.e(TAG, "Error when openOutputStream");
+                        error = true;
+                    }
+                }
+                if (!error) {
+                    updateValues = new ContentValues();
+                    updateValues.put(BluetoothShare.CURRENT_BYTES, 0);
+                    updateValues.put(BluetoothShare.STATUS, BluetoothShare.STATUS_RUNNING);
+                    mContext1.getContentResolver().update(contentUri, updateValues, null, null);
+                }
+
+                if (!error) {
+                    int position = 0;
+                    int readLength = 0;
+                    boolean okToProceed = false;
+                    long timestamp = 0;
+                    int outputBufferSize = putOperation.getMaxPacketSize();
+                    byte[] buffer = new byte[outputBufferSize];
+                    BufferedInputStream a = new BufferedInputStream(fileInfo.mInputStream, 0x4000);
+
+                    if (!mInterrupted && (position != fileInfo.mLength)) {
+                        readLength = a.read(buffer, 0, outputBufferSize);
+
+                        mCallback.sendMessageDelayed(mCallback
+                                .obtainMessage(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT),
+                                BluetoothOppObexSession.SESSION_TIMEOUT);
+                        synchronized (this) {
+                            mWaitingForRemote = true;
+                        }
+
+                        // first packet will block here
+                        outputStream.write(buffer, 0, readLength);
+
+                        position += readLength;
+
+                        if (position != fileInfo.mLength) {
+                            mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
+                            synchronized (this) {
+                                mWaitingForRemote = false;
+                            }
+                        } else {
+                            // if file length is smaller than buffer size, only one packet
+                            // so block point is here
+                            outputStream.close();
+                            mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
+                            synchronized (this) {
+                                mWaitingForRemote = false;
+                            }
+                        }
+                        /* check remote accept or reject */
+                        responseCode = putOperation.getResponseCode();
+
+                        if (responseCode == ResponseCodes.OBEX_HTTP_CONTINUE
+                                || responseCode == ResponseCodes.OBEX_HTTP_OK) {
+                            if (V) Log.v(TAG, "Remote accept");
+                            okToProceed = true;
+                            updateValues = new ContentValues();
+                            updateValues.put(BluetoothShare.CURRENT_BYTES, position);
+                            mContext1.getContentResolver().update(contentUri, updateValues, null,
+                                    null);
+                        } else {
+                            Log.i(TAG, "Remote reject, Response code is " + responseCode);
+                        }
+                    }
+
+                    while (!mInterrupted && okToProceed && (position != fileInfo.mLength)) {
+                        {
+                            if (V) timestamp = System.currentTimeMillis();
+
+                            readLength = a.read(buffer, 0, outputBufferSize);
+                            outputStream.write(buffer, 0, readLength);
+
+                            /* check remote abort */
+                            responseCode = putOperation.getResponseCode();
+                            if (V) Log.v(TAG, "Response code is " + responseCode);
+                            if (responseCode != ResponseCodes.OBEX_HTTP_CONTINUE
+                                    && responseCode != ResponseCodes.OBEX_HTTP_OK) {
+                                /* abort happens */
+                                okToProceed = false;
+                            } else {
+                                position += readLength;
+                                if (V) {
+                                    Log.v(TAG, "Sending file position = " + position
+                                            + " readLength " + readLength + " bytes took "
+                                            + (System.currentTimeMillis() - timestamp) + " ms");
+                                }
+                                updateValues = new ContentValues();
+                                updateValues.put(BluetoothShare.CURRENT_BYTES, position);
+                                mContext1.getContentResolver().update(contentUri, updateValues,
+                                        null, null);
+                            }
+                        }
+                    }
+
+                    if (responseCode == ResponseCodes.OBEX_HTTP_FORBIDDEN
+                            || responseCode == ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE) {
+                        Log.i(TAG, "Remote reject file " + fileInfo.mFileName + " length "
+                                + fileInfo.mLength);
+                        status = BluetoothShare.STATUS_FORBIDDEN;
+                    } else if (responseCode == ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE) {
+                        Log.i(TAG, "Remote reject file type " + fileInfo.mMimetype);
+                        status = BluetoothShare.STATUS_NOT_ACCEPTABLE;
+                    } else if (!mInterrupted && position == fileInfo.mLength) {
+                        Log.i(TAG, "SendFile finished send out file " + fileInfo.mFileName
+                                + " length " + fileInfo.mLength);
+                        outputStream.close();
+                    } else {
+                        error = true;
+                        status = BluetoothShare.STATUS_CANCELED;
+                        putOperation.abort();
+                        /* interrupted */
+                        Log.i(TAG, "SendFile interrupted when send out file " + fileInfo.mFileName
+                                + " at " + position + " of " + fileInfo.mLength);
+                    }
+                }
+            } catch (IOException e) {
+                status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
+                Log.e(TAG, "Error when sending file");
+                Constants.updateShareStatus(mContext1, mInfo.mId, status);
+                mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
+            } finally {
+                try {
+                    fileInfo.mInputStream.close();
+                    if (!error) {
+                        responseCode = putOperation.getResponseCode();
+                        if (responseCode != -1) {
+                            if (V) Log.v(TAG, "Get response code " + responseCode);
+                            if (responseCode != ResponseCodes.OBEX_HTTP_OK) {
+                                Log.i(TAG, "Response error code is " + responseCode);
+                                status = BluetoothShare.STATUS_UNHANDLED_OBEX_CODE;
+                                if (responseCode == ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE) {
+                                    status = BluetoothShare.STATUS_NOT_ACCEPTABLE;
+                                }
+                                if (responseCode == ResponseCodes.OBEX_HTTP_FORBIDDEN
+                                        || responseCode == ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE) {
+                                    status = BluetoothShare.STATUS_FORBIDDEN;
+                                }
+                            }
+                        } else {
+                            // responseCode is -1, which means connection error
+                            status = BluetoothShare.STATUS_CONNECTION_ERROR;
+                        }
+                    }
+
+                    Constants.updateShareStatus(mContext1, mInfo.mId, status);
+
+                    if (inputStream != null) {
+                        inputStream.close();
+                    }
+                    if (putOperation != null) {
+                        putOperation.close();
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "Error when closing stream after send");
+                }
+            }
+            return status;
+        }
+
+        @Override
+        public void interrupt() {
+            super.interrupt();
+            synchronized (this) {
+                if (mWaitingForRemote) {
+                    if (V) Log.v(TAG, "Interrupted when waitingForRemote");
+                    try {
+                        mTransport1.close();
+                    } catch (IOException e) {
+                        Log.e(TAG, "mTransport.close error");
+                    }
+                    Message msg = Message.obtain(mCallback);
+                    msg.what = BluetoothOppObexSession.MSG_SHARE_INTERRUPTED;
+                    if (mInfo != null) {
+                        msg.obj = mInfo;
+                    }
+                    msg.sendToTarget();
+                }
+            }
+        }
+    }
+
+    public static void applyRemoteDeviceQuirks(HeaderSet request, BluetoothOppSendFileInfo info) {
+        String address = info.mDestAddr;
+        if (address == null) {
+            return;
+        }
+        if (address.startsWith("00:04:48")) {
+            // Poloroid Pogo
+            // Rejects filenames with more than one '.'. Rename to '_'.
+            // for example: 'a.b.jpg' -> 'a_b.jpg'
+            //              'abc.jpg' NOT CHANGED
+            String filename = info.mFileName;
+
+            char[] c = filename.toCharArray();
+            boolean firstDot = true;
+            boolean modified = false;
+            for (int i = c.length - 1; i >= 0; i--) {
+                if (c[i] == '.') {
+                    if (!firstDot) {
+                        modified = true;
+                        c[i] = '_';
+                    }
+                    firstDot = false;
+                }
+            }
+
+            if (modified) {
+                String newFilename = new String(c);
+                request.setHeader(HeaderSet.NAME, newFilename);
+                Log.i(TAG, "Sending file \"" + filename + "\" as \"" + newFilename +
+                        "\" to workaround Poloroid filename quirk");
+            }
+        }
+    }
+
+    public void unblock() {
+        // Not used for client case
+    }
+
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
new file mode 100644
index 0000000..278d29e
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java
@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import javax.obex.HeaderSet;
+import javax.obex.ObexTransport;
+import javax.obex.Operation;
+import javax.obex.ResponseCodes;
+import javax.obex.ServerRequestHandler;
+import javax.obex.ServerSession;
+
+/**
+ * This class runs as an OBEX server
+ */
+public class BluetoothOppObexServerSession extends ServerRequestHandler implements
+        BluetoothOppObexSession {
+
+    private static final String TAG = "BtOppObexServer";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    private ObexTransport mTransport;
+
+    private Context mContext;
+
+    private Handler mCallback = null;
+
+    /* status when server is blocking for user/auto confirmation */
+    private boolean mServerBlocking = true;
+
+    /* the current transfer info */
+    private BluetoothOppShareInfo mInfo;
+
+    /* info id when we insert the record */
+    private int mLocalShareInfoId;
+
+    private int mAccepted = BluetoothShare.USER_CONFIRMATION_PENDING;
+
+    private boolean mInterrupted = false;
+
+    private ServerSession mSession;
+
+    private long mTimestamp;
+
+    private BluetoothOppReceiveFileInfo mFileInfo;
+
+    private WakeLock mWakeLock;
+
+    private WakeLock mPartialWakeLock;
+
+    boolean mTimeoutMsgSent = false;
+
+    public BluetoothOppObexServerSession(Context context, ObexTransport transport) {
+        mContext = context;
+        mTransport = transport;
+        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP
+                | PowerManager.ON_AFTER_RELEASE, TAG);
+        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+    }
+
+    public void unblock() {
+        mServerBlocking = false;
+    }
+
+    /**
+     * Called when connection is accepted from remote, to retrieve the first
+     * Header then wait for user confirmation
+     */
+    public void preStart() {
+        if (D) Log.d(TAG, "acquire full WakeLock");
+        mWakeLock.acquire();
+        try {
+            if (D) Log.d(TAG, "Create ServerSession with transport " + mTransport.toString());
+            mSession = new ServerSession(mTransport, this, null);
+        } catch (IOException e) {
+            Log.e(TAG, "Create server session error" + e);
+        }
+    }
+
+    /**
+     * Called from BluetoothOppTransfer to start the "Transfer"
+     */
+    public void start(Handler handler) {
+        if (D) Log.d(TAG, "Start!");
+        mCallback = handler;
+
+    }
+
+    /**
+     * Called from BluetoothOppTransfer to cancel the "Transfer" Otherwise,
+     * server should end by itself.
+     */
+    public void stop() {
+        /*
+         * TODO now we implement in a tough way, just close the socket.
+         * maybe need nice way
+         */
+        if (D) Log.d(TAG, "Stop!");
+        mInterrupted = true;
+        if (mSession != null) {
+            try {
+                mSession.close();
+                mTransport.close();
+            } catch (IOException e) {
+                Log.e(TAG, "close mTransport error" + e);
+            }
+        }
+        mCallback = null;
+        mSession = null;
+    }
+
+    public void addShare(BluetoothOppShareInfo info) {
+        if (D) Log.d(TAG, "addShare for id " + info.mId);
+        mInfo = info;
+        mFileInfo = processShareInfo();
+    }
+
+    @Override
+    public int onPut(Operation op) {
+        if (D) Log.d(TAG, "onPut " + op.toString());
+        HeaderSet request;
+        String name, mimeType;
+        Long length;
+
+        int obexResponse = ResponseCodes.OBEX_HTTP_OK;
+
+        /**
+         * For multiple objects, reject further objects after user deny the
+         * first one
+         */
+        if (mAccepted == BluetoothShare.USER_CONFIRMATION_DENIED) {
+            return ResponseCodes.OBEX_HTTP_FORBIDDEN;
+        }
+
+        try {
+            boolean pre_reject = false;
+            request = op.getReceivedHeader();
+            if (V) Constants.logHeader(request);
+            name = (String)request.getHeader(HeaderSet.NAME);
+            length = (Long)request.getHeader(HeaderSet.LENGTH);
+            mimeType = (String)request.getHeader(HeaderSet.TYPE);
+
+            if (length == 0) {
+                if (D) Log.w(TAG, "length is 0, reject the transfer");
+                pre_reject = true;
+                obexResponse = ResponseCodes.OBEX_HTTP_LENGTH_REQUIRED;
+            }
+
+            if (name == null || name.equals("")) {
+                if (D) Log.w(TAG, "name is null or empty, reject the transfer");
+                pre_reject = true;
+                obexResponse = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            }
+
+            if (!pre_reject) {
+                /* first we look for Mimetype in Android map */
+                String extension, type;
+                int dotIndex = name.lastIndexOf(".");
+                if (dotIndex < 0) {
+                    if (D) Log.w(TAG, "There is no file extension, reject the transfer");
+                    pre_reject = true;
+                    obexResponse = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+                } else {
+                    extension = name.substring(dotIndex + 1).toLowerCase();
+                    MimeTypeMap map = MimeTypeMap.getSingleton();
+                    type = map.getMimeTypeFromExtension(extension);
+                    if (V) Log.v(TAG, "Mimetype guessed from extension " + extension + " is " + type);
+                    if (type != null) {
+                        mimeType = type;
+
+                    } else {
+                        if (mimeType == null) {
+                            if (D) Log.w(TAG, "Can't get mimetype, reject the transfer");
+                            pre_reject = true;
+                            obexResponse = ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE;
+                        }
+                    }
+                    if (mimeType != null) {
+                        mimeType = mimeType.toLowerCase();
+                    }
+                }
+            }
+
+            // Reject policy: anything outside the "white list" plus unspecified
+            // MIME Types.
+            if (!pre_reject
+                    && (mimeType == null || (!Constants.mimeTypeMatches(mimeType,
+                            Constants.ACCEPTABLE_SHARE_INBOUND_TYPES)))) {
+                if (D) Log.w(TAG, "mimeType is null or in unacceptable list, reject the transfer");
+                pre_reject = true;
+                obexResponse = ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE;
+            }
+
+            if (pre_reject && obexResponse != ResponseCodes.OBEX_HTTP_OK) {
+                // some bad implemented client won't send disconnect
+                return obexResponse;
+            }
+
+        } catch (IOException e) {
+            Log.e(TAG, "get getReceivedHeaders error " + e);
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+
+        ContentValues values = new ContentValues();
+
+        values.put(BluetoothShare.FILENAME_HINT, name);
+        values.put(BluetoothShare.TOTAL_BYTES, length.intValue());
+        values.put(BluetoothShare.MIMETYPE, mimeType);
+
+        if (mTransport instanceof BluetoothOppRfcommTransport) {
+            String a = ((BluetoothOppRfcommTransport)mTransport).getRemoteAddress();
+            values.put(BluetoothShare.DESTINATION, a);
+        } else {
+            values.put(BluetoothShare.DESTINATION, "FF:FF:FF:00:00:00");
+        }
+
+        values.put(BluetoothShare.DIRECTION, BluetoothShare.DIRECTION_INBOUND);
+        values.put(BluetoothShare.TIMESTAMP, mTimestamp);
+
+        boolean needConfirm = true;
+        /** It's not first put if !serverBlocking, so we auto accept it */
+        if (!mServerBlocking) {
+            values.put(BluetoothShare.USER_CONFIRMATION,
+                    BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED);
+            needConfirm = false;
+        }
+
+        Uri contentUri = mContext.getContentResolver().insert(BluetoothShare.CONTENT_URI, values);
+        mLocalShareInfoId = Integer.parseInt(contentUri.getPathSegments().get(1));
+
+        if (needConfirm) {
+            Intent in = new Intent(BluetoothShare.INCOMING_FILE_CONFIRMATION_REQUEST_ACTION);
+            in.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+            mContext.sendBroadcast(in);
+        }
+
+        if (V) Log.v(TAG, "insert contentUri: " + contentUri);
+        if (V) Log.v(TAG, "mLocalShareInfoId = " + mLocalShareInfoId);
+
+        if (V) Log.v(TAG, "acquire partial WakeLock");
+        if (mWakeLock.isHeld()) {
+            mPartialWakeLock.acquire();
+            mWakeLock.release();
+        }
+
+        mServerBlocking = true;
+
+        synchronized (this) {
+            try {
+
+                while (mServerBlocking) {
+                    wait(1000);
+                    if (mCallback != null && !mTimeoutMsgSent) {
+                        mCallback.sendMessageDelayed(mCallback
+                                .obtainMessage(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT),
+                                BluetoothOppObexSession.SESSION_TIMEOUT);
+                        mTimeoutMsgSent = true;
+                        if (V) Log.v(TAG, "MSG_CONNECT_TIMEOUT sent");
+                    }
+                }
+            } catch (InterruptedException e) {
+                if (V) Log.v(TAG, "Interrupted in onPut blocking");
+            }
+        }
+        if (D) Log.d(TAG, "Server unblocked ");
+        synchronized (this) {
+            if (mCallback != null && mTimeoutMsgSent) {
+                mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
+            }
+        }
+
+        /* we should have mInfo now */
+
+        /*
+         * TODO check if this mInfo match the one that we insert before server
+         * blocking? just to make sure no error happens
+         */
+        if (mInfo.mId != mLocalShareInfoId) {
+            Log.e(TAG, "Unexpected error!");
+        }
+        mAccepted = mInfo.mConfirm;
+
+        if (V) Log.v(TAG, "after confirm: userAccepted=" + mAccepted);
+        int status = BluetoothShare.STATUS_SUCCESS;
+
+        if (mAccepted == BluetoothShare.USER_CONFIRMATION_CONFIRMED
+                || mAccepted == BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED) {
+            /* Confirm or auto-confirm */
+
+            if (mFileInfo.mFileName == null) {
+                status = mFileInfo.mStatus;
+                /* TODO need to check if this line is correct */
+                mInfo.mStatus = mFileInfo.mStatus;
+                Constants.updateShareStatus(mContext, mInfo.mId, status);
+                obexResponse = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+
+            }
+
+            if (mFileInfo.mFileName != null) {
+
+                ContentValues updateValues = new ContentValues();
+                contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + mInfo.mId);
+                updateValues.put(BluetoothShare._DATA, mFileInfo.mFileName);
+                updateValues.put(BluetoothShare.STATUS, BluetoothShare.STATUS_RUNNING);
+                mContext.getContentResolver().update(contentUri, updateValues, null, null);
+
+                status = receiveFile(mFileInfo, op);
+                /*
+                 * TODO map status to obex response code
+                 */
+                if (status != BluetoothShare.STATUS_SUCCESS) {
+                    obexResponse = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                }
+                Constants.updateShareStatus(mContext, mInfo.mId, status);
+            }
+
+            if (status == BluetoothShare.STATUS_SUCCESS) {
+                Message msg = Message.obtain(mCallback, BluetoothOppObexSession.MSG_SHARE_COMPLETE);
+                msg.obj = mInfo;
+                msg.sendToTarget();
+            } else {
+                if (mCallback != null) {
+                    Message msg = Message.obtain(mCallback,
+                            BluetoothOppObexSession.MSG_SESSION_ERROR);
+                    mInfo.mStatus = status;
+                    msg.obj = mInfo;
+                    msg.sendToTarget();
+                }
+            }
+        } else if (mAccepted == BluetoothShare.USER_CONFIRMATION_DENIED
+                || mAccepted == BluetoothShare.USER_CONFIRMATION_TIMEOUT) {
+            /* user actively deny the inbound transfer */
+            /*
+             * Note There is a question: what's next if user deny the first obj?
+             * Option 1 :continue prompt for next objects
+             * Option 2 :reject next objects and finish the session
+             * Now we take option 2:
+             */
+
+            Log.i(TAG, "Rejected incoming request");
+            if (mFileInfo.mFileName != null) {
+                try {
+                    mFileInfo.mOutputStream.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "error close file stream");
+                }
+                new File(mFileInfo.mFileName).delete();
+            }
+            // set status as local cancel
+            status = BluetoothShare.STATUS_CANCELED;
+            Constants.updateShareStatus(mContext, mInfo.mId, status);
+            obexResponse = ResponseCodes.OBEX_HTTP_FORBIDDEN;
+
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothOppObexSession.MSG_SHARE_INTERRUPTED;
+            mInfo.mStatus = status;
+            msg.obj = mInfo;
+            msg.sendToTarget();
+        }
+        return obexResponse;
+    }
+
+    private int receiveFile(BluetoothOppReceiveFileInfo fileInfo, Operation op) {
+        /*
+         * implement receive file
+         */
+        int status = -1;
+        BufferedOutputStream bos = null;
+
+        InputStream is = null;
+        boolean error = false;
+        try {
+            is = op.openInputStream();
+        } catch (IOException e1) {
+            Log.e(TAG, "Error when openInputStream");
+            status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
+            error = true;
+        }
+
+        Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + mInfo.mId);
+
+        if (!error) {
+            ContentValues updateValues = new ContentValues();
+            updateValues.put(BluetoothShare._DATA, fileInfo.mFileName);
+            mContext.getContentResolver().update(contentUri, updateValues, null, null);
+        }
+
+        int position = 0;
+        if (!error) {
+            bos = new BufferedOutputStream(fileInfo.mOutputStream, 0x10000);
+        }
+
+        if (!error) {
+            int outputBufferSize = op.getMaxPacketSize();
+            byte[] b = new byte[outputBufferSize];
+            int readLength = 0;
+            long timestamp = 0;
+            try {
+                while ((!mInterrupted) && (position != fileInfo.mLength)) {
+
+                    if (V) timestamp = System.currentTimeMillis();
+
+                    readLength = is.read(b);
+
+                    if (readLength == -1) {
+                        if (D) Log.d(TAG, "Receive file reached stream end at position" + position);
+                        break;
+                    }
+
+                    bos.write(b, 0, readLength);
+                    position += readLength;
+
+                    if (V) {
+                        Log.v(TAG, "Receive file position = " + position + " readLength "
+                                + readLength + " bytes took "
+                                + (System.currentTimeMillis() - timestamp) + " ms");
+                    }
+
+                    ContentValues updateValues = new ContentValues();
+                    updateValues.put(BluetoothShare.CURRENT_BYTES, position);
+                    mContext.getContentResolver().update(contentUri, updateValues, null, null);
+                }
+            } catch (IOException e1) {
+                Log.e(TAG, "Error when receiving file");
+                status = BluetoothShare.STATUS_OBEX_DATA_ERROR;
+                error = true;
+            }
+        }
+
+        if (mInterrupted) {
+            if (D) Log.d(TAG, "receiving file interrupted by user.");
+            status = BluetoothShare.STATUS_CANCELED;
+        } else {
+            if (position == fileInfo.mLength) {
+                if (D) Log.d(TAG, "Receiving file completed for " + fileInfo.mFileName);
+                status = BluetoothShare.STATUS_SUCCESS;
+            } else {
+                if (D) Log.d(TAG, "Reading file failed at " + position + " of " + fileInfo.mLength);
+                if (status == -1) {
+                    status = BluetoothShare.STATUS_UNKNOWN_ERROR;
+                }
+            }
+        }
+
+        if (bos != null) {
+            try {
+                bos.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Error when closing stream after send");
+            }
+        }
+        return status;
+    }
+
+    private BluetoothOppReceiveFileInfo processShareInfo() {
+        if (D) Log.d(TAG, "processShareInfo() " + mInfo.mId);
+        BluetoothOppReceiveFileInfo fileInfo = BluetoothOppReceiveFileInfo.generateFileInfo(
+                mContext, mInfo.mId);
+        if (V) {
+            Log.v(TAG, "Generate BluetoothOppReceiveFileInfo:");
+            Log.v(TAG, "filename  :" + fileInfo.mFileName);
+            Log.v(TAG, "length    :" + fileInfo.mLength);
+            Log.v(TAG, "status    :" + fileInfo.mStatus);
+        }
+        return fileInfo;
+    }
+
+    @Override
+    public int onConnect(HeaderSet request, HeaderSet reply) {
+
+        if (D) Log.d(TAG, "onConnect");
+        if (V) Constants.logHeader(request);
+        try {
+            byte[] uuid = (byte[])request.getHeader(HeaderSet.TARGET);
+            if (V) Log.v(TAG, "onConnect(): uuid =" + Arrays.toString(uuid));
+            if(uuid != null) {
+                reply.setHeader(HeaderSet.WHO, uuid);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+        mTimestamp = System.currentTimeMillis();
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    @Override
+    public void onDisconnect(HeaderSet req, HeaderSet resp) {
+        if (D) Log.d(TAG, "onDisconnect");
+        resp.responseCode = ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    @Override
+    public void onClose() {
+        if (V) Log.v(TAG, "release WakeLock");
+        if (mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
+        if (mPartialWakeLock.isHeld()) {
+            mPartialWakeLock.release();
+        }
+        /* onClose could happen even before start() where mCallback is set */
+        if (mCallback != null) {
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothOppObexSession.MSG_SESSION_COMPLETE;
+            msg.obj = mInfo;
+            msg.sendToTarget();
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexSession.java
new file mode 100644
index 0000000..ce16875
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexSession.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import android.os.Handler;
+
+/**
+ * Interface for control the state of an OBEX Session.
+ */
+public interface BluetoothOppObexSession {
+
+    /**
+     * Message to notify when a transfer is completed For outbound share, it
+     * means one file has been sent. For inbounds share, it means one file has
+     * been received.
+     */
+    int MSG_SHARE_COMPLETE = 0;
+
+    /**
+     * Message to notify when a session is completed For outbound share, it
+     * should be a consequence of session.stop() For inbounds share, it should
+     * be a consequence of remote disconnect
+     */
+    int MSG_SESSION_COMPLETE = 1;
+
+    /** Message to notify when a BluetoothOppObexSession get any error condition */
+    int MSG_SESSION_ERROR = 2;
+
+    /**
+     * Message to notify when a BluetoothOppObexSession is interrupted when
+     * waiting for remote
+     */
+    int MSG_SHARE_INTERRUPTED = 3;
+
+    int MSG_CONNECT_TIMEOUT = 4;
+
+    int SESSION_TIMEOUT = 50000;
+
+    void start(Handler sessionHandler);
+
+    void stop();
+
+    void addShare(BluetoothOppShareInfo share);
+
+    void unblock();
+
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppPreference.java b/src/com/android/bluetooth/opp/BluetoothOppPreference.java
new file mode 100644
index 0000000..3a83cb3
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppPreference.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import java.util.HashMap;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.util.Log;
+
+/**
+ * This class cache Bluetooth device name and channel locally. Its a temp
+ * solution which should be replaced by bluetooth_devices in SettingsProvider
+ */
+public class BluetoothOppPreference {
+    private static final String TAG = "BluetoothOppPreference";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    private static BluetoothOppPreference INSTANCE;
+
+    /* Used when obtaining a reference to the singleton instance. */
+    private static Object INSTANCE_LOCK = new Object();
+
+    private boolean mInitialized;
+
+    private Context mContext;
+
+    private SharedPreferences mNamePreference;
+
+    private SharedPreferences mChannelPreference;
+
+    private HashMap<String, Integer> mChannels = new HashMap<String, Integer>();
+
+    private HashMap<String, String> mNames = new HashMap<String, String>();
+
+    public static BluetoothOppPreference getInstance(Context context) {
+        synchronized (INSTANCE_LOCK) {
+            if (INSTANCE == null) {
+                INSTANCE = new BluetoothOppPreference();
+            }
+            if (!INSTANCE.init(context)) {
+                return null;
+            }
+            return INSTANCE;
+        }
+    }
+
+    private boolean init(Context context) {
+        if (mInitialized)
+            return true;
+        mInitialized = true;
+
+        // This will be around as long as this process is
+        mContext = context.getApplicationContext();
+
+        mNamePreference = mContext.getSharedPreferences(Constants.BLUETOOTHOPP_NAME_PREFERENCE,
+                Context.MODE_PRIVATE);
+        mChannelPreference = mContext.getSharedPreferences(
+                Constants.BLUETOOTHOPP_CHANNEL_PREFERENCE, Context.MODE_PRIVATE);
+
+        mNames = (HashMap<String, String>) mNamePreference.getAll();
+        mChannels = (HashMap<String, Integer>) mChannelPreference.getAll();
+
+        return true;
+    }
+
+    private String getChannelKey(BluetoothDevice remoteDevice, int uuid) {
+        return remoteDevice.getAddress() + "_" + Integer.toHexString(uuid);
+    }
+
+    public String getName(BluetoothDevice remoteDevice) {
+        if (remoteDevice.getAddress().equals("FF:FF:FF:00:00:00")) {
+            return "localhost";
+        }
+        if (!mNames.isEmpty()) {
+            String name = mNames.get(remoteDevice.getAddress());
+            if (name != null) {
+                return name;
+            }
+        }
+        return null;
+    }
+
+    public int getChannel(BluetoothDevice remoteDevice, int uuid) {
+        String key = getChannelKey(remoteDevice, uuid);
+        if (V) Log.v(TAG, "getChannel " + key);
+        Integer channel = null;
+        if (mChannels != null) {
+            channel = mChannels.get(key);
+            if (V) Log.v(TAG, "getChannel for " + remoteDevice + "_" + Integer.toHexString(uuid) +
+                        " as " + channel);
+        }
+        return (channel != null) ? channel : -1;
+    }
+
+    public void setName(BluetoothDevice remoteDevice, String name) {
+        if (V) Log.v(TAG, "Setname for " + remoteDevice + " to " + name);
+        if (!name.equals(getName(remoteDevice))) {
+            Editor ed = mNamePreference.edit();
+            ed.putString(remoteDevice.getAddress(), name);
+            ed.commit();
+            mNames.put(remoteDevice.getAddress(), name);
+        }
+    }
+
+    public void setChannel(BluetoothDevice remoteDevice, int uuid, int channel) {
+        if (V) Log.v(TAG, "Setchannel for " + remoteDevice + "_" + Integer.toHexString(uuid) + " to "
+                    + channel);
+        if (channel != getChannel(remoteDevice, uuid)) {
+            String key = getChannelKey(remoteDevice, uuid);
+            Editor ed = mChannelPreference.edit();
+            ed.putInt(key, channel);
+            ed.commit();
+            mChannels.put(key, channel);
+        }
+    }
+
+    public void removeChannel(BluetoothDevice remoteDevice, int uuid) {
+        String key = getChannelKey(remoteDevice, uuid);
+        Editor ed = mChannelPreference.edit();
+        ed.remove(key);
+        ed.commit();
+        mChannels.remove(key);
+    }
+
+    public void dump() {
+        Log.d(TAG, "Dumping Names:  ");
+        Log.d(TAG, mNames.toString());
+        Log.d(TAG, "Dumping Channels:  ");
+        Log.d(TAG, mChannels.toString());
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppProvider.java b/src/com/android/bluetooth/opp/BluetoothOppProvider.java
new file mode 100644
index 0000000..72dafc8
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppProvider.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.content.UriMatcher;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.provider.LiveFolders;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * This provider allows application to interact with Bluetooth OPP manager
+ */
+
+public final class BluetoothOppProvider extends ContentProvider {
+
+    private static final String TAG = "BluetoothOppProvider";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    /** Database filename */
+    private static final String DB_NAME = "btopp.db";
+
+    /** Current database version */
+    private static final int DB_VERSION = 1;
+
+    /** Database version from which upgrading is a nop */
+    private static final int DB_VERSION_NOP_UPGRADE_FROM = 0;
+
+    /** Database version to which upgrading is a nop */
+    private static final int DB_VERSION_NOP_UPGRADE_TO = 1;
+
+    /** Name of table in the database */
+    private static final String DB_TABLE = "btopp";
+
+    /** MIME type for the entire share list */
+    private static final String SHARE_LIST_TYPE = "vnd.android.cursor.dir/vnd.android.btopp";
+
+    /** MIME type for an individual share */
+    private static final String SHARE_TYPE = "vnd.android.cursor.item/vnd.android.btopp";
+
+    /** URI matcher used to recognize URIs sent by applications */
+    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+    /** URI matcher constant for the URI of the entire share list */
+    private static final int SHARES = 1;
+
+    /** URI matcher constant for the URI of an individual share */
+    private static final int SHARES_ID = 2;
+
+    /** URI matcher constant for the URI of live folder */
+    private static final int LIVE_FOLDER_RECEIVED_FILES = 3;
+    static {
+        sURIMatcher.addURI("com.android.bluetooth.opp", "btopp", SHARES);
+        sURIMatcher.addURI("com.android.bluetooth.opp", "btopp/#", SHARES_ID);
+        sURIMatcher.addURI("com.android.bluetooth.opp", "live_folders/received",
+                LIVE_FOLDER_RECEIVED_FILES);
+    }
+
+    private static final HashMap<String, String> LIVE_FOLDER_PROJECTION_MAP;
+    static {
+        LIVE_FOLDER_PROJECTION_MAP = new HashMap<String, String>();
+        LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders._ID, BluetoothShare._ID + " AS "
+                + LiveFolders._ID);
+        LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders.NAME, BluetoothShare.FILENAME_HINT + " AS "
+                + LiveFolders.NAME);
+    }
+
+    /** The database that lies underneath this content provider */
+    private SQLiteOpenHelper mOpenHelper = null;
+
+    /**
+     * Creates and updated database on demand when opening it. Helper class to
+     * create database the first time the provider is initialized and upgrade it
+     * when a new version of the provider needs an updated version of the
+     * database.
+     */
+    private final class DatabaseHelper extends SQLiteOpenHelper {
+
+        public DatabaseHelper(final Context context) {
+            super(context, DB_NAME, null, DB_VERSION);
+        }
+
+        /**
+         * Creates database the first time we try to open it.
+         */
+        @Override
+        public void onCreate(final SQLiteDatabase db) {
+            if (V) Log.v(TAG, "populating new database");
+            createTable(db);
+        }
+
+        //TODO: use this function to check garbage transfer left in db, for example,
+        // a crash incoming file
+        /*
+         * (not a javadoc comment) Checks data integrity when opening the
+         * database.
+         */
+        /*
+         * @Override public void onOpen(final SQLiteDatabase db) {
+         * super.onOpen(db); }
+         */
+
+        /**
+         * Updates the database format when a content provider is used with a
+         * database that was created with a different format.
+         */
+        // Note: technically, this could also be a downgrade, so if we want
+        // to gracefully handle upgrades we should be careful about
+        // what to do on downgrades.
+        @Override
+        public void onUpgrade(final SQLiteDatabase db, int oldV, final int newV) {
+            if (oldV == DB_VERSION_NOP_UPGRADE_FROM) {
+                if (newV == DB_VERSION_NOP_UPGRADE_TO) { // that's a no-op
+                    // upgrade.
+                    return;
+                }
+                // NOP_FROM and NOP_TO are identical, just in different
+                // codelines. Upgrading
+                // from NOP_FROM is the same as upgrading from NOP_TO.
+                oldV = DB_VERSION_NOP_UPGRADE_TO;
+            }
+            Log.i(TAG, "Upgrading downloads database from version " + oldV + " to "
+                    + newV + ", which will destroy all old data");
+            dropTable(db);
+            createTable(db);
+        }
+
+    }
+
+    private void createTable(SQLiteDatabase db) {
+        try {
+            db.execSQL("CREATE TABLE " + DB_TABLE + "(" + BluetoothShare._ID
+                    + " INTEGER PRIMARY KEY AUTOINCREMENT," + BluetoothShare.URI + " TEXT, "
+                    + BluetoothShare.FILENAME_HINT + " TEXT, " + BluetoothShare._DATA + " TEXT, "
+                    + BluetoothShare.MIMETYPE + " TEXT, " + BluetoothShare.DIRECTION + " INTEGER, "
+                    + BluetoothShare.DESTINATION + " TEXT, " + BluetoothShare.VISIBILITY
+                    + " INTEGER, " + BluetoothShare.USER_CONFIRMATION + " INTEGER, "
+                    + BluetoothShare.STATUS + " INTEGER, " + BluetoothShare.TOTAL_BYTES
+                    + " INTEGER, " + BluetoothShare.CURRENT_BYTES + " INTEGER, "
+                    + BluetoothShare.TIMESTAMP + " INTEGER," + Constants.MEDIA_SCANNED
+                    + " INTEGER); ");
+        } catch (SQLException ex) {
+            Log.e(TAG, "couldn't create table in downloads database");
+            throw ex;
+        }
+    }
+
+    private void dropTable(SQLiteDatabase db) {
+        try {
+            db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
+        } catch (SQLException ex) {
+            Log.e(TAG, "couldn't drop table in downloads database");
+            throw ex;
+        }
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        int match = sURIMatcher.match(uri);
+        switch (match) {
+            case SHARES: {
+                return SHARE_LIST_TYPE;
+            }
+            case SHARES_ID: {
+                return SHARE_TYPE;
+            }
+            default: {
+                if (D) Log.d(TAG, "calling getType on an unknown URI: " + uri);
+                throw new IllegalArgumentException("Unknown URI: " + uri);
+            }
+        }
+    }
+
+    private static final void copyString(String key, ContentValues from, ContentValues to) {
+        String s = from.getAsString(key);
+        if (s != null) {
+            to.put(key, s);
+        }
+    }
+
+    private static final void copyInteger(String key, ContentValues from, ContentValues to) {
+        Integer i = from.getAsInteger(key);
+        if (i != null) {
+            to.put(key, i);
+        }
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        if (sURIMatcher.match(uri) != SHARES) {
+            if (D) Log.d(TAG, "calling insert on an unknown/invalid URI: " + uri);
+            throw new IllegalArgumentException("Unknown/Invalid URI " + uri);
+        }
+
+        ContentValues filteredValues = new ContentValues();
+
+        copyString(BluetoothShare.URI, values, filteredValues);
+        copyString(BluetoothShare.FILENAME_HINT, values, filteredValues);
+        copyString(BluetoothShare.MIMETYPE, values, filteredValues);
+        copyString(BluetoothShare.DESTINATION, values, filteredValues);
+
+        copyInteger(BluetoothShare.VISIBILITY, values, filteredValues);
+        copyInteger(BluetoothShare.TOTAL_BYTES, values, filteredValues);
+
+        if (values.getAsInteger(BluetoothShare.VISIBILITY) == null) {
+            filteredValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_VISIBLE);
+        }
+        Integer dir = values.getAsInteger(BluetoothShare.DIRECTION);
+        Integer con = values.getAsInteger(BluetoothShare.USER_CONFIRMATION);
+
+        if (values.getAsInteger(BluetoothShare.DIRECTION) == null) {
+            dir = BluetoothShare.DIRECTION_OUTBOUND;
+        }
+        if (dir == BluetoothShare.DIRECTION_OUTBOUND && con == null) {
+            con = BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED;
+        }
+        if (dir == BluetoothShare.DIRECTION_INBOUND && con == null) {
+            con = BluetoothShare.USER_CONFIRMATION_PENDING;
+        }
+        filteredValues.put(BluetoothShare.USER_CONFIRMATION, con);
+        filteredValues.put(BluetoothShare.DIRECTION, dir);
+
+        filteredValues.put(BluetoothShare.STATUS, BluetoothShare.STATUS_PENDING);
+        filteredValues.put(Constants.MEDIA_SCANNED, 0);
+
+        Long ts = values.getAsLong(BluetoothShare.TIMESTAMP);
+        if (ts == null) {
+            ts = System.currentTimeMillis();
+        }
+        filteredValues.put(BluetoothShare.TIMESTAMP, ts);
+
+        Context context = getContext();
+        context.startService(new Intent(context, BluetoothOppService.class));
+
+        long rowID = db.insert(DB_TABLE, null, filteredValues);
+
+        Uri ret = null;
+
+        if (rowID != -1) {
+            context.startService(new Intent(context, BluetoothOppService.class));
+            ret = Uri.parse(BluetoothShare.CONTENT_URI + "/" + rowID);
+            context.getContentResolver().notifyChange(uri, null);
+        } else {
+            if (D) Log.d(TAG, "couldn't insert into btopp database");
+            }
+
+        return ret;
+    }
+
+    @Override
+    public boolean onCreate() {
+        mOpenHelper = new DatabaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+
+        int match = sURIMatcher.match(uri);
+        switch (match) {
+            case SHARES: {
+                qb.setTables(DB_TABLE);
+                break;
+            }
+            case SHARES_ID: {
+                qb.setTables(DB_TABLE);
+                qb.appendWhere(BluetoothShare._ID + "=");
+                qb.appendWhere(uri.getPathSegments().get(1));
+                break;
+            }
+            case LIVE_FOLDER_RECEIVED_FILES: {
+                qb.setTables(DB_TABLE);
+                qb.setProjectionMap(LIVE_FOLDER_PROJECTION_MAP);
+                qb.appendWhere(BluetoothShare.DIRECTION + "=" + BluetoothShare.DIRECTION_INBOUND
+                        + " AND " + BluetoothShare.STATUS + "=" + BluetoothShare.STATUS_SUCCESS);
+                sortOrder = "_id DESC, " + sortOrder;
+                break;
+            }
+            default: {
+                if (D) Log.d(TAG, "querying unknown URI: " + uri);
+                throw new IllegalArgumentException("Unknown URI: " + uri);
+            }
+        }
+
+        if (V) {
+            java.lang.StringBuilder sb = new java.lang.StringBuilder();
+            sb.append("starting query, database is ");
+            if (db != null) {
+                sb.append("not ");
+            }
+            sb.append("null; ");
+            if (projection == null) {
+                sb.append("projection is null; ");
+            } else if (projection.length == 0) {
+                sb.append("projection is empty; ");
+            } else {
+                for (int i = 0; i < projection.length; ++i) {
+                    sb.append("projection[");
+                    sb.append(i);
+                    sb.append("] is ");
+                    sb.append(projection[i]);
+                    sb.append("; ");
+                }
+            }
+            sb.append("selection is ");
+            sb.append(selection);
+            sb.append("; ");
+            if (selectionArgs == null) {
+                sb.append("selectionArgs is null; ");
+            } else if (selectionArgs.length == 0) {
+                sb.append("selectionArgs is empty; ");
+            } else {
+                for (int i = 0; i < selectionArgs.length; ++i) {
+                    sb.append("selectionArgs[");
+                    sb.append(i);
+                    sb.append("] is ");
+                    sb.append(selectionArgs[i]);
+                    sb.append("; ");
+                }
+            }
+            sb.append("sort is ");
+            sb.append(sortOrder);
+            sb.append(".");
+            Log.v(TAG, sb.toString());
+        }
+
+        Cursor ret = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
+
+        if (ret != null) {
+            ret.setNotificationUri(getContext().getContentResolver(), uri);
+            if (V) Log.v(TAG, "created cursor " + ret + " on behalf of ");// +
+        } else {
+            if (D) Log.d(TAG, "query failed in downloads database");
+            }
+
+        return ret;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        int count;
+        long rowId = 0;
+
+        int match = sURIMatcher.match(uri);
+        switch (match) {
+            case SHARES:
+            case SHARES_ID: {
+                String myWhere;
+                if (selection != null) {
+                    if (match == SHARES) {
+                        myWhere = "( " + selection + " )";
+                    } else {
+                        myWhere = "( " + selection + " ) AND ";
+                    }
+                } else {
+                    myWhere = "";
+                }
+                if (match == SHARES_ID) {
+                    String segment = uri.getPathSegments().get(1);
+                    rowId = Long.parseLong(segment);
+                    myWhere += " ( " + BluetoothShare._ID + " = " + rowId + " ) ";
+                }
+
+                if (values.size() > 0) {
+                    count = db.update(DB_TABLE, values, myWhere, selectionArgs);
+                } else {
+                    count = 0;
+                }
+                break;
+            }
+            default: {
+                if (D) Log.d(TAG, "updating unknown/invalid URI: " + uri);
+                throw new UnsupportedOperationException("Cannot update URI: " + uri);
+            }
+        }
+        getContext().getContentResolver().notifyChange(uri, null);
+
+        return count;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        int count;
+        int match = sURIMatcher.match(uri);
+        switch (match) {
+            case SHARES:
+            case SHARES_ID: {
+                String myWhere;
+                if (selection != null) {
+                    if (match == SHARES) {
+                        myWhere = "( " + selection + " )";
+                    } else {
+                        myWhere = "( " + selection + " ) AND ";
+                    }
+                } else {
+                    myWhere = "";
+                }
+                if (match == SHARES_ID) {
+                    String segment = uri.getPathSegments().get(1);
+                    long rowId = Long.parseLong(segment);
+                    myWhere += " ( " + BluetoothShare._ID + " = " + rowId + " ) ";
+                }
+
+                count = db.delete(DB_TABLE, myWhere, selectionArgs);
+                break;
+            }
+            default: {
+                if (D) Log.d(TAG, "deleting unknown/invalid URI: " + uri);
+                throw new UnsupportedOperationException("Cannot delete URI: " + uri);
+            }
+        }
+        getContext().getContentResolver().notifyChange(uri, null);
+        return count;
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppReceiveFileInfo.java b/src/com/android/bluetooth/opp/BluetoothOppReceiveFileInfo.java
new file mode 100644
index 0000000..b5ac274
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppReceiveFileInfo.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Random;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.StatFs;
+import android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * This class stores information about a single receiving file. It will only be
+ * used for inbounds share, e.g. receive a file to determine a correct save file
+ * name
+ */
+public class BluetoothOppReceiveFileInfo {
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+    private static String sDesiredStoragePath = null;
+
+    /** absolute store file name */
+    public String mFileName;
+
+    public long mLength;
+
+    public FileOutputStream mOutputStream;
+
+    public int mStatus;
+
+    public String mData;
+
+    public BluetoothOppReceiveFileInfo(String data, long length, int status) {
+        mData = data;
+        mStatus = status;
+        mLength = length;
+    }
+
+    public BluetoothOppReceiveFileInfo(String filename, long length, FileOutputStream outputStream,
+            int status) {
+        mFileName = filename;
+        mOutputStream = outputStream;
+        mStatus = status;
+        mLength = length;
+    }
+
+    public BluetoothOppReceiveFileInfo(int status) {
+        this(null, 0, null, status);
+    }
+
+    // public static final int BATCH_STATUS_CANCELED = 4;
+    public static BluetoothOppReceiveFileInfo generateFileInfo(Context context, int id) {
+
+        ContentResolver contentResolver = context.getContentResolver();
+        Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
+        String filename = null, hint = null;
+        long length = 0;
+        Cursor metadataCursor = contentResolver.query(contentUri, new String[] {
+                BluetoothShare.FILENAME_HINT, BluetoothShare.TOTAL_BYTES, BluetoothShare.MIMETYPE
+        }, null, null, null);
+        if (metadataCursor != null) {
+            try {
+                if (metadataCursor.moveToFirst()) {
+                    hint = metadataCursor.getString(0);
+                    length = metadataCursor.getInt(1);
+                }
+            } finally {
+                metadataCursor.close();
+            }
+        }
+
+        File base = null;
+        StatFs stat = null;
+
+        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+            String root = Environment.getExternalStorageDirectory().getPath();
+            base = new File(root + Constants.DEFAULT_STORE_SUBDIR);
+            if (!base.isDirectory() && !base.mkdir()) {
+                if (D) Log.d(Constants.TAG, "Receive File aborted - can't create base directory "
+                            + base.getPath());
+                return new BluetoothOppReceiveFileInfo(BluetoothShare.STATUS_FILE_ERROR);
+            }
+            stat = new StatFs(base.getPath());
+        } else {
+            if (D) Log.d(Constants.TAG, "Receive File aborted - no external storage");
+            return new BluetoothOppReceiveFileInfo(BluetoothShare.STATUS_ERROR_NO_SDCARD);
+        }
+
+        /*
+         * Check whether there's enough space on the target filesystem to save
+         * the file. Put a bit of margin (in case creating the file grows the
+         * system by a few blocks).
+         */
+        if (stat.getBlockSize() * ((long)stat.getAvailableBlocks() - 4) < length) {
+            if (D) Log.d(Constants.TAG, "Receive File aborted - not enough free space");
+            return new BluetoothOppReceiveFileInfo(BluetoothShare.STATUS_ERROR_SDCARD_FULL);
+        }
+
+        filename = choosefilename(hint);
+        if (filename == null) {
+            // should not happen. It must be pre-rejected
+            return new BluetoothOppReceiveFileInfo(BluetoothShare.STATUS_FILE_ERROR);
+        }
+        String extension = null;
+        int dotIndex = filename.lastIndexOf(".");
+        if (dotIndex < 0) {
+            // should not happen. It must be pre-rejected
+            return new BluetoothOppReceiveFileInfo(BluetoothShare.STATUS_FILE_ERROR);
+        } else {
+            extension = filename.substring(dotIndex);
+            filename = filename.substring(0, dotIndex);
+        }
+        filename = base.getPath() + File.separator + filename;
+        // Generate a unique filename, create the file, return it.
+        String fullfilename = chooseUniquefilename(filename, extension);
+
+        if (!safeCanonicalPath(fullfilename)) {
+            // If this second check fails, then we better reject the transfer
+            return new BluetoothOppReceiveFileInfo(BluetoothShare.STATUS_FILE_ERROR);
+        }
+        if (V) Log.v(Constants.TAG, "Generated received filename " + fullfilename);
+
+        if (fullfilename != null) {
+            try {
+                new FileOutputStream(fullfilename).close();
+                int index = fullfilename.lastIndexOf('/') + 1;
+                // update display name
+                if (index > 0) {
+                    String displayName = fullfilename.substring(index);
+                    if (V) Log.v(Constants.TAG, "New display name " + displayName);
+                    ContentValues updateValues = new ContentValues();
+                    updateValues.put(BluetoothShare.FILENAME_HINT, displayName);
+                    context.getContentResolver().update(contentUri, updateValues, null, null);
+
+                }
+                return new BluetoothOppReceiveFileInfo(fullfilename, length, new FileOutputStream(
+                        fullfilename), 0);
+            } catch (IOException e) {
+                if (D) Log.e(Constants.TAG, "Error when creating file " + fullfilename);
+                return new BluetoothOppReceiveFileInfo(BluetoothShare.STATUS_FILE_ERROR);
+            }
+        } else {
+            return new BluetoothOppReceiveFileInfo(BluetoothShare.STATUS_FILE_ERROR);
+        }
+
+    }
+
+    private static boolean safeCanonicalPath(String uniqueFileName) {
+        try {
+            File receiveFile = new File(uniqueFileName);
+            if (sDesiredStoragePath == null) {
+                sDesiredStoragePath = Environment.getExternalStorageDirectory().getPath() +
+                    Constants.DEFAULT_STORE_SUBDIR;
+            }
+            String canonicalPath = receiveFile.getCanonicalPath();
+
+            // Check if canonical path is complete - case sensitive-wise
+            if (!canonicalPath.startsWith(sDesiredStoragePath)) {
+                return false;
+            }
+
+	    	return true;
+        } catch (IOException ioe) {
+            // If an exception is thrown, there might be something wrong with the file.
+            return false;
+        }
+    }
+
+    private static String chooseUniquefilename(String filename, String extension) {
+        String fullfilename = filename + extension;
+        if (!new File(fullfilename).exists()) {
+            return fullfilename;
+        }
+        filename = filename + Constants.filename_SEQUENCE_SEPARATOR;
+        /*
+         * This number is used to generate partially randomized filenames to
+         * avoid collisions. It starts at 1. The next 9 iterations increment it
+         * by 1 at a time (up to 10). The next 9 iterations increment it by 1 to
+         * 10 (random) at a time. The next 9 iterations increment it by 1 to 100
+         * (random) at a time. ... Up to the point where it increases by
+         * 100000000 at a time. (the maximum value that can be reached is
+         * 1000000000) As soon as a number is reached that generates a filename
+         * that doesn't exist, that filename is used. If the filename coming in
+         * is [base].[ext], the generated filenames are [base]-[sequence].[ext].
+         */
+        Random rnd = new Random(SystemClock.uptimeMillis());
+        int sequence = 1;
+        for (int magnitude = 1; magnitude < 1000000000; magnitude *= 10) {
+            for (int iteration = 0; iteration < 9; ++iteration) {
+                fullfilename = filename + sequence + extension;
+                if (!new File(fullfilename).exists()) {
+                    return fullfilename;
+                }
+                if (V) Log.v(Constants.TAG, "file with sequence number " + sequence + " exists");
+                sequence += rnd.nextInt(magnitude) + 1;
+            }
+        }
+        return null;
+    }
+
+    private static String choosefilename(String hint) {
+        String filename = null;
+
+        // First, try to use the hint from the application, if there's one
+        if (filename == null && !(hint == null) && !hint.endsWith("/") && !hint.endsWith("\\")) {
+            // Prevent abuse of path backslashes by converting all backlashes '\\' chars
+            // to UNIX-style forward-slashes '/'
+            hint = hint.replace('\\', '/');
+            if (V) Log.v(Constants.TAG, "getting filename from hint");
+            int index = hint.lastIndexOf('/') + 1;
+            if (index > 0) {
+                filename = hint.substring(index);
+            } else {
+                filename = hint;
+            }
+        }
+        return filename;
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppReceiver.java b/src/com/android/bluetooth/opp/BluetoothOppReceiver.java
new file mode 100644
index 0000000..516947f
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppReceiver.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.app.NotificationManager;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDevicePicker;
+import android.content.BroadcastReceiver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Receives and handles: system broadcasts; Intents from other applications;
+ * Intents from OppService; Intents from modules in Opp application layer.
+ */
+public class BluetoothOppReceiver extends BroadcastReceiver {
+    private static final String TAG = "BluetoothOppReceiver";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+
+        if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+
+            context.startService(new Intent(context, BluetoothOppService.class));
+        } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+            if (BluetoothAdapter.STATE_ON == intent.getIntExtra(
+                    BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
+                if (V) Log.v(TAG, "Received BLUETOOTH_STATE_CHANGED_ACTION, BLUETOOTH_STATE_ON");
+                context.startService(new Intent(context, BluetoothOppService.class));
+
+                // If this is within a sending process, continue the handle
+                // logic to display device picker dialog.
+                synchronized (this) {
+                    if (BluetoothOppManager.getInstance(context).mSendingFlag) {
+                        // reset the flags
+                        BluetoothOppManager.getInstance(context).mSendingFlag = false;
+
+                        Intent in1 = new Intent(BluetoothDevicePicker.ACTION_LAUNCH);
+                        in1.putExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
+                        in1.putExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
+                                BluetoothDevicePicker.FILTER_TYPE_TRANSFER);
+                        in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE,
+                                Constants.THIS_PACKAGE_NAME);
+                        in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS,
+                                BluetoothOppReceiver.class.getName());
+
+                        in1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        context.startActivity(in1);
+                    }
+                }
+            }
+        } else if (action.equals(BluetoothDevicePicker.ACTION_DEVICE_SELECTED)) {
+            BluetoothOppManager mOppManager = BluetoothOppManager.getInstance(context);
+
+            BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+            if (V) Log.v(TAG, "Received BT device selected intent, bt device: " + remoteDevice);
+
+            // Insert transfer session record to database
+            mOppManager.startTransfer(remoteDevice);
+
+            // Display toast message
+            String deviceName = mOppManager.getDeviceName(remoteDevice);
+            String toastMsg;
+            if (mOppManager.mMultipleFlag) {
+                toastMsg = context.getString(R.string.bt_toast_5, Integer
+                        .toString(mOppManager.mfileNumInBatch), deviceName);
+            } else {
+                toastMsg = context.getString(R.string.bt_toast_4, deviceName);
+            }
+            Toast.makeText(context, toastMsg, Toast.LENGTH_SHORT).show();
+        } else if (action.equals(Constants.ACTION_INCOMING_FILE_CONFIRM)) {
+            if (V) Log.v(TAG, "Receiver ACTION_INCOMING_FILE_CONFIRM");
+
+            Uri uri = intent.getData();
+            Intent in = new Intent(context, BluetoothOppIncomingFileConfirmActivity.class);
+            in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            in.setData(uri);
+            context.startActivity(in);
+
+            NotificationManager notMgr = (NotificationManager)context
+                    .getSystemService(Context.NOTIFICATION_SERVICE);
+            if (notMgr != null) {
+                notMgr.cancel((int)ContentUris.parseId(intent.getData()));
+                if (V) Log.v(TAG, "notMgr.cancel called");
+            }
+        } else if (action.equals(BluetoothShare.INCOMING_FILE_CONFIRMATION_REQUEST_ACTION)) {
+            if (V) Log.v(TAG, "Receiver INCOMING_FILE_NOTIFICATION");
+
+            Toast.makeText(context, context.getString(R.string.incoming_file_toast_msg),
+                    Toast.LENGTH_SHORT).show();
+
+        } else if (action.equals(Constants.ACTION_OPEN) || action.equals(Constants.ACTION_LIST)) {
+            if (V) {
+                if (action.equals(Constants.ACTION_OPEN)) {
+                    Log.v(TAG, "Receiver open for " + intent.getData());
+                } else {
+                    Log.v(TAG, "Receiver list for " + intent.getData());
+                }
+            }
+
+            BluetoothOppTransferInfo transInfo = new BluetoothOppTransferInfo();
+            Uri uri = intent.getData();
+            transInfo = BluetoothOppUtility.queryRecord(context, uri);
+            if (transInfo == null) {
+                Log.e(TAG, "Error: Can not get data from db");
+                return;
+            }
+
+            if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND
+                    && BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
+                // if received file successfully, open this file
+                BluetoothOppUtility.openReceivedFile(context, transInfo.mFileName,
+                        transInfo.mFileType, transInfo.mTimeStamp);
+                BluetoothOppUtility.updateVisibilityToHidden(context, uri);
+            } else {
+                Intent in = new Intent(context, BluetoothOppTransferActivity.class);
+                in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                in.setData(uri);
+                context.startActivity(in);
+            }
+
+            NotificationManager notMgr = (NotificationManager)context
+                    .getSystemService(Context.NOTIFICATION_SERVICE);
+            if (notMgr != null) {
+                notMgr.cancel((int)ContentUris.parseId(intent.getData()));
+                if (V) Log.v(TAG, "notMgr.cancel called");
+                }
+        } else if (action.equals(Constants.ACTION_HIDE)) {
+            if (V) Log.v(TAG, "Receiver hide for " + intent.getData());
+            Cursor cursor = context.getContentResolver().query(intent.getData(), null, null, null,
+                    null);
+            if (cursor != null) {
+                if (cursor.moveToFirst()) {
+                    int statusColumn = cursor.getColumnIndexOrThrow(BluetoothShare.STATUS);
+                    int status = cursor.getInt(statusColumn);
+                    int visibilityColumn = cursor.getColumnIndexOrThrow(BluetoothShare.VISIBILITY);
+                    int visibility = cursor.getInt(visibilityColumn);
+                    int userConfirmationColumn = cursor
+                            .getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION);
+                    int userConfirmation = cursor.getInt(userConfirmationColumn);
+                    if ((BluetoothShare.isStatusCompleted(status) || (userConfirmation == BluetoothShare.USER_CONFIRMATION_PENDING))
+                            && visibility == BluetoothShare.VISIBILITY_VISIBLE) {
+                        ContentValues values = new ContentValues();
+                        values.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
+                        context.getContentResolver().update(intent.getData(), values, null, null);
+                        if (V) Log.v(TAG, "Action_hide received and db updated");
+                        }
+                }
+                cursor.close();
+            }
+        } else if (action.equals(BluetoothShare.TRANSFER_COMPLETED_ACTION)) {
+            if (V) Log.v(TAG, "Receiver Transfer Complete Intent for " + intent.getData());
+
+            String toastMsg = null;
+            BluetoothOppTransferInfo transInfo = new BluetoothOppTransferInfo();
+            transInfo = BluetoothOppUtility.queryRecord(context, intent.getData());
+            if (transInfo == null) {
+                Log.e(TAG, "Error: Can not get data from db");
+                return;
+            }
+
+            if (BluetoothShare.isStatusSuccess(transInfo.mStatus)) {
+                if (transInfo.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                    toastMsg = context.getString(R.string.notification_sent, transInfo.mFileName);
+                } else if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND) {
+                    toastMsg = context.getString(R.string.notification_received,
+                            transInfo.mFileName);
+                }
+
+            } else if (BluetoothShare.isStatusError(transInfo.mStatus)) {
+                if (transInfo.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                    toastMsg = context.getString(R.string.notification_sent_fail,
+                            transInfo.mFileName);
+                } else if (transInfo.mDirection == BluetoothShare.DIRECTION_INBOUND) {
+                    toastMsg = context.getString(R.string.download_fail_line1);
+                }
+            }
+            if (V) Log.v(TAG, "Toast msg == " + toastMsg);
+            if (toastMsg != null) {
+                Toast.makeText(context, toastMsg, Toast.LENGTH_SHORT).show();
+            }
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppRfcommListener.java b/src/com/android/bluetooth/opp/BluetoothOppRfcommListener.java
new file mode 100644
index 0000000..c33cb66
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppRfcommListener.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * This class listens on OPUSH channel for incoming connection
+ */
+public class BluetoothOppRfcommListener {
+    private static final String TAG = "BtOppRfcommListener";
+
+    private static final boolean D = Constants.DEBUG;
+
+    private static final boolean V = Constants.VERBOSE;
+
+    public static final int MSG_INCOMING_BTOPP_CONNECTION = 100;
+
+    private volatile boolean mInterrupted;
+
+    private Thread mSocketAcceptThread;
+
+    private Handler mCallback;
+
+    private static final int CREATE_RETRY_TIME = 10;
+
+    private static final int DEFAULT_OPP_CHANNEL = 12;
+
+    private final int mBtOppRfcommChannel;
+
+    private final BluetoothAdapter mAdapter;
+
+    private BluetoothServerSocket mBtServerSocket = null;
+
+    private ServerSocket mTcpServerSocket = null;
+
+    public BluetoothOppRfcommListener(BluetoothAdapter adapter) {
+        this(adapter, DEFAULT_OPP_CHANNEL);
+    }
+
+    public BluetoothOppRfcommListener(BluetoothAdapter adapter, int channel) {
+        mBtOppRfcommChannel = channel;
+        mAdapter = adapter;
+    }
+
+    public synchronized boolean start(Handler callback) {
+        if (mSocketAcceptThread == null) {
+            mCallback = callback;
+
+            mSocketAcceptThread = new Thread(TAG) {
+
+                public void run() {
+                    if (Constants.USE_TCP_DEBUG) {
+                        try {
+                            if (V) Log.v(TAG, "Create TCP ServerSocket");
+                            mTcpServerSocket = new ServerSocket(Constants.TCP_DEBUG_PORT, 1);
+                        } catch (IOException e) {
+                            Log.e(TAG, "Error listing on port" + Constants.TCP_DEBUG_PORT);
+                            mInterrupted = true;
+                        }
+                        while (!mInterrupted) {
+                            try {
+                                Socket clientSocket = mTcpServerSocket.accept();
+
+                                if (V) Log.v(TAG, "Socket connected!");
+                                TestTcpTransport transport = new TestTcpTransport(clientSocket);
+                                Message msg = Message.obtain();
+                                msg.setTarget(mCallback);
+                                msg.what = MSG_INCOMING_BTOPP_CONNECTION;
+                                msg.obj = transport;
+                                msg.sendToTarget();
+
+                            } catch (IOException e) {
+                                Log.e(TAG, "Error accept connection " + e);
+                            }
+                        }
+                        if (V) Log.v(TAG, "TCP listen thread finished");
+                    } else {
+                        boolean serverOK = true;
+
+                        /*
+                         * it's possible that create will fail in some cases.
+                         * retry for 10 times
+                         */
+                        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
+                            try {
+                                mBtServerSocket = mAdapter
+                                        .listenUsingInsecureRfcommOn(mBtOppRfcommChannel);
+                            } catch (IOException e1) {
+                                Log.e(TAG, "Error create RfcommServerSocket " + e1);
+                                serverOK = false;
+                            }
+                            if (!serverOK) {
+                                synchronized (this) {
+                                    try {
+                                        if (V) Log.v(TAG, "wait 3 seconds");
+                                        Thread.sleep(3000);
+                                    } catch (InterruptedException e) {
+                                        Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
+                                        mInterrupted = true;
+                                    }
+                                }
+                            } else {
+                                break;
+                            }
+                        }
+                        if (!serverOK) {
+                            Log.e(TAG, "Error start listening after " + CREATE_RETRY_TIME + " try");
+                            mInterrupted = true;
+                        }
+                        if (!mInterrupted) {
+                            Log.i(TAG, "Accept thread started on channel " + mBtOppRfcommChannel);
+                        }
+                        BluetoothSocket clientSocket;
+                        while (!mInterrupted) {
+                            try {
+                                clientSocket = mBtServerSocket.accept();
+                                Log.i(TAG, "Accepted connectoin from "
+                                        + clientSocket.getRemoteDevice());
+                                BluetoothOppRfcommTransport transport = new BluetoothOppRfcommTransport(
+                                        clientSocket);
+                                Message msg = Message.obtain();
+                                msg.setTarget(mCallback);
+                                msg.what = MSG_INCOMING_BTOPP_CONNECTION;
+                                msg.obj = transport;
+                                msg.sendToTarget();
+                            } catch (IOException e) {
+                                Log.e(TAG, "Error accept connection " + e);
+                            }
+                        }
+                        Log.i(TAG, "BluetoothSocket listen thread finished");
+                    }
+                }
+            };
+            mInterrupted = false;
+            if(!Constants.USE_TCP_SIMPLE_SERVER) {
+                mSocketAcceptThread.start();
+            }
+        }
+        return true;
+    }
+
+    public synchronized void stop() {
+        if (mSocketAcceptThread != null) {
+            Log.i(TAG, "stopping Accept Thread");
+
+            mInterrupted = true;
+            if (Constants.USE_TCP_DEBUG) {
+                if (V) Log.v(TAG, "close mTcpServerSocket");
+                if (mTcpServerSocket != null) {
+                    try {
+                        mTcpServerSocket.close();
+                    } catch (IOException e) {
+                        Log.e(TAG, "Error close mTcpServerSocket");
+                    }
+                }
+            } else {
+                if (V) Log.v(TAG, "close mBtServerSocket");
+
+                if (mBtServerSocket != null) {
+                    try {
+                        mBtServerSocket.close();
+                    } catch (IOException e) {
+                        Log.e(TAG, "Error close mBtServerSocket");
+                    }
+                }
+            }
+            try {
+                mSocketAcceptThread.interrupt();
+                if (V) Log.v(TAG, "waiting for thread to terminate");
+                mSocketAcceptThread.join();
+                mSocketAcceptThread = null;
+                mCallback = null;
+            } catch (InterruptedException e) {
+                if (V) Log.v(TAG, "Interrupted waiting for Accept Thread to join");
+            }
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppRfcommTransport.java b/src/com/android/bluetooth/opp/BluetoothOppRfcommTransport.java
new file mode 100644
index 0000000..a4ec0fe
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppRfcommTransport.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import android.bluetooth.BluetoothSocket;
+
+import javax.obex.ObexTransport;
+
+public class BluetoothOppRfcommTransport implements ObexTransport {
+
+    private final BluetoothSocket mSocket;
+
+    public BluetoothOppRfcommTransport(BluetoothSocket socket) {
+        super();
+        this.mSocket = socket;
+    }
+
+    public void close() throws IOException {
+        mSocket.close();
+    }
+
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return mSocket.getInputStream();
+    }
+
+    public OutputStream openOutputStream() throws IOException {
+        return mSocket.getOutputStream();
+    }
+
+    public void connect() throws IOException {
+    }
+
+    public void create() throws IOException {
+    }
+
+    public void disconnect() throws IOException {
+    }
+
+    public void listen() throws IOException {
+    }
+
+    public boolean isConnected() throws IOException {
+        //return mSocket.isConnected();
+        // TODO: add implementation
+        return true;
+    }
+
+    public String getRemoteAddress() {
+        if (mSocket == null)
+            return null;
+        return mSocket.getRemoteDevice().getAddress();
+    }
+
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
new file mode 100644
index 0000000..0ccfe7f
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.OpenableColumns;
+
+/**
+ * This class stores information about a single sending file It will only be
+ * used for outbound share.
+ */
+public class BluetoothOppSendFileInfo {
+    /** readable media file name */
+    public final String mFileName;
+
+    /** media file input stream */
+    public final FileInputStream mInputStream;
+
+    /** vCard string data */
+    public final String mData;
+
+    public final int mStatus;
+
+    public final String mMimetype;
+
+    public final long mLength;
+
+    public final String mDestAddr;
+
+    /** for media file */
+    public BluetoothOppSendFileInfo(String fileName, String type, long length,
+            FileInputStream inputStream, int status, String dest) {
+        mFileName = fileName;
+        mMimetype = type;
+        mLength = length;
+        mInputStream = inputStream;
+        mStatus = status;
+        mDestAddr = dest;
+        mData = null;
+    }
+
+    /** for vCard, or later for vCal, vNote. Not used currently */
+    public BluetoothOppSendFileInfo(String data, String type, long length, int status,
+            String dest) {
+        mFileName = null;
+        mInputStream = null;
+        mData = data;
+        mMimetype = type;
+        mLength = length;
+        mStatus = status;
+        mDestAddr = dest;
+    }
+
+    public static BluetoothOppSendFileInfo generateFileInfo(Context context, String uri,
+            String type, String dest) {
+        //TODO consider uri is a file:// uri
+        ContentResolver contentResolver = context.getContentResolver();
+        Uri u = Uri.parse(uri);
+        String scheme = u.getScheme();
+        String authority = u.getAuthority();
+        String fileName = null;
+        String contentType = null;
+        long length = 0;
+        if (scheme.equals("content") && authority.equals("media")) {
+
+            contentType = contentResolver.getType(u);
+            Cursor metadataCursor = contentResolver.query(u, new String[] {
+                    OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE
+            }, null, null, null);
+            if (metadataCursor != null) {
+                try {
+                    if (metadataCursor.moveToFirst()) {
+                        fileName = metadataCursor.getString(0);
+                        length = metadataCursor.getInt(1);
+                    }
+                } finally {
+                    metadataCursor.close();
+                }
+            }
+        } else if (scheme.equals("file")) {
+            fileName = u.getLastPathSegment();
+            contentType = type;
+            File f = new File(u.getPath());
+            length = f.length();
+        } else {
+            // currently don't accept other scheme
+            return new BluetoothOppSendFileInfo(null, null, 0, null,
+                    BluetoothShare.STATUS_FILE_ERROR, dest);
+        }
+        FileInputStream is;
+        try {
+            is = (FileInputStream)contentResolver.openInputStream(u);
+        } catch (FileNotFoundException e) {
+            return new BluetoothOppSendFileInfo(null, null, 0, null,
+                    BluetoothShare.STATUS_FILE_ERROR, dest);
+        }
+        return new BluetoothOppSendFileInfo(fileName, contentType, length, is, 0, dest);
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppService.java b/src/com/android/bluetooth/opp/BluetoothOppService.java
new file mode 100644
index 0000000..49226fa
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppService.java
@@ -0,0 +1,950 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.google.android.collect.Lists;
+import javax.obex.ObexTransport;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.CharArrayBuffer;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.media.MediaScannerConnection;
+import android.media.MediaScannerConnection.MediaScannerConnectionClient;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.util.Log;
+import android.os.Process;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+/**
+ * Performs the background Bluetooth OPP transfer. It also starts thread to
+ * accept incoming OPP connection.
+ */
+
+public class BluetoothOppService extends Service {
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    private boolean userAccepted = false;
+
+    private class BluetoothShareContentObserver extends ContentObserver {
+
+        public BluetoothShareContentObserver() {
+            super(new Handler());
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (V) Log.v(TAG, "ContentObserver received notification");
+            updateFromProvider();
+        }
+    }
+
+    private static final String TAG = "BtOpp Service";
+
+    /** Observer to get notified when the content observer's data changes */
+    private BluetoothShareContentObserver mObserver;
+
+    /** Class to handle Notification Manager updates */
+    private BluetoothOppNotification mNotifier;
+
+    private boolean mPendingUpdate;
+
+    private UpdateThread mUpdateThread;
+
+    private ArrayList<BluetoothOppShareInfo> mShares;
+
+    private ArrayList<BluetoothOppBatch> mBatchs;
+
+    private BluetoothOppTransfer mTransfer;
+
+    private BluetoothOppTransfer mServerTransfer;
+
+    private int mBatchId;
+
+    /**
+     * Array used when extracting strings from content provider
+     */
+    private CharArrayBuffer mOldChars;
+
+    /**
+     * Array used when extracting strings from content provider
+     */
+    private CharArrayBuffer mNewChars;
+
+    private BluetoothAdapter mAdapter;
+
+    private PowerManager mPowerManager;
+
+    private BluetoothOppRfcommListener mSocketListener;
+
+    private boolean mListenStarted = false;
+
+    private boolean mMediaScanInProgress;
+
+    private int mIncomingRetries = 0;
+
+    private ObexTransport mPendingConnection = null;
+
+    /*
+     * TODO No support for queue incoming from multiple devices.
+     * Make an array list of server session to support receiving queue from
+     * multiple devices
+     */
+    private BluetoothOppObexServerSession mServerSession;
+
+    @Override
+    public IBinder onBind(Intent arg0) {
+        throw new UnsupportedOperationException("Cannot bind to Bluetooth OPP Service");
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (V) Log.v(TAG, "Service onCreate");
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mSocketListener = new BluetoothOppRfcommListener(mAdapter);
+        mShares = Lists.newArrayList();
+        mBatchs = Lists.newArrayList();
+        mObserver = new BluetoothShareContentObserver();
+        getContentResolver().registerContentObserver(BluetoothShare.CONTENT_URI, true, mObserver);
+        mBatchId = 1;
+        mNotifier = new BluetoothOppNotification(this);
+        mNotifier.mNotificationMgr.cancelAll();
+        mNotifier.updateNotification();
+        mNotifier.finishNotification();
+
+        trimDatabase();
+
+        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+        registerReceiver(mBluetoothReceiver, filter);
+
+        synchronized (BluetoothOppService.this) {
+            if (mAdapter == null) {
+                Log.w(TAG, "Local BT device is not enabled");
+            } else {
+                startListenerDelayed();
+            }
+        }
+        if (V) BluetoothOppPreference.getInstance(this).dump();
+        updateFromProvider();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (V) Log.v(TAG, "Service onStartCommand");
+        int retCode = super.onStartCommand(intent, flags, startId);
+        if (retCode == START_STICKY) {
+            if (mAdapter == null) {
+                Log.w(TAG, "Local BT device is not enabled");
+            } else {
+                startListenerDelayed();
+            }
+            updateFromProvider();
+        }
+        return retCode;
+    }
+
+    private void startListenerDelayed() {
+        if (!mListenStarted) {
+            if (mAdapter.isEnabled()) {
+                if (V) Log.v(TAG, "Starting RfcommListener in 9 seconds");
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(START_LISTENER), 9000);
+                mListenStarted = true;
+            }
+        }
+    }
+
+    private static final int START_LISTENER = 1;
+
+    private static final int MEDIA_SCANNED = 2;
+
+    private static final int MEDIA_SCANNED_FAILED = 3;
+
+    private static final int MSG_INCOMING_CONNECTION_RETRY = 4;
+
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case START_LISTENER:
+                    if (mAdapter.isEnabled()) {
+                        startSocketListener();
+                    }
+                    break;
+                case MEDIA_SCANNED:
+                    if (V) Log.v(TAG, "Update mInfo.id " + msg.arg1 + " for data uri= "
+                                + msg.obj.toString());
+                    ContentValues updateValues = new ContentValues();
+                    Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + msg.arg1);
+                    updateValues.put(Constants.MEDIA_SCANNED, Constants.MEDIA_SCANNED_SCANNED_OK);
+                    updateValues.put(BluetoothShare.URI, msg.obj.toString()); // update
+                    updateValues.put(BluetoothShare.MIMETYPE, getContentResolver().getType(
+                            Uri.parse(msg.obj.toString())));
+                    getContentResolver().update(contentUri, updateValues, null, null);
+                    synchronized (BluetoothOppService.this) {
+                        mMediaScanInProgress = false;
+                    }
+                    break;
+                case MEDIA_SCANNED_FAILED:
+                    Log.v(TAG, "Update mInfo.id " + msg.arg1 + " for MEDIA_SCANNED_FAILED");
+                    ContentValues updateValues1 = new ContentValues();
+                    Uri contentUri1 = Uri.parse(BluetoothShare.CONTENT_URI + "/" + msg.arg1);
+                    updateValues1.put(Constants.MEDIA_SCANNED,
+                            Constants.MEDIA_SCANNED_SCANNED_FAILED);
+                    getContentResolver().update(contentUri1, updateValues1, null, null);
+                    synchronized (BluetoothOppService.this) {
+                        mMediaScanInProgress = false;
+                    }
+                    break;
+                case BluetoothOppRfcommListener.MSG_INCOMING_BTOPP_CONNECTION:
+                    if (D) Log.d(TAG, "Get incoming connection");
+                    ObexTransport transport = (ObexTransport)msg.obj;
+                    /*
+                     * Strategy for incoming connections:
+                     * 1. If there is no ongoing transfer, no on-hold connection, start it
+                     * 2. If there is ongoing transfer, hold it for 20 seconds(1 seconds * 20 times)
+                     * 3. If there is on-hold connection, reject directly
+                     */
+                    if (mBatchs.size() == 0 && mPendingConnection == null) {
+                        Log.i(TAG, "Start Obex Server");
+                        createServerSession(transport);
+                    } else {
+                        if (mPendingConnection != null) {
+                            Log.w(TAG, "OPP busy! Reject connection");
+                            try {
+                                transport.close();
+                            } catch (IOException e) {
+                                Log.e(TAG, "close tranport error");
+                            }
+                        } else if (Constants.USE_TCP_DEBUG && !Constants.USE_TCP_SIMPLE_SERVER){
+                            Log.i(TAG, "Start Obex Server in TCP DEBUG mode");
+                            createServerSession(transport);
+                        } else {
+                            Log.i(TAG, "OPP busy! Retry after 1 second");
+                            mIncomingRetries = mIncomingRetries + 1;
+                            mPendingConnection = transport;
+                            Message msg1 = Message.obtain(mHandler);
+                            msg1.what = MSG_INCOMING_CONNECTION_RETRY;
+                            mHandler.sendMessageDelayed(msg1, 1000);
+                        }
+                    }
+                    break;
+                case MSG_INCOMING_CONNECTION_RETRY:
+                    if (mBatchs.size() == 0) {
+                        Log.i(TAG, "Start Obex Server");
+                        createServerSession(mPendingConnection);
+                        mIncomingRetries = 0;
+                        mPendingConnection = null;
+                    } else {
+                        if (mIncomingRetries == 20) {
+                            Log.w(TAG, "Retried 20 seconds, reject connection");
+                            try {
+                                mPendingConnection.close();
+                            } catch (IOException e) {
+                                Log.e(TAG, "close tranport error");
+                            }
+                            mIncomingRetries = 0;
+                            mPendingConnection = null;
+                        } else {
+                            Log.i(TAG, "OPP busy! Retry after 1 second");
+                            mIncomingRetries = mIncomingRetries + 1;
+                            Message msg2 = Message.obtain(mHandler);
+                            msg2.what = MSG_INCOMING_CONNECTION_RETRY;
+                            mHandler.sendMessageDelayed(msg2, 1000);
+                        }
+                    }
+                    break;
+            }
+        }
+    };
+
+    private void startSocketListener() {
+
+        if (V) Log.v(TAG, "start RfcommListener");
+        mSocketListener.start(mHandler);
+        if (V) Log.v(TAG, "RfcommListener started");
+    }
+
+    @Override
+    public void onDestroy() {
+        if (V) Log.v(TAG, "Service onDestroy");
+        super.onDestroy();
+        mNotifier.finishNotification();
+        getContentResolver().unregisterContentObserver(mObserver);
+        unregisterReceiver(mBluetoothReceiver);
+        mSocketListener.stop();
+    }
+
+    /* suppose we auto accept an incoming OPUSH connection */
+    private void createServerSession(ObexTransport transport) {
+        mServerSession = new BluetoothOppObexServerSession(this, transport);
+        mServerSession.preStart();
+        if (D) Log.d(TAG, "Get ServerSession " + mServerSession.toString()
+                    + " for incoming connection" + transport.toString());
+    }
+
+    private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+                switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
+                    case BluetoothAdapter.STATE_ON:
+                        if (V) Log.v(TAG,
+                                    "Receiver BLUETOOTH_STATE_CHANGED_ACTION, BLUETOOTH_STATE_ON");
+                        startSocketListener();
+                        break;
+                    case BluetoothAdapter.STATE_TURNING_OFF:
+                        if (V) Log.v(TAG, "Receiver DISABLED_ACTION ");
+                        mSocketListener.stop();
+                        mListenStarted = false;
+                        synchronized (BluetoothOppService.this) {
+                            if (mUpdateThread == null) {
+                                stopSelf();
+                            }
+                        }
+                        break;
+                }
+            }
+        }
+    };
+
+    private void updateFromProvider() {
+        synchronized (BluetoothOppService.this) {
+            mPendingUpdate = true;
+            if (mUpdateThread == null) {
+                mUpdateThread = new UpdateThread();
+                mUpdateThread.start();
+            }
+        }
+    }
+
+    private class UpdateThread extends Thread {
+        public UpdateThread() {
+            super("Bluetooth Share Service");
+        }
+
+        @Override
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+            boolean keepService = false;
+            for (;;) {
+                synchronized (BluetoothOppService.this) {
+                    if (mUpdateThread != this) {
+                        throw new IllegalStateException(
+                                "multiple UpdateThreads in BluetoothOppService");
+                    }
+                    if (V) Log.v(TAG, "pendingUpdate is " + mPendingUpdate + " keepUpdateThread is "
+                                + keepService + " sListenStarted is " + mListenStarted);
+                    if (!mPendingUpdate) {
+                        mUpdateThread = null;
+                        if (!keepService && !mListenStarted) {
+                            stopSelf();
+                            break;
+                        }
+                        mNotifier.updateNotification();
+                        mNotifier.finishNotification();
+                        return;
+                    }
+                    mPendingUpdate = false;
+                }
+                Cursor cursor = getContentResolver().query(BluetoothShare.CONTENT_URI, null, null,
+                        null, BluetoothShare._ID);
+
+                if (cursor == null) {
+                    return;
+                }
+
+                cursor.moveToFirst();
+
+                int arrayPos = 0;
+
+                keepService = false;
+                boolean isAfterLast = cursor.isAfterLast();
+
+                int idColumn = cursor.getColumnIndexOrThrow(BluetoothShare._ID);
+                /*
+                 * Walk the cursor and the local array to keep them in sync. The
+                 * key to the algorithm is that the ids are unique and sorted
+                 * both in the cursor and in the array, so that they can be
+                 * processed in order in both sources at the same time: at each
+                 * step, both sources point to the lowest id that hasn't been
+                 * processed from that source, and the algorithm processes the
+                 * lowest id from those two possibilities. At each step: -If the
+                 * array contains an entry that's not in the cursor, remove the
+                 * entry, move to next entry in the array. -If the array
+                 * contains an entry that's in the cursor, nothing to do, move
+                 * to next cursor row and next array entry. -If the cursor
+                 * contains an entry that's not in the array, insert a new entry
+                 * in the array, move to next cursor row and next array entry.
+                 */
+                while (!isAfterLast || arrayPos < mShares.size()) {
+                    if (isAfterLast) {
+                        // We're beyond the end of the cursor but there's still
+                        // some
+                        // stuff in the local array, which can only be junk
+                        if (V) Log.v(TAG, "Array update: trimming " +
+                                mShares.get(arrayPos).mId + " @ " + arrayPos);
+
+                        if (shouldScanFile(arrayPos)) {
+                            scanFile(null, arrayPos);
+                        }
+                        deleteShare(arrayPos); // this advances in the array
+                    } else {
+                        int id = cursor.getInt(idColumn);
+
+                        if (arrayPos == mShares.size()) {
+                            insertShare(cursor, arrayPos);
+                            if (V) Log.v(TAG, "Array update: inserting " + id + " @ " + arrayPos);
+                            if (shouldScanFile(arrayPos) && (!scanFile(cursor, arrayPos))) {
+                                keepService = true;
+                            }
+                            if (visibleNotification(arrayPos)) {
+                                keepService = true;
+                            }
+                            if (needAction(arrayPos)) {
+                                keepService = true;
+                            }
+
+                            ++arrayPos;
+                            cursor.moveToNext();
+                            isAfterLast = cursor.isAfterLast();
+                        } else {
+                            int arrayId = mShares.get(arrayPos).mId;
+
+                            if (arrayId < id) {
+                                if (V) Log.v(TAG, "Array update: removing " + arrayId + " @ "
+                                            + arrayPos);
+                                if (shouldScanFile(arrayPos)) {
+                                    scanFile(null, arrayPos);
+                                }
+                                deleteShare(arrayPos);
+                            } else if (arrayId == id) {
+                                // This cursor row already exists in the stored
+                                // array
+                                updateShare(cursor, arrayPos, userAccepted);
+                                if (shouldScanFile(arrayPos) && (!scanFile(cursor, arrayPos))) {
+                                    keepService = true;
+                                }
+                                if (visibleNotification(arrayPos)) {
+                                    keepService = true;
+                                }
+                                if (needAction(arrayPos)) {
+                                    keepService = true;
+                                }
+
+                                ++arrayPos;
+                                cursor.moveToNext();
+                                isAfterLast = cursor.isAfterLast();
+                            } else {
+                                // This cursor entry didn't exist in the stored
+                                // array
+                                if (V) Log.v(TAG, "Array update: appending " + id + " @ " + arrayPos);
+                                insertShare(cursor, arrayPos);
+
+                                if (shouldScanFile(arrayPos) && (!scanFile(cursor, arrayPos))) {
+                                    keepService = true;
+                                }
+                                if (visibleNotification(arrayPos)) {
+                                    keepService = true;
+                                }
+                                if (needAction(arrayPos)) {
+                                    keepService = true;
+                                }
+                                ++arrayPos;
+                                cursor.moveToNext();
+                                isAfterLast = cursor.isAfterLast();
+                            }
+                        }
+                    }
+                }
+
+                mNotifier.updateNotification();
+
+                cursor.close();
+            }
+        }
+
+    }
+
+    private void insertShare(Cursor cursor, int arrayPos) {
+        BluetoothOppShareInfo info = new BluetoothOppShareInfo(
+                cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID)),
+                cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare.URI)),
+                cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT)),
+                cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare._DATA)),
+                cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare.MIMETYPE)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION)),
+                cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare.DESTINATION)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.VISIBILITY)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.STATUS)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(Constants.MEDIA_SCANNED)) != Constants.MEDIA_SCANNED_NOT_SCANNED);
+
+        if (V) {
+            Log.v(TAG, "Service adding new entry");
+            Log.v(TAG, "ID      : " + info.mId);
+            // Log.v(TAG, "URI     : " + ((info.mUri != null) ? "yes" : "no"));
+            Log.v(TAG, "URI     : " + info.mUri);
+            Log.v(TAG, "HINT    : " + info.mHint);
+            Log.v(TAG, "FILENAME: " + info.mFilename);
+            Log.v(TAG, "MIMETYPE: " + info.mMimetype);
+            Log.v(TAG, "DIRECTION: " + info.mDirection);
+            Log.v(TAG, "DESTINAT: " + info.mDestination);
+            Log.v(TAG, "VISIBILI: " + info.mVisibility);
+            Log.v(TAG, "CONFIRM : " + info.mConfirm);
+            Log.v(TAG, "STATUS  : " + info.mStatus);
+            Log.v(TAG, "TOTAL   : " + info.mTotalBytes);
+            Log.v(TAG, "CURRENT : " + info.mCurrentBytes);
+            Log.v(TAG, "TIMESTAMP : " + info.mTimestamp);
+            Log.v(TAG, "SCANNED : " + info.mMediaScanned);
+        }
+
+        mShares.add(arrayPos, info);
+
+        /* Mark the info as failed if it's in invalid status */
+        if (info.isObsolete()) {
+            Constants.updateShareStatus(this, info.mId, BluetoothShare.STATUS_UNKNOWN_ERROR);
+        }
+        /*
+         * Add info into a batch. The logic is
+         * 1) Only add valid and readyToStart info
+         * 2) If there is no batch, create a batch and insert this transfer into batch,
+         * then run the batch
+         * 3) If there is existing batch and timestamp match, insert transfer into batch
+         * 4) If there is existing batch and timestamp does not match, create a new batch and
+         * put in queue
+         */
+
+        if (info.isReadyToStart()) {
+            if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                /* check if the file exists */
+                InputStream i;
+                try {
+                    i = getContentResolver().openInputStream(Uri.parse(info.mUri));
+                } catch (FileNotFoundException e) {
+                    Log.e(TAG, "Can't open file for OUTBOUND info " + info.mId);
+                    Constants.updateShareStatus(this, info.mId, BluetoothShare.STATUS_BAD_REQUEST);
+                    return;
+                } catch (SecurityException e) {
+                    Log.e(TAG, "Exception:" + e.toString() + " for OUTBOUND info " + info.mId);
+                    Constants.updateShareStatus(this, info.mId, BluetoothShare.STATUS_BAD_REQUEST);
+                    return;
+                }
+
+                try {
+                    i.close();
+                } catch (IOException ex) {
+                    Log.e(TAG, "IO error when close file for OUTBOUND info " + info.mId);
+                    return;
+                }
+            }
+            if (mBatchs.size() == 0) {
+                BluetoothOppBatch newBatch = new BluetoothOppBatch(this, info);
+                newBatch.mId = mBatchId;
+                mBatchId++;
+                mBatchs.add(newBatch);
+                if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                    if (V) Log.v(TAG, "Service create new Batch " + newBatch.mId
+                                + " for OUTBOUND info " + info.mId);
+                    mTransfer = new BluetoothOppTransfer(this, mPowerManager, newBatch);
+                } else if (info.mDirection == BluetoothShare.DIRECTION_INBOUND) {
+                    if (V) Log.v(TAG, "Service create new Batch " + newBatch.mId
+                                + " for INBOUND info " + info.mId);
+                    mServerTransfer = new BluetoothOppTransfer(this, mPowerManager, newBatch,
+                            mServerSession);
+                }
+
+                if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND && mTransfer != null) {
+                    if (V) Log.v(TAG, "Service start transfer new Batch " + newBatch.mId
+                                + " for info " + info.mId);
+                    mTransfer.start();
+                } else if (info.mDirection == BluetoothShare.DIRECTION_INBOUND
+                        && mServerTransfer != null) {
+                    if (V) Log.v(TAG, "Service start server transfer new Batch " + newBatch.mId
+                                + " for info " + info.mId);
+                    mServerTransfer.start();
+                }
+
+            } else {
+                int i = findBatchWithTimeStamp(info.mTimestamp);
+                if (i != -1) {
+                    if (V) Log.v(TAG, "Service add info " + info.mId + " to existing batch "
+                                + mBatchs.get(i).mId);
+                    mBatchs.get(i).addShare(info);
+                } else {
+                    // There is ongoing batch
+                    BluetoothOppBatch newBatch = new BluetoothOppBatch(this, info);
+                    newBatch.mId = mBatchId;
+                    mBatchId++;
+                    mBatchs.add(newBatch);
+                    if (V) Log.v(TAG, "Service add new Batch " + newBatch.mId + " for info " +
+                            info.mId);
+                    if (Constants.USE_TCP_DEBUG && !Constants.USE_TCP_SIMPLE_SERVER) {
+                        // only allow  concurrent serverTransfer in debug mode
+                        if (info.mDirection == BluetoothShare.DIRECTION_INBOUND) {
+                            if (V) Log.v(TAG, "TCP_DEBUG start server transfer new Batch " +
+                                    newBatch.mId + " for info " + info.mId);
+                            mServerTransfer = new BluetoothOppTransfer(this, mPowerManager,
+                                    newBatch, mServerSession);
+                            mServerTransfer.start();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void updateShare(Cursor cursor, int arrayPos, boolean userAccepted) {
+        BluetoothOppShareInfo info = mShares.get(arrayPos);
+        int statusColumn = cursor.getColumnIndexOrThrow(BluetoothShare.STATUS);
+
+        info.mId = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
+        info.mUri = stringFromCursor(info.mUri, cursor, BluetoothShare.URI);
+        info.mHint = stringFromCursor(info.mHint, cursor, BluetoothShare.FILENAME_HINT);
+        info.mFilename = stringFromCursor(info.mFilename, cursor, BluetoothShare._DATA);
+        info.mMimetype = stringFromCursor(info.mMimetype, cursor, BluetoothShare.MIMETYPE);
+        info.mDirection = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.DIRECTION));
+        info.mDestination = stringFromCursor(info.mDestination, cursor, BluetoothShare.DESTINATION);
+        int newVisibility = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.VISIBILITY));
+
+        boolean confirmed = false;
+        int newConfirm = cursor.getInt(cursor
+                .getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION));
+
+        if (info.mVisibility == BluetoothShare.VISIBILITY_VISIBLE
+                && newVisibility != BluetoothShare.VISIBILITY_VISIBLE
+                && (BluetoothShare.isStatusCompleted(info.mStatus) || newConfirm == BluetoothShare.USER_CONFIRMATION_PENDING)) {
+            mNotifier.mNotificationMgr.cancel(info.mId);
+        }
+
+        info.mVisibility = newVisibility;
+
+        if (info.mConfirm == BluetoothShare.USER_CONFIRMATION_PENDING
+                && newConfirm != BluetoothShare.USER_CONFIRMATION_PENDING) {
+            confirmed = true;
+        }
+        info.mConfirm = cursor.getInt(cursor
+                .getColumnIndexOrThrow(BluetoothShare.USER_CONFIRMATION));
+        int newStatus = cursor.getInt(statusColumn);
+
+        if (!BluetoothShare.isStatusCompleted(info.mStatus)
+                && BluetoothShare.isStatusCompleted(newStatus)) {
+            mNotifier.mNotificationMgr.cancel(info.mId);
+        }
+
+        info.mStatus = newStatus;
+        info.mTotalBytes = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES));
+        info.mCurrentBytes = cursor.getInt(cursor
+                .getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES));
+        info.mTimestamp = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
+        info.mMediaScanned = (cursor.getInt(cursor.getColumnIndexOrThrow(Constants.MEDIA_SCANNED)) != Constants.MEDIA_SCANNED_NOT_SCANNED);
+
+        if (confirmed) {
+            if (V) Log.v(TAG, "Service handle info " + info.mId + " confirmed");
+            /* Inbounds transfer get user confirmation, so we start it */
+            int i = findBatchWithTimeStamp(info.mTimestamp);
+            if (i != -1) {
+                BluetoothOppBatch batch = mBatchs.get(i);
+                if (mServerTransfer != null && batch.mId == mServerTransfer.getBatchId()) {
+                    mServerTransfer.setConfirmed();
+                } //TODO need to think about else
+            }
+        }
+        int i = findBatchWithTimeStamp(info.mTimestamp);
+        if (i != -1) {
+            BluetoothOppBatch batch = mBatchs.get(i);
+            if (batch.mStatus == Constants.BATCH_STATUS_FINISHED
+                    || batch.mStatus == Constants.BATCH_STATUS_FAILED) {
+                if (V) Log.v(TAG, "Batch " + batch.mId + " is finished");
+                if (batch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                    if (mTransfer == null) {
+                        Log.e(TAG, "Unexpected error! mTransfer is null");
+                    } else if (batch.mId == mTransfer.getBatchId()) {
+                        mTransfer.stop();
+                    } else {
+                        Log.e(TAG, "Unexpected error! batch id " + batch.mId
+                                + " doesn't match mTransfer id " + mTransfer.getBatchId());
+                    }
+                    mTransfer = null;
+                } else {
+                    if (mServerTransfer == null) {
+                        Log.e(TAG, "Unexpected error! mServerTransfer is null");
+                    } else if (batch.mId == mServerTransfer.getBatchId()) {
+                        mServerTransfer.stop();
+                    } else {
+                        Log.e(TAG, "Unexpected error! batch id " + batch.mId
+                                + " doesn't match mServerTransfer id "
+                                + mServerTransfer.getBatchId());
+                    }
+                    mServerTransfer = null;
+                }
+                removeBatch(batch);
+            }
+        }
+    }
+
+    /**
+     * Removes the local copy of the info about a share.
+     */
+    private void deleteShare(int arrayPos) {
+        BluetoothOppShareInfo info = mShares.get(arrayPos);
+
+        /*
+         * Delete arrayPos from a batch. The logic is
+         * 1) Search existing batch for the info
+         * 2) cancel the batch
+         * 3) If the batch become empty delete the batch
+         */
+        int i = findBatchWithTimeStamp(info.mTimestamp);
+        if (i != -1) {
+            BluetoothOppBatch batch = mBatchs.get(i);
+            if (batch.hasShare(info)) {
+                if (V) Log.v(TAG, "Service cancel batch for share " + info.mId);
+                batch.cancelBatch();
+            }
+            if (batch.isEmpty()) {
+                if (V) Log.v(TAG, "Service remove batch  " + batch.mId);
+                removeBatch(batch);
+            }
+        }
+        mShares.remove(arrayPos);
+    }
+
+    private String stringFromCursor(String old, Cursor cursor, String column) {
+        int index = cursor.getColumnIndexOrThrow(column);
+        if (old == null) {
+            return cursor.getString(index);
+        }
+        if (mNewChars == null) {
+            mNewChars = new CharArrayBuffer(128);
+        }
+        cursor.copyStringToBuffer(index, mNewChars);
+        int length = mNewChars.sizeCopied;
+        if (length != old.length()) {
+            return cursor.getString(index);
+        }
+        if (mOldChars == null || mOldChars.sizeCopied < length) {
+            mOldChars = new CharArrayBuffer(length);
+        }
+        char[] oldArray = mOldChars.data;
+        char[] newArray = mNewChars.data;
+        old.getChars(0, length, oldArray, 0);
+        for (int i = length - 1; i >= 0; --i) {
+            if (oldArray[i] != newArray[i]) {
+                return new String(newArray, 0, length);
+            }
+        }
+        return old;
+    }
+
+    private int findBatchWithTimeStamp(long timestamp) {
+        for (int i = mBatchs.size() - 1; i >= 0; i--) {
+            if (mBatchs.get(i).mTimestamp == timestamp) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private void removeBatch(BluetoothOppBatch batch) {
+        if (V) Log.v(TAG, "Remove batch " + batch.mId);
+        mBatchs.remove(batch);
+        BluetoothOppBatch nextBatch;
+        if (mBatchs.size() > 0) {
+            for (int i = 0; i < mBatchs.size(); i++) {
+                // we have a running batch
+                nextBatch = mBatchs.get(i);
+                if (nextBatch.mStatus == Constants.BATCH_STATUS_RUNNING) {
+                    return;
+                } else {
+                    // just finish a transfer, start pending outbound transfer
+                    if (nextBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                        if (V) Log.v(TAG, "Start pending outbound batch " + nextBatch.mId);
+                        mTransfer = new BluetoothOppTransfer(this, mPowerManager, nextBatch);
+                        mTransfer.start();
+                        return;
+                    } else if (nextBatch.mDirection == BluetoothShare.DIRECTION_INBOUND
+                            && mServerSession != null) {
+                        // have to support pending inbound transfer
+                        // if an outbound transfer and incoming socket happens together
+                        if (V) Log.v(TAG, "Start pending inbound batch " + nextBatch.mId);
+                        mServerTransfer = new BluetoothOppTransfer(this, mPowerManager, nextBatch,
+                                                                   mServerSession);
+                        mServerTransfer.start();
+                        if (nextBatch.getPendingShare().mConfirm ==
+                                BluetoothShare.USER_CONFIRMATION_CONFIRMED) {
+                            mServerTransfer.setConfirmed();
+                        }
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean needAction(int arrayPos) {
+        BluetoothOppShareInfo info = mShares.get(arrayPos);
+        if (BluetoothShare.isStatusCompleted(info.mStatus)) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean visibleNotification(int arrayPos) {
+        BluetoothOppShareInfo info = mShares.get(arrayPos);
+        return info.hasCompletionNotification();
+    }
+
+    private boolean scanFile(Cursor cursor, int arrayPos) {
+        BluetoothOppShareInfo info = mShares.get(arrayPos);
+        synchronized (BluetoothOppService.this) {
+            if (D) Log.d(TAG, "Scanning file " + info.mFilename);
+            if (!mMediaScanInProgress) {
+                mMediaScanInProgress = true;
+                new MediaScannerNotifier(this, info, mHandler);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private boolean shouldScanFile(int arrayPos) {
+        BluetoothOppShareInfo info = mShares.get(arrayPos);
+        return BluetoothShare.isStatusSuccess(info.mStatus)
+                && info.mDirection == BluetoothShare.DIRECTION_INBOUND && !info.mMediaScanned;
+    }
+
+    private void trimDatabase() {
+        Cursor cursor = getContentResolver().query(BluetoothShare.CONTENT_URI, new String[] {
+            BluetoothShare._ID
+        }, BluetoothShare.STATUS + " >= '200'", null, BluetoothShare._ID);
+        if (cursor == null) {
+            // This isn't good - if we can't do basic queries in our database,
+            // nothing's gonna work
+            Log.e(TAG, "null cursor in trimDatabase");
+            return;
+        }
+        if (cursor.moveToFirst()) {
+            int numDelete = cursor.getCount() - Constants.MAX_RECORDS_IN_DATABASE;
+            int columnId = cursor.getColumnIndexOrThrow(BluetoothShare._ID);
+            while (numDelete > 0) {
+                getContentResolver().delete(
+                        ContentUris.withAppendedId(BluetoothShare.CONTENT_URI, cursor
+                                .getLong(columnId)), null, null);
+                if (!cursor.moveToNext()) {
+                    break;
+                }
+                numDelete--;
+            }
+        }
+        cursor.close();
+    }
+
+    private static class MediaScannerNotifier implements MediaScannerConnectionClient {
+
+        private MediaScannerConnection mConnection;
+
+        private BluetoothOppShareInfo mInfo;
+
+        private Context mContext;
+
+        private Handler mCallback;
+
+        public MediaScannerNotifier(Context context, BluetoothOppShareInfo info, Handler handler) {
+            mContext = context;
+            mInfo = info;
+            mCallback = handler;
+            mConnection = new MediaScannerConnection(mContext, this);
+            if (V) Log.v(TAG, "Connecting to MediaScannerConnection ");
+            mConnection.connect();
+        }
+
+        public void onMediaScannerConnected() {
+            if (V) Log.v(TAG, "MediaScannerConnection onMediaScannerConnected");
+            mConnection.scanFile(mInfo.mFilename, mInfo.mMimetype);
+        }
+
+        public void onScanCompleted(String path, Uri uri) {
+            try {
+                if (V) {
+                    Log.v(TAG, "MediaScannerConnection onScanCompleted");
+                    Log.v(TAG, "MediaScannerConnection path is " + path);
+                    Log.v(TAG, "MediaScannerConnection Uri is " + uri);
+                }
+                if (uri != null) {
+                    Message msg = Message.obtain();
+                    msg.setTarget(mCallback);
+                    msg.what = MEDIA_SCANNED;
+                    msg.arg1 = mInfo.mId;
+                    msg.obj = uri;
+                    msg.sendToTarget();
+                } else {
+                    Message msg = Message.obtain();
+                    msg.setTarget(mCallback);
+                    msg.what = MEDIA_SCANNED_FAILED;
+                    msg.arg1 = mInfo.mId;
+                    msg.sendToTarget();
+                }
+            } catch (Exception ex) {
+                Log.v(TAG, "!!!MediaScannerConnection exception: " + ex);
+            } finally {
+                if (V) Log.v(TAG, "MediaScannerConnection disconnect");
+                mConnection.disconnect();
+            }
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppShareInfo.java b/src/com/android/bluetooth/opp/BluetoothOppShareInfo.java
new file mode 100644
index 0000000..da57bd2
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppShareInfo.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+/**
+ * This class stores information about a single OBEX share, e.g. one object
+ * send/receive to a destination address.
+ */
+public class BluetoothOppShareInfo {
+
+    public int mId;
+
+    public String mUri;
+
+    public String mHint;
+
+    public String mFilename;
+
+    public String mMimetype;
+
+    public int mDirection;
+
+    public String mDestination;
+
+    public int mVisibility;
+
+    public int mConfirm;
+
+    public int mStatus;
+
+    public int mTotalBytes;
+
+    public int mCurrentBytes;
+
+    public long mTimestamp;
+
+    public boolean mMediaScanned;
+
+    public BluetoothOppShareInfo(int id, String uri, String hint, String filename, String mimetype,
+            int direction, String destination, int visibility, int confirm, int status,
+            int totalBytes, int currentBytes, int timestamp, boolean mediaScanned) {
+        mId = id;
+        mUri = uri;
+        mHint = hint;
+        mFilename = filename;
+        mMimetype = mimetype;
+        mDirection = direction;
+        mDestination = destination;
+        mVisibility = visibility;
+        mConfirm = confirm;
+        mStatus = status;
+        mTotalBytes = totalBytes;
+        mCurrentBytes = currentBytes;
+        mTimestamp = timestamp;
+        mMediaScanned = mediaScanned;
+    }
+
+    public boolean isReadyToStart() {
+        /*
+         * For outbound 1. status is pending.
+         * For inbound share 1. status is pending
+         */
+        if (mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+            if (mStatus == BluetoothShare.STATUS_PENDING && mUri != null) {
+                return true;
+            }
+        } else if (mDirection == BluetoothShare.DIRECTION_INBOUND) {
+            if (mStatus == BluetoothShare.STATUS_PENDING) {
+                //&& mConfirm != BluetoothShare.USER_CONFIRMATION_PENDING) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean hasCompletionNotification() {
+        if (!BluetoothShare.isStatusCompleted(mStatus)) {
+            return false;
+        }
+        if (mVisibility == BluetoothShare.VISIBILITY_VISIBLE) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if a ShareInfo is invalid because of previous error
+     */
+    public boolean isObsolete() {
+        if (BluetoothShare.STATUS_RUNNING == mStatus) {
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppTransfer.java b/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
new file mode 100644
index 0000000..d892e70
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
@@ -0,0 +1,797 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import javax.obex.ObexTransport;
+
+import android.app.NotificationManager;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.PowerManager;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/**
+ * This class run an actual Opp transfer session (from connect target device to
+ * disconnect)
+ */
+public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatchListener {
+    private static final String TAG = "BtOppTransfer";
+
+    private static final boolean D = Constants.DEBUG;
+
+    private static final boolean V = Constants.VERBOSE;
+
+    public static final int RFCOMM_ERROR = 10;
+
+    public static final int RFCOMM_CONNECTED = 11;
+
+    public static final int SDP_RESULT = 12;
+
+    private static final int CONNECT_WAIT_TIMEOUT = 45000;
+
+    private static final int CONNECT_RETRY_TIME = 100;
+
+    private static final short OPUSH_UUID16 = 0x1105;
+
+    private Context mContext;
+
+    private BluetoothAdapter mAdapter;
+
+    private BluetoothOppBatch mBatch;
+
+    private BluetoothOppObexSession mSession;
+
+    private BluetoothOppShareInfo mCurrentShare;
+
+    private ObexTransport mTransport;
+
+    private HandlerThread mHandlerThread;
+
+    private EventHandler mSessionHandler;
+
+    /*
+     * TODO check if we need PowerManager here
+     */
+    private PowerManager mPowerManager;
+
+    private long mTimestamp;
+
+    public BluetoothOppTransfer(Context context, PowerManager powerManager,
+            BluetoothOppBatch batch, BluetoothOppObexSession session) {
+
+        mContext = context;
+        mPowerManager = powerManager;
+        mBatch = batch;
+        mSession = session;
+
+        mBatch.registerListern(this);
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+    }
+
+    public BluetoothOppTransfer(Context context, PowerManager powerManager, BluetoothOppBatch batch) {
+        this(context, powerManager, batch, null);
+    }
+
+    public int getBatchId() {
+        return mBatch.mId;
+    }
+
+    /*
+     * Receives events from mConnectThread & mSession back in the main thread.
+     */
+    private class EventHandler extends Handler {
+        public EventHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SDP_RESULT:
+                    if (V) Log.v(TAG, "SDP request returned " + msg.arg1 + " (" +
+                            (System.currentTimeMillis() - mTimestamp + " ms)"));
+                    if (!((BluetoothDevice)msg.obj).equals(mBatch.mDestination)) {
+                        return;
+                    }
+                    try {
+                        mContext.unregisterReceiver(mReceiver);
+                    } catch (IllegalArgumentException e) {
+                        // ignore
+                    }
+                    if (msg.arg1 > 0) {
+                        mConnectThread = new SocketConnectThread(mBatch.mDestination, msg.arg1);
+                        mConnectThread.start();
+                    } else {
+                        /* SDP query fail case */
+                        Log.e(TAG, "SDP query failed!");
+                        markBatchFailed(BluetoothShare.STATUS_CONNECTION_ERROR);
+                        mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
+                    }
+
+                    break;
+
+                /*
+                 * RFCOMM connect fail is for outbound share only! Mark batch
+                 * failed, and all shares in batch failed
+                 */
+                case RFCOMM_ERROR:
+                    if (V) Log.v(TAG, "receive RFCOMM_ERROR msg");
+                    mConnectThread = null;
+                    markBatchFailed(BluetoothShare.STATUS_CONNECTION_ERROR);
+                    mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
+
+                    break;
+                /*
+                 * RFCOMM connected is for outbound share only! Create
+                 * BluetoothOppObexClientSession and start it
+                 */
+                case RFCOMM_CONNECTED:
+                    if (V) Log.v(TAG, "Transfer receive RFCOMM_CONNECTED msg");
+                    mConnectThread = null;
+                    mTransport = (ObexTransport)msg.obj;
+                    startObexSession();
+
+                    break;
+                /*
+                 * Put next share if available,or finish the transfer.
+                 * For outbound session, call session.addShare() to send next file,
+                 * or call session.stop().
+                 * For inbounds session, do nothing. If there is next file to receive,it
+                 * will be notified through onShareAdded()
+                 */
+                case BluetoothOppObexSession.MSG_SHARE_COMPLETE:
+                    BluetoothOppShareInfo info = (BluetoothOppShareInfo)msg.obj;
+                    if (V) Log.v(TAG, "receive MSG_SHARE_COMPLETE for info " + info.mId);
+                    if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                        mCurrentShare = mBatch.getPendingShare();
+
+                        if (mCurrentShare != null) {
+                            /* we have additional share to process */
+                            if (V) Log.v(TAG, "continue session for info " + mCurrentShare.mId +
+                                    " from batch " + mBatch.mId);
+                            processCurrentShare();
+                        } else {
+                            /* for outbound transfer, all shares are processed */
+                            if (V) Log.v(TAG, "Batch " + mBatch.mId + " is done");
+                            mSession.stop();
+                        }
+                    }
+                    break;
+                /*
+                 * Handle session completed status Set batch status to
+                 * finished
+                 */
+                case BluetoothOppObexSession.MSG_SESSION_COMPLETE:
+                    BluetoothOppShareInfo info1 = (BluetoothOppShareInfo)msg.obj;
+                    if (V) Log.v(TAG, "receive MSG_SESSION_COMPLETE for batch " + mBatch.mId);
+                    mBatch.mStatus = Constants.BATCH_STATUS_FINISHED;
+                    /*
+                     * trigger content provider again to know batch status change
+                     */
+                    tickShareStatus(info1);
+                    break;
+
+                /* Handle the error state of an Obex session */
+                case BluetoothOppObexSession.MSG_SESSION_ERROR:
+                    if (V) Log.v(TAG, "receive MSG_SESSION_ERROR for batch " + mBatch.mId);
+                    BluetoothOppShareInfo info2 = (BluetoothOppShareInfo)msg.obj;
+                    mSession.stop();
+                    mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
+                    markBatchFailed(info2.mStatus);
+                    tickShareStatus(mCurrentShare);
+                    break;
+
+                case BluetoothOppObexSession.MSG_SHARE_INTERRUPTED:
+                    if (V) Log.v(TAG, "receive MSG_SHARE_INTERRUPTED for batch " + mBatch.mId);
+                    BluetoothOppShareInfo info3 = (BluetoothOppShareInfo)msg.obj;
+                    if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                        try {
+                            if (mTransport == null) {
+                                Log.v(TAG, "receive MSG_SHARE_INTERRUPTED but mTransport = null");
+                            } else {
+                                mTransport.close();
+                            }
+                        } catch (IOException e) {
+                            Log.e(TAG, "failed to close mTransport");
+                        }
+                        if (V) Log.v(TAG, "mTransport closed ");
+                        mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
+                        if (info3 != null) {
+                            markBatchFailed(info3.mStatus);
+                        } else {
+                            markBatchFailed();
+                        }
+                        tickShareStatus(mCurrentShare);
+                    }
+                    break;
+
+                case BluetoothOppObexSession.MSG_CONNECT_TIMEOUT:
+                    if (V) Log.v(TAG, "receive MSG_CONNECT_TIMEOUT for batch " + mBatch.mId);
+                    /* for outbound transfer, the block point is BluetoothSocket.write()
+                     * The only way to unblock is to tear down lower transport
+                     * */
+                    if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                        try {
+                            if (mTransport == null) {
+                                Log.v(TAG, "receive MSG_SHARE_INTERRUPTED but mTransport = null");
+                            } else {
+                                mTransport.close();
+                            }
+                        } catch (IOException e) {
+                            Log.e(TAG, "failed to close mTransport");
+                        }
+                        if (V) Log.v(TAG, "mTransport closed ");
+                    } else {
+                        /*
+                         * For inbound transfer, the block point is waiting for
+                         * user confirmation we can interrupt it nicely
+                         */
+
+                        // Remove incoming file confirm notification
+                        NotificationManager nm = (NotificationManager)mContext
+                                .getSystemService(Context.NOTIFICATION_SERVICE);
+                        nm.cancel(mCurrentShare.mId);
+                        // Send intent to UI for timeout handling
+                        Intent in = new Intent(BluetoothShare.USER_CONFIRMATION_TIMEOUT_ACTION);
+                        mContext.sendBroadcast(in);
+
+                        markShareTimeout(mCurrentShare);
+                    }
+                    break;
+            }
+        }
+    }
+
+    private void markShareTimeout(BluetoothOppShareInfo share) {
+        Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + share.mId);
+        ContentValues updateValues = new ContentValues();
+        updateValues
+                .put(BluetoothShare.USER_CONFIRMATION, BluetoothShare.USER_CONFIRMATION_TIMEOUT);
+        mContext.getContentResolver().update(contentUri, updateValues, null, null);
+    }
+
+    private void markBatchFailed(int failReason) {
+        synchronized (this) {
+            try {
+                wait(1000);
+            } catch (InterruptedException e) {
+                if (V) Log.v(TAG, "Interrupted waiting for markBatchFailed");
+            }
+        }
+
+        if (D) Log.d(TAG, "Mark all ShareInfo in the batch as failed");
+        if (mCurrentShare != null) {
+            if (V) Log.v(TAG, "Current share has status " + mCurrentShare.mStatus);
+            if (BluetoothShare.isStatusError(mCurrentShare.mStatus)) {
+                failReason = mCurrentShare.mStatus;
+            }
+            if (mCurrentShare.mDirection == BluetoothShare.DIRECTION_INBOUND
+                    && mCurrentShare.mFilename != null) {
+                new File(mCurrentShare.mFilename).delete();
+            }
+        }
+
+        BluetoothOppShareInfo info = mBatch.getPendingShare();
+        while (info != null) {
+            if (info.mStatus < 200) {
+                info.mStatus = failReason;
+                Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + info.mId);
+                ContentValues updateValues = new ContentValues();
+                updateValues.put(BluetoothShare.STATUS, info.mStatus);
+                /* Update un-processed outbound transfer to show some info */
+                if (info.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                    BluetoothOppSendFileInfo fileInfo = null;
+                    fileInfo = BluetoothOppSendFileInfo.generateFileInfo(mContext, info.mUri,
+                            info.mMimetype, info.mDestination);
+                    if (fileInfo.mFileName != null) {
+                        updateValues.put(BluetoothShare.FILENAME_HINT, fileInfo.mFileName);
+                        updateValues.put(BluetoothShare.TOTAL_BYTES, fileInfo.mLength);
+                        updateValues.put(BluetoothShare.MIMETYPE, fileInfo.mMimetype);
+                    }
+                } else {
+                    if (info.mStatus < 200 && info.mFilename != null) {
+                        new File(info.mFilename).delete();
+                    }
+                }
+                mContext.getContentResolver().update(contentUri, updateValues, null, null);
+                Constants.sendIntentIfCompleted(mContext, contentUri, info.mStatus);
+            }
+            info = mBatch.getPendingShare();
+        }
+
+    }
+
+    private void markBatchFailed() {
+        markBatchFailed(BluetoothShare.STATUS_UNKNOWN_ERROR);
+    }
+
+    /*
+     * NOTE
+     * For outbound transfer
+     * 1) Check Bluetooth status
+     * 2) Start handler thread
+     * 3) new a thread to connect to target device
+     * 3.1) Try a few times to do SDP query for target device OPUSH channel
+     * 3.2) Try a few seconds to connect to target socket
+     * 4) After BluetoothSocket is connected,create an instance of RfcommTransport
+     * 5) Create an instance of BluetoothOppClientSession
+     * 6) Start the session and process the first share in batch
+     * For inbound transfer
+     * The transfer already has session and transport setup, just start it
+     * 1) Check Bluetooth status
+     * 2) Start handler thread
+     * 3) Start the session and process the first share in batch
+     */
+    /**
+     * Start the transfer
+     */
+    public void start() {
+        /* check Bluetooth enable status */
+        /*
+         * normally it's impossible to reach here if BT is disabled. Just check
+         * for safety
+         */
+        if (!mAdapter.isEnabled()) {
+            Log.e(TAG, "Can't start transfer when Bluetooth is disabled for " + mBatch.mId);
+            markBatchFailed();
+            mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
+            return;
+        }
+
+        if (mHandlerThread == null) {
+            if (V) Log.v(TAG, "Create handler thread for batch " + mBatch.mId);
+            mHandlerThread = new HandlerThread("BtOpp Transfer Handler",
+                    Process.THREAD_PRIORITY_BACKGROUND);
+            mHandlerThread.start();
+            mSessionHandler = new EventHandler(mHandlerThread.getLooper());
+
+            if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+                /* for outbound transfer, we do connect first */
+                startConnectSession();
+            } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {
+                /*
+                 * for inbound transfer, it's already connected, so we start
+                 * OBEX session directly
+                 */
+                startObexSession();
+            }
+        }
+    }
+
+    /**
+     * Stop the transfer
+     */
+    public void stop() {
+        if (V) Log.v(TAG, "stop");
+        if (mConnectThread != null) {
+            try {
+                mConnectThread.interrupt();
+                if (V) Log.v(TAG, "waiting for connect thread to terminate");
+                mConnectThread.join();
+            } catch (InterruptedException e) {
+                if (V) Log.v(TAG, "Interrupted waiting for connect thread to join");
+            }
+            mConnectThread = null;
+        }
+        if (mSession != null) {
+            if (V) Log.v(TAG, "Stop mSession");
+            mSession.stop();
+        }
+        if (mHandlerThread != null) {
+            mHandlerThread.getLooper().quit();
+            mHandlerThread.interrupt();
+            mHandlerThread = null;
+        }
+    }
+
+    private void startObexSession() {
+
+        mBatch.mStatus = Constants.BATCH_STATUS_RUNNING;
+
+        mCurrentShare = mBatch.getPendingShare();
+        if (mCurrentShare == null) {
+            /*
+             * TODO catch this error
+             */
+            Log.e(TAG, "Unexpected error happened !");
+            return;
+        }
+        if (V) Log.v(TAG, "Start session for info " + mCurrentShare.mId + " for batch " +
+                mBatch.mId);
+
+        if (mBatch.mDirection == BluetoothShare.DIRECTION_OUTBOUND) {
+            if (V) Log.v(TAG, "Create Client session with transport " + mTransport.toString());
+            mSession = new BluetoothOppObexClientSession(mContext, mTransport);
+        } else if (mBatch.mDirection == BluetoothShare.DIRECTION_INBOUND) {
+            /*
+             * For inbounds transfer, a server session should already exists
+             * before BluetoothOppTransfer is initialized. We should pass in a
+             * mSession instance.
+             */
+            if (mSession == null) {
+                /** set current share as error */
+                Log.e(TAG, "Unexpected error happened !");
+                markBatchFailed();
+                mBatch.mStatus = Constants.BATCH_STATUS_FAILED;
+                return;
+            }
+            if (V) Log.v(TAG, "Transfer has Server session" + mSession.toString());
+        }
+
+        mSession.start(mSessionHandler);
+        processCurrentShare();
+    }
+
+    private void processCurrentShare() {
+        /* This transfer need user confirm */
+        if (V) Log.v(TAG, "processCurrentShare" + mCurrentShare.mId);
+        mSession.addShare(mCurrentShare);
+    }
+
+    /**
+     * Set transfer confirmed status. It should only be called for inbound
+     * transfer
+     */
+    public void setConfirmed() {
+        /* unblock server session */
+        final Thread notifyThread = new Thread("Server Unblock thread") {
+            public void run() {
+                synchronized (mSession) {
+                    mSession.unblock();
+                    mSession.notify();
+                }
+            }
+        };
+        if (V) Log.v(TAG, "setConfirmed to unblock mSession" + mSession.toString());
+        notifyThread.start();
+    }
+
+    private void startConnectSession() {
+
+        if (Constants.USE_TCP_DEBUG) {
+            mConnectThread = new SocketConnectThread("localhost", Constants.TCP_DEBUG_PORT, 0);
+            mConnectThread.start();
+        } else {
+            int channel = BluetoothOppPreference.getInstance(mContext).getChannel(
+                    mBatch.mDestination, OPUSH_UUID16);
+            if (channel != -1) {
+                if (D) Log.d(TAG, "Get OPUSH channel " + channel + " from cache for " +
+                        mBatch.mDestination);
+                mTimestamp = System.currentTimeMillis();
+                mSessionHandler.obtainMessage(SDP_RESULT, channel, -1, mBatch.mDestination)
+                        .sendToTarget();
+            } else {
+                doOpushSdp();
+            }
+        }
+    }
+
+    private void doOpushSdp() {
+        if (V) Log.v(TAG, "Do Opush SDP request for address " + mBatch.mDestination);
+
+        mTimestamp = System.currentTimeMillis();
+
+        int channel;
+        channel = mBatch.mDestination.getServiceChannel(BluetoothUuid.ObexObjectPush);
+        if (channel != -1) {
+            if (D) Log.d(TAG, "Get OPUSH channel " + channel + " from SDP for "
+                    + mBatch.mDestination);
+
+            mSessionHandler.obtainMessage(SDP_RESULT, channel, -1, mBatch.mDestination)
+                    .sendToTarget();
+            return;
+
+        } else {
+            if (V) Log.v(TAG, "Remote Service channel not in cache");
+
+            if (!mBatch.mDestination.fetchUuidsWithSdp()) {
+                Log.e(TAG, "Start SDP query failed");
+            } else {
+                // we expect framework send us Intent ACTION_UUID. otherwise we will fail
+                if (V) Log.v(TAG, "Start new SDP, wait for result");
+                IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_UUID);
+                mContext.registerReceiver(mReceiver, intentFilter);
+                return;
+            }
+        }
+        Message msg = mSessionHandler.obtainMessage(SDP_RESULT, channel, -1, mBatch.mDestination);
+        mSessionHandler.sendMessageDelayed(msg, 2000);
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(BluetoothDevice.ACTION_UUID)) {
+                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                if (V) Log.v(TAG, "ACTION_UUID for device " + device);
+                if (device.equals(mBatch.mDestination)) {
+                    int channel = -1;
+                    Parcelable[] uuid = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
+                    if (uuid != null) {
+                        ParcelUuid[] uuids = new ParcelUuid[uuid.length];
+                        for (int i = 0; i < uuid.length; i++) {
+                            uuids[i] = (ParcelUuid)uuid[i];
+                        }
+                        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
+                            if (V) Log.v(TAG, "SDP get OPP result for device " + device);
+                            channel = mBatch.mDestination
+                                    .getServiceChannel(BluetoothUuid.ObexObjectPush);
+                        }
+                    }
+                    mSessionHandler.obtainMessage(SDP_RESULT, channel, -1, mBatch.mDestination)
+                            .sendToTarget();
+                }
+            }
+        }
+    };
+
+    private SocketConnectThread mConnectThread;
+
+    private class SocketConnectThread extends Thread {
+        private final String host;
+
+        private final BluetoothDevice device;
+
+        private final int channel;
+
+        private boolean isConnected;
+
+        private long timestamp;
+
+        private BluetoothSocket btSocket = null;
+
+        /* create a TCP socket */
+        public SocketConnectThread(String host, int port, int dummy) {
+            super("Socket Connect Thread");
+            this.host = host;
+            this.channel = port;
+            this.device = null;
+            isConnected = false;
+        }
+
+        /* create a Rfcomm Socket */
+        public SocketConnectThread(BluetoothDevice device, int channel) {
+            super("Socket Connect Thread");
+            this.device = device;
+            this.host = null;
+            this.channel = channel;
+            isConnected = false;
+        }
+
+        public void interrupt() {
+            if (!Constants.USE_TCP_DEBUG) {
+                if (btSocket != null) {
+                    try {
+                        btSocket.close();
+                    } catch (IOException e) {
+                        Log.v(TAG, "Error when close socket");
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+
+            timestamp = System.currentTimeMillis();
+
+            if (Constants.USE_TCP_DEBUG) {
+                /* Use TCP socket to connect */
+                Socket s = new Socket();
+
+                // Try to connect for 50 seconds
+                int result = 0;
+                for (int i = 0; i < CONNECT_RETRY_TIME && result == 0; i++) {
+                    try {
+                        s.connect(new InetSocketAddress(host, channel), CONNECT_WAIT_TIMEOUT);
+                    } catch (UnknownHostException e) {
+                        Log.e(TAG, "TCP socket connect unknown host ");
+                    } catch (IOException e) {
+                        Log.e(TAG, "TCP socket connect failed ");
+                    }
+                    if (s.isConnected()) {
+                        if (D) Log.d(TAG, "TCP socket connected ");
+                        isConnected = true;
+                        break;
+                    }
+                    if (isInterrupted()) {
+                        Log.e(TAG, "TCP socket connect interrupted ");
+                        markConnectionFailed(s);
+                        return;
+                    }
+                }
+                if (!isConnected) {
+                    Log.e(TAG, "TCP socket connect failed after 20 seconds");
+                    markConnectionFailed(s);
+                    return;
+                }
+
+                if (V) Log.v(TAG, "TCP Socket connection attempt took " +
+                        (System.currentTimeMillis() - timestamp) + " ms");
+
+                TestTcpTransport transport;
+                transport = new TestTcpTransport(s);
+
+                if (isInterrupted()) {
+                    isConnected = false;
+                    markConnectionFailed(s);
+                    transport = null;
+                    return;
+                }
+                if (!isConnected) {
+                    transport = null;
+                    Log.e(TAG, "TCP connect session error: ");
+                    markConnectionFailed(s);
+                    return;
+                } else {
+                    if (D) Log.d(TAG, "Send transport message " + transport.toString());
+                    mSessionHandler.obtainMessage(RFCOMM_CONNECTED, transport).sendToTarget();
+                }
+            } else {
+
+                /* Use BluetoothSocket to connect */
+
+                try {
+                    btSocket = device.createInsecureRfcommSocket(channel);
+                } catch (IOException e1) {
+                    Log.e(TAG, "Rfcomm socket create error");
+                    markConnectionFailed(btSocket);
+                    return;
+                }
+                try {
+                    btSocket.connect();
+
+                    if (V) Log.v(TAG, "Rfcomm socket connection attempt took " +
+                            (System.currentTimeMillis() - timestamp) + " ms");
+                    BluetoothOppRfcommTransport transport;
+                    transport = new BluetoothOppRfcommTransport(btSocket);
+
+                    BluetoothOppPreference.getInstance(mContext).setChannel(device, OPUSH_UUID16,
+                            channel);
+                    BluetoothOppPreference.getInstance(mContext).setName(device, device.getName());
+
+                    if (V) Log.v(TAG, "Send transport message " + transport.toString());
+
+                    mSessionHandler.obtainMessage(RFCOMM_CONNECTED, transport).sendToTarget();
+                } catch (IOException e) {
+                    Log.e(TAG, "Rfcomm socket connect exception ");
+                    BluetoothOppPreference.getInstance(mContext)
+                            .removeChannel(device, OPUSH_UUID16);
+                    markConnectionFailed(btSocket);
+                    return;
+                }
+            }
+        }
+
+        private void markConnectionFailed(Socket s) {
+            try {
+                s.close();
+            } catch (IOException e) {
+                Log.e(TAG, "TCP socket close error");
+            }
+            mSessionHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
+        }
+
+        private void markConnectionFailed(BluetoothSocket s) {
+            try {
+                s.close();
+            } catch (IOException e) {
+                if (V) Log.e(TAG, "Error when close socket");
+            }
+            mSessionHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
+            return;
+        }
+    };
+
+    /* update a trivial field of a share to notify Provider the batch status change */
+    private void tickShareStatus(BluetoothOppShareInfo share) {
+        Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + share.mId);
+        ContentValues updateValues = new ContentValues();
+        updateValues.put(BluetoothShare.DIRECTION, share.mDirection);
+        mContext.getContentResolver().update(contentUri, updateValues, null, null);
+    }
+
+    /*
+     * Note: For outbound transfer We don't implement this method now. If later
+     * we want to support merging a later added share into an existing session,
+     * we could implement here For inbounds transfer add share means it's
+     * multiple receive in the same session, we should handle it to fill it into
+     * mSession
+     */
+    /**
+     * Process when a share is added to current transfer
+     */
+    public void onShareAdded(int id) {
+        BluetoothOppShareInfo info = mBatch.getPendingShare();
+        if (info.mDirection == BluetoothShare.DIRECTION_INBOUND) {
+            mCurrentShare = mBatch.getPendingShare();
+            /*
+             * TODO what if it's not auto confirmed?
+             */
+            if (mCurrentShare != null
+                    && mCurrentShare.mConfirm == BluetoothShare.USER_CONFIRMATION_AUTO_CONFIRMED) {
+                /* have additional auto confirmed share to process */
+                if (V) Log.v(TAG, "Transfer continue session for info " + mCurrentShare.mId +
+                        " from batch " + mBatch.mId);
+                processCurrentShare();
+                setConfirmed();
+            }
+        }
+    }
+
+    /*
+     * NOTE We don't implement this method now. Now delete a single share from
+     * the batch means the whole batch should be canceled. If later we want to
+     * support single cancel, we could implement here For outbound transfer, if
+     * the share is currently in transfer, cancel it For inbounds transfer,
+     * delete share means the current receiving file should be canceled.
+     */
+    /**
+     * Process when a share is deleted from current transfer
+     */
+    public void onShareDeleted(int id) {
+
+    }
+
+    /**
+     * Process when current transfer is canceled
+     */
+    public void onBatchCanceled() {
+        if (V) Log.v(TAG, "Transfer on Batch canceled");
+
+        this.stop();
+        mBatch.mStatus = Constants.BATCH_STATUS_FINISHED;
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppTransferActivity.java b/src/com/android/bluetooth/opp/BluetoothOppTransferActivity.java
new file mode 100644
index 0000000..e98771f
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppTransferActivity.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.database.ContentObserver;
+import android.widget.ProgressBar;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import android.app.NotificationManager;
+import android.text.format.Formatter;
+
+/**
+ * Handle all transfer related dialogs: -Ongoing transfer -Receiving one file
+ * dialog -Sending one file dialog -sending multiple files dialog -Complete
+ * transfer -receive -receive success, will trigger corresponding handler
+ * -receive fail dialog -send -send success dialog -send fail dialog -Other
+ * dialogs - - DIALOG_RECEIVE_ONGOING will transition to
+ * DIALOG_RECEIVE_COMPLETE_SUCCESS or DIALOG_RECEIVE_COMPLETE_FAIL
+ * DIALOG_SEND_ONGOING will transition to DIALOG_SEND_COMPLETE_SUCCESS or
+ * DIALOG_SEND_COMPLETE_FAIL
+ */
+public class BluetoothOppTransferActivity extends AlertActivity implements
+        DialogInterface.OnClickListener {
+    private static final String TAG = "BluetoothOppTransferActivity";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    private Uri mUri;
+
+    // ongoing transfer-0 complete transfer-1
+    boolean mIsComplete;
+
+    private BluetoothOppTransferInfo mTransInfo;
+
+    private ProgressBar mProgressTransfer;
+
+    private TextView mPercentView;
+
+    private AlertController.AlertParams mPara;
+
+    private View mView = null;
+
+    private TextView mLine1View, mLine2View, mLine3View, mLine5View;
+
+    private int mWhichDialog;
+
+    private BluetoothAdapter mAdapter;
+
+    // Dialogs definition:
+    // Receive progress dialog
+    public static final int DIALOG_RECEIVE_ONGOING = 0;
+
+    // Receive complete and success dialog
+    public static final int DIALOG_RECEIVE_COMPLETE_SUCCESS = 1;
+
+    // Receive complete and fail dialog: will display some fail reason
+    public static final int DIALOG_RECEIVE_COMPLETE_FAIL = 2;
+
+    // Send progress dialog
+    public static final int DIALOG_SEND_ONGOING = 3;
+
+    // Send complete and success dialog
+    public static final int DIALOG_SEND_COMPLETE_SUCCESS = 4;
+
+    // Send complete and fail dialog: will let user retry
+    public static final int DIALOG_SEND_COMPLETE_FAIL = 5;
+
+    /** Observer to get notified when the content observer's data changes */
+    private BluetoothTransferContentObserver mObserver;
+
+    private class BluetoothTransferContentObserver extends ContentObserver {
+        public BluetoothTransferContentObserver() {
+            super(new Handler());
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (V) Log.v(TAG, "received db changes.");
+            updateProgressbar();
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = getIntent();
+        mUri = intent.getData();
+
+        mTransInfo = new BluetoothOppTransferInfo();
+        mTransInfo = BluetoothOppUtility.queryRecord(this, mUri);
+        if (mTransInfo == null) {
+            if (V) Log.e(TAG, "Error: Can not get data from db");
+            finish();
+            return;
+        }
+
+        mIsComplete = BluetoothShare.isStatusCompleted(mTransInfo.mStatus);
+
+        displayWhichDialog();
+
+        // update progress bar for ongoing transfer
+        if (!mIsComplete) {
+            mObserver = new BluetoothTransferContentObserver();
+            getContentResolver().registerContentObserver(BluetoothShare.CONTENT_URI, true,
+                    mObserver);
+        }
+
+        if (mWhichDialog != DIALOG_SEND_ONGOING && mWhichDialog != DIALOG_RECEIVE_ONGOING) {
+            // set this record to INVISIBLE
+            BluetoothOppUtility.updateVisibilityToHidden(this, mUri);
+        }
+
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        // Set up the "dialog"
+        setUpDialog();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (D) Log.d(TAG, "onDestroy()");
+
+        if (mObserver != null) {
+            getContentResolver().unregisterContentObserver(mObserver);
+        }
+        super.onDestroy();
+    }
+
+    private void displayWhichDialog() {
+        int direction = mTransInfo.mDirection;
+        boolean isSuccess = BluetoothShare.isStatusSuccess(mTransInfo.mStatus);
+        boolean isComplete = BluetoothShare.isStatusCompleted(mTransInfo.mStatus);
+
+        if (direction == BluetoothShare.DIRECTION_INBOUND) {
+            if (isComplete == true) {
+                if (isSuccess == true) {
+                    // should not go here
+                    mWhichDialog = DIALOG_RECEIVE_COMPLETE_SUCCESS;
+                } else if (isSuccess == false) {
+                    mWhichDialog = DIALOG_RECEIVE_COMPLETE_FAIL;
+                }
+            } else if (isComplete == false) {
+                mWhichDialog = DIALOG_RECEIVE_ONGOING;
+            }
+        } else if (direction == BluetoothShare.DIRECTION_OUTBOUND) {
+            if (isComplete == true) {
+                if (isSuccess == true) {
+                    mWhichDialog = DIALOG_SEND_COMPLETE_SUCCESS;
+
+                } else if (isSuccess == false) {
+                    mWhichDialog = DIALOG_SEND_COMPLETE_FAIL;
+                }
+            } else if (isComplete == false) {
+                mWhichDialog = DIALOG_SEND_ONGOING;
+            }
+        }
+
+        if (V) Log.v(TAG, " WhichDialog/dir/isComplete/failOrSuccess" + mWhichDialog + direction
+                    + isComplete + isSuccess);
+    }
+
+    private void setUpDialog() {
+        // final AlertController.AlertParams p = mAlertParams;
+        mPara = mAlertParams;
+        mPara.mIconId = android.R.drawable.ic_dialog_info;
+        mPara.mTitle = getString(R.string.download_title);
+
+        if ((mWhichDialog == DIALOG_RECEIVE_ONGOING) || (mWhichDialog == DIALOG_SEND_ONGOING)) {
+            mPara.mPositiveButtonText = getString(R.string.download_ok);
+            mPara.mPositiveButtonListener = this;
+            mPara.mNegativeButtonText = getString(R.string.download_cancel);
+            mPara.mNegativeButtonListener = this;
+        } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) {
+            mPara.mPositiveButtonText = getString(R.string.download_succ_ok);
+            mPara.mPositiveButtonListener = this;
+        } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_FAIL) {
+            mPara.mIconId = android.R.drawable.ic_dialog_alert;
+            mPara.mPositiveButtonText = getString(R.string.download_fail_ok);
+            mPara.mPositiveButtonListener = this;
+        } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) {
+            mPara.mPositiveButtonText = getString(R.string.upload_succ_ok);
+            mPara.mPositiveButtonListener = this;
+        } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) {
+            mPara.mIconId = android.R.drawable.ic_dialog_alert;
+            mPara.mPositiveButtonText = getString(R.string.upload_fail_ok);
+            mPara.mPositiveButtonListener = this;
+            mPara.mNegativeButtonText = getString(R.string.upload_fail_cancel);
+            mPara.mNegativeButtonListener = this;
+        }
+        mPara.mView = createView();
+        setupAlert();
+    }
+
+    private View createView() {
+
+        mView = getLayoutInflater().inflate(R.layout.file_transfer, null);
+
+        mProgressTransfer = (ProgressBar)mView.findViewById(R.id.progress_transfer);
+        mPercentView = (TextView)mView.findViewById(R.id.progress_percent);
+
+        customizeViewContent();
+
+        updateProgressbar();
+
+        return mView;
+    }
+
+    /**
+     * customize the content of view
+     */
+    private void customizeViewContent() {
+        String tmp;
+
+        if (mWhichDialog == DIALOG_RECEIVE_ONGOING
+                || mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) {
+            mLine1View = (TextView)mView.findViewById(R.id.line1_view);
+            tmp = getString(R.string.download_line1, mTransInfo.mDeviceName);
+            mLine1View.setText(tmp);
+            mLine2View = (TextView)mView.findViewById(R.id.line2_view);
+            tmp = getString(R.string.download_line2, mTransInfo.mFileName);
+            mLine2View.setText(tmp);
+            mLine3View = (TextView)mView.findViewById(R.id.line3_view);
+            tmp = getString(R.string.download_line3, Formatter.formatFileSize(this,
+                    mTransInfo.mTotalBytes));
+            mLine3View.setText(tmp);
+            mLine5View = (TextView)mView.findViewById(R.id.line5_view);
+            if (mWhichDialog == DIALOG_RECEIVE_ONGOING) {
+                tmp = getString(R.string.download_line5);
+            } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) {
+                tmp = getString(R.string.download_succ_line5);
+            }
+            mLine5View.setText(tmp);
+        } else if (mWhichDialog == DIALOG_SEND_ONGOING
+                || mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) {
+            mLine1View = (TextView)mView.findViewById(R.id.line1_view);
+            tmp = getString(R.string.upload_line1, mTransInfo.mDeviceName);
+            mLine1View.setText(tmp);
+            mLine2View = (TextView)mView.findViewById(R.id.line2_view);
+            tmp = getString(R.string.download_line2, mTransInfo.mFileName);
+            mLine2View.setText(tmp);
+            mLine3View = (TextView)mView.findViewById(R.id.line3_view);
+            tmp = getString(R.string.upload_line3, mTransInfo.mFileType, Formatter.formatFileSize(
+                    this, mTransInfo.mTotalBytes));
+            mLine3View.setText(tmp);
+            mLine5View = (TextView)mView.findViewById(R.id.line5_view);
+            if (mWhichDialog == DIALOG_SEND_ONGOING) {
+                tmp = getString(R.string.upload_line5);
+            } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) {
+                tmp = getString(R.string.upload_succ_line5);
+            }
+            mLine5View.setText(tmp);
+        } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_FAIL) {
+            if (mTransInfo.mStatus == BluetoothShare.STATUS_ERROR_SDCARD_FULL) {
+                mLine1View = (TextView)mView.findViewById(R.id.line1_view);
+                tmp = getString(R.string.bt_sm_2_1, mTransInfo.mDeviceName);
+                mLine1View.setText(tmp);
+                mLine2View = (TextView)mView.findViewById(R.id.line2_view);
+                tmp = getString(R.string.download_fail_line2, mTransInfo.mFileName);
+                mLine2View.setText(tmp);
+                mLine3View = (TextView)mView.findViewById(R.id.line3_view);
+                tmp = getString(R.string.bt_sm_2_2, Formatter.formatFileSize(this,
+                        mTransInfo.mTotalBytes));
+                mLine3View.setText(tmp);
+            } else {
+                mLine1View = (TextView)mView.findViewById(R.id.line1_view);
+                tmp = getString(R.string.download_fail_line1);
+                mLine1View.setText(tmp);
+                mLine2View = (TextView)mView.findViewById(R.id.line2_view);
+                tmp = getString(R.string.download_fail_line2, mTransInfo.mFileName);
+                mLine2View.setText(tmp);
+                mLine3View = (TextView)mView.findViewById(R.id.line3_view);
+                tmp = getString(R.string.download_fail_line3, BluetoothOppUtility
+                        .getStatusDescription(this, mTransInfo.mStatus));
+                mLine3View.setText(tmp);
+            }
+            mLine5View = (TextView)mView.findViewById(R.id.line5_view);
+            mLine5View.setVisibility(View.GONE);
+        } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) {
+            mLine1View = (TextView)mView.findViewById(R.id.line1_view);
+            tmp = getString(R.string.upload_fail_line1, mTransInfo.mDeviceName);
+            mLine1View.setText(tmp);
+            mLine2View = (TextView)mView.findViewById(R.id.line2_view);
+            tmp = getString(R.string.upload_fail_line1_2, mTransInfo.mFileName);
+            mLine2View.setText(tmp);
+            mLine3View = (TextView)mView.findViewById(R.id.line3_view);
+            tmp = getString(R.string.download_fail_line3, BluetoothOppUtility.getStatusDescription(
+                    this, mTransInfo.mStatus));
+            mLine3View.setText(tmp);
+            mLine5View = (TextView)mView.findViewById(R.id.line5_view);
+            mLine5View.setVisibility(View.GONE);
+        }
+
+        if (BluetoothShare.isStatusError(mTransInfo.mStatus)) {
+            mProgressTransfer.setVisibility(View.GONE);
+            mPercentView.setVisibility(View.GONE);
+        }
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) {
+                    // "Open" - open receive file
+                    BluetoothOppUtility.openReceivedFile(this, mTransInfo.mFileName,
+                            mTransInfo.mFileType, mTransInfo.mTimeStamp);
+
+                    // make current transfer "hidden"
+                    BluetoothOppUtility.updateVisibilityToHidden(this, mUri);
+
+                    // clear correspondent notification item
+                    ((NotificationManager)getSystemService(NOTIFICATION_SERVICE))
+                            .cancel(mTransInfo.mID);
+                } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) {
+                    // "try again"
+
+                    // make current transfer "hidden"
+                    BluetoothOppUtility.updateVisibilityToHidden(this, mUri);
+
+                    // clear correspondent notification item
+                    ((NotificationManager)getSystemService(NOTIFICATION_SERVICE))
+                            .cancel(mTransInfo.mID);
+
+                    // retry the failed transfer
+                    BluetoothOppUtility.retryTransfer(this, mTransInfo);
+
+                    BluetoothDevice remoteDevice = mAdapter.getRemoteDevice(mTransInfo.mDestAddr);
+
+                    // Display toast message
+                    Toast.makeText(
+                            this,
+                            this.getString(R.string.bt_toast_4, BluetoothOppManager.getInstance(
+                                    this).getDeviceName(remoteDevice)), Toast.LENGTH_SHORT)
+                            .show();
+
+                } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) {
+                    BluetoothOppUtility.updateVisibilityToHidden(this, mUri);
+                    ((NotificationManager)getSystemService(NOTIFICATION_SERVICE))
+                            .cancel(mTransInfo.mID);
+                }
+                break;
+
+            case DialogInterface.BUTTON_NEGATIVE:
+                if (mWhichDialog == DIALOG_RECEIVE_ONGOING || mWhichDialog == DIALOG_SEND_ONGOING) {
+                    // "Stop" button
+                    this.getContentResolver().delete(mUri, null, null);
+
+                    String msg = "";
+                    if (mWhichDialog == DIALOG_RECEIVE_ONGOING) {
+                        msg = getString(R.string.bt_toast_3, mTransInfo.mDeviceName);
+                    } else if (mWhichDialog == DIALOG_SEND_ONGOING) {
+                        msg = getString(R.string.bt_toast_6, mTransInfo.mDeviceName);
+                    }
+                    Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+
+                    ((NotificationManager)getSystemService(NOTIFICATION_SERVICE))
+                            .cancel(mTransInfo.mID);
+                } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) {
+
+                    BluetoothOppUtility.updateVisibilityToHidden(this, mUri);
+                }
+                break;
+        }
+        finish();
+    }
+
+    /**
+     * Update progress bar per data got from content provider
+     */
+    private void updateProgressbar() {
+        mTransInfo = BluetoothOppUtility.queryRecord(this, mUri);
+        if (mTransInfo == null) {
+            if (V) Log.e(TAG, "Error: Can not get data from db");
+            return;
+        }
+
+        if (mTransInfo.mTotalBytes == 0) {
+            // if Max and progress both equal 0, the progress display 100%.
+            // Below is to fix it.
+            mProgressTransfer.setMax(100);
+        } else {
+            mProgressTransfer.setMax(mTransInfo.mTotalBytes);
+        }
+
+        mProgressTransfer.setProgress(mTransInfo.mCurrentBytes);
+
+        mPercentView.setText(BluetoothOppUtility.formatProgressText(mTransInfo.mTotalBytes,
+                mTransInfo.mCurrentBytes));
+
+        // Handle the case when DIALOG_RECEIVE_ONGOING evolve to
+        // DIALOG_RECEIVE_COMPLETE_SUCCESS/DIALOG_RECEIVE_COMPLETE_FAIL
+        // Handle the case when DIALOG_SEND_ONGOING evolve to
+        // DIALOG_SEND_COMPLETE_SUCCESS/DIALOG_SEND_COMPLETE_FAIL
+        if (!mIsComplete && BluetoothShare.isStatusCompleted(mTransInfo.mStatus)) {
+
+            displayWhichDialog();
+
+            updateButton();
+
+            customizeViewContent();
+        }
+    }
+
+    /**
+     * Update button when one transfer goto complete from ongoing
+     */
+    private void updateButton() {
+        if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS) {
+            mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+            mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+                    getString(R.string.download_succ_ok));
+        } else if (mWhichDialog == DIALOG_RECEIVE_COMPLETE_FAIL) {
+            mAlert.setIcon(android.R.drawable.ic_dialog_alert);
+            mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+            mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+                    getString(R.string.download_fail_ok));
+        } else if (mWhichDialog == DIALOG_SEND_COMPLETE_SUCCESS) {
+            mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+            mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+                    getString(R.string.upload_succ_ok));
+        } else if (mWhichDialog == DIALOG_SEND_COMPLETE_FAIL) {
+            mAlert.setIcon(android.R.drawable.ic_dialog_alert);
+            mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+                    getString(R.string.upload_fail_ok));
+            mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setText(
+                    getString(R.string.upload_fail_cancel));
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppTransferInfo.java b/src/com/android/bluetooth/opp/BluetoothOppTransferInfo.java
new file mode 100644
index 0000000..b6ee08d
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppTransferInfo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+/**
+ * This is currently used by Application codes. This class stores information
+ * about a single OBEX transfer (operation)
+ */
+public class BluetoothOppTransferInfo {
+    int mID;
+
+    int mDirection;
+
+    int mTotalBytes;
+
+    int mCurrentBytes;
+
+    int mStatus;
+
+    Long mTimeStamp;
+
+    String mDestAddr; // bt address
+
+    String mFileName;
+
+    String mFileType;
+
+    String mFileUri; // the uri of the transferring file, related to the URI
+
+    String mDeviceName; // bt device name
+
+    // int mScanned;
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
new file mode 100644
index 0000000..0075227
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+import com.google.android.collect.Lists;
+
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.net.Uri;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class has some utilities for Opp application;
+ */
+public class BluetoothOppUtility {
+    private static final String TAG = "BluetoothOppUtility";
+    private static final boolean D = Constants.DEBUG;
+    private static final boolean V = Constants.VERBOSE;
+
+    public static BluetoothOppTransferInfo queryRecord(Context context, Uri uri) {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        BluetoothOppTransferInfo info = new BluetoothOppTransferInfo();
+        Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                info.mID = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare._ID));
+                info.mStatus = cursor.getInt(cursor.getColumnIndexOrThrow(BluetoothShare.STATUS));
+                info.mDirection = cursor.getInt(cursor
+                        .getColumnIndexOrThrow(BluetoothShare.DIRECTION));
+                info.mTotalBytes = cursor.getInt(cursor
+                        .getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES));
+                info.mCurrentBytes = cursor.getInt(cursor
+                        .getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES));
+                info.mTimeStamp = cursor.getLong(cursor
+                        .getColumnIndexOrThrow(BluetoothShare.TIMESTAMP));
+                info.mDestAddr = cursor.getString(cursor
+                        .getColumnIndexOrThrow(BluetoothShare.DESTINATION));
+
+                info.mFileName = cursor.getString(cursor
+                        .getColumnIndexOrThrow(BluetoothShare._DATA));
+                if (info.mFileName == null) {
+                    info.mFileName = cursor.getString(cursor
+                            .getColumnIndexOrThrow(BluetoothShare.FILENAME_HINT));
+                }
+                if (info.mFileName == null) {
+                    info.mFileName = context.getString(R.string.unknown_file);
+                }
+
+                info.mFileUri = cursor.getString(cursor.getColumnIndexOrThrow(BluetoothShare.URI));
+
+                if (info.mFileUri != null) {
+                    Uri u = Uri.parse(info.mFileUri);
+                    info.mFileType = context.getContentResolver().getType(u);
+                } else {
+                    Uri u = Uri.parse(info.mFileName);
+                    info.mFileType = context.getContentResolver().getType(u);
+                }
+                if (info.mFileType == null) {
+                    info.mFileType = cursor.getString(cursor
+                            .getColumnIndexOrThrow(BluetoothShare.MIMETYPE));
+                }
+
+                BluetoothDevice remoteDevice = adapter.getRemoteDevice(info.mDestAddr);
+                info.mDeviceName =
+                        BluetoothOppManager.getInstance(context).getDeviceName(remoteDevice);
+
+                if (V) Log.v(TAG, "Get data from db:" + info.mFileName + info.mFileType
+                            + info.mDestAddr);
+            }
+            cursor.close();
+        } else {
+            info = null;
+            if (V) Log.v(TAG, "BluetoothOppManager Error: not got data from db for uri:" + uri);
+        }
+        return info;
+    }
+
+    /**
+     * Organize Array list for transfers in one batch
+     */
+    // This function is used when UI show batch transfer. Currently only show single transfer.
+    public static ArrayList<String> queryTransfersInBatch(Context context, Long timeStamp) {
+        ArrayList<String> uris = Lists.newArrayList();
+        final String WHERE = BluetoothShare.TIMESTAMP + " == " + timeStamp;
+
+        Cursor metadataCursor = context.getContentResolver().query(BluetoothShare.CONTENT_URI,
+                new String[] {
+                    BluetoothShare._DATA
+                }, WHERE, null, BluetoothShare._ID);
+
+        if (metadataCursor == null) {
+            return null;
+        }
+
+        for (metadataCursor.moveToFirst(); !metadataCursor.isAfterLast(); metadataCursor
+                .moveToNext()) {
+            String fileName = metadataCursor.getString(0);
+            Uri path = Uri.parse(fileName);
+            // If there is no scheme, then it must be a file
+            if (path.getScheme() == null) {
+                path = Uri.fromFile(new File(fileName));
+            }
+            uris.add(path.toString());
+            if (V) Log.d(TAG, "Uri in this batch: " + path.toString());
+        }
+        metadataCursor.close();
+        return uris;
+    }
+
+    /**
+     * Open the received file with appropriate application, if can not find
+     * application to handle, display error dialog.
+     */
+    public static void openReceivedFile(Context context, String fileName, String mimetype,
+            Long timeStamp) {
+        if (fileName == null || mimetype == null) {
+            Log.e(TAG, "ERROR: Para fileName ==null, or mimetype == null");
+            return;
+        }
+
+        File f = new File(fileName);
+        if (!f.exists()) {
+            Intent in = new Intent(context, BluetoothOppBtErrorActivity.class);
+            in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            in.putExtra("title", context.getString(R.string.not_exist_file));
+            in.putExtra("content", context.getString(R.string.not_exist_file_desc));
+            context.startActivity(in);
+            return;
+        }
+
+        Uri path = Uri.parse(fileName);
+        // If there is no scheme, then it must be a file
+        if (path.getScheme() == null) {
+            path = Uri.fromFile(new File(fileName));
+        }
+
+        if (isRecognizedFileType(context, path, mimetype)) {
+            Intent activityIntent = new Intent(Intent.ACTION_VIEW);
+            activityIntent.setDataAndType(path, mimetype);
+
+            activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            try {
+                if (V) Log.d(TAG, "ACTION_VIEW intent sent out: " + path + " / " + mimetype);
+                context.startActivity(activityIntent);
+            } catch (ActivityNotFoundException ex) {
+                if (V) Log.d(TAG, "no activity for handling ACTION_VIEW intent:  " + mimetype, ex);
+            }
+        } else {
+            Intent in = new Intent(context, BluetoothOppBtErrorActivity.class);
+            in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            in.putExtra("title", context.getString(R.string.unknown_file));
+            in.putExtra("content", context.getString(R.string.unknown_file_desc));
+            context.startActivity(in);
+        }
+    }
+
+    /**
+     * To judge if the file type supported (can be handled by some app) by phone
+     * system.
+     */
+    public static boolean isRecognizedFileType(Context context, Uri fileUri, String mimetype) {
+        boolean ret = true;
+
+        if (D) Log.d(TAG, "RecognizedFileType() fileUri: " + fileUri + " mimetype: " + mimetype);
+
+        Intent mimetypeIntent = new Intent(Intent.ACTION_VIEW);
+        mimetypeIntent.setDataAndType(fileUri, mimetype);
+        List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(mimetypeIntent,
+                PackageManager.MATCH_DEFAULT_ONLY);
+
+        if (list.size() == 0) {
+            if (D) Log.d(TAG, "NO application to handle MIME type " + mimetype);
+            ret = false;
+        }
+        return ret;
+    }
+
+    /**
+     * update visibility to Hidden
+     */
+    public static void updateVisibilityToHidden(Context context, Uri uri) {
+        ContentValues updateValues = new ContentValues();
+        updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
+        context.getContentResolver().update(uri, updateValues, null, null);
+    }
+
+    /**
+     * Helper function to build the progress text.
+     */
+    public static String formatProgressText(long totalBytes, long currentBytes) {
+        if (totalBytes <= 0) {
+            return "0%";
+        }
+        long progress = currentBytes * 100 / totalBytes;
+        StringBuilder sb = new StringBuilder();
+        sb.append(progress);
+        sb.append('%');
+        return sb.toString();
+    }
+
+    /**
+     * Get status description according to status code.
+     */
+    public static String getStatusDescription(Context context, int statusCode) {
+        String ret;
+        if (statusCode == BluetoothShare.STATUS_PENDING) {
+            ret = context.getString(R.string.status_pending);
+        } else if (statusCode == BluetoothShare.STATUS_RUNNING) {
+            ret = context.getString(R.string.status_running);
+        } else if (statusCode == BluetoothShare.STATUS_SUCCESS) {
+            ret = context.getString(R.string.status_success);
+        } else if (statusCode == BluetoothShare.STATUS_NOT_ACCEPTABLE) {
+            ret = context.getString(R.string.status_not_accept);
+        } else if (statusCode == BluetoothShare.STATUS_FORBIDDEN) {
+            ret = context.getString(R.string.status_forbidden);
+        } else if (statusCode == BluetoothShare.STATUS_CANCELED) {
+            ret = context.getString(R.string.status_canceled);
+        } else if (statusCode == BluetoothShare.STATUS_FILE_ERROR) {
+            ret = context.getString(R.string.status_file_error);
+        } else if (statusCode == BluetoothShare.STATUS_ERROR_NO_SDCARD) {
+            ret = context.getString(R.string.status_no_sd_card);
+        } else if (statusCode == BluetoothShare.STATUS_CONNECTION_ERROR) {
+            ret = context.getString(R.string.status_connection_error);
+        } else if (statusCode == BluetoothShare.STATUS_ERROR_SDCARD_FULL) {
+            ret = context.getString(R.string.bt_sm_2_1);
+        } else if ((statusCode == BluetoothShare.STATUS_BAD_REQUEST)
+                || (statusCode == BluetoothShare.STATUS_LENGTH_REQUIRED)
+                || (statusCode == BluetoothShare.STATUS_PRECONDITION_FAILED)
+                || (statusCode == BluetoothShare.STATUS_UNHANDLED_OBEX_CODE)
+                || (statusCode == BluetoothShare.STATUS_OBEX_DATA_ERROR)) {
+            ret = context.getString(R.string.status_protocol_error);
+        } else {
+            ret = context.getString(R.string.status_unknown_error);
+        }
+        return ret;
+    }
+
+    /**
+     * Retry the failed transfer: Will insert a new transfer session to db
+     */
+    public static void retryTransfer(Context context, BluetoothOppTransferInfo transInfo) {
+        ContentValues values = new ContentValues();
+        values.put(BluetoothShare.URI, transInfo.mFileUri);
+        values.put(BluetoothShare.MIMETYPE, transInfo.mFileType);
+        values.put(BluetoothShare.DESTINATION, transInfo.mDestAddr);
+
+        final Uri contentUri = context.getContentResolver().insert(BluetoothShare.CONTENT_URI,
+                values);
+        if (V) Log.v(TAG, "Insert contentUri: " + contentUri + "  to device: " +
+                transInfo.mDeviceName);
+    }
+
+}
diff --git a/src/com/android/bluetooth/opp/BluetoothShare.java b/src/com/android/bluetooth/opp/BluetoothShare.java
new file mode 100644
index 0000000..d1fd516
--- /dev/null
+++ b/src/com/android/bluetooth/opp/BluetoothShare.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import android.provider.BaseColumns;
+import android.net.Uri;
+
+/**
+ * Exposes constants used to interact with the Bluetooth Share manager's content
+ * provider.
+ * @hide
+ */
+
+public final class BluetoothShare implements BaseColumns {
+    private BluetoothShare() {
+    }
+
+    /**
+     * The permission to access the Bluetooth Share Manager
+     */
+    public static final String PERMISSION_ACCESS = "android.permission.ACCESS_BLUETOOTH_SHARE";
+
+    /**
+     * The content:// URI for the data table in the provider
+     */
+    public static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.opp/btopp");
+
+    /**
+     * Broadcast Action: this is sent by the Bluetooth Share component to
+     * transfer complete. The request detail could be retrieved by app * as _ID
+     * is specified in the intent's data.
+     */
+    public static final String TRANSFER_COMPLETED_ACTION = "android.btopp.intent.action.TRANSFER_COMPLETE";
+
+    /**
+     * This is sent by the Bluetooth Share component to indicate there is an
+     * incoming file need user to confirm.
+     */
+    public static final String INCOMING_FILE_CONFIRMATION_REQUEST_ACTION = "android.btopp.intent.action.INCOMING_FILE_NOTIFICATION";
+
+    /**
+     * This is sent by the Bluetooth Share component to indicate there is an
+     * incoming file request timeout and need update UI.
+     */
+    public static final String USER_CONFIRMATION_TIMEOUT_ACTION = "android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT";
+
+    /**
+     * The name of the column containing the URI of the file being
+     * sent/received.
+     * <P>
+     * Type: TEXT
+     * </P>
+     * <P>
+     * Owner can Init/Read
+     * </P>
+     */
+    public static final String URI = "uri";
+
+    /**
+     * The name of the column containing the filename that the incoming file
+     * request recommends. When possible, the Bluetooth Share manager will
+     * attempt to use this filename, or a variation, as the actual name for the
+     * file.
+     * <P>
+     * Type: TEXT
+     * </P>
+     * <P>
+     * Owner can Init/Read
+     * </P>
+     */
+    public static final String FILENAME_HINT = "hint";
+
+    /**
+     * The name of the column containing the filename where the shared file was
+     * actually stored.
+     * <P>
+     * Type: TEXT
+     * </P>
+     * <P>
+     * Owner can Read
+     * </P>
+     */
+    public static final String _DATA = "_data";
+
+    /**
+     * The name of the column containing the MIME type of the shared file.
+     * <P>
+     * Type: TEXT
+     * </P>
+     * <P>
+     * Owner can Init/Read
+     * </P>
+     */
+    public static final String MIMETYPE = "mimetype";
+
+    /**
+     * The name of the column containing the direction (Inbound/Outbound) of the
+     * transfer. See the DIRECTION_* constants for a list of legal values.
+     * <P>
+     * Type: INTEGER
+     * </P>
+     * <P>
+     * Owner can Init/Read
+     * </P>
+     */
+    public static final String DIRECTION = "direction";
+
+    /**
+     * The name of the column containing Bluetooth Device Address that the
+     * transfer is associated with.
+     * <P>
+     * Type: TEXT
+     * </P>
+     * <P>
+     * Owner can Init/Read
+     * </P>
+     */
+    public static final String DESTINATION = "destination";
+
+    /**
+     * The name of the column containing the flags that controls whether the
+     * transfer is displayed by the UI. See the VISIBILITY_* constants for a
+     * list of legal values.
+     * <P>
+     * Type: INTEGER
+     * </P>
+     * <P>
+     * Owner can Init/Read/Write
+     * </P>
+     */
+    public static final String VISIBILITY = "visibility";
+
+    /**
+     * The name of the column containing the current user confirmation state of
+     * the transfer. Applications can write to this to confirm the transfer. the
+     * USER_CONFIRMATION_* constants for a list of legal values.
+     * <P>
+     * Type: INTEGER
+     * </P>
+     * <P>
+     * Owner can Init/Read/Write
+     * </P>
+     */
+    public static final String USER_CONFIRMATION = "confirm";
+
+    /**
+     * The name of the column containing the current status of the transfer.
+     * Applications can read this to follow the progress of each download. See
+     * the STATUS_* constants for a list of legal values.
+     * <P>
+     * Type: INTEGER
+     * </P>
+     * <P>
+     * Owner can Read
+     * </P>
+     */
+    public static final String STATUS = "status";
+
+    /**
+     * The name of the column containing the total size of the file being
+     * transferred.
+     * <P>
+     * Type: INTEGER
+     * </P>
+     * <P>
+     * Owner can Read
+     * </P>
+     */
+    public static final String TOTAL_BYTES = "total_bytes";
+
+    /**
+     * The name of the column containing the size of the part of the file that
+     * has been transferred so far.
+     * <P>
+     * Type: INTEGER
+     * </P>
+     * <P>
+     * Owner can Read
+     * </P>
+     */
+    public static final String CURRENT_BYTES = "current_bytes";
+
+    /**
+     * The name of the column containing the timestamp when the transfer is
+     * initialized.
+     * <P>
+     * Type: INTEGER
+     * </P>
+     * <P>
+     * Owner can Read
+     * </P>
+     */
+    public static final String TIMESTAMP = "timestamp";
+
+    /**
+     * This transfer is outbound, e.g. share file to other device.
+     */
+    public static final int DIRECTION_OUTBOUND = 0;
+
+    /**
+     * This transfer is inbound, e.g. receive file from other device.
+     */
+    public static final int DIRECTION_INBOUND = 1;
+
+    /**
+     * This transfer is waiting for user confirmation.
+     */
+    public static final int USER_CONFIRMATION_PENDING = 0;
+
+    /**
+     * This transfer is confirmed by user.
+     */
+    public static final int USER_CONFIRMATION_CONFIRMED = 1;
+
+    /**
+     * This transfer is auto-confirmed per previous user confirmation.
+     */
+    public static final int USER_CONFIRMATION_AUTO_CONFIRMED = 2;
+
+    /**
+     * This transfer is denied by user.
+     */
+    public static final int USER_CONFIRMATION_DENIED = 3;
+
+    /**
+     * This transfer is timeout before user action.
+     */
+    public static final int USER_CONFIRMATION_TIMEOUT = 4;
+
+    /**
+     * This transfer is visible and shows in the notifications while in progress
+     * and after completion.
+     */
+    public static final int VISIBILITY_VISIBLE = 0;
+
+    /**
+     * This transfer doesn't show in the notifications.
+     */
+    public static final int VISIBILITY_HIDDEN = 1;
+
+    /**
+     * Returns whether the status is informational (i.e. 1xx).
+     */
+    public static boolean isStatusInformational(int status) {
+        return (status >= 100 && status < 200);
+    }
+
+    /**
+     * Returns whether the transfer is suspended. (i.e. whether the transfer
+     * won't complete without some action from outside the transfer manager).
+     */
+    public static boolean isStatusSuspended(int status) {
+        return (status == STATUS_PENDING);
+    }
+
+    /**
+     * Returns whether the status is a success (i.e. 2xx).
+     */
+    public static boolean isStatusSuccess(int status) {
+        return (status >= 200 && status < 300);
+    }
+
+    /**
+     * Returns whether the status is an error (i.e. 4xx or 5xx).
+     */
+    public static boolean isStatusError(int status) {
+        return (status >= 400 && status < 600);
+    }
+
+    /**
+     * Returns whether the status is a client error (i.e. 4xx).
+     */
+    public static boolean isStatusClientError(int status) {
+        return (status >= 400 && status < 500);
+    }
+
+    /**
+     * Returns whether the status is a server error (i.e. 5xx).
+     */
+    public static boolean isStatusServerError(int status) {
+        return (status >= 500 && status < 600);
+    }
+
+    /**
+     * Returns whether the transfer has completed (either with success or
+     * error).
+     */
+    public static boolean isStatusCompleted(int status) {
+        return (status >= 200 && status < 300) || (status >= 400 && status < 600);
+    }
+
+    /**
+     * This transfer hasn't stated yet
+     */
+    public static final int STATUS_PENDING = 190;
+
+    /**
+     * This transfer has started
+     */
+    public static final int STATUS_RUNNING = 192;
+
+    /**
+     * This transfer has successfully completed. Warning: there might be other
+     * status values that indicate success in the future. Use isSucccess() to
+     * capture the entire category.
+     */
+    public static final int STATUS_SUCCESS = 200;
+
+    /**
+     * This request couldn't be parsed. This is also used when processing
+     * requests with unknown/unsupported URI schemes.
+     */
+    public static final int STATUS_BAD_REQUEST = 400;
+
+    /**
+     * This transfer is forbidden by target device.
+     */
+    public static final int STATUS_FORBIDDEN = 403;
+
+    /**
+     * This transfer can't be performed because the content cannot be handled.
+     */
+    public static final int STATUS_NOT_ACCEPTABLE = 406;
+
+    /**
+     * This transfer cannot be performed because the length cannot be determined
+     * accurately. This is the code for the HTTP error "Length Required", which
+     * is typically used when making requests that require a content length but
+     * don't have one, and it is also used in the client when a response is
+     * received whose length cannot be determined accurately (therefore making
+     * it impossible to know when a transfer completes).
+     */
+    public static final int STATUS_LENGTH_REQUIRED = 411;
+
+    /**
+     * This transfer was interrupted and cannot be resumed. This is the code for
+     * the OBEX error "Precondition Failed", and it is also used in situations
+     * where the client doesn't have an ETag at all.
+     */
+    public static final int STATUS_PRECONDITION_FAILED = 412;
+
+    /**
+     * This transfer was canceled
+     */
+    public static final int STATUS_CANCELED = 490;
+
+    /**
+     * This transfer has completed with an error. Warning: there will be other
+     * status values that indicate errors in the future. Use isStatusError() to
+     * capture the entire category.
+     */
+    public static final int STATUS_UNKNOWN_ERROR = 491;
+
+    /**
+     * This transfer couldn't be completed because of a storage issue.
+     * Typically, that's because the file system is missing or full.
+     */
+    public static final int STATUS_FILE_ERROR = 492;
+
+    /**
+     * This transfer couldn't be completed because of no sdcard.
+     */
+    public static final int STATUS_ERROR_NO_SDCARD = 493;
+
+    /**
+     * This transfer couldn't be completed because of sdcard full.
+     */
+    public static final int STATUS_ERROR_SDCARD_FULL = 494;
+
+    /**
+     * This transfer couldn't be completed because of an unspecified un-handled
+     * OBEX code.
+     */
+    public static final int STATUS_UNHANDLED_OBEX_CODE = 495;
+
+    /**
+     * This transfer couldn't be completed because of an error receiving or
+     * processing data at the OBEX level.
+     */
+    public static final int STATUS_OBEX_DATA_ERROR = 496;
+
+    /**
+     * This transfer couldn't be completed because of an error when establishing
+     * connection.
+     */
+    public static final int STATUS_CONNECTION_ERROR = 497;
+
+}
diff --git a/src/com/android/bluetooth/opp/Constants.java b/src/com/android/bluetooth/opp/Constants.java
new file mode 100644
index 0000000..c701920
--- /dev/null
+++ b/src/com/android/bluetooth/opp/Constants.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+import javax.obex.HeaderSet;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+/**
+ * Bluetooth OPP internal constants definition
+ */
+public class Constants {
+    /** Tag used for debugging/logging */
+    public static final String TAG = "BluetoothOpp";
+
+    /**
+     * The intent that gets sent when the service must wake up for a retry Note:
+     * only retry Outbound transfer
+     */
+    public static final String ACTION_RETRY = "android.btopp.intent.action.RETRY";
+
+    /** the intent that gets sent when clicking a successful transfer */
+    public static final String ACTION_OPEN = "android.btopp.intent.action.OPEN";
+
+    /** the intent that gets sent when clicking an incomplete/failed transfer */
+    public static final String ACTION_LIST = "android.btopp.intent.action.LIST";
+
+    /**
+     * the intent that gets sent when deleting the notification of a completed
+     * transfer
+     */
+    public static final String ACTION_HIDE = "android.btopp.intent.action.HIDE";
+
+    /**
+     * the intent that gets sent when clicking a incoming file confirm
+     * notification
+     */
+    public static final String ACTION_INCOMING_FILE_CONFIRM = "android.btopp.intent.action.CONFIRM";
+
+    public static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
+
+    /**
+     * The column that is used to remember whether the media scanner was invoked
+     */
+    public static final String MEDIA_SCANNED = "scanned";
+
+    public static final int MEDIA_SCANNED_NOT_SCANNED = 0;
+
+    public static final int MEDIA_SCANNED_SCANNED_OK = 1;
+
+    public static final int MEDIA_SCANNED_SCANNED_FAILED = 2;
+
+    /**
+     * The MIME type(s) of we could share to other device.
+     */
+    /*
+     * TODO: define correct type list
+     */
+    public static final String[] ACCEPTABLE_SHARE_OUTBOUND_TYPES = new String[] {
+        "image/*",
+    };
+
+    /**
+     * The MIME type(s) of we could not share to other device. TODO: define
+     * correct type list
+     */
+    public static final String[] UNACCEPTABLE_SHARE_OUTBOUND_TYPES = new String[] {
+        "virus/*",
+    };
+
+    /**
+     * The MIME type(s) of we could accept from other device.
+     * This is in essence a "white list" of acceptable types.
+     * Today, restricted to images, audio, video and certain text types.
+     */
+    public static final String[] ACCEPTABLE_SHARE_INBOUND_TYPES = new String[] {
+        "image/*",
+        "video/*",
+        "audio/*",
+        "text/plain",
+        "text/html",
+    };
+
+    /**
+     * The MIME type(s) of we could not accept from other device. TODO: define
+     * correct type list
+     */
+    public static final String[] UNACCEPTABLE_SHARE_INBOUND_TYPES = new String[] {
+        "text/x-vcalendar",
+        "text/x-vcard",
+    };
+
+    /** Where we store Bluetooth received files on the external storage */
+    public static final String DEFAULT_STORE_SUBDIR = "/bluetooth";
+
+    /**
+     * Debug level logging
+     */
+    public static final boolean DEBUG = false;
+
+    /**
+     * Verbose level logging
+     */
+    public static final boolean VERBOSE = false;
+
+    /** use TCP socket instead of Rfcomm Socket to develop */
+    public static final boolean USE_TCP_DEBUG = false;
+
+    /** use simple TCP server started from TestActivity */
+    public static final boolean USE_TCP_SIMPLE_SERVER = false;
+
+    /** Test TCP socket port */
+    public static final int TCP_DEBUG_PORT = 6500;
+
+    /** use emulator to debug */
+    public static final boolean USE_EMULATOR_DEBUG = false;
+
+    public static final int MAX_RECORDS_IN_DATABASE = 20;
+
+    public static final int BATCH_STATUS_PENDING = 0;
+
+    public static final int BATCH_STATUS_RUNNING = 1;
+
+    public static final int BATCH_STATUS_FINISHED = 2;
+
+    public static final int BATCH_STATUS_FAILED = 3;
+
+    public static final String BLUETOOTHOPP_NAME_PREFERENCE = "btopp_names";
+
+    public static final String BLUETOOTHOPP_CHANNEL_PREFERENCE = "btopp_channels";
+
+    public static String filename_SEQUENCE_SEPARATOR = "-";
+
+    public static void updateShareStatus(Context context, int id, int status) {
+        Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
+        ContentValues updateValues = new ContentValues();
+        updateValues.put(BluetoothShare.STATUS, status);
+        context.getContentResolver().update(contentUri, updateValues, null, null);
+        Constants.sendIntentIfCompleted(context, contentUri, status);
+    }
+
+    /*
+     * This function should be called whenever transfer status change to
+     * completed.
+     */
+    public static void sendIntentIfCompleted(Context context, Uri contentUri, int status) {
+        if (BluetoothShare.isStatusCompleted(status)) {
+            Intent intent = new Intent(BluetoothShare.TRANSFER_COMPLETED_ACTION);
+            intent.setClassName(THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
+            intent.setData(contentUri);
+            context.sendBroadcast(intent);
+        }
+    }
+
+    public static boolean mimeTypeMatches(String mimeType, String[] matchAgainst) {
+        for (String matchType : matchAgainst) {
+            if (mimeTypeMatches(mimeType, matchType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean mimeTypeMatches(String mimeType, String matchAgainst) {
+        Pattern p = Pattern.compile(matchAgainst.replaceAll("\\*", "\\.\\*"),
+                Pattern.CASE_INSENSITIVE);
+        return p.matcher(mimeType).matches();
+    }
+
+    public static void logHeader(HeaderSet hs) {
+        Log.v(TAG, "Dumping HeaderSet " + hs.toString());
+        try {
+
+            Log.v(TAG, "COUNT : " + hs.getHeader(HeaderSet.COUNT));
+            Log.v(TAG, "NAME : " + hs.getHeader(HeaderSet.NAME));
+            Log.v(TAG, "TYPE : " + hs.getHeader(HeaderSet.TYPE));
+            Log.v(TAG, "LENGTH : " + hs.getHeader(HeaderSet.LENGTH));
+            Log.v(TAG, "TIME_ISO_8601 : " + hs.getHeader(HeaderSet.TIME_ISO_8601));
+            Log.v(TAG, "TIME_4_BYTE : " + hs.getHeader(HeaderSet.TIME_4_BYTE));
+            Log.v(TAG, "DESCRIPTION : " + hs.getHeader(HeaderSet.DESCRIPTION));
+            Log.v(TAG, "TARGET : " + hs.getHeader(HeaderSet.TARGET));
+            Log.v(TAG, "HTTP : " + hs.getHeader(HeaderSet.HTTP));
+            Log.v(TAG, "WHO : " + hs.getHeader(HeaderSet.WHO));
+            Log.v(TAG, "OBJECT_CLASS : " + hs.getHeader(HeaderSet.OBJECT_CLASS));
+            Log.v(TAG, "APPLICATION_PARAMETER : " + hs.getHeader(HeaderSet.APPLICATION_PARAMETER));
+        } catch (IOException e) {
+            Log.e(TAG, "dump HeaderSet error " + e);
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/opp/TestActivity.java b/src/com/android/bluetooth/opp/TestActivity.java
new file mode 100644
index 0000000..4266f68
--- /dev/null
+++ b/src/com/android/bluetooth/opp/TestActivity.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.opp;
+
+import com.android.bluetooth.R;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.EditText;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+
+import javax.obex.Authenticator;
+import javax.obex.HeaderSet;
+import javax.obex.ObexTransport;
+import javax.obex.Operation;
+import javax.obex.ResponseCodes;
+import javax.obex.ServerRequestHandler;
+import javax.obex.ServerSession;
+
+public class TestActivity extends Activity {
+
+    public String currentInsert;
+
+    public int mCurrentByte = 0;
+
+    EditText mUpdateView;
+
+    EditText mAckView;
+
+    EditText mDeleteView;
+
+    EditText mInsertView;
+
+    EditText mAddressView;
+
+    EditText mMediaView;
+
+    TestTcpServer server;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+
+        String action = intent.getAction();
+
+        Context c = getBaseContext();
+
+        if (Intent.ACTION_SEND.equals(action)) {
+            /*
+             * Other application is trying to share a file via Bluetooth,
+             * probably Pictures, or vCard. The Intent should contain an
+             * EXTRA_STREAM with the data to attach.
+             */
+
+            String type = intent.getType();
+            Uri stream = (Uri)intent.getParcelableExtra(Intent.EXTRA_STREAM);
+
+            if (stream != null && type != null) {
+                /*
+                 * if (MimeUtility.mimeTypeMatches(type,
+                 * Email.ACCEPTABLE_ATTACHMENT_SEND_TYPES)) {
+                 * addAttachment(stream);
+                 */
+                Log.v(Constants.TAG, " Get share intent with Uri " + stream + " mimetype is "
+                        + type);
+                // Log.v(Constants.TAG, " trying Uri function " +
+                // stream.getAuthority() + " " + Uri.parse(stream));
+                Cursor cursor = c.getContentResolver().query(stream, null, null, null, null);
+                cursor.close();
+
+            }
+            /* start insert a record */
+            /*
+             * ContentValues values = new ContentValues();
+             * values.put(BluetoothShare.URI, stream.toString());
+             * values.put(BluetoothShare.DESTINATION, "FF:FF:FF:00:00:00");
+             * values.put(BluetoothShare.DIRECTION,
+             * BluetoothShare.DIRECTION_OUTBOUND); final Uri contentUri =
+             * getContentResolver().insert(BluetoothShare.CONTENT_URI, values);
+             * Log.v(Constants.TAG, "insert contentUri: " + contentUri);
+             */
+        }
+        /*
+         * Context c = getBaseContext(); c.startService(new Intent(c,
+         * BluetoothOppService.class));
+         */
+
+        setContentView(R.layout.testactivity_main);
+
+        Button mInsertRecord = (Button)findViewById(R.id.insert_record);
+        Button mDeleteRecord = (Button)findViewById(R.id.delete_record);
+        Button mUpdateRecord = (Button)findViewById(R.id.update_record);
+
+        Button mAckRecord = (Button)findViewById(R.id.ack_record);
+
+        Button mDeleteAllRecord = (Button)findViewById(R.id.deleteAll_record);
+        mUpdateView = (EditText)findViewById(R.id.update_text);
+        mAckView = (EditText)findViewById(R.id.ack_text);
+        mDeleteView = (EditText)findViewById(R.id.delete_text);
+        mInsertView = (EditText)findViewById(R.id.insert_text);
+
+        mAddressView = (EditText)findViewById(R.id.address_text);
+        mMediaView = (EditText)findViewById(R.id.media_text);
+
+        mInsertRecord.setOnClickListener(insertRecordListener);
+        mDeleteRecord.setOnClickListener(deleteRecordListener);
+        mUpdateRecord.setOnClickListener(updateRecordListener);
+        mAckRecord.setOnClickListener(ackRecordListener);
+        mDeleteAllRecord.setOnClickListener(deleteAllRecordListener);
+
+        Button mStartTcpServer = (Button)findViewById(R.id.start_server);
+        mStartTcpServer.setOnClickListener(startTcpServerListener);
+
+        Button mNotifyTcpServer = (Button)findViewById(R.id.notify_server);
+        mNotifyTcpServer.setOnClickListener(notifyTcpServerListener);
+        /* parse insert result Uri */
+        /*
+         * String id = contentUri.getPathSegments().get(1); Log.v(Constants.TAG,
+         * "insert record id is " + id); Uri contentUri1 =
+         * Uri.parse(BluetoothShare.CONTENT_URI + "/" + id);
+         */
+        /* update a single column of a record */
+        /*
+         * ContentValues updateValues = new ContentValues();
+         * updateValues.put(BluetoothShare.TOTAL_BYTES, 120000);
+         * getContentResolver().update(contentUri1,updateValues,null,null);
+         */
+        /* query a single column of a record */
+        /*
+         * Cursor queryC = getContentResolver().query(contentUri1, null, null,
+         * null, null); if (queryC != null) { if (queryC.moveToFirst()) { int
+         * currentByteColumn =
+         * queryC.getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES); int
+         * currentByte = queryC.getInt(currentByteColumn);
+         */
+        /* update a column of a record */
+        /*
+         * for(int i =0;i<100;i++){ currentByte ++;
+         * updateValues.put(BluetoothShare.CURRENT_BYTES, currentByte);
+         * getContentResolver().update(contentUri1,updateValues,null,null); } }
+         * }
+         */
+        /* query whole data base */
+        /*
+         * Cursor c = managedQuery(contentUri1, new String [] {"_id",
+         * BluetoothShare.URI, BluetoothShare.STATUS,
+         * BluetoothShare.TOTAL_BYTES, BluetoothShare.CURRENT_BYTES,
+         * BluetoothShare._DATA, BluetoothShare.DIRECTION,
+         * BluetoothShare.MIMETYPE, BluetoothShare.DESTINATION,
+         * BluetoothShare.VISIBILITY, BluetoothShare.USER_CONFIRMATION,
+         * BluetoothShare.TIMESTAMP}, null, null, null); Log.v(Constants.TAG,
+         * "query " + contentUri1 +" get " + c.getCount()+" records");
+         */
+        /* delete a record */
+        /*
+         * Uri contentUri2 = Uri.parse(BluetoothShare.CONTENT_URI + "/" + 1);
+         * getContentResolver().delete(contentUri2, null, null);
+         */
+
+    }
+
+    public OnClickListener insertRecordListener = new OnClickListener() {
+        public void onClick(View view) {
+
+            String address = null;
+            if (mAddressView.getText().length() != 0) {
+                address = mAddressView.getText().toString();
+                Log.v(Constants.TAG, "Send to address  " + address);
+            }
+            if (address == null) {
+                address = "00:17:83:58:5D:CC";
+            }
+
+            Integer media = null;
+            if (mMediaView.getText().length() != 0) {
+                media = Integer.parseInt(mMediaView.getText().toString().trim());
+                Log.v(Constants.TAG, "Send media no.  " + media);
+            }
+            if (media == null) {
+                media = 1;
+            }
+            ContentValues values = new ContentValues();
+            values.put(BluetoothShare.URI, "content://media/external/images/media/" + media);
+            // values.put(BluetoothShare.DESTINATION, "FF:FF:FF:00:00:00");
+            // baibai Q9 test
+            // values.put(BluetoothShare.DESTINATION, "12:34:56:78:9A:BC");
+            // java's nokia
+            // values.put(BluetoothShare.DESTINATION, "00:1B:33:F0:58:FB");
+            // Assis phone
+            // values.put(BluetoothShare.DESTINATION, "00:17:E5:5D:74:F3");
+            // Jackson E6
+            // values.put(BluetoothShare.DESTINATION, "00:1A:1B:7F:1E:F0");
+            // Baibai V950
+            // values.put(BluetoothShare.DESTINATION, "00:17:83:58:5D:CC");
+            // Baibai NSC1173
+            // values.put(BluetoothShare.DESTINATION, "00:16:41:49:5B:F3");
+
+            values.put(BluetoothShare.DESTINATION, address);
+
+            values.put(BluetoothShare.DIRECTION, BluetoothShare.DIRECTION_OUTBOUND);
+
+            Long ts = System.currentTimeMillis();
+            values.put(BluetoothShare.TIMESTAMP, ts);
+
+            Integer records = null;
+            if (mInsertView.getText().length() != 0) {
+                records = Integer.parseInt(mInsertView.getText().toString().trim());
+                Log.v(Constants.TAG, "parseInt  " + records);
+            }
+            if (records == null) {
+                records = 1;
+            }
+            for (int i = 0; i < records; i++) {
+                Uri contentUri = getContentResolver().insert(BluetoothShare.CONTENT_URI, values);
+                Log.v(Constants.TAG, "insert contentUri: " + contentUri);
+                currentInsert = contentUri.getPathSegments().get(1);
+                Log.v(Constants.TAG, "currentInsert = " + currentInsert);
+            }
+
+        }
+    };
+
+    public OnClickListener deleteRecordListener = new OnClickListener() {
+        public void onClick(View view) {
+            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/"
+                    + mDeleteView.getText().toString());
+            getContentResolver().delete(contentUri, null, null);
+        }
+    };
+
+    public OnClickListener updateRecordListener = new OnClickListener() {
+        public void onClick(View view) {
+            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/"
+                    + mUpdateView.getText().toString());
+            ContentValues updateValues = new ContentValues();
+            // mCurrentByte ++;
+            // updateValues.put(BluetoothShare.TOTAL_BYTES, "120000");
+            // updateValues.put(BluetoothShare.CURRENT_BYTES, mCurrentByte);
+            // updateValues.put(BluetoothShare.VISIBILITY,
+            // BluetoothShare.VISIBILITY_HIDDEN);
+            updateValues.put(BluetoothShare.USER_CONFIRMATION,
+                    BluetoothShare.USER_CONFIRMATION_CONFIRMED);
+            getContentResolver().update(contentUri, updateValues, null, null);
+        }
+    };
+
+    public OnClickListener ackRecordListener = new OnClickListener() {
+        public void onClick(View view) {
+            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "/"
+                    + mAckView.getText().toString());
+            ContentValues updateValues = new ContentValues();
+            // mCurrentByte ++;
+            // updateValues.put(BluetoothShare.TOTAL_BYTES, "120000");
+            // updateValues.put(BluetoothShare.CURRENT_BYTES, mCurrentByte);
+            updateValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_HIDDEN);
+            // updateValues.put(BluetoothShare.USER_CONFIRMATION,
+            // BluetoothShare.USER_CONFIRMATION_CONFIRMED);
+            getContentResolver().update(contentUri, updateValues, null, null);
+        }
+    };
+
+    public OnClickListener deleteAllRecordListener = new OnClickListener() {
+        public void onClick(View view) {
+            Uri contentUri = Uri.parse(BluetoothShare.CONTENT_URI + "");
+            getContentResolver().delete(contentUri, null, null);
+        }
+    };
+
+    public OnClickListener startTcpServerListener = new OnClickListener() {
+        public void onClick(View view) {
+            server = new TestTcpServer();
+            Thread server_thread = new Thread(server);
+            server_thread.start();
+
+        }
+    };
+
+    public OnClickListener notifyTcpServerListener = new OnClickListener() {
+        public void onClick(View view) {
+            final Thread notifyThread = new Thread() {
+                public void run() {
+                    synchronized (server) {
+                        server.a = true;
+                        server.notify();
+                    }
+                }
+
+            };
+            notifyThread.start();
+        }
+
+    };
+}
+
+/**
+ * This class listens on OPUSH channel for incoming connection
+ */
+class TestTcpListener {
+
+    private static final String TAG = "BtOppRfcommListener";
+
+    private static final boolean D = Constants.DEBUG;
+
+    private static final boolean V = Constants.VERBOSE;
+
+    private volatile boolean mInterrupted;
+
+    private Thread mSocketAcceptThread;
+
+    private Handler mCallback;
+
+    private static final int ACCEPT_WAIT_TIMEOUT = 5000;
+
+    public static final int DEFAULT_OPP_CHANNEL = 12;
+
+    public static final int MSG_INCOMING_BTOPP_CONNECTION = 100;
+
+    private int mBtOppRfcommChannel = -1;
+
+    public TestTcpListener() {
+        this(DEFAULT_OPP_CHANNEL);
+    }
+
+    public TestTcpListener(int channel) {
+        mBtOppRfcommChannel = channel;
+    }
+
+    public synchronized boolean start(Handler callback) {
+        if (mSocketAcceptThread == null) {
+            mCallback = callback;
+            mSocketAcceptThread = new Thread(TAG) {
+                ServerSocket mServerSocket;
+
+                public void run() {
+                    if (D) Log.d(TAG, "RfcommSocket listen thread starting");
+                    try {
+                        if (V)
+                            Log.v(TAG, "Create server RfcommSocket on channel"
+                                    + mBtOppRfcommChannel);
+                        mServerSocket = new ServerSocket(6500, 1);
+                    } catch (IOException e) {
+                        Log.e(TAG, "Error listing on channel" + mBtOppRfcommChannel);
+                        mInterrupted = true;
+                    }
+                    while (!mInterrupted) {
+                        try {
+                            mServerSocket.setSoTimeout(ACCEPT_WAIT_TIMEOUT);
+                            Socket clientSocket = mServerSocket.accept();
+                            if (clientSocket == null) {
+                                if (V) Log.v(TAG, "incomming connection time out");
+                            } else {
+                                if (D) Log.d(TAG, "RfcommSocket connected!");
+                                Log.d(TAG, "remote addr is "
+                                        + clientSocket.getRemoteSocketAddress());
+                                TestTcpTransport transport = new TestTcpTransport(clientSocket);
+                                Message msg = Message.obtain();
+                                msg.setTarget(mCallback);
+                                msg.what = MSG_INCOMING_BTOPP_CONNECTION;
+                                msg.obj = transport;
+                                msg.sendToTarget();
+                            }
+                        } catch (SocketException e) {
+                            Log.e(TAG, "Error accept connection " + e);
+                        } catch (IOException e) {
+                            Log.e(TAG, "Error accept connection " + e);
+                        }
+
+                        if (mInterrupted) {
+                            Log.e(TAG, "socketAcceptThread thread was interrupted (2), exiting");
+                        }
+                    }
+                    if (D) Log.d(TAG, "RfcommSocket listen thread finished");
+                }
+            };
+            mInterrupted = false;
+            mSocketAcceptThread.start();
+
+        }
+        return true;
+
+    }
+
+    public synchronized void stop() {
+        if (mSocketAcceptThread != null) {
+            if (D) Log.d(TAG, "stopping Connect Thread");
+            mInterrupted = true;
+            try {
+                mSocketAcceptThread.interrupt();
+                if (V) Log.v(TAG, "waiting for thread to terminate");
+                mSocketAcceptThread.join();
+                mSocketAcceptThread = null;
+                mCallback = null;
+            } catch (InterruptedException e) {
+                if (V) Log.v(TAG, "Interrupted waiting for Accept Thread to join");
+            }
+        }
+    }
+
+}
+
+class TestTcpServer extends ServerRequestHandler implements Runnable {
+    private static final String TAG = "ServerRequestHandler";
+
+    private static final boolean D = Constants.DEBUG;
+
+    private static final boolean V = Constants.VERBOSE;
+
+    static final int port = 6500;
+
+    public boolean a = false;
+
+    // TextView serverStatus = null;
+    public void run() {
+        try {
+            updateStatus("[server:] listen on port " + port);
+            TestTcpSessionNotifier rsn = new TestTcpSessionNotifier(port);
+
+            updateStatus("[server:] Now waiting for a client to connect");
+            rsn.acceptAndOpen(this);
+            updateStatus("[server:] A client is now connected");
+        } catch (Exception ex) {
+            updateStatus("[server:] Caught the error: " + ex);
+        }
+    }
+
+    public TestTcpServer() {
+        updateStatus("enter construtor of TcpServer");
+    }
+
+    public int onConnect(HeaderSet request, HeaderSet reply) {
+
+        updateStatus("[server:] The client has created an OBEX session");
+        /* sleep for 2000 ms to wait for the batch contains all ShareInfos */
+        synchronized (this) {
+            try {
+                while (!a) {
+                    wait(500);
+                }
+            } catch (InterruptedException e) {
+                if (V) Log.v(TAG, "Interrupted waiting for markBatchFailed");
+            }
+        }
+        updateStatus("[server:] we accpet the seesion");
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    public int onPut(Operation op) {
+        FileOutputStream fos = null;
+        try {
+            java.io.InputStream is = op.openInputStream();
+
+            updateStatus("Got data bytes " + is.available() + " name "
+                    + op.getReceivedHeader().getHeader(HeaderSet.NAME) + " type " + op.getType());
+
+            File f = new File((String)op.getReceivedHeader().getHeader(HeaderSet.NAME));
+            fos = new FileOutputStream(f);
+            byte b[] = new byte[1000];
+            int len;
+
+            while (is.available() > 0 && (len = is.read(b)) > 0) {
+                fos.write(b, 0, len);
+            }
+
+            fos.close();
+            is.close();
+            updateStatus("[server:] Wrote data to " + f.getAbsolutePath());
+        } catch (Exception e) {
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException e1) {
+                    e1.printStackTrace();
+                }
+            }
+            e.printStackTrace();
+        }
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    public void onDisconnect(HeaderSet req, HeaderSet resp) {
+        updateStatus("[server:] The client has disconnected the OBEX session");
+    }
+
+    public void updateStatus(String message) {
+        Log.v(TAG, "\n" + message);
+    }
+
+    public void onAuthenticationFailure(byte[] userName) {
+    }
+
+    public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
+
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    public int onDelete(HeaderSet request, HeaderSet reply) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    public int onGet(Operation op) {
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+}
+
+class TestTcpSessionNotifier {
+    /* implements SessionNotifier */
+
+    ServerSocket server = null;
+
+    Socket conn = null;
+
+    private static final String TAG = "TestTcpSessionNotifier";
+
+    public TestTcpSessionNotifier(int port) throws IOException {
+        server = new ServerSocket(port);
+    }
+
+    public ServerSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth)
+            throws IOException {
+        try {
+            conn = server.accept();
+
+        } catch (Exception ex) {
+            Log.v(TAG, "ex");
+        }
+
+        TestTcpTransport tt = new TestTcpTransport(conn);
+
+        return new ServerSession((ObexTransport)tt, handler, auth);
+
+    }
+
+    public ServerSession acceptAndOpen(ServerRequestHandler handler) throws IOException {
+
+        return acceptAndOpen(handler, null);
+
+    }
+
+}
+
+class TestTcpTransport implements ObexTransport {
+
+    Socket s = null;
+
+    public TestTcpTransport(Socket s) {
+        super();
+        this.s = s;
+    }
+
+    public void close() throws IOException {
+        s.close();
+    }
+
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return s.getInputStream();
+    }
+
+    public OutputStream openOutputStream() throws IOException {
+        return s.getOutputStream();
+    }
+
+    public void connect() throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void create() throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void disconnect() throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void listen() throws IOException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public boolean isConnected() throws IOException {
+        return s.isConnected();
+    }
+
+}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java b/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java
new file mode 100644
index 0000000..42e99d4
--- /dev/null
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.pbap;
+
+import com.android.bluetooth.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.util.Log;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Button;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.text.InputFilter.LengthFilter;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+/**
+ * PbapActivity shows two dialogues: One for accepting incoming pbap request and
+ * the other prompts the user to enter a session key for authentication with a
+ * remote Bluetooth device.
+ */
+public class BluetoothPbapActivity extends AlertActivity implements
+        DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, TextWatcher {
+    private static final String TAG = "BluetoothPbapActivity";
+
+    private static final boolean V = BluetoothPbapService.VERBOSE;
+
+    private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16;
+
+    private static final int DIALOG_YES_NO_CONNECT = 1;
+
+    private static final int DIALOG_YES_NO_AUTH = 2;
+
+    private static final String KEY_USER_TIMEOUT = "user_timeout";
+
+    private View mView;
+
+    private EditText mKeyView;
+
+    private TextView messageView;
+
+    private String mSessionKey = "";
+
+    private int mCurrentDialog;
+
+    private Button mOkButton;
+
+    private CheckBox mAlwaysAllowed;
+
+    private boolean mTimeout = false;
+
+    private boolean mAlwaysAllowedValue = true;
+
+    private static final int DISMISS_TIMEOUT_DIALOG = 0;
+
+    private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000;
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) {
+                return;
+            }
+            onTimeout();
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent i = getIntent();
+        String action = i.getAction();
+        if (action.equals(BluetoothPbapService.ACCESS_REQUEST_ACTION)) {
+            showPbapDialog(DIALOG_YES_NO_CONNECT);
+            mCurrentDialog = DIALOG_YES_NO_CONNECT;
+        } else if (action.equals(BluetoothPbapService.AUTH_CHALL_ACTION)) {
+            showPbapDialog(DIALOG_YES_NO_AUTH);
+            mCurrentDialog = DIALOG_YES_NO_AUTH;
+        } else {
+            Log.e(TAG, "Error: this activity may be started only with intent "
+                    + "PBAP_ACCESS_REQUEST or PBAP_AUTH_CHALL ");
+            finish();
+        }
+        registerReceiver(mReceiver, new IntentFilter(
+                BluetoothPbapService.USER_CONFIRM_TIMEOUT_ACTION));
+    }
+
+    private void showPbapDialog(int id) {
+        final AlertController.AlertParams p = mAlertParams;
+        switch (id) {
+            case DIALOG_YES_NO_CONNECT:
+                p.mIconId = android.R.drawable.ic_dialog_info;
+                p.mTitle = getString(R.string.pbap_acceptance_dialog_header);
+                p.mView = createView(DIALOG_YES_NO_CONNECT);
+                p.mPositiveButtonText = getString(android.R.string.yes);
+                p.mPositiveButtonListener = this;
+                p.mNegativeButtonText = getString(android.R.string.no);
+                p.mNegativeButtonListener = this;
+                mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+                setupAlert();
+                break;
+            case DIALOG_YES_NO_AUTH:
+                p.mIconId = android.R.drawable.ic_dialog_info;
+                p.mTitle = getString(R.string.pbap_session_key_dialog_header);
+                p.mView = createView(DIALOG_YES_NO_AUTH);
+                p.mPositiveButtonText = getString(android.R.string.ok);
+                p.mPositiveButtonListener = this;
+                p.mNegativeButtonText = getString(android.R.string.cancel);
+                p.mNegativeButtonListener = this;
+                setupAlert();
+                mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+                mOkButton.setEnabled(false);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private String createDisplayText(final int id) {
+        String mRemoteName = BluetoothPbapService.getRemoteDeviceName();
+        switch (id) {
+            case DIALOG_YES_NO_CONNECT:
+                String mMessage1 = getString(R.string.pbap_acceptance_dialog_title, mRemoteName,
+                        mRemoteName);
+                return mMessage1;
+            case DIALOG_YES_NO_AUTH:
+                String mMessage2 = getString(R.string.pbap_session_key_dialog_title, mRemoteName);
+                return mMessage2;
+            default:
+                return null;
+        }
+    }
+
+    private View createView(final int id) {
+        switch (id) {
+            case DIALOG_YES_NO_CONNECT:
+                mView = getLayoutInflater().inflate(R.layout.access, null);
+                messageView = (TextView)mView.findViewById(R.id.message);
+                messageView.setText(createDisplayText(id));
+                mAlwaysAllowed = (CheckBox)mView.findViewById(R.id.alwaysallowed);
+                mAlwaysAllowed.setChecked(true);
+                mAlwaysAllowed.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                        if (isChecked) {
+                            mAlwaysAllowedValue = true;
+                        } else {
+                            mAlwaysAllowedValue = false;
+                        }
+                    }
+                });
+                return mView;
+            case DIALOG_YES_NO_AUTH:
+                mView = getLayoutInflater().inflate(R.layout.auth, null);
+                messageView = (TextView)mView.findViewById(R.id.message);
+                messageView.setText(createDisplayText(id));
+                mKeyView = (EditText)mView.findViewById(R.id.text);
+                mKeyView.addTextChangedListener(this);
+                mKeyView.setFilters(new InputFilter[] {
+                    new LengthFilter(BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH)
+                });
+                return mView;
+            default:
+                return null;
+        }
+    }
+
+    private void onPositive() {
+        if (!mTimeout) {
+            if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+                sendIntentToReceiver(BluetoothPbapService.ACCESS_ALLOWED_ACTION,
+                        BluetoothPbapService.EXTRA_ALWAYS_ALLOWED, mAlwaysAllowedValue);
+            } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+                sendIntentToReceiver(BluetoothPbapService.AUTH_RESPONSE_ACTION,
+                        BluetoothPbapService.EXTRA_SESSION_KEY, mSessionKey);
+                mKeyView.removeTextChangedListener(this);
+            }
+        }
+        mTimeout = false;
+        finish();
+    }
+
+    private void onNegative() {
+        if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+            sendIntentToReceiver(BluetoothPbapService.ACCESS_DISALLOWED_ACTION, null, null);
+        } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+            sendIntentToReceiver(BluetoothPbapService.AUTH_CANCELLED_ACTION, null, null);
+            mKeyView.removeTextChangedListener(this);
+        }
+        finish();
+    }
+
+    private void sendIntentToReceiver(final String intentName, final String extraName,
+            final String extraValue) {
+        Intent intent = new Intent(intentName);
+        intent.setClassName(BluetoothPbapService.THIS_PACKAGE_NAME, BluetoothPbapReceiver.class
+                .getName());
+        if (extraName != null) {
+            intent.putExtra(extraName, extraValue);
+        }
+        sendBroadcast(intent);
+    }
+
+    private void sendIntentToReceiver(final String intentName, final String extraName,
+            final boolean extraValue) {
+        Intent intent = new Intent(intentName);
+        intent.setClassName(BluetoothPbapService.THIS_PACKAGE_NAME, BluetoothPbapReceiver.class
+                .getName());
+        if (extraName != null) {
+            intent.putExtra(extraName, extraValue);
+        }
+        sendBroadcast(intent);
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+                    mSessionKey = mKeyView.getText().toString();
+                }
+                onPositive();
+                break;
+
+            case DialogInterface.BUTTON_NEGATIVE:
+                onNegative();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void onTimeout() {
+        mTimeout = true;
+        if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+            messageView.setText(getString(R.string.pbap_acceptance_timeout_message,
+                    BluetoothPbapService.getRemoteDeviceName()));
+            mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+            mAlwaysAllowed.setVisibility(View.GONE);
+            mAlwaysAllowed.clearFocus();
+        } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+            messageView.setText(getString(R.string.pbap_authentication_timeout_message,
+                    BluetoothPbapService.getRemoteDeviceName()));
+            mKeyView.setVisibility(View.GONE);
+            mKeyView.clearFocus();
+            mKeyView.removeTextChangedListener(this);
+            mOkButton.setEnabled(true);
+            mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+        }
+
+        mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(DISMISS_TIMEOUT_DIALOG),
+                DISMISS_TIMEOUT_DIALOG_VALUE);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT);
+        if (V) Log.v(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout);
+        if (mTimeout) {
+            onTimeout();
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_USER_TIMEOUT, mTimeout);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        return true;
+    }
+
+    public void beforeTextChanged(CharSequence s, int start, int before, int after) {
+    }
+
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    public void afterTextChanged(android.text.Editable s) {
+        if (s.length() > 0) {
+            mOkButton.setEnabled(true);
+        }
+    }
+
+    private final Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DISMISS_TIMEOUT_DIALOG:
+                    if (V) Log.v(TAG, "Received DISMISS_TIMEOUT_DIALOG msg.");
+                    finish();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java b/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java
new file mode 100644
index 0000000..e9cca90
--- /dev/null
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.pbap;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import javax.obex.Authenticator;
+import javax.obex.PasswordAuthentication;
+
+/**
+ * BluetoothPbapAuthenticator is a used by BluetoothObexServer for obex
+ * authentication procedure.
+ */
+public class BluetoothPbapAuthenticator implements Authenticator {
+    private static final String TAG = "BluetoothPbapAuthenticator";
+
+    private boolean mChallenged;
+
+    private boolean mAuthCancelled;
+
+    private String mSessionKey;
+
+    private Handler mCallback;
+
+    public BluetoothPbapAuthenticator(final Handler callback) {
+        mCallback = callback;
+        mChallenged = false;
+        mAuthCancelled = false;
+        mSessionKey = null;
+    }
+
+    public final synchronized void setChallenged(final boolean bool) {
+        mChallenged = bool;
+    }
+
+    public final synchronized void setCancelled(final boolean bool) {
+        mAuthCancelled = bool;
+    }
+
+    public final synchronized void setSessionKey(final String string) {
+        mSessionKey = string;
+    }
+
+    private void waitUserConfirmation() {
+        Message msg = Message.obtain(mCallback);
+        msg.what = BluetoothPbapService.MSG_OBEX_AUTH_CHALL;
+        msg.sendToTarget();
+        synchronized (this) {
+            while (!mChallenged && !mAuthCancelled) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Interrupted while waiting on isChalled");
+                }
+            }
+        }
+    }
+
+    public PasswordAuthentication onAuthenticationChallenge(final String description,
+            final boolean isUserIdRequired, final boolean isFullAccess) {
+        waitUserConfirmation();
+        if (mSessionKey.trim().length() != 0) {
+            PasswordAuthentication pa = new PasswordAuthentication(null, mSessionKey.getBytes());
+            return pa;
+        }
+        return null;
+    }
+
+    // TODO: Reserved for future use only, in case PSE challenge PCE
+    public byte[] onAuthenticationResponse(final byte[] userName) {
+        byte[] b = null;
+        return b;
+    }
+}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java b/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java
new file mode 100644
index 0000000..bfadf70
--- /dev/null
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java
@@ -0,0 +1,1022 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.pbap;
+
+import android.content.Context;
+import android.os.Message;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.Contacts;
+import android.provider.CallLog;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.obex.ServerRequestHandler;
+import javax.obex.ResponseCodes;
+import javax.obex.ApplicationParameter;
+import javax.obex.Operation;
+import javax.obex.HeaderSet;
+
+public class BluetoothPbapObexServer extends ServerRequestHandler {
+
+    private static final String TAG = "BluetoothPbapObexServer";
+
+    private static final boolean D = BluetoothPbapService.DEBUG;
+
+    private static final boolean V = BluetoothPbapService.VERBOSE;
+
+    private static final int UUID_LENGTH = 16;
+
+    // The length of suffix of vcard name - ".vcf" is 5
+    private static final int VCARD_NAME_SUFFIX_LENGTH = 5;
+
+    // 128 bit UUID for PBAP
+    private static final byte[] PBAP_TARGET = new byte[] {
+            0x79, 0x61, 0x35, (byte)0xf0, (byte)0xf0, (byte)0xc5, 0x11, (byte)0xd8, 0x09, 0x66,
+            0x08, 0x00, 0x20, 0x0c, (byte)0x9a, 0x66
+    };
+
+    // Currently not support SIM card
+    private static final String[] LEGAL_PATH = {
+            "/telecom", "/telecom/pb", "/telecom/ich", "/telecom/och", "/telecom/mch",
+            "/telecom/cch"
+    };
+
+    @SuppressWarnings("unused")
+    private static final String[] LEGAL_PATH_WITH_SIM = {
+            "/telecom", "/telecom/pb", "/telecom/ich", "/telecom/och", "/telecom/mch",
+            "/telecom/cch", "/SIM1", "/SIM1/telecom", "/SIM1/telecom/ich", "/SIM1/telecom/och",
+            "/SIM1/telecom/mch", "/SIM1/telecom/cch", "/SIM1/telecom/pb"
+
+    };
+
+    // SIM card
+    private static final String SIM1 = "SIM1";
+
+    // missed call history
+    private static final String MCH = "mch";
+
+    // incoming call history
+    private static final String ICH = "ich";
+
+    // outgoing call history
+    private static final String OCH = "och";
+
+    // combined call history
+    private static final String CCH = "cch";
+
+    // phone book
+    private static final String PB = "pb";
+
+    private static final String ICH_PATH = "/telecom/ich";
+
+    private static final String OCH_PATH = "/telecom/och";
+
+    private static final String MCH_PATH = "/telecom/mch";
+
+    private static final String CCH_PATH = "/telecom/cch";
+
+    private static final String PB_PATH = "/telecom/pb";
+
+    // type for list vcard objects
+    private static final String TYPE_LISTING = "x-bt/vcard-listing";
+
+    // type for get single vcard object
+    private static final String TYPE_VCARD = "x-bt/vcard";
+
+    // to indicate if need send body besides headers
+    private static final int NEED_SEND_BODY = -1;
+
+    // type for download all vcard objects
+    private static final String TYPE_PB = "x-bt/phonebook";
+
+    // The number of indexes in the phone book.
+    private boolean mNeedPhonebookSize = false;
+
+    // The number of missed calls that have not been checked on the PSE at the
+    // point of the request. Only apply to "mch" case.
+    private boolean mNeedNewMissedCallsNum = false;
+
+    private int mMissedCallSize = 0;
+
+    // record current path the client are browsing
+    private String mCurrentPath = "";
+
+    private long mConnectionId;
+
+    private Handler mCallback = null;
+
+    private Context mContext;
+
+    private BluetoothPbapVcardManager mVcardManager;
+
+    private int mOrderBy  = ORDER_BY_INDEXED;
+
+    public static int ORDER_BY_INDEXED = 0;
+
+    public static int ORDER_BY_ALPHABETICAL = 1;
+
+    public static class ContentType {
+        public static final int PHONEBOOK = 1;
+
+        public static final int INCOMING_CALL_HISTORY = 2;
+
+        public static final int OUTGOING_CALL_HISTORY = 3;
+
+        public static final int MISSED_CALL_HISTORY = 4;
+
+        public static final int COMBINED_CALL_HISTORY = 5;
+    }
+
+    public BluetoothPbapObexServer(Handler callback, Context context) {
+        super();
+        mConnectionId = -1;
+        mCallback = callback;
+        mContext = context;
+        mVcardManager = new BluetoothPbapVcardManager(mContext);
+
+        // set initial value when ObexServer created
+        mMissedCallSize = mVcardManager.getPhonebookSize(ContentType.MISSED_CALL_HISTORY);
+        if (D) Log.d(TAG, "Initialize mMissedCallSize=" + mMissedCallSize);
+    }
+
+    @Override
+    public int onConnect(final HeaderSet request, HeaderSet reply) {
+        if (V) logHeader(request);
+        try {
+            byte[] uuid = (byte[])request.getHeader(HeaderSet.TARGET);
+            if (uuid == null) {
+                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+            }
+            if (D) Log.d(TAG, "onConnect(): uuid=" + Arrays.toString(uuid));
+
+            if (uuid.length != UUID_LENGTH) {
+                Log.w(TAG, "Wrong UUID length");
+                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+            }
+            for (int i = 0; i < UUID_LENGTH; i++) {
+                if (uuid[i] != PBAP_TARGET[i]) {
+                    Log.w(TAG, "Wrong UUID");
+                    return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+                }
+            }
+            reply.setHeader(HeaderSet.WHO, uuid);
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+
+        try {
+            byte[] remote = (byte[])request.getHeader(HeaderSet.WHO);
+            if (remote != null) {
+                if (D) Log.d(TAG, "onConnect(): remote=" + Arrays.toString(remote));
+                reply.setHeader(HeaderSet.TARGET, remote);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+
+        if (V) Log.v(TAG, "onConnect(): uuid is ok, will send out " +
+                "MSG_SESSION_ESTABLISHED msg.");
+
+        Message msg = Message.obtain(mCallback);
+        msg.what = BluetoothPbapService.MSG_SESSION_ESTABLISHED;
+        msg.sendToTarget();
+
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    @Override
+    public void onDisconnect(final HeaderSet req, final HeaderSet resp) {
+        if (D) Log.d(TAG, "onDisconnect(): enter");
+        if (V) logHeader(req);
+
+        resp.responseCode = ResponseCodes.OBEX_HTTP_OK;
+        if (mCallback != null) {
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothPbapService.MSG_SESSION_DISCONNECTED;
+            msg.sendToTarget();
+            if (V) Log.v(TAG, "onDisconnect(): msg MSG_SESSION_DISCONNECTED sent out.");
+        }
+    }
+
+    @Override
+    public int onPut(final Operation op) {
+        if (D) Log.d(TAG, "onPut(): not support PUT request.");
+        return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+    }
+
+    @Override
+    public int onSetPath(final HeaderSet request, final HeaderSet reply, final boolean backup,
+            final boolean create) {
+        if (V) logHeader(request);
+        if (D) Log.d(TAG, "before setPath, mCurrentPath ==  " + mCurrentPath);
+
+        String current_path_tmp = mCurrentPath;
+        String tmp_path = null;
+        try {
+            tmp_path = (String)request.getHeader(HeaderSet.NAME);
+        } catch (IOException e) {
+            Log.e(TAG, "Get name header fail");
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+        if (D) Log.d(TAG, "backup=" + backup + " create=" + create + " name=" + tmp_path);
+
+        if (backup) {
+            if (current_path_tmp.length() != 0) {
+                current_path_tmp = current_path_tmp.substring(0,
+                        current_path_tmp.lastIndexOf("/"));
+            }
+        } else {
+            if (tmp_path == null) {
+                current_path_tmp = "";
+            } else {
+                current_path_tmp = current_path_tmp + "/" + tmp_path;
+            }
+        }
+
+        if ((current_path_tmp.length() != 0) && (!isLegalPath(current_path_tmp))) {
+            if (create) {
+                Log.w(TAG, "path create is forbidden!");
+                return ResponseCodes.OBEX_HTTP_FORBIDDEN;
+            } else {
+                Log.w(TAG, "path is not legal");
+                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            }
+        }
+        mCurrentPath = current_path_tmp;
+        if (V) Log.v(TAG, "after setPath, mCurrentPath ==  " + mCurrentPath);
+
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    @Override
+    public void onClose() {
+        if (mCallback != null) {
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothPbapService.MSG_SERVERSESSION_CLOSE;
+            msg.sendToTarget();
+            if (D) Log.d(TAG, "onClose(): msg MSG_SERVERSESSION_CLOSE sent out.");
+        }
+    }
+
+    @Override
+    public int onGet(final Operation op) {
+        HeaderSet request = null;
+        HeaderSet reply = new HeaderSet();
+        String type = "";
+        String name = "";
+        byte[] appParam = null;
+        AppParamValue appParamValue = new AppParamValue();
+        try {
+            request = op.getReceivedHeader();
+            type = (String)request.getHeader(HeaderSet.TYPE);
+            name = (String)request.getHeader(HeaderSet.NAME);
+            appParam = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
+        } catch (IOException e) {
+            Log.e(TAG, "request headers error");
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+
+        if (V) logHeader(request);
+        if (D) Log.d(TAG, "OnGet type is " + type + "; name is " + name);
+
+        if (type == null) {
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+        // Accroding to specification,the name header could be omitted such as
+        // sony erriccsonHBH-DS980
+
+        // For "x-bt/phonebook" and "x-bt/vcard-listing":
+        // if name == null, guess what carkit actually want from current path
+        // For "x-bt/vcard":
+        // We decide which kind of content client would like per current path
+
+        boolean validName = true;
+        if (TextUtils.isEmpty(name)) {
+            validName = false;
+        }
+
+        if (!validName || (validName && type.equals(TYPE_VCARD))) {
+            if (D) Log.d(TAG, "Guess what carkit actually want from current path (" +
+                    mCurrentPath + ")");
+
+            if (mCurrentPath.equals(PB_PATH)) {
+                appParamValue.needTag = ContentType.PHONEBOOK;
+            } else if (mCurrentPath.equals(ICH_PATH)) {
+                appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY;
+            } else if (mCurrentPath.equals(OCH_PATH)) {
+                appParamValue.needTag = ContentType.OUTGOING_CALL_HISTORY;
+            } else if (mCurrentPath.equals(MCH_PATH)) {
+                appParamValue.needTag = ContentType.MISSED_CALL_HISTORY;
+                mNeedNewMissedCallsNum = true;
+            } else if (mCurrentPath.equals(CCH_PATH)) {
+                appParamValue.needTag = ContentType.COMBINED_CALL_HISTORY;
+            } else {
+                Log.w(TAG, "mCurrentpath is not valid path!!!");
+                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            }
+            if (D) Log.v(TAG, "onGet(): appParamValue.needTag=" + appParamValue.needTag);
+        } else {
+            // Not support SIM card currently
+            if (name.contains(SIM1.subSequence(0, SIM1.length()))) {
+                Log.w(TAG, "Not support access SIM card info!");
+                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+            }
+
+            // we have weak name checking here to provide better
+            // compatibility with other devices,although unique name such as
+            // "pb.vcf" is required by SIG spec.
+            if (name.contains(PB.subSequence(0, PB.length()))) {
+                appParamValue.needTag = ContentType.PHONEBOOK;
+                if (D) Log.v(TAG, "download phonebook request");
+            } else if (name.contains(ICH.subSequence(0, ICH.length()))) {
+                appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY;
+                if (D) Log.v(TAG, "download incoming calls request");
+            } else if (name.contains(OCH.subSequence(0, OCH.length()))) {
+                appParamValue.needTag = ContentType.OUTGOING_CALL_HISTORY;
+                if (D) Log.v(TAG, "download outgoing calls request");
+            } else if (name.contains(MCH.subSequence(0, MCH.length()))) {
+                appParamValue.needTag = ContentType.MISSED_CALL_HISTORY;
+                mNeedNewMissedCallsNum = true;
+                if (D) Log.v(TAG, "download missed calls request");
+            } else if (name.contains(CCH.subSequence(0, CCH.length()))) {
+                appParamValue.needTag = ContentType.COMBINED_CALL_HISTORY;
+                if (D) Log.v(TAG, "download combined calls request");
+            } else {
+                Log.w(TAG, "Input name doesn't contain valid info!!!");
+                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            }
+        }
+
+        if (!parseApplicationParameter(appParam, appParamValue)) {
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+
+        // listing request
+        if (type.equals(TYPE_LISTING)) {
+            return pullVcardListing(appParam, appParamValue, reply, op);
+        }
+        // pull vcard entry request
+        else if (type.equals(TYPE_VCARD)) {
+            return pullVcardEntry(appParam, appParamValue, op, name, mCurrentPath);
+        }
+        // down load phone book request
+        else if (type.equals(TYPE_PB)) {
+            return pullPhonebook(appParam, appParamValue, reply, op, name);
+        } else {
+            Log.w(TAG, "unknown type request!!!");
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+    }
+
+    /** check whether path is legal */
+    private final boolean isLegalPath(final String str) {
+        if (str.length() == 0) {
+            return true;
+        }
+        for (int i = 0; i < LEGAL_PATH.length; i++) {
+            if (str.equals(LEGAL_PATH[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private class AppParamValue {
+        public int maxListCount;
+
+        public int listStartOffset;
+
+        public String searchValue;
+
+        // Indicate which vCard parameter the search operation shall be carried
+        // out on. Can be "Name | Number | Sound", default value is "Name".
+        public String searchAttr;
+
+        // Indicate which sorting order shall be used for the
+        // <x-bt/vcard-listing> listing object.
+        // Can be "Alphabetical | Indexed | Phonetical", default value is
+        // "Indexed".
+        public String order;
+
+        public int needTag;
+
+        public boolean vcard21;
+
+        public AppParamValue() {
+            maxListCount = 0;
+            listStartOffset = 0;
+            searchValue = "";
+            searchAttr = "";
+            order = "";
+            needTag = 0x00;
+            vcard21 = true;
+        }
+
+        public void dump() {
+            Log.i(TAG, "maxListCount=" + maxListCount + " listStartOffset=" + listStartOffset
+                    + " searchValue=" + searchValue + " searchAttr=" + searchAttr + " needTag="
+                    + needTag + " vcard21=" + vcard21 + " order=" + order);
+        }
+    }
+
+    /** To parse obex application parameter */
+    private final boolean parseApplicationParameter(final byte[] appParam,
+            AppParamValue appParamValue) {
+        int i = 0;
+        boolean parseOk = true;
+        while (i < appParam.length) {
+            switch (appParam[i]) {
+                case ApplicationParameter.TRIPLET_TAGID.FILTER_TAGID:
+                    i += 2; // length and tag field in triplet
+                    i += ApplicationParameter.TRIPLET_LENGTH.FILTER_LENGTH;
+                    break;
+                case ApplicationParameter.TRIPLET_TAGID.ORDER_TAGID:
+                    i += 2; // length and tag field in triplet
+                    appParamValue.order = Byte.toString(appParam[i]);
+                    i += ApplicationParameter.TRIPLET_LENGTH.ORDER_LENGTH;
+                    break;
+                case ApplicationParameter.TRIPLET_TAGID.SEARCH_VALUE_TAGID:
+                    i += 1; // length field in triplet
+                    for (int k = 1; k <= appParam[i]; k++) {
+                        appParamValue.searchValue += Byte.toString(appParam[i + k]);
+                    }
+                    // length of search value is variable
+                    i += appParam[i];
+                    i += 1;
+                    break;
+                case ApplicationParameter.TRIPLET_TAGID.SEARCH_ATTRIBUTE_TAGID:
+                    i += 2;
+                    appParamValue.searchAttr = Byte.toString(appParam[i]);
+                    i += ApplicationParameter.TRIPLET_LENGTH.SEARCH_ATTRIBUTE_LENGTH;
+                    break;
+                case ApplicationParameter.TRIPLET_TAGID.MAXLISTCOUNT_TAGID:
+                    i += 2;
+                    if (appParam[i] == 0 && appParam[i + 1] == 0) {
+                        mNeedPhonebookSize = true;
+                    } else {
+                        int highValue = appParam[i] & 0xff;
+                        int lowValue = appParam[i + 1] & 0xff;
+                        appParamValue.maxListCount = highValue * 256 + lowValue;
+                    }
+                    i += ApplicationParameter.TRIPLET_LENGTH.MAXLISTCOUNT_LENGTH;
+                    break;
+                case ApplicationParameter.TRIPLET_TAGID.LISTSTARTOFFSET_TAGID:
+                    i += 2;
+                    int highValue = appParam[i] & 0xff;
+                    int lowValue = appParam[i + 1] & 0xff;
+                    appParamValue.listStartOffset = highValue * 256 + lowValue;
+                    i += ApplicationParameter.TRIPLET_LENGTH.LISTSTARTOFFSET_LENGTH;
+                    break;
+                case ApplicationParameter.TRIPLET_TAGID.FORMAT_TAGID:
+                    i += 2;// length field in triplet
+                    if (appParam[i] != 0) {
+                        appParamValue.vcard21 = false;
+                    }
+                    i += ApplicationParameter.TRIPLET_LENGTH.FORMAT_LENGTH;
+                    break;
+                default:
+                    parseOk = false;
+                    Log.e(TAG, "Parse Application Parameter error");
+                    break;
+            }
+        }
+
+        if (D) appParamValue.dump();
+
+        return parseOk;
+    }
+
+    /** Form and Send an XML format String to client for Phone book listing */
+    private final int sendVcardListingXml(final int type, final Operation op,
+            final int maxListCount, final int listStartOffset, final String searchValue,
+            String searchAttr) {
+        StringBuilder result = new StringBuilder();
+        int itemsFound = 0;
+        result.append("<?xml version=\"1.0\"?>");
+        result.append("<!DOCTYPE vcard-listing SYSTEM \"vcard-listing.dtd\">");
+        result.append("<vCard-listing version=\"1.0\">");
+
+        // Phonebook listing request
+        if (type == ContentType.PHONEBOOK) {
+            // begin of search by name
+            if (searchAttr.equals("0")) {
+                ArrayList<String> nameList = mVcardManager.getPhonebookNameList(mOrderBy );
+                int requestSize = nameList.size() >= maxListCount ? maxListCount : nameList.size();
+                int startPoint = listStartOffset;
+                int endPoint = startPoint + requestSize;
+                if (endPoint > nameList.size()) {
+                    endPoint = nameList.size();
+                }
+
+                if (D) Log.d(TAG, "search by name, size=" + requestSize + " offset=" +
+                        listStartOffset + " searchValue=" + searchValue);
+
+
+                // if searchValue if not set by client,provide the entire
+                // list by name
+                if (searchValue == null || searchValue.trim().length() == 0) {
+                    for (int j = startPoint; j < endPoint; j++) {
+                        result.append("<card handle=\"" + j + ".vcf\" name=\"" + nameList.get(j)
+                                + "\"" + "/>");
+                        itemsFound++;
+                    }
+                } else {
+                    for (int j = startPoint; j < endPoint; j++) {
+                        // only find the name which begins with the searchValue
+                        if (nameList.get(j).startsWith(searchValue.trim())) {
+                            // TODO: PCE not work with it
+                            itemsFound++;
+                            result.append("<card handle=\"" + j + ".vcf\" name=\""
+                                    + nameList.get(j) + "\"" + "/>");
+                        }
+                    }
+                }
+            }// end of search by name
+            // begin of search by number
+            else if (searchAttr.equals("1")) {
+                ArrayList<String> numberList = mVcardManager.getPhonebookNumberList();
+                int requestSize = numberList.size() >= maxListCount ? maxListCount : numberList
+                        .size();
+                int startPoint = listStartOffset;
+                int endPoint = startPoint + requestSize;
+                if (endPoint > numberList.size()) {
+                    endPoint = numberList.size();
+                }
+
+                if (D) Log.d(TAG, "search by number, size=" + requestSize + " offset="
+                            + listStartOffset + " searchValue=" + searchValue);
+
+                // if searchValue if not set by client,provide the entire
+                // list by number
+                if (searchValue == null || searchValue.trim().length() == 0) {
+                    for (int j = startPoint; j < endPoint; j++) {
+                        result.append("<card handle=\"" + j + ".vcf\" number=\""
+                                + numberList.get(j) + "\"" + "/>");
+                        itemsFound++;
+                    }
+                } else {
+                    for (int j = startPoint; j < endPoint; j++) {
+                        // only find the name which begins with the searchValue
+                        if (numberList.get(j).startsWith(searchValue.trim())) {
+                            itemsFound++;
+                            result.append("<card handle=\"" + j + ".vcf\" number=\""
+                                    + numberList.get(j) + "\"" + "/>");
+                        }
+                    }
+                }
+            }// end of search by number
+            else {
+                return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+            }
+        }
+        // Call history listing request
+        else {
+            ArrayList<String> nameList = mVcardManager.loadCallHistoryList(type);
+            int requestSize = nameList.size() >= maxListCount ? maxListCount : nameList.size();
+            int startPoint = listStartOffset;
+            int endPoint = startPoint + requestSize;
+            if (endPoint > nameList.size()) {
+                endPoint = nameList.size();
+            }
+            if (D) Log.d(TAG, "call log list, size=" + requestSize + " offset=" + listStartOffset);
+
+            for (int j = startPoint; j < endPoint; j++) {
+                // listing object begin with 1.vcf
+                result.append("<card handle=\"" + (j + 1) + ".vcf\" name=\"" + nameList.get(j)
+                        + "\"" + "/>");
+                itemsFound++;
+            }
+        }
+        result.append("</vCard-listing>");
+
+        if (V) Log.v(TAG, "itemsFound =" + itemsFound);
+
+        return pushBytes(op, result.toString());
+    }
+
+    /**
+     * Function to send obex header back to client such as get phonebook size
+     * request
+     */
+    private final int pushHeader(final Operation op, final HeaderSet reply) {
+        OutputStream outputStream = null;
+
+        if (D) Log.d(TAG, "Push Header");
+        if (D) Log.d(TAG, reply.toString());
+
+        int pushResult = ResponseCodes.OBEX_HTTP_OK;
+        try {
+            op.sendHeaders(reply);
+            outputStream = op.openOutputStream();
+            outputStream.flush();
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+            pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        } finally {
+            if (!closeStream(outputStream, op)) {
+                pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+            }
+        }
+        return pushResult;
+    }
+
+    /** Function to send vcard data to client */
+    private final int pushBytes(final Operation op, final String vcardString) {
+        if (vcardString == null) {
+            Log.w(TAG, "vcardString is null!");
+            return ResponseCodes.OBEX_HTTP_OK;
+        }
+
+        int vcardStringLen = vcardString.length();
+        if (D) Log.d(TAG, "Send Data: len=" + vcardStringLen);
+
+        OutputStream outputStream = null;
+        int pushResult = ResponseCodes.OBEX_HTTP_OK;
+        try {
+            outputStream = op.openOutputStream();
+        } catch (IOException e) {
+            Log.e(TAG, "open outputstrem failed" + e.toString());
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+
+        int position = 0;
+        long timestamp = 0;
+        int outputBufferSize = op.getMaxPacketSize();
+        if (V) Log.v(TAG, "outputBufferSize = " + outputBufferSize);
+        while (position != vcardStringLen) {
+            if (V) timestamp = System.currentTimeMillis();
+            int readLength = outputBufferSize;
+            if (vcardStringLen - position < outputBufferSize) {
+                readLength = vcardStringLen - position;
+            }
+            String subStr = vcardString.substring(position, position + readLength);
+            try {
+                outputStream.write(subStr.getBytes(), 0, readLength);
+            } catch (IOException e) {
+                Log.e(TAG, "write outputstrem failed" + e.toString());
+                pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                break;
+            }
+            if (V) {
+                Log.v(TAG, "Sending vcard String position = " + position + " readLength "
+                        + readLength + " bytes took " + (System.currentTimeMillis() - timestamp)
+                        + " ms");
+            }
+            position += readLength;
+        }
+
+        if (V) Log.v(TAG, "Send Data complete!");
+
+        if (!closeStream(outputStream, op)) {
+            pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+
+        return pushResult;
+    }
+
+    private final int handleAppParaForResponse(AppParamValue appParamValue, int size,
+            HeaderSet reply, final Operation op) {
+        byte[] misnum = new byte[1];
+        ApplicationParameter ap = new ApplicationParameter();
+
+        // In such case, PCE only want the number of index.
+        // So response not contain any Body header.
+        if (mNeedPhonebookSize) {
+            if (V) Log.v(TAG, "Need Phonebook size in response header.");
+            mNeedPhonebookSize = false;
+
+            byte[] pbsize = new byte[2];
+
+            pbsize[0] = (byte)((size / 256) & 0xff);// HIGH VALUE
+            pbsize[1] = (byte)((size % 256) & 0xff);// LOW VALUE
+            ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.PHONEBOOKSIZE_TAGID,
+                    ApplicationParameter.TRIPLET_LENGTH.PHONEBOOKSIZE_LENGTH, pbsize);
+
+            if (mNeedNewMissedCallsNum) {
+                int nmnum = size - mMissedCallSize;
+                mMissedCallSize = size;
+
+                nmnum = nmnum > 0 ? nmnum : 0;
+                misnum[0] = (byte)nmnum;
+                ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID,
+                        ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH, misnum);
+                if (D) Log.d(TAG, "handleAppParaForResponse(): mNeedNewMissedCallsNum=true,  num= "
+                            + nmnum);
+            }
+            reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+            if (D) Log.d(TAG, "Send back Phonebook size only, without body info! Size= " + size);
+
+            return pushHeader(op, reply);
+        }
+
+        // Only apply to "mch" download/listing.
+        // NewMissedCalls is used only in the response, together with Body
+        // header.
+        if (mNeedNewMissedCallsNum) {
+            if (V) Log.v(TAG, "Need new missed call num in response header.");
+            mNeedNewMissedCallsNum = false;
+
+            int nmnum = size - mMissedCallSize;
+            mMissedCallSize = size;
+
+            nmnum = nmnum > 0 ? nmnum : 0;
+            misnum[0] = (byte)nmnum;
+            ap.addAPPHeader(ApplicationParameter.TRIPLET_TAGID.NEWMISSEDCALLS_TAGID,
+                    ApplicationParameter.TRIPLET_LENGTH.NEWMISSEDCALLS_LENGTH, misnum);
+            reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+            if (D) Log.d(TAG, "handleAppParaForResponse(): mNeedNewMissedCallsNum=true,  num= "
+                        + nmnum);
+
+            // Only Specifies the headers, not write for now, will write to PCE
+            // together with Body
+            try {
+                op.sendHeaders(reply);
+            } catch (IOException e) {
+                Log.e(TAG, e.toString());
+                return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+            }
+        }
+        return NEED_SEND_BODY;
+    }
+
+    private final int pullVcardListing(byte[] appParam, AppParamValue appParamValue,
+            HeaderSet reply, final Operation op) {
+        String searchAttr = appParamValue.searchAttr.trim();
+
+        if (searchAttr == null || searchAttr.length() == 0) {
+            // If searchAttr is not set by PCE, set default value per spec.
+            appParamValue.searchAttr = "0";
+            if (D) Log.d(TAG, "searchAttr is not set by PCE, assume search by name by default");
+        } else if (!searchAttr.equals("0") && !searchAttr.equals("1")) {
+            Log.w(TAG, "search attr not supported");
+            if (searchAttr.equals("2")) {
+                // search by sound is not supported currently
+                Log.w(TAG, "do not support search by sound");
+                return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+            }
+            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+        } else {
+            Log.i(TAG, "searchAttr is valid: " + searchAttr);
+        }
+
+        int size = mVcardManager.getPhonebookSize(appParamValue.needTag);
+        int needSendBody = handleAppParaForResponse(appParamValue, size, reply, op);
+        if (needSendBody != NEED_SEND_BODY) {
+            return needSendBody;
+        }
+
+        if (size == 0) {
+            if (V) Log.v(TAG, "PhonebookSize is 0, return.");
+            return ResponseCodes.OBEX_HTTP_OK;
+        }
+
+        String orderPara = appParamValue.order.trim();
+        if (TextUtils.isEmpty(orderPara)) {
+            // If order parameter is not set by PCE, set default value per spec.
+            appParamValue.order = "0";
+            if (D) Log.d(TAG, "Order parameter is not set by PCE. " +
+                       "Assume order by 'Indexed' by default");
+        } else if (!orderPara.equals("0") && !orderPara.equals("1")) {
+            if (V) Log.v(TAG, "Order parameter is not supported: " + appParamValue.order);
+            if (orderPara.equals("2")) {
+                // Order by sound is not supported currently
+                Log.w(TAG, "Do not support order by sound");
+                return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+            }
+            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+        } else {
+            Log.i(TAG, "Order parameter is valid: " + orderPara);
+        }
+
+        if (orderPara.equals("0")) {
+            mOrderBy = ORDER_BY_INDEXED;
+        } else if (orderPara.equals("1")) {
+            mOrderBy = ORDER_BY_ALPHABETICAL;
+        }
+
+        int sendResult = sendVcardListingXml(appParamValue.needTag, op, appParamValue.maxListCount,
+                appParamValue.listStartOffset, appParamValue.searchValue,
+                appParamValue.searchAttr);
+        return sendResult;
+    }
+
+    private final int pullVcardEntry(byte[] appParam, AppParamValue appParamValue,
+            final Operation op, final String name, final String current_path) {
+        if (name == null || name.length() < VCARD_NAME_SUFFIX_LENGTH) {
+            if (D) Log.d(TAG, "Name is Null, or the length of name < 5 !");
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+        String strIndex = name.substring(0, name.length() - VCARD_NAME_SUFFIX_LENGTH + 1);
+        int intIndex = 0;
+        if (strIndex.trim().length() != 0) {
+            try {
+                intIndex = Integer.parseInt(strIndex);
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "catch number format exception " + e.toString());
+                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            }
+        }
+
+        int size = mVcardManager.getPhonebookSize(appParamValue.needTag);
+        if (size == 0) {
+            if (V) Log.v(TAG, "PhonebookSize is 0, return.");
+            return ResponseCodes.OBEX_HTTP_OK;
+        }
+
+        boolean vcard21 = appParamValue.vcard21;
+        if (appParamValue.needTag == 0) {
+            Log.w(TAG, "wrong path!");
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        } else if (appParamValue.needTag == ContentType.PHONEBOOK) {
+            if (intIndex < 0 || intIndex >= size) {
+                Log.w(TAG, "The requested vcard is not acceptable! name= " + name);
+                return ResponseCodes.OBEX_HTTP_OK;
+            } else if (intIndex == 0) {
+                // For PB_PATH, 0.vcf is the phone number of this phone.
+                String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21);
+                return pushBytes(op, ownerVcard);
+            } else {
+                return mVcardManager.composeAndSendPhonebookOneVcard(op, intIndex, vcard21, null,
+                        mOrderBy );
+            }
+        } else {
+            if (intIndex <= 0 || intIndex > size) {
+                Log.w(TAG, "The requested vcard is not acceptable! name= " + name);
+                return ResponseCodes.OBEX_HTTP_OK;
+            }
+            // For others (ich/och/cch/mch), 0.vcf is meaningless, and must
+            // begin from 1.vcf
+            if (intIndex >= 1) {
+                return mVcardManager.composeAndSendCallLogVcards(appParamValue.needTag, op,
+                        intIndex, intIndex, vcard21);
+            }
+        }
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    private final int pullPhonebook(byte[] appParam, AppParamValue appParamValue, HeaderSet reply,
+            final Operation op, final String name) {
+        // code start for passing PTS3.2 TC_PSE_PBD_BI_01_C
+        if (name != null) {
+            int dotIndex = name.indexOf(".");
+            String vcf = "vcf";
+            if (dotIndex >= 0 && dotIndex <= name.length()) {
+                if (name.regionMatches(dotIndex + 1, vcf, 0, vcf.length()) == false) {
+                    Log.w(TAG, "name is not .vcf");
+                    return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+                }
+            }
+        } // code end for passing PTS3.2 TC_PSE_PBD_BI_01_C
+
+        int pbSize = mVcardManager.getPhonebookSize(appParamValue.needTag);
+        int needSendBody = handleAppParaForResponse(appParamValue, pbSize, reply, op);
+        if (needSendBody != NEED_SEND_BODY) {
+            return needSendBody;
+        }
+
+        if (pbSize == 0) {
+            if (V) Log.v(TAG, "PhonebookSize is 0, return.");
+            return ResponseCodes.OBEX_HTTP_OK;
+        }
+
+        int requestSize = pbSize >= appParamValue.maxListCount ? appParamValue.maxListCount
+                : pbSize;
+        int startPoint = appParamValue.listStartOffset;
+        if (startPoint < 0 || startPoint >= pbSize) {
+            Log.w(TAG, "listStartOffset is not correct! " + startPoint);
+            return ResponseCodes.OBEX_HTTP_OK;
+        }
+
+        int endPoint = startPoint + requestSize - 1;
+        if (endPoint > pbSize - 1) {
+            endPoint = pbSize - 1;
+        }
+        if (D) Log.d(TAG, "pullPhonebook(): requestSize=" + requestSize + " startPoint=" +
+                startPoint + " endPoint=" + endPoint);
+
+        String result = null;
+        boolean vcard21 = appParamValue.vcard21;
+        if (appParamValue.needTag == BluetoothPbapObexServer.ContentType.PHONEBOOK) {
+            if (startPoint == 0) {
+                String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21);
+                if (endPoint == 0) {
+                    return pushBytes(op, ownerVcard);
+                } else {
+                    return mVcardManager.composeAndSendPhonebookVcards(op, 1, endPoint, vcard21,
+                            ownerVcard);
+                }
+            } else {
+                return mVcardManager.composeAndSendPhonebookVcards(op, startPoint, endPoint,
+                        vcard21, null);
+            }
+        } else {
+            return mVcardManager.composeAndSendCallLogVcards(appParamValue.needTag, op,
+                    startPoint + 1, endPoint + 1, vcard21);
+        }
+    }
+
+    public static boolean closeStream(final OutputStream out, final Operation op) {
+        boolean returnvalue = true;
+        try {
+            if (out != null) {
+                out.close();
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "outputStream close failed" + e.toString());
+            returnvalue = false;
+        }
+        try {
+            if (op != null) {
+                op.close();
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "operation close failed" + e.toString());
+            returnvalue = false;
+        }
+        return returnvalue;
+    }
+
+    // Reserved for future use. In case PSE challenge PCE and PCE input wrong
+    // session key.
+    public final void onAuthenticationFailure(final byte[] userName) {
+    }
+
+    public static final String createSelectionPara(final int type) {
+        String selection = null;
+        switch (type) {
+            case ContentType.INCOMING_CALL_HISTORY:
+                selection = Calls.TYPE + "=" + CallLog.Calls.INCOMING_TYPE;
+                break;
+            case ContentType.OUTGOING_CALL_HISTORY:
+                selection = Calls.TYPE + "=" + CallLog.Calls.OUTGOING_TYPE;
+                break;
+            case ContentType.MISSED_CALL_HISTORY:
+                selection = Calls.TYPE + "=" + CallLog.Calls.MISSED_TYPE;
+                break;
+            default:
+                break;
+        }
+        if (V) Log.v(TAG, "Call log selection: " + selection);
+        return selection;
+    }
+
+    public static final void logHeader(HeaderSet hs) {
+        Log.v(TAG, "Dumping HeaderSet " + hs.toString());
+        try {
+
+            Log.v(TAG, "COUNT : " + hs.getHeader(HeaderSet.COUNT));
+            Log.v(TAG, "NAME : " + hs.getHeader(HeaderSet.NAME));
+            Log.v(TAG, "TYPE : " + hs.getHeader(HeaderSet.TYPE));
+            Log.v(TAG, "LENGTH : " + hs.getHeader(HeaderSet.LENGTH));
+            Log.v(TAG, "TIME_ISO_8601 : " + hs.getHeader(HeaderSet.TIME_ISO_8601));
+            Log.v(TAG, "TIME_4_BYTE : " + hs.getHeader(HeaderSet.TIME_4_BYTE));
+            Log.v(TAG, "DESCRIPTION : " + hs.getHeader(HeaderSet.DESCRIPTION));
+            Log.v(TAG, "TARGET : " + hs.getHeader(HeaderSet.TARGET));
+            Log.v(TAG, "HTTP : " + hs.getHeader(HeaderSet.HTTP));
+            Log.v(TAG, "WHO : " + hs.getHeader(HeaderSet.WHO));
+            Log.v(TAG, "OBJECT_CLASS : " + hs.getHeader(HeaderSet.OBJECT_CLASS));
+            Log.v(TAG, "APPLICATION_PARAMETER : " + hs.getHeader(HeaderSet.APPLICATION_PARAMETER));
+        } catch (IOException e) {
+            Log.e(TAG, "dump HeaderSet error " + e);
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java b/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java
new file mode 100644
index 0000000..b450376
--- /dev/null
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapReceiver.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.pbap;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class BluetoothPbapReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "BluetoothPbapReceiver";
+
+    private static final boolean V = BluetoothPbapService.VERBOSE;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (V) Log.v(TAG, "PbapReceiver onReceive: " + intent.getAction());
+
+        Intent in = new Intent();
+        in.putExtras(intent);
+        in.setClass(context, BluetoothPbapService.class);
+        String action = intent.getAction();
+        in.putExtra("action", action);
+        boolean startService = true;
+        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+            in.putExtra(BluetoothAdapter.EXTRA_STATE, state);
+            if ((state == BluetoothAdapter.STATE_TURNING_ON)
+                    || (state == BluetoothAdapter.STATE_TURNING_OFF)) {
+                startService = false;
+            }
+        }
+        if (startService) {
+            context.startService(in);
+        }
+    }
+}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapRfcommTransport.java b/src/com/android/bluetooth/pbap/BluetoothPbapRfcommTransport.java
new file mode 100644
index 0000000..cb9ec5d
--- /dev/null
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapRfcommTransport.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.pbap;
+
+import android.bluetooth.BluetoothSocket;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.obex.ObexTransport;
+
+public class BluetoothPbapRfcommTransport implements ObexTransport {
+    private BluetoothSocket mSocket = null;
+
+    public BluetoothPbapRfcommTransport(BluetoothSocket rfs) {
+        super();
+        this.mSocket = rfs;
+    }
+
+    public void close() throws IOException {
+        mSocket.close();
+    }
+
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return mSocket.getInputStream();
+    }
+
+    public OutputStream openOutputStream() throws IOException {
+        return mSocket.getOutputStream();
+    }
+
+    public void connect() throws IOException {
+    }
+
+    public void create() throws IOException {
+    }
+
+    public void disconnect() throws IOException {
+    }
+
+    public void listen() throws IOException {
+    }
+
+    public boolean isConnected() throws IOException {
+        return true;
+    }
+
+}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/src/com/android/bluetooth/pbap/BluetoothPbapService.java
new file mode 100644
index 0000000..26ac61a
--- /dev/null
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapService.java
@@ -0,0 +1,744 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.pbap;
+
+import com.android.bluetooth.R;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPbap;
+import android.bluetooth.BluetoothSocket;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.IBluetoothPbap;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.PowerManager;
+import android.provider.ContactsContract.RawContacts;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.obex.ServerSession;
+
+public class BluetoothPbapService extends Service {
+    private static final String TAG = "BluetoothPbapService";
+
+    /**
+     * To enable PBAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
+     * restart com.android.bluetooth process. only enable DEBUG log:
+     * "setprop log.tag.BluetoothPbapService DEBUG"; enable both VERBOSE and
+     * DEBUG log: "setprop log.tag.BluetoothPbapService VERBOSE"
+     */
+
+    public static final boolean DEBUG = false;
+
+    public static final boolean VERBOSE = false;
+
+    /**
+     * Intent indicating incoming connection request which is sent to
+     * BluetoothPbapActivity
+     */
+    public static final String ACCESS_REQUEST_ACTION = "com.android.bluetooth.pbap.accessrequest";
+
+    /**
+     * Intent indicating incoming connection request accepted by user which is
+     * sent from BluetoothPbapActivity
+     */
+    public static final String ACCESS_ALLOWED_ACTION = "com.android.bluetooth.pbap.accessallowed";
+
+    /**
+     * Intent indicating incoming connection request denied by user which is
+     * sent from BluetoothPbapActivity
+     */
+    public static final String ACCESS_DISALLOWED_ACTION =
+            "com.android.bluetooth.pbap.accessdisallowed";
+
+    /**
+     * Intent indicating incoming obex authentication request which is from
+     * PCE(Carkit)
+     */
+    public static final String AUTH_CHALL_ACTION = "com.android.bluetooth.pbap.authchall";
+
+    /**
+     * Intent indicating obex session key input complete by user which is sent
+     * from BluetoothPbapActivity
+     */
+    public static final String AUTH_RESPONSE_ACTION = "com.android.bluetooth.pbap.authresponse";
+
+    /**
+     * Intent indicating user canceled obex authentication session key input
+     * which is sent from BluetoothPbapActivity
+     */
+    public static final String AUTH_CANCELLED_ACTION = "com.android.bluetooth.pbap.authcancelled";
+
+    /**
+     * Intent indicating timeout for user confirmation, which is sent to
+     * BluetoothPbapActivity
+     */
+    public static final String USER_CONFIRM_TIMEOUT_ACTION =
+            "com.android.bluetooth.pbap.userconfirmtimeout";
+
+    /**
+     * Intent Extra name indicating always allowed which is sent from
+     * BluetoothPbapActivity
+     */
+    public static final String EXTRA_ALWAYS_ALLOWED = "com.android.bluetooth.pbap.alwaysallowed";
+
+    /**
+     * Intent Extra name indicating session key which is sent from
+     * BluetoothPbapActivity
+     */
+    public static final String EXTRA_SESSION_KEY = "com.android.bluetooth.pbap.sessionkey";
+
+    public static final String THIS_PACKAGE_NAME = "com.android.bluetooth";
+
+    public static final int MSG_SERVERSESSION_CLOSE = 5000;
+
+    public static final int MSG_SESSION_ESTABLISHED = 5001;
+
+    public static final int MSG_SESSION_DISCONNECTED = 5002;
+
+    public static final int MSG_OBEX_AUTH_CHALL = 5003;
+
+    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+
+    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
+
+    private static final int START_LISTENER = 1;
+
+    private static final int USER_TIMEOUT = 2;
+
+    private static final int AUTH_TIMEOUT = 3;
+
+    private static final int PORT_NUM = 19;
+
+    private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
+
+    private static final int TIME_TO_WAIT_VALUE = 6000;
+
+    // Ensure not conflict with Opp notification ID
+    private static final int NOTIFICATION_ID_ACCESS = -1000001;
+
+    private static final int NOTIFICATION_ID_AUTH = -1000002;
+
+    private PowerManager.WakeLock mWakeLock = null;
+
+    private BluetoothAdapter mAdapter;
+
+    private SocketAcceptThread mAcceptThread = null;
+
+    private BluetoothPbapAuthenticator mAuth = null;
+
+    private BluetoothPbapObexServer mPbapServer;
+
+    private ServerSession mServerSession = null;
+
+    private BluetoothServerSocket mServerSocket = null;
+
+    private BluetoothSocket mConnSocket = null;
+
+    private BluetoothDevice mRemoteDevice = null;
+
+    private static String sLocalPhoneNum = null;
+
+    private static String sLocalPhoneName = null;
+
+    private static String sRemoteDeviceName = null;
+
+    private boolean mHasStarted = false;
+
+    private volatile boolean mInterrupted;
+
+    private int mState;
+
+    private int mStartId = -1;
+
+    public BluetoothPbapService() {
+        mState = BluetoothPbap.STATE_DISCONNECTED;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (VERBOSE) Log.v(TAG, "Pbap Service onCreate");
+
+        mInterrupted = false;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        if (!mHasStarted) {
+            mHasStarted = true;
+            if (VERBOSE) Log.v(TAG, "Starting PBAP service");
+
+            int state = mAdapter.getState();
+            if (state == BluetoothAdapter.STATE_ON) {
+                mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
+                        .obtainMessage(START_LISTENER), TIME_TO_WAIT_VALUE);
+            }
+        }
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (VERBOSE) Log.v(TAG, "Pbap Service onStartCommand");
+        int retCode = super.onStartCommand(intent, flags, startId);
+        if (retCode == START_STICKY) {
+            mStartId = startId;
+            if (mAdapter == null) {
+                Log.w(TAG, "Stopping BluetoothPbapService: "
+                        + "device does not have BT or device is not ready");
+                // Release all resources
+                closeService();
+            } else {
+                // No need to handle the null intent case, because we have
+                // all restart work done in onCreate()
+                if (intent != null) {
+                    parseIntent(intent);
+                }
+            }
+        }
+        return retCode;
+    }
+
+    // process the intent from receiver
+    private void parseIntent(final Intent intent) {
+        String action = intent.getStringExtra("action");
+        if (VERBOSE) Log.v(TAG, "action: " + action);
+
+        int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+        boolean removeTimeoutMsg = true;
+        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+            removeTimeoutMsg = false;
+            if (state == BluetoothAdapter.STATE_OFF) {
+                // Release all resources
+                closeService();
+            }
+        } else if (action.equals(ACCESS_ALLOWED_ACTION)) {
+            if (intent.getBooleanExtra(EXTRA_ALWAYS_ALLOWED, false)) {
+                boolean result = mRemoteDevice.setTrust(true);
+                if (VERBOSE) Log.v(TAG, "setTrust() result=" + result);
+            }
+            try {
+                if (mConnSocket != null) {
+                    startObexServerSession();
+                } else {
+                    stopObexServerSession();
+                }
+            } catch (IOException ex) {
+                Log.e(TAG, "Caught the error: " + ex.toString());
+            }
+        } else if (action.equals(ACCESS_DISALLOWED_ACTION)) {
+            stopObexServerSession();
+        } else if (action.equals(AUTH_RESPONSE_ACTION)) {
+            String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
+            notifyAuthKeyInput(sessionkey);
+        } else if (action.equals(AUTH_CANCELLED_ACTION)) {
+            notifyAuthCancelled();
+        } else {
+            removeTimeoutMsg = false;
+        }
+
+        if (removeTimeoutMsg) {
+            mSessionStatusHandler.removeMessages(USER_TIMEOUT);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (VERBOSE) Log.v(TAG, "Pbap Service onDestroy");
+
+        super.onDestroy();
+        setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
+        if (mWakeLock != null) {
+            mWakeLock.release();
+            mWakeLock = null;
+        }
+        closeService();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (VERBOSE) Log.v(TAG, "Pbap Service onBind");
+        return mBinder;
+    }
+
+    private void startRfcommSocketListener() {
+        if (VERBOSE) Log.v(TAG, "Pbap Service startRfcommSocketListener");
+
+        if (mServerSocket == null) {
+            if (!initSocket()) {
+                closeService();
+                return;
+            }
+        }
+        if (mAcceptThread == null) {
+            mAcceptThread = new SocketAcceptThread();
+            mAcceptThread.setName("BluetoothPbapAcceptThread");
+            mAcceptThread.start();
+        }
+    }
+
+    private final boolean initSocket() {
+        if (VERBOSE) Log.v(TAG, "Pbap Service initSocket");
+
+        boolean initSocketOK = true;
+        final int CREATE_RETRY_TIME = 10;
+
+        // It's possible that create will fail in some cases. retry for 10 times
+        for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
+            try {
+                // It is mandatory for PSE to support initiation of bonding and
+                // encryption.
+                mServerSocket = mAdapter.listenUsingRfcommOn(PORT_NUM);
+            } catch (IOException e) {
+                Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
+                initSocketOK = false;
+            }
+            if (!initSocketOK) {
+                synchronized (this) {
+                    try {
+                        if (VERBOSE) Log.v(TAG, "wait 3 seconds");
+                        Thread.sleep(3000);
+                    } catch (InterruptedException e) {
+                        Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
+                        mInterrupted = true;
+                    }
+                }
+            } else {
+                break;
+            }
+        }
+
+        if (initSocketOK) {
+            if (VERBOSE) Log.v(TAG, "Succeed to create listening socket on channel " + PORT_NUM);
+
+        } else {
+            Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
+        }
+        return initSocketOK;
+    }
+
+    private final void closeSocket(boolean server, boolean accept) throws IOException {
+        if (server == true) {
+            // Stop the possible trying to init serverSocket
+            mInterrupted = true;
+
+            if (mServerSocket != null) {
+                mServerSocket.close();
+            }
+        }
+
+        if (accept == true) {
+            if (mConnSocket != null) {
+                mConnSocket.close();
+            }
+        }
+    }
+
+    private final void closeService() {
+        if (VERBOSE) Log.v(TAG, "Pbap Service closeService");
+
+        try {
+            closeSocket(true, true);
+        } catch (IOException ex) {
+            Log.e(TAG, "CloseSocket error: " + ex);
+        }
+
+        if (mAcceptThread != null) {
+            try {
+                mAcceptThread.shutdown();
+                mAcceptThread.join();
+                mAcceptThread = null;
+            } catch (InterruptedException ex) {
+                Log.w(TAG, "mAcceptThread close error" + ex);
+            }
+        }
+        mServerSocket = null;
+        mConnSocket = null;
+
+        if (mServerSession != null) {
+            mServerSession.close();
+            mServerSession = null;
+        }
+
+        mHasStarted = false;
+        if (stopSelfResult(mStartId)) {
+            if (VERBOSE) Log.v(TAG, "successfully stopped pbap service");
+        }
+    }
+
+    private final void startObexServerSession() throws IOException {
+        if (VERBOSE) Log.v(TAG, "Pbap Service startObexServerSession");
+
+        // acquire the wakeLock before start Obex transaction thread
+        if (mWakeLock == null) {
+            PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    "StartingObexPbapTransaction");
+            mWakeLock.setReferenceCounted(false);
+            mWakeLock.acquire();
+        }
+        TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
+        if (tm != null) {
+            sLocalPhoneNum = tm.getLine1Number();
+            sLocalPhoneName = tm.getLine1AlphaTag();
+            if (TextUtils.isEmpty(sLocalPhoneName)) {
+                sLocalPhoneName = this.getString(R.string.localPhoneName);
+            }
+        }
+
+        mPbapServer = new BluetoothPbapObexServer(mSessionStatusHandler, this);
+        synchronized (this) {
+            mAuth = new BluetoothPbapAuthenticator(mSessionStatusHandler);
+            mAuth.setChallenged(false);
+            mAuth.setCancelled(false);
+        }
+        BluetoothPbapRfcommTransport transport = new BluetoothPbapRfcommTransport(mConnSocket);
+        mServerSession = new ServerSession(transport, mPbapServer, mAuth);
+        setState(BluetoothPbap.STATE_CONNECTED);
+        if (VERBOSE) {
+            Log.v(TAG, "startObexServerSession() success!");
+        }
+    }
+
+    private void stopObexServerSession() {
+        if (VERBOSE) Log.v(TAG, "Pbap Service stopObexServerSession");
+
+        // Release the wake lock if obex transaction is over
+        if (mWakeLock != null) {
+            mWakeLock.release();
+            mWakeLock = null;
+        }
+
+        if (mServerSession != null) {
+            mServerSession.close();
+            mServerSession = null;
+        }
+
+        mAcceptThread = null;
+
+        try {
+            closeSocket(false, true);
+            mConnSocket = null;
+        } catch (IOException e) {
+            Log.e(TAG, "closeSocket error: " + e.toString());
+        }
+        // Last obex transaction is finished, we start to listen for incoming
+        // connection again
+        if (mAdapter.isEnabled()) {
+            startRfcommSocketListener();
+        }
+        setState(BluetoothPbap.STATE_DISCONNECTED);
+    }
+
+    private void notifyAuthKeyInput(final String key) {
+        synchronized (mAuth) {
+            if (key != null) {
+                mAuth.setSessionKey(key);
+            }
+            mAuth.setChallenged(true);
+            mAuth.notify();
+        }
+    }
+
+    private void notifyAuthCancelled() {
+        synchronized (mAuth) {
+            mAuth.setCancelled(true);
+            mAuth.notify();
+        }
+    }
+
+    /**
+     * A thread that runs in the background waiting for remote rfcomm
+     * connect.Once a remote socket connected, this thread shall be
+     * shutdown.When the remote disconnect,this thread shall run again waiting
+     * for next request.
+     */
+    private class SocketAcceptThread extends Thread {
+
+        private boolean stopped = false;
+
+        @Override
+        public void run() {
+            while (!stopped) {
+                try {
+                    mConnSocket = mServerSocket.accept();
+
+                    mRemoteDevice = mConnSocket.getRemoteDevice();
+                    if (mRemoteDevice == null) {
+                        Log.i(TAG, "getRemoteDevice() = null");
+                        break;
+                    }
+                    sRemoteDeviceName = mRemoteDevice.getName();
+                    // In case getRemoteName failed and return null
+                    if (TextUtils.isEmpty(sRemoteDeviceName)) {
+                        sRemoteDeviceName = getString(R.string.defaultname);
+                    }
+                    boolean trust = mRemoteDevice.getTrustState();
+                    if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust);
+
+                    if (trust) {
+                        try {
+                            if (VERBOSE) Log.v(TAG, "incomming connection accepted from: "
+                                + sRemoteDeviceName + " automatically as trusted device");
+                            startObexServerSession();
+                        } catch (IOException ex) {
+                            Log.e(TAG, "catch exception starting obex server session"
+                                    + ex.toString());
+                        }
+                    } else {
+                        createPbapNotification(ACCESS_REQUEST_ACTION);
+                        if (VERBOSE) Log.v(TAG, "incomming connection accepted from: "
+                                + sRemoteDeviceName);
+
+                        // In case car kit time out and try to use HFP for
+                        // phonebook
+                        // access, while UI still there waiting for user to
+                        // confirm
+                        mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
+                                .obtainMessage(USER_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
+                    }
+                    stopped = true; // job done ,close this thread;
+                } catch (IOException ex) {
+                    if (stopped) {
+                        break;
+                    }
+                    if (VERBOSE) Log.v(TAG, "Accept exception: " + ex.toString());
+                }
+            }
+        }
+
+        void shutdown() {
+            stopped = true;
+            interrupt();
+        }
+    }
+
+    private final Handler mSessionStatusHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
+
+            CharSequence tmpTxt;
+            switch (msg.what) {
+                case START_LISTENER:
+                    if (mAdapter.isEnabled()) {
+                        startRfcommSocketListener();
+                    } else {
+                        closeService();// release all resources
+                    }
+                    break;
+                case USER_TIMEOUT:
+                    Intent intent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
+                    sendBroadcast(intent);
+                    removePbapNotification(NOTIFICATION_ID_ACCESS);
+                    stopObexServerSession();
+                    break;
+                case AUTH_TIMEOUT:
+                    Intent i = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
+                    sendBroadcast(i);
+                    removePbapNotification(NOTIFICATION_ID_AUTH);
+                    notifyAuthCancelled();
+                    break;
+                case MSG_SERVERSESSION_CLOSE:
+                    stopObexServerSession();
+                    break;
+                case MSG_SESSION_ESTABLISHED:
+                    break;
+                case MSG_SESSION_DISCONNECTED:
+                    // case MSG_SERVERSESSION_CLOSE will handle ,so just skip
+                    break;
+                case MSG_OBEX_AUTH_CHALL:
+                    createPbapNotification(AUTH_CHALL_ACTION);
+                    mSessionStatusHandler.sendMessageDelayed(mSessionStatusHandler
+                            .obtainMessage(AUTH_TIMEOUT), USER_CONFIRM_TIMEOUT_VALUE);
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    private void setState(int state) {
+        setState(state, BluetoothPbap.RESULT_SUCCESS);
+    }
+
+    private synchronized void setState(int state, int result) {
+        if (state != mState) {
+            if (DEBUG) Log.d(TAG, "Pbap state " + mState + " -> " + state + ", result = "
+                    + result);
+            Intent intent = new Intent(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
+            intent.putExtra(BluetoothPbap.PBAP_PREVIOUS_STATE, mState);
+            mState = state;
+            intent.putExtra(BluetoothPbap.PBAP_STATE, mState);
+            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
+            sendBroadcast(intent, BLUETOOTH_PERM);
+        }
+    }
+
+    private void createPbapNotification(String action) {
+        Context context = getApplicationContext();
+
+        NotificationManager nm = (NotificationManager)context
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        // Create an intent triggered by clicking on the status icon.
+        Intent clickIntent = new Intent();
+        clickIntent.setClass(context, BluetoothPbapActivity.class);
+        clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        clickIntent.setAction(action);
+
+        // Create an intent triggered by clicking on the
+        // "Clear All Notifications" button
+        Intent deleteIntent = new Intent();
+        deleteIntent.setClass(context, BluetoothPbapReceiver.class);
+
+        Notification notification = null;
+        String name = getRemoteDeviceName();
+        if (action.equals(ACCESS_REQUEST_ACTION)) {
+            deleteIntent.setAction(ACCESS_DISALLOWED_ACTION);
+            notification = new Notification(android.R.drawable.stat_sys_data_bluetooth, context
+                    .getString(R.string.pbap_notif_ticker), System.currentTimeMillis());
+            notification.setLatestEventInfo(context, context.getString(R.string.pbap_notif_title),
+                    context.getString(R.string.pbap_notif_message, name), PendingIntent
+                            .getActivity(context, 0, clickIntent, 0));
+
+            notification.flags |= Notification.FLAG_AUTO_CANCEL;
+            notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
+            notification.defaults = Notification.DEFAULT_SOUND;
+            notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
+            nm.notify(NOTIFICATION_ID_ACCESS, notification);
+        } else if (action.equals(AUTH_CHALL_ACTION)) {
+            deleteIntent.setAction(AUTH_CANCELLED_ACTION);
+            notification = new Notification(android.R.drawable.stat_sys_data_bluetooth, context
+                    .getString(R.string.auth_notif_ticker), System.currentTimeMillis());
+            notification.setLatestEventInfo(context, context.getString(R.string.auth_notif_title),
+                    context.getString(R.string.auth_notif_message, name), PendingIntent
+                            .getActivity(context, 0, clickIntent, 0));
+
+            notification.flags |= Notification.FLAG_AUTO_CANCEL;
+            notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
+            notification.defaults = Notification.DEFAULT_SOUND;
+            notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
+            nm.notify(NOTIFICATION_ID_AUTH, notification);
+        }
+    }
+
+    private void removePbapNotification(int id) {
+        Context context = getApplicationContext();
+        NotificationManager nm = (NotificationManager)context
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        nm.cancel(id);
+    }
+
+    public static String getLocalPhoneNum() {
+        return sLocalPhoneNum;
+    }
+
+    public static String getLocalPhoneName() {
+        return sLocalPhoneName;
+    }
+
+    public static String getRemoteDeviceName() {
+        return sRemoteDeviceName;
+    }
+
+    /**
+     * Handlers for incoming service calls
+     */
+    private final IBluetoothPbap.Stub mBinder = new IBluetoothPbap.Stub() {
+        public int getState() {
+            if (DEBUG) Log.d(TAG, "getState " + mState);
+
+            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+            return mState;
+        }
+
+        public BluetoothDevice getClient() {
+            if (DEBUG) Log.d(TAG, "getClient" + mRemoteDevice);
+
+            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+            if (mState == BluetoothPbap.STATE_DISCONNECTED) {
+                return null;
+            }
+            return mRemoteDevice;
+        }
+
+        public boolean isConnected(BluetoothDevice device) {
+            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+            return mState == BluetoothPbap.STATE_CONNECTED && mRemoteDevice.equals(device);
+        }
+
+        public boolean connect(BluetoothDevice device) {
+            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                    "Need BLUETOOTH_ADMIN permission");
+            return false;
+        }
+
+        public void disconnect() {
+            if (DEBUG) Log.d(TAG, "disconnect");
+
+            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                    "Need BLUETOOTH_ADMIN permission");
+            synchronized (BluetoothPbapService.this) {
+                switch (mState) {
+                    case BluetoothPbap.STATE_CONNECTED:
+                        if (mServerSession != null) {
+                            mServerSession.close();
+                            mServerSession = null;
+                        }
+                        try {
+                            closeSocket(false, true);
+                            mConnSocket = null;
+                        } catch (IOException ex) {
+                            Log.e(TAG, "Caught the error: " + ex);
+                        }
+                        setState(BluetoothPbap.STATE_DISCONNECTED, BluetoothPbap.RESULT_CANCELED);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    };
+}
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
new file mode 100644
index 0000000..f16ce13
--- /dev/null
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
@@ -0,0 +1,560 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.pbap;
+
+import com.android.bluetooth.R;
+
+import android.net.Uri;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import android.database.Cursor;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.PhoneLookup;
+import android.pim.vcard.VCardComposer;
+import android.pim.vcard.VCardConfig;
+import android.pim.vcard.VCardComposer.OneEntryHandler;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+
+import javax.obex.ResponseCodes;
+import javax.obex.Operation;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+
+public class BluetoothPbapVcardManager {
+    private static final String TAG = "BluetoothPbapVcardManager";
+
+    private static final boolean V = BluetoothPbapService.VERBOSE;
+
+    private ContentResolver mResolver;
+
+    private Context mContext;
+
+    private StringBuilder mVcardResults = null;
+
+    static final String[] PHONES_PROJECTION = new String[] {
+            Data._ID, // 0
+            CommonDataKinds.Phone.TYPE, // 1
+            CommonDataKinds.Phone.LABEL, // 2
+            CommonDataKinds.Phone.NUMBER, // 3
+            Contacts.DISPLAY_NAME, // 4
+    };
+
+    private static final int ID_COLUMN_INDEX = 0;
+
+    private static final int PHONE_TYPE_COLUMN_INDEX = 1;
+
+    private static final int PHONE_LABEL_COLUMN_INDEX = 2;
+
+    private static final int PHOEN_NUMBER_COLUMN_INDEX = 3;
+
+    private static final int CONTACTS_DISPLAY_NAME_COLUMN_INDEX = 4;
+
+    static final String SORT_ORDER_PHONE_NUMBER = CommonDataKinds.Phone.NUMBER + " ASC";
+
+    static final String[] CONTACTS_PROJECTION = new String[] {
+            Contacts._ID, // 0
+            Contacts.DISPLAY_NAME, // 1
+    };
+
+    static final int CONTACTS_ID_COLUMN_INDEX = 0;
+
+    static final int CONTACTS_NAME_COLUMN_INDEX = 1;
+
+    // call histories use dynamic handles, and handles should order by date; the
+    // most recently one should be the first handle. In table "calls", _id and
+    // date are consistent in ordering, to implement simply, we sort by _id
+    // here.
+    static final String CALLLOG_SORT_ORDER = Calls._ID + " DESC";
+
+    private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
+
+    public BluetoothPbapVcardManager(final Context context) {
+        mContext = context;
+        mResolver = mContext.getContentResolver();
+    }
+
+    public final String getOwnerPhoneNumberVcard(final boolean vcardType21) {
+        VCardComposer composer = new VCardComposer(mContext);
+        String name = BluetoothPbapService.getLocalPhoneName();
+        String number = BluetoothPbapService.getLocalPhoneNum();
+        String vcard = composer.composeVCardForPhoneOwnNumber(Phone.TYPE_MOBILE, name, number,
+                vcardType21);
+        return vcard;
+    }
+
+    public final int getPhonebookSize(final int type) {
+        int size;
+        switch (type) {
+            case BluetoothPbapObexServer.ContentType.PHONEBOOK:
+                size = getContactsSize();
+                break;
+            default:
+                size = getCallHistorySize(type);
+                break;
+        }
+        if (V) Log.v(TAG, "getPhonebookSzie size = " + size + " type = " + type);
+        return size;
+    }
+
+    public final int getContactsSize() {
+        final Uri myUri = Contacts.CONTENT_URI;
+        int size = 0;
+        Cursor contactCursor = null;
+        try {
+            contactCursor = mResolver.query(myUri, null, CLAUSE_ONLY_VISIBLE, null, null);
+            if (contactCursor != null) {
+                size = contactCursor.getCount() + 1; // always has the 0.vcf
+            }
+        } finally {
+            if (contactCursor != null) {
+                contactCursor.close();
+            }
+        }
+        return size;
+    }
+
+    public final int getCallHistorySize(final int type) {
+        final Uri myUri = CallLog.Calls.CONTENT_URI;
+        String selection = BluetoothPbapObexServer.createSelectionPara(type);
+        int size = 0;
+        Cursor callCursor = null;
+        try {
+            callCursor = mResolver.query(myUri, null, selection, null,
+                    CallLog.Calls.DEFAULT_SORT_ORDER);
+            if (callCursor != null) {
+                size = callCursor.getCount();
+            }
+        } finally {
+            if (callCursor != null) {
+                callCursor.close();
+            }
+        }
+        return size;
+    }
+
+    public final ArrayList<String> loadCallHistoryList(final int type) {
+        final Uri myUri = CallLog.Calls.CONTENT_URI;
+        String selection = BluetoothPbapObexServer.createSelectionPara(type);
+        String[] projection = new String[] {
+                Calls.NUMBER, Calls.CACHED_NAME
+        };
+        final int CALLS_NUMBER_COLUMN_INDEX = 0;
+        final int CALLS_NAME_COLUMN_INDEX = 1;
+
+        Cursor callCursor = null;
+        ArrayList<String> list = new ArrayList<String>();
+        try {
+            callCursor = mResolver.query(myUri, projection, selection, null,
+                    CALLLOG_SORT_ORDER);
+            if (callCursor != null) {
+                for (callCursor.moveToFirst(); !callCursor.isAfterLast();
+                        callCursor.moveToNext()) {
+                    String name = callCursor.getString(CALLS_NAME_COLUMN_INDEX);
+                    if (TextUtils.isEmpty(name)) {
+                        // name not found,use number instead
+                        name = callCursor.getString(CALLS_NUMBER_COLUMN_INDEX);
+                    }
+                    list.add(name);
+                }
+            }
+        } finally {
+            if (callCursor != null) {
+                callCursor.close();
+            }
+        }
+        return list;
+    }
+
+    public final ArrayList<String> getPhonebookNameList(final int orderByWhat) {
+        ArrayList<String> nameList = new ArrayList<String>();
+        nameList.add(BluetoothPbapService.getLocalPhoneName());
+
+        final Uri myUri = Contacts.CONTENT_URI;
+        Cursor contactCursor = null;
+        try {
+            if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_INDEXED) {
+                contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE,
+                        null, Contacts._ID);
+            } else if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_ALPHABETICAL) {
+                contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE,
+                        null, Contacts.DISPLAY_NAME);
+            }
+            if (contactCursor != null) {
+                for (contactCursor.moveToFirst(); !contactCursor.isAfterLast(); contactCursor
+                        .moveToNext()) {
+                    String name = contactCursor.getString(CONTACTS_NAME_COLUMN_INDEX);
+                    if (TextUtils.isEmpty(name)) {
+                        name = mContext.getString(android.R.string.unknownName);
+                    }
+                    nameList.add(name);
+                }
+            }
+        } finally {
+            if (contactCursor != null) {
+                contactCursor.close();
+            }
+        }
+        return nameList;
+    }
+
+    public final ArrayList<String> getPhonebookNumberList() {
+        ArrayList<String> numberList = new ArrayList<String>();
+        numberList.add(BluetoothPbapService.getLocalPhoneNum());
+
+        final Uri myUri = Phone.CONTENT_URI;
+        Cursor phoneCursor = null;
+        try {
+            phoneCursor = mResolver.query(myUri, PHONES_PROJECTION, CLAUSE_ONLY_VISIBLE, null,
+                    SORT_ORDER_PHONE_NUMBER);
+            if (phoneCursor != null) {
+                for (phoneCursor.moveToFirst(); !phoneCursor.isAfterLast(); phoneCursor
+                        .moveToNext()) {
+                    String number = phoneCursor.getString(PHOEN_NUMBER_COLUMN_INDEX);
+                    if (TextUtils.isEmpty(number)) {
+                        number = mContext.getString(R.string.defaultnumber);
+                    }
+                    numberList.add(number);
+                }
+            }
+        } finally {
+            if (phoneCursor != null) {
+                phoneCursor.close();
+            }
+        }
+        return numberList;
+    }
+
+    public final int composeAndSendCallLogVcards(final int type, final Operation op,
+            final int startPoint, final int endPoint, final boolean vcardType21) {
+        if (startPoint < 1 || startPoint > endPoint) {
+            Log.e(TAG, "internal error: startPoint or endPoint is not correct.");
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+        String typeSelection = BluetoothPbapObexServer.createSelectionPara(type);
+
+        final Uri myUri = CallLog.Calls.CONTENT_URI;
+        final String[] CALLLOG_PROJECTION = new String[] {
+            CallLog.Calls._ID, // 0
+        };
+        final int ID_COLUMN_INDEX = 0;
+
+        Cursor callsCursor = null;
+        long startPointId = 0;
+        long endPointId = 0;
+        try {
+            // Need test to see if order by _ID is ok here, or by date?
+            callsCursor = mResolver.query(myUri, CALLLOG_PROJECTION, typeSelection, null,
+                    CALLLOG_SORT_ORDER);
+            if (callsCursor != null) {
+                callsCursor.moveToPosition(startPoint - 1);
+                startPointId = callsCursor.getLong(ID_COLUMN_INDEX);
+                if (V) Log.v(TAG, "Call Log query startPointId = " + startPointId);
+                if (startPoint == endPoint) {
+                    endPointId = startPointId;
+                } else {
+                    callsCursor.moveToPosition(endPoint - 1);
+                    endPointId = callsCursor.getLong(ID_COLUMN_INDEX);
+                }
+                if (V) Log.v(TAG, "Call log query endPointId = " + endPointId);
+            }
+        } finally {
+            if (callsCursor != null) {
+                callsCursor.close();
+            }
+        }
+
+        String recordSelection;
+        if (startPoint == endPoint) {
+            recordSelection = Calls._ID + "=" + startPointId;
+        } else {
+            // The query to call table is by "_id DESC" order, so change
+            // correspondingly.
+            recordSelection = Calls._ID + ">=" + endPointId + " AND " + Calls._ID + "<="
+                    + startPointId;
+        }
+
+        String selection;
+        if (typeSelection == null) {
+            selection = recordSelection;
+        } else {
+            selection = "(" + typeSelection + ") AND (" + recordSelection + ")";
+        }
+
+        if (V) Log.v(TAG, "Call log query selection is: " + selection);
+
+        return composeAndSendVCards(op, selection, vcardType21, null, false);
+    }
+
+    public final int composeAndSendPhonebookVcards(final Operation op, final int startPoint,
+            final int endPoint, final boolean vcardType21, String ownerVCard) {
+        if (startPoint < 1 || startPoint > endPoint) {
+            Log.e(TAG, "internal error: startPoint or endPoint is not correct.");
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+        final Uri myUri = Contacts.CONTENT_URI;
+
+        Cursor contactCursor = null;
+        long startPointId = 0;
+        long endPointId = 0;
+        try {
+            contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE, null,
+                    Contacts._ID);
+            if (contactCursor != null) {
+                contactCursor.moveToPosition(startPoint - 1);
+                startPointId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
+                if (V) Log.v(TAG, "Query startPointId = " + startPointId);
+                if (startPoint == endPoint) {
+                    endPointId = startPointId;
+                } else {
+                    contactCursor.moveToPosition(endPoint - 1);
+                    endPointId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
+                }
+                if (V) Log.v(TAG, "Query endPointId = " + endPointId);
+            }
+        } finally {
+            if (contactCursor != null) {
+                contactCursor.close();
+            }
+        }
+
+        final String selection;
+        if (startPoint == endPoint) {
+            selection = Contacts._ID + "=" + startPointId + " AND " + CLAUSE_ONLY_VISIBLE;
+        } else {
+            selection = Contacts._ID + ">=" + startPointId + " AND " + Contacts._ID + "<="
+                    + endPointId + " AND " + CLAUSE_ONLY_VISIBLE;
+        }
+
+        if (V) Log.v(TAG, "Query selection is: " + selection);
+
+        return composeAndSendVCards(op, selection, vcardType21, ownerVCard, true);
+    }
+
+    public final int composeAndSendPhonebookOneVcard(final Operation op, final int offset,
+            final boolean vcardType21, String ownerVCard, int orderByWhat) {
+        if (offset < 1) {
+            Log.e(TAG, "Internal error: offset is not correct.");
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+        final Uri myUri = Contacts.CONTENT_URI;
+        Cursor contactCursor = null;
+        String selection = null;
+        long contactId = 0;
+        if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_INDEXED) {
+            try {
+                contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE,
+                        null, Contacts._ID);
+                if (contactCursor != null) {
+                    contactCursor.moveToPosition(offset - 1);
+                    contactId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
+                    if (V) Log.v(TAG, "Query startPointId = " + contactId);
+                }
+            } finally {
+                if (contactCursor != null) {
+                    contactCursor.close();
+                }
+            }
+        } else if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_ALPHABETICAL) {
+            try {
+                contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, CLAUSE_ONLY_VISIBLE,
+                        null, Contacts.DISPLAY_NAME);
+                if (contactCursor != null) {
+                    contactCursor.moveToPosition(offset - 1);
+                    contactId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
+                    if (V) Log.v(TAG, "Query startPointId = " + contactId);
+                }
+            } finally {
+                if (contactCursor != null) {
+                    contactCursor.close();
+                }
+            }
+        } else {
+            Log.e(TAG, "Parameter orderByWhat is not supported!");
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+        selection = Contacts._ID + "=" + contactId;
+
+        if (V) Log.v(TAG, "Query selection is: " + selection);
+
+        return composeAndSendVCards(op, selection, vcardType21, ownerVCard, true);
+    }
+
+    public final int composeAndSendVCards(final Operation op, final String selection,
+            final boolean vcardType21, String ownerVCard, boolean isContacts) {
+        long timestamp = 0;
+        if (V) timestamp = System.currentTimeMillis();
+
+        VCardComposer composer = null;
+        try {
+            // Currently only support Generic Vcard 2.1 and 3.0
+            final int vcardType;
+            if (vcardType21) {
+                vcardType = VCardConfig.VCARD_TYPE_V21_GENERIC;
+            } else {
+                vcardType = VCardConfig.VCARD_TYPE_V30_GENERIC;
+            }
+
+            final boolean careHandlerErrors = true;
+            final boolean needPhoto = false; //We disable photo for the time being
+            final boolean isCallLogComposer = !isContacts;
+
+            composer = new VCardComposer(mContext, vcardType, careHandlerErrors, isCallLogComposer,
+                                         needPhoto);
+
+            composer.addHandler(new HandlerForStringBuffer(op, ownerVCard));
+
+            if (!composer.init(selection, null)) {
+                return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+            }
+
+            while (!composer.isAfterLast()) {
+                if (!composer.createOneEntry()) {
+                    Log.e(TAG, "Failed to read a contact. Error reason: "
+                            + composer.getErrorReason());
+                    return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+                }
+            }
+        } finally {
+            if (composer != null) {
+                composer.terminate();
+            }
+        }
+
+        if (V) Log.v(TAG, "Total vcard composing and sending out takes "
+                    + (System.currentTimeMillis() - timestamp) + " ms");
+
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    /**
+     * Handler to emit VCard String to PCE once size grow to maxPacketSize.
+     */
+    public class HandlerForStringBuffer implements OneEntryHandler {
+        @SuppressWarnings("hiding")
+        private Operation operation;
+
+        private OutputStream outputStream;
+
+        private int maxPacketSize;
+
+        private String phoneOwnVCard = null;
+
+        public HandlerForStringBuffer(Operation op, String ownerVCard) {
+            operation = op;
+            maxPacketSize = operation.getMaxPacketSize();
+            if (V) Log.v(TAG, "getMaxPacketSize() = " + maxPacketSize);
+            if (ownerVCard != null) {
+                phoneOwnVCard = ownerVCard;
+                if (V) Log.v(TAG, "phone own number vcard:");
+                if (V) Log.v(TAG, phoneOwnVCard);
+            }
+        }
+
+        public boolean onInit(Context context) {
+            try {
+                outputStream = operation.openOutputStream();
+                mVcardResults = new StringBuilder();
+                if (phoneOwnVCard != null) {
+                    mVcardResults.append(phoneOwnVCard);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "open outputstrem failed" + e.toString());
+                return false;
+            }
+            if (V) Log.v(TAG, "openOutputStream() ok.");
+            return true;
+        }
+
+        public boolean onEntryCreated(String vcard) {
+            int vcardLen = vcard.length();
+            if (V) Log.v(TAG, "The length of this vcard is: " + vcardLen);
+
+            mVcardResults.append(vcard);
+            int vcardStringLen = mVcardResults.toString().length();
+            if (V) Log.v(TAG, "The length of this vcardResults is: " + vcardStringLen);
+
+            if (vcardStringLen >= maxPacketSize) {
+                long timestamp = 0;
+                int position = 0;
+
+                // Need while loop to handle the big vcard case
+                while (position < (vcardStringLen - maxPacketSize)) {
+                    if (V) timestamp = System.currentTimeMillis();
+
+                    String subStr = mVcardResults.toString().substring(position,
+                            position + maxPacketSize);
+                    try {
+                        outputStream.write(subStr.getBytes(), 0, maxPacketSize);
+                    } catch (IOException e) {
+                        Log.e(TAG, "write outputstrem failed" + e.toString());
+                        return false;
+                    }
+                    if (V) Log.v(TAG, "Sending vcard String " + maxPacketSize + " bytes took "
+                            + (System.currentTimeMillis() - timestamp) + " ms");
+
+                    position += maxPacketSize;
+                }
+                mVcardResults.delete(0, position);
+            }
+            return true;
+        }
+
+        public void onTerminate() {
+            // Send out last packet
+            String lastStr = mVcardResults.toString();
+            try {
+                outputStream.write(lastStr.getBytes(), 0, lastStr.length());
+            } catch (IOException e) {
+                Log.e(TAG, "write outputstrem failed" + e.toString());
+            }
+            if (V) Log.v(TAG, "Last packet sent out, sending process complete!");
+
+            if (!BluetoothPbapObexServer.closeStream(outputStream, operation)) {
+                if (V) Log.v(TAG, "CloseStream failed!");
+            } else {
+                if (V) Log.v(TAG, "CloseStream ok!");
+            }
+        }
+    }
+}