[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:
@@ -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",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
35
Tethering/AndroidManifest_InProcess.xml
Normal file
35
Tethering/AndroidManifest_InProcess.xml
Normal 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>
|
||||
@@ -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"],
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
106
Tethering/src/android/net/util/VersionedBroadcastListener.java
Normal file
106
Tethering/src/android/net/util/VersionedBroadcastListener.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user