[Tether13] Move TetheringManager into framework

Move tethering out of ConnectivityService. All client would
use TetheringManager to talk with TetheringService directly.

Bug: 144320246
Test: -build, flash, boot
      -atest TetheringTests

Change-Id: Ib051bea724a256f9c4572b566e46ae7b9c4abe6e
Merged-In: Ib051bea724a256f9c4572b566e46ae7b9c4abe6e
diff --git a/Android.bp b/Android.bp
index 561166e..aaf1069 100644
--- a/Android.bp
+++ b/Android.bp
@@ -266,6 +266,7 @@
     name: "framework-updatable-sources",
     srcs: [
         ":framework-sdkext-sources",
+        ":framework-tethering-srcs",
         ":updatable-media-srcs",
     ]
 }
@@ -370,6 +371,7 @@
         "ext",
         "unsupportedappusage",
         "updatable_media_stubs",
+        "framework-tethering",
     ],
 
     jarjar_rules: ":framework-jarjar-rules",
@@ -604,10 +606,26 @@
     ],
 }
 
+// keep these files in sync with the package/Tethering/jarjar-rules.txt for the tethering module.
 filegroup {
     name: "framework-tethering-shared-srcs",
     srcs: [
         "core/java/android/util/LocalLog.java",
+        "core/java/com/android/internal/util/BitUtils.java",
+        "core/java/com/android/internal/util/IndentingPrintWriter.java",
+        "core/java/com/android/internal/util/IState.java",
+        "core/java/com/android/internal/util/MessageUtils.java",
+        "core/java/com/android/internal/util/Preconditions.java",
+        "core/java/com/android/internal/util/State.java",
+        "core/java/com/android/internal/util/StateMachine.java",
+    ],
+}
+
+filegroup {
+    name: "framework-tethering-annotations",
+    srcs: [
+        "core/java/android/annotation/NonNull.java",
+        "core/java/android/annotation/SystemApi.java",
     ],
 }
 // Build ext.jar
diff --git a/api/system-current.txt b/api/system-current.txt
index 62c2d79..5cf1f8f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1554,6 +1554,7 @@
     field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
     field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
     field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
+    field public static final String TETHERING_SERVICE = "tethering";
     field public static final String VR_SERVICE = "vrmanager";
     field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
     field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -6642,6 +6643,7 @@
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
     field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
     field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
+    field public static final String TETHER_SUPPORTED = "tether_supported";
     field public static final String THEATER_MODE_ON = "theater_mode_on";
     field public static final String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
     field public static final String WIFI_BADGING_THRESHOLDS = "wifi_badging_thresholds";
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index c56e8d8..82df702 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -111,6 +111,7 @@
 import android.net.NetworkScoreManager;
 import android.net.NetworkWatchlistManager;
 import android.net.TestNetworkManager;
+import android.net.TetheringManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
 import android.net.nsd.INsdManager;
@@ -340,6 +341,17 @@
             }
         });
 
+        registerService(Context.TETHERING_SERVICE, TetheringManager.class,
+                new CachedServiceFetcher<TetheringManager>() {
+            @Override
+            public TetheringManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                IBinder b = ServiceManager.getService(Context.TETHERING_SERVICE);
+                if (b == null) return null;
+
+                return new TetheringManager(ctx, b);
+            }});
+
+
         registerService(Context.IPSEC_SERVICE, IpSecManager.class,
                 new CachedServiceFetcher<IpSecManager>() {
             @Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 39a7115..902c6b9d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3858,6 +3858,15 @@
     public static final String NETWORK_STACK_SERVICE = "network_stack";
 
     /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link ITetheringConnector} IBinder for communicating with the tethering service
+     * @hide
+     * @see TetheringClient
+     */
+    @SystemApi
+    public static final String TETHERING_SERVICE = "tethering";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.net.IpSecManager} for encrypting Sockets or Networks with
      * IPSec.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a6e070e..82cecb6 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -50,6 +50,7 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -57,7 +58,6 @@
 import android.util.Log;
 import android.util.SparseIntArray;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.Protocol;
 
@@ -802,6 +802,7 @@
 
     private INetworkManagementService mNMService;
     private INetworkPolicyManager mNPManager;
+    private TetheringManager mTetheringManager;
 
     /**
      * Tests if a given integer represents a valid network type.
@@ -2340,6 +2341,28 @@
         return getInstanceOrNull();
     }
 
+    private static final int TETHERING_TIMEOUT_MS = 60_000;
+    private final Object mTetheringLock = new Object();
+
+    private TetheringManager getTetheringManager() {
+        synchronized (mTetheringLock) {
+            if (mTetheringManager != null) {
+                return mTetheringManager;
+            }
+            final long before = System.currentTimeMillis();
+            while ((mTetheringManager = (TetheringManager) mContext.getSystemService(
+                    Context.TETHERING_SERVICE)) == null) {
+                if (System.currentTimeMillis() - before > TETHERING_TIMEOUT_MS) {
+                    Log.e(TAG, "Timeout waiting tethering service not ready yet");
+                    throw new IllegalStateException("No tethering service yet");
+                }
+                SystemClock.sleep(100);
+            }
+
+            return mTetheringManager;
+        }
+    }
+
     /**
      * Get the set of tetherable, available interfaces.  This list is limited by
      * device configuration and current interface existence.
@@ -2351,11 +2374,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @UnsupportedAppUsage
     public String[] getTetherableIfaces() {
-        try {
-            return mService.getTetherableIfaces();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().getTetherableIfaces();
     }
 
     /**
@@ -2368,11 +2387,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @UnsupportedAppUsage
     public String[] getTetheredIfaces() {
-        try {
-            return mService.getTetheredIfaces();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().getTetheredIfaces();
     }
 
     /**
@@ -2391,11 +2406,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @UnsupportedAppUsage
     public String[] getTetheringErroredIfaces() {
-        try {
-            return mService.getTetheringErroredIfaces();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().getTetheringErroredIfaces();
     }
 
     /**
@@ -2406,11 +2417,7 @@
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String[] getTetheredDhcpRanges() {
-        try {
-            return mService.getTetheredDhcpRanges();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().getTetheredDhcpRanges();
     }
 
     /**
@@ -2439,13 +2446,7 @@
      */
     @UnsupportedAppUsage
     public int tether(String iface) {
-        try {
-            String pkgName = mContext.getOpPackageName();
-            Log.i(TAG, "tether caller:" + pkgName);
-            return mService.tether(iface, pkgName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().tether(iface);
     }
 
     /**
@@ -2468,13 +2469,7 @@
      */
     @UnsupportedAppUsage
     public int untether(String iface) {
-        try {
-            String pkgName = mContext.getOpPackageName();
-            Log.i(TAG, "untether caller:" + pkgName);
-            return mService.untether(iface, pkgName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().untether(iface);
     }
 
     /**
@@ -2499,16 +2494,7 @@
     @RequiresPermission(anyOf = {android.Manifest.permission.TETHER_PRIVILEGED,
             android.Manifest.permission.WRITE_SETTINGS})
     public boolean isTetheringSupported() {
-        String pkgName = mContext.getOpPackageName();
-        try {
-            return mService.isTetheringSupported(pkgName);
-        } catch (SecurityException e) {
-            // This API is not available to this caller, but for backward-compatibility
-            // this will just return false instead of throwing.
-            return false;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().isTetheringSupported();
     }
 
     /**
@@ -2577,14 +2563,7 @@
             }
         };
 
-        try {
-            String pkgName = mContext.getOpPackageName();
-            Log.i(TAG, "startTethering caller:" + pkgName);
-            mService.startTethering(type, wrappedCallback, showProvisioningUi, pkgName);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception trying to start tethering.", e);
-            wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
-        }
+        getTetheringManager().startTethering(type, wrappedCallback, showProvisioningUi);
     }
 
     /**
@@ -2600,13 +2579,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
     public void stopTethering(int type) {
-        try {
-            String pkgName = mContext.getOpPackageName();
-            Log.i(TAG, "stopTethering caller:" + pkgName);
-            mService.stopTethering(type, pkgName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        getTetheringManager().stopTethering(type);
     }
 
     /**
@@ -2628,10 +2601,6 @@
         public void onUpstreamChanged(@Nullable Network network) {}
     }
 
-    @GuardedBy("mTetheringEventCallbacks")
-    private final ArrayMap<OnTetheringEventCallback, ITetheringEventCallback>
-            mTetheringEventCallbacks = new ArrayMap<>();
-
     /**
      * Start listening to tethering change events. Any new added callback will receive the last
      * tethering status right away. If callback is registered when tethering has no upstream or
@@ -2649,27 +2618,7 @@
             @NonNull final OnTetheringEventCallback callback) {
         Preconditions.checkNotNull(callback, "OnTetheringEventCallback cannot be null.");
 
-        synchronized (mTetheringEventCallbacks) {
-            Preconditions.checkArgument(!mTetheringEventCallbacks.containsKey(callback),
-                    "callback was already registered.");
-            ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() {
-                @Override
-                public void onUpstreamChanged(Network network) throws RemoteException {
-                    Binder.withCleanCallingIdentity(() ->
-                            executor.execute(() -> {
-                                callback.onUpstreamChanged(network);
-                            }));
-                }
-            };
-            try {
-                String pkgName = mContext.getOpPackageName();
-                Log.i(TAG, "registerTetheringUpstreamCallback:" + pkgName);
-                mService.registerTetheringEventCallback(remoteCallback, pkgName);
-                mTetheringEventCallbacks.put(callback, remoteCallback);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
+        getTetheringManager().registerTetheringEventCallback(executor, callback);
     }
 
     /**
@@ -2683,17 +2632,7 @@
     @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
     public void unregisterTetheringEventCallback(
             @NonNull final OnTetheringEventCallback callback) {
-        synchronized (mTetheringEventCallbacks) {
-            ITetheringEventCallback remoteCallback = mTetheringEventCallbacks.remove(callback);
-            Preconditions.checkNotNull(remoteCallback, "callback was not registered.");
-            try {
-                String pkgName = mContext.getOpPackageName();
-                Log.i(TAG, "unregisterTetheringEventCallback:" + pkgName);
-                mService.unregisterTetheringEventCallback(remoteCallback, pkgName);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
+        getTetheringManager().unregisterTetheringEventCallback(callback);
     }
 
 
@@ -2710,11 +2649,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @UnsupportedAppUsage
     public String[] getTetherableUsbRegexs() {
-        try {
-            return mService.getTetherableUsbRegexs();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().getTetherableUsbRegexs();
     }
 
     /**
@@ -2730,11 +2665,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @UnsupportedAppUsage
     public String[] getTetherableWifiRegexs() {
-        try {
-            return mService.getTetherableWifiRegexs();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().getTetherableWifiRegexs();
     }
 
     /**
@@ -2750,11 +2681,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @UnsupportedAppUsage
     public String[] getTetherableBluetoothRegexs() {
-        try {
-            return mService.getTetherableBluetoothRegexs();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().getTetherableBluetoothRegexs();
     }
 
     /**
@@ -2776,13 +2703,7 @@
      */
     @UnsupportedAppUsage
     public int setUsbTethering(boolean enable) {
-        try {
-            String pkgName = mContext.getOpPackageName();
-            Log.i(TAG, "setUsbTethering caller:" + pkgName);
-            return mService.setUsbTethering(enable, pkgName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().setUsbTethering(enable);
     }
 
     /** {@hide} */
@@ -2830,11 +2751,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @UnsupportedAppUsage
     public int getLastTetherError(String iface) {
-        try {
-            return mService.getLastTetherError(iface);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getTetheringManager().getLastTetherError(iface);
     }
 
     /** @hide */
@@ -2900,14 +2817,8 @@
             }
         };
 
-        try {
-            String pkgName = mContext.getOpPackageName();
-            Log.i(TAG, "getLatestTetheringEntitlementResult:" + pkgName);
-            mService.getLatestTetheringEntitlementResult(type, wrappedListener,
-                    showEntitlementUi, pkgName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        getTetheringManager().requestLatestTetheringEntitlementResult(type, wrappedListener,
+                    showEntitlementUi);
     }
 
     /**
@@ -4332,6 +4243,7 @@
     public void factoryReset() {
         try {
             mService.factoryReset();
+            getTetheringManager().stopAllTethering();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 5f662f9..09c02ef 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -19,7 +19,6 @@
 import android.app.PendingIntent;
 import android.net.ConnectionInfo;
 import android.net.LinkProperties;
-import android.net.ITetheringEventCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
@@ -78,41 +77,31 @@
 
     boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
 
-    int tether(String iface, String callerPkg);
-
-    int untether(String iface, String callerPkg);
-
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 29,
+            publicAlternatives = "Use {@code TetheringManager#getLastTetherError} as alternative")
     int getLastTetherError(String iface);
 
-    boolean isTetheringSupported(String callerPkg);
-
-    void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi,
-            String callerPkg);
-
-    void stopTethering(int type, String callerPkg);
-
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 29,
+            publicAlternatives = "Use {@code TetheringManager#getTetherableIfaces} as alternative")
     String[] getTetherableIfaces();
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 29,
+            publicAlternatives = "Use {@code TetheringManager#getTetheredIfaces} as alternative")
     String[] getTetheredIfaces();
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 29,
+            publicAlternatives = "Use {@code TetheringManager#getTetheringErroredIfaces} "
+            + "as Alternative")
     String[] getTetheringErroredIfaces();
 
-    String[] getTetheredDhcpRanges();
-
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 29,
+            publicAlternatives = "Use {@code TetheringManager#getTetherableUsbRegexs} as alternative")
     String[] getTetherableUsbRegexs();
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = 29,
+            publicAlternatives = "Use {@code TetheringManager#getTetherableWifiRegexs} as alternative")
     String[] getTetherableWifiRegexs();
 
-    String[] getTetherableBluetoothRegexs();
-
-    int setUsbTethering(boolean enable, String callerPkg);
-
     @UnsupportedAppUsage(maxTargetSdk = 28)
     void reportInetCondition(int networkType, int percentage);
 
@@ -217,11 +206,5 @@
     boolean isCallerCurrentAlwaysOnVpnApp();
     boolean isCallerCurrentAlwaysOnVpnLockdownApp();
 
-    void getLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
-            boolean showEntitlementUi, String callerPkg);
-
-    void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
-    void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
-
     IBinder startOrGetTestNetworkService();
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index da4bd27..e348b5f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10402,35 +10402,37 @@
          */
         public static final String SMS_SHORT_CODE_RULE = "sms_short_code_rule";
 
-       /**
-        * Used to select TCP's default initial receiver window size in segments - defaults to a build config value
-        * @hide
-        */
-       public static final String TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd";
+        /**
+         * Used to select TCP's default initial receiver window size in segments - defaults to a
+         * build config value.
+         * @hide
+         */
+        public static final String TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd";
 
-       /**
-        * Used to disable Tethering on a device - defaults to true
-        * @hide
-        */
-       public static final String TETHER_SUPPORTED = "tether_supported";
+        /**
+         * Used to disable Tethering on a device - defaults to true.
+         * @hide
+         */
+        @SystemApi
+        public static final String TETHER_SUPPORTED = "tether_supported";
 
-       /**
-        * Used to require DUN APN on the device or not - defaults to a build config value
-        * which defaults to false
-        * @hide
-        */
-       public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
+        /**
+         * Used to require DUN APN on the device or not - defaults to a build config value
+         * which defaults to false.
+         * @hide
+         */
+        public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
 
-       /**
-        * Used to hold a gservices-provisioned apn value for DUN.  If set, or the
-        * corresponding build config values are set it will override the APN DB
-        * values.
-        * Consists of a comma seperated list of strings:
-        * "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
-        * note that empty fields can be omitted: "name,apn,,,,,,,,,310,260,,DUN"
-        * @hide
-        */
-       public static final String TETHER_DUN_APN = "tether_dun_apn";
+        /**
+         * Used to hold a gservices-provisioned apn value for DUN.  If set, or the
+         * corresponding build config values are set it will override the APN DB
+         * values.
+         * Consists of a comma separated list of strings:
+         * "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+         * note that empty fields can be omitted: "name,apn,,,,,,,,,310,260,,DUN"
+         * @hide
+         */
+        public static final String TETHER_DUN_APN = "tether_dun_apn";
 
         /**
          * Used to disable trying to talk to any available tethering offload HAL.
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index e24f26c1..d282744 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -57,4 +57,7 @@
   <hidden-api-whitelisted-app package="com.android.terminal" />
   <hidden-api-whitelisted-app package="com.android.wallpaper" />
   <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
+  <!-- STOPSHIP: Remove this when fixing all @hide usage for tethering.-->
+  <hidden-api-whitelisted-app package="com.android.networkstack.tethering" />
+  <hidden-api-whitelisted-app package="com.android.networkstack" />
 </config>
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 3c953b3..08552cb 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -29,9 +29,11 @@
         "netlink-client",
         "networkstack-aidl-interfaces-unstable-java",
         "android.hardware.tetheroffload.control-V1.0-java",
-        "tethering-client",
     ],
-    libs: ["unsupportedappusage"],
+    libs: [
+        "framework-tethering",
+    ],
+
     manifest: "AndroidManifestBase.xml",
 }
 
@@ -90,6 +92,10 @@
     resource_dirs: [
         "res",
     ],
+    libs: [
+        "framework-tethering",
+    ],
+    jarjar_rules: "jarjar-rules.txt",
     optimize: {
         proguard_flags_files: ["proguard.flags"],
     },
@@ -104,7 +110,6 @@
     manifest: "AndroidManifest_InProcess.xml",
     // InProcessTethering is a replacement for Tethering
     overrides: ["Tethering"],
-    // TODO: use PlatformNetworkPermissionConfig.
 }
 
 // Updatable tethering packaged as an application
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index 8ba05df..87a8c3f 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -21,6 +21,21 @@
           android:sharedUserId="android.uid.networkstack">
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
 
+    <!-- Permissions must be defined here, and not in the base manifest, as the tethering
+         running in the system server process does not need any permission, and having
+         privileged permissions added would cause crashes on startup unless they are also
+         added to the privileged permissions whitelist for that package. -->
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.MANAGE_USB" />
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
     <application
         android:process="com.android.networkstack.process"
         android:extractNativeLibs="false"
diff --git a/packages/Tethering/AndroidManifest_InProcess.xml b/packages/Tethering/AndroidManifest_InProcess.xml
index 029b6c3..02ea551 100644
--- a/packages/Tethering/AndroidManifest_InProcess.xml
+++ b/packages/Tethering/AndroidManifest_InProcess.xml
@@ -22,11 +22,9 @@
           android:process="system">
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
     <application>
-        <!-- TODO: Using MAINLINE_NETWORK_STACK instead of NETWORK_STACK when tethering run in the
-                   same process with networkstack -->
         <service android:name="com.android.server.connectivity.tethering.TetheringService"
                  android:process="system"
-                 android:permission="android.permission.NETWORK_STACK">
+                 android:permission="android.permission.MAINLINE_NETWORK_STACK">
             <intent-filter>
                 <action android:name="android.net.ITetheringConnector.InProcess"/>
             </intent-filter>
diff --git a/packages/Tethering/CleanSpec.mk b/packages/Tethering/CleanSpec.mk
index 70db351..30bdd58 100644
--- a/packages/Tethering/CleanSpec.mk
+++ b/packages/Tethering/CleanSpec.mk
@@ -46,6 +46,12 @@
 
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Tethering)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/InProcessTethering)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/InProcessTethering*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/InProcessTethering*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other/system/priv-app/InProcessTethering)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/apex/com.android.tethering)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/apex/com.android.tethering.apex)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/ETC/com.android.tethering*)
 
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index af6af93..94ef11c 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -16,6 +16,7 @@
 
 apex {
     name: "com.android.tethering",
+    java_libs: ["framework-tethering"],
     apps: ["Tethering"],
     manifest: "manifest.json",
     key: "com.android.tethering.key",
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index adc5a72..5785707 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -12,7 +12,6 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-//
 
 // AIDL interfaces between the core system and the tethering mainline module.
 aidl_interface {
@@ -20,10 +19,7 @@
     local_include_dir: "src",
     include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
     srcs: [
-        "src/android/net/ITetherInternalCallback.aidl",
-        "src/android/net/ITetheringConnector.aidl",
-        "src/android/net/TetheringConfigurationParcel.aidl",
-        "src/android/net/TetherStatesParcel.aidl",
+        "src/android/net/*.aidl",
     ],
     backend: {
         ndk: {
@@ -36,16 +32,32 @@
 }
 
 java_library {
-    name: "tethering-client",
+    name: "framework-tethering",
     sdk_version: "system_current",
+    srcs: [
+        "src/android/net/TetheringManager.java",
+        ":framework-tethering-annotations",
+    ],
     static_libs: [
         "tethering-aidl-interfaces-java",
     ],
+    jarjar_rules: "jarjar-rules.txt",
+    installable: true,
+
+    libs: [
+        "android_system_stubs_current",
+    ],
 }
 
-// This is temporary file group which would be removed after TetheringManager is built
-// into tethering-client. Will be done by aosp/1156906.
 filegroup {
-    name: "tethering-manager",
-    srcs: ["src/android/net/TetheringManager.java"],
+    name: "framework-tethering-srcs",
+    srcs: [
+        "src/android/net/TetheringManager.java",
+        "src/android/net/IIntResultListener.aidl",
+        "src/android/net/ITetheringEventCallback.aidl",
+        "src/android/net/ITetheringConnector.aidl",
+        "src/android/net/TetheringConfigurationParcel.aidl",
+        "src/android/net/TetherStatesParcel.aidl",
+    ],
+    path: "src"
 }
diff --git a/packages/Tethering/common/TetheringLib/jarjar-rules.txt b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
new file mode 100644
index 0000000..35e0f88
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
@@ -0,0 +1 @@
+rule android.annotation.** com.android.networkstack.tethering.annotation.@1
diff --git a/core/java/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl
similarity index 77%
rename from core/java/android/net/ITetheringEventCallback.aidl
rename to packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl
index d502088..c3d66ee 100644
--- a/core/java/android/net/ITetheringEventCallback.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl
@@ -16,13 +16,10 @@
 
 package android.net;
 
-import android.net.Network;
-
 /**
- * Callback class for receiving tethering changed events
- * @hide
+ * Listener interface allowing objects to listen to various module event.
+ * {@hide}
  */
-oneway interface ITetheringEventCallback
-{
-    void onUpstreamChanged(in Network network);
+oneway interface IIntResultListener {
+    void onResult(int resultCode);
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index bfe502f..d30c399 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -15,23 +15,31 @@
  */
 package android.net;
 
-import android.net.ITetherInternalCallback;
+import android.net.IIntResultListener;
+import android.net.ITetheringEventCallback;
 import android.os.ResultReceiver;
 
 /** @hide */
 oneway interface ITetheringConnector {
-    void tether(String iface);
+    void tether(String iface, String callerPkg, IIntResultListener receiver);
 
-    void untether(String iface);
+    void untether(String iface, String callerPkg, IIntResultListener receiver);
 
-    void setUsbTethering(boolean enable);
+    void setUsbTethering(boolean enable, String callerPkg, IIntResultListener receiver);
 
-    void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi);
+    void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi,
+            String callerPkg);
 
-    void stopTethering(int type);
+    void stopTethering(int type, String callerPkg, IIntResultListener receiver);
 
     void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
-            boolean showEntitlementUi);
+            boolean showEntitlementUi, String callerPkg);
 
-    void registerTetherInternalCallback(ITetherInternalCallback callback);
+    void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
+
+    void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
+
+    void isTetheringSupported(String callerPkg, IIntResultListener receiver);
+
+    void stopAllTethering(String callerPkg, IIntResultListener receiver);
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
similarity index 83%
rename from packages/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl
rename to packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
index abb00e8..2836195 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
@@ -21,14 +21,15 @@
 import android.net.TetherStatesParcel;
 
 /**
- * Callback class for receiving tethering changed events
+ * Callback class for receiving tethering changed events.
  * @hide
  */
-oneway interface ITetherInternalCallback
+oneway interface ITetheringEventCallback
 {
+    void onCallbackStarted(in Network network, in TetheringConfigurationParcel config,
+            in TetherStatesParcel states);
+    void onCallbackStopped(int errorCode);
     void onUpstreamChanged(in Network network);
     void onConfigurationChanged(in TetheringConfigurationParcel config);
     void onTetherStatesChanged(in TetherStatesParcel states);
-    void onCallbackCreated(in Network network, in TetheringConfigurationParcel config,
-            in TetherStatesParcel states);
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 7fb286b..a49ab85 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,95 +15,142 @@
  */
 package android.net;
 
-import static android.Manifest.permission.NETWORK_STACK;
 import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.util.SharedLog;
+import android.content.Context;
+import android.net.ConnectivityManager.OnTetheringEventCallback;
 import android.os.ConditionVariable;
 import android.os.IBinder;
-import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.util.Slog;
+import android.util.ArrayMap;
+import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.PrintWriter;
-import java.util.StringJoiner;
+import java.util.concurrent.Executor;
 
 /**
- * Service used to communicate with the tethering, which is running in a separate module.
+ * This class provides the APIs to control the tethering service.
+ * <p> The primary responsibilities of this class are to provide the APIs for applications to
+ * start tethering, stop tethering, query configuration and query status.
+ *
  * @hide
  */
+// TODO: make it @SystemApi
 public class TetheringManager {
     private static final String TAG = TetheringManager.class.getSimpleName();
+    private static final int DEFAULT_TIMEOUT_MS = 60_000;
 
     private static TetheringManager sInstance;
 
-    @Nullable
-    private ITetheringConnector mConnector;
-    private TetherInternalCallback mCallback;
-    private Network mTetherUpstream;
+    private final ITetheringConnector mConnector;
+    private final TetheringCallbackInternal mCallback;
+    private final Context mContext;
+    private final ArrayMap<OnTetheringEventCallback, ITetheringEventCallback>
+            mTetheringEventCallbacks = new ArrayMap<>();
+
     private TetheringConfigurationParcel mTetheringConfiguration;
     private TetherStatesParcel mTetherStatesParcel;
 
-    private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
-            new RemoteCallbackList<>();
-    @GuardedBy("mLog")
-    private final SharedLog mLog = new SharedLog(TAG);
-
-    private TetheringManager() { }
+    public static final int TETHER_ERROR_NO_ERROR           = 0;
+    public static final int TETHER_ERROR_UNKNOWN_IFACE      = 1;
+    public static final int TETHER_ERROR_SERVICE_UNAVAIL    = 2;
+    public static final int TETHER_ERROR_UNSUPPORTED        = 3;
+    public static final int TETHER_ERROR_UNAVAIL_IFACE      = 4;
+    public static final int TETHER_ERROR_MASTER_ERROR       = 5;
+    public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
+    public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
+    public static final int TETHER_ERROR_ENABLE_NAT_ERROR     = 8;
+    public static final int TETHER_ERROR_DISABLE_NAT_ERROR    = 9;
+    public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
+    public static final int TETHER_ERROR_PROVISION_FAILED     = 11;
+    public static final int TETHER_ERROR_DHCPSERVER_ERROR     = 12;
+    public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN  = 13;
+    public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14;
+    public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15;
 
     /**
-     * Get the TetheringManager singleton instance.
+     * Create a TetheringManager object for interacting with the tethering service.
      */
-    public static synchronized TetheringManager getInstance() {
-        if (sInstance == null) {
-            sInstance = new TetheringManager();
-        }
-        return sInstance;
-    }
+    public TetheringManager(@NonNull final Context context, @NonNull final IBinder service) {
+        mContext = context;
+        mConnector = ITetheringConnector.Stub.asInterface(service);
+        mCallback = new TetheringCallbackInternal();
 
-    private class TetheringConnection implements
-            ConnectivityModuleConnector.ModuleServiceCallback {
-        @Override
-        public void onModuleServiceConnected(@NonNull IBinder service) {
-            logi("Tethering service connected");
-            registerTetheringService(service);
-        }
-    }
-
-    private void registerTetheringService(@NonNull IBinder service) {
-        final ITetheringConnector connector = ITetheringConnector.Stub.asInterface(service);
-
-        log("Tethering service registered");
-
-        // Currently TetheringManager instance is only used by ConnectivityService and mConnector
-        // only expect to assign once when system server start and bind tethering service.
-        // STOPSHIP: Change mConnector to final before TetheringManager put into boot classpath.
-        mConnector = connector;
-        mCallback = new TetherInternalCallback();
+        final String pkgName = mContext.getOpPackageName();
+        Log.i(TAG, "registerTetheringEventCallback:" + pkgName);
         try {
-            mConnector.registerTetherInternalCallback(mCallback);
+            mConnector.registerTetheringEventCallback(mCallback, pkgName);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw new IllegalStateException(e);
         }
     }
 
-    private class TetherInternalCallback extends ITetherInternalCallback.Stub {
-        private final ConditionVariable mWaitForCallback = new ConditionVariable(false);
-        private static final int EVENT_CALLBACK_TIMEOUT_MS = 60_000;
+    private interface RequestHelper {
+        void runRequest(IIntResultListener listener);
+    }
+
+    private class RequestDispatcher {
+        private final ConditionVariable mWaiting;
+        public int mRemoteResult;
+
+        private final IIntResultListener mListener = new IIntResultListener.Stub() {
+                @Override
+                public void onResult(final int resultCode) {
+                    mRemoteResult = resultCode;
+                    mWaiting.open();
+                }
+        };
+
+        RequestDispatcher() {
+            mWaiting = new ConditionVariable();
+        }
+
+        int waitForResult(final RequestHelper request) {
+            request.runRequest(mListener);
+            if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) {
+                throw new IllegalStateException("Callback timeout");
+            }
+
+            throwIfPermissionFailure(mRemoteResult);
+
+            return mRemoteResult;
+        }
+    }
+
+    private void throwIfPermissionFailure(final int errorCode) {
+        switch (errorCode) {
+            case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:
+                throw new SecurityException("No android.permission.TETHER_PRIVILEGED"
+                        + " or android.permission.WRITE_SETTINGS permission");
+            case TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION:
+                throw new SecurityException(
+                        "No android.permission.ACCESS_NETWORK_STATE permission");
+        }
+    }
+
+    private class TetheringCallbackInternal extends ITetheringEventCallback.Stub {
+        private int mError = TETHER_ERROR_NO_ERROR;
+        private final ConditionVariable mWaitForCallback = new ConditionVariable();
 
         @Override
-        public void onUpstreamChanged(Network network) {
-            mTetherUpstream = network;
-            reportUpstreamChanged(network);
+        public void onCallbackStarted(Network network, TetheringConfigurationParcel config,
+                TetherStatesParcel states) {
+            mTetheringConfiguration = config;
+            mTetherStatesParcel = states;
+            mWaitForCallback.open();
         }
 
         @Override
+        public void onCallbackStopped(int errorCode) {
+            mError = errorCode;
+            mWaitForCallback.open();
+        }
+
+        @Override
+        public void onUpstreamChanged(Network network) { }
+
+        @Override
         public void onConfigurationChanged(TetheringConfigurationParcel config) {
             mTetheringConfiguration = config;
         }
@@ -113,49 +160,10 @@
             mTetherStatesParcel = states;
         }
 
-        @Override
-        public void onCallbackCreated(Network network, TetheringConfigurationParcel config,
-                TetherStatesParcel states) {
-            mTetherUpstream = network;
-            mTetheringConfiguration = config;
-            mTetherStatesParcel = states;
-            mWaitForCallback.open();
+        public void waitForStarted() {
+            mWaitForCallback.block(DEFAULT_TIMEOUT_MS);
+            throwIfPermissionFailure(mError);
         }
-
-        boolean awaitCallbackCreation() {
-            return mWaitForCallback.block(EVENT_CALLBACK_TIMEOUT_MS);
-        }
-    }
-
-    private void reportUpstreamChanged(Network network) {
-        final int length = mTetheringEventCallbacks.beginBroadcast();
-        try {
-            for (int i = 0; i < length; i++) {
-                try {
-                    mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network);
-                } catch (RemoteException e) {
-                    // Not really very much to do here.
-                }
-            }
-        } finally {
-            mTetheringEventCallbacks.finishBroadcast();
-        }
-    }
-
-    /**
-     * Start the tethering service. Should be called only once on device startup.
-     *
-     * <p>This method will start the tethering service either in the network stack process,
-     * or inside the system server on devices that do not support the tethering module.
-     *
-     * {@hide}
-     */
-    public void start() {
-        // Using MAINLINE_NETWORK_STACK permission after cutting off the dpendency of system server.
-        ConnectivityModuleConnector.getInstance().startModuleService(
-                ITetheringConnector.class.getName(), NETWORK_STACK,
-                new TetheringConnection());
-        log("Tethering service start requested");
     }
 
     /**
@@ -165,108 +173,110 @@
      * IP network interface is available, dhcp will still run and traffic will be
      * allowed between the tethered devices and this device, though upstream net
      * access will of course fail until an upstream network interface becomes
-     * active. Note: return value do not have any meaning. It is better to use
-     * #getTetherableIfaces() to ensure corresponding interface is available for
-     * tethering before calling #tether().
+     * active.
      *
-     * @deprecated The only usages should be in PanService and Wifi P2P which
-     * need direct access.
+     * @deprecated The only usages is PanService. It uses this for legacy reasons
+     * and will migrate away as soon as possible.
      *
-     * {@hide}
+     * @param iface the interface name to tether.
+     * @return error a {@code TETHER_ERROR} value indicating success or failure type
      */
     @Deprecated
-    public int tether(@NonNull String iface) {
-        if (mConnector == null) {
-            Slog.wtf(TAG, "Tethering not ready yet");
-            return TETHER_ERROR_SERVICE_UNAVAIL;
-        }
-        try {
-            mConnector.tether(iface);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-        return TETHER_ERROR_NO_ERROR;
+    public int tether(@NonNull final String iface) {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "tether caller:" + callerPkg);
+        final RequestDispatcher dispatcher = new RequestDispatcher();
+
+        return dispatcher.waitForResult(listener -> {
+            try {
+                mConnector.tether(iface, callerPkg, listener);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+        });
     }
 
     /**
      * Stop tethering the named interface.
      *
-     * @deprecated
-     * {@hide}
+     * @deprecated The only usages is PanService. It uses this for legacy reasons
+     * and will migrate away as soon as possible.
      */
     @Deprecated
-    public int untether(@NonNull String iface) {
-        if (mConnector == null) {
-            Slog.wtf(TAG, "Tethering not ready yet");
-            return TETHER_ERROR_SERVICE_UNAVAIL;
-        }
-        try {
-            mConnector.untether(iface);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-        return TETHER_ERROR_NO_ERROR;
+    public int untether(@NonNull final String iface) {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "untether caller:" + callerPkg);
+
+        final RequestDispatcher dispatcher = new RequestDispatcher();
+
+        return dispatcher.waitForResult(listener -> {
+            try {
+                mConnector.untether(iface, callerPkg, listener);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+        });
     }
 
     /**
-     * Attempt to both alter the mode of USB and Tethering of USB. WARNING: New client should not
-     * use this API anymore. All clients should use #startTethering or #stopTethering which
-     * encapsulate proper entitlement logic. If the API is used and an entitlement check is needed,
-     * downstream USB tethering will be enabled but will not have any upstream.
+     * Attempt to both alter the mode of USB and Tethering of USB.
      *
-     * @deprecated
-     * {@hide}
+     * @deprecated New client should not use this API anymore. All clients should use
+     * #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is
+     * used and an entitlement check is needed, downstream USB tethering will be enabled but will
+     * not have any upstream.
      */
     @Deprecated
-    public int setUsbTethering(boolean enable) {
-        if (mConnector == null) {
-            Slog.wtf(TAG, "Tethering not ready yet");
-            return TETHER_ERROR_SERVICE_UNAVAIL;
-        }
-        try {
-            mConnector.setUsbTethering(enable);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-        return TETHER_ERROR_NO_ERROR;
+    public int setUsbTethering(final boolean enable) {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "setUsbTethering caller:" + callerPkg);
+
+        final RequestDispatcher dispatcher = new RequestDispatcher();
+
+        return dispatcher.waitForResult(listener -> {
+            try {
+                mConnector.setUsbTethering(enable, callerPkg, listener);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+        });
     }
 
     /**
      * Starts tethering and runs tether provisioning for the given type if needed. If provisioning
      * fails, stopTethering will be called automatically.
      *
-     * {@hide}
      */
     // TODO: improve the usage of ResultReceiver, b/145096122
-    public void startTethering(int type, @NonNull ResultReceiver receiver,
-            boolean showProvisioningUi) {
-        if (mConnector == null) {
-            Slog.wtf(TAG, "Tethering not ready yet");
-            return;
-        }
+    public void startTethering(final int type, @NonNull final ResultReceiver receiver,
+            final boolean showProvisioningUi) {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "startTethering caller:" + callerPkg);
+
         try {
-            mConnector.startTethering(type, receiver, showProvisioningUi);
+            mConnector.startTethering(type, receiver, showProvisioningUi, callerPkg);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw new IllegalStateException(e);
         }
     }
 
     /**
      * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
      * applicable.
-     *
-     * {@hide}
      */
-    public void stopTethering(int type) {
-        if (mConnector == null) {
-            Slog.wtf(TAG, "Tethering not ready yet");
-            return;
-        }
-        try {
-            mConnector.stopTethering(type);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
+    public void stopTethering(final int type) {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "stopTethering caller:" + callerPkg);
+
+        final RequestDispatcher dispatcher = new RequestDispatcher();
+
+        dispatcher.waitForResult(listener -> {
+            try {
+                mConnector.stopTethering(type, callerPkg, listener);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+        });
     }
 
     /**
@@ -277,47 +287,109 @@
      * if it's really needed.
      */
     // TODO: improve the usage of ResultReceiver, b/145096122
-    public void requestLatestTetheringEntitlementResult(int type, @NonNull ResultReceiver receiver,
-            boolean showEntitlementUi) {
-        if (mConnector == null) {
-            Slog.wtf(TAG, "Tethering not ready yet");
-            return;
-        }
+    public void requestLatestTetheringEntitlementResult(final int type,
+            @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg);
+
         try {
-            mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
+            mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi,
+                    callerPkg);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw new IllegalStateException(e);
         }
     }
 
     /**
-     * Register tethering event callback.
+     * Start listening to tethering change events. Any new added callback will receive the last
+     * tethering status right away. If callback is registered,
+     * {@link OnTetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering
+     * has no upstream or disabled, the argument of callback will be null. The same callback object
+     * cannot be registered twice.
      *
-     * {@hide}
+     * @param executor the executor on which callback will be invoked.
+     * @param callback the callback to be called when tethering has change events.
      */
-    public void registerTetheringEventCallback(@NonNull ITetheringEventCallback callback) {
-        mTetheringEventCallbacks.register(callback);
+    public void registerTetheringEventCallback(@NonNull Executor executor,
+            @NonNull OnTetheringEventCallback callback) {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg);
+
+        synchronized (mTetheringEventCallbacks) {
+            if (!mTetheringEventCallbacks.containsKey(callback)) {
+                throw new IllegalArgumentException("callback was already registered.");
+            }
+            final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() {
+                @Override
+                public void onUpstreamChanged(Network network) throws RemoteException {
+                    executor.execute(() -> {
+                        callback.onUpstreamChanged(network);
+                    });
+                }
+
+                @Override
+                public void onCallbackStarted(Network network, TetheringConfigurationParcel config,
+                        TetherStatesParcel states) {
+                    executor.execute(() -> {
+                        callback.onUpstreamChanged(network);
+                    });
+                }
+
+                @Override
+                public void onCallbackStopped(int errorCode) {
+                    executor.execute(() -> {
+                        throwIfPermissionFailure(errorCode);
+                    });
+                }
+
+                @Override
+                public void onConfigurationChanged(TetheringConfigurationParcel config) { }
+
+                @Override
+                public void onTetherStatesChanged(TetherStatesParcel states) { }
+            };
+            try {
+                mConnector.registerTetheringEventCallback(remoteCallback, callerPkg);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+            mTetheringEventCallbacks.put(callback, remoteCallback);
+        }
     }
 
     /**
-     * Unregister tethering event callback.
+     * Remove tethering event callback previously registered with
+     * {@link #registerTetheringEventCallback}.
      *
-     * {@hide}
+     * @param callback previously registered callback.
      */
-    public void unregisterTetheringEventCallback(@NonNull ITetheringEventCallback callback) {
-        mTetheringEventCallbacks.unregister(callback);
+    public void unregisterTetheringEventCallback(@NonNull final OnTetheringEventCallback callback) {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg);
+
+        synchronized (mTetheringEventCallbacks) {
+            ITetheringEventCallback remoteCallback = mTetheringEventCallbacks.remove(callback);
+            if (remoteCallback == null) {
+                throw new IllegalArgumentException("callback was not registered.");
+            }
+            try {
+                mConnector.unregisterTetheringEventCallback(remoteCallback, callerPkg);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+        }
     }
 
     /**
      * Get a more detailed error code after a Tethering or Untethering
      * request asynchronously failed.
      *
-     * {@hide}
+     * @param iface The name of the interface of interest
+     * @return error The error code of the last error tethering or untethering the named
+     *               interface
      */
-    public int getLastTetherError(@NonNull String iface) {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
+    public int getLastTetherError(@NonNull final String iface) {
+        mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
 
         int i = 0;
@@ -334,12 +406,11 @@
      * USB network interfaces.  If USB tethering is not supported by the
      * device, this list should be empty.
      *
-     * {@hide}
+     * @return an array of 0 or more regular expression Strings defining
+     *        what interfaces are considered tetherable usb interfaces.
      */
     public @NonNull String[] getTetherableUsbRegexs() {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
+        mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableUsbRegexs;
     }
 
@@ -348,12 +419,11 @@
      * Wifi network interfaces.  If Wifi tethering is not supported by the
      * device, this list should be empty.
      *
-     * {@hide}
+     * @return an array of 0 or more regular expression Strings defining
+     *        what interfaces are considered tetherable wifi interfaces.
      */
     public @NonNull String[] getTetherableWifiRegexs() {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
+        mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableWifiRegexs;
     }
 
@@ -362,12 +432,11 @@
      * Bluetooth network interfaces.  If Bluetooth tethering is not supported by the
      * device, this list should be empty.
      *
-     * {@hide}
+     * @return an array of 0 or more regular expression Strings defining
+     *        what interfaces are considered tetherable bluetooth interfaces.
      */
     public @NonNull String[] getTetherableBluetoothRegexs() {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
+        mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableBluetoothRegexs;
     }
 
@@ -375,40 +444,42 @@
      * Get the set of tetherable, available interfaces.  This list is limited by
      * device configuration and current interface existence.
      *
-     * {@hide}
+     * @return an array of 0 or more Strings of tetherable interface names.
      */
     public @NonNull String[] getTetherableIfaces() {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
+        mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
+
         return mTetherStatesParcel.availableList;
     }
 
     /**
      * Get the set of tethered interfaces.
      *
-     * {@hide}
+     * @return an array of 0 or more String of currently tethered interface names.
      */
     public @NonNull String[] getTetheredIfaces() {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
+        mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
+
         return mTetherStatesParcel.tetheredList;
     }
 
     /**
      * Get the set of interface names which attempted to tether but
-     * failed.
+     * failed.  Re-attempting to tether may cause them to reset to the Tethered
+     * state.  Alternatively, causing the interface to be destroyed and recreated
+     * may cause them to reset to the available state.
+     * {@link ConnectivityManager#getLastTetherError} can be used to get more
+     * information on the cause of the errors.
      *
-     * {@hide}
+     * @return an array of 0 or more String indicating the interface names
+     *        which failed to tether.
      */
     public @NonNull String[] getTetheringErroredIfaces() {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
+        mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
+
         return mTetherStatesParcel.erroredIfaceList;
     }
 
@@ -416,123 +487,49 @@
      * Get the set of tethered dhcp ranges.
      *
      * @deprecated This API just return the default value which is not used in DhcpServer.
-     * {@hide}
      */
     @Deprecated
     public @NonNull String[] getTetheredDhcpRanges() {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
+        mCallback.waitForStarted();
         return mTetheringConfiguration.legacyDhcpRanges;
     }
 
     /**
-     * Check if the device allows for tethering.
+     * Check if the device allows for tethering.  It may be disabled via
+     * {@code ro.tether.denied} system property, Settings.TETHER_SUPPORTED or
+     * due to device configuration.
      *
-     * {@hide}
+     * @return a boolean - {@code true} indicating Tethering is supported.
      */
-    public boolean hasTetherableConfiguration() {
-        if (!mCallback.awaitCallbackCreation()) {
-            throw new NullPointerException("callback was not ready yet");
-        }
-        final boolean hasDownstreamConfiguration =
-                (mTetheringConfiguration.tetherableUsbRegexs.length != 0)
-                || (mTetheringConfiguration.tetherableWifiRegexs.length != 0)
-                || (mTetheringConfiguration.tetherableBluetoothRegexs.length != 0);
-        final boolean hasUpstreamConfiguration =
-                (mTetheringConfiguration.preferredUpstreamIfaceTypes.length != 0)
-                || mTetheringConfiguration.chooseUpstreamAutomatically;
+    public boolean isTetheringSupported() {
+        final String callerPkg = mContext.getOpPackageName();
 
-        return hasDownstreamConfiguration && hasUpstreamConfiguration;
+        final RequestDispatcher dispatcher = new RequestDispatcher();
+        final int ret = dispatcher.waitForResult(listener -> {
+            try {
+                mConnector.isTetheringSupported(callerPkg, listener);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+        });
+
+        return ret == TETHER_ERROR_NO_ERROR;
     }
 
     /**
-     * Log a message in the local log.
+     * Stop all active tethering.
      */
-    private void log(@NonNull String message) {
-        synchronized (mLog) {
-            mLog.log(message);
-        }
-    }
+    public void stopAllTethering() {
+        final String callerPkg = mContext.getOpPackageName();
+        Log.i(TAG, "stopAllTethering caller:" + callerPkg);
 
-    /**
-     * Log a condition that should never happen.
-     */
-    private void logWtf(@NonNull String message, @Nullable Throwable e) {
-        Slog.wtf(TAG, message);
-        synchronized (mLog) {
-            mLog.e(message, e);
-        }
-    }
-
-    /**
-     * Log a ERROR level message in the local and system logs.
-     */
-    private void loge(@NonNull String message, @Nullable Throwable e) {
-        synchronized (mLog) {
-            mLog.e(message, e);
-        }
-    }
-
-    /**
-     * Log a INFO level message in the local and system logs.
-     */
-    private void logi(@NonNull String message) {
-        synchronized (mLog) {
-            mLog.i(message);
-        }
-    }
-
-    /**
-     * Dump TetheringManager logs to the specified {@link PrintWriter}.
-     */
-    public void dump(@NonNull PrintWriter pw) {
-        // dump is thread-safe on SharedLog
-        mLog.dump(null, pw, null);
-
-        pw.print("subId: ");
-        pw.println(mTetheringConfiguration.subId);
-
-        dumpStringArray(pw, "tetherableUsbRegexs",
-                mTetheringConfiguration.tetherableUsbRegexs);
-        dumpStringArray(pw, "tetherableWifiRegexs",
-                mTetheringConfiguration.tetherableWifiRegexs);
-        dumpStringArray(pw, "tetherableBluetoothRegexs",
-                mTetheringConfiguration.tetherableBluetoothRegexs);
-
-        pw.print("isDunRequired: ");
-        pw.println(mTetheringConfiguration.isDunRequired);
-
-        pw.print("chooseUpstreamAutomatically: ");
-        pw.println(mTetheringConfiguration.chooseUpstreamAutomatically);
-
-        dumpStringArray(pw, "legacyDhcpRanges", mTetheringConfiguration.legacyDhcpRanges);
-        dumpStringArray(pw, "defaultIPv4DNS", mTetheringConfiguration.defaultIPv4DNS);
-
-        dumpStringArray(pw, "provisioningApp", mTetheringConfiguration.provisioningApp);
-        pw.print("provisioningAppNoUi: ");
-        pw.println(mTetheringConfiguration.provisioningAppNoUi);
-
-        pw.print("enableLegacyDhcpServer: ");
-        pw.println(mTetheringConfiguration.enableLegacyDhcpServer);
-
-        pw.println();
-    }
-
-    private static void dumpStringArray(@NonNull PrintWriter pw, @NonNull String label,
-            @Nullable String[] values) {
-        pw.print(label);
-        pw.print(": ");
-
-        if (values != null) {
-            final StringJoiner sj = new StringJoiner(", ", "[", "]");
-            for (String value : values) sj.add(value);
-
-            pw.print(sj.toString());
-        } else {
-            pw.print("null");
-        }
-
-        pw.println();
+        final RequestDispatcher dispatcher = new RequestDispatcher();
+        dispatcher.waitForResult(listener -> {
+            try {
+                mConnector.stopAllTethering(callerPkg, listener);
+            } catch (RemoteException e) {
+                throw new IllegalStateException(e);
+            }
+        });
     }
 }
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
new file mode 100644
index 0000000..dd9eab7
--- /dev/null
+++ b/packages/Tethering/jarjar-rules.txt
@@ -0,0 +1,15 @@
+# These must be kept in sync with the framework-tethering-shared-srcs filegroup.
+# If there are files in that filegroup that do not appear here, the classes in the
+# module will be overwritten by the ones in the framework.
+# Don't jar-jar the entire package because tethering still use some internal classes
+# (like TrafficStatsConstants in com.android.internal.util)
+# TODO: simply these when tethering is built as system_current.
+rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1
+rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
+rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
+rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
+rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1
+rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
+rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
+
+rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
diff --git a/packages/Tethering/proguard.flags b/packages/Tethering/proguard.flags
index 77fc024..1f83a66 100644
--- a/packages/Tethering/proguard.flags
+++ b/packages/Tethering/proguard.flags
@@ -1 +1,9 @@
-#TBD
+# Keep class's integer static field for MessageUtils to parsing their name.
+-keep class com.android.server.connectivity.tethering.Tethering$TetherMasterSM {
+    static final int CMD_*;
+    static final int EVENT_*;
+}
+
+-keepclassmembers class android.net.ip.IpServer {
+    static final int CMD_*;
+}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index ff3d7bc..8fde520 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -30,7 +30,6 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.NetworkStackClient;
 import android.net.RouteInfo;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
@@ -122,7 +121,7 @@
          * @param state one of STATE_*
          * @param lastError one of ConnectivityManager.TETHER_ERROR_*
          */
-        public void updateInterfaceState(IpServer who, int state, int lastError) {}
+        public void updateInterfaceState(IpServer who, int state, int lastError) { }
 
         /**
          * Notify that |who| has new LinkProperties.
@@ -130,11 +129,11 @@
          * @param who the calling instance of IpServer
          * @param newLp the new LinkProperties to report
          */
-        public void updateLinkProperties(IpServer who, LinkProperties newLp) {}
+        public void updateLinkProperties(IpServer who, LinkProperties newLp) { }
     }
 
     /** Capture IpServer dependencies, for injection. */
-    public static class Dependencies {
+    public abstract static class Dependencies {
         /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
             return new RouterAdvertisementDaemon(ifParams);
@@ -149,13 +148,9 @@
             return NetdService.getInstance();
         }
 
-        /**
-         * Create a DhcpServer instance to be used by IpServer.
-         */
-        public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
-                DhcpServerCallbacks cb) {
-            NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb);
-        }
+        /** Create a DhcpServer instance to be used by IpServer. */
+        public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+                DhcpServerCallbacks cb);
     }
 
     private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index b083901..b89cfe6 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
@@ -63,7 +64,7 @@
 import android.net.INetd;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
-import android.net.ITetherInternalCallback;
+import android.net.ITetheringEventCallback;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -89,6 +90,7 @@
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
@@ -103,7 +105,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.Protocol;
@@ -162,6 +163,8 @@
     }
 
     private final SharedLog mLog = new SharedLog(TAG);
+    private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
+            new RemoteCallbackList<>();
 
     // used to synchronize public access to members
     private final Object mPublicSync;
@@ -188,8 +191,8 @@
     private final NetdCallback mNetdCallback;
     private final UserRestrictionActionListener mTetheringRestriction;
     private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
-    // All the usage of mTetherInternalCallback should run in the same thread.
-    private ITetherInternalCallback mTetherInternalCallback = null;
+    // All the usage of mTetheringEventCallback should run in the same thread.
+    private ITetheringEventCallback mTetheringEventCallback = null;
 
     private volatile TetheringConfiguration mConfig;
     private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -573,6 +576,11 @@
         }
     }
 
+    boolean isTetherProvisioningRequired() {
+        final TetheringConfiguration cfg = mConfig;
+        return mEntitlementMgr.isTetherProvisioningRequired(cfg);
+    }
+
     // TODO: Figure out how to update for local hotspot mode interfaces.
     private void sendTetherStateChangedBroadcast() {
         if (!mDeps.isTetheringSupported()) return;
@@ -588,7 +596,7 @@
         boolean bluetoothTethered = false;
 
         final TetheringConfiguration cfg = mConfig;
-        final TetherStatesParcel mTetherStatesParcel = new TetherStatesParcel();
+        mTetherStatesParcel = new TetherStatesParcel();
 
         synchronized (mPublicSync) {
             for (int i = 0; i < mTetherStates.size(); i++) {
@@ -1796,47 +1804,67 @@
     }
 
     /** Register tethering event callback */
-    void registerTetherInternalCallback(ITetherInternalCallback callback) {
+    void registerTetheringEventCallback(ITetheringEventCallback callback) {
         mHandler.post(() -> {
-            mTetherInternalCallback = callback;
+            mTetheringEventCallbacks.register(callback);
             try {
-                mTetherInternalCallback.onCallbackCreated(mTetherUpstream,
-                        mConfig.toStableParcelable(), mTetherStatesParcel);
+                callback.onCallbackStarted(mTetherUpstream, mConfig.toStableParcelable(),
+                        mTetherStatesParcel);
             } catch (RemoteException e) {
                 // Not really very much to do here.
             }
         });
     }
 
-    private void reportUpstreamChanged(Network network) {
-        // Don't need to synchronized mTetherInternalCallback because all the usage of this variable
-        // should run at the same thread.
-        if (mTetherInternalCallback == null) return;
+    /** Unregister tethering event callback */
+    void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
+        mHandler.post(() -> {
+            mTetheringEventCallbacks.unregister(callback);
+        });
+    }
 
+    private void reportUpstreamChanged(Network network) {
+        final int length = mTetheringEventCallbacks.beginBroadcast();
         try {
-            mTetherInternalCallback.onUpstreamChanged(network);
-        } catch (RemoteException e) {
-            // Not really very much to do here.
+            for (int i = 0; i < length; i++) {
+                try {
+                    mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network);
+                } catch (RemoteException e) {
+                    // Not really very much to do here.
+                }
+            }
+        } finally {
+            mTetheringEventCallbacks.finishBroadcast();
         }
     }
 
     private void reportConfigurationChanged(TetheringConfigurationParcel config) {
-        if (mTetherInternalCallback == null) return;
-
+        final int length = mTetheringEventCallbacks.beginBroadcast();
         try {
-            mTetherInternalCallback.onConfigurationChanged(config);
-        } catch (RemoteException e) {
-            // Not really very much to do here.
+            for (int i = 0; i < length; i++) {
+                try {
+                    mTetheringEventCallbacks.getBroadcastItem(i).onConfigurationChanged(config);
+                } catch (RemoteException e) {
+                    // Not really very much to do here.
+                }
+            }
+        } finally {
+            mTetheringEventCallbacks.finishBroadcast();
         }
     }
 
     private void reportTetherStateChanged(TetherStatesParcel states) {
-        if (mTetherInternalCallback == null) return;
-
+        final int length = mTetheringEventCallbacks.beginBroadcast();
         try {
-            mTetherInternalCallback.onTetherStatesChanged(states);
-        } catch (RemoteException e) {
-            // Not really very much to do here.
+            for (int i = 0; i < length; i++) {
+                try {
+                    mTetheringEventCallbacks.getBroadcastItem(i).onTetherStatesChanged(states);
+                } catch (RemoteException e) {
+                    // Not really very much to do here.
+                }
+            }
+        } finally {
+            mTetheringEventCallbacks.finishBroadcast();
         }
     }
 
@@ -1844,7 +1872,11 @@
         // Binder.java closes the resource for us.
         @SuppressWarnings("resource")
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump.");
+            return;
+        }
 
         pw.println("Tethering:");
         pw.increaseIndent();
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index 0ba8412..b16b329 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -39,7 +39,7 @@
  *
  * @hide
  */
-public class TetheringDependencies {
+public abstract class TetheringDependencies {
     /**
      * Get a reference to the offload hardware interface to be used by tethering.
      */
@@ -66,9 +66,7 @@
     /**
      * Get dependencies to be used by IpServer.
      */
-    public IpServer.Dependencies getIpServerDependencies() {
-        return new IpServer.Dependencies();
-    }
+    public abstract IpServer.Dependencies getIpServerDependencies();
 
     /**
      * Indicates whether tethering is supported on the device.
@@ -80,9 +78,7 @@
     /**
      * Get the NetworkRequest that should be fulfilled by the default network.
      */
-    public NetworkRequest getDefaultNetworkRequest() {
-        return null;
-    }
+    public abstract NetworkRequest getDefaultNetworkRequest();
 
     /**
      * Get a reference to the EntitlementManager to be used by tethering.
@@ -138,14 +134,10 @@
     /**
      * Get tethering thread looper.
      */
-    public Looper getTetheringLooper() {
-        return null;
-    }
+    public abstract Looper getTetheringLooper();
 
     /**
      *  Get Context of TetheringSerice.
      */
-    public Context getContext() {
-        return null;
-    }
+    public abstract Context getContext();
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 456f2f7..ba30845 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -16,20 +16,36 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
+
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
-import android.net.ITetherInternalCallback;
+import android.net.IIntResultListener;
+import android.net.INetworkStackConnector;
 import android.net.ITetheringConnector;
+import android.net.ITetheringEventCallback;
 import android.net.NetworkRequest;
+import android.net.dhcp.DhcpServerCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.ip.IpServer;
 import android.net.util.SharedLog;
+import android.os.Binder;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.os.UserManager;
 import android.provider.Settings;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -52,6 +68,7 @@
     private Context mContext;
     private TetheringDependencies mDeps;
     private Tethering mTethering;
+    private UserManager mUserManager;
 
     @Override
     public void onCreate() {
@@ -59,6 +76,7 @@
         mDeps = getTetheringDependencies();
         mContext = mDeps.getContext();
         mTethering = makeTethering(mDeps);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
     }
 
     /**
@@ -74,7 +92,7 @@
      */
     private synchronized IBinder makeConnector() {
         if (mConnector == null) {
-            mConnector = new TetheringConnector(mTethering);
+            mConnector = new TetheringConnector(mTethering, TetheringService.this);
         }
         return mConnector;
     }
@@ -87,55 +105,197 @@
     }
 
     private static class TetheringConnector extends ITetheringConnector.Stub {
-        private final Tethering mService;
+        private final TetheringService mService;
+        private final Tethering mTethering;
 
-        TetheringConnector(Tethering tether) {
-            mService = tether;
+        TetheringConnector(Tethering tether, TetheringService service) {
+            mTethering = tether;
+            mService = service;
         }
 
         @Override
-        public void tether(String iface) {
-            mService.tether(iface);
+        public void tether(String iface, String callerPkg, IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, listener)) return;
+
+            try {
+                listener.onResult(mTethering.tether(iface));
+            } catch (RemoteException e) { }
         }
 
         @Override
-        public void untether(String iface) {
-            mService.untether(iface);
+        public void untether(String iface, String callerPkg, IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, listener)) return;
+
+            try {
+                listener.onResult(mTethering.untether(iface));
+            } catch (RemoteException e) { }
         }
 
         @Override
-        public void setUsbTethering(boolean enable) {
-            mService.setUsbTethering(enable);
+        public void setUsbTethering(boolean enable, String callerPkg, IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, listener)) return;
+
+            try {
+                listener.onResult(mTethering.setUsbTethering(enable));
+            } catch (RemoteException e) { }
         }
 
         @Override
-        public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
-            mService.startTethering(type, receiver, showProvisioningUi);
+        public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,
+                String callerPkg) {
+            if (checkAndNotifyCommonError(callerPkg, receiver)) return;
+
+            mTethering.startTethering(type, receiver, showProvisioningUi);
         }
 
         @Override
-        public void stopTethering(int type) {
-            mService.stopTethering(type);
+        public void stopTethering(int type, String callerPkg, IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, listener)) return;
+
+            try {
+                mTethering.stopTethering(type);
+                listener.onResult(TETHER_ERROR_NO_ERROR);
+            } catch (RemoteException e) { }
         }
 
         @Override
         public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
-                boolean showEntitlementUi) {
-            mService.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
+                boolean showEntitlementUi, String callerPkg) {
+            if (checkAndNotifyCommonError(callerPkg, receiver)) return;
+
+            mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
         }
 
         @Override
-        public void registerTetherInternalCallback(ITetherInternalCallback callback) {
-            mService.registerTetherInternalCallback(callback);
+        public void registerTetheringEventCallback(ITetheringEventCallback callback,
+                String callerPkg) {
+            try {
+                if (!mService.hasTetherAccessPermission()) {
+                    callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
+                    return;
+                }
+                mTethering.registerTetheringEventCallback(callback);
+            } catch (RemoteException e) { }
         }
+
+        @Override
+        public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
+                String callerPkg) {
+            try {
+                if (!mService.hasTetherAccessPermission()) {
+                    callback.onCallbackStopped(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION);
+                    return;
+                }
+                mTethering.unregisterTetheringEventCallback(callback);
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void stopAllTethering(String callerPkg, IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, listener)) return;
+
+            try {
+                mTethering.untetherAll();
+                listener.onResult(TETHER_ERROR_NO_ERROR);
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        public void isTetheringSupported(String callerPkg, IIntResultListener listener) {
+            if (checkAndNotifyCommonError(callerPkg, listener)) return;
+
+            try {
+                listener.onResult(TETHER_ERROR_NO_ERROR);
+            } catch (RemoteException e) { }
+        }
+
+        @Override
+        protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+                    @Nullable String[] args) {
+            mTethering.dump(fd, writer, args);
+        }
+
+        private boolean checkAndNotifyCommonError(String callerPkg, IIntResultListener listener) {
+            try {
+                if (!mService.hasTetherChangePermission(callerPkg)) {
+                    listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+                    return true;
+                }
+                if (!mService.isTetheringSupported()) {
+                    listener.onResult(TETHER_ERROR_UNSUPPORTED);
+                    return true;
+                }
+            } catch (RemoteException e) {
+                return true;
+            }
+
+            return false;
+        }
+
+        private boolean checkAndNotifyCommonError(String callerPkg, ResultReceiver receiver) {
+            if (!mService.hasTetherChangePermission(callerPkg)) {
+                receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null);
+                return true;
+            }
+            if (!mService.isTetheringSupported()) {
+                receiver.send(TETHER_ERROR_UNSUPPORTED, null);
+                return true;
+            }
+
+            return false;
+        }
+
     }
 
-    @Override
-    protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
-                @Nullable String[] args) {
-        mTethering.dump(fd, writer, args);
+    // if ro.tether.denied = true we default to no tethering
+    // gservices could set the secure setting to 1 though to enable it on a build where it
+    // had previously been turned off.
+    private boolean isTetheringSupported() {
+        final int defaultVal =
+                SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
+        final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
+        final boolean tetherEnabledInSettings = tetherSupported
+                && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
+
+        return tetherEnabledInSettings && mTethering.hasTetherableConfiguration();
     }
 
+    private boolean hasTetherChangePermission(String callerPkg) {
+        if (checkCallingOrSelfPermission(
+                android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) {
+            return true;
+        }
+
+        if (mTethering.isTetherProvisioningRequired()) return false;
+
+
+        int uid = Binder.getCallingUid();
+        // If callerPkg's uid is not same as Binder.getCallingUid(),
+        // checkAndNoteWriteSettingsOperation will return false and the operation will be denied.
+        if (Settings.checkAndNoteWriteSettingsOperation(mContext, uid, callerPkg,
+                false /* throwException */)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean hasTetherAccessPermission() {
+        if (checkCallingOrSelfPermission(
+                android.Manifest.permission.TETHER_PRIVILEGED) == PERMISSION_GRANTED) {
+            return true;
+        }
+
+        if (checkCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE) == PERMISSION_GRANTED) {
+            return true;
+        }
+
+        return false;
+    }
+
+
     /**
      * An injection method for testing.
      */
@@ -159,20 +319,55 @@
 
                 @Override
                 public boolean isTetheringSupported() {
-                    int defaultVal =
-                            SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
-                    boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
-                            Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
-                    return tetherSupported;
+                    return TetheringService.this.isTetheringSupported();
                 }
 
                 @Override
                 public Context getContext() {
                     return TetheringService.this;
                 }
+
+                @Override
+                public IpServer.Dependencies getIpServerDependencies() {
+                    return new IpServer.Dependencies() {
+                        @Override
+                        public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+                                DhcpServerCallbacks cb) {
+                            try {
+                                final INetworkStackConnector service = getNetworkStackConnector();
+                                if (service == null) return;
+
+                                service.makeDhcpServer(ifName, params, cb);
+                            } catch (RemoteException e) {
+                                e.rethrowFromSystemServer();
+                            }
+                        }
+                    };
+                }
+
+                // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring
+                // networkStackClient.
+                static final int NETWORKSTACK_TIMEOUT_MS = 60_000;
+                private INetworkStackConnector getNetworkStackConnector() {
+                    IBinder connector;
+                    try {
+                        final long before = System.currentTimeMillis();
+                        while ((connector = ServiceManager.getService(
+                                Context.NETWORK_STACK_SERVICE)) == null) {
+                            if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+                                Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
+                                return null;
+                            }
+                            Thread.sleep(200);
+                        }
+                    } catch (InterruptedException e) {
+                        Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector");
+                        return null;
+                    }
+                    return INetworkStackConnector.Stub.asInterface(connector);
+                }
             };
         }
-
         return mDeps;
     }
 }
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 5b018df..81a0548 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -33,10 +33,12 @@
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
+        "framework-tethering",
     ],
     jni_libs: [
         // For mockito extended
         "libdexmakerjvmtiagent",
         "libstaticjvmtiagent",
     ],
+    jarjar_rules: "jarjar-rules.txt",
 }
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt
new file mode 100644
index 0000000..64fdebd
--- /dev/null
+++ b/packages/Tethering/tests/unit/jarjar-rules.txt
@@ -0,0 +1,11 @@
+# Don't jar-jar the entire package because this test use some
+# internal classes (like ArrayUtils in com.android.internal.util)
+rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1
+rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
+rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
+rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
+rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1
+rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
+rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
+
+rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 0273ed3..15ae977 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -72,7 +72,7 @@
 import android.net.INetd;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
-import android.net.ITetherInternalCallback;
+import android.net.ITetheringEventCallback;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -81,6 +81,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
@@ -167,6 +168,7 @@
     @Mock private IDhcpServer mDhcpServer;
     @Mock private INetd mNetd;
     @Mock private UserManager mUserManager;
+    @Mock private NetworkRequest mNetworkRequest;
 
     private final MockIpServerDependencies mIpServerDependencies =
             spy(new MockIpServerDependencies());
@@ -311,6 +313,11 @@
         }
 
         @Override
+        public NetworkRequest getDefaultNetworkRequest() {
+            return mNetworkRequest;
+        }
+
+        @Override
         public boolean isTetheringSupported() {
             mIsTetheringSupportedCalls++;
             return true;
@@ -1039,7 +1046,7 @@
                 expectedInteractionsWithShowNotification);
     }
 
-    private class TestTetherInternalCallback extends ITetherInternalCallback.Stub {
+    private class TestTetheringEventCallback extends ITetheringEventCallback.Stub {
         private final ArrayList<Network> mActualUpstreams = new ArrayList<>();
         private final ArrayList<TetheringConfigurationParcel> mTetheringConfigs =
                 new ArrayList<>();
@@ -1100,13 +1107,16 @@
         }
 
         @Override
-        public void onCallbackCreated(Network network, TetheringConfigurationParcel config,
+        public void onCallbackStarted(Network network, TetheringConfigurationParcel config,
                 TetherStatesParcel states) {
             mActualUpstreams.add(network);
             mTetheringConfigs.add(config);
             mTetherStates.add(states);
         }
 
+        @Override
+        public void onCallbackStopped(int errorCode) { }
+
         public void assertNoUpstreamChangeCallback() {
             assertTrue(mActualUpstreams.isEmpty());
         }
@@ -1115,10 +1125,20 @@
             assertTrue(mTetheringConfigs.isEmpty());
         }
 
+        public void assertNoStateChangeCallback() {
+            assertTrue(mTetherStates.isEmpty());
+        }
+
         public void assertStateChangeCallback() {
             assertFalse(mTetherStates.isEmpty());
         }
 
+        public void assertNoCallback() {
+            assertNoUpstreamChangeCallback();
+            assertNoConfigChangeCallback();
+            assertNoStateChangeCallback();
+        }
+
         private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual,
                 @NonNull TetheringConfigurationParcel expect) {
             assertEquals(actual.subId, expect.subId);
@@ -1139,18 +1159,19 @@
     }
 
     @Test
-    public void testRegisterTetherInternalCallback() throws Exception {
-        TestTetherInternalCallback callback = new TestTetherInternalCallback();
+    public void testRegisterTetheringEventCallback() throws Exception {
+        TestTetheringEventCallback callback = new TestTetheringEventCallback();
+        TestTetheringEventCallback callback2 = new TestTetheringEventCallback();
 
         // 1. Register one callback before running any tethering.
-        mTethering.registerTetherInternalCallback(callback);
+        mTethering.registerTetheringEventCallback(callback);
         mLooper.dispatchAll();
         callback.expectUpstreamChanged(new Network[] {null});
         callback.expectConfigurationChanged(
                 mTethering.getTetheringConfiguration().toStableParcelable());
         TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
         assertEquals(tetherState, null);
-        // 2. Enable wifi tethering
+        // 2. Enable wifi tethering.
         NetworkState upstreamState = buildMobileDualStackUpstreamState();
         when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
         when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
@@ -1168,14 +1189,26 @@
         assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
         callback.expectUpstreamChanged(upstreamState.network);
 
-        // 3. Disable wifi tethering.
+        // 3. Register second callback.
+        mTethering.registerTetheringEventCallback(callback2);
+        mLooper.dispatchAll();
+        callback2.expectUpstreamChanged(upstreamState.network);
+        callback2.expectConfigurationChanged(
+                mTethering.getTetheringConfiguration().toStableParcelable());
+        tetherState = callback2.pollTetherStatesChanged();
+        assertEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
+
+        // 4. Unregister first callback and disable wifi tethering
+        mTethering.unregisterTetheringEventCallback(callback);
+        mLooper.dispatchAll();
         mTethering.stopTethering(TETHERING_WIFI);
         sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
         mLooper.dispatchAll();
-        tetherState = callback.pollTetherStatesChanged();
+        tetherState = callback2.pollTetherStatesChanged();
         assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
         mLooper.dispatchAll();
-        callback.expectUpstreamChanged(new Network[] {null});
+        callback2.expectUpstreamChanged(new Network[] {null});
+        callback.assertNoCallback();
     }
 
     @Test
diff --git a/services/Android.bp b/services/Android.bp
index 3b56607..6709e5d 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -72,6 +72,7 @@
 
     libs: [
         "android.hidl.manager-V1.0-java",
+        "framework-tethering"
     ],
 
     plugins: [
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 725303d..cbd095b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -29,6 +29,7 @@
         "android.hardware.tv.cec-V1.0-java",
         "app-compat-annotations",
         "vintf-vibrator-java",
+        "framework-tethering",
     ],
 
     required: [
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b719435..bb78ace 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -77,7 +77,6 @@
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.ISocketKeepaliveCallback;
-import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
 import android.net.IpMemoryStore;
 import android.net.IpPrefix;
@@ -278,8 +277,6 @@
 
     private MockableSystemProperties mSystemProperties;
 
-    private TetheringManager mTetheringManager;
-
     @VisibleForTesting
     protected final PermissionMonitor mPermissionMonitor;
 
@@ -867,13 +864,6 @@
         }
 
         /**
-         * Get a reference to the TetheringManager.
-         */
-        public TetheringManager getTetheringManager() {
-            return TetheringManager.getInstance();
-        }
-
-        /**
          * @see ProxyTracker
          */
         public ProxyTracker makeProxyTracker(@NonNull Context context,
@@ -1072,8 +1062,6 @@
 
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
-        mTetheringManager = mDeps.getTetheringManager();
-
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
 
         // Set up the listener for user state for creating user VPNs.
@@ -1887,14 +1875,6 @@
             }
             mHandler.sendMessage(mHandler.obtainMessage(
                     EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
-
-            // TODO: relocate this specific callback in Tethering.
-            if (restrictBackground) {
-                log("onRestrictBackgroundChanged(true): disabling tethering");
-                mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
-                mTetheringManager.stopTethering(ConnectivityManager.TETHERING_USB);
-                mTetheringManager.stopTethering(ConnectivityManager.TETHERING_BLUETOOTH);
-            }
         }
     };
 
@@ -2024,12 +2004,6 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
     }
 
-    private void enforceTetherAccessPermission() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.ACCESS_NETWORK_STATE,
-                "ConnectivityService");
-    }
-
     private void enforceControlAlwaysOnVpnPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CONTROL_ALWAYS_ON_VPN,
@@ -2463,12 +2437,6 @@
         mKeepaliveTracker.dump(pw);
 
         pw.println();
-        pw.println("TetheringManager logs:");
-        pw.increaseIndent();
-        TetheringManager.getInstance().dump(pw);
-        pw.decreaseIndent();
-
-        pw.println();
         dumpAvoidBadWifiSettings(pw);
 
         pw.println();
@@ -3993,183 +3961,55 @@
         }
     }
 
-    // javadoc from interface
     @Override
-    public int tether(String iface, String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-        if (isTetheringSupported()) {
-            return mTetheringManager.tether(iface);
-        } else {
-            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
-        }
-    }
-
-    // javadoc from interface
-    @Override
-    public int untether(String iface, String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-
-        if (isTetheringSupported()) {
-            return mTetheringManager.untether(iface);
-        } else {
-            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
-        }
-    }
-
-    // javadoc from interface
-    @Override
+    @Deprecated
     public int getLastTetherError(String iface) {
-        enforceTetherAccessPermission();
-
-        if (isTetheringSupported()) {
-            return mTetheringManager.getLastTetherError(iface);
-        } else {
-            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
-        }
-    }
-
-    // TODO - proper iface API for selection by property, inspection, etc
-    @Override
-    public String[] getTetherableUsbRegexs() {
-        enforceTetherAccessPermission();
-        if (isTetheringSupported()) {
-            return mTetheringManager.getTetherableUsbRegexs();
-        } else {
-            return new String[0];
-        }
+        final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+                Context.TETHERING_SERVICE);
+        return tm.getLastTetherError(iface);
     }
 
     @Override
-    public String[] getTetherableWifiRegexs() {
-        enforceTetherAccessPermission();
-        if (isTetheringSupported()) {
-            return mTetheringManager.getTetherableWifiRegexs();
-        } else {
-            return new String[0];
-        }
-    }
-
-    @Override
-    public String[] getTetherableBluetoothRegexs() {
-        enforceTetherAccessPermission();
-        if (isTetheringSupported()) {
-            return mTetheringManager.getTetherableBluetoothRegexs();
-        } else {
-            return new String[0];
-        }
-    }
-
-    @Override
-    public int setUsbTethering(boolean enable, String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-        if (isTetheringSupported()) {
-            return mTetheringManager.setUsbTethering(enable);
-        } else {
-            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
-        }
-    }
-
-    // TODO - move iface listing, queries, etc to new module
-    // javadoc from interface
-    @Override
+    @Deprecated
     public String[] getTetherableIfaces() {
-        enforceTetherAccessPermission();
-        return mTetheringManager.getTetherableIfaces();
+        final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+                Context.TETHERING_SERVICE);
+        return tm.getTetherableIfaces();
     }
 
     @Override
+    @Deprecated
     public String[] getTetheredIfaces() {
-        enforceTetherAccessPermission();
-        return mTetheringManager.getTetheredIfaces();
+        final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+                Context.TETHERING_SERVICE);
+        return tm.getTetheredIfaces();
     }
 
+
     @Override
+    @Deprecated
     public String[] getTetheringErroredIfaces() {
-        enforceTetherAccessPermission();
-        return mTetheringManager.getTetheringErroredIfaces();
+        final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+                Context.TETHERING_SERVICE);
+
+        return tm.getTetheringErroredIfaces();
     }
 
     @Override
-    public String[] getTetheredDhcpRanges() {
-        enforceSettingsPermission();
-        return mTetheringManager.getTetheredDhcpRanges();
+    @Deprecated
+    public String[] getTetherableUsbRegexs() {
+        final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+                Context.TETHERING_SERVICE);
+
+        return tm.getTetherableUsbRegexs();
     }
 
     @Override
-    public boolean isTetheringSupported(String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-        return isTetheringSupported();
-    }
-
-    // if ro.tether.denied = true we default to no tethering
-    // gservices could set the secure setting to 1 though to enable it on a build where it
-    // had previously been turned off.
-    private boolean isTetheringSupported() {
-        int defaultVal = encodeBool(!mSystemProperties.get("ro.tether.denied").equals("true"));
-        boolean tetherSupported = toBool(Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.TETHER_SUPPORTED, defaultVal));
-        boolean tetherEnabledInSettings = tetherSupported
-                && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
-
-        // Elevate to system UID to avoid caller requiring MANAGE_USERS permission.
-        boolean adminUser = false;
-        final long token = Binder.clearCallingIdentity();
-        try {
-            adminUser = mUserManager.isAdminUser();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-
-        return tetherEnabledInSettings && adminUser
-                && mTetheringManager.hasTetherableConfiguration();
-    }
-
-    @Override
-    public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,
-            String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-        if (!isTetheringSupported()) {
-            receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
-            return;
-        }
-        mTetheringManager.startTethering(type, receiver, showProvisioningUi);
-    }
-
-    @Override
-    public void stopTethering(int type, String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-        mTetheringManager.stopTethering(type);
-    }
-
-    /**
-     * Get the latest value of the tethering entitlement check.
-     *
-     * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns
-     * out some such apps are observed to abuse this API, change to per-UID limits on this API
-     * if it's really needed.
-     */
-    @Override
-    public void getLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
-            boolean showEntitlementUi, String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-        mTetheringManager.requestLatestTetheringEntitlementResult(
-                type, receiver, showEntitlementUi);
-    }
-
-    /** Register tethering event callback. */
-    @Override
-    public void registerTetheringEventCallback(ITetheringEventCallback callback,
-            String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-        mTetheringManager.registerTetheringEventCallback(callback);
-    }
-
-    /** Unregister tethering event callback. */
-    @Override
-    public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
-            String callerPkg) {
-        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
-        mTetheringManager.unregisterTetheringEventCallback(callback);
+    @Deprecated
+    public String[] getTetherableWifiRegexs() {
+        final TetheringManager tm = (TetheringManager) mContext.getSystemService(
+                Context.TETHERING_SERVICE);
+        return tm.getTetherableWifiRegexs();
     }
 
     // Called when we lose the default network and have no replacement yet.
@@ -7050,14 +6890,6 @@
         // Turn airplane mode off
         setAirplaneMode(false);
 
-        if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
-            // Untether
-            String pkgName = mContext.getOpPackageName();
-            for (String tether : getTetheredIfaces()) {
-                untether(tether, pkgName);
-            }
-        }
-
         if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
             // Remove always-on package
             synchronized (mVpns) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2528063f..b5b21f4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -40,8 +41,8 @@
 import android.database.sqlite.SQLiteGlobal;
 import android.hardware.display.DisplayManagerInternal;
 import android.net.ConnectivityModuleConnector;
+import android.net.ITetheringConnector;
 import android.net.NetworkStackClient;
-import android.net.TetheringManager;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -2212,8 +2213,14 @@
 
             traceBeginAndSlog("StartTethering");
             try {
-                // Tethering must start after ConnectivityService and NetworkStack.
-                TetheringManager.getInstance().start();
+                // TODO: hide implementation details, b/146312721.
+                ConnectivityModuleConnector.getInstance().startModuleService(
+                        ITetheringConnector.class.getName(),
+                        PERMISSION_MAINLINE_NETWORK_STACK, service -> {
+                            ServiceManager.addService(Context.TETHERING_SERVICE, service,
+                                    false /* allowIsolated */,
+                                    DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+                        });
             } catch (Throwable e) {
                 reportWtf("starting Tethering", e);
             }
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 3babb0b..9c7cfc1 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -10,14 +10,12 @@
     srcs: [
         ":net-module-utils-srcs",
         ":services.net-sources",
-        ":tethering-manager",
     ],
     static_libs: [
         "dnsresolver_aidl_interface-V2-java",
         "netd_aidl_interface-unstable-java",
         "netlink-client",
         "networkstack-client",
-        "tethering-client",
     ],
 }
 
@@ -25,8 +23,6 @@
     name: "services-tethering-shared-srcs",
     srcs: [
         ":framework-annotations",
-        "java/android/net/ConnectivityModuleConnector.java",
-        "java/android/net/NetworkStackClient.java",
         "java/android/net/util/NetdService.java",
         "java/android/net/util/NetworkConstants.java",
     ],
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 25028fb..c4801aa 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -32,7 +32,6 @@
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
 import android.net.NetworkRequest
 import android.net.TestNetworkStackClient
-import android.net.TetheringManager
 import android.net.metrics.IpConnectivityLog
 import android.os.ConditionVariable
 import android.os.IBinder
@@ -169,7 +168,6 @@
         val deps = spy(ConnectivityService.Dependencies())
         doReturn(networkStackClient).`when`(deps).networkStack
         doReturn(metricsLogger).`when`(deps).metricsLogger
-        doReturn(mock(TetheringManager::class.java)).`when`(deps).getTetheringManager()
         doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
         doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
         doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a24426b..b2d363e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -164,7 +164,6 @@
 import android.net.ResolverParamsParcel;
 import android.net.RouteInfo;
 import android.net.SocketKeepalive;
-import android.net.TetheringManager;
 import android.net.UidRange;
 import android.net.metrics.IpConnectivityLog;
 import android.net.shared.NetworkMonitorUtils;
@@ -1133,7 +1132,6 @@
         doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
         doReturn(mNetworkStack).when(deps).getNetworkStack();
         doReturn(systemProperties).when(deps).getSystemProperties();
-        doReturn(mock(TetheringManager.class)).when(deps).getTetheringManager();
         doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
         doReturn(mMetricsService).when(deps).getMetricsLogger();
         doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());