Merge "Bluetooth SCO audio API improvements."
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
index 6f4c15b..d698b54 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -18,6 +18,7 @@
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.os.Bundle;
+import android.os.Handler;
import android.util.Log;
/**
@@ -49,16 +50,20 @@
private final float mPan;
private final Object mStateLock = new Object();
- private AudioTrack mAudioTrack = null;
+ private final Handler mAudioTrackHandler;
+ private volatile AudioTrack mAudioTrack = null;
private boolean mStopped = false;
private boolean mDone = false;
+ private volatile boolean mWriteErrorOccured;
PlaybackSynthesisRequest(String text, Bundle params,
- int streamType, float volume, float pan) {
+ int streamType, float volume, float pan, Handler audioTrackHandler) {
super(text, params);
mStreamType = streamType;
mVolume = volume;
mPan = pan;
+ mAudioTrackHandler = audioTrackHandler;
+ mWriteErrorOccured = false;
}
@Override
@@ -70,14 +75,28 @@
}
}
+ // Always guarded by mStateLock.
private void cleanUp() {
if (DBG) Log.d(TAG, "cleanUp()");
- if (mAudioTrack != null) {
- mAudioTrack.flush();
- mAudioTrack.stop();
- mAudioTrack.release();
- mAudioTrack = null;
+ if (mAudioTrack == null) {
+ return;
}
+
+ final AudioTrack audioTrack = mAudioTrack;
+ mAudioTrack = null;
+
+ // Clean up on the audiotrack handler thread.
+ //
+ // NOTE: It isn't very clear whether AudioTrack is thread safe.
+ // If it is we can clean up on the current (synthesis) thread.
+ mAudioTrackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ audioTrack.flush();
+ audioTrack.stop();
+ audioTrack.release();
+ }
+ });
}
@Override
@@ -146,10 +165,15 @@
Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
+ offset + "," + length + ")");
}
- if (length > getMaxBufferSize()) {
- throw new IllegalArgumentException("buffer is too large (" + length + " bytes)");
+ if (length > getMaxBufferSize() || length <= 0) {
+ throw new IllegalArgumentException("buffer is too large or of zero length (" +
+ + length + " bytes)");
}
synchronized (mStateLock) {
+ if (mWriteErrorOccured) {
+ if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+ return TextToSpeech.ERROR;
+ }
if (mStopped) {
if (DBG) Log.d(TAG, "Request has been aborted.");
return TextToSpeech.ERROR;
@@ -158,22 +182,33 @@
Log.e(TAG, "audioAvailable(): Not started");
return TextToSpeech.ERROR;
}
- int playState = mAudioTrack.getPlayState();
- if (playState == AudioTrack.PLAYSTATE_STOPPED) {
- if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
- mAudioTrack.play();
- }
- // TODO: loop until all data is written?
- if (DBG) Log.d(TAG, "AudioTrack.write()");
- int count = mAudioTrack.write(buffer, offset, length);
- if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count);
- if (count < 0) {
- Log.e(TAG, "Writing to AudioTrack failed: " + count);
- cleanUp();
- return TextToSpeech.ERROR;
- } else {
- return TextToSpeech.SUCCESS;
- }
+ final AudioTrack audioTrack = mAudioTrack;
+ // Sigh, another copy.
+ final byte[] bufferCopy = new byte[length];
+ System.arraycopy(buffer, offset, bufferCopy, 0, length);
+
+ mAudioTrackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ int playState = audioTrack.getPlayState();
+ if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+ if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
+ audioTrack.play();
+ }
+ // TODO: loop until all data is written?
+ if (DBG) Log.d(TAG, "AudioTrack.write()");
+ int count = audioTrack.write(bufferCopy, 0, bufferCopy.length);
+ // The semantics of this change very slightly. Earlier, we would
+ // report an error immediately, Now we will return an error on
+ // the next API call, usually done( ) or another audioAvailable( )
+ // call.
+ if (count < 0) {
+ mWriteErrorOccured = true;
+ }
+ }
+ });
+
+ return TextToSpeech.SUCCESS;
}
}
@@ -181,6 +216,10 @@
public int done() {
if (DBG) Log.d(TAG, "done()");
synchronized (mStateLock) {
+ if (mWriteErrorOccured) {
+ if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+ return TextToSpeech.ERROR;
+ }
if (mStopped) {
if (DBG) Log.d(TAG, "Request has been aborted.");
return TextToSpeech.ERROR;
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index f32474f..717dde8 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -51,6 +51,7 @@
private static final String SYNTH_THREAD_NAME = "SynthThread";
private SynthHandler mSynthHandler;
+ private Handler mAudioTrackHandler;
private CallbackMap mCallbacks;
@@ -63,6 +64,10 @@
synthThread.start();
mSynthHandler = new SynthHandler(synthThread.getLooper());
+ HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread");
+ audioTrackThread.start();
+ mAudioTrackHandler = new Handler(audioTrackThread.getLooper());
+
mCallbacks = new CallbackMap();
// Load default language
@@ -75,6 +80,7 @@
// Tell the synthesizer to stop
mSynthHandler.quit();
+ mAudioTrackHandler.getLooper().quit();
// Unregister all callbacks.
mCallbacks.kill();
@@ -444,7 +450,7 @@
protected SynthesisRequest createSynthesisRequest() {
return new PlaybackSynthesisRequest(mText, mParams,
- getStreamType(), getVolume(), getPan());
+ getStreamType(), getVolume(), getPan(), mAudioTrackHandler);
}
private void setRequestParams(SynthesisRequest request) {
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index b0b16bb..66fcc27 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -360,10 +360,9 @@
}
// User agent strings.
- private static final String DESKTOP_USERAGENT =
- "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
- + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
- + " Safari/530.17";
+ private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " +
+ "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " +
+ "Chrome/11.0.696.34 Safari/534.24";
private static final String IPHONE_USERAGENT =
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
+ " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ccb3518..7f4cc81 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1852,7 +1852,7 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
- AppleWebKit/534.16 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.16</string>
+ AppleWebKit/534.20 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.20</string>
<!-- Do not translate. WebView User Agent targeted content -->
<string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index 33cc97e..fb53686 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -32,6 +32,7 @@
import java.io.ByteArrayOutputStream;
+import java.util.Locale;
/**
* Enumeration for representing the tag value of COMPREHENSION-TLV objects. If
@@ -273,8 +274,20 @@
sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
break;
case PROVIDE_LOCAL_INFORMATION:
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
- return;
+ ResponseData resp;
+ switch (cmdParams.cmdDet.commandQualifier) {
+ case CommandParamsFactory.DTTZ_SETTING:
+ resp = new DTTZResponseData(null);
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
+ break;
+ case CommandParamsFactory.LANGUAGE_SETTING:
+ resp = new LanguageResponseData(Locale.getDefault().getLanguage());
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
+ break;
+ default:
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ return;
+ }
case LAUNCH_BROWSER:
case SELECT_ITEM:
case GET_INPUT:
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 12204a0..686fe46 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -53,6 +53,7 @@
static final int REFRESH_UICC_RESET = 0x04;
// Command Qualifier values for PLI command
+ static final int DTTZ_SETTING = 0x03;
static final int LANGUAGE_SETTING = 0x04;
static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
@@ -883,6 +884,10 @@
throws ResultException {
CatLog.d(this, "process ProvideLocalInfo");
switch (cmdDet.commandQualifier) {
+ case DTTZ_SETTING:
+ CatLog.d(this, "PLI [DTTZ_SETTING]");
+ mCmdParams = new CommandParams(cmdDet);
+ break;
case LANGUAGE_SETTING:
CatLog.d(this, "PLI [LANGUAGE_SETTING]");
mCmdParams = new CommandParams(cmdDet);
diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
index 95f0399..55a2b63 100644
--- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
@@ -18,6 +18,8 @@
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.GsmAlphabet;
+import java.util.Calendar;
+import com.android.internal.telephony.cat.AppInterface.CommandType;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
@@ -147,4 +149,109 @@
}
}
+// For "PROVIDE LOCAL INFORMATION" command.
+// See TS 31.111 section 6.4.15/ETSI TS 102 223
+// TS 31.124 section 27.22.4.15 for test spec
+class LanguageResponseData extends ResponseData {
+ private String lang;
+
+ public LanguageResponseData(String lang) {
+ super();
+ this.lang = lang;
+ }
+
+ @Override
+ public void format(ByteArrayOutputStream buf) {
+ if (buf == null) {
+ return;
+ }
+
+ // Text string object
+ int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value();
+ buf.write(tag); // tag
+
+ byte[] data;
+
+ if (lang != null && lang.length() > 0) {
+ data = GsmAlphabet.stringToGsm8BitPacked(lang);
+ }
+ else {
+ data = new byte[0];
+ }
+
+ buf.write(data.length);
+
+ for (byte b : data) {
+ buf.write(b);
+ }
+ }
+}
+
+// For "PROVIDE LOCAL INFORMATION" command.
+// See TS 31.111 section 6.4.15/ETSI TS 102 223
+// TS 31.124 section 27.22.4.15 for test spec
+class DTTZResponseData extends ResponseData {
+ private Calendar calendar;
+
+ public DTTZResponseData(Calendar cal) {
+ super();
+ calendar = cal;
+ }
+
+ @Override
+ public void format(ByteArrayOutputStream buf) {
+ if (buf == null) {
+ return;
+ }
+
+ // DTTZ object
+ int tag = 0x80 | CommandType.PROVIDE_LOCAL_INFORMATION.value();
+ buf.write(tag); // tag
+
+ byte[] data = new byte[8];
+ byte btmp; // temp variable
+
+ data[0] = 0x07; // Write length of DTTZ data
+
+ if (calendar == null) {
+ calendar = Calendar.getInstance();
+ }
+ // Fill year byte
+ btmp = (byte) (calendar.get(java.util.Calendar.YEAR) % 100);
+ data[1] = (byte) (btmp / 10);
+ data[1] += (byte) ((btmp % 10) << 4);
+
+ // Fill month byte
+ btmp = (byte) (calendar.get(java.util.Calendar.MONTH) + 1);
+ data[2] = (byte) (btmp / 10);
+ data[2] += (byte) ((btmp % 10) << 4);
+
+ // Fill day byte
+ btmp = (byte) (calendar.get(java.util.Calendar.DATE));
+ data[3] = (byte) (btmp / 10);
+ data[3] += (byte) ((btmp % 10) << 4);
+
+ // Fill hour byte
+ btmp = (byte) (calendar.get(java.util.Calendar.HOUR_OF_DAY));
+ data[4] = (byte) (btmp / 10);
+ data[4] += (byte) ((btmp % 10) << 4);
+
+ // Fill minute byte
+ btmp = (byte) (calendar.get(java.util.Calendar.MINUTE));
+ data[5] = (byte) (btmp / 10);
+ data[5] += (byte) ((btmp % 10) << 4);
+
+ // Fill second byte
+ btmp = (byte) (calendar.get(java.util.Calendar.SECOND));
+ data[6] = (byte) (btmp / 10);
+ data[6] += (byte) ((btmp % 10) << 4);
+
+ // No time zone info
+ data[7] = (byte) 0xFF;
+
+ for (byte b : data) {
+ buf.write(b);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 134f29f..bd6da64 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -491,7 +491,7 @@
mNetworkInfo.setIsAvailable(false);
mLinkProperties.clear();
mLastBssid = null;
- mLastNetworkId = -1;
+ mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSignalLevel = -1;
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
@@ -837,11 +837,12 @@
}
public void connectNetwork(WifiConfiguration wifiConfig) {
- /* arg1 is used to indicate netId, force a netId value of -1 when
- * we are passing a configuration since the default value of
- * 0 is a valid netId
+ /* arg1 is used to indicate netId, force a netId value of
+ * WifiConfiguration.INVALID_NETWORK_ID when we are passing
+ * a configuration since the default value of 0 is a valid netId
*/
- sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
+ sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
+ 0, wifiConfig));
}
public void saveNetwork(WifiConfiguration wifiConfig) {
@@ -1445,7 +1446,7 @@
mWifiInfo.setInetAddress(null);
mWifiInfo.setBSSID(null);
mWifiInfo.setSSID(null);
- mWifiInfo.setNetworkId(-1);
+ mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
mWifiInfo.setRssi(MIN_RSSI);
mWifiInfo.setLinkSpeed(-1);
@@ -1457,7 +1458,7 @@
mLinkProperties.clear();
mLastBssid= null;
- mLastNetworkId = -1;
+ mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
}
@@ -2050,7 +2051,7 @@
mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
/* Initialize data structures */
mLastBssid = null;
- mLastNetworkId = -1;
+ mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSignalLevel = -1;
mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
@@ -2545,7 +2546,10 @@
// Network id is only valid when we start connecting
if (SupplicantState.isConnecting(state)) {
mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ } else {
+ mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
}
+
if (state == SupplicantState.ASSOCIATING) {
/* BSSID is valid only in ASSOCIATING state */
mWifiInfo.setBSSID(stateChangeResult.BSSID);