[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
This commit is contained in:
markchien
2019-12-16 20:15:20 +08:00
parent 7b2a29361c
commit 6d06f6d51a
35 changed files with 933 additions and 835 deletions

View File

@ -266,6 +266,7 @@ filegroup {
name: "framework-updatable-sources",
srcs: [
":framework-sdkext-sources",
":framework-tethering-srcs",
":updatable-media-srcs",
]
}
@ -370,6 +371,7 @@ java_defaults {
"ext",
"unsupportedappusage",
"updatable_media_stubs",
"framework-tethering",
],
jarjar_rules: ":framework-jarjar-rules",
@ -604,10 +606,26 @@ filegroup {
],
}
// 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

View File

@ -1554,6 +1554,7 @@ package android.content {
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 @@ package android.provider {
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";

View File

@ -111,6 +111,7 @@ import android.net.NetworkPolicyManager;
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 @@ final class SystemServiceRegistry {
}
});
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

View File

@ -3857,6 +3857,15 @@ public abstract class Context {
*/
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

View File

@ -50,6 +50,7 @@ import android.os.RemoteException;
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.ArrayMap;
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 @@ public class ConnectivityManager {
private INetworkManagementService mNMService;
private INetworkPolicyManager mNPManager;
private TetheringManager mTetheringManager;
/**
* Tests if a given integer represents a valid network type.
@ -2340,6 +2341,28 @@ public class ConnectivityManager {
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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
*/
@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 @@ public class ConnectivityManager {
*/
@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 @@ public class ConnectivityManager {
*/
@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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
}
};
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 @@ public class ConnectivityManager {
@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 class ConnectivityManager {
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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
*/
@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 @@ public class ConnectivityManager {
@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 @@ public class ConnectivityManager {
}
};
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 class ConnectivityManager {
public void factoryReset() {
try {
mService.factoryReset();
getTetheringManager().stopAllTethering();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@ -19,7 +19,6 @@ package android.net;
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 @@ interface IConnectivityManager
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 @@ interface IConnectivityManager
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();
}

View File

@ -10402,35 +10402,37 @@ public final class Settings {
*/
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.

View File

@ -57,4 +57,7 @@ platform cert need to be included, as apps signed with the platform cert are exe
<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>

View File

@ -29,9 +29,11 @@ java_defaults {
"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 @@ java_defaults {
resource_dirs: [
"res",
],
libs: [
"framework-tethering",
],
jarjar_rules: "jarjar-rules.txt",
optimize: {
proguard_flags_files: ["proguard.flags"],
},
@ -104,7 +110,6 @@ android_app {
manifest: "AndroidManifest_InProcess.xml",
// InProcessTethering is a replacement for Tethering
overrides: ["Tethering"],
// TODO: use PlatformNetworkPermissionConfig.
}
// Updatable tethering packaged as an application

View File

@ -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"

View File

@ -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>

View File

@ -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

View File

@ -16,6 +16,7 @@
apex {
name: "com.android.tethering",
java_libs: ["framework-tethering"],
apps: ["Tethering"],
manifest: "manifest.json",
key: "com.android.tethering.key",

View File

@ -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 @@ aidl_interface {
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 @@ aidl_interface {
}
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"
}

View File

@ -0,0 +1 @@
rule android.annotation.** com.android.networkstack.tethering.annotation.@1

View File

@ -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);
}

View File

@ -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);
}

View File

@ -21,14 +21,15 @@ import android.net.TetheringConfigurationParcel;
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);
}

View File

@ -15,94 +15,141 @@
*/
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 @@ public class TetheringManager {
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 @@ public class TetheringManager {
* 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 @@ public class TetheringManager {
* 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 @@ public class TetheringManager {
* 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 @@ public class TetheringManager {
* 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 @@ public class TetheringManager {
* 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 @@ public class TetheringManager {
* 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 @@ public class TetheringManager {
* 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);
}
});
}
}

View File

@ -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

View File

@ -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_*;
}

View File

@ -30,7 +30,6 @@ import android.net.InterfaceConfiguration;
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 @@ public class IpServer extends StateMachine {
* @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 @@ public class IpServer extends StateMachine {
* @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 @@ public class IpServer extends StateMachine {
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;

View File

@ -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.hardware.usb.UsbManager;
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.Handler;
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 android.util.SparseArray;
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 @@ public class Tethering {
}
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 @@ public class Tethering {
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 @@ public class Tethering {
}
}
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 @@ public class Tethering {
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 @@ public class Tethering {
}
/** 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 @@ public class Tethering {
// 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();

View File

@ -39,7 +39,7 @@ import java.util.ArrayList;
*
* @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 @@ public class TetheringDependencies {
/**
* 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 @@ public class TetheringDependencies {
/**
* 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 @@ public class TetheringDependencies {
/**
* 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();
}

View File

@ -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 @@ public class TetheringService extends Service {
private Context mContext;
private TetheringDependencies mDeps;
private Tethering mTethering;
private UserManager mUserManager;
@Override
public void onCreate() {
@ -59,6 +76,7 @@ public class TetheringService extends Service {
mDeps = getTetheringDependencies();
mContext = mDeps.getContext();
mTethering = makeTethering(mDeps);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
/**
@ -74,7 +92,7 @@ public class TetheringService extends Service {
*/
private synchronized IBinder makeConnector() {
if (mConnector == null) {
mConnector = new TetheringConnector(mTethering);
mConnector = new TetheringConnector(mTethering, TetheringService.this);
}
return mConnector;
}
@ -87,55 +105,197 @@ public class TetheringService extends Service {
}
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 @@ public class TetheringService extends Service {
@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;
}
}

View File

@ -33,10 +33,12 @@ android_test {
"android.test.runner",
"android.test.base",
"android.test.mock",
"framework-tethering",
],
jni_libs: [
// For mockito extended
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
jarjar_rules: "jarjar-rules.txt",
}

View File

@ -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

View File

@ -72,7 +72,7 @@ import android.hardware.usb.UsbManager;
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.MacAddress;
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 @@ public class TetheringTest {
@Mock private IDhcpServer mDhcpServer;
@Mock private INetd mNetd;
@Mock private UserManager mUserManager;
@Mock private NetworkRequest mNetworkRequest;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@ -310,6 +312,11 @@ public class TetheringTest {
return mIpServerDependencies;
}
@Override
public NetworkRequest getDefaultNetworkRequest() {
return mNetworkRequest;
}
@Override
public boolean isTetheringSupported() {
mIsTetheringSupportedCalls++;
@ -1039,7 +1046,7 @@ public class TetheringTest {
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 @@ public class TetheringTest {
}
@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 @@ public class TetheringTest {
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 @@ public class TetheringTest {
}
@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 @@ public class TetheringTest {
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

View File

@ -72,6 +72,7 @@ java_library {
libs: [
"android.hidl.manager-V1.0-java",
"framework-tethering"
],
plugins: [

View File

@ -29,6 +29,7 @@ java_library_static {
"android.hardware.tv.cec-V1.0-java",
"app-compat-annotations",
"vintf-vibrator-java",
"framework-tethering",
],
required: [

View File

@ -77,7 +77,6 @@ import android.net.INetworkPolicyListener;
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 @@ public class ConnectivityService extends IConnectivityManager.Stub
private MockableSystemProperties mSystemProperties;
private TetheringManager mTetheringManager;
@VisibleForTesting
protected final PermissionMonitor mPermissionMonitor;
@ -866,13 +863,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return NetworkStackClient.getInstance();
}
/**
* Get a reference to the TetheringManager.
*/
public TetheringManager getTetheringManager() {
return TetheringManager.getInstance();
}
/**
* @see ProxyTracker
*/
@ -1072,8 +1062,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
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 @@ public class ConnectivityService extends IConnectivityManager.Stub
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,
@ -2462,12 +2436,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.println();
mKeepaliveTracker.dump(pw);
pw.println();
pw.println("TetheringManager logs:");
pw.increaseIndent();
TetheringManager.getInstance().dump(pw);
pw.decreaseIndent();
pw.println();
dumpAvoidBadWifiSettings(pw);
@ -3993,183 +3961,55 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
// 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 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 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) {

View File

@ -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.SQLiteCompatibilityWalFlags;
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 @@ public final class SystemServer {
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);
}

View File

@ -10,14 +10,12 @@ java_library_static {
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 @@ filegroup {
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",
],

View File

@ -32,7 +32,6 @@ import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
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 @@ class ConnectivityServiceIntegrationTest {
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()

View File

@ -164,7 +164,6 @@ import android.net.ProxyInfo;
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 @@ public class ConnectivityServiceTest {
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());