[Tether07] Migrate Tethering into module

Now tethering would be run in dedicated service.
TetheringManager is the interface used to communicate with
TetheringService. The new call flow would be: ConnectivityManager
-> ConnectivityService -> TetheringManager -> TetheringService.
Note: the return value of #tether(), #untether() and #setUsbTethering()
APIs would always be no error. Client can use #getLastTetherError()
or #getTetheredIfaces or listen tether state change to check
status of corresponding interface.

Bug: 136040414
Bug: 144742179
Test: -build, flash, boot
      -atest TetheringTests
      -atest FrameworksNetTests

Change-Id: I7e78c0e0a3e70f940a749ba2a39ece7c7ec5b9b3
Merged-In: I7e78c0e0a3e70f940a749ba2a39ece7c7ec5b9b3
This commit is contained in:
markchien
2019-09-30 14:40:57 +08:00
committed by Mark Chien
parent 810aa68d27
commit ee6ad37837
24 changed files with 5158 additions and 80 deletions

View File

@@ -22,13 +22,12 @@ java_defaults {
":framework-tethering-shared-srcs",
":net-module-utils-srcs",
":services-tethering-shared-srcs",
":servicescore-tethering-src",
],
static_libs: [
"androidx.annotation_annotation",
"netd_aidl_interface-java",
"netd_aidl_interface-unstable-java",
"netlink-client",
"networkstack-aidl-interfaces-java",
"networkstack-aidl-interfaces-unstable-java",
"android.hardware.tetheroffload.control-V1.0-java",
"tethering-client",
],
@@ -96,7 +95,16 @@ java_defaults {
}
// Non-updatable tethering running in the system server process for devices not using the module
// TODO: build in-process tethering APK here.
android_app {
name: "InProcessTethering",
defaults: ["TetheringAppDefaults"],
static_libs: ["TetheringApiCurrentLib"],
certificate: "platform",
manifest: "AndroidManifest_InProcess.xml",
// InProcessTethering is a replacement for Tethering
overrides: ["Tethering"],
// TODO: use PlatformNetworkPermissionConfig.
}
// Updatable tethering packaged as an application
android_app {
@@ -109,36 +117,3 @@ android_app {
// The permission configuration *must* be included to ensure security of the device
required: ["NetworkPermissionConfig"],
}
// This group will be removed when tethering migration is done.
filegroup {
name: "tethering-servicescore-srcs",
srcs: [
"src/com/android/server/connectivity/tethering/EntitlementManager.java",
"src/com/android/server/connectivity/tethering/OffloadController.java",
"src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java",
"src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
"src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java",
],
}
// This group will be removed when tethering migration is done.
filegroup {
name: "tethering-servicesnet-srcs",
srcs: [
"src/android/net/dhcp/DhcpServerCallbacks.java",
"src/android/net/dhcp/DhcpServingParamsParcelExt.java",
"src/android/net/ip/IpServer.java",
"src/android/net/ip/RouterAdvertisementDaemon.java",
"src/android/net/util/InterfaceSet.java",
"src/android/net/util/PrefixUtils.java",
],
}
// This group would be removed when tethering migration is done.
filegroup {
name: "tethering-jni-srcs",
srcs: [
"jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
],
}

View File

@@ -25,5 +25,11 @@
android:process="com.android.networkstack.process"
android:extractNativeLibs="false"
android:persistent="true">
<service android:name="com.android.server.connectivity.tethering.TetheringService"
android:permission="android.permission.MAINLINE_NETWORK_STACK">
<intent-filter>
<action android:name="android.net.ITetheringConnector"/>
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.tethering.inprocess"
android:sharedUserId="android.uid.system"
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">
<intent-filter>
<action android:name="android.net.ITetheringConnector.InProcess"/>
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -18,8 +18,12 @@
aidl_interface {
name: "tethering-aidl-interfaces",
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",
],
backend: {
ndk: {
@@ -33,8 +37,15 @@ aidl_interface {
java_library {
name: "tethering-client",
platform_apis: true,
sdk_version: "system_current",
static_libs: [
"tethering-aidl-interfaces-java",
],
}
// 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"],
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.net.Network;
import android.net.TetheringConfigurationParcel;
import android.net.TetherStatesParcel;
/**
* Callback class for receiving tethering changed events
* @hide
*/
oneway interface ITetherInternalCallback
{
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,6 +15,23 @@
*/
package android.net;
import android.net.ITetherInternalCallback;
import android.os.ResultReceiver;
/** @hide */
oneway interface ITetheringConnector {
void tether(String iface);
void untether(String iface);
void setUsbTethering(boolean enable);
void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi);
void stopTethering(int type);
void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
boolean showEntitlementUi);
void registerTetherInternalCallback(ITetherInternalCallback callback);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/**
* Status details for tethering downstream interfaces.
* {@hide}
*/
parcelable TetherStatesParcel {
String[] availableList;
String[] tetheredList;
String[] localOnlyList;
String[] erroredIfaceList;
// List of Last error code corresponding to each errored iface in erroredIfaceList. */
// TODO: Improve this as b/143122247.
int[] lastErrorList;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/**
* Configuration details for a tethering.
* @hide
*/
parcelable TetheringConfigurationParcel {
int subId;
String[] tetherableUsbRegexs;
String[] tetherableWifiRegexs;
String[] tetherableBluetoothRegexs;
boolean isDunRequired;
boolean chooseUpstreamAutomatically;
int[] preferredUpstreamIfaceTypes;
String[] legacyDhcpRanges;
String[] defaultIPv4DNS;
boolean enableLegacyDhcpServer;
String[] provisioningApp;
String provisioningAppNoUi;
int provisioningCheckPeriod;
}

View File

@@ -0,0 +1,507 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import static android.Manifest.permission.NETWORK_STACK;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.util.SharedLog;
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 com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.StringJoiner;
/**
* Service used to communicate with the tethering, which is running in a separate module.
* @hide
*/
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
private static TetheringManager sInstance;
@Nullable
private ITetheringConnector mConnector;
private TetherInternalCallback mCallback;
private Network mTetherUpstream;
private TetheringConfigurationParcel mTetheringConfiguration;
private TetherStatesParcel mTetherStatesParcel;
private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
new RemoteCallbackList<>();
@GuardedBy("mLog")
private final SharedLog mLog = new SharedLog(TAG);
private TetheringManager() { }
/**
* Get the TetheringManager singleton instance.
*/
public static synchronized TetheringManager getInstance() {
if (sInstance == null) {
sInstance = new TetheringManager();
}
return sInstance;
}
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();
try {
mConnector.registerTetherInternalCallback(mCallback);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
private class TetherInternalCallback extends ITetherInternalCallback.Stub {
private final ConditionVariable mWaitForCallback = new ConditionVariable(false);
private static final int EVENT_CALLBACK_TIMEOUT_MS = 60_000;
@Override
public void onUpstreamChanged(Network network) {
mTetherUpstream = network;
reportUpstreamChanged(network);
}
@Override
public void onConfigurationChanged(TetheringConfigurationParcel config) {
mTetheringConfiguration = config;
}
@Override
public void onTetherStatesChanged(TetherStatesParcel states) {
mTetherStatesParcel = states;
}
@Override
public void onCallbackCreated(Network network, TetheringConfigurationParcel config,
TetherStatesParcel states) {
mTetherUpstream = network;
mTetheringConfiguration = config;
mTetherStatesParcel = states;
mWaitForCallback.open();
}
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");
}
/**
* Attempt to tether the named interface. This will setup a dhcp server
* on the interface, forward and NAT IP v4 packets and forward DNS requests
* to the best active upstream network interface. Note that if no upstream
* 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().
*
* TODO: Deprecate this API. The only usages should be in PanService and Wifi P2P which
* need direct access.
*
* {@hide}
*/
public int tether(@NonNull String iface) {
try {
mConnector.tether(iface);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
return TETHER_ERROR_NO_ERROR;
}
/**
* Stop tethering the named interface.
*
* {@hide}
*/
public int untether(@NonNull String iface) {
try {
mConnector.untether(iface);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
return TETHER_ERROR_NO_ERROR;
}
/**
* 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.
*
* @Deprecated
* {@hide}
*/
public int setUsbTethering(boolean enable) {
try {
mConnector.setUsbTethering(enable);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
return TETHER_ERROR_NO_ERROR;
}
/**
* 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) {
try {
mConnector.startTethering(type, receiver, showProvisioningUi);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
* applicable.
*
* {@hide}
*/
public void stopTethering(int type) {
try {
mConnector.stopTethering(type);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Request 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.
*/
// TODO: improve the usage of ResultReceiver, b/145096122
public void requestLatestTetheringEntitlementResult(int type, @NonNull ResultReceiver receiver,
boolean showEntitlementUi) {
try {
mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Register tethering event callback.
*
* {@hide}
*/
public void registerTetheringEventCallback(@NonNull ITetheringEventCallback callback) {
mTetheringEventCallbacks.register(callback);
}
/**
* Unregister tethering event callback.
*
* {@hide}
*/
public void unregisterTetheringEventCallback(@NonNull ITetheringEventCallback callback) {
mTetheringEventCallbacks.unregister(callback);
}
/**
* Get a more detailed error code after a Tethering or Untethering
* request asynchronously failed.
*
* {@hide}
*/
public int getLastTetherError(@NonNull String iface) {
if (!mCallback.awaitCallbackCreation()) {
throw new NullPointerException("callback was not ready yet");
}
if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
int i = 0;
for (String errored : mTetherStatesParcel.erroredIfaceList) {
if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i];
i++;
}
return TETHER_ERROR_NO_ERROR;
}
/**
* Get the list of regular expressions that define any tetherable
* USB network interfaces. If USB tethering is not supported by the
* device, this list should be empty.
*
* {@hide}
*/
public @NonNull String[] getTetherableUsbRegexs() {
if (!mCallback.awaitCallbackCreation()) {
throw new NullPointerException("callback was not ready yet");
}
return mTetheringConfiguration.tetherableUsbRegexs;
}
/**
* Get the list of regular expressions that define any tetherable
* Wifi network interfaces. If Wifi tethering is not supported by the
* device, this list should be empty.
*
* {@hide}
*/
public @NonNull String[] getTetherableWifiRegexs() {
if (!mCallback.awaitCallbackCreation()) {
throw new NullPointerException("callback was not ready yet");
}
return mTetheringConfiguration.tetherableWifiRegexs;
}
/**
* Get the list of regular expressions that define any tetherable
* Bluetooth network interfaces. If Bluetooth tethering is not supported by the
* device, this list should be empty.
*
* {@hide}
*/
public @NonNull String[] getTetherableBluetoothRegexs() {
if (!mCallback.awaitCallbackCreation()) {
throw new NullPointerException("callback was not ready yet");
}
return mTetheringConfiguration.tetherableBluetoothRegexs;
}
/**
* Get the set of tetherable, available interfaces. This list is limited by
* device configuration and current interface existence.
*
* {@hide}
*/
public @NonNull String[] getTetherableIfaces() {
if (!mCallback.awaitCallbackCreation()) {
throw new NullPointerException("callback was not ready yet");
}
if (mTetherStatesParcel == null) return new String[0];
return mTetherStatesParcel.availableList;
}
/**
* Get the set of tethered interfaces.
*
* {@hide}
*/
public @NonNull String[] getTetheredIfaces() {
if (!mCallback.awaitCallbackCreation()) {
throw new NullPointerException("callback was not ready yet");
}
if (mTetherStatesParcel == null) return new String[0];
return mTetherStatesParcel.tetheredList;
}
/**
* Get the set of interface names which attempted to tether but
* failed.
*
* {@hide}
*/
public @NonNull String[] getTetheringErroredIfaces() {
if (!mCallback.awaitCallbackCreation()) {
throw new NullPointerException("callback was not ready yet");
}
if (mTetherStatesParcel == null) return new String[0];
return mTetherStatesParcel.erroredIfaceList;
}
/**
* Get the set of tethered dhcp ranges.
*
* {@hide}
*/
public @NonNull String[] getTetheredDhcpRanges() {
if (!mCallback.awaitCallbackCreation()) {
throw new NullPointerException("callback was not ready yet");
}
return mTetheringConfiguration.legacyDhcpRanges;
}
/**
* Check if the device allows for tethering.
*
* {@hide}
*/
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;
return hasDownstreamConfiguration && hasUpstreamConfiguration;
}
/**
* Log a message in the local log.
*/
private void log(@NonNull String message) {
synchronized (mLog) {
mLog.log(message);
}
}
/**
* 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();
}
}

View File

@@ -145,4 +145,18 @@ int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIE
gMethods, NELEM(gMethods));
}
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
ALOGE("ERROR: GetEnv failed");
return JNI_ERR;
}
if (register_android_server_connectivity_tethering_OffloadHardwareInterface(env) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
}; // namespace android

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.util;
import android.net.INetdUnsolicitedEventListener;
import androidx.annotation.NonNull;
/**
* Base {@link INetdUnsolicitedEventListener} that provides no-op implementations which can be
* overridden.
*/
public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
@Override
public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs,
int uid) { }
@Override
public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { }
@Override
public void onInterfaceDnsServerInfo(@NonNull String ifName, long lifetimeS,
@NonNull String[] servers) { }
@Override
public void onInterfaceAddressUpdated(@NonNull String addr, String ifName, int flags,
int scope) { }
@Override
public void onInterfaceAddressRemoved(@NonNull String addr, @NonNull String ifName, int flags,
int scope) { }
@Override
public void onInterfaceAdded(@NonNull String ifName) { }
@Override
public void onInterfaceRemoved(@NonNull String ifName) { }
@Override
public void onInterfaceChanged(@NonNull String ifName, boolean up) { }
@Override
public void onInterfaceLinkStateChanged(@NonNull String ifName, boolean up) { }
@Override
public void onRouteChanged(boolean updated, @NonNull String route, @NonNull String gateway,
@NonNull String ifName) { }
@Override
public void onStrictCleartextDetected(int uid, @NonNull String hex) { }
@Override
public int getInterfaceVersion() {
return INetdUnsolicitedEventListener.VERSION;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.util;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.util.Log;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
* A utility class that runs the provided callback on the provided handler when
* intents matching the provided filter arrive. Intents received by a stale
* receiver are safely ignored.
*
* Calls to startListening() and stopListening() must happen on the same thread.
*
* @hide
*/
public class VersionedBroadcastListener {
private static final boolean DBG = false;
private final String mTag;
private final Context mContext;
private final Handler mHandler;
private final IntentFilter mFilter;
private final Consumer<Intent> mCallback;
private final AtomicInteger mGenerationNumber;
private BroadcastReceiver mReceiver;
public VersionedBroadcastListener(String tag, Context ctx, Handler handler,
IntentFilter filter, Consumer<Intent> callback) {
mTag = tag;
mContext = ctx;
mHandler = handler;
mFilter = filter;
mCallback = callback;
mGenerationNumber = new AtomicInteger(0);
}
/** Start listening to intent broadcast. */
public void startListening() {
if (DBG) Log.d(mTag, "startListening");
if (mReceiver != null) return;
mReceiver = new Receiver(mTag, mGenerationNumber, mCallback);
mContext.registerReceiver(mReceiver, mFilter, null, mHandler);
}
/** Stop listening to intent broadcast. */
public void stopListening() {
if (DBG) Log.d(mTag, "stopListening");
if (mReceiver == null) return;
mGenerationNumber.incrementAndGet();
mContext.unregisterReceiver(mReceiver);
mReceiver = null;
}
private static class Receiver extends BroadcastReceiver {
public final String tag;
public final AtomicInteger atomicGenerationNumber;
public final Consumer<Intent> callback;
// Used to verify this receiver is still current.
public final int generationNumber;
Receiver(String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) {
this.tag = tag;
this.atomicGenerationNumber = atomicGenerationNumber;
this.callback = callback;
generationNumber = atomicGenerationNumber.incrementAndGet();
}
@Override
public void onReceive(Context context, Intent intent) {
final int currentGenerationNumber = atomicGenerationNumber.get();
if (DBG) {
Log.d(tag, "receiver generationNumber=" + generationNumber
+ ", current generationNumber=" + currentGenerationNumber);
}
if (generationNumber != currentGenerationNumber) return;
callback.accept(intent);
}
}
}

View File

@@ -47,6 +47,7 @@ import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
@@ -55,7 +56,6 @@ import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StateMachine;
import com.android.server.connectivity.MockableSystemProperties;
import java.io.PrintWriter;
@@ -94,7 +94,6 @@ public class EntitlementManager {
private final ArraySet<Integer> mCurrentTethers;
private final Context mContext;
private final int mPermissionChangeMessageCode;
private final MockableSystemProperties mSystemProperties;
private final SharedLog mLog;
private final SparseIntArray mEntitlementCacheValue;
private final EntitlementHandler mHandler;
@@ -110,12 +109,12 @@ public class EntitlementManager {
private TetheringConfigurationFetcher mFetcher;
public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
int permissionChangeMessageCode, MockableSystemProperties systemProperties) {
int permissionChangeMessageCode) {
mContext = ctx;
mLog = log.forSubComponent(TAG);
mCurrentTethers = new ArraySet<Integer>();
mCellularPermitted = new SparseIntArray();
mSystemProperties = systemProperties;
mEntitlementCacheValue = new SparseIntArray();
mTetherMasterSM = tetherMasterSM;
mPermissionChangeMessageCode = permissionChangeMessageCode;
@@ -287,7 +286,7 @@ public class EntitlementManager {
*/
@VisibleForTesting
protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) {
if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
|| config.provisioningApp.length == 0) {
return false;
}
@@ -526,8 +525,8 @@ public class EntitlementManager {
handleMaybeRunProvisioning(config);
break;
case EVENT_GET_ENTITLEMENT_VALUE:
handleGetLatestTetheringEntitlementValue(msg.arg1, (ResultReceiver) msg.obj,
toBool(msg.arg2));
handleRequestLatestTetheringEntitlementValue(msg.arg1,
(ResultReceiver) msg.obj, toBool(msg.arg2));
break;
default:
mLog.log("Unknown event: " + msg.what);
@@ -651,15 +650,15 @@ public class EntitlementManager {
}
/** Get the last value of the tethering entitlement check. */
public void getLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
boolean showEntitlementUi) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
downstream, encodeBool(showEntitlementUi), receiver));
}
private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
boolean showEntitlementUi) {
private void handleRequestLatestTetheringEntitlementValue(int downstream,
ResultReceiver receiver, boolean showEntitlementUi) {
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
if (!isTetherProvisioningRequired(config)) {
receiver.send(TETHER_ERROR_NO_ERROR, null);

View File

@@ -0,0 +1,311 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.connectivity.tethering;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkState;
import android.net.RouteInfo;
import android.net.ip.IpServer;
import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
import android.util.Log;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Random;
/**
* IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
* This coordinator is responsible for evaluating the dedicated prefixes
* assigned to the device and deciding how to divvy them up among downstream
* interfaces.
*
* @hide
*/
public class IPv6TetheringCoordinator {
private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
private static final boolean DBG = false;
private static final boolean VDBG = false;
private static class Downstream {
public final IpServer ipServer;
public final int mode; // IpServer.STATE_*
// Used to append to a ULA /48, constructing a ULA /64 for local use.
public final short subnetId;
Downstream(IpServer ipServer, int mode, short subnetId) {
this.ipServer = ipServer;
this.mode = mode;
this.subnetId = subnetId;
}
}
private final ArrayList<IpServer> mNotifyList;
private final SharedLog mLog;
// NOTE: mActiveDownstreams is a list and not a hash data structure because
// we keep active downstreams in arrival order. This is done so /64s can
// be parceled out on a "first come, first served" basis and a /64 used by
// a downstream that is no longer active can be redistributed to any next
// waiting active downstream (again, in arrival order).
private final LinkedList<Downstream> mActiveDownstreams;
private final byte[] mUniqueLocalPrefix;
private short mNextSubnetId;
private NetworkState mUpstreamNetworkState;
public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) {
mNotifyList = notifyList;
mLog = log.forSubComponent(TAG);
mActiveDownstreams = new LinkedList<>();
mUniqueLocalPrefix = generateUniqueLocalPrefix();
mNextSubnetId = 0;
}
/** Add active downstream to ipv6 tethering candidate list. */
public void addActiveDownstream(IpServer downstream, int mode) {
if (findDownstream(downstream) == null) {
// Adding a new downstream appends it to the list. Adding a
// downstream a second time without first removing it has no effect.
// We never change the mode of a downstream except by first removing
// it and then re-adding it (with its new mode specified);
if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) {
// Make sure subnet IDs are always positive. They are appended
// to a ULA /48 to make a ULA /64 for local use.
mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1);
}
updateIPv6TetheringInterfaces();
}
}
/** Remove downstream from ipv6 tethering candidate list. */
public void removeActiveDownstream(IpServer downstream) {
stopIPv6TetheringOn(downstream);
if (mActiveDownstreams.remove(findDownstream(downstream))) {
updateIPv6TetheringInterfaces();
}
// When tethering is stopping we can reset the subnet counter.
if (mNotifyList.isEmpty()) {
if (!mActiveDownstreams.isEmpty()) {
Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty.");
}
mNextSubnetId = 0;
}
}
/**
* Call when upstream NetworkState may be changed.
* If upstream has ipv6 for tethering, update this new NetworkState
* to IpServer. Otherwise stop ipv6 tethering on downstream interfaces.
*/
public void updateUpstreamNetworkState(NetworkState ns) {
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
stopIPv6TetheringOnAllInterfaces();
setUpstreamNetworkState(null);
return;
}
if (mUpstreamNetworkState != null
&& !ns.network.equals(mUpstreamNetworkState.network)) {
stopIPv6TetheringOnAllInterfaces();
}
setUpstreamNetworkState(ns);
updateIPv6TetheringInterfaces();
}
private void stopIPv6TetheringOnAllInterfaces() {
for (IpServer ipServer : mNotifyList) {
stopIPv6TetheringOn(ipServer);
}
}
private void setUpstreamNetworkState(NetworkState ns) {
if (ns == null) {
mUpstreamNetworkState = null;
} else {
// Make a deep copy of the parts we need.
mUpstreamNetworkState = new NetworkState(
null,
new LinkProperties(ns.linkProperties),
new NetworkCapabilities(ns.networkCapabilities),
new Network(ns.network),
null,
null);
}
mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
}
private void updateIPv6TetheringInterfaces() {
for (IpServer ipServer : mNotifyList) {
final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer);
ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
break;
}
}
private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
final Downstream ds = findDownstream(ipServer);
if (ds == null) return null;
if (ds.mode == IpServer.STATE_LOCAL_ONLY) {
// Build a Unique Locally-assigned Prefix configuration.
return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
}
// This downstream is in IpServer.STATE_TETHERED mode.
if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
return null;
}
// NOTE: Here, in future, we would have policies to decide how to divvy
// up the available dedicated prefixes among downstream interfaces.
// At this time we have no such mechanism--we only support tethering
// IPv6 toward the oldest (first requested) active downstream.
final Downstream currentActive = mActiveDownstreams.peek();
if (currentActive != null && currentActive.ipServer == ipServer) {
final LinkProperties lp = getIPv6OnlyLinkProperties(
mUpstreamNetworkState.linkProperties);
if (lp.hasIpv6DefaultRoute() && lp.hasGlobalIpv6Address()) {
return lp;
}
}
return null;
}
Downstream findDownstream(IpServer ipServer) {
for (Downstream ds : mActiveDownstreams) {
if (ds.ipServer == ipServer) return ds;
}
return null;
}
private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
final LinkProperties v6only = new LinkProperties();
if (lp == null) {
return v6only;
}
// NOTE: At this time we don't copy over any information about any
// stacked links. No current stacked link configuration has IPv6.
v6only.setInterfaceName(lp.getInterfaceName());
v6only.setMtu(lp.getMtu());
for (LinkAddress linkAddr : lp.getLinkAddresses()) {
if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
v6only.addLinkAddress(linkAddr);
}
}
for (RouteInfo routeInfo : lp.getRoutes()) {
final IpPrefix destination = routeInfo.getDestination();
if ((destination.getAddress() instanceof Inet6Address)
&& (destination.getPrefixLength() <= 64)) {
v6only.addRoute(routeInfo);
}
}
for (InetAddress dnsServer : lp.getDnsServers()) {
if (isIPv6GlobalAddress(dnsServer)) {
// For now we include ULAs.
v6only.addDnsServer(dnsServer);
}
}
v6only.setDomains(lp.getDomains());
return v6only;
}
// TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
// announce our own IPv6 address as DNS server.
private static boolean isIPv6GlobalAddress(InetAddress ip) {
return (ip instanceof Inet6Address)
&& !ip.isAnyLocalAddress()
&& !ip.isLoopbackAddress()
&& !ip.isLinkLocalAddress()
&& !ip.isSiteLocalAddress()
&& !ip.isMulticastAddress();
}
private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) {
final LinkProperties lp = new LinkProperties();
final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48);
lp.addRoute(new RouteInfo(local48, null, null));
final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64);
// Because this is a locally-generated ULA, we don't have an upstream
// address. But because the downstream IP address management code gets
// its prefix from the upstream's IP address, we create a fake one here.
lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64));
lp.setMtu(NetworkConstants.ETHER_MTU);
return lp;
}
private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) {
final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length);
bytes[7] = (byte) (subnetId >> 8);
bytes[8] = (byte) subnetId;
return new IpPrefix(bytes, prefixlen);
}
// Generates a Unique Locally-assigned Prefix:
//
// https://tools.ietf.org/html/rfc4193#section-3.1
//
// The result is a /48 that can be used for local-only communications.
private static byte[] generateUniqueLocalPrefix() {
final byte[] ulp = new byte[6]; // 6 = 48bits / 8bits/byte
(new Random()).nextBytes(ulp);
final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN);
in6addr[0] = (byte) 0xfd; // fc00::/7 and L=1
return in6addr;
}
private static String toDebugString(NetworkState ns) {
if (ns == null) {
return "NetworkState{null}";
}
return String.format("NetworkState{%s, %s, %s}",
ns.network,
ns.networkCapabilities,
ns.linkProperties);
}
private static void stopIPv6TetheringOn(IpServer ipServer) {
ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
}
}

View File

@@ -107,6 +107,8 @@ public class OffloadHardwareInterface {
public OffloadHardwareInterface(Handler h, SharedLog log) {
mHandler = h;
mLog = log.forSubComponent(TAG);
System.loadLibrary("tetheroffloadjni");
}
/** Get default value indicating whether offload is supported. */

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.TetheringConfigurationParcel;
import android.net.util.SharedLog;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -384,4 +385,32 @@ public class TetheringConfiguration {
}
return false;
}
/**
* Convert this TetheringConfiguration to a TetheringConfigurationParcel.
*/
public TetheringConfigurationParcel toStableParcelable() {
final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel();
parcel.subId = subId;
parcel.tetherableUsbRegexs = tetherableUsbRegexs;
parcel.tetherableWifiRegexs = tetherableWifiRegexs;
parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs;
parcel.isDunRequired = isDunRequired;
parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically;
int[] preferredTypes = new int[preferredUpstreamIfaceTypes.size()];
int index = 0;
for (Integer type : preferredUpstreamIfaceTypes) {
preferredTypes[index++] = type;
}
parcel.preferredUpstreamIfaceTypes = preferredTypes;
parcel.legacyDhcpRanges = legacyDhcpRanges;
parcel.defaultIPv4DNS = defaultIPv4DNS;
parcel.enableLegacyDhcpServer = enableLegacyDhcpServer;
parcel.provisioningApp = provisioningApp;
parcel.provisioningAppNoUi = provisioningAppNoUi;
parcel.provisioningCheckPeriod = provisioningCheckPeriod;
return parcel;
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.connectivity.tethering;
import android.content.Context;
import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.NetworkRequest;
import android.net.ip.IpServer;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.ServiceManager;
import com.android.internal.util.StateMachine;
import java.util.ArrayList;
/**
* Capture tethering dependencies, for injection.
*
* @hide
*/
public class TetheringDependencies {
/**
* Get a reference to the offload hardware interface to be used by tethering.
*/
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
return new OffloadHardwareInterface(h, log);
}
/**
* Get a reference to the UpstreamNetworkMonitor to be used by tethering.
*/
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
SharedLog log, int what) {
return new UpstreamNetworkMonitor(ctx, target, log, what);
}
/**
* Get a reference to the IPv6TetheringCoordinator to be used by tethering.
*/
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
ArrayList<IpServer> notifyList, SharedLog log) {
return new IPv6TetheringCoordinator(notifyList, log);
}
/**
* Get dependencies to be used by IpServer.
*/
public IpServer.Dependencies getIpServerDependencies() {
return new IpServer.Dependencies();
}
/**
* Indicates whether tethering is supported on the device.
*/
public boolean isTetheringSupported() {
return true;
}
/**
* Get the NetworkRequest that should be fulfilled by the default network.
*/
public NetworkRequest getDefaultNetworkRequest() {
return null;
}
/**
* Get a reference to the EntitlementManager to be used by tethering.
*/
public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
SharedLog log, int what) {
return new EntitlementManager(ctx, target, log, what);
}
/**
* Generate a new TetheringConfiguration according to input sub Id.
*/
public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
int subId) {
return new TetheringConfiguration(ctx, log, subId);
}
/**
* Get a reference to INetworkManagementService to registerTetheringStatsProvider from
* OffloadController. Note: This should be removed soon by Usage refactor work in R
* development cycle.
*/
public INetworkManagementService getINetworkManagementService() {
return INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
}
/**
* Get a reference to INetworkStatsService to force update tethering usage.
* Note: This should be removed in R development cycle.
*/
public INetworkStatsService getINetworkStatsService() {
return INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
}
/**
* Get a reference to INetworkPolicyManager to be used by tethering.
*/
public INetworkPolicyManager getINetworkPolicyManager() {
return INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
}
/**
* Get a reference to INetd to be used by tethering.
*/
public INetd getINetd(Context context) {
return INetd.Stub.asInterface(
(IBinder) context.getSystemService(Context.NETD_SERVICE));
}
/**
* Get tethering thread looper.
*/
public Looper getTetheringLooper() {
return null;
}
/**
* Get Context of TetheringSerice.
*/
public Context getContext() {
return null;
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.connectivity.tethering;
import android.annotation.Nullable;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.NetworkState;
import android.net.RouteInfo;
import android.net.util.InterfaceSet;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
/**
* @hide
*/
public final class TetheringInterfaceUtils {
/**
* Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
* @return null if there is no usable interface, or a set of at least one interface otherwise.
*/
public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) {
if (ns == null) {
return null;
}
final LinkProperties lp = ns.linkProperties;
final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY);
final String if6 = getIPv6Interface(ns);
return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
}
/**
* Get the upstream interface for IPv6 tethering.
* @return null if there is no usable interface, or the interface name otherwise.
*/
public static @Nullable String getIPv6Interface(NetworkState ns) {
// Broadly speaking:
//
// [1] does the upstream have an IPv6 default route?
//
// and
//
// [2] does the upstream have one or more global IPv6 /64s
// dedicated to this device?
//
// In lieu of Prefix Delegation and other evaluation of whether a
// prefix may or may not be dedicated to this device, for now just
// check whether the upstream is TRANSPORT_CELLULAR. This works
// because "[t]he 3GPP network allocates each default bearer a unique
// /64 prefix", per RFC 6459, Section 5.2.
final boolean canTether =
(ns != null) && (ns.network != null)
&& (ns.linkProperties != null) && (ns.networkCapabilities != null)
// At least one upstream DNS server:
&& ns.linkProperties.hasIpv6DnsServer()
// Minimal amount of IPv6 provisioning:
&& ns.linkProperties.hasGlobalIpv6Address()
// Temporary approximation of "dedicated prefix":
&& ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
return canTether
? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY)
: null;
}
private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
final RouteInfo ri = (lp != null)
? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
: null;
return (ri != null) ? ri.getInterface() : null;
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.connectivity.tethering;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.ITetherInternalCallback;
import android.net.ITetheringConnector;
import android.net.NetworkRequest;
import android.net.util.SharedLog;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* Android service used to manage tethering.
*
* <p>The service returns a binder for the system server to communicate with the tethering.
*/
public class TetheringService extends Service {
private static final String TAG = TetheringService.class.getSimpleName();
private final SharedLog mLog = new SharedLog(TAG);
private TetheringConnector mConnector;
private Context mContext;
private TetheringDependencies mDeps;
private Tethering mTethering;
@Override
public void onCreate() {
mLog.mark("onCreate");
mDeps = getTetheringDependencies();
mContext = mDeps.getContext();
mTethering = makeTethering(mDeps);
}
/**
* Make a reference to Tethering object.
*/
@VisibleForTesting
public Tethering makeTethering(TetheringDependencies deps) {
return new Tethering(deps);
}
/**
* Create a binder connector for the system server to communicate with the tethering.
*/
private synchronized IBinder makeConnector() {
if (mConnector == null) {
mConnector = new TetheringConnector(mTethering);
}
return mConnector;
}
@NonNull
@Override
public IBinder onBind(Intent intent) {
mLog.mark("onBind");
return makeConnector();
}
private static class TetheringConnector extends ITetheringConnector.Stub {
private final Tethering mService;
TetheringConnector(Tethering tether) {
mService = tether;
}
@Override
public void tether(String iface) {
mService.tether(iface);
}
@Override
public void untether(String iface) {
mService.untether(iface);
}
@Override
public void setUsbTethering(boolean enable) {
mService.setUsbTethering(enable);
}
@Override
public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
mService.startTethering(type, receiver, showProvisioningUi);
}
@Override
public void stopTethering(int type) {
mService.stopTethering(type);
}
@Override
public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
boolean showEntitlementUi) {
mService.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
}
@Override
public void registerTetherInternalCallback(ITetherInternalCallback callback) {
mService.registerTetherInternalCallback(callback);
}
}
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
mTethering.dump(fd, writer, args);
}
/**
* An injection method for testing.
*/
@VisibleForTesting
public TetheringDependencies getTetheringDependencies() {
if (mDeps == null) {
mDeps = new TetheringDependencies() {
@Override
public NetworkRequest getDefaultNetworkRequest() {
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
return cm.getDefaultRequest();
}
@Override
public Looper getTetheringLooper() {
final HandlerThread tetherThread = new HandlerThread("android.tethering");
tetherThread.start();
return tetherThread.getLooper();
}
@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;
}
@Override
public Context getContext() {
return TetheringService.this;
}
};
}
return mDeps;
}
}

View File

@@ -18,7 +18,6 @@ android_test {
name: "TetheringTests",
certificate: "platform",
srcs: [
":servicescore-tethering-src",
"src/**/*.java",
],
test_suites: ["device-tests"],
@@ -41,17 +40,3 @@ android_test {
"libstaticjvmtiagent",
],
}
// This group would be removed when tethering migration is done.
filegroup {
name: "tethering-tests-src",
srcs: [
"src/com/android/server/connectivity/tethering/EntitlementManagerTest.java",
"src/com/android/server/connectivity/tethering/OffloadControllerTest.java",
"src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
"src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java",
"src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
"src/android/net/ip/IpServerTest.java",
"src/android/net/util/InterfaceSetTest.java",
],
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.util;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.reset;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.BroadcastInterceptingContext;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VersionedBroadcastListenerTest {
private static final String TAG = VersionedBroadcastListenerTest.class.getSimpleName();
private static final String ACTION_TEST = "action.test.happy.broadcasts";
@Mock private Context mContext;
private BroadcastInterceptingContext mServiceContext;
private Handler mHandler;
private VersionedBroadcastListener mListener;
private int mCallbackCount;
private void doCallback() {
mCallbackCount++;
}
private class MockContext extends BroadcastInterceptingContext {
MockContext(Context base) {
super(base);
}
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
if (Looper.myLooper() == null) {
Looper.prepare();
}
}
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
reset(mContext);
mServiceContext = new MockContext(mContext);
mHandler = new Handler(Looper.myLooper());
mCallbackCount = 0;
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_TEST);
mListener = new VersionedBroadcastListener(
TAG, mServiceContext, mHandler, filter, (Intent intent) -> doCallback());
}
@After public void tearDown() throws Exception {
if (mListener != null) {
mListener.stopListening();
mListener = null;
}
}
private void sendBroadcast() {
final Intent intent = new Intent(ACTION_TEST);
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@Test
public void testBasicListening() {
assertEquals(0, mCallbackCount);
mListener.startListening();
for (int i = 0; i < 5; i++) {
sendBroadcast();
assertEquals(i + 1, mCallbackCount);
}
mListener.stopListening();
}
@Test
public void testBroadcastsBeforeStartAreIgnored() {
assertEquals(0, mCallbackCount);
for (int i = 0; i < 5; i++) {
sendBroadcast();
assertEquals(0, mCallbackCount);
}
mListener.startListening();
sendBroadcast();
assertEquals(1, mCallbackCount);
}
@Test
public void testBroadcastsAfterStopAreIgnored() {
mListener.startListening();
sendBroadcast();
assertEquals(1, mCallbackCount);
mListener.stopListening();
for (int i = 0; i < 5; i++) {
sendBroadcast();
assertEquals(1, mCallbackCount);
}
}
}

View File

@@ -24,6 +24,9 @@ import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -44,6 +47,7 @@ import android.os.Bundle;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
@@ -57,7 +61,6 @@ import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.connectivity.MockableSystemProperties;
import org.junit.After;
import org.junit.Before;
@@ -65,6 +68,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
@@ -80,7 +85,6 @@ public final class EntitlementManagerTest {
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private Context mContext;
@Mock private MockableSystemProperties mSystemProperties;
@Mock private Resources mResources;
@Mock private SharedLog mLog;
@Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener;
@@ -95,6 +99,7 @@ public final class EntitlementManagerTest {
private TestStateMachine mSM;
private WrappedEntitlementManager mEnMgr;
private TetheringConfiguration mConfig;
private MockitoSession mMockingSession;
private class MockContext extends BroadcastInterceptingContext {
MockContext(Context base) {
@@ -118,8 +123,8 @@ public final class EntitlementManagerTest {
public int silentProvisionCount = 0;
public WrappedEntitlementManager(Context ctx, StateMachine target,
SharedLog log, int what, MockableSystemProperties systemProperties) {
super(ctx, target, log, what, systemProperties);
SharedLog log, int what) {
super(ctx, target, log, what);
}
public void reset() {
@@ -144,6 +149,15 @@ public final class EntitlementManagerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mMockingSession = mockitoSession()
.initMocks(this)
.spyStatic(SystemProperties.class)
.strictness(Strictness.WARN)
.startMocking();
// Don't disable tethering provisioning unless requested.
doReturn(false).when(
() -> SystemProperties.getBoolean(
eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
@@ -161,8 +175,7 @@ public final class EntitlementManagerTest {
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
mMockContext = new MockContext(mContext);
mSM = new TestStateMachine();
mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE,
mSystemProperties);
mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE);
mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mEnMgr.setTetheringConfigurationFetcher(() -> {
@@ -176,6 +189,7 @@ public final class EntitlementManagerTest {
mSM.quit();
mSM = null;
}
mMockingSession.finishMocking();
}
private void setupForRequiredProvisioning() {
@@ -184,9 +198,6 @@ public final class EntitlementManagerTest {
.thenReturn(PROVISIONING_APP_NAME);
when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
.thenReturn(PROVISIONING_NO_UI_APP_NAME);
// Don't disable tethering provisioning unless requested.
when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
anyBoolean())).thenReturn(false);
// Act like the CarrierConfigManager is present and ready unless told otherwise.
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(mCarrierConfigManager);
@@ -244,7 +255,7 @@ public final class EntitlementManagerTest {
}
@Test
public void testGetLastEntitlementCacheValue() throws Exception {
public void testRequestLastEntitlementCacheValue() throws Exception {
final CountDownLatch mCallbacklatch = new CountDownLatch(1);
// 1. Entitlement check is not required.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
@@ -255,7 +266,7 @@ public final class EntitlementManagerTest {
mCallbacklatch.countDown();
}
};
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -270,7 +281,7 @@ public final class EntitlementManagerTest {
mCallbacklatch.countDown();
}
};
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -284,7 +295,7 @@ public final class EntitlementManagerTest {
mCallbacklatch.countDown();
}
};
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
@@ -298,7 +309,7 @@ public final class EntitlementManagerTest {
mCallbacklatch.countDown();
}
};
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -312,7 +323,7 @@ public final class EntitlementManagerTest {
mCallbacklatch.countDown();
}
};
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
@@ -326,7 +337,7 @@ public final class EntitlementManagerTest {
mCallbacklatch.countDown();
}
};
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -339,7 +350,7 @@ public final class EntitlementManagerTest {
mCallbacklatch.countDown();
}
};
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);