blob: c2e1617cd82a52c87590b25baee44c8e0fe78275 [file] [log] [blame]
/*
* 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.networkstack.tethering;
import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_INVALID;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
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 com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.SharedLog;
import com.android.testutils.DevSdkIgnoreRule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class EntitlementManagerTest {
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
private static final String PROVISIONING_APP_RESPONSE = "app_response";
private static final String TEST_PACKAGE_NAME = "com.android.tethering.test";
private static final String FAILED_TETHERING_REASON = "Tethering provisioning failed.";
private static final int RECHECK_TIMER_HOURS = 24;
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private Context mContext;
@Mock private Resources mResources;
@Mock private SharedLog mLog;
@Mock private PackageManager mPm;
@Mock private EntitlementManager
.OnTetherProvisioningFailedListener mTetherProvisioningFailedListener;
@Mock private AlarmManager mAlarmManager;
@Mock private PendingIntent mAlarmIntent;
@Rule
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
// Like so many Android system APIs, these cannot be mocked because it is marked final.
// We have to use the real versions.
private final PersistableBundle mCarrierConfig = new PersistableBundle();
private final TestLooper mLooper = new TestLooper();
private MockContext mMockContext;
private Runnable mPermissionChangeCallback;
private WrappedEntitlementManager mEnMgr;
private TetheringConfiguration mConfig;
private MockitoSession mMockingSession;
private class MockContext extends BroadcastInterceptingContext {
MockContext(Context base) {
super(base);
}
@Override
public Resources getResources() {
return mResources;
}
@Override
public Object getSystemService(String name) {
if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
return super.getSystemService(name);
}
}
public class WrappedEntitlementManager extends EntitlementManager {
public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN;
public int uiProvisionCount = 0;
public int silentProvisionCount = 0;
public WrappedEntitlementManager(Context ctx, Handler h, SharedLog log,
Runnable callback) {
super(ctx, h, log, callback);
}
public void reset() {
fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN;
uiProvisionCount = 0;
silentProvisionCount = 0;
}
@Override
protected Intent runUiTetherProvisioning(int type,
final TetheringConfiguration config, final ResultReceiver receiver) {
Intent intent = super.runUiTetherProvisioning(type, config, receiver);
assertUiTetherProvisioningIntent(type, config, receiver, intent);
uiProvisionCount++;
receiver.send(fakeEntitlementResult, null);
return intent;
}
private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config,
final ResultReceiver receiver, final Intent intent) {
assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction());
assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
final String[] appName = intent.getStringArrayExtra(
EXTRA_TETHER_UI_PROVISIONING_APP_NAME);
assertEquals(PROVISIONING_APP_NAME.length, appName.length);
for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) {
assertEquals(PROVISIONING_APP_NAME[i], appName[i]);
}
assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK));
assertEquals(config.activeDataSubId,
intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
}
@Override
protected Intent runSilentTetherProvisioning(int type,
final TetheringConfiguration config, final ResultReceiver receiver) {
Intent intent = super.runSilentTetherProvisioning(type, config, receiver);
assertSilentTetherProvisioning(type, config, intent);
silentProvisionCount++;
addDownstreamMapping(type, fakeEntitlementResult);
return intent;
}
private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config,
final Intent intent) {
assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false));
assertEquals(PROVISIONING_NO_UI_APP_NAME,
intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION));
assertEquals(PROVISIONING_APP_RESPONSE,
intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE));
assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK));
assertEquals(config.activeDataSubId,
intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
}
@Override
PendingIntent createRecheckAlarmIntent(final String pkgName) {
assertEquals(TEST_PACKAGE_NAME, pkgName);
return mAlarmIntent;
}
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mMockingSession = mockitoSession()
.initMocks(this)
.mockStatic(SystemProperties.class)
.mockStatic(DeviceConfig.class)
.strictness(Strictness.WARN)
.startMocking();
// Don't disable tethering provisioning unless requested.
doReturn(false).when(
() -> SystemProperties.getBoolean(
eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
doReturn(null).when(
() -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), anyString()));
doReturn(mPm).when(mContext).getPackageManager();
doReturn(TEST_PACKAGE_NAME).when(mContext).getPackageName();
doReturn(new PackageInfo()).when(mPm).getPackageInfo(anyString(), anyInt());
doReturn(new ModuleInfo()).when(mPm).getModuleInfo(anyString(), anyInt());
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
when(mResources.getIntArray(R.array.config_tether_upstream_types))
.thenReturn(new int[0]);
when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
false);
when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
mMockContext = new MockContext(mContext);
mPermissionChangeCallback = spy(() -> { });
mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog,
mPermissionChangeCallback);
mEnMgr.setOnTetherProvisioningFailedListener(mTetherProvisioningFailedListener);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mEnMgr.setTetheringConfigurationFetcher(() -> {
return mConfig;
});
}
@After
public void tearDown() throws Exception {
mMockingSession.finishMocking();
}
private void setupForRequiredProvisioning() {
// Produce some acceptable looking provision app setting if requested.
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(PROVISIONING_APP_NAME);
when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
.thenReturn(PROVISIONING_NO_UI_APP_NAME);
when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn(
PROVISIONING_APP_RESPONSE);
when(mResources.getInteger(R.integer.config_mobile_hotspot_provision_check_period))
.thenReturn(RECHECK_TIMER_HOURS);
// Act like the CarrierConfigManager is present and ready unless told otherwise.
mockService(Context.CARRIER_CONFIG_SERVICE,
CarrierConfigManager.class, mCarrierConfigManager);
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
}
private void setupCarrierConfig(boolean carrierSupported) {
mCarrierConfig.putBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, carrierSupported);
}
private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
when(mMockContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
when(mMockContext.getSystemService(serviceName)).thenReturn(service);
}
@Test
public void canRequireProvisioning() {
setupForRequiredProvisioning();
assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
}
@Test
public void provisioningNotRequiredWhenAppNotFound() {
setupForRequiredProvisioning();
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(null);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(new String[] {"malformedApp"});
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
}
@Test
public void testRequestLastEntitlementCacheValue() throws Exception {
// 1. Entitlement check is not required.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
ResultReceiver receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
setupForRequiredProvisioning();
// 2. No cache value and don't need to run entitlement check.
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 3. No cache value and ui entitlement check is needed.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement
// check.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 6. Cache value is TETHER_ERROR_NO_ERROR.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 7. Test get value for other downstream type.
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 8. Test get value for invalid downstream type.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
}
private void assertPermissionChangeCallback(InOrder inOrder) {
inOrder.verify(mPermissionChangeCallback, times(1)).run();
}
private void assertNoPermissionChange(InOrder inOrder) {
inOrder.verifyNoMoreInteractions();
}
@Test
public void verifyPermissionResult() {
final InOrder inOrder = inOrder(mPermissionChangeCallback);
setupForRequiredProvisioning();
mEnMgr.notifyUpstream(true);
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
// Permitted: true -> false
assertPermissionChangeCallback(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
mLooper.dispatchAll();
// Permitted: false -> false
assertNoPermissionChange(inOrder);
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
// Permitted: false -> true
assertPermissionChangeCallback(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
}
@Test
public void verifyPermissionIfAllNotApproved() {
final InOrder inOrder = inOrder(mPermissionChangeCallback);
setupForRequiredProvisioning();
mEnMgr.notifyUpstream(true);
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
// Permitted: true -> false
assertPermissionChangeCallback(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
mLooper.dispatchAll();
// Permitted: false -> false
assertNoPermissionChange(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
mLooper.dispatchAll();
// Permitted: false -> false
assertNoPermissionChange(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
}
@Test
public void verifyPermissionIfAnyApproved() {
final InOrder inOrder = inOrder(mPermissionChangeCallback);
setupForRequiredProvisioning();
mEnMgr.notifyUpstream(true);
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
// Permitted: true -> true
assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
mLooper.dispatchAll();
// Permitted: true -> true
assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
mLooper.dispatchAll();
// Permitted: true -> false
assertPermissionChangeCallback(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
}
@Test
public void verifyPermissionWhenProvisioningNotStarted() {
final InOrder inOrder = inOrder(mPermissionChangeCallback);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
assertNoPermissionChange(inOrder);
setupForRequiredProvisioning();
assertFalse(mEnMgr.isCellularUpstreamPermitted());
assertNoPermissionChange(inOrder);
}
@Test
public void testRunTetherProvisioning() {
final InOrder inOrder = inOrder(mPermissionChangeCallback);
setupForRequiredProvisioning();
// 1. start ui provisioning, upstream is mobile
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
// Permitted: true -> true
assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
// 2. start no-ui provisioning
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(1, mEnMgr.silentProvisionCount);
// Permitted: true -> true
assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
// 3. tear down mobile, then start ui provisioning
mEnMgr.notifyUpstream(false);
mLooper.dispatchAll();
mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
assertNoPermissionChange(inOrder);
mEnMgr.reset();
// 4. switch upstream back to mobile
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
// Permitted: true -> true
assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
// 5. tear down mobile, then switch SIM
mEnMgr.notifyUpstream(false);
mLooper.dispatchAll();
mEnMgr.reevaluateSimCardProvisioning(mConfig);
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
assertNoPermissionChange(inOrder);
mEnMgr.reset();
// 6. switch upstream back to mobile again
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(3, mEnMgr.silentProvisionCount);
// Permitted: true -> false
assertPermissionChangeCallback(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
// 7. start ui provisioning, upstream is mobile, downstream is ethernet
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true);
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
// Permitted: false -> true
assertPermissionChangeCallback(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
// 8. downstream is invalid
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
assertNoPermissionChange(inOrder);
mEnMgr.reset();
}
@Test
public void testCallStopTetheringWhenUiProvisioningFail() {
setupForRequiredProvisioning();
verify(mTetherProvisioningFailedListener, times(0))
.onTetherProvisioningFailed(TETHERING_WIFI, FAILED_TETHERING_REASON);
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
verify(mTetherProvisioningFailedListener, times(1))
.onTetherProvisioningFailed(TETHERING_WIFI, FAILED_TETHERING_REASON);
}
@Test
public void testsetExemptedDownstreamType() throws Exception {
setupForRequiredProvisioning();
// Cellular upstream is not permitted when no entitlement result.
assertFalse(mEnMgr.isCellularUpstreamPermitted());
// If there is exempted downstream and no other non-exempted downstreams, cellular is
// permitted.
mEnMgr.setExemptedDownstreamType(TETHERING_WIFI);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
// If second downstream run entitlement check fail, cellular upstream is not permitted.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
mLooper.dispatchAll();
assertFalse(mEnMgr.isCellularUpstreamPermitted());
// When second downstream is down, exempted downstream can use cellular upstream.
assertEquals(1, mEnMgr.uiProvisionCount);
verify(mTetherProvisioningFailedListener).onTetherProvisioningFailed(TETHERING_USB,
FAILED_TETHERING_REASON);
mEnMgr.stopProvisioningIfNeeded(TETHERING_USB);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
}
private void sendProvisioningRecheckAlarm() {
final Intent intent = new Intent(EntitlementManager.ACTION_PROVISIONING_ALARM);
mMockContext.sendBroadcastAsUser(intent, UserHandle.ALL);
mLooper.dispatchAll();
}
@Test
public void testScheduleProvisioningReCheck() throws Exception {
setupForRequiredProvisioning();
assertFalse(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
assertTrue(mEnMgr.isCellularUpstreamPermitted());
verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(),
eq(mAlarmIntent));
reset(mAlarmManager);
sendProvisioningRecheckAlarm();
verify(mAlarmManager).cancel(eq(mAlarmIntent));
verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(),
eq(mAlarmIntent));
}
@Test
@IgnoreUpTo(SC_V2)
public void requestLatestTetheringEntitlementResult_carrierDoesNotSupport_noProvisionCount()
throws Exception {
setupCarrierConfig(false);
setupForRequiredProvisioning();
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
ResultReceiver receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
}
@Test
@IgnoreUpTo(SC_V2)
public void reevaluateSimCardProvisioning_carrierUnsupportAndSimswitch() {
setupForRequiredProvisioning();
// Start a tethering with cellular data without provisioning.
mEnMgr.notifyUpstream(true);
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false);
mLooper.dispatchAll();
// Tear down mobile, then switch SIM.
mEnMgr.notifyUpstream(false);
mLooper.dispatchAll();
setupCarrierConfig(false);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mEnMgr.reevaluateSimCardProvisioning(mConfig);
// Turn on upstream.
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
verify(mTetherProvisioningFailedListener)
.onTetherProvisioningFailed(TETHERING_WIFI, "Carrier does not support.");
}
@Test
@IgnoreUpTo(SC_V2)
public void startProvisioningIfNeeded_carrierUnsupport()
throws Exception {
setupCarrierConfig(false);
setupForRequiredProvisioning();
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
verify(mTetherProvisioningFailedListener, never())
.onTetherProvisioningFailed(TETHERING_WIFI, "Carrier does not support.");
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
verify(mTetherProvisioningFailedListener)
.onTetherProvisioningFailed(TETHERING_WIFI, "Carrier does not support.");
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
reset(mTetherProvisioningFailedListener);
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
verify(mTetherProvisioningFailedListener)
.onTetherProvisioningFailed(TETHERING_WIFI, "Carrier does not support.");
}
@Test
public void isTetherProvisioningRequired_carrierUnSupport() {
setupForRequiredProvisioning();
setupCarrierConfig(false);
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(new String[0]);
mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
if (SdkLevel.isAtLeastT()) {
assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
} else {
assertFalse(mEnMgr.isTetherProvisioningRequired(mConfig));
}
}
}