Merge "Fix issue #4902856: Don't let apps register non-explicit PendingIntents"
diff --git a/api/current.txt b/api/current.txt
index f21a4f3..1c2950a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22395,6 +22395,7 @@
     method public void requestDisallowInterceptTouchEvent(boolean);
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
+    method protected void resetLayoutDirectionResolution();
     method public void scheduleLayoutAnimation();
     method public void setAddStatesFromChildren(boolean);
     method public void setAlwaysDrawnWithCacheEnabled(boolean);
@@ -26063,6 +26064,7 @@
     method protected void onTextChanged(java.lang.CharSequence, int, int, int);
     method public boolean onTextContextMenuItem(int);
     method public void removeTextChangedListener(android.text.TextWatcher);
+    method protected void resetLayoutDirectionResolution();
     method public final void setAutoLinkMask(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawables(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 5ade9eb..0eb8cd8 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -338,7 +338,15 @@
      * @hide
      */
     public static NdefRecord createUri(Uri uri) {
-        String uriString = uri.toString();
+        return createUri(uri.toString());
+    }
+
+    /**
+     * Creates an NDEF record of well known type URI.
+     * TODO: Make a public API
+     * @hide
+     */
+    public static NdefRecord createUri(String uriString) {
         byte prefix = 0x0;
         for (int i = 1; i < URI_PREFIX_MAP.length; i++) {
             if (uriString.startsWith(URI_PREFIX_MAP[i])) {
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 3971045..b492615 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -17,9 +17,6 @@
 package android.provider;
 
 
-import com.android.internal.util.ArrayUtils;
-
-import android.accounts.Account;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.ContentProviderClient;
@@ -35,13 +32,10 @@
 import android.database.DatabaseUtils;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.format.Time;
 import android.util.Log;
 
-import java.util.Arrays;
-
 /**
  * <p>
  * The contract between the calendar provider and applications. Contains
@@ -97,6 +91,8 @@
     /**
      * Broadcast Action: This is the intent that gets fired when an alarm
      * notification needs to be posted for a reminder.
+     *
+     * @SdkConstant
      */
     public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER";
 
@@ -122,8 +118,7 @@
     /**
      * The content:// style URL for the top-level calendar authority
      */
-    public static final Uri CONTENT_URI =
-        Uri.parse("content://" + AUTHORITY);
+    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
 
     /**
      * An optional insert, update or delete URI parameter that allows the caller
@@ -572,29 +567,6 @@
      * </ul>
      */
     public static class Calendars implements BaseColumns, SyncColumns, CalendarColumns {
-        private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?"
-                + " AND "
-                + Calendars.ACCOUNT_TYPE + "=?";
-
-        /**
-         * Helper function for generating a calendars query. This is blocking
-         * and should not be used on the UI thread. See
-         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
-         * for more details about using the parameters.
-         *
-         * @param cr The ContentResolver to query with
-         * @param projection A list of columns to return
-         * @param selection A formatted selection string
-         * @param selectionArgs arguments to the selection string
-         * @param orderBy How to order the returned rows
-         * @return
-         */
-        public static final Cursor query(ContentResolver cr, String[] projection, String selection,
-                String[] selectionArgs, String orderBy) {
-            return cr.query(CONTENT_URI, projection, selection, selectionArgs,
-                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-        }
-
         /**
          * The content:// style URL for accessing Calendars
          */
@@ -622,7 +594,9 @@
          * These fields are only writable by a sync adapter. To modify them the
          * caller must include {@link #CALLER_IS_SYNCADAPTER},
          * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the Uri's query
-         * parameters.
+         * parameters. TODO move to provider
+         *
+         * @hide
          */
         public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
             ACCOUNT_NAME,
@@ -737,7 +711,7 @@
         /**
          * the projection used by the attendees query
          */
-        private static final String[] PROJECTION = new String[] {
+        public static final String[] PROJECTION = new String[] {
                 _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,};
         private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
 
@@ -1420,37 +1394,6 @@
             CalendarColumns {
 
         /**
-         * Queries all events with the given projection. This is a blocking call
-         * and should not be done on the UI thread.
-         *
-         * @param cr The content resolver to use for the query
-         * @param projection The columns to return
-         * @return A Cursor containing all events in the db
-         */
-        public static final Cursor query(ContentResolver cr, String[] projection) {
-            return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
-        }
-
-        /**
-         * Queries events using the given projection, selection filter, and
-         * ordering. This is a blocking call and should not be done on the UI
-         * thread. For selection and selectionArgs usage see
-         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
-         *
-         * @param cr The content resolver to use for the query
-         * @param projection The columns to return
-         * @param selection Filter on the query as an SQL WHERE statement
-         * @param selectionArgs Args to replace any '?'s in the selection
-         * @param orderBy How to order the rows as an SQL ORDER BY statement
-         * @return A Cursor containing the matching events
-         */
-        public static final Cursor query(ContentResolver cr, String[] projection, String selection,
-                String[] selectionArgs, String orderBy) {
-            return cr.query(CONTENT_URI, projection, selection, null,
-                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-        }
-
-        /**
          * The content:// style URL for interacting with events. Appending an
          * event id using {@link ContentUris#withAppendedId(Uri, long)} will
          * specify a single event.
@@ -1464,7 +1407,7 @@
          * appended event ID.  Deletion of exceptions requires both the original event ID and
          * the exception event ID (see {@link Uri.Builder#appendPath}).
          */
-        public static final Uri EXCEPTION_CONTENT_URI =
+        public static final Uri CONTENT_EXCEPTION_URI =
                 Uri.parse("content://" + AUTHORITY + "/exception");
 
         /**
@@ -1475,7 +1418,9 @@
         /**
          * These are columns that should only ever be updated by the provider,
          * either because they are views mapped to another table or because they
-         * are used for provider only functionality.
+         * are used for provider only functionality. TODO move to provider
+         *
+         * @hide
          */
         public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
                 ACCOUNT_NAME,
@@ -1505,7 +1450,9 @@
         /**
          * These fields are only writable by a sync adapter. To modify them the
          * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
-         * _SYNC_ACCOUNT_TYPE in the query parameters.
+         * _SYNC_ACCOUNT_TYPE in the query parameters. TODO move to provider.
+         *
+         * @hide
          */
         public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
             _SYNC_ID,
@@ -1672,11 +1619,6 @@
         public static final String END_MINUTE = "endMinute";
     }
 
-    /**
-     * CalendarCache stores some settings for calendar including the current
-     * time zone for the instaces. These settings are stored using a key/value
-     * scheme.
-     */
     protected interface CalendarCacheColumns {
         /**
          * The key for the setting. Keys are defined in {@link CalendarCache}.
@@ -1689,6 +1631,11 @@
         public static final String VALUE = "value";
     }
 
+    /**
+     * CalendarCache stores some settings for calendar including the current
+     * time zone for the instances. These settings are stored using a key/value
+     * scheme. A {@link #KEY} must be specified when updating these values.
+     */
     public static class CalendarCache implements CalendarCacheColumns {
         /**
          * The URI to use for retrieving the properties from the Calendar db.
@@ -1697,22 +1644,11 @@
                 Uri.parse("content://" + AUTHORITY + "/properties");
 
         /**
-         * If updating a property, this must be provided as the selection. All
-         * other selections will fail. For queries this field can be omitted to
-         * retrieve all properties or used to query a single property. Valid
-         * keys include {@link #TIMEZONE_KEY_TYPE},
-         * {@link #TIMEZONE_KEY_INSTANCES}, and
-         * {@link #TIMEZONE_KEY_INSTANCES_PREVIOUS}, though the last one can
-         * only be read, not written.
-         */
-        public static final String WHERE = "key=?";
-
-        /**
          * They key for updating the use of auto/home time zones in Calendar.
          * Valid values are {@link #TIMEZONE_TYPE_AUTO} or
          * {@link #TIMEZONE_TYPE_HOME}.
          */
-        public static final String TIMEZONE_KEY_TYPE = "timezoneType";
+        public static final String KEY_TIMEZONE_TYPE = "timezoneType";
 
         /**
          * The key for updating the time zone used by the provider when it
@@ -1720,24 +1656,24 @@
          * type is set to {@link #TIMEZONE_TYPE_HOME}. A valid time zone id
          * should be written to this field.
          */
-        public static final String TIMEZONE_KEY_INSTANCES = "timezoneInstances";
+        public static final String KEY_TIMEZONE_INSTANCES = "timezoneInstances";
 
         /**
          * The key for reading the last time zone set by the user. This should
          * only be read by apps and it will be automatically updated whenever
-         * {@link #TIMEZONE_KEY_INSTANCES} is updated with
+         * {@link #KEY_TIMEZONE_INSTANCES} is updated with
          * {@link #TIMEZONE_TYPE_HOME} set.
          */
-        public static final String TIMEZONE_KEY_INSTANCES_PREVIOUS = "timezoneInstancesPrevious";
+        public static final String KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious";
 
         /**
-         * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider
+         * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
          * should stay in sync with the device's time zone.
          */
         public static final String TIMEZONE_TYPE_AUTO = "auto";
 
         /**
-         * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider
+         * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider
          * should use a fixed time zone set by the user.
          */
         public static final String TIMEZONE_TYPE_HOME = "home";
@@ -1814,7 +1750,7 @@
         /**
          * The projection used by the EventDays query.
          */
-        private static final String[] PROJECTION = {
+        public static final String[] PROJECTION = {
                 STARTDAY, ENDDAY
         };
         private static final String SELECTION = "selected=1";
@@ -1900,7 +1836,7 @@
         /**
          * The projection used by the reminders query.
          */
-        private static final String[] PROJECTION = new String[] {
+        public static final String[] PROJECTION = new String[] {
                 _ID, MINUTES, METHOD,};
         @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
@@ -1967,17 +1903,28 @@
         public static final String NOTIFY_TIME = "notifyTime";
 
         /**
-         * The state of this alert. It starts out as {@link #SCHEDULED}, then
-         * when the alarm goes off, it changes to {@link #FIRED}, and then when
-         * the user dismisses the alarm it changes to {@link #DISMISSED}. Column
+         * The state of this alert. It starts out as {@link #STATE_SCHEDULED}, then
+         * when the alarm goes off, it changes to {@link #STATE_FIRED}, and then when
+         * the user dismisses the alarm it changes to {@link #STATE_DISMISSED}. Column
          * name.
          * <P>Type: INTEGER</P>
          */
         public static final String STATE = "state";
 
-        public static final int SCHEDULED = 0;
-        public static final int FIRED = 1;
-        public static final int DISMISSED = 2;
+        /**
+         * An alert begins in this state when it is first created.
+         */
+        public static final int STATE_SCHEDULED = 0;
+        /**
+         * After a notification for an alert has been created it should be
+         * updated to fired.
+         */
+        public static final int STATE_FIRED = 1;
+        /**
+         * Once the user has dismissed the notification the alert's state should
+         * be set to dismissed so it is not fired again.
+         */
+        public static final int STATE_DISMISSED = 2;
 
         /**
          * The number of minutes that this alarm precedes the start time. Column
@@ -2024,7 +1971,7 @@
         private static final String WHERE_FINDNEXTALARMTIME = ALARM_TIME + ">=?";
         private static final String SORT_ORDER_ALARMTIME_ASC = ALARM_TIME + " ASC";
 
-        private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + SCHEDULED
+        private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + STATE_SCHEDULED
                 + " AND " + ALARM_TIME + "<?"
                 + " AND " + ALARM_TIME + ">?"
                 + " AND " + END + ">=?";
@@ -2038,10 +1985,11 @@
         public static final Uri CONTENT_URI_BY_INSTANCE =
             Uri.parse("content://" + AUTHORITY + "/calendar_alerts/by_instance");
 
-        private static final boolean DEBUG = true;
+        private static final boolean DEBUG = false;
 
         /**
-         * Helper for inserting an alarm time associated with an event
+         * Helper for inserting an alarm time associated with an event TODO move
+         * to Provider
          *
          * @hide
          */
@@ -2056,51 +2004,32 @@
             values.put(CalendarAlerts.CREATION_TIME, currentTime);
             values.put(CalendarAlerts.RECEIVED_TIME, 0);
             values.put(CalendarAlerts.NOTIFY_TIME, 0);
-            values.put(CalendarAlerts.STATE, SCHEDULED);
+            values.put(CalendarAlerts.STATE, STATE_SCHEDULED);
             values.put(CalendarAlerts.MINUTES, minutes);
             return cr.insert(CONTENT_URI, values);
         }
 
         /**
-         * Queries alerts info using the given projection, selection filter, and
-         * ordering. This is a blocking call and should not be done on the UI
-         * thread. For selection and selectionArgs usage see
-         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
-         *
-         * @param cr The content resolver to use for the query
-         * @param projection The columns to return
-         * @param selection Filter on the query as an SQL WHERE statement
-         * @param selectionArgs Args to replace any '?'s in the selection
-         * @param sortOrder How to order the rows as an SQL ORDER BY statement
-         * @return A Cursor containing the matching alerts
-         */
-        public static final Cursor query(ContentResolver cr, String[] projection,
-                String selection, String[] selectionArgs, String sortOrder) {
-            return cr.query(CONTENT_URI, projection, selection, selectionArgs,
-                    sortOrder);
-        }
-
-        /**
          * Finds the next alarm after (or equal to) the given time and returns
          * the time of that alarm or -1 if no such alarm exists. This is a
-         * blocking call and should not be done on the UI thread.
+         * blocking call and should not be done on the UI thread. TODO move to
+         * provider
          *
          * @param cr the ContentResolver
          * @param millis the time in UTC milliseconds
          * @return the next alarm time greater than or equal to "millis", or -1
          *         if no such alarm exists.
+         * @hide
          */
         public static final long findNextAlarmTime(ContentResolver cr, long millis) {
             String selection = ALARM_TIME + ">=" + millis;
             // TODO: construct an explicit SQL query so that we can add
             // "LIMIT 1" to the end and get just one result.
             String[] projection = new String[] { ALARM_TIME };
-            Cursor cursor = query(cr, projection,
-                    WHERE_FINDNEXTALARMTIME,
-                    new String[] {
+            Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_FINDNEXTALARMTIME,
+                    (new String[] {
                         Long.toString(millis)
-                    },
-                    SORT_ORDER_ALARMTIME_ASC);
+                    }), SORT_ORDER_ALARMTIME_ASC);
             long alarmTime = -1;
             try {
                 if (cursor != null && cursor.moveToFirst()) {
@@ -2116,13 +2045,14 @@
 
         /**
          * Searches the CalendarAlerts table for alarms that should have fired
-         * but have not and then reschedules them.  This method can be called
-         * at boot time to restore alarms that may have been lost due to a
-         * phone reboot.
+         * but have not and then reschedules them. This method can be called at
+         * boot time to restore alarms that may have been lost due to a phone
+         * reboot. TODO move to provider
          *
          * @param cr the ContentResolver
          * @param context the Context
          * @param manager the AlarmManager
+         * @hide
          */
         public static final void rescheduleMissedAlarms(ContentResolver cr,
                 Context context, AlarmManager manager) {
@@ -2136,15 +2066,10 @@
 
             // TODO: construct an explicit SQL query so that we can add
             // "GROUPBY" instead of doing a sort and de-dup
-            Cursor cursor = CalendarAlerts.query(cr,
-                    projection,
-                    WHERE_RESCHEDULE_MISSED_ALARMS,
-                    new String[] {
-                        Long.toString(now),
-                        Long.toString(ancient),
-                        Long.toString(now)
-                    },
-                    SORT_ORDER_ALARMTIME_ASC);
+            Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection,
+                    WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] {
+                            Long.toString(now), Long.toString(ancient), Long.toString(now)
+                    }), SORT_ORDER_ALARMTIME_ASC);
             if (cursor == null) {
                 return;
             }
@@ -2177,12 +2102,13 @@
          * keep scheduled reminders up to date but apps may use this to
          * implement snooze functionality without modifying the reminders table.
          * Scheduled alarms will generate an intent using
-         * {@link #ACTION_EVENT_REMINDER}.
+         * {@link #ACTION_EVENT_REMINDER}. TODO Move to provider
          *
          * @param context A context for referencing system resources
          * @param manager The AlarmManager to use or null
          * @param alarmTime The time to fire the intent in UTC millis since
          *            epoch
+         * @hide
          */
         public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) {
             if (DEBUG) {
@@ -2204,31 +2130,28 @@
         }
 
         /**
-         * Searches for an entry in the CalendarAlerts table that matches
-         * the given event id, begin time and alarm time.  If one is found
-         * then this alarm already exists and this method returns true.
-         *
+         * Searches for an entry in the CalendarAlerts table that matches the
+         * given event id, begin time and alarm time. If one is found then this
+         * alarm already exists and this method returns true. TODO Move to
+         * provider
+         * 
          * @param cr the ContentResolver
          * @param eventId the event id to match
          * @param begin the start time of the event in UTC millis
          * @param alarmTime the alarm time of the event in UTC millis
-         * @return true if there is already an alarm for the given event
-         *   with the same start time and alarm time.
+         * @return true if there is already an alarm for the given event with
+         *         the same start time and alarm time.
+         * @hide
          */
         public static final boolean alarmExists(ContentResolver cr, long eventId,
                 long begin, long alarmTime) {
             // TODO: construct an explicit SQL query so that we can add
             // "LIMIT 1" to the end and get just one result.
             String[] projection = new String[] { ALARM_TIME };
-            Cursor cursor = query(cr,
-                    projection,
-                    WHERE_ALARM_EXISTS,
-                    new String[] {
-                        Long.toString(eventId),
-                        Long.toString(begin),
-                        Long.toString(alarmTime)
-                    },
-                    null);
+            Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_ALARM_EXISTS,
+                    (new String[] {
+                            Long.toString(eventId), Long.toString(begin), Long.toString(alarmTime)
+                    }), null);
             boolean found = false;
             try {
                 if (cursor != null && cursor.getCount() > 0) {
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 4107c5a..aae9ccf 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1,4 +1,4 @@
-/*
+ /*
  * Copyright (C) 2006 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -266,7 +266,7 @@
             }
         }
 
-        Alignment align = mAlignment;
+        Alignment paraAlign = mAlignment;
         TabStops tabStops = null;
         boolean tabStopsIsInitialized = false;
 
@@ -310,10 +310,10 @@
                                                     ParagraphStyle.class);
                     spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
 
-                    align = mAlignment;
+                    paraAlign = mAlignment;
                     for (int n = spans.length-1; n >= 0; n--) {
                         if (spans[n] instanceof AlignmentSpan) {
-                            align = ((AlignmentSpan) spans[n]).getAlignment();
+                            paraAlign = ((AlignmentSpan) spans[n]).getAlignment();
                             break;
                         }
                     }
@@ -360,6 +360,16 @@
                 tabStopsIsInitialized = true;
             }
 
+            // Determine whether the line aligns to normal, opposite, or center.
+            Alignment align = paraAlign;
+            if (align == Alignment.ALIGN_LEFT) {
+                align = (dir == DIR_LEFT_TO_RIGHT) ?
+                    Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
+            } else if (align == Alignment.ALIGN_RIGHT) {
+                align = (dir == DIR_LEFT_TO_RIGHT) ?
+                    Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL;
+            }
+
             int x;
             if (align == Alignment.ALIGN_NORMAL) {
                 if (dir == DIR_LEFT_TO_RIGHT) {
@@ -411,7 +421,9 @@
         int dir = getParagraphDirection(line);
 
         int x;
-        if (align == Alignment.ALIGN_NORMAL) {
+        if (align == Alignment.ALIGN_LEFT) {
+            x = left;
+        } else if (align == Alignment.ALIGN_NORMAL) {
             if (dir == DIR_LEFT_TO_RIGHT) {
                 x = left;
             } else {
@@ -430,7 +442,9 @@
                 }
             }
             int max = (int)getLineExtent(line, tabStops, false);
-            if (align == Alignment.ALIGN_OPPOSITE) {
+            if (align == Alignment.ALIGN_RIGHT) {
+                x = right - max;
+            } else if (align == Alignment.ALIGN_OPPOSITE) {
                 if (dir == DIR_LEFT_TO_RIGHT) {
                     x = right - max;
                 } else {
@@ -738,11 +752,15 @@
         int dir = getParagraphDirection(line);
         Alignment align = getParagraphAlignment(line);
 
-        if (align == Alignment.ALIGN_NORMAL) {
+        if (align == Alignment.ALIGN_LEFT) {
+            return 0;
+        } else if (align == Alignment.ALIGN_NORMAL) {
             if (dir == DIR_RIGHT_TO_LEFT)
                 return getParagraphRight(line) - getLineMax(line);
             else
                 return 0;
+        } else if (align == Alignment.ALIGN_RIGHT) {
+            return mWidth - getLineMax(line);
         } else if (align == Alignment.ALIGN_OPPOSITE) {
             if (dir == DIR_RIGHT_TO_LEFT)
                 return 0;
@@ -765,11 +783,15 @@
         int dir = getParagraphDirection(line);
         Alignment align = getParagraphAlignment(line);
 
-        if (align == Alignment.ALIGN_NORMAL) {
+        if (align == Alignment.ALIGN_LEFT) {
+            return getParagraphLeft(line) + getLineMax(line);
+        } else if (align == Alignment.ALIGN_NORMAL) {
             if (dir == DIR_RIGHT_TO_LEFT)
                 return mWidth;
             else
                 return getParagraphLeft(line) + getLineMax(line);
+        } else if (align == Alignment.ALIGN_RIGHT) {
+            return mWidth;
         } else if (align == Alignment.ALIGN_OPPOSITE) {
             if (dir == DIR_RIGHT_TO_LEFT)
                 return getLineMax(line);
@@ -1765,8 +1787,10 @@
         ALIGN_NORMAL,
         ALIGN_OPPOSITE,
         ALIGN_CENTER,
-        // XXX ALIGN_LEFT,
-        // XXX ALIGN_RIGHT,
+        /** @hide */
+        ALIGN_LEFT,
+        /** @hide */
+        ALIGN_RIGHT,
     }
 
     private static final int TAB_INCREMENT = 20;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index bf7f359..bb5c954 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9121,9 +9121,15 @@
     }
 
     /**
-     * Reset the resolved layout direction by clearing the corresponding flag
+     * Reset the resolved layout direction.
+     *
+     * Subclasses need to override this method to clear cached information that depends on the
+     * resolved layout direction, or to inform child views that inherit their layout direction.
+     * Overrides must also call the superclass implementation at the start of their implementation.
+     *
+     * @hide
      */
-    void resetLayoutDirectionResolution() {
+    protected void resetLayoutDirectionResolution() {
         // Reset the current View resolution
         mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED;
     }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2a90dde..da88fbb 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1746,6 +1746,7 @@
                 final long now = SystemClock.uptimeMillis();
                 event = MotionEvent.obtain(now, now,
                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
                 syntheticEvent = true;
             }
 
@@ -4998,12 +4999,8 @@
         viewAncestor.requestTransitionStart(transition);
     }
 
-    /**
-     * This method will be called when we need to reset the layout direction resolution flag
-     *
-     */
     @Override
-    void resetLayoutDirectionResolution() {
+    protected void resetLayoutDirectionResolution() {
         super.resetLayoutDirectionResolution();
 
         // Take care of resetting the children resolution too
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 7e41d36..4f97066 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1950,6 +1950,7 @@
     // mInitialViewState is set by didFirstLayout() and then reset in the
     // next webkitDraw after passing the state to the UI thread.
     private ViewState mInitialViewState = null;
+    private boolean mFirstLayoutForNonStandardLoad;
 
     static class ViewState {
         float mMinScale;
@@ -1977,6 +1978,7 @@
         int mMinPrefWidth;
         // only non-null if it is for the first picture set after the first layout
         ViewState mViewState;
+        boolean mFirstLayoutForNonStandardLoad;
         boolean mFocusSizeChanged;
     }
 
@@ -2026,6 +2028,10 @@
                 draw.mViewState = mInitialViewState;
                 mInitialViewState = null;
             }
+            if (mFirstLayoutForNonStandardLoad) {
+                draw.mFirstLayoutForNonStandardLoad = true;
+                mFirstLayoutForNonStandardLoad = false;
+            }
             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
             Message.obtain(mWebView.mPrivateHandler,
                     WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
@@ -2312,6 +2318,8 @@
 
         // if mViewportWidth is 0, it means device-width, always update.
         if (mViewportWidth != 0 && !updateViewState) {
+            // For non standard load, since updateViewState will be false.
+            mFirstLayoutForNonStandardLoad = true;
             ViewState viewState = new ViewState();
             viewState.mMinScale = mViewportMinimumScale / 100.0f;
             viewState.mMaxScale = mViewportMaximumScale / 100.0f;
@@ -2471,9 +2479,10 @@
     // called by JNI
     private void restoreScale(float scale, float textWrapScale) {
         if (mBrowserFrame.firstLayoutDone() == false) {
-            mRestoredScale = scale;
+            final float defaultScale = mWebView.getDefaultZoomScale();
+            mRestoredScale = (scale <= 0.0) ? defaultScale : scale;
             if (mSettings.getUseWideViewPort()) {
-                mRestoredTextWrapScale = textWrapScale;
+                mRestoredTextWrapScale = (textWrapScale <= 0.0) ? defaultScale : textWrapScale;
             }
         }
     }
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 6c6974b..7d43e94 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1024,6 +1024,11 @@
         } else {
             mInZoomOverview = !scaleHasDiff;
         }
+        if (drawData.mFirstLayoutForNonStandardLoad && settings.getLoadWithOverviewMode()) {
+            // Set mInitialZoomOverview in case this is the first picture for non standard load,
+            // so next new picture could be forced into overview mode if it's true.
+            mInitialZoomOverview = mInZoomOverview;
+        }
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2a70ac8..939779f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -340,6 +340,16 @@
 
     private WordIterator mWordIterator;
 
+    // The alignment to pass to Layout, or null if not resolved.
+    private Layout.Alignment mLayoutAlignment;
+
+    // The default value for mTextAlign.
+    private TextAlign mTextAlign = TextAlign.INHERIT;
+
+    private static enum TextAlign {
+        INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END;
+    }
+
     /*
      * Kick-start the font cache for the zygote process (to pay the cost of
      * initializing freetype for our default font only once).
@@ -5532,6 +5542,73 @@
                       physicalWidth, false);
     }
 
+    @Override
+    protected void resetLayoutDirectionResolution() {
+        super.resetLayoutDirectionResolution();
+
+        if (mLayoutAlignment != null &&
+                (mTextAlign == TextAlign.VIEW_START ||
+                mTextAlign == TextAlign.VIEW_END)) {
+            mLayoutAlignment = null;
+        }
+    }
+
+    private Layout.Alignment getLayoutAlignment() {
+        if (mLayoutAlignment == null) {
+            Layout.Alignment alignment;
+            TextAlign textAlign = mTextAlign;
+            switch (textAlign) {
+                case INHERIT:
+                    // fall through to gravity temporarily
+                    // intention is to inherit value through view hierarchy.
+                case GRAVITY:
+                    switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
+                        case Gravity.START:
+                            alignment = Layout.Alignment.ALIGN_NORMAL;
+                            break;
+                        case Gravity.END:
+                            alignment = Layout.Alignment.ALIGN_OPPOSITE;
+                            break;
+                        case Gravity.LEFT:
+                            alignment = Layout.Alignment.ALIGN_LEFT;
+                            break;
+                        case Gravity.RIGHT:
+                            alignment = Layout.Alignment.ALIGN_RIGHT;
+                            break;
+                        case Gravity.CENTER_HORIZONTAL:
+                            alignment = Layout.Alignment.ALIGN_CENTER;
+                            break;
+                        default:
+                            alignment = Layout.Alignment.ALIGN_NORMAL;
+                            break;
+                    }
+                    break;
+                case TEXT_START:
+                    alignment = Layout.Alignment.ALIGN_NORMAL;
+                    break;
+                case TEXT_END:
+                    alignment = Layout.Alignment.ALIGN_OPPOSITE;
+                    break;
+                case CENTER:
+                    alignment = Layout.Alignment.ALIGN_CENTER;
+                    break;
+                case VIEW_START:
+                    alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+                            Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
+                    break;
+                case VIEW_END:
+                    alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+                            Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
+                    break;
+                default:
+                    alignment = Layout.Alignment.ALIGN_NORMAL;
+                    break;
+            }
+            mLayoutAlignment = alignment;
+        }
+        return mLayoutAlignment;
+    }
+
     /**
      * The width passed in is now the desired layout width,
      * not the full view width with padding.
@@ -5552,25 +5629,7 @@
             hintWidth = 0;
         }
 
-        final int layoutDirection = getResolvedLayoutDirection();
-        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
-        Layout.Alignment alignment;
-        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-            case Gravity.CENTER_HORIZONTAL:
-                alignment = Layout.Alignment.ALIGN_CENTER;
-                break;
-
-            case Gravity.RIGHT:
-                // Note, Layout resolves ALIGN_OPPOSITE to left or
-                // right based on the paragraph direction.
-                alignment = Layout.Alignment.ALIGN_OPPOSITE;
-                break;
-
-            default:
-                alignment = Layout.Alignment.ALIGN_NORMAL;
-        }
-
+        Layout.Alignment alignment = getLayoutAlignment();
         boolean shouldEllipsize = mEllipsize != null && mInput == null;
 
         if (mText instanceof Spannable) {
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index f5efda9..54bddb2 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -90,7 +90,7 @@
 
         // Reset the interface and hide the notification.
         if (mInterfaceName != null) {
-            nativeReset(mInterfaceName);
+            jniResetInterface(mInterfaceName);
             mCallback.restore();
             hideNotification();
             mInterfaceName = null;
@@ -119,7 +119,7 @@
     public void protect(ParcelFileDescriptor socket, String name) {
         try {
             mContext.enforceCallingPermission(VPN, "protect");
-            nativeProtect(socket.getFd(), name);
+            jniProtectSocket(socket.getFd(), name);
         } finally {
             try {
                 socket.close();
@@ -152,17 +152,22 @@
         }
 
         // Create and configure the interface.
-        ParcelFileDescriptor descriptor = ParcelFileDescriptor.adoptFd(
-                nativeEstablish(config.mtu, config.addresses, config.routes));
+        ParcelFileDescriptor descriptor =
+                ParcelFileDescriptor.adoptFd(jniCreateInterface(config.mtu));
 
-        // Replace the interface and abort if it fails.
+        // Abort if any of the following steps fails.
         try {
-            String interfaceName = nativeGetName(descriptor.getFd());
-
-            if (mInterfaceName != null && !mInterfaceName.equals(interfaceName)) {
-                nativeReset(mInterfaceName);
+            String name = jniGetInterfaceName(descriptor.getFd());
+            if (jniSetAddresses(name, config.addresses) < 1) {
+                throw new IllegalArgumentException("At least one address must be specified");
             }
-            mInterfaceName = interfaceName;
+            if (config.routes != null) {
+                jniSetRoutes(name, config.routes);
+            }
+            if (mInterfaceName != null && !mInterfaceName.equals(name)) {
+                jniResetInterface(mInterfaceName);
+            }
+            mInterfaceName = name;
         } catch (RuntimeException e) {
             try {
                 descriptor.close();
@@ -195,7 +200,7 @@
 
     // INetworkManagementEventObserver.Stub
     public synchronized void interfaceRemoved(String name) {
-        if (name.equals(mInterfaceName) && nativeCheck(name) == 0) {
+        if (name.equals(mInterfaceName) && jniCheckInterface(name) == 0) {
             hideNotification();
             mInterfaceName = null;
             mCallback.restore();
@@ -253,11 +258,13 @@
         }
     }
 
-    private native int nativeEstablish(int mtu, String addresses, String routes);
-    private native String nativeGetName(int fd);
-    private native void nativeReset(String name);
-    private native int nativeCheck(String name);
-    private native void nativeProtect(int fd, String name);
+    private native int jniCreateInterface(int mtu);
+    private native String jniGetInterfaceName(int fd);
+    private native int jniSetAddresses(String name, String addresses);
+    private native int jniSetRoutes(String name, String routes);
+    private native void jniResetInterface(String name);
+    private native int jniCheckInterface(String name);
+    private native void jniProtectSocket(int fd, String name);
 
     /**
      * Handle legacy VPN requests. This method stops the services and restart
diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp
index ae7fbfe..a0ea92b 100644
--- a/services/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/jni/com_android_server_connectivity_Vpn.cpp
@@ -42,6 +42,9 @@
 namespace android
 {
 
+static int inet4 = -1;
+static int inet6 = -1;
+
 static inline in_addr_t *as_in_addr(sockaddr *sa) {
     return &((sockaddr_in *)sa)->sin_addr.s_addr;
 }
@@ -51,11 +54,9 @@
 #define SYSTEM_ERROR -1
 #define BAD_ARGUMENT -2
 
-static int create_interface(int mtu, char *name, int *index)
+static int create_interface(int mtu)
 {
-    int tun = open("/dev/tun", O_RDWR);
-    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-    int flags;
+    int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
 
     ifreq ifr4;
     memset(&ifr4, 0, sizeof(ifr4));
@@ -81,38 +82,45 @@
         goto error;
     }
 
-    // Get interface index.
-    if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
-        LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno));
-        goto error;
-    }
-
-    // Make it non-blocking.
-    flags = fcntl(tun, F_GETFL, 0);
-    if (flags == -1 || fcntl(tun, F_SETFL, flags | O_NONBLOCK)) {
-        LOGE("Cannot set non-blocking on %s: %s", ifr4.ifr_name, strerror(errno));
-        goto error;
-    }
-
-    strcpy(name, ifr4.ifr_name);
-    *index = ifr4.ifr_ifindex;
-    close(inet4);
     return tun;
 
 error:
     close(tun);
-    close(inet4);
     return SYSTEM_ERROR;
 }
 
-static int set_addresses(const char *name, int index, const char *addresses)
+static int get_interface_name(char *name, int tun)
 {
-    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-    int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+    ifreq ifr4;
+    if (ioctl(tun, TUNGETIFF, &ifr4)) {
+        LOGE("Cannot get interface name: %s", strerror(errno));
+        return SYSTEM_ERROR;
+    }
+    strncpy(name, ifr4.ifr_name, IFNAMSIZ);
+    return 0;
+}
+
+static int get_interface_index(const char *name)
+{
+    ifreq ifr4;
+    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+    if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
+        LOGE("Cannot get index of %s: %s", name, strerror(errno));
+        return SYSTEM_ERROR;
+    }
+    return ifr4.ifr_ifindex;
+}
+
+static int set_addresses(const char *name, const char *addresses)
+{
+    int index = get_interface_index(name);
+    if (index < 0) {
+        return index;
+    }
 
     ifreq ifr4;
     memset(&ifr4, 0, sizeof(ifr4));
-    strcpy(ifr4.ifr_name, name);
+    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
     ifr4.ifr_addr.sa_family = AF_INET;
 
     in6_ifreq ifr6;
@@ -121,7 +129,6 @@
 
     char address[65];
     int prefix;
-
     int chars;
     int count = 0;
 
@@ -164,7 +171,7 @@
                 break;
             }
         }
-        LOGV("Address added on %s: %s/%d", name, address, prefix);
+        LOGD("Address added on %s: %s/%d", name, address, prefix);
         ++count;
     }
 
@@ -177,15 +184,15 @@
         count = BAD_ARGUMENT;
     }
 
-    close(inet4);
-    close(inet6);
     return count;
 }
 
-static int set_routes(const char *name, int index, const char *routes)
+static int set_routes(const char *name, const char *routes)
 {
-    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-    int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+    int index = get_interface_index(name);
+    if (index < 0) {
+        return index;
+    }
 
     rtentry rt4;
     memset(&rt4, 0, sizeof(rt4));
@@ -201,7 +208,6 @@
 
     char address[65];
     int prefix;
-
     int chars;
     int count = 0;
 
@@ -211,32 +217,50 @@
         if (strchr(address, ':')) {
             // Add an IPv6 route.
             if (inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 ||
-                    prefix < 1 || prefix > 128) {
+                    prefix < 0 || prefix > 128) {
                 count = BAD_ARGUMENT;
                 break;
             }
 
-            rt6.rtmsg_dst_len = prefix;
+            rt6.rtmsg_dst_len = prefix ? prefix : 1;
             if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
                 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
                 break;
             }
+
+            if (!prefix) {
+                // Split the route instead of replacing the default route.
+                rt6.rtmsg_dst.s6_addr[0] ^= 0x80;
+                if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) {
+                    count = SYSTEM_ERROR;
+                    break;
+                }
+            }
         } else {
             // Add an IPv4 route.
             if (inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 ||
-                    prefix < 1 || prefix > 32) {
+                    prefix < 0 || prefix > 32) {
                 count = BAD_ARGUMENT;
                 break;
             }
 
-            in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
+            in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 1;
             *as_in_addr(&rt4.rt_genmask) = htonl(mask);
             if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
                 count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
                 break;
             }
+
+            if (!prefix) {
+                // Split the route instead of replacing the default route.
+                *as_in_addr(&rt4.rt_dst) ^= htonl(0x80000000);
+                if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) {
+                    count = SYSTEM_ERROR;
+                    break;
+                }
+            }
         }
-        LOGV("Route added on %s: %s/%d", name, address, prefix);
+        LOGD("Route added on %s: %s/%d", name, address, prefix);
         ++count;
     }
 
@@ -250,43 +274,24 @@
         count = BAD_ARGUMENT;
     }
 
-    close(inet4);
-    close(inet6);
     return count;
 }
 
-static int get_interface_name(char *name, int tun)
-{
-    ifreq ifr4;
-    if (ioctl(tun, TUNGETIFF, &ifr4)) {
-        LOGE("Cannot get interface name: %s", strerror(errno));
-        return SYSTEM_ERROR;
-    }
-    strcpy(name, ifr4.ifr_name);
-    return 0;
-}
-
 static int reset_interface(const char *name)
 {
-    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-
     ifreq ifr4;
-    ifr4.ifr_flags = 0;
     strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+    ifr4.ifr_flags = 0;
 
     if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
         LOGE("Cannot reset %s: %s", name, strerror(errno));
-        close(inet4);
         return SYSTEM_ERROR;
     }
-    close(inet4);
     return 0;
 }
 
 static int check_interface(const char *name)
 {
-    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
-
     ifreq ifr4;
     strncpy(ifr4.ifr_name, name, IFNAMSIZ);
     ifr4.ifr_flags = 0;
@@ -294,7 +299,6 @@
     if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
         LOGE("Cannot check %s: %s", name, strerror(errno));
     }
-    close(inet4);
     return ifr4.ifr_flags;
 }
 
@@ -318,86 +322,108 @@
     }
 }
 
-static jint establish(JNIEnv *env, jobject thiz,
-        jint mtu, jstring jAddresses, jstring jRoutes)
+static jint createInterface(JNIEnv *env, jobject thiz, jint mtu)
 {
-    char name[IFNAMSIZ];
-    int index;
-    int tun = create_interface(mtu, name, &index);
+    int tun = create_interface(mtu);
     if (tun < 0) {
         throwException(env, tun, "Cannot create interface");
         return -1;
     }
-    LOGD("%s is created", name);
-
-    const char *addresses;
-    const char *routes;
-    int count;
-
-    // Addresses are required.
-    addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
-    if (!addresses) {
-        jniThrowNullPointerException(env, "address");
-        goto error;
-    }
-    count = set_addresses(name, index, addresses);
-    env->ReleaseStringUTFChars(jAddresses, addresses);
-    if (count <= 0) {
-        throwException(env, count, "Cannot set address");
-        goto error;
-    }
-    LOGD("Configured %d address(es) on %s", count, name);
-
-    // Routes are optional.
-    routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
-    if (routes) {
-        count = set_routes(name, index, routes);
-        env->ReleaseStringUTFChars(jRoutes, routes);
-        if (count < 0) {
-            throwException(env, count, "Cannot set route");
-            goto error;
-        }
-        LOGD("Configured %d route(s) on %s", count, name);
-    }
-
     return tun;
-
-error:
-    close(tun);
-    LOGD("%s is destroyed", name);
-    return -1;
 }
 
-static jstring getName(JNIEnv *env, jobject thiz, jint fd)
+static jstring getInterfaceName(JNIEnv *env, jobject thiz, jint tun)
 {
     char name[IFNAMSIZ];
-    if (get_interface_name(name, fd) < 0) {
+    if (get_interface_name(name, tun) < 0) {
         throwException(env, SYSTEM_ERROR, "Cannot get interface name");
         return NULL;
     }
     return env->NewStringUTF(name);
 }
 
-static void reset(JNIEnv *env, jobject thiz, jstring jName)
+static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName,
+        jstring jAddresses)
 {
-    const char *name = jName ?
-            env->GetStringUTFChars(jName, NULL) : NULL;
+    const char *name = NULL;
+    const char *addresses = NULL;
+    int count = -1;
+
+    name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        goto error;
+    }
+    addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
+    if (!addresses) {
+        jniThrowNullPointerException(env, "addresses");
+        goto error;
+    }
+    count = set_addresses(name, addresses);
+    if (count < 0) {
+        throwException(env, count, "Cannot set address");
+        count = -1;
+    }
+
+error:
+    if (name) {
+        env->ReleaseStringUTFChars(jName, name);
+    }
+    if (addresses) {
+        env->ReleaseStringUTFChars(jAddresses, addresses);
+    }
+    return count;
+}
+
+static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName,
+        jstring jRoutes)
+{
+    const char *name = NULL;
+    const char *routes = NULL;
+    int count = -1;
+
+    name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        goto error;
+    }
+    routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
+    if (!routes) {
+        jniThrowNullPointerException(env, "routes");
+        goto error;
+    }
+    count = set_routes(name, routes);
+    if (count < 0) {
+        throwException(env, count, "Cannot set address");
+        count = -1;
+    }
+
+error:
+    if (name) {
+        env->ReleaseStringUTFChars(jName, name);
+    }
+    if (routes) {
+        env->ReleaseStringUTFChars(jRoutes, routes);
+    }
+    return count;
+}
+
+static void resetInterface(JNIEnv *env, jobject thiz, jstring jName)
+{
+    const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
     if (!name) {
         jniThrowNullPointerException(env, "name");
         return;
     }
     if (reset_interface(name) < 0) {
         throwException(env, SYSTEM_ERROR, "Cannot reset interface");
-    } else {
-        LOGD("%s is deactivated", name);
     }
     env->ReleaseStringUTFChars(jName, name);
 }
 
-static jint check(JNIEnv *env, jobject thiz, jstring jName)
+static jint checkInterface(JNIEnv *env, jobject thiz, jstring jName)
 {
-    const char *name = jName ?
-            env->GetStringUTFChars(jName, NULL) : NULL;
+    const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
     if (!name) {
         jniThrowNullPointerException(env, "name");
         return 0;
@@ -407,10 +433,9 @@
     return flags;
 }
 
-static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName)
+static void protectSocket(JNIEnv *env, jobject thiz, jint fd, jstring jName)
 {
-    const char *name = jName ?
-            env->GetStringUTFChars(jName, NULL) : NULL;
+    const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
     if (!name) {
         jniThrowNullPointerException(env, "name");
         return;
@@ -424,15 +449,23 @@
 //------------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"nativeEstablish", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)establish},
-    {"nativeGetName", "(I)Ljava/lang/String;", (void *)getName},
-    {"nativeReset", "(Ljava/lang/String;)V", (void *)reset},
-    {"nativeCheck", "(Ljava/lang/String;)I", (void *)check},
-    {"nativeProtect", "(ILjava/lang/String;)V", (void *)protect},
+    {"jniCreateInterface", "(I)I", (void *)createInterface},
+    {"jniGetInterfaceName", "(I)Ljava/lang/String;", (void *)getInterfaceName},
+    {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
+    {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes},
+    {"jniResetInterface", "(Ljava/lang/String;)V", (void *)resetInterface},
+    {"jniCheckInterface", "(Ljava/lang/String;)I", (void *)checkInterface},
+    {"jniProtectSocket", "(ILjava/lang/String;)V", (void *)protectSocket},
 };
 
 int register_android_server_connectivity_Vpn(JNIEnv *env)
 {
+    if (inet4 == -1) {
+        inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+    }
+    if (inet6 == -1) {
+        inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+    }
     return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
             gMethods, NELEM(gMethods));
 }