diff options
author | Amith Yamasani <yamasani@google.com> | 2015-04-29 23:04:50 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-04-29 23:04:50 +0000 |
commit | 98322c8e885cf64698b96dfbc5035a9fd3ee92eb (patch) | |
tree | 8b2aef580bdb4f432552c2d3c460f966e2048cc8 | |
parent | a4a1c6e94ece1881728cb8e773d62fec7ab59c70 (diff) | |
parent | 278886d50a66689529320f9bd735177c068e210d (diff) | |
download | frameworks_base-98322c8e885cf64698b96dfbc5035a9fd3ee92eb.zip frameworks_base-98322c8e885cf64698b96dfbc5035a9fd3ee92eb.tar.gz frameworks_base-98322c8e885cf64698b96dfbc5035a9fd3ee92eb.tar.bz2 |
am 278886d5: am 3184b286: am fa4eda44: Merge "Remove network access for idle apps" into mnc-dev
* commit '278886d50a66689529320f9bd735177c068e210d':
Remove network access for idle apps
7 files changed, 283 insertions, 45 deletions
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index b4c7b2b..ecc3fb4 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -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("]"); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index f93550a..b29e8d0 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -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. diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 12a99b0..1a75b8a 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -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); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index b5b62b4..f6998ca 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -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()) { diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 6ffe6ac..0f88883 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -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; } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index e45092c..57df1c3 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -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); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 04984d3..c2e61c6 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -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(); } } |