[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
This commit is contained in:
@@ -22,13 +22,12 @@ java_defaults {
|
|||||||
":framework-tethering-shared-srcs",
|
":framework-tethering-shared-srcs",
|
||||||
":net-module-utils-srcs",
|
":net-module-utils-srcs",
|
||||||
":services-tethering-shared-srcs",
|
":services-tethering-shared-srcs",
|
||||||
":servicescore-tethering-src",
|
|
||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"androidx.annotation_annotation",
|
"androidx.annotation_annotation",
|
||||||
"netd_aidl_interface-java",
|
"netd_aidl_interface-unstable-java",
|
||||||
"netlink-client",
|
"netlink-client",
|
||||||
"networkstack-aidl-interfaces-java",
|
"networkstack-aidl-interfaces-unstable-java",
|
||||||
"android.hardware.tetheroffload.control-V1.0-java",
|
"android.hardware.tetheroffload.control-V1.0-java",
|
||||||
"tethering-client",
|
"tethering-client",
|
||||||
],
|
],
|
||||||
@@ -96,7 +95,16 @@ java_defaults {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Non-updatable tethering running in the system server process for devices not using the module
|
// 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
|
// Updatable tethering packaged as an application
|
||||||
android_app {
|
android_app {
|
||||||
@@ -109,36 +117,3 @@ android_app {
|
|||||||
// The permission configuration *must* be included to ensure security of the device
|
// The permission configuration *must* be included to ensure security of the device
|
||||||
required: ["NetworkPermissionConfig"],
|
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:process="com.android.networkstack.process"
|
||||||
android:extractNativeLibs="false"
|
android:extractNativeLibs="false"
|
||||||
android:persistent="true">
|
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>
|
</application>
|
||||||
</manifest>
|
</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 {
|
aidl_interface {
|
||||||
name: "tethering-aidl-interfaces",
|
name: "tethering-aidl-interfaces",
|
||||||
local_include_dir: "src",
|
local_include_dir: "src",
|
||||||
|
include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
|
||||||
srcs: [
|
srcs: [
|
||||||
|
"src/android/net/ITetherInternalCallback.aidl",
|
||||||
"src/android/net/ITetheringConnector.aidl",
|
"src/android/net/ITetheringConnector.aidl",
|
||||||
|
"src/android/net/TetheringConfigurationParcel.aidl",
|
||||||
|
"src/android/net/TetherStatesParcel.aidl",
|
||||||
],
|
],
|
||||||
backend: {
|
backend: {
|
||||||
ndk: {
|
ndk: {
|
||||||
@@ -33,8 +37,15 @@ aidl_interface {
|
|||||||
|
|
||||||
java_library {
|
java_library {
|
||||||
name: "tethering-client",
|
name: "tethering-client",
|
||||||
platform_apis: true,
|
sdk_version: "system_current",
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"tethering-aidl-interfaces-java",
|
"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;
|
package android.net;
|
||||||
|
|
||||||
|
import android.net.ITetherInternalCallback;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
oneway interface ITetheringConnector {
|
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));
|
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
|
}; // 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.PersistableBundle;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.CarrierConfigManager;
|
import android.telephony.CarrierConfigManager;
|
||||||
@@ -55,7 +56,6 @@ import android.util.SparseIntArray;
|
|||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.StateMachine;
|
import com.android.internal.util.StateMachine;
|
||||||
import com.android.server.connectivity.MockableSystemProperties;
|
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
@@ -94,7 +94,6 @@ public class EntitlementManager {
|
|||||||
private final ArraySet<Integer> mCurrentTethers;
|
private final ArraySet<Integer> mCurrentTethers;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final int mPermissionChangeMessageCode;
|
private final int mPermissionChangeMessageCode;
|
||||||
private final MockableSystemProperties mSystemProperties;
|
|
||||||
private final SharedLog mLog;
|
private final SharedLog mLog;
|
||||||
private final SparseIntArray mEntitlementCacheValue;
|
private final SparseIntArray mEntitlementCacheValue;
|
||||||
private final EntitlementHandler mHandler;
|
private final EntitlementHandler mHandler;
|
||||||
@@ -110,12 +109,12 @@ public class EntitlementManager {
|
|||||||
private TetheringConfigurationFetcher mFetcher;
|
private TetheringConfigurationFetcher mFetcher;
|
||||||
|
|
||||||
public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
|
public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
|
||||||
int permissionChangeMessageCode, MockableSystemProperties systemProperties) {
|
int permissionChangeMessageCode) {
|
||||||
|
|
||||||
mContext = ctx;
|
mContext = ctx;
|
||||||
mLog = log.forSubComponent(TAG);
|
mLog = log.forSubComponent(TAG);
|
||||||
mCurrentTethers = new ArraySet<Integer>();
|
mCurrentTethers = new ArraySet<Integer>();
|
||||||
mCellularPermitted = new SparseIntArray();
|
mCellularPermitted = new SparseIntArray();
|
||||||
mSystemProperties = systemProperties;
|
|
||||||
mEntitlementCacheValue = new SparseIntArray();
|
mEntitlementCacheValue = new SparseIntArray();
|
||||||
mTetherMasterSM = tetherMasterSM;
|
mTetherMasterSM = tetherMasterSM;
|
||||||
mPermissionChangeMessageCode = permissionChangeMessageCode;
|
mPermissionChangeMessageCode = permissionChangeMessageCode;
|
||||||
@@ -287,7 +286,7 @@ public class EntitlementManager {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) {
|
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) {
|
|| config.provisioningApp.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -526,8 +525,8 @@ public class EntitlementManager {
|
|||||||
handleMaybeRunProvisioning(config);
|
handleMaybeRunProvisioning(config);
|
||||||
break;
|
break;
|
||||||
case EVENT_GET_ENTITLEMENT_VALUE:
|
case EVENT_GET_ENTITLEMENT_VALUE:
|
||||||
handleGetLatestTetheringEntitlementValue(msg.arg1, (ResultReceiver) msg.obj,
|
handleRequestLatestTetheringEntitlementValue(msg.arg1,
|
||||||
toBool(msg.arg2));
|
(ResultReceiver) msg.obj, toBool(msg.arg2));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mLog.log("Unknown event: " + msg.what);
|
mLog.log("Unknown event: " + msg.what);
|
||||||
@@ -651,15 +650,15 @@ public class EntitlementManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Get the last value of the tethering entitlement check. */
|
/** 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) {
|
boolean showEntitlementUi) {
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
|
mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
|
||||||
downstream, encodeBool(showEntitlementUi), receiver));
|
downstream, encodeBool(showEntitlementUi), receiver));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
|
private void handleRequestLatestTetheringEntitlementValue(int downstream,
|
||||||
boolean showEntitlementUi) {
|
ResultReceiver receiver, boolean showEntitlementUi) {
|
||||||
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
|
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
|
||||||
if (!isTetherProvisioningRequired(config)) {
|
if (!isTetherProvisioningRequired(config)) {
|
||||||
receiver.send(TETHER_ERROR_NO_ERROR, null);
|
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) {
|
public OffloadHardwareInterface(Handler h, SharedLog log) {
|
||||||
mHandler = h;
|
mHandler = h;
|
||||||
mLog = log.forSubComponent(TAG);
|
mLog = log.forSubComponent(TAG);
|
||||||
|
|
||||||
|
System.loadLibrary("tetheroffloadjni");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get default value indicating whether offload is supported. */
|
/** 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.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.TetheringConfigurationParcel;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.SubscriptionManager;
|
import android.telephony.SubscriptionManager;
|
||||||
@@ -384,4 +385,32 @@ public class TetheringConfiguration {
|
|||||||
}
|
}
|
||||||
return false;
|
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",
|
name: "TetheringTests",
|
||||||
certificate: "platform",
|
certificate: "platform",
|
||||||
srcs: [
|
srcs: [
|
||||||
":servicescore-tethering-src",
|
|
||||||
"src/**/*.java",
|
"src/**/*.java",
|
||||||
],
|
],
|
||||||
test_suites: ["device-tests"],
|
test_suites: ["device-tests"],
|
||||||
@@ -41,17 +40,3 @@ android_test {
|
|||||||
"libstaticjvmtiagent",
|
"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.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
|
||||||
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -44,6 +47,7 @@ import android.os.Bundle;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
|
import android.os.SystemProperties;
|
||||||
import android.os.test.TestLooper;
|
import android.os.test.TestLooper;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.CarrierConfigManager;
|
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.StateMachine;
|
||||||
import com.android.internal.util.test.BroadcastInterceptingContext;
|
import com.android.internal.util.test.BroadcastInterceptingContext;
|
||||||
import com.android.internal.util.test.FakeSettingsProvider;
|
import com.android.internal.util.test.FakeSettingsProvider;
|
||||||
import com.android.server.connectivity.MockableSystemProperties;
|
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -65,6 +68,8 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.MockitoSession;
|
||||||
|
import org.mockito.quality.Strictness;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
@@ -80,7 +85,6 @@ public final class EntitlementManagerTest {
|
|||||||
|
|
||||||
@Mock private CarrierConfigManager mCarrierConfigManager;
|
@Mock private CarrierConfigManager mCarrierConfigManager;
|
||||||
@Mock private Context mContext;
|
@Mock private Context mContext;
|
||||||
@Mock private MockableSystemProperties mSystemProperties;
|
|
||||||
@Mock private Resources mResources;
|
@Mock private Resources mResources;
|
||||||
@Mock private SharedLog mLog;
|
@Mock private SharedLog mLog;
|
||||||
@Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener;
|
@Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener;
|
||||||
@@ -95,6 +99,7 @@ public final class EntitlementManagerTest {
|
|||||||
private TestStateMachine mSM;
|
private TestStateMachine mSM;
|
||||||
private WrappedEntitlementManager mEnMgr;
|
private WrappedEntitlementManager mEnMgr;
|
||||||
private TetheringConfiguration mConfig;
|
private TetheringConfiguration mConfig;
|
||||||
|
private MockitoSession mMockingSession;
|
||||||
|
|
||||||
private class MockContext extends BroadcastInterceptingContext {
|
private class MockContext extends BroadcastInterceptingContext {
|
||||||
MockContext(Context base) {
|
MockContext(Context base) {
|
||||||
@@ -118,8 +123,8 @@ public final class EntitlementManagerTest {
|
|||||||
public int silentProvisionCount = 0;
|
public int silentProvisionCount = 0;
|
||||||
|
|
||||||
public WrappedEntitlementManager(Context ctx, StateMachine target,
|
public WrappedEntitlementManager(Context ctx, StateMachine target,
|
||||||
SharedLog log, int what, MockableSystemProperties systemProperties) {
|
SharedLog log, int what) {
|
||||||
super(ctx, target, log, what, systemProperties);
|
super(ctx, target, log, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
@@ -144,6 +149,15 @@ public final class EntitlementManagerTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
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))
|
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
|
||||||
.thenReturn(new String[0]);
|
.thenReturn(new String[0]);
|
||||||
@@ -161,8 +175,7 @@ public final class EntitlementManagerTest {
|
|||||||
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
|
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
|
||||||
mMockContext = new MockContext(mContext);
|
mMockContext = new MockContext(mContext);
|
||||||
mSM = new TestStateMachine();
|
mSM = new TestStateMachine();
|
||||||
mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE,
|
mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE);
|
||||||
mSystemProperties);
|
|
||||||
mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
|
mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
|
||||||
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
|
||||||
mEnMgr.setTetheringConfigurationFetcher(() -> {
|
mEnMgr.setTetheringConfigurationFetcher(() -> {
|
||||||
@@ -176,6 +189,7 @@ public final class EntitlementManagerTest {
|
|||||||
mSM.quit();
|
mSM.quit();
|
||||||
mSM = null;
|
mSM = null;
|
||||||
}
|
}
|
||||||
|
mMockingSession.finishMocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupForRequiredProvisioning() {
|
private void setupForRequiredProvisioning() {
|
||||||
@@ -184,9 +198,6 @@ public final class EntitlementManagerTest {
|
|||||||
.thenReturn(PROVISIONING_APP_NAME);
|
.thenReturn(PROVISIONING_APP_NAME);
|
||||||
when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
|
when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
|
||||||
.thenReturn(PROVISIONING_NO_UI_APP_NAME);
|
.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.
|
// Act like the CarrierConfigManager is present and ready unless told otherwise.
|
||||||
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
|
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
|
||||||
.thenReturn(mCarrierConfigManager);
|
.thenReturn(mCarrierConfigManager);
|
||||||
@@ -244,7 +255,7 @@ public final class EntitlementManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetLastEntitlementCacheValue() throws Exception {
|
public void testRequestLastEntitlementCacheValue() throws Exception {
|
||||||
final CountDownLatch mCallbacklatch = new CountDownLatch(1);
|
final CountDownLatch mCallbacklatch = new CountDownLatch(1);
|
||||||
// 1. Entitlement check is not required.
|
// 1. Entitlement check is not required.
|
||||||
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
|
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
|
||||||
@@ -255,7 +266,7 @@ public final class EntitlementManagerTest {
|
|||||||
mCallbacklatch.countDown();
|
mCallbacklatch.countDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertEquals(0, mEnMgr.uiProvisionCount);
|
assertEquals(0, mEnMgr.uiProvisionCount);
|
||||||
@@ -270,7 +281,7 @@ public final class EntitlementManagerTest {
|
|||||||
mCallbacklatch.countDown();
|
mCallbacklatch.countDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
|
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertEquals(0, mEnMgr.uiProvisionCount);
|
assertEquals(0, mEnMgr.uiProvisionCount);
|
||||||
@@ -284,7 +295,7 @@ public final class EntitlementManagerTest {
|
|||||||
mCallbacklatch.countDown();
|
mCallbacklatch.countDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertEquals(1, mEnMgr.uiProvisionCount);
|
assertEquals(1, mEnMgr.uiProvisionCount);
|
||||||
@@ -298,7 +309,7 @@ public final class EntitlementManagerTest {
|
|||||||
mCallbacklatch.countDown();
|
mCallbacklatch.countDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
|
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertEquals(0, mEnMgr.uiProvisionCount);
|
assertEquals(0, mEnMgr.uiProvisionCount);
|
||||||
@@ -312,7 +323,7 @@ public final class EntitlementManagerTest {
|
|||||||
mCallbacklatch.countDown();
|
mCallbacklatch.countDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertEquals(1, mEnMgr.uiProvisionCount);
|
assertEquals(1, mEnMgr.uiProvisionCount);
|
||||||
@@ -326,7 +337,7 @@ public final class EntitlementManagerTest {
|
|||||||
mCallbacklatch.countDown();
|
mCallbacklatch.countDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertEquals(0, mEnMgr.uiProvisionCount);
|
assertEquals(0, mEnMgr.uiProvisionCount);
|
||||||
@@ -339,7 +350,7 @@ public final class EntitlementManagerTest {
|
|||||||
mCallbacklatch.countDown();
|
mCallbacklatch.countDown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
|
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
callbackTimeoutHelper(mCallbacklatch);
|
callbackTimeoutHelper(mCallbacklatch);
|
||||||
assertEquals(0, mEnMgr.uiProvisionCount);
|
assertEquals(0, mEnMgr.uiProvisionCount);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user