summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-07-29 14:57:05 -0700
committerJean-Baptiste Queru <jbq@google.com>2009-07-29 14:57:05 -0700
commit61e4248f8f6ae8a8f40550cc0800e5190cd1dc09 (patch)
tree07d964042985825b97f51b3744977c25b685b2f1 /packages
parent2af1b3db3d4f687d008db74b150f149e956b4bc6 (diff)
parenta8675f67e33bc7337d148358783b0fd138b501ff (diff)
downloadframeworks_base-61e4248f8f6ae8a8f40550cc0800e5190cd1dc09.zip
frameworks_base-61e4248f8f6ae8a8f40550cc0800e5190cd1dc09.tar.gz
frameworks_base-61e4248f8f6ae8a8f40550cc0800e5190cd1dc09.tar.bz2
merge from donut
Diffstat (limited to 'packages')
-rw-r--r--packages/SettingsProvider/etc/bookmarks.xml4
-rw-r--r--packages/SettingsProvider/res/values-cs/defaults.xml22
-rw-r--r--packages/SettingsProvider/res/values-cs/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-de/defaults.xml22
-rw-r--r--packages/SettingsProvider/res/values-de/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-es/defaults.xml22
-rw-r--r--packages/SettingsProvider/res/values-es/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-fr/defaults.xml22
-rw-r--r--packages/SettingsProvider/res/values-fr/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-it/defaults.xml22
-rw-r--r--packages/SettingsProvider/res/values-it/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-nl/defaults.xml22
-rw-r--r--packages/SettingsProvider/res/values-nl/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-pl/defaults.xml22
-rw-r--r--packages/SettingsProvider/res/values-pl/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-zh-rTW/defaults.xml22
-rw-r--r--packages/SettingsProvider/res/values-zh-rTW/strings.xml19
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java105
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java11
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java7
-rw-r--r--packages/SubscribedFeedsProvider/res/values-cs/strings.xml19
-rw-r--r--packages/SubscribedFeedsProvider/res/values-de/strings.xml19
-rw-r--r--packages/SubscribedFeedsProvider/res/values-es/strings.xml19
-rw-r--r--packages/SubscribedFeedsProvider/res/values-fr/strings.xml19
-rw-r--r--packages/SubscribedFeedsProvider/res/values-it/strings.xml19
-rw-r--r--packages/SubscribedFeedsProvider/res/values-nl/strings.xml19
-rw-r--r--packages/SubscribedFeedsProvider/res/values-pl/strings.xml19
-rw-r--r--packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml19
-rw-r--r--packages/TtsService/jni/android_tts_SynthProxy.cpp149
-rwxr-xr-xpackages/TtsService/src/android/tts/SynthProxy.java23
-rwxr-xr-xpackages/TtsService/src/android/tts/TtsService.java516
-rw-r--r--packages/VpnServices/res/values-cs/strings.xml25
-rw-r--r--packages/VpnServices/res/values-de/strings.xml25
-rw-r--r--packages/VpnServices/res/values-es/strings.xml25
-rw-r--r--packages/VpnServices/res/values-fr/strings.xml25
-rw-r--r--packages/VpnServices/res/values-it/strings.xml25
-rw-r--r--packages/VpnServices/res/values-nl/strings.xml25
-rw-r--r--packages/VpnServices/res/values-pl/strings.xml25
-rw-r--r--packages/VpnServices/res/values-zh-rTW/strings.xml25
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java64
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java3
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java4
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java1
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/VpnConnectingError.java35
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/VpnService.java144
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java23
46 files changed, 1387 insertions, 382 deletions
diff --git a/packages/SettingsProvider/etc/bookmarks.xml b/packages/SettingsProvider/etc/bookmarks.xml
index 5fb6608..5af416a 100644
--- a/packages/SettingsProvider/etc/bookmarks.xml
+++ b/packages/SettingsProvider/etc/bookmarks.xml
@@ -53,6 +53,6 @@
shortcut="s" />
<bookmark
package="com.google.android.youtube"
- class="com.google.android.youtube.HomePage"
+ class="com.google.android.youtube.HomeActivity"
shortcut="y" />
-</bookmarks> \ No newline at end of file
+</bookmarks>
diff --git a/packages/SettingsProvider/res/values-cs/defaults.xml b/packages/SettingsProvider/res/values-cs/defaults.xml
new file mode 100644
index 0000000..a7c01b3
--- /dev/null
+++ b/packages/SettingsProvider/res/values-cs/defaults.xml
@@ -0,0 +1,22 @@
+<?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="def_airplane_mode_radios">"mobil,bluetooth,wifi"</string>
+ <string name="def_location_providers_allowed">"gps"</string>
+ <!-- no translation found for def_backup_transport (6764822064303377157) -->
+ <skip />
+</resources>
diff --git a/packages/SettingsProvider/res/values-cs/strings.xml b/packages/SettingsProvider/res/values-cs/strings.xml
new file mode 100644
index 0000000..dc75a92
--- /dev/null
+++ b/packages/SettingsProvider/res/values-cs/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Paměť pro nastavení"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-de/defaults.xml b/packages/SettingsProvider/res/values-de/defaults.xml
new file mode 100644
index 0000000..f85d3f0
--- /dev/null
+++ b/packages/SettingsProvider/res/values-de/defaults.xml
@@ -0,0 +1,22 @@
+<?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="def_airplane_mode_radios">"Mobilfunk, Bluetooth, WLAN"</string>
+ <string name="def_location_providers_allowed">"GPS"</string>
+ <!-- no translation found for def_backup_transport (6764822064303377157) -->
+ <skip />
+</resources>
diff --git a/packages/SettingsProvider/res/values-de/strings.xml b/packages/SettingsProvider/res/values-de/strings.xml
new file mode 100644
index 0000000..50c8a14
--- /dev/null
+++ b/packages/SettingsProvider/res/values-de/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Einstellungsspeicher"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-es/defaults.xml b/packages/SettingsProvider/res/values-es/defaults.xml
new file mode 100644
index 0000000..a64805a
--- /dev/null
+++ b/packages/SettingsProvider/res/values-es/defaults.xml
@@ -0,0 +1,22 @@
+<?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="def_airplane_mode_radios">"móvil,bluetooth,wifi"</string>
+ <string name="def_location_providers_allowed">"gps"</string>
+ <!-- no translation found for def_backup_transport (6764822064303377157) -->
+ <skip />
+</resources>
diff --git a/packages/SettingsProvider/res/values-es/strings.xml b/packages/SettingsProvider/res/values-es/strings.xml
new file mode 100644
index 0000000..d30d195
--- /dev/null
+++ b/packages/SettingsProvider/res/values-es/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Almacenamiento de configuración"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-fr/defaults.xml b/packages/SettingsProvider/res/values-fr/defaults.xml
new file mode 100644
index 0000000..56334cc
--- /dev/null
+++ b/packages/SettingsProvider/res/values-fr/defaults.xml
@@ -0,0 +1,22 @@
+<?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="def_airplane_mode_radios">"cellulaire, Bluetooth, Wi-Fi"</string>
+ <string name="def_location_providers_allowed">"gps"</string>
+ <!-- no translation found for def_backup_transport (6764822064303377157) -->
+ <skip />
+</resources>
diff --git a/packages/SettingsProvider/res/values-fr/strings.xml b/packages/SettingsProvider/res/values-fr/strings.xml
new file mode 100644
index 0000000..686ec8b
--- /dev/null
+++ b/packages/SettingsProvider/res/values-fr/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Stockage des paramètres"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-it/defaults.xml b/packages/SettingsProvider/res/values-it/defaults.xml
new file mode 100644
index 0000000..19c0896
--- /dev/null
+++ b/packages/SettingsProvider/res/values-it/defaults.xml
@@ -0,0 +1,22 @@
+<?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="def_airplane_mode_radios">"cellulare,bluetooth,wifi"</string>
+ <string name="def_location_providers_allowed">"gps"</string>
+ <!-- no translation found for def_backup_transport (6764822064303377157) -->
+ <skip />
+</resources>
diff --git a/packages/SettingsProvider/res/values-it/strings.xml b/packages/SettingsProvider/res/values-it/strings.xml
new file mode 100644
index 0000000..29e462f
--- /dev/null
+++ b/packages/SettingsProvider/res/values-it/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Archiviazione impostazioni"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-nl/defaults.xml b/packages/SettingsProvider/res/values-nl/defaults.xml
new file mode 100644
index 0000000..625235a
--- /dev/null
+++ b/packages/SettingsProvider/res/values-nl/defaults.xml
@@ -0,0 +1,22 @@
+<?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="def_airplane_mode_radios">"mobiel,bluetooth,wifi"</string>
+ <string name="def_location_providers_allowed">"gps"</string>
+ <!-- no translation found for def_backup_transport (6764822064303377157) -->
+ <skip />
+</resources>
diff --git a/packages/SettingsProvider/res/values-nl/strings.xml b/packages/SettingsProvider/res/values-nl/strings.xml
new file mode 100644
index 0000000..b37b535
--- /dev/null
+++ b/packages/SettingsProvider/res/values-nl/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Opslagruimte voor instellingen"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-pl/defaults.xml b/packages/SettingsProvider/res/values-pl/defaults.xml
new file mode 100644
index 0000000..b60832e
--- /dev/null
+++ b/packages/SettingsProvider/res/values-pl/defaults.xml
@@ -0,0 +1,22 @@
+<?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="def_airplane_mode_radios">"komórka,bluetooth,wifi"</string>
+ <string name="def_location_providers_allowed">"gps"</string>
+ <!-- no translation found for def_backup_transport (6764822064303377157) -->
+ <skip />
+</resources>
diff --git a/packages/SettingsProvider/res/values-pl/strings.xml b/packages/SettingsProvider/res/values-pl/strings.xml
new file mode 100644
index 0000000..4ab1e91
--- /dev/null
+++ b/packages/SettingsProvider/res/values-pl/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Pamięć ustawień"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rTW/defaults.xml b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml
new file mode 100644
index 0000000..fdbba88
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zh-rTW/defaults.xml
@@ -0,0 +1,22 @@
+<?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="def_airplane_mode_radios">"手機,藍牙,wifi"</string>
+ <string name="def_location_providers_allowed">"gps"</string>
+ <!-- no translation found for def_backup_transport (6764822064303377157) -->
+ <skip />
+</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rTW/strings.xml b/packages/SettingsProvider/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..b24144a
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zh-rTW/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"設定儲存空間"</string>
+</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index b6bc8a5..2b36904 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -16,12 +16,16 @@
package com.android.providers.settings;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.EOFException;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.zip.CRC32;
import android.backup.BackupDataInput;
import android.backup.BackupDataOutput;
@@ -34,7 +38,9 @@ import android.database.Cursor;
import android.media.AudioManager;
import android.net.Uri;
import android.net.wifi.WifiManager;
+import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
@@ -52,6 +58,13 @@ public class SettingsBackupAgent extends BackupHelperAgent {
private static final String KEY_SYNC = "sync_providers";
private static final String KEY_LOCALE = "locale";
+ private static final int STATE_SYSTEM = 0;
+ private static final int STATE_SECURE = 1;
+ private static final int STATE_SYNC = 2;
+ private static final int STATE_LOCALE = 3;
+ private static final int STATE_WIFI = 4;
+ private static final int STATE_SIZE = 5; // The number of state items
+
private static String[] sortedSystemKeys = null;
private static String[] sortedSecureKeys = null;
@@ -87,20 +100,22 @@ public class SettingsBackupAgent extends BackupHelperAgent {
byte[] secureSettingsData = getSecureSettings();
byte[] syncProviders = mSettingsHelper.getSyncProviders();
byte[] locale = mSettingsHelper.getLocaleData();
-
- data.writeEntityHeader(KEY_SYSTEM, systemSettingsData.length);
- data.writeEntityData(systemSettingsData, systemSettingsData.length);
-
- data.writeEntityHeader(KEY_SECURE, secureSettingsData.length);
- data.writeEntityData(secureSettingsData, secureSettingsData.length);
-
- data.writeEntityHeader(KEY_SYNC, syncProviders.length);
- data.writeEntityData(syncProviders, syncProviders.length);
-
- data.writeEntityHeader(KEY_LOCALE, locale.length);
- data.writeEntityData(locale, locale.length);
-
- backupFile(FILE_WIFI_SUPPLICANT, data);
+ byte[] wifiData = getFileData(FILE_WIFI_SUPPLICANT);
+
+ long[] stateChecksums = readOldChecksums(oldState);
+
+ stateChecksums[STATE_SYSTEM] =
+ writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
+ stateChecksums[STATE_SECURE] =
+ writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
+ stateChecksums[STATE_SYNC] =
+ writeIfChanged(stateChecksums[STATE_SYNC], KEY_SYNC, syncProviders, data);
+ stateChecksums[STATE_LOCALE] =
+ writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
+ stateChecksums[STATE_WIFI] =
+ writeIfChanged(stateChecksums[STATE_WIFI], FILE_WIFI_SUPPLICANT, wifiData, data);
+
+ writeNewChecksums(stateChecksums, newState);
}
@Override
@@ -115,11 +130,15 @@ public class SettingsBackupAgent extends BackupHelperAgent {
final int size = data.getDataSize();
if (KEY_SYSTEM.equals(key)) {
restoreSettings(data, Settings.System.CONTENT_URI);
+ mSettingsHelper.applyAudioSettings();
} else if (KEY_SECURE.equals(key)) {
restoreSettings(data, Settings.Secure.CONTENT_URI);
-// TODO: Re-enable WIFI restore when we figure out a solution for the permissions
-// } else if (FILE_WIFI_SUPPLICANT.equals(key)) {
-// restoreFile(FILE_WIFI_SUPPLICANT, data);
+ } else if (FILE_WIFI_SUPPLICANT.equals(key)) {
+ restoreFile(FILE_WIFI_SUPPLICANT, data);
+ FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+ FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+ Process.myUid(), Process.WIFI_UID);
} else if (KEY_SYNC.equals(key)) {
mSettingsHelper.setSyncProviders(data);
} else if (KEY_LOCALE.equals(key)) {
@@ -132,6 +151,49 @@ public class SettingsBackupAgent extends BackupHelperAgent {
}
}
+ private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
+ long[] stateChecksums = new long[STATE_SIZE];
+
+ DataInputStream dataInput = new DataInputStream(
+ new FileInputStream(oldState.getFileDescriptor()));
+ for (int i = 0; i < STATE_SIZE; i++) {
+ try {
+ stateChecksums[i] = dataInput.readLong();
+ } catch (EOFException eof) {
+ break;
+ }
+ }
+ dataInput.close();
+ return stateChecksums;
+ }
+
+ private void writeNewChecksums(long[] checksums, ParcelFileDescriptor newState)
+ throws IOException {
+ DataOutputStream dataOutput = new DataOutputStream(
+ new FileOutputStream(newState.getFileDescriptor()));
+ for (int i = 0; i < STATE_SIZE; i++) {
+ dataOutput.writeLong(checksums[i]);
+ }
+ dataOutput.close();
+ }
+
+ private long writeIfChanged(long oldChecksum, String key, byte[] data,
+ BackupDataOutput output) {
+ CRC32 checkSummer = new CRC32();
+ checkSummer.update(data);
+ long newChecksum = checkSummer.getValue();
+ if (oldChecksum == newChecksum) {
+ return oldChecksum;
+ }
+ try {
+ output.writeEntityHeader(key, data.length);
+ output.writeEntityData(data, data.length);
+ } catch (IOException ioe) {
+ // Bail
+ }
+ return newChecksum;
+ }
+
private byte[] getSystemSettings() {
Cursor sortedCursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION,
null, null, Settings.NameValueTable.NAME);
@@ -248,7 +310,7 @@ public class SettingsBackupAgent extends BackupHelperAgent {
return result;
}
- private void backupFile(String filename, BackupDataOutput data) {
+ private byte[] getFileData(String filename) {
try {
File file = new File(filename);
if (file.exists()) {
@@ -260,14 +322,13 @@ public class SettingsBackupAgent extends BackupHelperAgent {
got = fis.read(bytes, offset, bytes.length - offset);
if (got > 0) offset += got;
} while (offset < bytes.length && got > 0);
- data.writeEntityHeader(filename, bytes.length);
- data.writeEntityData(bytes, bytes.length);
+ return bytes;
} else {
- data.writeEntityHeader(filename, 0);
- data.writeEntityData(EMPTY_DATA, 0);
+ return EMPTY_DATA;
}
} catch (IOException ioe) {
Log.w(TAG, "Couldn't backup " + filename);
+ return EMPTY_DATA;
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 2c5775a..ca739e6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -167,6 +167,9 @@ public class SettingsHelper {
// Check if locale was set by the user:
Configuration conf = mContext.getResources().getConfiguration();
Locale loc = conf.locale;
+ // TODO: The following is not working as intended because the network is forcing a locale
+ // change after registering. Need to find some other way to detect if the user manually
+ // changed the locale
if (conf.userSetLocale) return; // Don't change if user set it in the SetupWizard
final String[] availableLocales = mContext.getAssets().getLocales();
@@ -193,6 +196,14 @@ public class SettingsHelper {
} catch (RemoteException e) {
// Intentionally left blank
}
+ }
+ /**
+ * Informs the audio service of changes to the settings so that
+ * they can be re-read and applied.
+ */
+ void applyAudioSettings() {
+ AudioManager am = new AudioManager(mContext);
+ am.reloadAudioSettings();
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2abf8b3..c0de9a5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -126,11 +126,14 @@ public class SettingsProvider extends ContentProvider {
// a notification and then using the contract class to get their data,
// the system property will be updated and they'll get the new data.
+ boolean backedUpDataChanged = false;
String property = null, table = uri.getPathSegments().get(0);
if (table.equals("system")) {
property = Settings.System.SYS_PROP_SETTING_VERSION;
+ backedUpDataChanged = true;
} else if (table.equals("secure")) {
property = Settings.Secure.SYS_PROP_SETTING_VERSION;
+ backedUpDataChanged = true;
} else if (table.equals("gservices")) {
property = Settings.Gservices.SYS_PROP_SETTING_VERSION;
}
@@ -142,7 +145,9 @@ public class SettingsProvider extends ContentProvider {
}
// Inform the backup manager about a data change
- mBackupManager.dataChanged();
+ if (backedUpDataChanged) {
+ mBackupManager.dataChanged();
+ }
// Now send the notification through the content framework.
String notify = uri.getQueryParameter("notify");
diff --git a/packages/SubscribedFeedsProvider/res/values-cs/strings.xml b/packages/SubscribedFeedsProvider/res/values-cs/strings.xml
new file mode 100644
index 0000000..9b782b0
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values-cs/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Synchronizace zdrojů"</string>
+</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-de/strings.xml b/packages/SubscribedFeedsProvider/res/values-de/strings.xml
new file mode 100644
index 0000000..1ade594
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values-de/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Feedsynchronisierung"</string>
+</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-es/strings.xml b/packages/SubscribedFeedsProvider/res/values-es/strings.xml
new file mode 100644
index 0000000..86c6946
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values-es/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Sincronización de feeds"</string>
+</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-fr/strings.xml b/packages/SubscribedFeedsProvider/res/values-fr/strings.xml
new file mode 100644
index 0000000..924b960
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values-fr/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Synchronisation des flux"</string>
+</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-it/strings.xml b/packages/SubscribedFeedsProvider/res/values-it/strings.xml
new file mode 100644
index 0000000..eabb17e
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values-it/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Sincronizzazione feed"</string>
+</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-nl/strings.xml b/packages/SubscribedFeedsProvider/res/values-nl/strings.xml
new file mode 100644
index 0000000..b9e82d1
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values-nl/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Feeds synchroniseren"</string>
+</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-pl/strings.xml b/packages/SubscribedFeedsProvider/res/values-pl/strings.xml
new file mode 100644
index 0000000..02da9f3
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values-pl/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"Synchronizowanie kanałów"</string>
+</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml b/packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..e6643cd
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml
@@ -0,0 +1,19 @@
+<?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="app_label">"同步資訊提供"</string>
+</resources>
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 1958ba9..99d7723 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -33,6 +33,8 @@
#define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT
#define DEFAULT_TTS_NB_CHANNELS 1
#define DEFAULT_TTS_BUFFERSIZE 1024
+// TODO use the TTS stream type when available
+#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC
#define USAGEMODE_PLAY_IMMEDIATELY 0
#define USAGEMODE_WRITE_TO_FILE 1
@@ -46,22 +48,28 @@ struct fields_t {
jmethodID synthProxyMethodPost;
};
+// structure to hold the data that is used each time the TTS engine has synthesized more data
struct afterSynthData_t {
jint jniStorage;
int usageMode;
FILE* outputFile;
+ AudioSystem::stream_type streamType;
};
// ----------------------------------------------------------------------------
static fields_t javaTTSFields;
+// TODO move to synth member once we have multiple simultaneous engines running
+static Mutex engineMutex;
+
// ----------------------------------------------------------------------------
class SynthProxyJniStorage {
public :
- //jclass tts_class;
jobject tts_ref;
TtsEngine* mNativeSynthInterface;
+ void* mEngineLibHandle;
AudioTrack* mAudioOut;
+ AudioSystem::stream_type mStreamType;
uint32_t mSampleRate;
AudioSystem::audio_format mAudFormat;
int mNbChannels;
@@ -69,23 +77,31 @@ class SynthProxyJniStorage {
size_t mBufferSize;
SynthProxyJniStorage() {
- //tts_class = NULL;
tts_ref = NULL;
mNativeSynthInterface = NULL;
+ mEngineLibHandle = NULL;
mAudioOut = NULL;
+ mStreamType = DEFAULT_TTS_STREAM_TYPE;
mSampleRate = DEFAULT_TTS_RATE;
mAudFormat = DEFAULT_TTS_FORMAT;
mNbChannels = DEFAULT_TTS_NB_CHANNELS;
mBufferSize = DEFAULT_TTS_BUFFERSIZE;
mBuffer = new int8_t[mBufferSize];
+ memset(mBuffer, 0, mBufferSize);
}
~SynthProxyJniStorage() {
+ //LOGV("entering ~SynthProxyJniStorage()");
killAudio();
if (mNativeSynthInterface) {
mNativeSynthInterface->shutdown();
mNativeSynthInterface = NULL;
}
+ if (mEngineLibHandle) {
+ //LOGE("~SynthProxyJniStorage(): before close library");
+ int res = dlclose(mEngineLibHandle);
+ LOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res);
+ }
delete mBuffer;
}
@@ -97,66 +113,66 @@ class SynthProxyJniStorage {
}
}
- void createAudioOut(uint32_t rate, AudioSystem::audio_format format,
- int channel) {
+ void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate,
+ AudioSystem::audio_format format, int channel) {
mSampleRate = rate;
mAudFormat = format;
mNbChannels = channel;
- // TODO use the TTS stream type
- int streamType = AudioSystem::MUSIC;
+ mStreamType = streamType;
// retrieve system properties to ensure successful creation of the
// AudioTrack object for playback
int afSampleRate;
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+ if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
afSampleRate = 44100;
}
int afFrameCount;
- if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
+ if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
afFrameCount = 2048;
}
uint32_t afLatency;
- if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
+ if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) {
afLatency = 500;
}
uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
if (minBufCount < 2) minBufCount = 2;
int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
- mAudioOut = new AudioTrack(streamType, rate, format, channel,
+ mAudioOut = new AudioTrack(mStreamType, rate, format, channel,
minFrameCount > 4096 ? minFrameCount : 4096,
0, 0, 0, 0); // not using an AudioTrack callback
if (mAudioOut->initCheck() != NO_ERROR) {
- LOGI("AudioTrack error");
+ LOGE("createAudioOut(): AudioTrack error");
delete mAudioOut;
mAudioOut = NULL;
} else {
//LOGI("AudioTrack OK");
+ mAudioOut->setVolume(2.0f, 2.0f);
mAudioOut->start();
- LOGI("AudioTrack started");
+ LOGV("AudioTrack started");
}
}
};
// ----------------------------------------------------------------------------
-void prepAudioTrack(SynthProxyJniStorage* pJniData,
- uint32_t rate, AudioSystem::audio_format format, int channel)
-{
+void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType,
+ uint32_t rate, AudioSystem::audio_format format, int channel) {
// Don't bother creating a new audiotrack object if the current
- // object is already set.
+ // object is already initialized with the same audio parameters.
if ( pJniData->mAudioOut &&
(rate == pJniData->mSampleRate) &&
(format == pJniData->mAudFormat) &&
- (channel == pJniData->mNbChannels) ){
+ (channel == pJniData->mNbChannels) &&
+ (streamType == pJniData->mStreamType) ){
return;
}
if (pJniData->mAudioOut){
pJniData->killAudio();
}
- pJniData->createAudioOut(rate, format, channel);
+ pJniData->createAudioOut(streamType, rate, format, channel);
}
@@ -186,9 +202,10 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
}
if (bufferSize > 0) {
- prepAudioTrack(pJniData, rate, format, channel);
+ prepAudioTrack(pJniData, pForAfter->streamType, rate, format, channel);
if (pJniData->mAudioOut) {
pJniData->mAudioOut->write(wav, bufferSize);
+ memset(wav, 0, bufferSize);
//LOGV("AudioTrack wrote: %d bytes", bufferSize);
} else {
LOGE("Can't play, null audiotrack");
@@ -203,6 +220,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
}
if (bufferSize > 0){
fwrite(wav, 1, bufferSize, pForAfter->outputFile);
+ memset(wav, 0, bufferSize);
}
}
// Future update:
@@ -241,23 +259,25 @@ android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
prepAudioTrack(pJniStorage,
- DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
+ DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
const char *nativeSoLibNativeString =
env->GetStringUTFChars(nativeSoLib, 0);
void *engine_lib_handle = dlopen(nativeSoLibNativeString,
RTLD_NOW | RTLD_LOCAL);
- if (engine_lib_handle==NULL) {
- LOGI("engine_lib_handle==NULL");
+ if (engine_lib_handle == NULL) {
+ LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL");
// TODO report error so the TTS can't be used
} else {
TtsEngine *(*get_TtsEngine)() =
reinterpret_cast<TtsEngine* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
pJniStorage->mNativeSynthInterface = (*get_TtsEngine)();
+ pJniStorage->mEngineLibHandle = engine_lib_handle;
if (pJniStorage->mNativeSynthInterface) {
+ Mutex::Autolock l(engineMutex);
pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB);
}
}
@@ -276,10 +296,29 @@ android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
static void
android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
{
- if (jniData) {
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- delete pSynthData;
+ //LOGV("entering android_tts_SynthProxy_finalize()");
+ if (jniData == 0) {
+ //LOGE("android_tts_SynthProxy_native_finalize(): invalid JNI data");
+ return;
}
+
+ Mutex::Autolock l(engineMutex);
+
+ SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+ env->DeleteGlobalRef(pSynthData->tts_ref);
+ delete pSynthData;
+
+ env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, 0);
+}
+
+
+static void
+android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
+{
+ //LOGV("entering android_tts_SynthProxy_shutdown()");
+
+ // do everything a call to finalize would
+ android_tts_SynthProxy_native_finalize(env, thiz, jniData);
}
@@ -321,6 +360,8 @@ android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
return result;
}
+ Mutex::Autolock l(engineMutex);
+
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
const char *langNativeString = env->GetStringUTFChars(language, 0);
const char *countryNativeString = env->GetStringUTFChars(country, 0);
@@ -380,6 +421,8 @@ android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
char buffer [bufSize];
sprintf(buffer, "%d", speechRate);
+ Mutex::Autolock l(engineMutex);
+
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
LOGI("setting speech rate to %d", speechRate);
@@ -402,6 +445,8 @@ android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData,
return result;
}
+ Mutex::Autolock l(engineMutex);
+
int bufSize = 10;
char buffer [bufSize];
sprintf(buffer, "%d", pitch);
@@ -434,6 +479,8 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
return result;
}
+ Mutex::Autolock l(engineMutex);
+
// Retrieve audio parameters before writing the file header
AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT;
uint32_t rate = DEFAULT_TTS_RATE;
@@ -468,6 +515,7 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
unsigned int unique_identifier;
+ memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
result = pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
@@ -526,7 +574,7 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
static int
android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
- jstring textJavaString)
+ jstring textJavaString, jint javaStreamType)
{
int result = TTS_FAILURE;
@@ -535,19 +583,22 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
return result;
}
+ Mutex::Autolock l(engineMutex);
+
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
if (pSynthData->mAudioOut) {
- pSynthData->mAudioOut->stop();
pSynthData->mAudioOut->start();
}
afterSynthData_t* pForAfter = new (afterSynthData_t);
pForAfter->jniStorage = jniData;
pForAfter->usageMode = USAGEMODE_PLAY_IMMEDIATELY;
+ pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;
if (pSynthData->mNativeSynthInterface) {
const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+ memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
result = pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
env->ReleaseStringUTFChars(textJavaString, textNativeString);
@@ -569,48 +620,14 @@ android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- if (pSynthData->mNativeSynthInterface) {
- result = pSynthData->mNativeSynthInterface->stop();
- }
if (pSynthData->mAudioOut) {
pSynthData->mAudioOut->stop();
}
-
- return result;
-}
-
-
-static void
-android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
-{
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_shutdown(): invalid JNI data");
- return;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
if (pSynthData->mNativeSynthInterface) {
- pSynthData->mNativeSynthInterface->shutdown();
- pSynthData->mNativeSynthInterface = NULL;
- }
-}
-
-
-// TODO add buffer format
-static void
-android_tts_SynthProxy_playAudioBuffer(JNIEnv *env, jobject thiz, jint jniData,
- int bufferPointer, int bufferSize)
-{
-LOGI("android_tts_SynthProxy_playAudioBuffer");
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_playAudioBuffer(): invalid JNI data");
- return;
+ result = pSynthData->mNativeSynthInterface->stop();
}
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- short* wav = (short*) bufferPointer;
- pSynthData->mAudioOut->write(wav, bufferSize);
- //LOGI("AudioTrack wrote: %d bytes", bufferSize);
+ return result;
}
@@ -672,7 +689,7 @@ static JNINativeMethod gMethods[] = {
(void*)android_tts_SynthProxy_stop
},
{ "native_speak",
- "(ILjava/lang/String;)I",
+ "(ILjava/lang/String;I)I",
(void*)android_tts_SynthProxy_speak
},
{ "native_synthesizeToFile",
@@ -699,10 +716,6 @@ static JNINativeMethod gMethods[] = {
"(II)I",
(void*)android_tts_SynthProxy_setPitch
},
- { "native_playAudioBuffer",
- "(III)V",
- (void*)android_tts_SynthProxy_playAudioBuffer
- },
{ "native_getLanguage",
"(I)[Ljava/lang/String;",
(void*)android_tts_SynthProxy_getLanguage
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index bb16b14..a0814aa 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -15,6 +15,8 @@
*/
package android.tts;
+import android.media.AudioManager;
+import android.media.AudioSystem;
import android.util.Log;
import java.lang.ref.WeakReference;
@@ -52,8 +54,13 @@ public class SynthProxy {
/**
* Synthesize speech and speak it directly using AudioTrack.
*/
- public int speak(String text) {
- return native_speak(mJniData, text);
+ public int speak(String text, int streamType) {
+ if ((streamType > -1) && (streamType < AudioSystem.getNumStreamTypes())) {
+ return native_speak(mJniData, text, streamType);
+ } else {
+ Log.e("SynthProxy", "Trying to speak with invalid stream type " + streamType);
+ return native_speak(mJniData, text, AudioManager.STREAM_MUSIC);
+ }
}
/**
@@ -102,13 +109,6 @@ public class SynthProxy {
}
/**
- * Plays the given audio buffer.
- */
- public void playAudioBuffer(int bufferPointer, int bufferSize) {
- native_playAudioBuffer(mJniData, bufferPointer, bufferSize);
- }
-
- /**
* Returns the currently set language, country and variant information.
*/
public String[] getLanguage() {
@@ -156,7 +156,7 @@ public class SynthProxy {
private native final int native_stop(int jniData);
- private native final int native_speak(int jniData, String text);
+ private native final int native_speak(int jniData, String text, int streamType);
private native final int native_synthesizeToFile(int jniData, String text, String filename);
@@ -173,9 +173,6 @@ public class SynthProxy {
private native final int native_setPitch(int jniData, int speechRate);
- // TODO add buffer format
- private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
-
private native final String[] native_getLanguage(int jniData);
private native final int native_getRate(int jniData);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index a713edf..e52ba80 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
@@ -38,6 +39,8 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.TimeUnit;
+
/**
* @hide Synthesizes speech from text. This is implemented as a service so that
@@ -57,23 +60,29 @@ public class TtsService extends Service implements OnCompletionListener {
public int mType = TEXT;
public long mDuration = 0;
public String mFilename = null;
+ public String mCallingApp = "";
- public SpeechItem(String text, ArrayList<String> params, int itemType) {
+ public SpeechItem(String source, String text, ArrayList<String> params, int itemType) {
mText = text;
mParams = params;
mType = itemType;
+ mCallingApp = source;
}
- public SpeechItem(long silenceTime) {
+ public SpeechItem(String source, long silenceTime, ArrayList<String> params) {
mDuration = silenceTime;
+ mParams = params;
mType = SILENCE;
+ mCallingApp = source;
}
- public SpeechItem(String text, ArrayList<String> params, int itemType, String filename) {
+ public SpeechItem(String source, String text, ArrayList<String> params,
+ int itemType, String filename) {
mText = text;
mParams = params;
mType = itemType;
mFilename = filename;
+ mCallingApp = source;
}
}
@@ -103,18 +112,26 @@ public class TtsService extends Service implements OnCompletionListener {
private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
private static final int MAX_FILENAME_LENGTH = 250;
+ // TODO use the TTS stream type when available
+ private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
private static final String CATEGORY = "android.intent.category.TTS";
private static final String PKGNAME = "android.tts";
- final RemoteCallbackList<android.speech.tts.ITtsCallback> mCallbacks = new RemoteCallbackList<ITtsCallback>();
+ private final RemoteCallbackList<ITtsCallback> mCallbacks
+ = new RemoteCallbackList<ITtsCallback>();
+
+ private HashMap<String, ITtsCallback> mCallbacksMap;
private Boolean mIsSpeaking;
private ArrayList<SpeechItem> mSpeechQueue;
private HashMap<String, SoundResource> mEarcons;
private HashMap<String, SoundResource> mUtterances;
private MediaPlayer mPlayer;
+ private SpeechItem mCurrentSpeechItem;
+ private HashMap<SpeechItem, Boolean> mKillList; // Used to ensure that in-flight synth calls
+ // are killed when stop is used.
private TtsService mSelf;
private ContentResolver mResolver;
@@ -122,25 +139,30 @@ public class TtsService extends Service implements OnCompletionListener {
private final ReentrantLock speechQueueLock = new ReentrantLock();
private final ReentrantLock synthesizerLock = new ReentrantLock();
- private SynthProxy nativeSynth;
+ private static SynthProxy sNativeSynth = null;
@Override
public void onCreate() {
super.onCreate();
- //Log.i("TTS", "TTS starting");
+ Log.i("TtsService", "TtsService.onCreate()");
mResolver = getContentResolver();
String soLibPath = "/system/lib/libttspico.so";
- nativeSynth = new SynthProxy(soLibPath);
+ if (sNativeSynth == null) {
+ sNativeSynth = new SynthProxy(soLibPath);
+ }
mSelf = this;
mIsSpeaking = false;
mEarcons = new HashMap<String, SoundResource>();
mUtterances = new HashMap<String, SoundResource>();
+ mCallbacksMap = new HashMap<String, android.speech.tts.ITtsCallback>();
mSpeechQueue = new ArrayList<SpeechItem>();
mPlayer = null;
+ mCurrentSpeechItem = null;
+ mKillList = new HashMap<SpeechItem, Boolean>();
setDefaultSettings();
}
@@ -151,7 +173,8 @@ public class TtsService extends Service implements OnCompletionListener {
// Don't hog the media player
cleanUpPlayer();
- nativeSynth.shutdown();
+ sNativeSynth.shutdown();
+ sNativeSynth = null;
// Unregister all callbacks.
mCallbacks.kill();
@@ -159,10 +182,10 @@ public class TtsService extends Service implements OnCompletionListener {
private void setDefaultSettings() {
- setLanguage(this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
+ setLanguage("", this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
// speech rate
- setSpeechRate(getDefaultRate());
+ setSpeechRate("", getDefaultRate());
}
@@ -217,38 +240,38 @@ public class TtsService extends Service implements OnCompletionListener {
}
- private int setSpeechRate(int rate) {
+ private int setSpeechRate(String callingApp, int rate) {
if (isDefaultEnforced()) {
- return nativeSynth.setSpeechRate(getDefaultRate());
+ return sNativeSynth.setSpeechRate(getDefaultRate());
} else {
- return nativeSynth.setSpeechRate(rate);
+ return sNativeSynth.setSpeechRate(rate);
}
}
- private int setPitch(int pitch) {
- return nativeSynth.setPitch(pitch);
+ private int setPitch(String callingApp, int pitch) {
+ return sNativeSynth.setPitch(pitch);
}
private int isLanguageAvailable(String lang, String country, String variant) {
- //Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
- return nativeSynth.isLanguageAvailable(lang, country, variant);
+ //Log.v("TtsService", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
+ return sNativeSynth.isLanguageAvailable(lang, country, variant);
}
private String[] getLanguage() {
- return nativeSynth.getLanguage();
+ return sNativeSynth.getLanguage();
}
- private int setLanguage(String lang, String country, String variant) {
- //Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
+ private int setLanguage(String callingApp, String lang, String country, String variant) {
+ Log.v("TtsService", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
if (isDefaultEnforced()) {
- return nativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
+ return sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
getDefaultLocVariant());
} else {
- return nativeSynth.setLanguage(lang, country, variant);
+ return sNativeSynth.setLanguage(lang, country, variant);
}
}
@@ -263,7 +286,7 @@ public class TtsService extends Service implements OnCompletionListener {
* @param resId
* The resource ID of the sound within its package
*/
- private void addSpeech(String text, String packageName, int resId) {
+ private void addSpeech(String callingApp, String text, String packageName, int resId) {
mUtterances.put(text, new SoundResource(packageName, resId));
}
@@ -276,7 +299,7 @@ public class TtsService extends Service implements OnCompletionListener {
* The filename of the sound resource. This must be a complete
* path like: (/sdcard/mysounds/mysoundbite.mp3).
*/
- private void addSpeech(String text, String filename) {
+ private void addSpeech(String callingApp, String text, String filename) {
mUtterances.put(text, new SoundResource(filename));
}
@@ -290,7 +313,7 @@ public class TtsService extends Service implements OnCompletionListener {
* @param resId
* The resource ID of the sound within its package
*/
- private void addEarcon(String earcon, String packageName, int resId) {
+ private void addEarcon(String callingApp, String earcon, String packageName, int resId) {
mEarcons.put(earcon, new SoundResource(packageName, resId));
}
@@ -303,7 +326,7 @@ public class TtsService extends Service implements OnCompletionListener {
* The filename of the sound resource. This must be a complete
* path like: (/sdcard/mysounds/mysoundbite.mp3).
*/
- private void addEarcon(String earcon, String filename) {
+ private void addEarcon(String callingApp, String earcon, String filename) {
mEarcons.put(earcon, new SoundResource(filename));
}
@@ -313,17 +336,20 @@ public class TtsService extends Service implements OnCompletionListener {
* @param text
* The text that should be spoken
* @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
+ * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
+ * TextToSpeech.TTS_QUEUE_ADD for queued
* @param params
* An ArrayList of parameters. This is not implemented for all
* engines.
*/
- private int speak(String text, int queueMode, ArrayList<String> params) {
- if (queueMode == 0) {
- stop();
+ private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) {
+ Log.v("TtsService", "TTS service received " + text);
+ if (queueMode == TextToSpeech.TTS_QUEUE_FLUSH) {
+ stop(callingApp);
+ } else if (queueMode == 2) {
+ stopAll(callingApp);
}
- mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT));
+ mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT));
if (!mIsSpeaking) {
processSpeechQueue();
}
@@ -336,18 +362,20 @@ public class TtsService extends Service implements OnCompletionListener {
* @param earcon
* The earcon that should be played
* @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
+ * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
+ * TextToSpeech.TTS_QUEUE_ADD for queued
* @param params
* An ArrayList of parameters. This is not implemented for all
* engines.
*/
- private int playEarcon(String earcon, int queueMode,
+ private int playEarcon(String callingApp, String earcon, int queueMode,
ArrayList<String> params) {
- if (queueMode == 0) {
- stop();
+ if (queueMode == TextToSpeech.TTS_QUEUE_FLUSH) {
+ stop(callingApp);
+ } else if (queueMode == 2) {
+ stopAll(callingApp);
}
- mSpeechQueue.add(new SpeechItem(earcon, params, SpeechItem.EARCON));
+ mSpeechQueue.add(new SpeechItem(callingApp, earcon, params, SpeechItem.EARCON));
if (!mIsSpeaking) {
processSpeechQueue();
}
@@ -355,49 +383,153 @@ public class TtsService extends Service implements OnCompletionListener {
}
/**
- * Stops all speech output and removes any utterances still in the queue.
+ * Stops all speech output and removes any utterances still in the queue for the calling app.
*/
- private int stop() {
- Log.i("TTS", "Stopping");
- mSpeechQueue.clear();
+ private int stop(String callingApp) {
+ int result = TextToSpeech.TTS_ERROR;
+ boolean speechQueueAvailable = false;
+ try{
+ // If the queue is locked for more than 1 second,
+ // something has gone very wrong with processSpeechQueue.
+ speechQueueAvailable = speechQueueLock.tryLock(1000, TimeUnit.MILLISECONDS);
+ if (speechQueueAvailable) {
+ Log.i("TtsService", "Stopping");
+ for (int i = mSpeechQueue.size() - 1; i > -1; i--){
+ if (mSpeechQueue.get(i).mCallingApp.equals(callingApp)){
+ mSpeechQueue.remove(i);
+ }
+ }
+ if ((mCurrentSpeechItem != null) &&
+ mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
+ result = sNativeSynth.stop();
+ mKillList.put(mCurrentSpeechItem, true);
+ if (mPlayer != null) {
+ try {
+ mPlayer.stop();
+ } catch (IllegalStateException e) {
+ // Do nothing, the player is already stopped.
+ }
+ }
+ mIsSpeaking = false;
+ mCurrentSpeechItem = null;
+ } else {
+ result = TextToSpeech.TTS_SUCCESS;
+ }
+ Log.i("TtsService", "Stopped");
+ }
+ } catch (InterruptedException e) {
+ Log.e("TtsService", "TTS stop: tryLock interrupted");
+ e.printStackTrace();
+ } finally {
+ // This check is needed because finally will always run; even if the
+ // method returns somewhere in the try block.
+ if (speechQueueAvailable) {
+ speechQueueLock.unlock();
+ }
+ return result;
+ }
+ }
- int result = nativeSynth.stop();
- mIsSpeaking = false;
- if (mPlayer != null) {
- try {
- mPlayer.stop();
- } catch (IllegalStateException e) {
- // Do nothing, the player is already stopped.
+
+
+ /**
+ * Stops all speech output and removes any utterances still in the queue globally.
+ */
+ private int stopAll(String callingApp) {
+ int result = TextToSpeech.TTS_ERROR;
+ boolean speechQueueAvailable = false;
+ try{
+ // If the queue is locked for more than 1 second,
+ // something has gone very wrong with processSpeechQueue.
+ speechQueueAvailable = speechQueueLock.tryLock(1000, TimeUnit.MILLISECONDS);
+ if (speechQueueAvailable) {
+ for (int i = mSpeechQueue.size() - 1; i > -1; i--){
+ if (mSpeechQueue.get(i).mType != SpeechItem.TEXT_TO_FILE){
+ mSpeechQueue.remove(i);
+ }
+ }
+ if ((mCurrentSpeechItem != null) &&
+ ((mCurrentSpeechItem.mType != SpeechItem.TEXT_TO_FILE) ||
+ mCurrentSpeechItem.mCallingApp.equals(callingApp))) {
+ result = sNativeSynth.stop();
+ mKillList.put(mCurrentSpeechItem, true);
+ if (mPlayer != null) {
+ try {
+ mPlayer.stop();
+ } catch (IllegalStateException e) {
+ // Do nothing, the player is already stopped.
+ }
+ }
+ mIsSpeaking = false;
+ mCurrentSpeechItem = null;
+ } else {
+ result = TextToSpeech.TTS_SUCCESS;
+ }
+ Log.i("TtsService", "Stopped all");
}
+ } catch (InterruptedException e) {
+ Log.e("TtsService", "TTS stopAll: tryLock interrupted");
+ e.printStackTrace();
+ } finally {
+ // This check is needed because finally will always run; even if the
+ // method returns somewhere in the try block.
+ if (speechQueueAvailable) {
+ speechQueueLock.unlock();
+ }
+ return result;
}
- Log.i("TTS", "Stopped");
- return result;
}
public void onCompletion(MediaPlayer arg0) {
+ String callingApp = mCurrentSpeechItem.mCallingApp;
+ ArrayList<String> params = mCurrentSpeechItem.mParams;
+ String utteranceId = "";
+ if (params != null){
+ for (int i = 0; i < params.size() - 1; i = i + 2){
+ String param = params.get(i);
+ if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_UTTERANCE_ID)){
+ utteranceId = params.get(i+1);
+ }
+ }
+ }
+ if (utteranceId.length() > 0){
+ dispatchUtteranceCompletedCallback(utteranceId, callingApp);
+ }
processSpeechQueue();
}
- private int playSilence(long duration, int queueMode,
+ private int playSilence(String callingApp, long duration, int queueMode,
ArrayList<String> params) {
- if (queueMode == 0) {
- stop();
+ if (queueMode == TextToSpeech.TTS_QUEUE_FLUSH) {
+ stop(callingApp);
}
- mSpeechQueue.add(new SpeechItem(duration));
+ mSpeechQueue.add(new SpeechItem(callingApp, duration, params));
if (!mIsSpeaking) {
processSpeechQueue();
}
return TextToSpeech.TTS_SUCCESS;
}
- private void silence(final long duration) {
+ private void silence(final SpeechItem speechItem) {
class SilenceThread implements Runnable {
public void run() {
+ String utteranceId = "";
+ if (speechItem.mParams != null){
+ for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
+ String param = speechItem.mParams.get(i);
+ if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_UTTERANCE_ID)){
+ utteranceId = speechItem.mParams.get(i+1);
+ }
+ }
+ }
try {
- Thread.sleep(duration);
+ Thread.sleep(speechItem.mDuration);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
+ if (utteranceId.length() > 0){
+ dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
+ }
processSpeechQueue();
}
}
@@ -407,47 +539,70 @@ public class TtsService extends Service implements OnCompletionListener {
slnc.start();
}
- private void speakInternalOnly(final String text,
- final ArrayList<String> params) {
+ private void speakInternalOnly(final SpeechItem speechItem) {
class SynthThread implements Runnable {
public void run() {
boolean synthAvailable = false;
+ String utteranceId = "";
try {
synthAvailable = synthesizerLock.tryLock();
if (!synthAvailable) {
Thread.sleep(100);
Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
+ //synth.setPriority(Thread.MIN_PRIORITY);
synth.start();
return;
}
- if (params != null){
- String language = "";
- String country = "";
- String variant = "";
- for (int i = 0; i < params.size() - 1; i = i + 2){
- String param = params.get(i);
- if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
- setSpeechRate(Integer.parseInt(params.get(i+1)));
- } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
- language = params.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
- country = params.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
- variant = params.get(i+1);
+ int streamType = DEFAULT_STREAM_TYPE;
+ String language = "";
+ String country = "";
+ String variant = "";
+ String speechRate = "";
+ if (speechItem.mParams != null){
+ for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
+ String param = speechItem.mParams.get(i);
+ if (param != null) {
+ if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)) {
+ speechRate = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
+ language = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
+ country = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
+ variant = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_UTTERANCE_ID)){
+ utteranceId = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_STREAM)) {
+ try {
+ streamType
+ = Integer.parseInt(speechItem.mParams.get(i + 1));
+ } catch (NumberFormatException e) {
+ streamType = DEFAULT_STREAM_TYPE;
+ }
+ }
}
}
+ }
+ // Only do the synthesis if it has not been killed by a subsequent utterance.
+ if (mKillList.get(speechItem) == null) {
if (language.length() > 0){
- setLanguage(language, country, variant);
+ setLanguage("", language, country, variant);
+ }
+ if (speechRate.length() > 0){
+ setSpeechRate("", Integer.parseInt(speechRate));
}
+ sNativeSynth.speak(speechItem.mText, streamType);
}
- nativeSynth.speak(text);
} catch (InterruptedException e) {
+ Log.e("TtsService", "TTS speakInternalOnly(): tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run;
// even if the
// method returns somewhere in the try block.
+ if (utteranceId.length() > 0){
+ dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
+ }
if (synthAvailable) {
synthesizerLock.unlock();
}
@@ -456,52 +611,67 @@ public class TtsService extends Service implements OnCompletionListener {
}
}
Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
+ //synth.setPriority(Thread.MIN_PRIORITY);
synth.start();
}
- private void synthToFileInternalOnly(final String text,
- final ArrayList<String> params, final String filename) {
+ private void synthToFileInternalOnly(final SpeechItem speechItem) {
class SynthThread implements Runnable {
public void run() {
- Log.i("TTS", "Synthesizing to " + filename);
boolean synthAvailable = false;
+ String utteranceId = "";
+ Log.i("TtsService", "Synthesizing to " + speechItem.mFilename);
try {
synthAvailable = synthesizerLock.tryLock();
if (!synthAvailable) {
Thread.sleep(100);
Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
+ //synth.setPriority(Thread.MIN_PRIORITY);
synth.start();
return;
}
- if (params != null){
- String language = "";
- String country = "";
- String variant = "";
- for (int i = 0; i < params.size() - 1; i = i + 2){
- String param = params.get(i);
- if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
- setSpeechRate(Integer.parseInt(params.get(i+1)));
- } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
- language = params.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
- country = params.get(i+1);
- } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
- variant = params.get(i+1);
+ String language = "";
+ String country = "";
+ String variant = "";
+ String speechRate = "";
+ if (speechItem.mParams != null){
+ for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
+ String param = speechItem.mParams.get(i);
+ if (param != null) {
+ if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)) {
+ speechRate = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
+ language = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
+ country = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
+ variant = speechItem.mParams.get(i+1);
+ } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_UTTERANCE_ID)){
+ utteranceId = speechItem.mParams.get(i+1);
+ }
}
}
+ }
+ // Only do the synthesis if it has not been killed by a subsequent utterance.
+ if (mKillList.get(speechItem) == null){
if (language.length() > 0){
- setLanguage(language, country, variant);
+ setLanguage("", language, country, variant);
+ }
+ if (speechRate.length() > 0){
+ setSpeechRate("", Integer.parseInt(speechRate));
}
+ sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
}
- nativeSynth.synthesizeToFile(text, filename);
} catch (InterruptedException e) {
+ Log.e("TtsService", "TTS synthToFileInternalOnly(): tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run;
// even if the
// method returns somewhere in the try block.
+ if (utteranceId.length() > 0){
+ dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
+ }
if (synthAvailable) {
synthesizerLock.unlock();
}
@@ -510,7 +680,7 @@ public class TtsService extends Service implements OnCompletionListener {
}
}
Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
+ //synth.setPriority(Thread.MIN_PRIORITY);
synth.start();
}
@@ -532,26 +702,30 @@ public class TtsService extends Service implements OnCompletionListener {
sendBroadcast(i);
}
- private void dispatchSpeechCompletedCallbacks(String mark) {
- Log.i("TTS callback", "dispatch started");
+
+ private void dispatchUtteranceCompletedCallback(String utteranceId, String packageName) {
+ ITtsCallback cb = mCallbacksMap.get(packageName);
+ if (cb == null){
+ return;
+ }
+ Log.i("TtsService", "TTS callback: dispatch started");
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
- for (int i = 0; i < N; i++) {
- try {
- mCallbacks.getBroadcastItem(i).markReached(mark);
- } catch (RemoteException e) {
- // The RemoteCallbackList will take care of removing
- // the dead object for us.
- }
+ try {
+ cb.utteranceCompleted(utteranceId);
+ } catch (RemoteException e) {
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
}
mCallbacks.finishBroadcast();
- Log.i("TTS callback", "dispatch completed to " + N);
+ Log.i("TtsService", "TTS callback: dispatch completed to " + N);
}
private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){
return currentSpeechItem;
} else {
+ String callingApp = currentSpeechItem.mCallingApp;
ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>();
int start = 0;
int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
@@ -559,13 +733,13 @@ public class TtsService extends Service implements OnCompletionListener {
SpeechItem splitItem;
while (end < currentSpeechItem.mText.length()){
splitText = currentSpeechItem.mText.substring(start, end);
- splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT);
+ splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
splitItems.add(splitItem);
start = end;
end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
}
splitText = currentSpeechItem.mText.substring(start);
- splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT);
+ splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
splitItems.add(splitItem);
mSpeechQueue.remove(0);
for (int i = splitItems.size() - 1; i >= 0; i--){
@@ -588,23 +762,21 @@ public class TtsService extends Service implements OnCompletionListener {
return;
}
- SpeechItem currentSpeechItem = mSpeechQueue.get(0);
+ mCurrentSpeechItem = mSpeechQueue.get(0);
mIsSpeaking = true;
- SoundResource sr = getSoundResource(currentSpeechItem);
+ SoundResource sr = getSoundResource(mCurrentSpeechItem);
// Synth speech as needed - synthesizer should call
// processSpeechQueue to continue running the queue
- Log.i("TTS processing: ", currentSpeechItem.mText);
+ Log.i("TtsService", "TTS processing: " + mCurrentSpeechItem.mText);
if (sr == null) {
- if (currentSpeechItem.mType == SpeechItem.TEXT) {
- currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem);
- speakInternalOnly(currentSpeechItem.mText,
- currentSpeechItem.mParams);
- } else if (currentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
- synthToFileInternalOnly(currentSpeechItem.mText,
- currentSpeechItem.mParams, currentSpeechItem.mFilename);
+ if (mCurrentSpeechItem.mType == SpeechItem.TEXT) {
+ mCurrentSpeechItem = splitCurrentTextIfNeeded(mCurrentSpeechItem);
+ speakInternalOnly(mCurrentSpeechItem);
+ } else if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
+ synthToFileInternalOnly(mCurrentSpeechItem);
} else {
// This is either silence or an earcon that was missing
- silence(currentSpeechItem.mDuration);
+ silence(mCurrentSpeechItem);
}
} else {
cleanUpPlayer();
@@ -615,8 +787,7 @@ public class TtsService extends Service implements OnCompletionListener {
// Utterance is part of the app calling the library
Context ctx;
try {
- ctx = this.createPackageContext(sr.mSourcePackageName,
- 0);
+ ctx = this.createPackageContext(sr.mSourcePackageName, 0);
} catch (NameNotFoundException e) {
e.printStackTrace();
mSpeechQueue.remove(0); // Remove it from the queue and
@@ -639,6 +810,7 @@ public class TtsService extends Service implements OnCompletionListener {
}
mPlayer.setOnCompletionListener(this);
try {
+ mPlayer.setAudioStreamType(getStreamTypeFromParams(mCurrentSpeechItem.mParams));
mPlayer.start();
} catch (IllegalStateException e) {
mSpeechQueue.clear();
@@ -659,6 +831,24 @@ public class TtsService extends Service implements OnCompletionListener {
}
}
+ private int getStreamTypeFromParams(ArrayList<String> paramList) {
+ int streamType = DEFAULT_STREAM_TYPE;
+ if (paramList == null) {
+ return streamType;
+ }
+ for (int i = 0; i < paramList.size() - 1; i = i + 2) {
+ String param = paramList.get(i);
+ if ((param != null) && (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_STREAM))) {
+ try {
+ streamType = Integer.parseInt(paramList.get(i + 1));
+ } catch (NumberFormatException e) {
+ streamType = DEFAULT_STREAM_TYPE;
+ }
+ }
+ }
+ return streamType;
+ }
+
private void cleanUpPlayer() {
if (mPlayer != null) {
mPlayer.release();
@@ -679,7 +869,7 @@ public class TtsService extends Service implements OnCompletionListener {
* something like "/sdcard/myappsounds/mysound.wav".
* @return A boolean that indicates if the synthesis succeeded
*/
- private boolean synthesizeToFile(String text, ArrayList<String> params,
+ private boolean synthesizeToFile(String callingApp, String text, ArrayList<String> params,
String filename) {
// Don't allow a filename that is too long
if (filename.length() > MAX_FILENAME_LENGTH) {
@@ -690,7 +880,7 @@ public class TtsService extends Service implements OnCompletionListener {
if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
return false;
}
- mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT_TO_FILE, filename));
+ mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT_TO_FILE, filename));
if (!mIsSpeaking) {
processSpeechQueue();
}
@@ -711,14 +901,22 @@ public class TtsService extends Service implements OnCompletionListener {
private final android.speech.tts.ITts.Stub mBinder = new Stub() {
- public void registerCallback(ITtsCallback cb) {
- if (cb != null)
+ public int registerCallback(String packageName, ITtsCallback cb) {
+ if (cb != null) {
mCallbacks.register(cb);
+ mCallbacksMap.put(packageName, cb);
+ return TextToSpeech.TTS_SUCCESS;
+ }
+ return TextToSpeech.TTS_ERROR;
}
- public void unregisterCallback(ITtsCallback cb) {
- if (cb != null)
+ public int unregisterCallback(String packageName, ITtsCallback cb) {
+ if (cb != null) {
+ mCallbacksMap.remove(packageName);
mCallbacks.unregister(cb);
+ return TextToSpeech.TTS_SUCCESS;
+ }
+ return TextToSpeech.TTS_ERROR;
}
/**
@@ -728,18 +926,18 @@ public class TtsService extends Service implements OnCompletionListener {
* @param text
* The text that should be spoken
* @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
+ * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
+ * TextToSpeech.TTS_QUEUE_ADD for queued
* @param params
* An ArrayList of parameters. The first element of this
* array controls the type of voice to use.
*/
- public int speak(String text, int queueMode, String[] params) {
+ public int speak(String callingApp, String text, int queueMode, String[] params) {
ArrayList<String> speakingParams = new ArrayList<String>();
if (params != null) {
speakingParams = new ArrayList<String>(Arrays.asList(params));
}
- return mSelf.speak(text, queueMode, speakingParams);
+ return mSelf.speak(callingApp, text, queueMode, speakingParams);
}
/**
@@ -748,17 +946,17 @@ public class TtsService extends Service implements OnCompletionListener {
* @param earcon
* The earcon that should be played
* @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
+ * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
+ * TextToSpeech.TTS_QUEUE_ADD for queued
* @param params
* An ArrayList of parameters.
*/
- public int playEarcon(String earcon, int queueMode, String[] params) {
+ public int playEarcon(String callingApp, String earcon, int queueMode, String[] params) {
ArrayList<String> speakingParams = new ArrayList<String>();
if (params != null) {
speakingParams = new ArrayList<String>(Arrays.asList(params));
}
- return mSelf.playEarcon(earcon, queueMode, speakingParams);
+ return mSelf.playEarcon(callingApp, earcon, queueMode, speakingParams);
}
/**
@@ -767,25 +965,25 @@ public class TtsService extends Service implements OnCompletionListener {
* @param duration
* The duration of the silence that should be played
* @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
+ * TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
+ * TextToSpeech.TTS_QUEUE_ADD for queued
* @param params
* An ArrayList of parameters.
*/
- public int playSilence(long duration, int queueMode, String[] params) {
+ public int playSilence(String callingApp, long duration, int queueMode, String[] params) {
ArrayList<String> speakingParams = new ArrayList<String>();
if (params != null) {
speakingParams = new ArrayList<String>(Arrays.asList(params));
}
- return mSelf.playSilence(duration, queueMode, speakingParams);
+ return mSelf.playSilence(callingApp, duration, queueMode, speakingParams);
}
/**
* Stops all speech output and removes any utterances still in the
* queue.
*/
- public int stop() {
- return mSelf.stop();
+ public int stop(String callingApp) {
+ return mSelf.stop(callingApp);
}
/**
@@ -807,8 +1005,8 @@ public class TtsService extends Service implements OnCompletionListener {
* @param resId
* The resource ID of the sound within its package
*/
- public void addSpeech(String text, String packageName, int resId) {
- mSelf.addSpeech(text, packageName, resId);
+ public void addSpeech(String callingApp, String text, String packageName, int resId) {
+ mSelf.addSpeech(callingApp, text, packageName, resId);
}
/**
@@ -820,8 +1018,8 @@ public class TtsService extends Service implements OnCompletionListener {
* The filename of the sound resource. This must be a
* complete path like: (/sdcard/mysounds/mysoundbite.mp3).
*/
- public void addSpeechFile(String text, String filename) {
- mSelf.addSpeech(text, filename);
+ public void addSpeechFile(String callingApp, String text, String filename) {
+ mSelf.addSpeech(callingApp, text, filename);
}
/**
@@ -834,8 +1032,8 @@ public class TtsService extends Service implements OnCompletionListener {
* @param resId
* The resource ID of the sound within its package
*/
- public void addEarcon(String earcon, String packageName, int resId) {
- mSelf.addEarcon(earcon, packageName, resId);
+ public void addEarcon(String callingApp, String earcon, String packageName, int resId) {
+ mSelf.addEarcon(callingApp, earcon, packageName, resId);
}
/**
@@ -847,8 +1045,8 @@ public class TtsService extends Service implements OnCompletionListener {
* The filename of the sound resource. This must be a
* complete path like: (/sdcard/mysounds/mysoundbite.mp3).
*/
- public void addEarconFile(String earcon, String filename) {
- mSelf.addEarcon(earcon, filename);
+ public void addEarconFile(String callingApp, String earcon, String filename) {
+ mSelf.addEarcon(callingApp, earcon, filename);
}
/**
@@ -858,8 +1056,8 @@ public class TtsService extends Service implements OnCompletionListener {
* @param speechRate
* The speech rate that should be used
*/
- public int setSpeechRate(int speechRate) {
- return mSelf.setSpeechRate(speechRate);
+ public int setSpeechRate(String callingApp, int speechRate) {
+ return mSelf.setSpeechRate(callingApp, speechRate);
}
/**
@@ -869,8 +1067,8 @@ public class TtsService extends Service implements OnCompletionListener {
* @param pitch
* The pitch that should be used for the synthesized voice
*/
- public int setPitch(int pitch) {
- return mSelf.setPitch(pitch);
+ public int setPitch(String callingApp, int pitch) {
+ return mSelf.setPitch(callingApp, pitch);
}
/**
@@ -904,8 +1102,8 @@ public class TtsService extends Service implements OnCompletionListener {
* @param country the three letter ISO country code.
* @param variant the variant code associated with the country and language pair.
*/
- public int setLanguage(String lang, String country, String variant) {
- return mSelf.setLanguage(lang, country, variant);
+ public int setLanguage(String callingApp, String lang, String country, String variant) {
+ return mSelf.setLanguage(callingApp, lang, country, variant);
}
/**
@@ -922,13 +1120,13 @@ public class TtsService extends Service implements OnCompletionListener {
* be something like "/sdcard/myappsounds/mysound.wav".
* @return A boolean that indicates if the synthesis succeeded
*/
- public boolean synthesizeToFile(String text, String[] params,
+ public boolean synthesizeToFile(String callingApp, String text, String[] params,
String filename) {
ArrayList<String> speakingParams = new ArrayList<String>();
if (params != null) {
speakingParams = new ArrayList<String>(Arrays.asList(params));
}
- return mSelf.synthesizeToFile(text, speakingParams, filename);
+ return mSelf.synthesizeToFile(callingApp, text, speakingParams, filename);
}
};
diff --git a/packages/VpnServices/res/values-cs/strings.xml b/packages/VpnServices/res/values-cs/strings.xml
new file mode 100644
index 0000000..5f3522d
--- /dev/null
+++ b/packages/VpnServices/res/values-cs/strings.xml
@@ -0,0 +1,25 @@
+<?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="app_label">"Služby VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (2567196609405266775) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (3564361788336568244) -->
+ <skip />
+ <!-- no translation found for vpn_notification_hint_disconnected (1952209867082269429) -->
+ <skip />
+</resources>
diff --git a/packages/VpnServices/res/values-de/strings.xml b/packages/VpnServices/res/values-de/strings.xml
new file mode 100644
index 0000000..93fa474
--- /dev/null
+++ b/packages/VpnServices/res/values-de/strings.xml
@@ -0,0 +1,25 @@
+<?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="app_label">"VPN-Dienste"</string>
+ <!-- no translation found for vpn_notification_title_connected (2567196609405266775) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (3564361788336568244) -->
+ <skip />
+ <!-- no translation found for vpn_notification_hint_disconnected (1952209867082269429) -->
+ <skip />
+</resources>
diff --git a/packages/VpnServices/res/values-es/strings.xml b/packages/VpnServices/res/values-es/strings.xml
new file mode 100644
index 0000000..bb4f348
--- /dev/null
+++ b/packages/VpnServices/res/values-es/strings.xml
@@ -0,0 +1,25 @@
+<?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="app_label">"Servicios VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (2567196609405266775) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (3564361788336568244) -->
+ <skip />
+ <!-- no translation found for vpn_notification_hint_disconnected (1952209867082269429) -->
+ <skip />
+</resources>
diff --git a/packages/VpnServices/res/values-fr/strings.xml b/packages/VpnServices/res/values-fr/strings.xml
new file mode 100644
index 0000000..4395d03
--- /dev/null
+++ b/packages/VpnServices/res/values-fr/strings.xml
@@ -0,0 +1,25 @@
+<?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="app_label">"Services VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (2567196609405266775) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (3564361788336568244) -->
+ <skip />
+ <!-- no translation found for vpn_notification_hint_disconnected (1952209867082269429) -->
+ <skip />
+</resources>
diff --git a/packages/VpnServices/res/values-it/strings.xml b/packages/VpnServices/res/values-it/strings.xml
new file mode 100644
index 0000000..76e0214
--- /dev/null
+++ b/packages/VpnServices/res/values-it/strings.xml
@@ -0,0 +1,25 @@
+<?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="app_label">"Servizi VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (2567196609405266775) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (3564361788336568244) -->
+ <skip />
+ <!-- no translation found for vpn_notification_hint_disconnected (1952209867082269429) -->
+ <skip />
+</resources>
diff --git a/packages/VpnServices/res/values-nl/strings.xml b/packages/VpnServices/res/values-nl/strings.xml
new file mode 100644
index 0000000..9a50f3b
--- /dev/null
+++ b/packages/VpnServices/res/values-nl/strings.xml
@@ -0,0 +1,25 @@
+<?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="app_label">"VPN-services"</string>
+ <!-- no translation found for vpn_notification_title_connected (2567196609405266775) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (3564361788336568244) -->
+ <skip />
+ <!-- no translation found for vpn_notification_hint_disconnected (1952209867082269429) -->
+ <skip />
+</resources>
diff --git a/packages/VpnServices/res/values-pl/strings.xml b/packages/VpnServices/res/values-pl/strings.xml
new file mode 100644
index 0000000..d2e8c60
--- /dev/null
+++ b/packages/VpnServices/res/values-pl/strings.xml
@@ -0,0 +1,25 @@
+<?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="app_label">"Usługi VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (2567196609405266775) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (3564361788336568244) -->
+ <skip />
+ <!-- no translation found for vpn_notification_hint_disconnected (1952209867082269429) -->
+ <skip />
+</resources>
diff --git a/packages/VpnServices/res/values-zh-rTW/strings.xml b/packages/VpnServices/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..021077b
--- /dev/null
+++ b/packages/VpnServices/res/values-zh-rTW/strings.xml
@@ -0,0 +1,25 @@
+<?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="app_label">"VPN 服務"</string>
+ <!-- no translation found for vpn_notification_title_connected (2567196609405266775) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (3564361788336568244) -->
+ <skip />
+ <!-- no translation found for vpn_notification_hint_disconnected (1952209867082269429) -->
+ <skip />
+</resources>
diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
index 7dd9d9e..e4c070f 100644
--- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
@@ -18,6 +18,7 @@ package com.android.server.vpn;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
+import android.net.vpn.VpnManager;
import android.os.SystemProperties;
import android.util.Log;
@@ -48,6 +49,9 @@ public class AndroidServiceProxy extends ProcessProxy {
private static final int END_OF_ARGUMENTS = 255;
+ private static final int STOP_SERVICE = -1;
+ private static final int AUTH_ERROR_CODE = 51;
+
private String mServiceName;
private String mSocketName;
private LocalSocket mKeepaliveSocket;
@@ -72,14 +76,22 @@ public class AndroidServiceProxy extends ProcessProxy {
@Override
public synchronized void stop() {
- if (isRunning()) setResultAndCloseControlSocket(-1);
+ if (isRunning()) {
+ try {
+ setResultAndCloseControlSocket(STOP_SERVICE);
+ } catch (IOException e) {
+ // should not occur
+ throw new RuntimeException(e);
+ }
+ }
+ Log.d(mTag, "----- Stop: " + mServiceName);
SystemProperties.set(SVC_STOP_CMD, mServiceName);
}
/**
* Sends a command with arguments to the service through the control socket.
*/
- public void sendCommand(String ...args) throws IOException {
+ public synchronized void sendCommand(String ...args) throws IOException {
OutputStream out = getControlSocketOutput();
for (String arg : args) outputString(out, arg);
out.write(END_OF_ARGUMENTS);
@@ -94,7 +106,14 @@ public class AndroidServiceProxy extends ProcessProxy {
@Override
protected void performTask() throws IOException {
String svc = mServiceName;
- Log.d(mTag, "+++++ Execute: " + svc);
+ Log.d(mTag, "----- Stop the daemon just in case: " + mServiceName);
+ SystemProperties.set(SVC_STOP_CMD, mServiceName);
+ if (!blockUntil(SVC_STATE_STOPPED, 5)) {
+ throw new IOException("cannot start service anew: " + svc
+ + ", it is still running");
+ }
+
+ Log.d(mTag, "+++++ Start: " + svc);
SystemProperties.set(SVC_START_CMD, svc);
boolean success = blockUntil(SVC_STATE_RUNNING, WAITING_TIME);
@@ -114,30 +133,22 @@ public class AndroidServiceProxy extends ProcessProxy {
InputStream in = s.getInputStream();
int data = in.read();
if (data >= 0) {
- Log.d(mTag, "got data from keepalive socket: " + data);
-
- if (data == 0) {
- // re-establish the connection:
- // synchronized here so that checkSocketResult()
- // returns when new mKeepaliveSocket is available for
- // next cmd
- synchronized (this) {
- setResultAndCloseControlSocket((byte) data);
- s = mKeepaliveSocket = createServiceSocket();
- }
- } else {
- // keep the socket
- setSocketResult(data);
- }
+ Log.d(mTag, "got data from control socket: " + data);
+
+ setSocketResult(data);
} else {
// service is gone
if (mControlSocketInUse) setSocketResult(-1);
break;
}
}
- Log.d(mTag, "keepalive connection closed");
+ Log.d(mTag, "control connection closed");
} catch (IOException e) {
- Log.d(mTag, "keepalive socket broken: " + e.getMessage());
+ if (e instanceof VpnConnectingError) {
+ throw e;
+ } else {
+ Log.d(mTag, "control socket broken: " + e.getMessage());
+ }
}
// Wait 5 seconds for the service to exit
@@ -179,7 +190,7 @@ public class AndroidServiceProxy extends ProcessProxy {
}
}
- private synchronized void checkSocketResult() throws IOException {
+ private void checkSocketResult() throws IOException {
try {
// will be notified when the result comes back from service
if (mSocketResult == null) wait();
@@ -194,14 +205,21 @@ public class AndroidServiceProxy extends ProcessProxy {
}
}
- private synchronized void setSocketResult(int result) {
+ private synchronized void setSocketResult(int result)
+ throws VpnConnectingError {
if (mControlSocketInUse) {
mSocketResult = result;
notifyAll();
+ } else if (result > 0) {
+ // error from daemon
+ throw new VpnConnectingError((result == AUTH_ERROR_CODE)
+ ? VpnManager.VPN_ERROR_AUTH
+ : VpnManager.VPN_ERROR_CONNECTION_FAILED);
}
}
- private void setResultAndCloseControlSocket(int result) {
+ private void setResultAndCloseControlSocket(int result)
+ throws VpnConnectingError {
setSocketResult(result);
try {
mKeepaliveSocket.shutdownInput();
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
index 6abf81c..7b3ddf8 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
@@ -30,12 +30,11 @@ class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> {
@Override
protected void connect(String serverIp, String username, String password)
throws IOException {
- String hostIp = getHostIp();
L2tpIpsecPskProfile p = getProfile();
// IPSEC
AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
- ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
+ ipsecService.sendCommand(serverIp, L2tpService.L2TP_PORT,
p.getPresharedKey());
sleep(2000); // 2 seconds
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
index 825953c..e2d4ff4 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
@@ -30,11 +30,9 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
@Override
protected void connect(String serverIp, String username, String password)
throws IOException {
- String hostIp = getHostIp();
-
// IPSEC
AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
- ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
+ ipsecService.sendCommand(serverIp, L2tpService.L2TP_PORT,
getUserkeyPath(), getUserCertPath(), getCaCertPath());
sleep(2000); // 2 seconds
diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
index 16d253a..5fac799 100644
--- a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
+++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
@@ -48,7 +48,6 @@ class MtpdHelper {
"linkname", VPN_LINKNAME,
"name", username,
"password", password,
- "ipparam", serverIp + "@" + vpnService.getGatewayIp(),
"refuse-eap", "nodefaultroute", "usepeerdns",
"idle", "1800",
"mtu", "1400",
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnConnectingError.java b/packages/VpnServices/src/com/android/server/vpn/VpnConnectingError.java
new file mode 100644
index 0000000..3c4ec7d
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnConnectingError.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package com.android.server.vpn;
+
+import java.io.IOException;
+
+/**
+ * Exception thrown when a connecting attempt fails.
+ */
+class VpnConnectingError extends IOException {
+ private int mErrorCode;
+
+ VpnConnectingError(int errorCode) {
+ super("Connecting error: " + errorCode);
+ mErrorCode = errorCode;
+ }
+
+ int getErrorCode() {
+ return mErrorCode;
+ }
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index a60788a..87bd780 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -20,7 +20,6 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
-import android.net.NetworkUtils;
import android.net.vpn.VpnManager;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState;
@@ -30,10 +29,8 @@ import android.util.Log;
import java.io.IOException;
import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.Socket;
+import java.net.UnknownHostException;
import java.util.ArrayList;
-import java.util.Enumeration;
import java.util.List;
/**
@@ -46,9 +43,9 @@ abstract class VpnService<E extends VpnProfile> {
private static final String DNS2 = "net.dns2";
private static final String VPN_DNS1 = "vpn.dns1";
private static final String VPN_DNS2 = "vpn.dns2";
- private static final String VPN_UP = "vpn.up";
- private static final String VPN_IS_UP = "1";
- private static final String VPN_IS_DOWN = "0";
+ private static final String VPN_STATUS = "vpn.status";
+ private static final String VPN_IS_UP = "ok";
+ private static final String VPN_IS_DOWN = "down";
private static final String REMOTE_IP = "net.ipremote";
private static final String DNS_DOMAIN_SUFFICES = "net.dns.search";
@@ -60,6 +57,7 @@ abstract class VpnService<E extends VpnProfile> {
private VpnState mState = VpnState.IDLE;
private boolean mInError;
+ private VpnConnectingError mError;
// connection settings
private String mOriginalDns1;
@@ -67,7 +65,6 @@ abstract class VpnService<E extends VpnProfile> {
private String mVpnDns1 = "";
private String mVpnDns2 = "";
private String mOriginalDomainSuffices;
- private String mHostIp;
private long mStartTime; // VPN connection start time
@@ -106,14 +103,6 @@ abstract class VpnService<E extends VpnProfile> {
}
/**
- * Returns the host IP for establishing the VPN connection.
- */
- protected String getHostIp() throws IOException {
- if (mHostIp == null) mHostIp = reallyGetHostIp();
- return mHostIp;
- }
-
- /**
* Returns the IP address of the specified host name.
*/
protected String getIp(String hostName) throws IOException {
@@ -121,21 +110,6 @@ abstract class VpnService<E extends VpnProfile> {
}
/**
- * Returns the IP address of the default gateway.
- */
- protected String getGatewayIp() throws IOException {
- Enumeration<NetworkInterface> ifces =
- NetworkInterface.getNetworkInterfaces();
- for (; ifces.hasMoreElements(); ) {
- NetworkInterface ni = ifces.nextElement();
- int gateway = NetworkUtils.getDefaultRoute(ni.getName());
- if (gateway == 0) continue;
- return toInetAddress(gateway).getHostAddress();
- }
- throw new IOException("Default gateway is not available");
- }
-
- /**
* Sets the system property. The method is blocked until the value is
* settled in.
* @param name the name of the property
@@ -166,20 +140,28 @@ abstract class VpnService<E extends VpnProfile> {
return mState;
}
- synchronized void onConnect(String username, String password)
- throws IOException {
- mState = VpnState.CONNECTING;
- broadcastConnectivity(VpnState.CONNECTING);
+ synchronized boolean onConnect(String username, String password) {
+ try {
+ mState = VpnState.CONNECTING;
+ broadcastConnectivity(VpnState.CONNECTING);
- String serverIp = getIp(getProfile().getServerName());
+ String serverIp = getIp(getProfile().getServerName());
- onBeforeConnect();
- connect(serverIp, username, password);
- waitUntilConnectedOrTimedout();
+ onBeforeConnect();
+ connect(serverIp, username, password);
+ waitUntilConnectedOrTimedout();
+ return true;
+ } catch (Throwable e) {
+ Log.e(TAG, "onConnect()", e);
+ mError = newConnectingError(e);
+ onError();
+ return false;
+ }
}
synchronized void onDisconnect(boolean cleanUpServices) {
try {
+ Log.d(TAG, " disconnecting VPN...");
mState = VpnState.DISCONNECTING;
broadcastConnectivity(VpnState.DISCONNECTING);
mNotification.showDisconnect();
@@ -189,7 +171,7 @@ abstract class VpnService<E extends VpnProfile> {
mServiceHelper.stop();
} catch (Throwable e) {
- Log.e(TAG, "onError()", e);
+ Log.e(TAG, "onDisconnect()", e);
onFinalCleanUp();
}
}
@@ -214,26 +196,35 @@ abstract class VpnService<E extends VpnProfile> {
SystemProperties.set(VPN_DNS1, "-");
SystemProperties.set(VPN_DNS2, "-");
- SystemProperties.set(VPN_UP, VPN_IS_DOWN);
- Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_UP));
+ SystemProperties.set(VPN_STATUS, VPN_IS_DOWN);
+ Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_STATUS));
}
private void waitUntilConnectedOrTimedout() {
- sleep(2000); // 2 seconds
- for (int i = 0; i < 60; i++) {
- if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) {
- onConnected();
- return;
- }
- sleep(500); // 0.5 second
- }
+ // Run this in the background thread to not block UI
+ new Thread(new Runnable() {
+ public void run() {
+ sleep(2000); // 2 seconds
+ for (int i = 0; i < 60; i++) {
+ if (VPN_IS_UP.equals(SystemProperties.get(VPN_STATUS))) {
+ onConnected();
+ return;
+ } else if (mState != VpnState.CONNECTING) {
+ break;
+ }
+ sleep(500); // 0.5 second
+ }
- synchronized (this) {
- if (mState == VpnState.CONNECTING) {
- Log.d(TAG, " connecting timed out !!");
- onError();
+ synchronized (VpnService.this) {
+ if (mState == VpnState.CONNECTING) {
+ Log.d(TAG, " connecting timed out !!");
+ mError = newConnectingError(
+ new IOException("Connecting timed out"));
+ onError();
+ }
+ }
}
- }
+ }).start();
}
private synchronized void onConnected() {
@@ -264,6 +255,13 @@ abstract class VpnService<E extends VpnProfile> {
mContext.stopSelf();
}
+ private VpnConnectingError newConnectingError(Throwable e) {
+ return new VpnConnectingError(
+ (e instanceof UnknownHostException)
+ ? VpnManager.VPN_ERROR_UNKNOWN_SERVER
+ : VpnManager.VPN_ERROR_CONNECTION_FAILED);
+ }
+
private synchronized void onOneServiceGone() {
switch (mState) {
case IDLE:
@@ -304,12 +302,12 @@ abstract class VpnService<E extends VpnProfile> {
private void saveVpnDnsProperties() {
mOriginalDns1 = mOriginalDns2 = "";
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < 5; i++) {
mVpnDns1 = SystemProperties.get(VPN_DNS1);
mVpnDns2 = SystemProperties.get(VPN_DNS2);
if (mOriginalDns1.equals(mVpnDns1)) {
Log.d(TAG, "wait for vpn dns to settle in..." + i);
- sleep(500);
+ sleep(200);
} else {
mOriginalDns1 = SystemProperties.get(DNS1);
mOriginalDns2 = SystemProperties.get(DNS2);
@@ -322,7 +320,9 @@ abstract class VpnService<E extends VpnProfile> {
return;
}
}
- Log.e(TAG, "saveVpnDnsProperties(): DNS not updated??");
+ Log.d(TAG, "saveVpnDnsProperties(): DNS not updated??");
+ mOriginalDns1 = mVpnDns1 = SystemProperties.get(DNS1);
+ mOriginalDns2 = mVpnDns2 = SystemProperties.get(DNS2);
}
private void saveAndSetDomainSuffices() {
@@ -340,7 +340,13 @@ abstract class VpnService<E extends VpnProfile> {
}
private void broadcastConnectivity(VpnState s) {
- new VpnManager(mContext).broadcastConnectivity(mProfile.getName(), s);
+ VpnManager m = new VpnManager(mContext);
+ if ((s == VpnState.IDLE) && (mError != null)) {
+ m.broadcastConnectivity(mProfile.getName(), s,
+ mError.getErrorCode());
+ } else {
+ m.broadcastConnectivity(mProfile.getName(), s);
+ }
}
private void startConnectivityMonitor() {
@@ -373,26 +379,11 @@ abstract class VpnService<E extends VpnProfile> {
private void checkDnsProperties() {
String dns1 = SystemProperties.get(DNS1);
if (!mVpnDns1.equals(dns1)) {
- Log.w(TAG, " @@ !!! dns being overridden");
+ Log.w(TAG, " dns being overridden by: " + dns1);
onError();
}
}
- private String reallyGetHostIp() throws IOException {
- Enumeration<NetworkInterface> ifces =
- NetworkInterface.getNetworkInterfaces();
- for (; ifces.hasMoreElements(); ) {
- NetworkInterface ni = ifces.nextElement();
- int gateway = NetworkUtils.getDefaultRoute(ni.getName());
- if (gateway == 0) continue;
- Enumeration<InetAddress> addrs = ni.getInetAddresses();
- for (; addrs.hasMoreElements(); ) {
- return addrs.nextElement().getHostAddress();
- }
- }
- throw new IOException("Host IP is not available");
- }
-
protected void sleep(int ms) {
try {
Thread.currentThread().sleep(ms);
@@ -440,6 +431,9 @@ abstract class VpnService<E extends VpnProfile> {
//@Override
public void error(ProcessProxy p, Throwable e) {
Log.e(TAG, "service error: " + p.getName(), e);
+ if (e instanceof VpnConnectingError) {
+ mError = (VpnConnectingError) e;
+ }
commonCallback((AndroidServiceProxy) p);
}
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
index 617875e..32b8e51 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
@@ -27,7 +27,6 @@ import android.net.vpn.VpnManager;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState;
import android.os.IBinder;
-import android.util.Log;
import java.io.IOException;
@@ -55,6 +54,12 @@ public class VpnServiceBinder extends Service {
}
};
+ public void onStart (Intent intent, int startId) {
+ super.onStart(intent, startId);
+ setForeground(true);
+ android.util.Log.d("VpnServiceBinder", "becomes a foreground service");
+ }
+
public IBinder onBind(Intent intent) {
return mBinder;
}
@@ -62,21 +67,13 @@ public class VpnServiceBinder extends Service {
private synchronized boolean connect(
VpnProfile p, String username, String password) {
if (mService != null) return false;
- try {
- mService = createService(p);
- mService.onConnect(username, password);
- return true;
- } catch (Throwable e) {
- Log.e(TAG, "connect()", e);
- if (mService != null) mService.onError();
- return false;
- }
+ mService = createService(p);
+ return mService.onConnect(username, password);
}
private synchronized void checkStatus(VpnProfile p) {
- if (mService == null) broadcastConnectivity(p.getName(), VpnState.IDLE);
-
- if (!p.getName().equals(mService.mProfile.getName())) {
+ if ((mService == null)
+ || (!p.getName().equals(mService.mProfile.getName()))) {
broadcastConnectivity(p.getName(), VpnState.IDLE);
} else {
broadcastConnectivity(p.getName(), mService.getState());