Merge change 7406 into donut

* changes:
  Update Searchables test with new GlobalSearch name
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index 39360cd..474fa82 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -38,6 +38,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Vector;
 import java.util.regex.Pattern;
@@ -72,7 +73,12 @@
     private static final int HTTP_NOT_FOUND = 404;
     private static final int HTTP_PROXY_AUTH = 407;
 
-    private static final String CERT_MIMETYPE = "application/x-x509-ca-cert";
+    private static HashSet<String> sCertificateMimeTypeMap;
+    static {
+        sCertificateMimeTypeMap = new HashSet<String>();
+        sCertificateMimeTypeMap.add("application/x-x509-ca-cert");
+        sCertificateMimeTypeMap.add("application/x-pkcs12");
+    }
 
     private static int sNativeLoaderCount;
 
@@ -318,7 +324,17 @@
             if (mMimeType.equalsIgnoreCase("text/plain") ||
                     mMimeType.equalsIgnoreCase("application/octet-stream")) {
 
-                String newMimeType = guessMimeTypeFromExtension();
+                // for attachment, use the filename in the Content-Disposition
+                // to guess the mimetype
+                String contentDisposition = headers.getContentDisposition();
+                String url = null;
+                if (contentDisposition != null) {
+                    url = URLUtil.parseContentDisposition(contentDisposition);
+                }
+                if (url == null) {
+                    url = mUrl;
+                }
+                String newMimeType = guessMimeTypeFromExtension(url);
                 if (newMimeType != null) {
                     mMimeType = newMimeType;
                 }
@@ -936,7 +952,7 @@
 
     // This commits the headers without checking the response status code.
     private void commitHeaders() {
-        if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) {
+        if (mIsMainPageLoader && sCertificateMimeTypeMap.contains(mMimeType)) {
             // In the case of downloading certificate, we will save it to the
             // Keystore in commitLoad. Do not call webcore.
             return;
@@ -982,7 +998,7 @@
     private void commitLoad() {
         if (mCancelled) return;
 
-        if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) {
+        if (mIsMainPageLoader && sCertificateMimeTypeMap.contains(mMimeType)) {
             // In the case of downloading certificate, we will save it to the
             // Keystore and stop the current loading so that it will not
             // generate a new history page
@@ -1409,7 +1425,7 @@
             // of frames. If no content-type was specified, it is fine to
             // default to text/html.
             mMimeType = "text/html";
-            String newMimeType = guessMimeTypeFromExtension();
+            String newMimeType = guessMimeTypeFromExtension(mUrl);
             if (newMimeType != null) {
                 mMimeType =  newMimeType;
             }
@@ -1419,15 +1435,15 @@
     /**
      * guess MIME type based on the file extension.
      */
-    private String guessMimeTypeFromExtension() {
+    private String guessMimeTypeFromExtension(String url) {
         // PENDING: need to normalize url
         if (WebView.LOGV_ENABLED) {
-            Log.v(LOGTAG, "guessMimeTypeFromExtension: mURL = " + mUrl);
+            Log.v(LOGTAG, "guessMimeTypeFromExtension: url = " + url);
         }
 
         String mimeType =
                 MimeTypeMap.getSingleton().getMimeTypeFromExtension(
-                        MimeTypeMap.getFileExtensionFromUrl(mUrl));
+                        MimeTypeMap.getFileExtensionFromUrl(url));
 
         if (mimeType != null) {
             // XXX: Until the servers send us either correct xhtml or
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 85c2275..fdbc692 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -335,6 +335,7 @@
             sMimeTypeMap.loadEntry("application/x-object", "o", false);
             sMimeTypeMap.loadEntry("application/x-oz-application", "oza", 
                     false);
+            sMimeTypeMap.loadEntry("application/x-pkcs12", "p12", false);
             sMimeTypeMap.loadEntry("application/x-pkcs7-certreqresp", "p7r", 
                     false);
             sMimeTypeMap.loadEntry("application/x-pkcs7-crl", "crl", false);
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index d6ac3e9..9889fe9 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -348,7 +348,7 @@
      * This header provides a filename for content that is going to be
      * downloaded to the file system. We only support the attachment type.
      */
-    private static String parseContentDisposition(String contentDisposition) {
+    static String parseContentDisposition(String contentDisposition) {
         try {
             Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
             if (m.find()) {
diff --git a/data/sounds/AudioPackage2.mk b/data/sounds/AudioPackage2.mk
index aea3f0b..649787e 100644
--- a/data/sounds/AudioPackage2.mk
+++ b/data/sounds/AudioPackage2.mk
@@ -23,7 +23,7 @@
 	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
 	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
 	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/Silence.ogg \
+	$(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/notifications/Silence.ogg \
 	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
 	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
 	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk
index 8c8fc32..fc1e921 100644
--- a/data/sounds/OriginalAudio.mk
+++ b/data/sounds/OriginalAudio.mk
@@ -22,7 +22,7 @@
 	$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
 	$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
 	$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
-	$(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/Silence.ogg \
+	$(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/notifications/Silence.ogg \
 	$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
 	$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
 	$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 4d25183..7c4996e 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -340,6 +340,8 @@
         Log.i("TTS service received", text);
         if (queueMode == TextToSpeech.TTS_QUEUE_FLUSH) {
             stop(callingApp);
+        } else if (queueMode == 2) {
+            stopAll(callingApp);
         }
         mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT));
         if (!mIsSpeaking) {
@@ -364,6 +366,8 @@
             ArrayList<String> params) {
         if (queueMode == TextToSpeech.TTS_QUEUE_FLUSH) {
             stop(callingApp);
+        } else if (queueMode == 2) {
+            stopAll(callingApp);
         }
         mSpeechQueue.add(new SpeechItem(callingApp, earcon, params, SpeechItem.EARCON));
         if (!mIsSpeaking) {
@@ -373,7 +377,7 @@
     }
 
     /**
-     * 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(String callingApp) {
         int result = TextToSpeech.TTS_ERROR;
@@ -389,15 +393,20 @@
                         mSpeechQueue.remove(i);
                     }
                 }
-
-                result = nativeSynth.stop();
-                mIsSpeaking = false;
-                if (mPlayer != null) {
-                    try {
-                        mPlayer.stop();
-                    } catch (IllegalStateException e) {
-                        // Do nothing, the player is already stopped.
+                if ((mCurrentSpeechItem != null) &&
+                     mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
+                    result = nativeSynth.stop();
+                    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("TTS", "Stopped");
             }
@@ -407,7 +416,55 @@
         } finally {
             // This check is needed because finally will always run; even if the
             // method returns somewhere in the try block.
-            mCurrentSpeechItem = null;
+            if (speechQueueAvailable) {
+                speechQueueLock.unlock();
+            }
+            return result;
+        }
+    }
+
+
+
+    /**
+     * 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 = nativeSynth.stop();
+                    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("TTS", "Stopped all");
+            }
+        } catch (InterruptedException e) {
+          Log.e("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();
             }
@@ -430,7 +487,6 @@
         if (utteranceId.length() > 0){
             dispatchUtteranceCompletedCallback(utteranceId, callingApp);
         }
-        mCurrentSpeechItem = null;
         processSpeechQueue();
     }
 
@@ -466,7 +522,6 @@
                     if (utteranceId.length() > 0){
                         dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
                     }
-                    mCurrentSpeechItem = null;
                     processSpeechQueue();
                 }
             }
@@ -531,13 +586,12 @@
                     // This check is needed because finally will always run;
                     // even if the
                     // method returns somewhere in the try block.
-                    if (synthAvailable) {
-                        synthesizerLock.unlock();
-                    }
                     if (utteranceId.length() > 0){
                         dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
                     }
-                    mCurrentSpeechItem = null;
+                    if (synthAvailable) {
+                        synthesizerLock.unlock();
+                    }
                     processSpeechQueue();
                 }
             }
@@ -595,13 +649,12 @@
                     // This check is needed because finally will always run;
                     // even if the
                     // method returns somewhere in the try block.
-                    if (synthAvailable) {
-                        synthesizerLock.unlock();
-                    }
                     if (utteranceId.length() > 0){
                         dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
                     }
-                    mCurrentSpeechItem = null;
+                    if (synthAvailable) {
+                        synthesizerLock.unlock();
+                    }
                     processSpeechQueue();
                 }
             }