Merge "MountService: Send UMS_CONNECTED broadcast on boot if UMS connected."
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index acb8473..1163106 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -3176,6 +3176,56 @@
     }
 
     /**
+     * Additional columns returned by the {@link Contacts#CONTENT_FILTER_URI} providing the
+     * explanation of why the filter matched the contact.  Specifically, they contain the
+     * data type and element that was used for matching.
+     * <p>
+     * This is temporary API, it will need to change when we move to FTS.
+     *
+     * @hide
+     */
+    public static class SearchSnippetColumns {
+
+        /**
+         * The ID of the data row that was matched by the filter.
+         *
+         * @hide
+         */
+        public static final String SNIPPET_DATA_ID = "snippet_data_id";
+
+        /**
+         * The type of data that was matched by the filter.
+         *
+         * @hide
+         */
+        public static final String SNIPPET_MIMETYPE = "snippet_mimetype";
+
+        /**
+         * The {@link CommonDataKinds.CommonColumns#DATA} field of the data row
+         * that was matched by the filter.
+         *
+         * @hide
+         */
+        public static final String SNIPPET_DATA = "snippet_data";
+
+        /**
+         * The {@link CommonDataKinds.CommonColumns#TYPE} field of the data row
+         * that was matched by the filter.
+         *
+         * @hide
+         */
+        public static final String SNIPPET_TYPE = "snippet_type";
+
+        /**
+         * The {@link CommonDataKinds.CommonColumns#LABEL} field of the data row
+         * that was matched by the filter.
+         *
+         * @hide
+         */
+        public static final String SNIPPET_LABEL = "snippet_label";
+    }
+
+    /**
      * Container for definitions of common data types stored in the {@link ContactsContract.Data}
      * table.
      */
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 9fcb829..4bd3a82 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -658,14 +658,13 @@
                 // Optimization: don't bother measuring children who are going to use
                 // leftover space. These views will get measured again down below if
                 // there is any leftover space.
-                final int totalLength = mTotalLength;
-                mTotalLength = Math.max(totalLength, totalLength + lp.leftMargin + lp.rightMargin);
+                mTotalLength += lp.leftMargin + lp.rightMargin;
 
                 // Baseline alignment requires to measure widgets to obtain the
-                // baseline offset (in particular for TextViews).
-                // The following defeats the optimization mentioned above.
-                // Allow the child to use as much space as it wants because we
-                // can shrink things later (and re-measure).
+                // baseline offset (in particular for TextViews). The following
+                // defeats the optimization mentioned above. Allow the child to
+                // use as much space as it wants because we can shrink things
+                // later (and re-measure).
                 if (baselineAligned) {
                     final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                     child.measure(freeSpec, freeSpec);
@@ -695,9 +694,8 @@
                 }
 
                 final int childWidth = child.getMeasuredWidth();
-                final int totalLength = mTotalLength;
-                mTotalLength = Math.max(totalLength, totalLength + childWidth + lp.leftMargin +
-                        lp.rightMargin + getNextLocationOffset(child));
+                mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
+                        getNextLocationOffset(child);
 
                 if (useLargestChild) {
                     largestChildWidth = Math.max(childWidth, largestChildWidth);
@@ -782,9 +780,8 @@
 
                 final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                         child.getLayoutParams();
-                final int totalLength = mTotalLength;
-                mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
-                        lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
+                mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
+                        getNextLocationOffset(child);
             }
         }
 
@@ -854,9 +851,8 @@
                     }
                 }
 
-                final int totalLength = mTotalLength;
-                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredWidth() +
-                        lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
+                mTotalLength += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin +
+                        getNextLocationOffset(child);
 
                 boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY &&
                         lp.height == LayoutParams.MATCH_PARENT;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index bb3b07e..fd24058 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -115,6 +115,18 @@
     private int mTouchSlop;
     private int mMinimumVelocity;
     private int mMaximumVelocity;
+    
+    /**
+     * ID of the active pointer. This is used to retain consistency during
+     * drags/flings if multiple pointers are used.
+     */
+    private int mActivePointerId = INVALID_POINTER;
+    
+    /**
+     * Sentinel value for no current active pointer.
+     * Used by {@link #mActivePointerId}.
+     */
+    private static final int INVALID_POINTER = -1;
 
     public ScrollView(Context context) {
         this(context, null);
@@ -392,10 +404,8 @@
             return true;
         }
 
-        final float y = ev.getY();
-
-        switch (action) {
-            case MotionEvent.ACTION_MOVE:
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_MOVE: {
                 /*
                  * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                  * whether the user has moved far enough from his original down touch.
@@ -405,21 +415,29 @@
                 * Locally do absolute value. mLastMotionY is set to the y value
                 * of the down event.
                 */
+                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                final float y = ev.getY(pointerIndex);
                 final int yDiff = (int) Math.abs(y - mLastMotionY);
                 if (yDiff > mTouchSlop) {
                     mIsBeingDragged = true;
                     mLastMotionY = y;
                 }
                 break;
+            }
 
-            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_DOWN: {
+                final float y = ev.getY();
                 if (!inChild((int)ev.getX(), (int)y)) {
                     mIsBeingDragged = false;
                     break;
                 }
 
-                /* Remember location of down touch */
+                /*
+                 * Remember location of down touch.
+                 * ACTION_DOWN always refers to pointer index 0.
+                 */
                 mLastMotionY = y;
+                mActivePointerId = ev.getPointerId(0);
 
                 /*
                 * If being flinged and user touches the screen, initiate drag;
@@ -428,11 +446,16 @@
                 */
                 mIsBeingDragged = !mScroller.isFinished();
                 break;
+            }
 
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 /* Release the drag */
                 mIsBeingDragged = false;
+                mActivePointerId = INVALID_POINTER;
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
                 break;
         }
 
@@ -458,10 +481,9 @@
         mVelocityTracker.addMovement(ev);
 
         final int action = ev.getAction();
-        final float y = ev.getY();
 
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
                 /*
                 * If being flinged and user touches, stop the fling. isFinished
                 * will be false if being flinged.
@@ -470,29 +492,33 @@
                     mScroller.abortAnimation();
                 }
 
-                if (!inChild((int)ev.getX(), (int)y)) {
-                    mIsBeingDragged = false;
+                final float y = ev.getY();
+                if (!(mIsBeingDragged = inChild((int)ev.getX(), (int)y))) {
                     return false;
                 }
-
+                
                 // Remember where the motion event started
                 mLastMotionY = y;
+                mActivePointerId = ev.getPointerId(0);
                 break;
+            }
             case MotionEvent.ACTION_MOVE:
                 if (mIsBeingDragged) {
                     // Scroll to follow the motion event
+                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                    final float y = ev.getY(activePointerIndex);
                     final int deltaY = (int) (mLastMotionY - y);
                     mLastMotionY = y;
 
                     overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(),
                             0, getOverscrollMax());
-                    break;
                 }
-            case MotionEvent.ACTION_UP:
+                break;
+            case MotionEvent.ACTION_UP: 
                 if (mIsBeingDragged) {
                     final VelocityTracker velocityTracker = mVelocityTracker;
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                    int initialVelocity = (int) velocityTracker.getYVelocity();
+                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
 
                     if (getChildCount() > 0) {
                         if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
@@ -505,15 +531,39 @@
                         }
                     }
 
+                    mActivePointerId = INVALID_POINTER;
+                    mIsBeingDragged = false;
+
                     if (mVelocityTracker != null) {
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
                     }
                 }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
+                break;
         }
         return true;
     }
     
+    private void onSecondaryPointerUp(MotionEvent ev) {
+        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        final int pointerId = ev.getPointerId(pointerIndex);
+        if (pointerId == mActivePointerId) {
+            // This was our active pointer going up. Choose a new
+            // active pointer and adjust accordingly.
+            // TODO: Make this decision more intelligent.
+            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+            mLastMotionY = ev.getY(newPointerIndex);
+            mActivePointerId = ev.getPointerId(newPointerIndex);
+            if (mVelocityTracker != null) {
+                mVelocityTracker.clear();
+            }
+        }
+    }
+    
     @Override
     protected void onOverscrolled(int scrollX, int scrollY,
             boolean clampedX, boolean clampedY) {
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
index a48ccf9..5d71231 100644
--- a/core/java/com/android/internal/app/TetherActivity.java
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -141,7 +141,7 @@
                 }
             } else {
                 for (String t : tethered) {
-                    if (!cm.untether("ppp0")) {
+                    if (!cm.untether(t)) {
                         error = true;
                     }
                 }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 10d6e3a..16638bc 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -76,14 +76,21 @@
 
     private BroadcastReceiver mStateReceiver;
 
+    private static final String USB_NEAR_IFACE_ADDR      = "169.254.2.1";
+
     private String[] mDhcpRange;
+    private static final String DHCP_DEFAULT_RANGE_START = "169.254.2.10";
+    private static final String DHCP_DEFAULT_RANGE_STOP  = "169.254.2.64";
 
     private String[] mDnsServers;
+    private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
+    private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2";
 
     private String mUpstreamIfaceName;
 
     // turning on/off RNDIS resets the interface generating and extra discon/conn cycle
     // count how many to ignore..  Self correcting if you plug/unplug a bunch of times.
+    // TODO - brittle - maybe don't need?
     private int mUsbResetExpected = 0;
 
     HierarchicalStateMachine mTetherMasterSM;
@@ -119,8 +126,8 @@
                 com.android.internal.R.array.config_tether_dhcp_range);
         if (mDhcpRange.length == 0) {
             mDhcpRange = new String[2];
-            mDhcpRange[0] = new String("169.254.2.2");
-            mDhcpRange[1] = new String("169.254.2.64");
+            mDhcpRange[0] = DHCP_DEFAULT_RANGE_START;
+            mDhcpRange[1] = DHCP_DEFAULT_RANGE_STOP;
         } else if(mDhcpRange.length == 1) {
             String[] tmp = new String[2];
             tmp[0] = mDhcpRange[0];
@@ -135,8 +142,8 @@
 
         // TODO - remove and rely on real notifications of the current iface
         mDnsServers = new String[2];
-        mDnsServers[0] = "8.8.8.8";
-        mDnsServers[1] = "4.2.2.2";
+        mDnsServers[0] = DNS_DEFAULT_SERVER1;
+        mDnsServers[1] = DNS_DEFAULT_SERVER2;
         mUpstreamIfaceName = "rmnet0";
     }
 
@@ -300,7 +307,8 @@
         broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
                 erroredList);
         mContext.sendStickyBroadcast(broadcast);
-
+        Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
+                activeList.size() + ", " + erroredList.size());
         // check if we need to send a USB notification
         // Check if the user wants to be bothered
         boolean tellUser = (Settings.Secure.getInt(mContext.getContentResolver(),
@@ -429,7 +437,7 @@
                         return;
                     }
                 }
-                Tethering.this.toggleUsbIfaces(true); // add them
+                Tethering.this.enableUsbIfaces(true); // add them
             } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
                 Log.w(TAG, "got UMS disconneded broadcast");
                 synchronized (Tethering.this) {
@@ -439,7 +447,7 @@
                         return;
                     }
                 }
-                Tethering.this.toggleUsbIfaces(false); // remove them
+                Tethering.this.enableUsbIfaces(false); // remove them
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                 IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
@@ -458,7 +466,7 @@
     }
 
     // used on cable insert/remove
-    private void toggleUsbIfaces(boolean add) {
+    private void enableUsbIfaces(boolean enable) {
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
         String[] ifaces = new String[0];
@@ -471,7 +479,7 @@
         for (String iface : ifaces) {
             for (String regex : mTetherableUsbRegexs) {
                 if (iface.matches(regex)) {
-                    if (add) {
+                    if (enable) {
                         interfaceAdded(iface);
                     } else {
                         interfaceRemoved(iface);
@@ -482,8 +490,8 @@
     }
 
     // toggled when we enter/leave the fully teathered state
-    private boolean enableRndisUsb(boolean enabled) {
-        Log.d(TAG, "enableRndisUsb(" + enabled + ")");
+    private boolean enableUsbRndis(boolean enabled) {
+        Log.d(TAG, "enableUsbRndis(" + enabled + ")");
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
                 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
 
@@ -509,8 +517,8 @@
     }
 
     // configured when we start tethering and unconfig'd on error or conclusion
-    private boolean configureUsb(boolean enabled) {
-        Log.d(TAG, "configureUsb(" + enabled + ")");
+    private boolean configureUsbIface(boolean enabled) {
+        Log.d(TAG, "configureUsbIface(" + enabled + ")");
 
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -536,6 +544,10 @@
                                 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
                             } else {
                                 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
+                                // TODO - clean this up - maybe a better regex?
+                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" running", "");
+                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running ","");
+                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running","");
                             }
                             service.setInterfaceConfig(iface, ifcg);
                         }
@@ -547,21 +559,6 @@
             }
         }
 
-        if (!enabled) {
-            // turn off ndis
-            try {
-                synchronized (this) {
-                    if (service.isUsbRNDISStarted()) {
-                        mUsbResetExpected += 2;
-                        service.stopUsbRNDIS();
-                    }
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "Error stopping usb RNDIS :" + e);
-                return false;
-            }
-        }
-
         return true;
     }
 
@@ -822,7 +819,7 @@
             if (errored && mUsb) {
                 // note everything's been unwound by this point so nothing to do on
                 // further error..
-                Tethering.this.configureUsb(false);
+                Tethering.this.configureUsbIface(false);
             }
         }
 
@@ -863,7 +860,7 @@
             public void enter() {
                 setAvailable(false);
                 if (mUsb) {
-                    if (!Tethering.this.configureUsb(true)) {
+                    if (!Tethering.this.configureUsbIface(true)) {
                         Message m = mTetherMasterSM.obtainMessage(
                                 TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
                         m.obj = TetherInterfaceSM.this;
@@ -889,7 +886,7 @@
                         m.obj = TetherInterfaceSM.this;
                         mTetherMasterSM.sendMessage(m);
                         if (mUsb) {
-                            if (!Tethering.this.configureUsb(false)) {
+                            if (!Tethering.this.configureUsbIface(false)) {
                                 transitionTo(mUsbConfigurationErrorState);
                                 break;
                             }
@@ -947,7 +944,7 @@
                     sendMessageAtFrontOfQueue(m);
                     return;
                 }
-                if (mUsb) Tethering.this.enableRndisUsb(true);
+                if (mUsb) Tethering.this.enableUsbRndis(true);
                 Log.d(TAG, "Tethered " + mIfaceName);
                 setAvailable(false);
                 setTethered(true);
@@ -955,7 +952,7 @@
             }
             @Override
             public void exit() {
-                if(mUsb) Tethering.this.enableRndisUsb(false);
+                if (mUsb) Tethering.this.enableUsbRndis(false);
             }
             @Override
             public boolean processMessage(Message message) {
@@ -986,7 +983,7 @@
                         mTetherMasterSM.sendMessage(m);
                         if (message.what == CMD_TETHER_UNREQUESTED) {
                             if (mUsb) {
-                                if (!Tethering.this.configureUsb(false)) {
+                                if (!Tethering.this.configureUsbIface(false)) {
                                     transitionTo(mUsbConfigurationErrorState);
                                 } else {
                                     transitionTo(mInitialState);
@@ -998,7 +995,6 @@
                             transitionTo(mUnavailableState);
                         }
                         Log.d(TAG, "Untethered " + mIfaceName);
-                        sendTetherStateChangedBroadcast();
                         break;
                     case CMD_CELL_DUN_ERROR:
                     case CMD_IP_FORWARDING_ENABLE_ERROR:
@@ -1030,7 +1026,7 @@
                         Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                         sendTetherStateChangedBroadcast();
                         if (mUsb) {
-                            if (!Tethering.this.configureUsb(false)) {
+                            if (!Tethering.this.configureUsbIface(false)) {
                                 transitionTo(mUsbConfigurationErrorState);
                                 break;
                             }
@@ -1187,7 +1183,6 @@
         private HierarchicalState mCellDunRequestedState;
         private HierarchicalState mCellDunAliveState;
         private HierarchicalState mTetherModeAliveState;
-        private HierarchicalState mCellDunUnRequestedState;
 
         private HierarchicalState mCellDunErrorState;
         private HierarchicalState mSetIpForwardingEnabledErrorState;
@@ -1215,8 +1210,6 @@
             addState(mCellDunAliveState);
             mTetherModeAliveState = new TetherModeAliveState();
             addState(mTetherModeAliveState);
-            mCellDunUnRequestedState = new CellDunUnRequestedState();
-            addState(mCellDunUnRequestedState);
 
             mCellDunErrorState = new CellDunErrorState();
             addState(mCellDunErrorState);
@@ -1236,8 +1229,81 @@
             setInitialState(mInitialState);
         }
 
+        class TetherMasterUtilState extends HierarchicalState {
+            @Override
+            public boolean processMessage(Message m) {
+                return false;
+            }
+            public int turnOnMobileDun() {
+                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+                int retValue = Phone.APN_REQUEST_FAILED;
+                try {
+                    retValue = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+                            Phone.FEATURE_ENABLE_DUN, new Binder());
+                } catch (Exception e) {
+                }
+                return retValue;
+            }
+            public boolean turnOffMobileDun() {
+                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+                IConnectivityManager service =
+                        IConnectivityManager.Stub.asInterface(b);
+                try {
+                    service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+                            Phone.FEATURE_ENABLE_DUN);
+                } catch (Exception e) {
+                    return false;
+                }
+                return true;
+            }
+            public boolean turnOnMasterTetherSettings() {
+                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService service =
+                        INetworkManagementService.Stub.asInterface(b);
+                try {
+                    service.setIpForwardingEnabled(true);
+                } catch (Exception e) {
+                    transitionTo(mSetIpForwardingEnabledErrorState);
+                    return false;
+                }
+                try {
+                    service.startTethering(mDhcpRange[0], mDhcpRange[1]);
+                } catch (Exception e) {
+                    transitionTo(mStartTetheringErrorState);
+                    return false;
+                }
+                try {
+                    service.setDnsForwarders(mDnsServers);
+                } catch (Exception e) {
+                    transitionTo(mSetDnsForwardersErrorState);
+                    return false;
+                }
+                transitionTo(mTetherModeAliveState);
+                return true;
+            }
+            public boolean turnOffMasterTetherSettings() {
+                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService service =
+                        INetworkManagementService.Stub.asInterface(b);
+                try {
+                    service.stopTethering();
+                } catch (Exception e) {
+                    transitionTo(mStopTetheringErrorState);
+                    return false;
+                }
+                try {
+                    service.setIpForwardingEnabled(false);
+                } catch (Exception e) {
+                    transitionTo(mSetIpForwardingDisabledErrorState);
+                    return false;
+                }
+                transitionTo(mInitialState);
+                return true;
+            }
+        }
 
-        class InitialState extends HierarchicalState {
+        class InitialState extends TetherMasterUtilState {
             @Override
             public boolean processMessage(Message message) {
                 Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
@@ -1267,20 +1333,11 @@
                 return retValue;
             }
         }
-        class CellDunRequestedState extends HierarchicalState {
+        class CellDunRequestedState extends TetherMasterUtilState {
             @Override
             public void enter() {
                 ++mSequenceNumber;
-                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager service =
-                        IConnectivityManager.Stub.asInterface(b);
-                int result;
-                try {
-                    result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                            Phone.FEATURE_ENABLE_DUN, new Binder());
-                } catch (Exception e) {
-                    result = Phone.APN_REQUEST_FAILED;
-                }
+                int result = turnOnMobileDun();
                 switch (result) {
                     case Phone.APN_ALREADY_ACTIVE:
                         Log.d(TAG, "Dun already active");
@@ -1319,34 +1376,13 @@
                         if (index != -1) {
                             mNotifyList.remove(index);
                             if (mNotifyList.isEmpty()) {
-                                transitionTo(mCellDunUnRequestedState);
+                                turnOffMobileDun();
+                                transitionTo(mInitialState);
                             }
                         }
                         break;
                     case CMD_CELL_DUN_ENABLED:
-                        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        INetworkManagementService service =
-                                INetworkManagementService.Stub.asInterface(b);
-
-                        try {
-                            service.setIpForwardingEnabled(true);
-                        } catch (Exception e) {
-                            transitionTo(mSetIpForwardingEnabledErrorState);
-                            break;
-                        }
-                        try {
-                            service.startTethering(mDhcpRange[0], mDhcpRange[1]);
-                        } catch (Exception e) {
-                            transitionTo(mStartTetheringErrorState);
-                            break;
-                        }
-                        try {
-                            service.setDnsForwarders(mDnsServers);
-                        } catch (Exception e) {
-                            transitionTo(mSetDnsForwardersErrorState);
-                            break;
-                        }
-                        transitionTo(mTetherModeAliveState);
+                        turnOnMasterTetherSettings();
                         break;
                     case CMD_CELL_DUN_DISABLED:
                         break;
@@ -1363,7 +1399,7 @@
             }
         }
 
-        class CellDunAliveState extends HierarchicalState {
+        class CellDunAliveState extends TetherMasterUtilState {
             @Override
             public void enter() {
                 Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
@@ -1378,29 +1414,7 @@
                     case CMD_TETHER_MODE_REQUESTED:
                         TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
                         mNotifyList.add(who);
-                        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        INetworkManagementService service =
-                                INetworkManagementService.Stub.asInterface(b);
-
-                        try {
-                            service.setIpForwardingEnabled(true);
-                        } catch (Exception e) {
-                            transitionTo(mSetIpForwardingEnabledErrorState);
-                            break;
-                        }
-                        try {
-                            service.startTethering(mDhcpRange[0], mDhcpRange[1]);
-                        } catch (Exception e) {
-                            transitionTo(mStartTetheringErrorState);
-                            break;
-                        }
-                        try {
-                            service.setDnsForwarders(mDnsServers);
-                        } catch (Exception e) {
-                            transitionTo(mSetDnsForwardersErrorState);
-                            break;
-                        }
-                        transitionTo(mTetherModeAliveState);
+                        turnOnMasterTetherSettings();
                         break;
                     case CMD_TETHER_MODE_UNREQUESTED:
                         who = (TetherInterfaceSM)message.obj;
@@ -1408,7 +1422,8 @@
                         if (index != -1) {
                             mNotifyList.remove(index);
                             if (mNotifyList.isEmpty()) {
-                                transitionTo(mCellDunUnRequestedState);
+                                turnOffMobileDun();
+                                transitionTo(mInitialState);
                             }
                         }
                         break;
@@ -1418,13 +1433,7 @@
                     case CMD_CELL_DUN_RENEW:
                         Log.d(TAG, "renewing dun connection - requeuing for another " +
                                 CELL_DUN_RENEW_MS + "ms");
-                        b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                        IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b);
-                        try {
-                            cservice.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                                    Phone.FEATURE_ENABLE_DUN, new Binder());
-                        } catch (Exception e) {
-                        }
+                        turnOnMobileDun();
                         sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
                         break;
                     default:
@@ -1435,7 +1444,7 @@
             }
         }
 
-        class TetherModeAliveState extends HierarchicalState {
+        class TetherModeAliveState extends TetherMasterUtilState {
             @Override
             public void enter() {
                 Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
@@ -1461,7 +1470,8 @@
                         if (index != -1) {
                             mNotifyList.remove(index);
                             if (mNotifyList.isEmpty()) {
-                                transitionTo(mCellDunUnRequestedState);
+                                turnOffMobileDun();
+                                turnOffMasterTetherSettings(); // transitions appropriately
                             }
                         }
                         break;
@@ -1473,33 +1483,12 @@
                             sm.sendMessage(sm.obtainMessage(
                                     TetherInterfaceSM.CMD_TETHER_MODE_DEAD));
                         }
-                        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                        INetworkManagementService service =
-                                INetworkManagementService.Stub.asInterface(b);
-                        try {
-                            service.stopTethering();
-                        } catch (Exception e) {
-                            transitionTo(mStopTetheringErrorState);
-                            break;
-                        }
-                        try {
-                            service.setIpForwardingEnabled(false);
-                        } catch (Exception e) {
-                            transitionTo(mSetIpForwardingDisabledErrorState);
-                            break;
-                        }
-                        transitionTo(mInitialState);
+                        turnOffMasterTetherSettings(); // transitions appropriately
                         break;
                     case CMD_CELL_DUN_RENEW:
                         Log.d(TAG, "renewing dun connection - requeuing for another " +
                                 CELL_DUN_RENEW_MS + "ms");
-                        b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                        IConnectivityManager cservice = IConnectivityManager.Stub.asInterface(b);
-                        try {
-                            cservice.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                                    Phone.FEATURE_ENABLE_DUN, new Binder());
-                        } catch (Exception e) {
-                        }
+                        turnOnMobileDun();
                         sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
                         break;
                     default:
@@ -1510,58 +1499,6 @@
             }
         }
 
-        class CellDunUnRequestedState extends HierarchicalState {
-            @Override
-            public void enter() {
-                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager service =
-                        IConnectivityManager.Stub.asInterface(b);
-                NetworkInfo dunInfo = null;
-                try {
-                    dunInfo = service.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_DUN);
-                } catch (Exception e) {}
-                if (dunInfo != null && !dunInfo.isConnectedOrConnecting()) {
-                    sendMessage(obtainMessage(CMD_CELL_DUN_DISABLED));
-                    return;
-                }
-                try {
-                    service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                            Phone.FEATURE_ENABLE_DUN);
-                } catch (Exception e) {}
-                Message m =  obtainMessage(CMD_CELL_DUN_TIMEOUT);
-                m.arg1 = ++mSequenceNumber;
-                // use a short timeout - this will often be a no-op and
-                // we just want this request to get into the queue before we
-                // try again.
-                sendMessageDelayed(m, CELL_DISABLE_DUN_TIMEOUT_MS);
-            }
-            @Override
-            public boolean processMessage(Message message) {
-                Log.d(TAG, "CellDunUnRequestedState.processMessage what=" + message.what);
-                boolean retValue = true;
-                switch (message.what) {
-                    case CMD_TETHER_MODE_REQUESTED:
-                    case CMD_TETHER_MODE_UNREQUESTED:
-                        deferMessage(message);
-                        break;
-                    case CMD_CELL_DUN_DISABLED:
-                        transitionTo(mInitialState);
-                        break;
-                    case CMD_CELL_DUN_TIMEOUT:
-                        // if we aren't using a sep apn, we won't get a disconnect broadcast..
-                        // just go back to initial after our short pause
-                        if (message.arg1 == mSequenceNumber) {
-                            transitionTo(mInitialState);
-                        }
-                        break;
-                    default:
-                        retValue = false;
-                        break;
-                }
-                return retValue;
-            }
-        }
-
         class ErrorState extends HierarchicalState {
             int mErrorNotification;
             @Override