Merge change 9455

* changes:
  Close the "Please connect charger" alert after a charger is connected.
diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java
index 5f84e57..c0dbf4d 100644
--- a/core/java/android/provider/Contacts.java
+++ b/core/java/android/provider/Contacts.java
@@ -1523,6 +1523,15 @@
                 ContactsContract.Intents.EXTRA_CREATE_DESCRIPTION;
 
         /**
+         * Optional extra used with {@link #SHOW_OR_CREATE_CONTACT} to specify a
+         * dialog location using screen coordinates. When not specified, the
+         * dialog will be centered.
+         *
+         * @hide pending API council review
+         */
+        public static final String EXTRA_TARGET_RECT = ContactsContract.Intents.EXTRA_TARGET_RECT;
+
+        /**
          * Intents related to the Contacts app UI.
          */
         public static final class UI {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 01189fe..227c675 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1009,6 +1009,8 @@
 
             public static final String PROTOCOL = "data5";
 
+            public static final String CUSTOM_PROTOCOL = "data6";
+
             /**
              * The predefined IM protocol types. The protocol can either be non-present, one
              * of these types, or a free-form string. These cases are encoded in the PROTOCOL
@@ -1019,6 +1021,7 @@
              * <li>custom:&lt;a string&gt;</li>
              * </ul>
              */
+            public static final int PROTOCOL_CUSTOM = -1;
             public static final int PROTOCOL_AIM = 0;
             public static final int PROTOCOL_MSN = 1;
             public static final int PROTOCOL_YAHOO = 2;
@@ -1027,31 +1030,6 @@
             public static final int PROTOCOL_GOOGLE_TALK = 5;
             public static final int PROTOCOL_ICQ = 6;
             public static final int PROTOCOL_JABBER = 7;
-
-            public static String encodePredefinedImProtocol(int protocol) {
-               return "pre:" + protocol;
-            }
-
-            public static String encodeCustomImProtocol(String protocolString) {
-               return "custom:" + protocolString;
-            }
-
-            public static Object decodeImProtocol(String encodedString) {
-               if (encodedString == null) {
-                   return null;
-               }
-
-               if (encodedString.startsWith("pre:")) {
-                   return Integer.parseInt(encodedString.substring(4));
-               }
-
-               if (encodedString.startsWith("custom:")) {
-                   return encodedString.substring(7);
-               }
-
-               throw new IllegalArgumentException(
-                       "the value is not a valid encoded protocol, " + encodedString);
-            }
         }
 
         /**
@@ -1397,6 +1375,13 @@
             "com.android.contacts.action.CREATE_DESCRIPTION";
 
         /**
+         * Optional extra used with {@link #SHOW_OR_CREATE_CONTACT} to specify a
+         * dialog location using screen coordinates. When not specified, the
+         * dialog will be centered.
+         */
+        public static final String EXTRA_TARGET_RECT = "target_rect";
+
+        /**
          * Intents related to the Contacts app UI.
          */
         public static final class UI {
diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java
new file mode 100644
index 0000000..94d4edc
--- /dev/null
+++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java
@@ -0,0 +1,349 @@
+/*
+ * 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.internal.widget;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.SocialContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.SocialContract.Activities;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+
+/* Widget that is used across system apps for displaying a header banner with contact info */
+public class ContactHeaderWidget extends FrameLayout implements View.OnClickListener {
+
+    private static final String TAG = "ContactHeaderWidget";
+
+    private TextView mDisplayNameView;
+    private TextView mPhoneticNameView;
+    private CheckBox mStarredView;
+    private ImageView mPhotoView;
+    private TextView mStatusView;
+    private int mNoPhotoResource;
+    private QueryHandler mQueryHandler;
+
+    protected long mContactId;
+    protected Uri mContactDataUri;
+    protected Uri mContactUri;
+    protected Uri mStatusUri;
+
+    protected ContentResolver mContentResolver;
+
+    //Projection used for the summary info in the header.
+    protected static final String[] HEADER_PROJECTION = new String[] {
+        Contacts.DISPLAY_NAME,
+        Contacts.STARRED,
+        Contacts.PHOTO_ID,
+    };
+    protected static final int HEADER_DISPLAY_NAME_COLUMN_INDEX = 0;
+    //TODO: We need to figure out how we're going to get the phonetic name.
+    //static final int HEADER_PHONETIC_NAME_COLUMN_INDEX
+    protected static final int HEADER_STARRED_COLUMN_INDEX = 1;
+    protected static final int HEADER_PHOTO_ID_COLUMN_INDEX = 2;
+
+    //Projection used for finding the most recent social status.
+    protected static final String[] SOCIAL_PROJECTION = new String[] {
+        Activities.TITLE,
+        Activities.PUBLISHED,
+    };
+    protected static final int SOCIAL_TITLE_COLUMN_INDEX = 0;
+    protected static final int SOCIAL_PUBLISHED_COLUMN_INDEX = 1;
+
+    //Projection used for looking up contact id from phone number
+    protected static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
+        RawContacts.CONTACT_ID,
+    };
+    protected static final int PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
+
+    //Projection used for looking up contact id from email address
+    protected static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
+        RawContacts.CONTACT_ID,
+    };
+    protected static final int EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
+
+
+    private static final int TOKEN_CONTACT_INFO = 0;
+    private static final int TOKEN_SOCIAL = 1;
+
+    public ContactHeaderWidget(Context context) {
+        this(context, null);
+    }
+
+    public ContactHeaderWidget(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ContactHeaderWidget(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        mContentResolver = mContext.getContentResolver();
+
+        LayoutInflater inflater =
+            (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(R.layout.contact_header, this);
+
+        mDisplayNameView = (TextView) findViewById(R.id.name);
+        mPhoneticNameView = (TextView) findViewById(R.id.phonetic_name);
+        mStarredView = (CheckBox) findViewById(R.id.star);
+        mStarredView.setOnClickListener(this);
+        // Don't show start by default.
+        mStarredView.setVisibility(View.GONE);
+        mPhotoView = (ImageView) findViewById(R.id.photo);
+        mStatusView = (TextView) findViewById(R.id.status);
+
+        // Set the photo with a random "no contact" image
+        long now = SystemClock.elapsedRealtime();
+        int num = (int) now & 0xf;
+        if (num < 9) {
+            // Leaning in from right, common
+            mNoPhotoResource = R.drawable.ic_contact_picture;
+        } else if (num < 14) {
+            // Leaning in from left uncommon
+            mNoPhotoResource = R.drawable.ic_contact_picture_2;
+        } else {
+            // Coming in from the top, rare
+            mNoPhotoResource = R.drawable.ic_contact_picture_3;
+        }
+
+        mQueryHandler = new QueryHandler(mContentResolver);
+    }
+
+    private class QueryHandler extends AsyncQueryHandler {
+
+        public QueryHandler(ContentResolver cr) {
+            super(cr);
+        }
+
+        @Override
+        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+            try{
+                if (token == TOKEN_CONTACT_INFO) {
+                    bindContactInfo(cursor);
+                    invalidate();
+                } else if (token == TOKEN_SOCIAL) {
+                    bindSocial(cursor);
+                    invalidate();
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+    }
+
+
+    /** {@inheritDoc} */
+    public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+        try{
+            if (token == TOKEN_CONTACT_INFO) {
+                bindContactInfo(cursor);
+                invalidate();
+            } else if (token == TOKEN_SOCIAL) {
+                bindSocial(cursor);
+                invalidate();
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    /**
+     * Turn on/off showing of the star element.
+     */
+    public void showStar(boolean showStar) {
+        mStarredView.setVisibility(showStar ? View.VISIBLE : View.GONE);
+    }
+
+    /**
+     * Convenience method for binding all available data from an existing
+     * contact.
+     *
+     * @param contactId the contact id of the contact whose info should be displayed.
+     */
+    public void bindFromContactId(long contactId) {
+        mContactId = contactId;
+        mContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, mContactId);
+        mContactDataUri = Uri.withAppendedPath(mContactUri, Contacts.Data.CONTENT_DIRECTORY);
+        mStatusUri = ContentUris.withAppendedId(
+                SocialContract.Activities.CONTENT_CONTACT_STATUS_URI, mContactId);
+        redrawHeader();
+    }
+
+    /**
+     * Convenience method for binding all available data from an existing
+     * contact.
+     *
+     * @param emailAddress The email address used to do a reverse lookup in
+     * the contacts database. If more than one contact contains this email
+     * address, one of them will be chosen to bind to.
+     */
+    public void bindFromEmail(String emailAddress) {
+        Cursor c = null;
+        try {
+            c = mContentResolver.query(Uri.withAppendedPath(
+                    RawContacts.CONTENT_FILTER_EMAIL_URI, Uri.encode(emailAddress)),
+                    EMAIL_LOOKUP_PROJECTION, null, null, null);
+            c.moveToFirst();
+            long contactId = c.getLong(EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX);
+            bindFromContactId(contactId);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    /**
+     * Convenience method for binding all available data from an existing
+     * contact.
+     *
+     * @param number The phone number used to do a reverse lookup in
+     * the contacts database. If more than one contact contains this phone
+     * number, one of them will be chosen to bind to.
+     */
+    public void bindFromPhoneNumber(String number) {
+        Cursor c = null;
+        try {
+            c = mContentResolver.query(Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number),
+                    PHONE_LOOKUP_PROJECTION, null, null, null);
+            c.moveToFirst();
+            long contactId = c.getLong(PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX);
+            bindFromContactId(contactId);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    protected void redrawHeader() {
+        if (mContactDataUri != null) {
+            mQueryHandler.startQuery(TOKEN_CONTACT_INFO, null, mContactDataUri, HEADER_PROJECTION,
+                    null, null, null);
+        }
+
+        if (mStatusUri != null) {
+            mQueryHandler.startQuery(TOKEN_SOCIAL, null, mStatusUri, SOCIAL_PROJECTION,
+                    null, null, null);
+        }
+    }
+
+    protected void bindContactInfo(Cursor c) {
+        if (c == null) {
+            return;
+        }
+        if (c.moveToFirst()) {
+            //Set name
+            String displayName = c.getString(HEADER_DISPLAY_NAME_COLUMN_INDEX);
+            Log.i(TAG, displayName);
+            mDisplayNameView.setText(displayName);
+            //TODO: Bring back phonetic name
+            /*if (mPhoneticNameView != null) {
+                String phoneticName = c.getString(CONTACT_PHONETIC_NAME_COLUMN);
+                mPhoneticNameView.setText(phoneticName);
+            }*/
+
+            //Set starred
+            mStarredView.setChecked(c.getInt(HEADER_STARRED_COLUMN_INDEX) == 1);
+
+            //Set the photo
+            Bitmap photoBitmap = loadContactPhoto(c.getLong(HEADER_PHOTO_ID_COLUMN_INDEX), null);
+            if (photoBitmap == null) {
+                photoBitmap = loadPlaceholderPhoto(null);
+            }
+            mPhotoView.setImageBitmap(photoBitmap);
+        }
+    }
+
+    protected void bindSocial(Cursor c) {
+        if (c == null) {
+            return;
+        }
+        if (c.moveToFirst()) {
+            String status = c.getString(SOCIAL_TITLE_COLUMN_INDEX);
+            mStatusView.setText(status);
+        }
+    }
+
+    public void onClick(View view) {
+        if (view.getId() == R.id.star) {
+            ContentValues values = new ContentValues(1);
+            values.put(Contacts.STARRED, mStarredView.isChecked());
+            mContentResolver.update(mContactUri, values, null, null);
+        }
+    }
+
+    private Bitmap loadContactPhoto(long photoId, BitmapFactory.Options options) {
+        Cursor photoCursor = null;
+        Bitmap photoBm = null;
+
+        try {
+            photoCursor = mContentResolver.query(
+                    ContentUris.withAppendedId(Data.CONTENT_URI, photoId),
+                    new String[] { Photo.PHOTO },
+                    null, null, null);
+
+            if (photoCursor.moveToFirst() && !photoCursor.isNull(0)) {
+                byte[] photoData = photoCursor.getBlob(0);
+                photoBm = BitmapFactory.decodeByteArray(photoData, 0,
+                        photoData.length, options);
+            }
+        } finally {
+            if (photoCursor != null) {
+                photoCursor.close();
+            }
+        }
+
+        return photoBm;
+    }
+
+    private Bitmap loadPlaceholderPhoto(BitmapFactory.Options options) {
+        if (mNoPhotoResource == 0) {
+            return null;
+        }
+        return BitmapFactory.decodeResource(mContext.getResources(),
+                mNoPhotoResource, options);
+    }
+}
diff --git a/core/res/res/drawable/contact_header_bg.9.png b/core/res/res/drawable/contact_header_bg.9.png
new file mode 100644
index 0000000..7f9a5a3
--- /dev/null
+++ b/core/res/res/drawable/contact_header_bg.9.png
Binary files differ
diff --git a/core/res/res/drawable/contact_picture_bg.9.png b/core/res/res/drawable/contact_picture_bg.9.png
new file mode 100644
index 0000000..ae9c709
--- /dev/null
+++ b/core/res/res/drawable/contact_picture_bg.9.png
Binary files differ
diff --git a/core/res/res/drawable/ic_contact_picture_2.png b/core/res/res/drawable/ic_contact_picture_2.png
new file mode 100644
index 0000000..8b184af
--- /dev/null
+++ b/core/res/res/drawable/ic_contact_picture_2.png
Binary files differ
diff --git a/core/res/res/drawable/ic_contact_picture_3.png b/core/res/res/drawable/ic_contact_picture_3.png
new file mode 100644
index 0000000..a2d08b5
--- /dev/null
+++ b/core/res/res/drawable/ic_contact_picture_3.png
Binary files differ
diff --git a/core/res/res/layout-ja/contact_header_name.xml b/core/res/res/layout-ja/contact_header_name.xml
new file mode 100644
index 0000000..03332b1
--- /dev/null
+++ b/core/res/res/layout-ja/contact_header_name.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<!-- In Japanese-language locales, the "Name" field contains two separate
+     TextViews: the name itself, and also the phonetic ("furigana") field. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="0dip"
+    android:layout_weight="1"
+    android:layout_height="wrap_content">
+
+    <TextView android:id="@+id/name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+    	android:textAppearance="?android:attr/textAppearanceLargeInverse"
+    	android:textColor="@android:color/secondary_text_light"
+        />
+
+    <TextView android:id="@+id/phonetic_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+    	android:textAppearance="?android:attr/textAppearanceSmallInverse"
+    	android:textColor="@android:color/secondary_text_light"
+        />
+
+</LinearLayout>
diff --git a/core/res/res/layout/contact_header.xml b/core/res/res/layout/contact_header.xml
new file mode 100644
index 0000000..04b34b8
--- /dev/null
+++ b/core/res/res/layout/contact_header.xml
@@ -0,0 +1,56 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	android:id="@+id/banner"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="@drawable/contact_header_bg"
+    android:paddingRight="5dip"
+    android:gravity="center_vertical">
+    
+    <ImageView android:id="@+id/photo"
+        android:layout_width="64dip"
+        android:layout_height="64dip"
+        android:layout_marginRight="7dip"
+        android:layout_marginLeft="2dip"
+        android:scaleType="fitCenter"
+        android:background="@drawable/contact_picture_bg"/>
+    
+    <LinearLayout
+        android:layout_width="0dip"
+        android:layout_height="fill_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <!-- "Name" field is locale-specific. -->
+        <include layout="@layout/contact_header_name"/>
+
+        <TextView android:id="@+id/status"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:maxLines="2"/>
+                
+    </LinearLayout>
+
+    <CheckBox android:id="@+id/star"
+        style="?android:attr/starStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/contact_header_name.xml b/core/res/res/layout/contact_header_name.xml
new file mode 100644
index 0000000..aec943e
--- /dev/null
+++ b/core/res/res/layout/contact_header_name.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<!-- In the default locale, the "Name" field is a single TextView -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/name"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceLargeInverse"
+    android:textColor="@android:color/secondary_text_light"
+    android:maxLines="2"
+    android:ellipsize="end"
+    />
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/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/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index c71003b..d4ec85a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -853,7 +853,7 @@
         }
     }
 
-    private static String decodeIa5(byte[] data, int offset, int numFields)
+    private static String decode7bitAscii(byte[] data, int offset, int numFields)
         throws CodingException
     {
         try {
@@ -868,38 +868,20 @@
             inStream.skip(offset);
             for (int i = 0; i < numFields; i++) {
                 int charCode = inStream.read(7);
-                if ((charCode < UserData.IA5_MAP_BASE_INDEX) ||
-                        (charCode > UserData.IA5_MAP_MAX_INDEX)) {
-                    throw new CodingException("unsupported AI5 character code (" + charCode + ")");
+                if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) ||
+                        (charCode <= UserData.ASCII_MAP_MAX_INDEX)) {
+                    strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]);
+                } else if (charCode == UserData.ASCII_LF_INDEX) {
+                    strBuf.append('\r');
+                } else if (charCode == UserData.ASCII_CR_INDEX) {
+                    strBuf.append('\n');
+                } else {
+                    /* For other charCodes, they are unprintable, and so simply use SPACE. */
+                    strBuf.append(' ');
                 }
-                strBuf.append(UserData.IA5_MAP[charCode - UserData.IA5_MAP_BASE_INDEX]);
             }
             return strBuf.toString();
         } catch (BitwiseInputStream.AccessException ex) {
-            throw new CodingException("AI5 decode failed: " + ex);
-        }
-    }
-
-    private static String decode7bitAscii(byte[] data, int offset, int numFields)
-        throws CodingException
-    {
-        try {
-            offset *= 8;
-            BitwiseInputStream inStream = new BitwiseInputStream(data);
-            int wantedBits = offset + (numFields * 7);
-            if (inStream.available() < wantedBits) {
-                throw new CodingException("insufficient data (wanted " + wantedBits +
-                                          " bits, but only have " + inStream.available() + ")");
-            }
-            inStream.skip(offset);
-            byte[] expandedData = new byte[numFields];
-            for (int i = 0; i < numFields; i++) {
-                expandedData[i] = (byte)inStream.read(7);
-            }
-            return new String(expandedData, 0, numFields, "US-ASCII");
-        } catch (java.io.UnsupportedEncodingException ex) {
-            throw new CodingException("7bit ASCII decode failed: " + ex);
-        } catch (BitwiseInputStream.AccessException ex) {
             throw new CodingException("7bit ASCII decode failed: " + ex);
         }
     }
@@ -956,12 +938,10 @@
             // octet encoded.
             userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
             break;
+        case UserData.ENCODING_IA5:
         case UserData.ENCODING_7BIT_ASCII:
             userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
             break;
-        case UserData.ENCODING_IA5:
-            userData.payloadStr = decodeIa5(userData.payload, offset, userData.numFields);
-            break;
         case UserData.ENCODING_UNICODE_16:
             userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
             break;
@@ -1003,7 +983,7 @@
         try {
             StringBuffer strbuf = new StringBuffer(dataLen);
             while (inStream.available() >= 6) {
-                strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+                strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
             }
             String data = strbuf.toString();
             bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
@@ -1045,7 +1025,7 @@
         }
         StringBuffer strbuf = new StringBuffer(dataLen);
         for (int i = 0; i < numFields; i++) {
-            strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+            strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
         }
         bData.userData.payloadStr = strbuf.toString();
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index 54c1f80..15cb5e4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -49,19 +49,20 @@
     public static final int IS91_MSG_TYPE_SHORT_MESSAGE      = 0x85;
 
     /**
-     * IA5 data encoding character mappings.
-     * (See CCITT Rec. T.50 Tables 1 and 3)
+     * US ASCII character mapping table.
      *
-     * Note this mapping is the the same as for printable ASCII
-     * characters, with a 0x20 offset, meaning that the ASCII SPACE
-     * character occurs with code 0x20.
+     * This table contains only the printable ASCII characters, with a
+     * 0x20 offset, meaning that the ASCII SPACE character is at index
+     * 0, with the resulting code of 0x20.
      *
-     * Note this mapping is also equivalent to that used by the IS-91
-     * protocol, except for the latter using only 6 bits, and hence
-     * mapping only entries up to the '_' character.
+     * Note this mapping is also equivalent to that used by both the
+     * IS5 and the IS-91 encodings.  For the former this is defined
+     * using CCITT Rec. T.50 Tables 1 and 3.  For the latter IS 637 B,
+     * Table 4.3.1.4.1-1 -- and note the encoding uses only 6 bits,
+     * and hence only maps entries up to the '_' character.
      *
      */
-    public static final char[] IA5_MAP = {
+    public static final char[] ASCII_MAP = {
         ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
         '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
@@ -85,8 +86,8 @@
     public static final int ASCII_CR_INDEX = 0x0D;
     public static final SparseIntArray charToAscii = new SparseIntArray();
     static {
-        for (int i = 0; i < IA5_MAP.length; i++) {
-            charToAscii.put(IA5_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i);
+        for (int i = 0; i < ASCII_MAP.length; i++) {
+            charToAscii.put(ASCII_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i);
         }
         charToAscii.put('\r', ASCII_LF_INDEX);
         charToAscii.put('\n', ASCII_CR_INDEX);
@@ -113,11 +114,11 @@
     }
 
     /**
-     * Mapping for IA5 values less than 32 are flow control signals
+     * Mapping for ASCII values less than 32 are flow control signals
      * and not used here.
      */
-    public static final int IA5_MAP_BASE_INDEX = 0x20;
-    public static final int IA5_MAP_MAX_INDEX = IA5_MAP_BASE_INDEX + IA5_MAP.length - 1;
+    public static final int ASCII_MAP_BASE_INDEX = 0x20;
+    public static final int ASCII_MAP_MAX_INDEX = ASCII_MAP_BASE_INDEX + ASCII_MAP.length - 1;
 
     /**
      * Contains the data header of the user data
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";