[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
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index adc5a72..5785707 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/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/Tethering/common/TetheringLib/jarjar-rules.txt b/Tethering/common/TetheringLib/jarjar-rules.txt
new file mode 100644
index 0000000..35e0f88
--- /dev/null
+++ b/Tethering/common/TetheringLib/jarjar-rules.txt
@@ -0,0 +1 @@
+rule android.annotation.** com.android.networkstack.tethering.annotation.@1
diff --git a/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl b/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl
new file mode 100644
index 0000000..c3d66ee
--- /dev/null
+++ b/Tethering/common/TetheringLib/src/android/net/IIntResultListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+/**
+ * Listener interface allowing objects to listen to various module event.
+ * {@hide}
+ */
+oneway interface IIntResultListener {
+    void onResult(int resultCode);
+}
diff --git a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index bfe502f..d30c399 100644
--- a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/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/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
similarity index 83%
rename from Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl
rename to Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
index abb00e8..2836195 100644
--- a/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl
+++ b/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/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 7fb286b..a49ab85 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/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);
+            }
+        });
     }
 }