diff options
4 files changed, 120 insertions, 18 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 001e09406e3a..42f80109e045 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.content.Intent; import android.os.UserManager; import android.service.quicksettings.Tile; +import android.util.Log; import android.widget.Switch; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -200,6 +201,14 @@ public class HotspotTile extends QSTileImpl<BooleanState> { mCallbackInfo.numConnectedDevices = numDevices; refreshState(mCallbackInfo); } + + @Override + public void onHotspotAvailabilityChanged(boolean available) { + if (!available) { + Log.d(TAG, "Tile removed. Hotspot no longer available"); + mHost.removeTile(getTileSpec()); + } + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java index 830b50e35490..8231f8b3a09b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java @@ -30,5 +30,6 @@ public interface HotspotController extends CallbackController<Callback>, Dumpabl interface Callback { void onHotspotChanged(boolean enabled, int numDevices); + default void onHotspotAvailabilityChanged(boolean available) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index df9c3f4d6e26..d0904049d85a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.policy; import android.app.ActivityManager; import android.content.Context; import android.net.ConnectivityManager; +import android.net.TetheringManager; import android.net.wifi.WifiClient; import android.net.wifi.WifiManager; import android.os.Handler; @@ -26,6 +27,8 @@ import android.os.HandlerExecutor; import android.os.UserManager; import android.util.Log; +import com.android.internal.util.ConcurrentUtils; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import java.io.FileDescriptor; @@ -46,36 +49,63 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final ArrayList<Callback> mCallbacks = new ArrayList<>(); - private final ConnectivityManager mConnectivityManager; + private final TetheringManager mTetheringManager; private final WifiManager mWifiManager; private final Handler mMainHandler; private final Context mContext; private int mHotspotState; private volatile int mNumConnectedDevices; + private volatile boolean mIsTetheringSupported; + private volatile boolean mHasTetherableWifiRegexs; private boolean mWaitingForTerminalState; + private TetheringManager.TetheringEventCallback mTetheringCallback = + new TetheringManager.TetheringEventCallback() { + @Override + public void onTetheringSupported(boolean supported) { + super.onTetheringSupported(supported); + if (mIsTetheringSupported != supported) { + mIsTetheringSupported = supported; + fireHotspotAvailabilityChanged(); + } + } + + @Override + public void onTetherableInterfaceRegexpsChanged( + TetheringManager.TetheringInterfaceRegexps reg) { + super.onTetherableInterfaceRegexpsChanged(reg); + final boolean newValue = reg.getTetherableWifiRegexs().size() != 0; + if (mHasTetherableWifiRegexs != newValue) { + mHasTetherableWifiRegexs = newValue; + fireHotspotAvailabilityChanged(); + } + } + }; + /** * Controller used to retrieve information related to a hotspot. */ @Inject - public HotspotControllerImpl(Context context, @Main Handler mainHandler) { + public HotspotControllerImpl(Context context, @Main Handler mainHandler, + @Background Handler backgroundHandler) { mContext = context; - mConnectivityManager = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + mTetheringManager = context.getSystemService(TetheringManager.class); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mMainHandler = mainHandler; + mTetheringManager.registerTetheringEventCallback( + new HandlerExecutor(backgroundHandler), mTetheringCallback); } @Override public boolean isHotspotSupported() { - return mConnectivityManager.isTetheringSupported() - && mConnectivityManager.getTetherableWifiRegexs().length != 0 + return mIsTetheringSupported && mHasTetherableWifiRegexs && UserManager.get(mContext).isUserAdmin(ActivityManager.getCurrentUser()); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("HotspotController state:"); + pw.print(" available="); pw.println(isHotspotSupported()); pw.print(" mHotspotState="); pw.println(stateToString(mHotspotState)); pw.print(" mNumConnectedDevices="); pw.println(mNumConnectedDevices); pw.print(" mWaitingForTerminalState="); pw.println(mWaitingForTerminalState); @@ -152,17 +182,18 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof if (enabled) { mWaitingForTerminalState = true; if (DEBUG) Log.d(TAG, "Starting tethering"); - mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, false, - new ConnectivityManager.OnStartTetheringCallback() { + mTetheringManager.startTethering(ConnectivityManager.TETHERING_WIFI, + ConcurrentUtils.DIRECT_EXECUTOR, + new TetheringManager.StartTetheringCallback() { @Override - public void onTetheringFailed() { + public void onTetheringFailed(final int result) { if (DEBUG) Log.d(TAG, "onTetheringFailed"); maybeResetSoftApState(); fireHotspotChangedCallback(); } }); } else { - mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); + mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI); } } @@ -177,10 +208,25 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof * (as it can be blocked). */ private void fireHotspotChangedCallback() { + List<Callback> list; synchronized (mCallbacks) { - for (Callback callback : mCallbacks) { - callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices); - } + list = new ArrayList<>(mCallbacks); + } + for (Callback callback : list) { + callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices); + } + } + + /** + * Sends a hotspot available changed callback. + */ + private void fireHotspotAvailabilityChanged() { + List<Callback> list; + synchronized (mCallbacks) { + list = new ArrayList<>(mCallbacks); + } + for (Callback callback : list) { + callback.onHotspotAvailabilityChanged(isHotspotSupported()); } } @@ -206,7 +252,7 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof switch (mHotspotState) { case WifiManager.WIFI_AP_STATE_FAILED: // TODO(b/110697252): must be called to reset soft ap state after failure - mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); + mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI); // Fall through case WifiManager.WIFI_AP_STATE_ENABLED: case WifiManager.WIFI_AP_STATE_DISABLED: diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java index 631c580a490d..cd91f22bb753 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -24,10 +26,12 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -import android.net.ConnectivityManager; +import android.net.TetheringManager; import android.net.wifi.WifiManager; import android.os.Handler; +import android.os.UserManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -38,11 +42,14 @@ import com.android.systemui.SysuiTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import java.util.ArrayList; +import java.util.Collections; import java.util.concurrent.Executor; @SmallTest @@ -51,13 +58,19 @@ import java.util.concurrent.Executor; public class HotspotControllerImplTest extends SysuiTestCase { @Mock - private ConnectivityManager mConnectivityManager; + private TetheringManager mTetheringManager; @Mock private WifiManager mWifiManager; @Mock + private UserManager mUserManager; + @Mock private HotspotController.Callback mCallback1; @Mock private HotspotController.Callback mCallback2; + @Mock + private TetheringManager.TetheringInterfaceRegexps mTetheringInterfaceRegexps; + @Captor + private ArgumentCaptor<TetheringManager.TetheringEventCallback> mTetheringCallbackCaptor; private HotspotControllerImpl mController; private TestableLooper mLooper; @@ -66,8 +79,13 @@ public class HotspotControllerImplTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mLooper = TestableLooper.get(this); - mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager); mContext.addMockSystemService(WifiManager.class, mWifiManager); + mContext.addMockSystemService(TetheringManager.class, mTetheringManager); + mContext.addMockSystemService(UserManager.class, mUserManager); + + when(mUserManager.isUserAdmin(anyInt())).thenReturn(true); + when(mTetheringInterfaceRegexps.getTetherableWifiRegexs()).thenReturn( + Collections.singletonList("test")); doAnswer((InvocationOnMock invocation) -> { ((WifiManager.SoftApCallback) invocation.getArgument(1)) @@ -76,7 +94,11 @@ public class HotspotControllerImplTest extends SysuiTestCase { }).when(mWifiManager).registerSoftApCallback(any(Executor.class), any(WifiManager.SoftApCallback.class)); - mController = new HotspotControllerImpl(mContext, new Handler(mLooper.getLooper())); + Handler handler = new Handler(mLooper.getLooper()); + + mController = new HotspotControllerImpl(mContext, handler, handler); + verify(mTetheringManager) + .registerTetheringEventCallback(any(), mTetheringCallbackCaptor.capture()); } @Test @@ -117,4 +139,28 @@ public class HotspotControllerImplTest extends SysuiTestCase { verify(mWifiManager, never()).unregisterSoftApCallback(any()); } + @Test + public void testDefault_hotspotNotSupported() { + assertFalse(mController.isHotspotSupported()); + } + + @Test + public void testHotspotSupported_rightConditions() { + mTetheringCallbackCaptor.getValue().onTetheringSupported(true); + mTetheringCallbackCaptor.getValue() + .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps); + + assertTrue(mController.isHotspotSupported()); + } + + @Test + public void testHotspotSupported_callbackCalledOnChange() { + mController.addCallback(mCallback1); + mTetheringCallbackCaptor.getValue().onTetheringSupported(true); + mTetheringCallbackCaptor.getValue() + .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps); + + verify(mCallback1).onHotspotAvailabilityChanged(true); + } + } |