Merge change 9502 into donut

* changes:
  Fix bug 2025765. Talkback produced a null pointer exception when being enabled and disabled repeatedly due to a race condition between the onDestroy() of the service, and the use of the service itself. The error occurs when one Talkback thread initiates the destruction of the service (call to onDestroy()) when it invokes shutdown() on its TextToSpeech instance (the client of the service). At the same time, Talkback tries to say that "Accessibility" is unchecked from another thread. During onDestroy(), the reference to the TTS engine (sNativeSynth) is reset to null, which is used in the service in speakInternalOnly(), and setLanguage(). The fix consists in the addition of a static variable that signals that the service has entered onDestroy(). Once this flag is set, all method invocations on sNativeSynth will be dismissed. Note that access to the native resources used by sNativeSynth are synchronized at the native layer, therefore preventing sNativeSynth.shutdown() to interfere with a sNativeSynth.speak() call already underway.
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/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 6a9bcfb..b8f0a7e 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -463,6 +463,7 @@
         if (matrix == null && !mMatrix.isIdentity() ||
                 matrix != null && !mMatrix.equals(matrix)) {
             mMatrix.set(matrix);
+            configureBounds();
             invalidate();
         }
     }
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/JetPlayer.java b/media/java/android/media/JetPlayer.java
index 4fb0ead..d75d81d 100644
--- a/media/java/android/media/JetPlayer.java
+++ b/media/java/android/media/JetPlayer.java
@@ -183,6 +183,7 @@
      */
     public void release() {
         native_release();
+        singletonRef = null;
     }
     
     
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/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 1fdecdd..00a121b 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -93,7 +93,7 @@
 
 void SoundPool::addToRestartList(SoundChannel* channel)
 {
-    Mutex::Autolock lock(&mLock);
+    Mutex::Autolock lock(&mRestartLock);
     mRestart.push_back(channel);
     mCondition.signal();
 }
@@ -106,9 +106,9 @@
 
 int SoundPool::run()
 {
-    mLock.lock();
+    mRestartLock.lock();
     while (!mQuit) {
-        mCondition.wait(mLock);
+        mCondition.wait(mRestartLock);
         LOGV("awake");
         if (mQuit) break;
 
@@ -125,19 +125,19 @@
 
     mRestart.clear();
     mCondition.signal();
-    mLock.unlock();
+    mRestartLock.unlock();
     LOGV("goodbye");
     return 0;
 }
 
 void SoundPool::quit()
 {
-    mLock.lock();
+    mRestartLock.lock();
     mQuit = true;
     mCondition.signal();
-    mCondition.wait(mLock);
+    mCondition.wait(mRestartLock);
     LOGV("return from quit");
-    mLock.unlock();
+    mRestartLock.unlock();
 }
 
 bool SoundPool::startThreads()
@@ -484,11 +484,8 @@
     // if not idle, this voice is being stolen
     if (mState != IDLE) {
         LOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID);
-        stop_l();
         mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
-#ifdef USE_SHARED_MEM_BUFFER
-        mSoundPool->done(this);
-#endif
+        stop();
         return;
     }
 
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 7802781..ab86e90 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -204,6 +204,7 @@
 
     jobject                 mSoundPoolRef;
     Mutex                   mLock;
+    Mutex                   mRestartLock;
     Condition               mCondition;
     SoundPoolThread*        mDecodeThread;
     SoundChannel*           mChannelPool;
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";