Merge change 9378 into donut

* changes:
  add "since" info for reference docs
diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c
index eac455e..637e0d8 100644
--- a/cmds/keystore/netkeystore.c
+++ b/cmds/keystore/netkeystore.c
@@ -37,6 +37,7 @@
 #include "netkeystore.h"
 #include "keymgmt.h"
 
+#define  DBG  1
 #define  CMD_PUT_WITH_FILE  "putfile"
 
 typedef void CMD_FUNC(LPC_MARSHAL *cmd, LPC_MARSHAL *reply);
@@ -397,12 +398,12 @@
 
         // read the command, execute and send the result back.
         if(read_marshal(s, &cmd)) goto err;
-        LOGI("new connection\n");
+        if (DBG) LOGD("new connection\n");
         execute(&cmd, &reply);
         write_marshal(s, &reply);
 err:
         memset(&reply, 0, sizeof(LPC_MARSHAL));
-        LOGI("closing connection\n");
+        if (DBG) LOGD("closing connection\n");
         close(s);
     }
 
diff --git a/core/java/android/net/http/EventHandler.java b/core/java/android/net/http/EventHandler.java
index 830d1f1..a035c19 100644
--- a/core/java/android/net/http/EventHandler.java
+++ b/core/java/android/net/http/EventHandler.java
@@ -141,7 +141,10 @@
      * SSL certificate error callback. Handles SSL error(s) on the way
      * up to the user. The callback has to make sure that restartConnection() is called,
      * otherwise the connection will be suspended indefinitely.
+     * @return True if the callback can handle the error, which means it will
+     *              call restartConnection() to unblock the thread later,
+     *              otherwise return false.
      */
-    public void handleSslErrorRequest(SslError error);
+    public boolean handleSslErrorRequest(SslError error);
 
 }
diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java
index 55b733f..8a69d0d 100644
--- a/core/java/android/net/http/HttpsConnection.java
+++ b/core/java/android/net/http/HttpsConnection.java
@@ -323,7 +323,10 @@
                 mSuspended = true;
             }
             // don't hold the lock while calling out to the event handler
-            eventHandler.handleSslErrorRequest(error);
+            boolean canHandle = eventHandler.handleSslErrorRequest(error);
+            if(!canHandle) {
+                throw new IOException("failed to handle "+ error);
+            }
             synchronized (mSuspendLock) {
                 if (mSuspended) {
                     try {
diff --git a/core/java/android/net/http/LoggingEventHandler.java b/core/java/android/net/http/LoggingEventHandler.java
index 1b18651..bdafa0b 100644
--- a/core/java/android/net/http/LoggingEventHandler.java
+++ b/core/java/android/net/http/LoggingEventHandler.java
@@ -82,9 +82,11 @@
         }
     }
 
-    public void handleSslErrorRequest(SslError error) {
+    public boolean handleSslErrorRequest(SslError error) {
         if (HttpLog.LOGV) {
             HttpLog.v("LoggingEventHandler: handleSslErrorRequest():" + error);
         }
+        // return false so that the caller thread won't wait forever
+        return false;
     }
 }
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index 9ca2909..c3f3594 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -106,6 +106,7 @@
     private String   mErrorDescription;
     private SslError mSslError;
     private RequestHandle mRequestHandle;
+    private RequestHandle mSslErrorRequestHandle;
 
     // Request data. It is only valid when we are doing a load from the
     // cache. It is needed if the cache returns a redirect
@@ -693,7 +694,7 @@
      * IMPORTANT: as this is called from network thread, can't call native
      * directly
      */
-    public void handleSslErrorRequest(SslError error) {
+    public boolean handleSslErrorRequest(SslError error) {
         if (WebView.LOGV_ENABLED) {
             Log.v(LOGTAG,
                     "LoadListener.handleSslErrorRequest(): url:" + url() +
@@ -701,6 +702,15 @@
                     " certificate: " + error.getCertificate());
         }
         sendMessageInternal(obtainMessage(MSG_SSL_ERROR, error));
+        // if it has been canceled, return false so that the network thread
+        // won't be blocked. If it is not canceled, save the mRequestHandle
+        // so that if it is canceled when MSG_SSL_ERROR is handled, we can
+        // still call handleSslErrorResponse which will call restartConnection
+        // to unblock the network thread.
+        if (!mCancelled) {
+            mSslErrorRequestHandle = mRequestHandle;
+        }
+        return !mCancelled;
     }
 
     // Handle the ssl error on the WebCore thread.
@@ -708,7 +718,10 @@
         if (!mCancelled) {
             mSslError = error;
             Network.getInstance(mContext).handleSslErrorRequest(this);
+        } else if (mSslErrorRequestHandle != null) {
+            mSslErrorRequestHandle.handleSslErrorResponse(true);
         }
+        mSslErrorRequestHandle = null;
     }
 
     /**
diff --git a/docs/html/guide/appendix/api-levels.jd b/docs/html/guide/appendix/api-levels.jd
new file mode 100644
index 0000000..9af0918
--- /dev/null
+++ b/docs/html/guide/appendix/api-levels.jd
@@ -0,0 +1,79 @@
+page.title=Android API Levels
+@jd:body
+
+
+<p>The Android <em>API Level</em> is an integer that indicates a set of APIs available in an Android SDK
+and on a version of the Android platform. Each version of the Android platform supports a specific set
+of APIs, which are always backward-compatible. For example, Android 1.5 supports all APIs available in
+Android 1.0, but the reverse is not true. If an application uses APIs
+available in Android 1.5 that are not available in 1.0, then the application should never be installed
+on an Android 1.0 device, because it will fail due to missing APIs. The API Level ensures this does not happen
+by comparing the minimum API Level required by the applicaiton to the API Level available on the device.</p>
+
+<p>When a new version of Android adds APIs, a new API Level is added to the platform. The new APIs
+are available only to applications that declare a minimum API Level that is equal-to or greater-than 
+the API Level in which the APIs were introduced. The API Level required by an application is declared with the
+<code>&lt;uses-sdk></code> element inside the Android manifest, like this:</p>
+
+<pre>&lt;uses-sdk android:minSdkVersion="3" /></pre>
+
+<p>The value for <code>minSdkVersion</code> is the minimum API Level required by the application.
+If this is not declared, then it is assumed that the application is compatible with all versions and defaults to 
+API Level 1. In which case, if the application actually uses APIs introduced with an API Level greater than 1, then
+the application will fail in unpredictable ways when installed on a device that only supports API Level 1 
+(such as an Android 1.0 device).  
+See the <code><a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">&lt;uses-sdk></a></code> 
+documentation for more about declaring the API Level in your manifest.</p>
+
+<p>For example, the {@link android.appwidget} package was introduced with API Level 3. If your application
+has set <code>minSdkVersion</code> to 1 or 2, then your application cannot use this package, 
+even if the device running your application uses a version of Android that supports it.
+In order to use the {@link android.appwidget} package, your application must set <code>minSdkVersion</code> 
+to 3 or higher. When the <code>minSdkVersion</code> is set to 3, the application will no longer be able to install 
+on a device running a platform version with an API Level less than 3.</p>
+
+<p>Despite the name of the manifest attribute (<code>minSdkVersion</code>), the API Level is not directly 
+associated with a specific SDK. For example, the SDK for Android 1.0 uses
+API Level 1 and the SDK for Android 1.1 uses API Level 2. So it may seem that the API Level increases consistently.
+However, it's possible that a subsequent platform
+releases will not introduce new APIs, and thus, the API Level will remain the same. In addition, there are often
+multiple SDK releases for a single platform version (there were three SDK releases for Android 1.5), and
+there's no guarantee that the API Level will remain the same between these. It's possible (but unlikely) that
+a second or third SDK for a given version of the platform will provide new APIs and add a new API Level.
+When you install a new SDK, be sure to read the SDK Contents on the install page, which specifies the API 
+Level for each platform available in the SDK. Also see the comparison of 
+<a href="#VersionsVsApiLevels">Platform Versions vs. API Levels</a>, below.</p>
+
+<p class="note"><strong>Note:</strong> During "preview" SDK releases, there may not yet be an official platform version
+or API Level number specified. In these cases, a string value equal to the
+current codename will be a valid value for <code>minSdkVersion</code>, instead of an integer. This codename value
+will only be valid while using the preview SDK. When the final SDK is released, you must update your manifest to use
+the official API Level integer.</p>
+
+<h2 id="VersionsVsApiLevels">Platform Versions vs. API Levels</h2>
+
+<p>The following table specifies the <em>maximum</em> API Level supported by each version of the Android platform.</p>
+
+<table>
+  <tr><th>Platform Version</th><th>API Level</th></tr>
+  <tr><td>Android 1.0</td><td>1</td></tr>
+  <tr><td>Android 1.1</td><td>2</td></tr>
+  <tr><td>Android 1.5</td><td>3</td></tr>
+  <tr><td>Android Donut</td><td>Donut</td></tr>
+</table>
+
+
+<h2 id="ViewingTheApiReference">Viewing the API Reference Based on API Level</h2>
+
+<p>The Android API reference includes information that specififies the minimum API Level required for each 
+package, class, and member. You can see this information on the right side of each header or label.</p>
+
+<p>By default, the reference documentation shows all APIs available with the latest SDK release.
+This means that the reference assumes you're using the latest API Level and will show you everything available
+with it. If you're developing applications for a version of Android that does not support the latest API Level, 
+then you can filter the reference to reveal only the packages, classes, and members available for that API Level.
+When viewing the reference, use the "Filter by API Level" selection box (below the search bar) to pick the API Level
+you'd like to view.</p>
+
+
+
diff --git a/keystore/java/android/security/CertTool.java b/keystore/java/android/security/CertTool.java
index b1b78ea..989432a 100644
--- a/keystore/java/android/security/CertTool.java
+++ b/keystore/java/android/security/CertTool.java
@@ -209,6 +209,10 @@
             }
             freeX509Certificate(handle);
         }
-        if (intent != null) context.startActivity(intent);
+        if (intent != null) {
+            context.startActivity(intent);
+        } else {
+            Log.w("CertTool", "incorrect data for addCertificate()");
+        }
     }
 }
diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java
index 6178d59..dddf654 100644
--- a/keystore/java/android/security/ServiceCommand.java
+++ b/keystore/java/android/security/ServiceCommand.java
@@ -49,6 +49,8 @@
 
     public static final int BUFFER_LENGTH = 4096;
 
+    private static final boolean DBG = true;
+
     private String mServiceName;
     private String mTag;
     private InputStream mIn;
@@ -59,7 +61,7 @@
         if (mSocket != null) {
             return true;
         }
-        Log.i(mTag, "connecting...");
+        if (DBG) Log.d(mTag, "connecting...");
         try {
             mSocket = new LocalSocket();
 
@@ -78,7 +80,7 @@
     }
 
     private void disconnect() {
-        Log.i(mTag,"disconnecting...");
+        if (DBG) Log.d(mTag,"disconnecting...");
         try {
             if (mSocket != null) mSocket.close();
         } catch (IOException ex) { }
@@ -105,7 +107,7 @@
                 }
                 off += count;
             } catch (IOException ex) {
-                Log.e(mTag,"read exception");
+                Log.e(mTag,"read exception", ex);
                 break;
             }
         }
@@ -156,7 +158,7 @@
             mOut.write(buf, 0, 8);
             mOut.write(data, 0, len);
         } catch (IOException ex) {
-            Log.e(mTag,"write error");
+            Log.e(mTag,"write error", ex);
             disconnect();
             return false;
         }
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 95f3680..8db874a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -674,6 +674,26 @@
                 }
                 values.put(MediaStore.MediaColumns.TITLE, title);
             }
+            String album = values.getAsString(Audio.Media.ALBUM);
+            if (MediaFile.UNKNOWN_STRING.equals(album)) {
+                album = values.getAsString(MediaStore.MediaColumns.DATA);
+                // extract last path segment before file name
+                int lastSlash = album.lastIndexOf('/');
+                if (lastSlash >= 0) {
+                    int previousSlash = 0;
+                    while (true) {
+                        int idx = album.indexOf('/', previousSlash + 1);
+                        if (idx < 0 || idx >= lastSlash) {
+                            break;
+                        }
+                        previousSlash = idx;
+                    }
+                    if (previousSlash != 0) {
+                        album = album.substring(previousSlash + 1, lastSlash);
+                        values.put(Audio.Media.ALBUM, album);
+                    }
+                }
+            }
             if (isAudio) {
                 values.put(Audio.Media.IS_RINGTONE, ringtones);
                 values.put(Audio.Media.IS_NOTIFICATION, notifications);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 1b99d32..70960b5 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -142,6 +142,8 @@
     private final ReentrantLock synthesizerLock = new ReentrantLock();
 
     private static SynthProxy sNativeSynth = null;
+    private static Boolean sIsKillingSynth = true;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -152,6 +154,7 @@
         String soLibPath = "/system/lib/libttspico.so";
         if (sNativeSynth == null) {
             sNativeSynth = new SynthProxy(soLibPath);
+            sIsKillingSynth = false;
         }
 
         mSelf = this;
@@ -172,6 +175,9 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
+
+        sIsKillingSynth = true;
+        Log.i("TtsService", "TtsService.onDestroy()");
         // Don't hog the media player
         cleanUpPlayer();
 
@@ -180,6 +186,7 @@
 
         // Unregister all callbacks.
         mCallbacks.kill();
+        //Log.i("TtsService", "TtsService.onDestroy() ended");
     }
 
 
@@ -243,6 +250,9 @@
 
 
     private int setSpeechRate(String callingApp, int rate) {
+        if (sIsKillingSynth) {
+            return TextToSpeech.ERROR;
+        }
         if (isDefaultEnforced()) {
             return sNativeSynth.setSpeechRate(getDefaultRate());
         } else {
@@ -252,23 +262,37 @@
 
 
     private int setPitch(String callingApp, int pitch) {
+        if (sIsKillingSynth) {
+            return TextToSpeech.ERROR;
+        }
         return sNativeSynth.setPitch(pitch);
     }
 
 
     private int isLanguageAvailable(String lang, String country, String variant) {
+        if (sIsKillingSynth) {
+            return TextToSpeech.LANG_NOT_SUPPORTED;
+        }
         //Log.v("TtsService", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
         return sNativeSynth.isLanguageAvailable(lang, country, variant);
     }
 
 
     private String[] getLanguage() {
+        if (sIsKillingSynth) {
+            Log.v("TtsService", "killing synth:: aborting getLanguage()");
+            return null;
+        }
         return sNativeSynth.getLanguage();
     }
 
 
     private int setLanguage(String callingApp, String lang, String country, String variant) {
         Log.v("TtsService", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
+        if (sIsKillingSynth) {
+            Log.v("TtsService", "killing synth:: aborting setLanguage()");
+            return TextToSpeech.ERROR;
+        }
         if (isDefaultEnforced()) {
             return sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
                     getDefaultLocVariant());
@@ -402,7 +426,12 @@
                 }
                 if ((mCurrentSpeechItem != null) &&
                      mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
-                    result = sNativeSynth.stop();
+                    if (sIsKillingSynth) {
+                        Log.v("TtsService", "killing synth:: aborting stop()");
+                        result = TextToSpeech.ERROR;
+                    } else {
+                        result = sNativeSynth.stop();
+                    }
                     mKillList.put(mCurrentSpeechItem, true);
                     if (mPlayer != null) {
                         try {
@@ -451,7 +480,12 @@
                 if ((mCurrentSpeechItem != null) &&
                     ((mCurrentSpeechItem.mType != SpeechItem.TEXT_TO_FILE) ||
                       mCurrentSpeechItem.mCallingApp.equals(callingApp))) {
-                    result = sNativeSynth.stop();
+                    if (sIsKillingSynth) {
+                        Log.v("TtsService", "killing synth:: aborting stop()");
+                        result = TextToSpeech.ERROR;
+                    } else {
+                        result = sNativeSynth.stop();
+                    }
                     mKillList.put(mCurrentSpeechItem, true);
                     if (mPlayer != null) {
                         try {
@@ -591,7 +625,9 @@
                         if (speechRate.length() > 0){
                             setSpeechRate("", Integer.parseInt(speechRate));
                         }
-                        sNativeSynth.speak(speechItem.mText, streamType);
+                        if (!sIsKillingSynth) {
+                            sNativeSynth.speak(speechItem.mText, streamType);
+                        }
                     }
                 } catch (InterruptedException e) {
                     Log.e("TtsService", "TTS speakInternalOnly(): tryLock interrupted");
@@ -660,7 +696,9 @@
                         if (speechRate.length() > 0){
                             setSpeechRate("", Integer.parseInt(speechRate));
                         }
-                        sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
+                        if (!sIsKillingSynth) {
+                            sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
+                        }
                     }
                 } catch (InterruptedException e) {
                     Log.e("TtsService", "TTS synthToFileInternalOnly(): tryLock interrupted");
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index b107c7d..f410c7b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -147,8 +147,7 @@
 
     synchronized boolean onConnect(String username, String password) {
         try {
-            mState = VpnState.CONNECTING;
-            broadcastConnectivity(VpnState.CONNECTING);
+            setState(VpnState.CONNECTING);
 
             stopPreviouslyRunDaemons();
             String serverIp = getIp(getProfile().getServerName());
@@ -166,8 +165,7 @@
     synchronized void onDisconnect() {
         try {
             Log.i(TAG, "disconnecting VPN...");
-            mState = VpnState.DISCONNECTING;
-            broadcastConnectivity(VpnState.DISCONNECTING);
+            setState(VpnState.DISCONNECTING);
             mNotification.showDisconnect();
 
             mDaemonHelper.stopAll();
@@ -235,14 +233,13 @@
         saveOriginalDns();
         saveAndSetDomainSuffices();
 
-        mState = VpnState.CONNECTED;
         mStartTime = System.currentTimeMillis();
 
         // set DNS after saving the states in case the process gets killed
         // before states are saved
         saveSelf();
         setVpnDns();
-        broadcastConnectivity(VpnState.CONNECTED);
+        setState(VpnState.CONNECTED);
 
         enterConnectivityLoop();
     }
@@ -261,10 +258,10 @@
 
         restoreOriginalDns();
         restoreOriginalDomainSuffices();
-        mState = VpnState.IDLE;
-        broadcastConnectivity(VpnState.IDLE);
+        setState(VpnState.IDLE);
 
         // stop the service itself
+        SystemProperties.set(VPN_STATUS, VPN_IS_DOWN);
         mContext.removeStates();
         mContext.stopSelf();
     }
@@ -316,6 +313,11 @@
         SystemProperties.set(DNS_DOMAIN_SUFFICES, mOriginalDomainSuffices);
     }
 
+    private void setState(VpnState newState) {
+        mState = newState;
+        broadcastConnectivity(newState);
+    }
+
     private void broadcastConnectivity(VpnState s) {
         VpnManager m = new VpnManager(mContext);
         Throwable err = mError;
@@ -326,6 +328,9 @@
             } else if (err instanceof VpnConnectingError) {
                 m.broadcastConnectivity(mProfile.getName(), s,
                         ((VpnConnectingError) err).getErrorCode());
+            } else if (VPN_IS_UP.equals(SystemProperties.get(VPN_STATUS))) {
+                m.broadcastConnectivity(mProfile.getName(), s,
+                        VpnManager.VPN_ERROR_CONNECTION_LOST);
             } else {
                 m.broadcastConnectivity(mProfile.getName(), s,
                         VpnManager.VPN_ERROR_CONNECTION_FAILED);
@@ -373,7 +378,7 @@
     // returns false if vpn connectivity is broken
     private boolean checkConnectivity() {
         if (mDaemonHelper.anyDaemonStopped() || isLocalIpChanged()) {
-            onDisconnect();
+            onError(new IOException("Connectivity lost"));
             return false;
         } else {
             return true;
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
index e448e5a..f71bbea 100644
--- a/vpn/java/android/net/vpn/VpnManager.java
+++ b/vpn/java/android/net/vpn/VpnManager.java
@@ -54,6 +54,8 @@
     public static final int VPN_ERROR_CHALLENGE = 4;
     /** Error code to indicate an error of remote server hanging up. */
     public static final int VPN_ERROR_REMOTE_HUNG_UP = 5;
+    /** Error code to indicate an error of losing connectivity. */
+    public static final int VPN_ERROR_CONNECTION_LOST = 6;
     private static final int VPN_ERROR_NO_ERROR = 0;
 
     public static final String PROFILES_PATH = "/data/misc/vpn/profiles";