diff options
-rw-r--r-- | service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java | 56 | ||||
-rw-r--r-- | tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java | 69 |
2 files changed, 118 insertions, 7 deletions
diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java index a00908259..3267cfd4c 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java @@ -22,6 +22,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.Manifest; import android.app.AlarmManager; import android.content.Context; +import android.location.LocationManager; import android.net.wifi.IWifiScanner; import android.net.wifi.ScanResult; import android.net.wifi.WifiScanner; @@ -133,6 +134,51 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { "NetworkStack"); } + // Helper method to check if the incoming message is for a privileged request. + private boolean isPrivilegedMessage(int msgWhat) { + return (msgWhat == WifiScanner.CMD_ENABLE + || msgWhat == WifiScanner.CMD_DISABLE + || msgWhat == WifiScanner.CMD_START_PNO_SCAN + || msgWhat == WifiScanner.CMD_STOP_PNO_SCAN); + } + + // Retrieves a handle to LocationManager (if not already done) and check if location is enabled. + private boolean isLocationEnabled() { + if (mLocationManager == null) { + mLocationManager = + (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); + } + if (mLocationManager == null) return false; + return mLocationManager.isLocationEnabled(); + } + + /** + * Enforce the necessary client permissions for WifiScanner. + * If the client has NETWORK_STACK permission, then it can "always" send "any" request. + * If the client has only LOCATION_HARDWARE permission, then it can + * a) Only make scan related requests when location is turned on. + * b) Can never make one of the privileged requests. + * + * @param uid Uid of the client. + * @param msgWhat {@link Message#what} of the incoming request. + * @throws {@link SecurityException} if the client does not have the necessary permissions. + */ + private void enforcePermission(int uid, int msgWhat) throws SecurityException { + try { + enforceNetworkStack(uid); + } catch (SecurityException e) { + if (!isLocationEnabled()) { + // Location not enabled, only requests from clients with NETWORK_STACK allowed! + throw e; + } + if (isPrivilegedMessage(msgWhat)) { + // Privileged message, only requests from clients with NETWORK_STACK allowed! + throw e; + } + enforceLocationHardwarePermission(uid); + } + } + private class ClientHandler extends WifiHandler { ClientHandler(String tag, Looper looper) { @@ -188,13 +234,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } try { - if (msg.what == WifiScanner.CMD_ENABLE || msg.what == WifiScanner.CMD_DISABLE - || msg.what == WifiScanner.CMD_START_PNO_SCAN - || msg.what == WifiScanner.CMD_STOP_PNO_SCAN) { - enforceNetworkStack(msg.sendingUid); - } else { - enforceLocationHardwarePermission(msg.sendingUid); - } + enforcePermission(msg.sendingUid, msg.what); } catch (SecurityException e) { localLog("failed to authorize app: " + e); replyFailed(msg, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized"); @@ -287,6 +327,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private WifiSingleScanStateMachine mSingleScanStateMachine; private WifiPnoScanStateMachine mPnoScanStateMachine; private ClientHandler mClientHandler; + // This is retrieved lazily because location service is started after wifi scanner. + private LocationManager mLocationManager; private final IBatteryStats mBatteryStats; private final AlarmManager mAlarmManager; private final WifiMetrics mWifiMetrics; diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java index cf00069b5..1280013fe 100644 --- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java +++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java @@ -39,6 +39,7 @@ import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; @@ -55,6 +56,7 @@ import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.app.test.TestAlarmManager; import android.content.BroadcastReceiver; import android.content.Context; +import android.location.LocationManager; import android.net.wifi.ScanResult; import android.net.wifi.WifiScanner; import android.os.Binder; @@ -120,6 +122,7 @@ public class WifiScanningServiceTest { @Mock FrameworkFacade mFrameworkFacade; @Mock Clock mClock; @Spy FakeWifiLog mLog; + @Mock LocationManager mLocationManager; WifiMetrics mWifiMetrics; TestLooper mLooper; WifiScanningServiceImpl mWifiScanningServiceImpl; @@ -132,6 +135,8 @@ public class WifiScanningServiceTest { mAlarmManager = new TestAlarmManager(); when(mContext.getSystemService(Context.ALARM_SERVICE)) .thenReturn(mAlarmManager.getAlarmManager()); + when(mContext.getSystemService(Context.LOCATION_SERVICE)) + .thenReturn(mLocationManager); ChannelHelper channelHelper = new PresetKnownBandsChannelHelper( new int[]{2400, 2450}, @@ -393,6 +398,7 @@ public class WifiScanningServiceTest { private static final int MAX_AP_PER_SCAN = 16; private void startServiceAndLoadDriver() { mWifiScanningServiceImpl.startService(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); setupAndLoadDriver(TEST_MAX_SCAN_BUCKETS_IN_CAPABILITIES); } @@ -2462,6 +2468,7 @@ public class WifiScanningServiceTest { @Test public void rejectRestrictedMessagesFromNonPrivilegedApps() throws Exception { mWifiScanningServiceImpl.startService(); + mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog); Handler handler = mock(Handler.class); BidirectionalAsyncChannel controlChannel = connectChannel(handler); @@ -2498,4 +2505,66 @@ public class WifiScanningServiceTest { verify(mWifiScannerImplFactory, never()).create(any(), any(), any()); } + + /** + * Verifies that clients without NETWORK_STACK permission cannot issue any messages when + * location is turned off. + */ + @Test + public void rejectAllMessagesFromNonPrivilegedAppsWhenLocationIsTurnedOff() throws Exception { + // Start service & initialize it. + startServiceAndLoadDriver(); + // Location turned off. + when(mLocationManager.isLocationEnabled()).thenReturn(false); + + Handler handler = mock(Handler.class); + BidirectionalAsyncChannel controlChannel = connectChannel(handler); + + // Client doesn't have NETWORK_STACK permission. + doThrow(new SecurityException()).when(mContext).enforcePermission( + eq(Manifest.permission.NETWORK_STACK), anyInt(), eq(Binder.getCallingUid()), any()); + + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_START_SINGLE_SCAN)); + mLooper.dispatchAll(); + + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_GET_SCAN_RESULTS)); + mLooper.dispatchAll(); + + controlChannel.sendMessage(Message.obtain(null, WifiScanner.CMD_START_BACKGROUND_SCAN)); + mLooper.dispatchAll(); + + // All the above messages should have been rejected because the app doesn't have + // the privileged permissions & location is turned off. + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(handler, times(3)).handleMessage(messageCaptor.capture()); + assertFailedResponse(0, WifiScanner.REASON_NOT_AUTHORIZED, + "Not authorized", messageCaptor.getAllValues().get(0)); + assertFailedResponse(0, WifiScanner.REASON_NOT_AUTHORIZED, + "Not authorized", messageCaptor.getAllValues().get(1)); + assertFailedResponse(0, WifiScanner.REASON_NOT_AUTHORIZED, + "Not authorized", messageCaptor.getAllValues().get(2)); + + // Validate the initialization sequence. + verify(mWifiScannerImpl).getChannelHelper(); + verify(mWifiScannerImpl).getScanCapabilities(any()); + + // Ensure we didn't start any scans after. + verifyNoMoreInteractions(mWifiScannerImpl); + } + + /** + * Verifies that clients with NETWORK_STACK permission can issue any messages even when + * location is turned off. + */ + @Test + public void allowMessagesFromPrivilegedAppsWhenLocationIsTurnedOff() throws Exception { + // Location turned off. + when(mLocationManager.isLocationEnabled()).thenReturn(false); + // Client does have NETWORK_STACK permission. + doNothing().when(mContext).enforcePermission( + eq(Manifest.permission.NETWORK_STACK), anyInt(), eq(Binder.getCallingUid()), any()); + + sendSingleScanAllChannelsRequest(); + sendBackgroundScanBandRequest(); + } } |