diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2c1796370558d..feb9cfe9d21f5 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7498,6 +7498,15 @@ public final class Settings { */ public static final String TETHER_DUN_APN = "tether_dun_apn"; + /** + * Used to disable trying to talk to any available tethering offload HAL. + * + * Integer values are interpreted as boolean, and the absence of an explicit setting + * is interpreted as |false|. + * @hide + */ + public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled"; + /** * List of carrier apps which are whitelisted to prompt the user for install when * a sim card with matching uicc carrier privilege rules is inserted. diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 80885543288d1..dffa670f6e7a2 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -199,6 +199,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering final Handler smHandler = mTetherMasterSM.getHandler(); mOffloadController = new OffloadController(smHandler, deps.getOffloadHardwareInterface(smHandler, mLog), + mContext.getContentResolver(), mLog); mUpstreamNetworkMonitor = new UpstreamNetworkMonitor( mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK, mLog); diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index 12899d803ca43..c64e70516abc6 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -16,9 +16,13 @@ package com.android.server.connectivity.tethering; +import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; + +import android.content.ContentResolver; import android.net.LinkProperties; -import android.os.Handler; import android.net.util.SharedLog; +import android.os.Handler; +import android.provider.Settings; /** * A class to encapsulate the business logic of programming the tethering @@ -31,19 +35,22 @@ public class OffloadController { private final Handler mHandler; private final OffloadHardwareInterface mHwInterface; + private final ContentResolver mContentResolver; private final SharedLog mLog; private boolean mConfigInitialized; private boolean mControlInitialized; private LinkProperties mUpstreamLinkProperties; - public OffloadController(Handler h, OffloadHardwareInterface hwi, SharedLog log) { + public OffloadController(Handler h, OffloadHardwareInterface hwi, + ContentResolver contentResolver, SharedLog log) { mHandler = h; mHwInterface = hwi; + mContentResolver = contentResolver; mLog = log.forSubComponent(TAG); } public void start() { - if (started()) return; + if (isOffloadDisabled() || started()) return; if (!mConfigInitialized) { mConfigInitialized = mHwInterface.initOffloadConfig(); @@ -91,6 +98,11 @@ public class OffloadController { // TODO: public void addDownStream(...) + private boolean isOffloadDisabled() { + // Defaults to |false| if not present. + return (Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0) != 0); + } + private boolean started() { return mConfigInitialized && mControlInitialized; } diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index d5a0b864583a8..241b828d8f4dd 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; @@ -52,11 +53,14 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.os.test.TestLooper; import android.os.UserHandle; +import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.telephony.CarrierConfigManager; +import android.test.mock.MockContentResolver; import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.connectivity.tethering.OffloadHardwareInterface; import com.android.server.connectivity.tethering.TetheringDependencies; @@ -96,6 +100,7 @@ public class TetheringTest { private Vector mIntents; private BroadcastInterceptingContext mServiceContext; + private MockContentResolver mContentResolver; private BroadcastReceiver mBroadcastReceiver; private Tethering mTethering; @@ -104,6 +109,12 @@ public class TetheringTest { super(base); } + @Override + public ContentResolver getContentResolver() { return mContentResolver; } + + @Override + public String getPackageName() { return "TetheringTest"; } + @Override public Resources getResources() { return mResources; } @@ -134,6 +145,8 @@ public class TetheringTest { .thenReturn(new InterfaceConfiguration()); mServiceContext = new MockContext(mContext); + mContentResolver = new MockContentResolver(mServiceContext); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); mIntents = new Vector<>(); mBroadcastReceiver = new BroadcastReceiver() { @Override diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java new file mode 100644 index 0000000000000..fb7971e1e1046 --- /dev/null +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -0,0 +1,117 @@ +/* + * 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 static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.util.SharedLog; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.test.mock.MockContentResolver; +import com.android.internal.util.test.FakeSettingsProvider; + +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class OffloadControllerTest { + + @Mock private OffloadHardwareInterface mHardware; + @Mock private Context mContext; + private MockContentResolver mContentResolver; + + @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mContentResolver = new MockContentResolver(mContext); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + when(mContext.getPackageName()).thenReturn("OffloadControllerTest"); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + } + + private void setupFunctioningHardwareInterface() { + when(mHardware.initOffloadConfig()).thenReturn(true); + when(mHardware.initOffloadControl(any(OffloadHardwareInterface.ControlCallback.class))) + .thenReturn(true); + } + + @Test + public void testNoSettingsValueAllowsStart() { + setupFunctioningHardwareInterface(); + try { + Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED); + fail(); + } catch (SettingNotFoundException expected) {} + + final OffloadController offload = + new OffloadController(null, mHardware, mContentResolver, new SharedLog("test")); + offload.start(); + + final InOrder inOrder = inOrder(mHardware); + inOrder.verify(mHardware, times(1)).initOffloadConfig(); + inOrder.verify(mHardware, times(1)).initOffloadControl( + any(OffloadHardwareInterface.ControlCallback.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testSettingsAllowsStart() { + setupFunctioningHardwareInterface(); + Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); + + final OffloadController offload = + new OffloadController(null, mHardware, mContentResolver, new SharedLog("test")); + offload.start(); + + final InOrder inOrder = inOrder(mHardware); + inOrder.verify(mHardware, times(1)).initOffloadConfig(); + inOrder.verify(mHardware, times(1)).initOffloadControl( + any(OffloadHardwareInterface.ControlCallback.class)); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testSettingsDisablesStart() { + setupFunctioningHardwareInterface(); + Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1); + + final OffloadController offload = + new OffloadController(null, mHardware, mContentResolver, new SharedLog("test")); + offload.start(); + + final InOrder inOrder = inOrder(mHardware); + inOrder.verify(mHardware, never()).initOffloadConfig(); + inOrder.verify(mHardware, never()).initOffloadControl(anyObject()); + inOrder.verifyNoMoreInteractions(); + } +}