diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java index 60fcfd0437..4ca36df967 100644 --- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java +++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java @@ -70,7 +70,8 @@ public class EntitlementManager { @VisibleForTesting protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; - private static final String ACTION_PROVISIONING_ALARM = + @VisibleForTesting + protected static final String ACTION_PROVISIONING_ALARM = "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; private final ComponentName mSilentProvisioningService; @@ -410,20 +411,23 @@ public class EntitlementManager { return intent; } + @VisibleForTesting + PendingIntent createRecheckAlarmIntent() { + final Intent intent = new Intent(ACTION_PROVISIONING_ALARM); + return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE); + } + // Not needed to check if this don't run on the handler thread because it's private. - private void scheduleProvisioningRechecks(final TetheringConfiguration config) { + private void scheduleProvisioningRecheck(final TetheringConfiguration config) { if (mProvisioningRecheckAlarm == null) { final int period = config.provisioningCheckPeriod; if (period <= 0) return; - Intent intent = new Intent(ACTION_PROVISIONING_ALARM); - mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent, - PendingIntent.FLAG_IMMUTABLE); + mProvisioningRecheckAlarm = createRecheckAlarmIntent(); AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( Context.ALARM_SERVICE); - long periodMs = period * MS_PER_HOUR; - long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs; - alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs, + long triggerAtMillis = SystemClock.elapsedRealtime() + (period * MS_PER_HOUR); + alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, mProvisioningRecheckAlarm); } } @@ -437,6 +441,11 @@ public class EntitlementManager { } } + private void rescheduleProvisioningRecheck(final TetheringConfiguration config) { + cancelTetherProvisioningRechecks(); + scheduleProvisioningRecheck(config); + } + private void evaluateCellularPermission(final TetheringConfiguration config) { final boolean permitted = isCellularUpstreamPermitted(config); @@ -452,7 +461,7 @@ public class EntitlementManager { // Only schedule periodic re-check when tether is provisioned // and the result is ok. if (permitted && mCurrentEntitlementResults.size() > 0) { - scheduleProvisioningRechecks(config); + scheduleProvisioningRecheck(config); } else { cancelTetherProvisioningRechecks(); } @@ -493,6 +502,7 @@ public class EntitlementManager { if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) { mLog.log("Received provisioning alarm"); final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); + rescheduleProvisioningRecheck(config); reevaluateSimCardProvisioning(config); } } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java index 442be1ea1f..46ce82cf7a 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java @@ -43,14 +43,18 @@ 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.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; @@ -63,6 +67,7 @@ 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; @@ -91,6 +96,7 @@ public final class EntitlementManagerTest { 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 int RECHECK_TIMER_HOURS = 24; @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private Context mContext; @@ -98,12 +104,14 @@ public final class EntitlementManagerTest { @Mock private SharedLog mLog; @Mock private PackageManager mPm; @Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener; + @Mock private AlarmManager mAlarmManager; + @Mock private PendingIntent mAlarmIntent; // 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 Context mMockContext; + private MockContext mMockContext; private Runnable mPermissionChangeCallback; private WrappedEntitlementManager mEnMgr; @@ -119,6 +127,13 @@ public final class EntitlementManagerTest { 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 { @@ -184,6 +199,11 @@ public final class EntitlementManagerTest { assertEquals(config.activeDataSubId, intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID)); } + + @Override + PendingIntent createRecheckAlarmIntent() { + return mAlarmIntent; + } } @Before @@ -245,6 +265,8 @@ public final class EntitlementManagerTest { .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. when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE)) .thenReturn(mCarrierConfigManager); @@ -629,4 +651,31 @@ public final class EntitlementManagerTest { 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)); + } }