am 3184b286: am fa4eda44: Merge "Remove network access for idle apps" into mnc-dev

* commit '3184b28648a782a3a037de90bc76f64839b91560':
  Remove network access for idle apps
This commit is contained in:
Amith Yamasani
2015-04-29 22:52:08 +00:00
committed by Android Git Automerger
7 changed files with 283 additions and 45 deletions

View File

@@ -41,6 +41,7 @@ import java.util.HashSet;
*/
public class NetworkPolicyManager {
/* POLICY_* are masks and can be ORed */
/** No specific network policy, use system default. */
public static final int POLICY_NONE = 0x0;
/** Reject network usage on metered networks when application in background. */
@@ -48,10 +49,17 @@ public class NetworkPolicyManager {
/** Allow network use (metered or not) in the background in battery save mode. */
public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2;
/* RULE_* are not masks and they must be exclusive */
/** All network traffic should be allowed. */
public static final int RULE_ALLOW_ALL = 0x0;
/** Reject traffic on metered networks. */
public static final int RULE_REJECT_METERED = 0x1;
/** Reject traffic on all networks. */
public static final int RULE_REJECT_ALL = 0x2;
public static final int FIREWALL_RULE_DEFAULT = 0;
public static final int FIREWALL_RULE_ALLOW = 1;
public static final int FIREWALL_RULE_DENY = 2;
private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
@@ -80,7 +88,7 @@ public class NetworkPolicyManager {
* Set policy flags for specific UID.
*
* @param policy {@link #POLICY_NONE} or combination of flags like
* {@link #POLICY_REJECT_METERED_BACKGROUND}, {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
* {@link #POLICY_REJECT_METERED_BACKGROUND} or {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
*/
public void setUidPolicy(int uid, int policy) {
try {
@@ -322,6 +330,8 @@ public class NetworkPolicyManager {
fout.write("[");
if ((rules & RULE_REJECT_METERED) != 0) {
fout.write("REJECT_METERED");
} else if ((rules & RULE_REJECT_ALL) != 0) {
fout.write("REJECT_ALL");
}
fout.write("]");
}

View File

@@ -342,7 +342,7 @@ interface INetworkManagementService
void setFirewallInterfaceRule(String iface, boolean allow);
void setFirewallEgressSourceRule(String addr, boolean allow);
void setFirewallEgressDestRule(String addr, int port, boolean allow);
void setFirewallUidRule(int uid, boolean allow);
void setFirewallUidRule(int uid, int rule);
/**
* Set all packets from users in ranges to go through VPN specified by netId.

View File

@@ -24,6 +24,7 @@ import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.annotation.Nullable;
@@ -832,7 +833,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
}
if (networkCostly && (uidRules & RULE_REJECT_METERED) != 0) {
if ((uidRules & RULE_REJECT_ALL) != 0
|| (networkCostly && (uidRules & RULE_REJECT_METERED) != 0)) {
return true;
}
@@ -3490,7 +3492,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized(mRulesLock) {
uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
}
if ((uidRules & RULE_REJECT_METERED) != 0) {
if ((uidRules & (RULE_REJECT_METERED | RULE_REJECT_ALL)) != 0) {
// we could silently fail or we can filter the available nets to only give
// them those they have access to. Chose the more useful
networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);

View File

@@ -43,6 +43,7 @@ import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.Network;
import android.net.NetworkPolicyManager;
import android.net.NetworkStats;
import android.net.NetworkUtils;
import android.net.RouteInfo;
@@ -107,8 +108,8 @@ import java.util.concurrent.CountDownLatch;
*/
public class NetworkManagementService extends INetworkManagementService.Stub
implements Watchdog.Monitor {
private static final String TAG = "NetworkManagementService";
private static final boolean DBG = false;
private static final String TAG = "NetworkManagement";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final String NETD_TAG = "NetdConnector";
private static final String NETD_SOCKET_NAME = "netd";
@@ -188,6 +189,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
/** Set of UIDs with cleartext penalties. */
@GuardedBy("mQuotaLock")
private SparseIntArray mUidCleartextPolicy = new SparseIntArray();
/** Set of UIDs that are to be blocked/allowed by firewall controller. */
@GuardedBy("mQuotaLock")
private SparseIntArray mUidFirewallRules = new SparseIntArray();
private Object mIdleTimerLock = new Object();
/** Set of interfaces with active idle timers. */
@@ -563,10 +567,19 @@ public class NetworkManagementService extends INetworkManagementService.Stub
setUidCleartextNetworkPolicy(local.keyAt(i), local.valueAt(i));
}
}
}
// TODO: Push any existing firewall state
setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
size = mUidFirewallRules.size();
if (size > 0) {
Slog.d(TAG, "Pushing " + size + " active firewall UID rules");
final SparseIntArray uidFirewallRules = mUidFirewallRules;
mUidFirewallRules = new SparseIntArray();
for (int i = 0; i < uidFirewallRules.size(); i++) {
setFirewallUidRule(uidFirewallRules.keyAt(i), uidFirewallRules.valueAt(i));
}
}
}
}
/**
@@ -1899,7 +1912,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public void setFirewallEnabled(boolean enabled) {
enforceSystemUid();
try {
mConnector.execute("firewall", enabled ? "enable" : "disable");
mConnector.execute("firewall", "enable", enabled ? "whitelist" : "blacklist");
mFirewallEnabled = enabled;
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
@@ -1949,14 +1962,48 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
public void setFirewallUidRule(int uid, boolean allow) {
public void setFirewallUidRule(int uid, int rule) {
enforceSystemUid();
Preconditions.checkState(mFirewallEnabled);
final String rule = allow ? "allow" : "deny";
try {
mConnector.execute("firewall", "set_uid_rule", uid, rule);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
Preconditions.checkState(mFirewallEnabled);
}
synchronized (mQuotaLock) {
final int oldUidFirewallRule = mUidFirewallRules.get(uid);
if (DBG) {
Slog.d(TAG, "oldRule = " + oldUidFirewallRule
+ ", newRule=" + rule + " for uid=" + uid);
}
if (oldUidFirewallRule == rule) {
if (DBG) Slog.d(TAG, "!!!!! Skipping change");
// TODO: eventually consider throwing
return;
}
try {
String ruleName;
if (isFirewallEnabled()) { // Whitelist mode
if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
ruleName = "allow";
} else {
ruleName = "deny";
}
} else { // Blacklist mode
if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) {
ruleName = "deny";
} else {
ruleName = "allow";
}
}
if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
mUidFirewallRules.delete(uid);
} else {
mUidFirewallRules.put(uid, rule);
}
mConnector.execute("firewall", "set_uid_rule", uid, ruleName);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
}
@@ -2072,6 +2119,18 @@ public class NetworkManagementService extends INetworkManagementService.Stub
pw.println("]");
}
synchronized (mUidFirewallRules) {
pw.print("UID firewall rule: [");
final int size = mUidFirewallRules.size();
for (int i = 0; i < size; i++) {
pw.print(mUidFirewallRules.keyAt(i));
pw.print(":");
pw.print(mUidFirewallRules.valueAt(i));
if (i < size - 1) pw.print(",");
}
pw.println("]");
}
synchronized (mIdleTimerLock) {
pw.println("Idle timers:");
for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) {

View File

@@ -17,6 +17,8 @@
package com.android.server.net;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import android.app.Notification;
import android.app.NotificationManager;
@@ -31,6 +33,7 @@ import android.net.LinkAddress;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.NetworkPolicyManager;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.security.Credentials;
@@ -198,8 +201,8 @@ public class LockdownVpnTracker {
setFirewallEgressSourceRule(addr, true);
}
mNetService.setFirewallUidRule(ROOT_UID, true);
mNetService.setFirewallUidRule(Os.getuid(), true);
mNetService.setFirewallUidRule(ROOT_UID, FIREWALL_RULE_ALLOW);
mNetService.setFirewallUidRule(Os.getuid(), FIREWALL_RULE_ALLOW);
mErrorCount = 0;
mAcceptedIface = iface;
@@ -288,8 +291,8 @@ public class LockdownVpnTracker {
setFirewallEgressSourceRule(addr, false);
}
mNetService.setFirewallUidRule(ROOT_UID, false);
mNetService.setFirewallUidRule(Os.getuid(), false);
mNetService.setFirewallUidRule(ROOT_UID, FIREWALL_RULE_DEFAULT);
mNetService.setFirewallUidRule(Os.getuid(), FIREWALL_RULE_DEFAULT);
mAcceptedSourceAddr = null;
}

View File

@@ -36,11 +36,14 @@ import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.dumpPolicy;
import static android.net.NetworkPolicyManager.dumpRules;
@@ -80,6 +83,8 @@ import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -88,6 +93,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
@@ -140,8 +146,6 @@ import android.util.SparseIntArray;
import android.util.TrustedTime;
import android.util.Xml;
import com.android.server.AppOpsService;
import com.android.server.DeviceIdleController;
import libcore.io.IoUtils;
import com.android.internal.R;
@@ -149,6 +153,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.AppOpsService;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.google.android.collect.Lists;
@@ -176,7 +182,8 @@ import java.util.List;
* and delivers to listeners, such as {@link ConnectivityManager}, for
* enforcement.
*/
public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub
implements AppIdleStateChangeListener {
private static final String TAG = "NetworkPolicy";
private static final boolean LOGD = false;
private static final boolean LOGV = false;
@@ -244,6 +251,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private final IPowerManager mPowerManager;
private final INetworkStatsService mNetworkStats;
private final INetworkManagementService mNetworkManager;
private UsageStatsManagerInternal mUsageStats;
private final TrustedTime mTime;
private IConnectivityManager mConnManager;
@@ -368,6 +376,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return;
}
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
final PackageManager pm = mContext.getPackageManager();
synchronized (mRulesLock) {
updatePowerSaveWhitelistLocked();
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -460,6 +472,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
WifiManager.NETWORK_STATE_CHANGED_ACTION);
mContext.registerReceiver(mWifiStateReceiver, wifiStateFilter, null, mHandler);
mUsageStats.addAppIdleStateChangeListener(this);
}
private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@@ -568,12 +582,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userId == -1) return;
synchronized (mRulesLock) {
// Remove any policies for given user; both cleaning up after a
// USER_REMOVED, and one last sanity check during USER_ADDED
removePoliciesForUserLocked(userId);
// Update global restrict for new user
updateRulesForGlobalChangeLocked(true);
switch (action) {
case ACTION_USER_REMOVED:
case ACTION_USER_ADDED:
synchronized (mRulesLock) {
// Remove any policies for given user; both cleaning up after a
// USER_REMOVED, and one last sanity check during USER_ADDED
removePoliciesForUserLocked(userId);
// Update global restrict for new user
updateRulesForGlobalChangeLocked(true);
}
break;
}
}
};
@@ -2040,6 +2059,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return false;
}
private boolean isUidIdle(int uid) {
final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
final int userId = UserHandle.getUserId(uid);
for (String packageName : packages) {
if (!mUsageStats.isAppIdle(packageName, userId)) {
return false;
}
}
return true;
}
/**
* Applies network rules to bandwidth and firewall controllers based on uid policy.
* @param uid The uid for which to apply the latest policy
*/
void updateRulesForUidLocked(int uid) {
if (!isUidValidForRules(uid)) return;
@@ -2056,10 +2091,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final boolean uidForeground = isUidForegroundLocked(uid);
final boolean uidIdle = isUidIdle(uid);
// derive active rules based on policy and active state
int uidRules = RULE_ALLOW_ALL;
if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
if (uidIdle && !mPowerSaveWhitelistAppIds.get(UserHandle.getAppId(uid))) {
uidRules = RULE_REJECT_ALL;
} else if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
// uid in background, and policy says to block metered data
uidRules = RULE_REJECT_METERED;
} else if (mRestrictBackground) {
@@ -2078,7 +2117,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
// TODO: only dispatch when rules actually change
final int oldRules = mUidRules.get(uid);
if (uidRules == RULE_ALLOW_ALL) {
mUidRules.delete(uid);
@@ -2086,11 +2125,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mUidRules.put(uid, uidRules);
}
// Update bandwidth rules if necessary
final boolean oldRejectMetered = (oldRules & RULE_REJECT_METERED) != 0;
final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0;
setUidNetworkRules(uid, rejectMetered);
if (oldRejectMetered != rejectMetered) {
setUidNetworkRules(uid, rejectMetered);
}
// Update firewall rules if necessary
final boolean oldFirewallReject = (oldRules & RULE_REJECT_ALL) != 0;
final boolean firewallReject = (uidRules & RULE_REJECT_ALL) != 0;
if (oldFirewallReject != firewallReject) {
setUidFirewallRules(uid, firewallReject);
}
// dispatch changed rule to existing listeners
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget();
if (oldRules != uidRules) {
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget();
}
try {
// adjust stats accounting based on foreground status
@@ -2100,6 +2152,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
try {
int uid = mContext.getPackageManager().getPackageUid(packageName, userId);
synchronized (mRulesLock) {
updateRulesForUidLocked(uid);
}
} catch (NameNotFoundException nnfe) {
return;
}
}
private Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
@@ -2223,6 +2287,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
/**
* Add or remove a uid to the firewall blacklist for all network ifaces.
* @param uid
* @param rejectOnAll
*/
private void setUidFirewallRules(int uid, boolean rejectOnAll) {
try {
mNetworkManager.setFirewallUidRule(uid,
rejectOnAll ? FIREWALL_RULE_DENY : FIREWALL_RULE_DEFAULT);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
}
private long getTotalBytes(NetworkTemplate template, long start, long end) {
try {
return mNetworkStats.getNetworkTotalBytes(template, start, end);

View File

@@ -28,7 +28,6 @@ import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -41,6 +40,7 @@ import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -81,6 +81,8 @@ public class UsageStatsService extends SystemService implements
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
private static final long FLUSH_INTERVAL = DEBUG ? TEN_SECONDS : TWENTY_MINUTES;
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 2L * 24 * 60 * 60 * 1000; // 1 day
static final long DEFAULT_CHECK_IDLE_INTERVAL = 8 * 3600 * 1000; // 8 hours
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
@@ -88,6 +90,7 @@ public class UsageStatsService extends SystemService implements
static final int MSG_REMOVE_USER = 2;
static final int MSG_INFORM_LISTENERS = 3;
static final int MSG_RESET_LAST_TIMESTAMP = 4;
static final int MSG_CHECK_IDLE_STATES = 5;
private final Object mLock = new Object();
Handler mHandler;
@@ -98,9 +101,11 @@ public class UsageStatsService extends SystemService implements
private File mUsageStatsDir;
long mRealTimeSnapshot;
long mSystemTimeSnapshot;
boolean mAppIdleParoled;
private static final long DEFAULT_APP_IDLE_THRESHOLD_MILLIS = 1L * 24 * 60 * 60 * 1000; // 1 day
private long mAppIdleDurationMillis;
long mAppIdleDurationMillis;
long mCheckIdleIntervalMillis = DEFAULT_CHECK_IDLE_INTERVAL;
private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
mPackageAccessListeners = new ArrayList<>();
@@ -113,6 +118,7 @@ public class UsageStatsService extends SystemService implements
public void onStart() {
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
mHandler = new H(BackgroundThread.get().getLooper());
File systemDataDir = new File(Environment.getDataDirectory(), "system");
@@ -123,9 +129,14 @@ public class UsageStatsService extends SystemService implements
+ mUsageStatsDir.getAbsolutePath());
}
getContext().registerReceiver(new UserRemovedReceiver(),
new IntentFilter(Intent.ACTION_USER_REMOVED));
IntentFilter userActions = new IntentFilter(Intent.ACTION_USER_REMOVED);
userActions.addAction(Intent.ACTION_USER_STARTED);
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, userActions,
null, null);
IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
synchronized (mLock) {
cleanUpRemovedUsersLocked();
}
@@ -147,18 +158,35 @@ public class UsageStatsService extends SystemService implements
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// Observe changes to the threshold
new SettingsObserver(mHandler).registerObserver();
} else if (phase == PHASE_BOOT_COMPLETED) {
setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging());
}
}
private class UserRemovedReceiver extends BroadcastReceiver {
private class UserActionsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
if (userId >= 0) {
mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
}
} else if (Intent.ACTION_USER_STARTED.equals(intent.getAction())) {
if (userId >=0) {
postCheckIdleStates();
}
}
}
}
private class DeviceStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BatteryManager.ACTION_CHARGING.equals(action)
|| BatteryManager.ACTION_DISCHARGING.equals(action)) {
setAppIdleParoled(BatteryManager.ACTION_CHARGING.equals(action));
}
}
}
@@ -195,6 +223,49 @@ public class UsageStatsService extends SystemService implements
}
}
void setAppIdleParoled(boolean paroled) {
synchronized (mLock) {
if (mAppIdleParoled != paroled) {
mAppIdleParoled = paroled;
postCheckIdleStates();
}
}
}
void postCheckIdleStates() {
mHandler.removeMessages(MSG_CHECK_IDLE_STATES);
mHandler.sendEmptyMessage(MSG_CHECK_IDLE_STATES);
}
/** Check all running users' apps to see if they enter an idle state. */
void checkIdleStates() {
final int[] runningUsers;
try {
runningUsers = ActivityManagerNative.getDefault().getRunningUserIds();
} catch (RemoteException re) {
return;
}
for (int i = 0; i < runningUsers.length; i++) {
final int userId = runningUsers[i];
List<PackageInfo> packages =
getContext().getPackageManager().getInstalledPackages(
PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.GET_UNINSTALLED_PACKAGES,
userId);
synchronized (mLock) {
final int packageCount = packages.size();
for (int p = 0; p < packageCount; p++) {
final String packageName = packages.get(p).packageName;
final boolean isIdle = isAppIdle(packageName, userId);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
userId, isIdle ? 1 : 0, packageName));
}
}
}
mHandler.sendEmptyMessageDelayed(MSG_CHECK_IDLE_STATES, mCheckIdleIntervalMillis);
}
private static void deleteRecursively(File f) {
File[] files = f.listFiles();
if (files != null) {
@@ -291,7 +362,7 @@ public class UsageStatsService extends SystemService implements
void resetLastTimestamp(String packageName, int userId, boolean idle) {
synchronized (mLock) {
final long timeNow = checkAndGetTimeLocked();
final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0);
final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0) - 5000;
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
@@ -409,14 +480,22 @@ public class UsageStatsService extends SystemService implements
private boolean hasPassedIdleDuration(long lastUsed) {
final long now = System.currentTimeMillis();
return lastUsed < now - mAppIdleDurationMillis;
return lastUsed <= now - mAppIdleDurationMillis;
}
boolean isAppIdle(String packageName, int userId) {
if (packageName == null) return false;
synchronized (mLock) {
// Temporary exemption, probably due to device charging or occasional allowance to
// be allowed to sync, etc.
if (mAppIdleParoled) {
return false;
}
}
if (SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) {
return false;
}
// TODO: Optimize this check
if (isActiveDeviceAdmin(packageName, userId)) {
return false;
}
@@ -518,6 +597,9 @@ public class UsageStatsService extends SystemService implements
resetLastTimestamp((String) msg.obj, msg.arg1, msg.arg2 == 1);
break;
case MSG_CHECK_IDLE_STATES:
checkIdleStates();
break;
default:
super.handleMessage(msg);
break;
@@ -544,7 +626,9 @@ public class UsageStatsService extends SystemService implements
mAppIdleDurationMillis = Settings.Secure.getLongForUser(getContext().getContentResolver(),
Settings.Secure.APP_IDLE_DURATION, DEFAULT_APP_IDLE_THRESHOLD_MILLIS,
UserHandle.USER_OWNER);
// TODO: Check if we need to update idle states of all the apps
mCheckIdleIntervalMillis = Math.min(DEFAULT_CHECK_IDLE_INTERVAL,
mAppIdleDurationMillis / 4);
postCheckIdleStates();
}
}