summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorWei Wang <weiwa@google.com>2017-04-28 01:25:12 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-04-28 01:25:12 +0000
commit5be1372a78bd45091d1fd656ef71578d530cda82 (patch)
tree31cc396447c43791cdf457886b622cecee5f7100 /service
parentabeba8806b30d4109c22d5c3ee86bebeb7a09e7a (diff)
parent1ddaf32d5cbac5c9237a86eb214397df7c33bc00 (diff)
Merge "Throttle wifi scan requests from background apps (2/2)." into oc-dev am: a6ff93d77c
am: 1ddaf32d5c Change-Id: Id77304dc8f987653cc4820b3ca9ee336befb179a
Diffstat (limited to 'service')
-rw-r--r--service/java/com/android/server/wifi/WifiServiceImpl.java153
1 files changed, 133 insertions, 20 deletions
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index b1569c17d..ed1b49314 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -30,6 +30,8 @@ import static com.android.server.wifi.WifiController.CMD_USER_PRESENT;
import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
import android.Manifest;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
@@ -74,12 +76,13 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -131,15 +134,26 @@ public class WifiServiceImpl extends IWifiManager.Stub {
private static final String DUMP_ARG_SET_IPREACH_DISCONNECT_ENABLED = "enabled";
private static final String DUMP_ARG_SET_IPREACH_DISCONNECT_DISABLED = "disabled";
+ // Default scan background throttling interval if not overriden in settings
+ private static final long DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
+
+ // Apps with importance higher than this value is considered as background app.
+ private static final int BACKGROUND_IMPORTANCE_CUTOFF =
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+
final WifiStateMachine mWifiStateMachine;
private final Context mContext;
private final FrameworkFacade mFacade;
+ private final Clock mClock;
+ private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
private final PowerManager mPowerManager;
private final AppOpsManager mAppOps;
private final UserManager mUserManager;
+ private final ActivityManager mActivityManager;
private final WifiCountryCode mCountryCode;
+ private long mBackgroundThrottleInterval;
// Debug counter tracking scan requests sent by WifiManager
private int scanRequestCounter = 0;
@@ -156,9 +170,12 @@ public class WifiServiceImpl extends IWifiManager.Stub {
/* Backup/Restore Module */
private final WifiBackupRestore mWifiBackupRestore;
- private WifiScanner mWifiScanner;
+ // Map of package name of background scan apps and last scan timestamp.
+ private final ArrayMap<String, Long> mLastScanTimestamps;
+ private WifiScanner mWifiScanner;
private WifiLog mLog;
+
/**
* Asynchronous channel to WifiStateMachine
*/
@@ -328,6 +345,7 @@ public class WifiServiceImpl extends IWifiManager.Stub {
public WifiServiceImpl(Context context, WifiInjector wifiInjector, AsyncChannel asyncChannel) {
mContext = context;
mWifiInjector = wifiInjector;
+ mClock = wifiInjector.getClock();
mFacade = mWifiInjector.getFrameworkFacade();
mWifiMetrics = mWifiInjector.getWifiMetrics();
@@ -339,6 +357,7 @@ public class WifiServiceImpl extends IWifiManager.Stub {
mSettingsStore = mWifiInjector.getWifiSettingsStore();
mPowerManager = mContext.getSystemService(PowerManager.class);
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mCertManager = mWifiInjector.getWifiCertManager();
mWifiLockManager = mWifiInjector.getWifiLockManager();
mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager();
@@ -354,6 +373,9 @@ public class WifiServiceImpl extends IWifiManager.Stub {
mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil();
mLog = mWifiInjector.makeLog(TAG);
mFrameworkFacade = wifiInjector.getFrameworkFacade();
+ mLastScanTimestamps = new ArrayMap<>();
+ updateBackgroundThrottleInterval();
+ updateBackgroundThrottlingWhitelist();
enableVerboseLoggingInternal(getVerboseLoggingLevel());
}
@@ -387,6 +409,7 @@ public class WifiServiceImpl extends IWifiManager.Stub {
(wifiEnabled ? "enabled" : "disabled"));
registerForScanModeChange();
+ registerForBackgroundThrottleChanges();
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
@@ -461,11 +484,25 @@ public class WifiServiceImpl extends IWifiManager.Stub {
*
* @param settings If null, use default parameter, i.e. full scan.
* @param workSource If null, all blame is given to the calling uid.
+ * @param packageName Package name of the app that requests wifi scan.
*/
@Override
- public void startScan(ScanSettings settings, WorkSource workSource) {
+ public void startScan(ScanSettings settings, WorkSource workSource, String packageName) {
enforceChangePermission();
+
mLog.trace("startScan uid=%").c(Binder.getCallingUid()).flush();
+ // Check and throttle background apps for wifi scan.
+ if (isRequestFromBackground(packageName)) {
+ long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L);
+ long elapsedRealtime = mClock.getElapsedSinceBootMillis();
+
+ if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < mBackgroundThrottleInterval) {
+ sendFailedScanBroadcast();
+ return;
+ }
+ // Proceed with the scan request and record the time.
+ mLastScanTimestamps.put(packageName, elapsedRealtime);
+ }
synchronized (this) {
if (mWifiScanner == null) {
mWifiScanner = mWifiInjector.getWifiScanner();
@@ -474,21 +511,11 @@ public class WifiServiceImpl extends IWifiManager.Stub {
// Need to send an immediate scan result broadcast in case the
// caller is waiting for a result ..
- // clear calling identity to send broadcast
- long callingIdentity = Binder.clearCallingIdentity();
- try {
- // TODO: investigate if the logic to cancel scans when idle can move to
- // WifiScanningServiceImpl. This will 1 - clean up WifiServiceImpl and 2 -
- // avoid plumbing an awkward path to report a cancelled/failed scan. This will
- // be sent directly until b/31398592 is fixed.
- Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- } finally {
- // restore calling identity
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ // TODO: investigate if the logic to cancel scans when idle can move to
+ // WifiScanningServiceImpl. This will 1 - clean up WifiServiceImpl and 2 -
+ // avoid plumbing an awkward path to report a cancelled/failed scan. This will
+ // be sent directly until b/31398592 is fixed.
+ sendFailedScanBroadcast();
mScanPending = true;
return;
}
@@ -513,6 +540,45 @@ public class WifiServiceImpl extends IWifiManager.Stub {
settings, workSource);
}
+ // Send a failed scan broadcast to indicate the current scan request failed.
+ private void sendFailedScanBroadcast() {
+ // clear calling identity to send broadcast
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ // restore calling identity
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+
+ }
+
+ // Check if the request comes from background.
+ private boolean isRequestFromBackground(String packageName) {
+ // Requests from system or wifi are not background.
+ if (Binder.getCallingUid() == Process.SYSTEM_UID
+ || Binder.getCallingUid() == Process.WIFI_UID) {
+ return false;
+ }
+ mAppOps.checkPackage(Binder.getCallingUid(), packageName);
+ if (mBackgroundThrottlePackageWhitelist.contains(packageName)) {
+ return false;
+ }
+
+ // getPackageImportance requires PACKAGE_USAGE_STATS permission, so clearing the incoming
+ // identify so the permission check can be done on system process where wifi runs in.
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return mActivityManager.getPackageImportance(packageName)
+ > BACKGROUND_IMPORTANCE_CUTOFF;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
@Override
public String getCurrentNetworkWpsNfcConfigurationToken() {
enforceConnectivityInternalPermission();
@@ -541,7 +607,8 @@ public class WifiServiceImpl extends IWifiManager.Stub {
}
if (doScan) {
// Someone requested a scan while we were idle; do a full scan now.
- startScan(null, null);
+ // The package name doesn't matter as the request comes from System UID.
+ startScan(null, null, "");
}
}
@@ -953,7 +1020,7 @@ public class WifiServiceImpl extends IWifiManager.Stub {
}
// Convert the LinkLayerStats into EnergyActivity
- energyInfo = new WifiActivityEnergyInfo(SystemClock.elapsedRealtime(),
+ energyInfo = new WifiActivityEnergyInfo(mClock.getElapsedSinceBootMillis(),
WifiActivityEnergyInfo.STACK_STATE_STATE_IDLE, stats.tx_time,
txTimePerLevel, stats.rx_time, rxIdleTime, energyUsed);
}
@@ -1588,6 +1655,52 @@ public class WifiServiceImpl extends IWifiManager.Stub {
mFrameworkFacade.registerContentObserver(mContext,
Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
false, contentObserver);
+
+ }
+
+ // Monitors settings changes related to background wifi scan throttling.
+ private void registerForBackgroundThrottleChanges() {
+ mFrameworkFacade.registerContentObserver(
+ mContext,
+ Settings.Global.getUriFor(
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS),
+ false,
+ new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateBackgroundThrottleInterval();
+ }
+ }
+ );
+ mFrameworkFacade.registerContentObserver(
+ mContext,
+ Settings.Global.getUriFor(
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
+ false,
+ new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateBackgroundThrottlingWhitelist();
+ }
+ }
+ );
+ }
+
+ private void updateBackgroundThrottleInterval() {
+ mBackgroundThrottleInterval = mFrameworkFacade.getLongSetting(
+ mContext,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
+ DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS);
+ }
+
+ private void updateBackgroundThrottlingWhitelist() {
+ String setting = mFrameworkFacade.getStringSetting(
+ mContext,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+ mBackgroundThrottlePackageWhitelist.clear();
+ if (setting != null) {
+ mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(",")));
+ }
}
private void registerForBroadcasts() {