am 9b2534cf: am acfd2380: reconcile main tree with open-source eclair

Merge commit '9b2534cf5610f6680f9ed50cd265479a51d66259'

* commit '9b2534cf5610f6680f9ed50cd265479a51d66259':
  android-2.1_r1 snapshot
  Use 128p map tiles for high dpi devices.
  Throttle nitz updates as the are too numerous on cdma.
  don't request a sync when temp providers are created
  Increase light sensor delay from one to two seconds.
  Revert jparks code from IPCThreadState.
  Add a warning when we leave threads in the binder thread pool in the background scheduling group.
  PowerManagerService: Ensure that recent changes do not effect the hardware ALS case.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a127df0..d435df5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -116,6 +116,24 @@
             "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
 
     /**
+     * Broadcast Action: A tetherable connection has come or gone
+     * TODO - finish the doc
+     * @hide
+     */
+    public static final String ACTION_TETHER_STATE_CHANGED =
+            "android.net.conn.TETHER_STATE_CHANGED";
+
+    /**
+     * @hide
+     */
+    public static final String EXTRA_AVAILABLE_TETHER_COUNT = "availableCount";
+
+    /**
+     * @hide
+     */
+    public static final String EXTRA_ACTIVE_TETHER_COUNT = "activeCount";
+
+    /**
      * The Default Mobile data connection.  When active, all data traffic
      * will use this connection by default.  Should not coexist with other
      * default connections.
@@ -129,37 +147,33 @@
     public static final int TYPE_WIFI        = 1;
     /**
      * An MMS-specific Mobile data connection.  This connection may be the
-     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * same as {@link #TYPE_MOBILE} but it may be different.  This is used
      * by applications needing to talk to the carrier's Multimedia Messaging
      * Service servers.  It may coexist with default data connections.
-     * {@hide}
      */
     public static final int TYPE_MOBILE_MMS  = 2;
     /**
      * A SUPL-specific Mobile data connection.  This connection may be the
-     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * same as {@link #TYPE_MOBILE} but it may be different.  This is used
      * by applications needing to talk to the carrier's Secure User Plane
      * Location servers for help locating the device.  It may coexist with
      * default data connections.
-     * {@hide}
      */
     public static final int TYPE_MOBILE_SUPL = 3;
     /**
      * A DUN-specific Mobile data connection.  This connection may be the
-     * same as {@link #TYPEMOBILE} but it may be different.  This is used
+     * same as {@link #TYPE_MOBILE} but it may be different.  This is used
      * by applicaitons performing a Dial Up Networking bridge so that
      * the carrier is aware of DUN traffic.  It may coexist with default data
      * connections.
-     * {@hide}
      */
     public static final int TYPE_MOBILE_DUN  = 4;
     /**
      * A High Priority Mobile data connection.  This connection is typically
-     * the same as {@link #TYPEMOBILE} but the routing setup is different.
+     * the same as {@link #TYPE_MOBILE} but the routing setup is different.
      * Only requesting processes will have access to the Mobile DNS servers
      * and only IP's explicitly requested via {@link #requestRouteToHost}
-     * will route over this interface.
-     *{@hide}
+     * will route over this interface if a default route exists.
      */
     public static final int TYPE_MOBILE_HIPRI = 5;
     /** {@hide} */
@@ -342,4 +356,48 @@
         }
         mService = service;
     }
+
+    /**
+     * {@hide}
+     */
+    public String[] getTetherableIfaces() {
+        try {
+            return mService.getTetherableIfaces();
+        } catch (RemoteException e) {
+            return new String[0];
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public String[] getTetheredIfaces() {
+        try {
+            return mService.getTetheredIfaces();
+        } catch (RemoteException e) {
+            return new String[0];
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public boolean tether(String iface) {
+        try {
+            return mService.tether(iface);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * {@hide}
+     */
+    public boolean untether(String iface) {
+        try {
+            return mService.untether(iface);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 9f59cce..caa3f2b 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -50,4 +50,12 @@
     boolean getBackgroundDataSetting();
 
     void setBackgroundDataSetting(boolean allowBackgroundData);
+
+    boolean tether(String iface);
+
+    boolean untether(String iface);
+
+    String[] getTetherableIfaces();
+
+    String[] getTetheredIfaces();
 }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 3d7025d..4259016 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -43,6 +43,8 @@
 
 import com.android.internal.telephony.Phone;
 
+import com.android.server.connectivity.Tethering;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -56,15 +58,15 @@
     private static final boolean DBG = true;
     private static final String TAG = "ConnectivityService";
 
-    // Event log tags (must be in sync with event-log-tags)
-    private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
-
     // how long to wait before switching back to a radio's default network
     private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
     // system property that can override the above value
     private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
             "android.telephony.apn-restore";
 
+
+    private Tethering mTethering;
+
     /**
      * Sometimes we want to refer to the individual network state
      * trackers separately, and sometimes we just want to treat them
@@ -101,9 +103,9 @@
     private List mFeatureUsers;
 
     private boolean mSystemReady;
-    private ArrayList<Intent> mDeferredBroadcasts;
+    private Intent mInitialBroadcast;
 
-    private class NetworkAttributes {
+    private static class NetworkAttributes {
         /**
          * Class for holding settings read from resources.
          */
@@ -115,23 +117,9 @@
         public NetworkAttributes(String init) {
             String fragments[] = init.split(",");
             mName = fragments[0].toLowerCase();
-            if (fragments[1].toLowerCase().equals("wifi")) {
-                mRadio = ConnectivityManager.TYPE_WIFI;
-            } else {
-                mRadio = ConnectivityManager.TYPE_MOBILE;
-            }
-            if (mName.equals("default")) {
-                mType = mRadio;
-            } else if (mName.equals("mms")) {
-                mType = ConnectivityManager.TYPE_MOBILE_MMS;
-            } else if (mName.equals("supl")) {
-                mType = ConnectivityManager.TYPE_MOBILE_SUPL;
-            } else if (mName.equals("dun")) {
-                mType = ConnectivityManager.TYPE_MOBILE_DUN;
-            } else if (mName.equals("hipri")) {
-                mType = ConnectivityManager.TYPE_MOBILE_HIPRI;
-            }
-            mPriority = Integer.parseInt(fragments[2]);
+            mType = Integer.parseInt(fragments[1]);
+            mRadio = Integer.parseInt(fragments[2]);
+            mPriority = Integer.parseInt(fragments[3]);
             mLastState = NetworkInfo.State.UNKNOWN;
         }
         public boolean isDefault() {
@@ -139,22 +127,15 @@
         }
     }
     NetworkAttributes[] mNetAttributes;
+    int mNetworksDefined;
 
-    private class RadioAttributes {
-        public String mName;
-        public int mPriority;
+    private static class RadioAttributes {
         public int mSimultaneity;
         public int mType;
         public RadioAttributes(String init) {
             String fragments[] = init.split(",");
-            mName = fragments[0].toLowerCase();
-            mPriority = Integer.parseInt(fragments[1]);
-            mSimultaneity = Integer.parseInt(fragments[2]);
-            if (mName.equals("wifi")) {
-                mType = ConnectivityManager.TYPE_WIFI;
-            } else {
-                mType = ConnectivityManager.TYPE_MOBILE;
-            }
+            mType = Integer.parseInt(fragments[0]);
+            mSimultaneity = Integer.parseInt(fragments[1]);
         }
     }
     RadioAttributes[] mRadioAttributes;
@@ -204,6 +185,15 @@
 
     private ConnectivityService(Context context) {
         if (DBG) Log.v(TAG, "ConnectivityService starting up");
+
+        // setup our unique device name
+        String id = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.ANDROID_ID);
+        if (id != null && id.length() > 0) {
+            String name = new String("android_").concat(id);
+            SystemProperties.set("net.hostname", name);
+        }
+
         mContext = context;
         mNetTrackers = new NetworkStateTracker[
                 ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -211,48 +201,87 @@
 
         mNetworkPreference = getPersistedNetworkPreference();
 
+        mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1];
+        mNetAttributes = new NetworkAttributes[ConnectivityManager.MAX_NETWORK_TYPE+1];
+
         // Load device network attributes from resources
-        mNetAttributes = new NetworkAttributes[
-                ConnectivityManager.MAX_NETWORK_TYPE+1];
-        mRadioAttributes = new RadioAttributes[
-                ConnectivityManager.MAX_RADIO_TYPE+1];
-        String[] naStrings = context.getResources().getStringArray(
-                com.android.internal.R.array.networkAttributes);
-        // TODO - what if the setting has gaps/unknown types?
-        for (String a : naStrings) {
-            NetworkAttributes n = new NetworkAttributes(a);
-            mNetAttributes[n.mType] = n;
-        }
         String[] raStrings = context.getResources().getStringArray(
                 com.android.internal.R.array.radioAttributes);
-        for (String a : raStrings) {
-            RadioAttributes r = new RadioAttributes(a);
+        for (String raString : raStrings) {
+            RadioAttributes r = new RadioAttributes(raString);
+            if (r.mType > ConnectivityManager.MAX_RADIO_TYPE) {
+                Log.e(TAG, "Error in radioAttributes - ignoring attempt to define type " + r.mType);
+                continue;
+            }
+            if (mRadioAttributes[r.mType] != null) {
+                Log.e(TAG, "Error in radioAttributes - ignoring attempt to redefine type " +
+                        r.mType);
+                continue;
+            }
             mRadioAttributes[r.mType] = r;
         }
 
-        // high priority first
-        mPriorityList = new int[naStrings.length];
-        {
-            int priority = 0; //lowest
-            int nextPos = naStrings.length-1;
-            while (nextPos>-1) {
-                for (int i = 0; i < mNetAttributes.length; i++) {
-                    if(mNetAttributes[i].mPriority == priority) {
-                        mPriorityList[nextPos--] = i;
-                    }
+        String[] naStrings = context.getResources().getStringArray(
+                com.android.internal.R.array.networkAttributes);
+        for (String naString : naStrings) {
+            try {
+                NetworkAttributes n = new NetworkAttributes(naString);
+                if (n.mType > ConnectivityManager.MAX_NETWORK_TYPE) {
+                    Log.e(TAG, "Error in networkAttributes - ignoring attempt to define type " +
+                            n.mType);
+                    continue;
                 }
-                priority++;
+                if (mNetAttributes[n.mType] != null) {
+                    Log.e(TAG, "Error in networkAttributes - ignoring attempt to redefine type " +
+                            n.mType);
+                    continue;
+                }
+                if (mRadioAttributes[n.mRadio] == null) {
+                    Log.e(TAG, "Error in networkAttributes - ignoring attempt to use undefined " +
+                            "radio " + n.mRadio + " in network type " + n.mType);
+                    continue;
+                }
+                mNetAttributes[n.mType] = n;
+                mNetworksDefined++;
+            } catch(Exception e) {
+                // ignore it - leave the entry null
             }
         }
 
-        mNetRequestersPids =
-                new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
-        for (int i=0; i<=ConnectivityManager.MAX_NETWORK_TYPE; i++) {
+        // high priority first
+        mPriorityList = new int[mNetworksDefined];
+        {
+            int insertionPoint = mNetworksDefined-1;
+            int currentLowest = 0;
+            int nextLowest = 0;
+            while (insertionPoint > -1) {
+                for (NetworkAttributes na : mNetAttributes) {
+                    if (na == null) continue;
+                    if (na.mPriority < currentLowest) continue;
+                    if (na.mPriority > currentLowest) {
+                        if (na.mPriority < nextLowest || nextLowest == 0) {
+                            nextLowest = na.mPriority;
+                        }
+                        continue;
+                    }
+                    mPriorityList[insertionPoint--] = na.mType;
+                }
+                currentLowest = nextLowest;
+                nextLowest = 0;
+            }
+        }
+
+        mNetRequestersPids = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
+        for (int i : mPriorityList) {
             mNetRequestersPids[i] = new ArrayList();
         }
 
         mFeatureUsers = new ArrayList();
 
+        mNumDnsEntries = 0;
+
+        mTestMode = SystemProperties.get("cm.test.mode").equals("true")
+                && SystemProperties.get("ro.build.type").equals("eng");
         /*
          * Create the network state trackers for Wi-Fi and mobile
          * data. Maybe this could be done with a factory class,
@@ -260,49 +289,35 @@
          * the number of different network types is not going
          * to change very often.
          */
-        if (DBG) Log.v(TAG, "Starting Wifi Service.");
-        WifiStateTracker wst = new WifiStateTracker(context, mHandler);
-        WifiService wifiService = new WifiService(context, wst);
-        ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
-        mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
+        for (int netType : mPriorityList) {
+            switch (mNetAttributes[netType].mRadio) {
+            case ConnectivityManager.TYPE_WIFI:
+                if (DBG) Log.v(TAG, "Starting Wifi Service.");
+                WifiStateTracker wst = new WifiStateTracker(context, mHandler);
+                WifiService wifiService = new WifiService(context, wst);
+                ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
+                mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
+                wst.startMonitoring();
 
-        mNetTrackers[ConnectivityManager.TYPE_MOBILE] =
-                new MobileDataStateTracker(context, mHandler,
-                ConnectivityManager.TYPE_MOBILE, Phone.APN_TYPE_DEFAULT,
-                "MOBILE");
+                // Constructing this starts it too
+                mWifiWatchdogService = new WifiWatchdogService(context, wst);
+                break;
+            case ConnectivityManager.TYPE_MOBILE:
+                mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler,
+                    netType, mNetAttributes[netType].mName);
+                mNetTrackers[netType].startMonitoring();
+                break;
+            default:
+                Log.e(TAG, "Trying to create a DataStateTracker for an unknown radio type " +
+                        mNetAttributes[netType].mRadio);
+                continue;
+            }
+        }
 
-        mNetTrackers[ConnectivityManager.TYPE_MOBILE_MMS] =
-                new MobileDataStateTracker(context, mHandler,
-                ConnectivityManager.TYPE_MOBILE_MMS, Phone.APN_TYPE_MMS,
-                "MOBILE_MMS");
-
-        mNetTrackers[ConnectivityManager.TYPE_MOBILE_SUPL] =
-                new MobileDataStateTracker(context, mHandler,
-                ConnectivityManager.TYPE_MOBILE_SUPL, Phone.APN_TYPE_SUPL,
-                "MOBILE_SUPL");
-
-        mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] =
-                new MobileDataStateTracker(context, mHandler,
-                ConnectivityManager.TYPE_MOBILE_DUN, Phone.APN_TYPE_DUN,
-                "MOBILE_DUN");
-
-        mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI] =
-                new MobileDataStateTracker(context, mHandler,
-                ConnectivityManager.TYPE_MOBILE_HIPRI, Phone.APN_TYPE_HIPRI,
-                "MOBILE_HIPRI");
-
-        mNumDnsEntries = 0;
-
-        mTestMode = SystemProperties.get("cm.test.mode").equals("true")
-                && SystemProperties.get("ro.build.type").equals("eng");
-
-        for (NetworkStateTracker t : mNetTrackers)
-            t.startMonitoring();
-
-        // Constructing this starts it too
-        mWifiWatchdogService = new WifiWatchdogService(context, wst);
+        mTethering = new Tethering(mContext);
     }
 
+
     /**
      * Sets the preferred network.
      * @param preference the new preference
@@ -310,6 +325,7 @@
     public synchronized void setNetworkPreference(int preference) {
         enforceChangePermission();
         if (ConnectivityManager.isNetworkTypeValid(preference) &&
+                mNetAttributes[preference] != null &&
                 mNetAttributes[preference].isDefault()) {
             if (mNetworkPreference != preference) {
                 persistNetworkPreference(preference);
@@ -357,7 +373,7 @@
             return;
 
         for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
-            if (t != mNetworkPreference &&
+            if (t != mNetworkPreference && mNetTrackers[t] != null &&
                     mNetTrackers[t].getNetworkInfo().isConnected()) {
                 if (DBG) {
                     Log.d(TAG, "tearing down " +
@@ -388,7 +404,7 @@
     public NetworkInfo getActiveNetworkInfo() {
         enforceAccessPermission();
         for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
-            if (!mNetAttributes[type].isDefault()) {
+            if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) {
                 continue;
             }
             NetworkStateTracker t = mNetTrackers[type];
@@ -415,10 +431,10 @@
 
     public NetworkInfo[] getAllNetworkInfo() {
         enforceAccessPermission();
-        NetworkInfo[] result = new NetworkInfo[mNetTrackers.length];
+        NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
         int i = 0;
         for (NetworkStateTracker t : mNetTrackers) {
-            result[i++] = t.getNetworkInfo();
+            if(t != null) result[i++] = t.getNetworkInfo();
         }
         return result;
     }
@@ -427,7 +443,7 @@
         boolean result = true;
         enforceChangePermission();
         for (NetworkStateTracker t : mNetTrackers) {
-            result = t.setRadio(turnOn) && result;
+            if (t != null) result = t.setRadio(turnOn) && result;
         }
         return result;
     }
@@ -504,7 +520,8 @@
                     ": " + feature);
         }
         enforceChangePermission();
-        if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
+        if (!ConnectivityManager.isNetworkTypeValid(networkType) ||
+                mNetAttributes[networkType] == null) {
             return Phone.APN_REQUEST_FAILED;
         }
 
@@ -583,6 +600,8 @@
 
     // javadoc from interface
     public int stopUsingNetworkFeature(int networkType, String feature) {
+        enforceChangePermission();
+
         int pid = getCallingPid();
         int uid = getCallingUid();
 
@@ -623,7 +642,7 @@
             Log.d(TAG, "stopUsingNetworkFeature for net " + networkType +
                     ": " + feature);
         }
-        enforceChangePermission();
+
         if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
             return -1;
         }
@@ -670,7 +689,10 @@
                 }
             }
             tracker =  mNetTrackers[usedNetworkType];
-            if(usedNetworkType != networkType) {
+            if (tracker == null) {
+                return -1;
+            }
+            if (usedNetworkType != networkType) {
                 Integer currentPid = new Integer(pid);
                 mNetRequestersPids[usedNetworkType].remove(currentPid);
                 reassessPidDns(pid, true);
@@ -708,9 +730,10 @@
         }
         NetworkStateTracker tracker = mNetTrackers[networkType];
 
-        if (!tracker.getNetworkInfo().isConnected() || tracker.isTeardownRequested()) {
+        if (tracker == null || !tracker.getNetworkInfo().isConnected() ||
+                tracker.isTeardownRequested()) {
             if (DBG) {
-                Log.d(TAG, "requestRouteToHost on down network (" + networkType + " - dropped");
+                Log.d(TAG, "requestRouteToHost on down network (" + networkType + ") - dropped");
             }
             return false;
         }
@@ -748,7 +771,7 @@
         int numConnectedNets = 0;
 
         for (NetworkStateTracker nt : mNetTrackers) {
-            if (nt.getNetworkInfo().isConnected() &&
+            if (nt != null && nt.getNetworkInfo().isConnected() &&
                     !nt.isTeardownRequested()) {
                 ++numConnectedNets;
             }
@@ -768,6 +791,13 @@
                 "ConnectivityService");
     }
 
+    // TODO Make this a special check when it goes public
+    private void enforceTetherChangePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_NETWORK_STATE,
+                "ConnectivityService");
+    }
+
     /**
      * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
      * network, we ignore it. If it is for the active network, we send out a
@@ -798,6 +828,7 @@
         }
 
         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
         if (info.isFailover()) {
             intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -849,9 +880,8 @@
             int newType = -1;
             int newPriority = -1;
             for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
-                if (checkType == prevNetType) {
-                    continue;
-                }
+                if (checkType == prevNetType) continue;
+                if (mNetAttributes[checkType] == null) continue;
                 if (mNetAttributes[checkType].isDefault()) {
                     /* TODO - if we have multiple nets we could use
                      * we may want to put more thought into which we choose
@@ -860,10 +890,9 @@
                         newType = checkType;
                         break;
                     }
-                    if (mRadioAttributes[mNetAttributes[checkType].mRadio].
-                            mPriority > newPriority) {
+                    if (mNetAttributes[checkType].mPriority > newPriority) {
                         newType = checkType;
-                        newPriority = mRadioAttributes[mNetAttributes[newType].mRadio].mPriority;
+                        newPriority = mNetAttributes[newType].mPriority;
                     }
                 }
             }
@@ -904,6 +933,7 @@
 
     private void sendConnectedBroadcast(NetworkInfo info) {
         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
         if (info.isFailover()) {
             intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -941,6 +971,7 @@
         }
 
         Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
         if (getActiveNetworkInfo() == null) {
             intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
@@ -982,26 +1013,20 @@
 
     private void sendStickyBroadcast(Intent intent) {
         synchronized(this) {
-            if (mSystemReady) {
-                mContext.sendStickyBroadcast(intent);
-            } else {
-                if (mDeferredBroadcasts == null) {
-                    mDeferredBroadcasts = new ArrayList<Intent>();
-                }
-                mDeferredBroadcasts.add(intent);
+            if (!mSystemReady) {
+                mInitialBroadcast = new Intent(intent);
             }
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            mContext.sendStickyBroadcast(intent);
         }
     }
 
     void systemReady() {
         synchronized(this) {
             mSystemReady = true;
-            if (mDeferredBroadcasts != null) {
-                int count = mDeferredBroadcasts.size();
-                for (int i = 0; i < count; i++) {
-                    mContext.sendStickyBroadcast(mDeferredBroadcasts.get(i));
-                }
-                mDeferredBroadcasts = null;
+            if (mInitialBroadcast != null) {
+                mContext.sendStickyBroadcast(mInitialBroadcast);
+                mInitialBroadcast = null;
             }
         }
     }
@@ -1308,7 +1333,7 @@
                     int eventLogParam = (info.getType() & 0x7) |
                             ((info.getDetailedState().ordinal() & 0x3f) << 3) |
                             (info.getSubtype() << 9);
-                    EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED,
+                    EventLog.writeEvent(EventLogTags.CONNECTIVITY_STATE_CHANGED,
                             eventLogParam);
 
                     if (info.getDetailedState() ==
@@ -1357,4 +1382,28 @@
             }
         }
     }
+
+    // javadoc from interface
+    public boolean tether(String iface) {
+        enforceTetherChangePermission();
+        return mTethering.tether(iface);
+    }
+
+    // javadoc from interface
+    public boolean untether(String iface) {
+        enforceTetherChangePermission();
+        return mTethering.untether(iface);
+    }
+
+    // TODO - move iface listing, queries, etc to new module
+    // javadoc from interface
+    public String[] getTetherableIfaces() {
+        enforceAccessPermission();
+        return mTethering.getTetherableIfaces();
+    }
+
+    public String[] getTetheredIfaces() {
+        enforceAccessPermission();
+        return mTethering.getTetheredIfaces();
+    }
 }