Flag to mark foreground jobs, fix data saver.

When a job will eventually run in the foreground, the internal
scheduling needs to ignore any background network restrictions when
satisfying constraints.  This also means the job should ignore the
current device doze state, since the requesting app could get the
same behavior by starting their own foreground service.

Always dispatch network policy changes to ConnectivityService first
to ensure that it has up-to-date information.  Fix bugs around data
saver that were causing networks to not be marked as BLOCKED for
background apps; before this fix apps would have been spinning in
internal connectivity loops, thinking that the network was actually
connected when the kernel was actually blocking their traffic.

Offer new ConnectivityService method overloads to ignore the blocked
state for a specific UID.

Print unsatisfied job constraints to aid debugging.

Bug: 26571724
Change-Id: Iaaa17933e6dc1bf6d3dff26d0bfc12222e51e241
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 30aab8c..50d9368 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -48,7 +48,6 @@
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
 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_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
@@ -349,6 +348,9 @@
     /** Foreground at UID granularity. */
     final SparseIntArray mUidState = new SparseIntArray();
 
+    /** Higher priority listener before general event dispatch */
+    private INetworkPolicyListener mConnectivityListener;
+
     private final RemoteCallbackList<INetworkPolicyListener>
             mListeners = new RemoteCallbackList<>();
 
@@ -1391,6 +1393,7 @@
                 final String tag = in.getName();
                 if (type == START_TAG) {
                     if (TAG_POLICY_LIST.equals(tag)) {
+                        final boolean oldValue = mRestrictBackground;
                         version = readIntAttribute(in, ATTR_VERSION);
                         if (version >= VERSION_ADDED_RESTRICT_BACKGROUND) {
                             mRestrictBackground = readBooleanAttribute(
@@ -1398,6 +1401,12 @@
                         } else {
                             mRestrictBackground = false;
                         }
+                        if (mRestrictBackground != oldValue) {
+                            // Some early services may have read the default value,
+                            // so notify them that it's changed
+                            mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED,
+                                    mRestrictBackground ? 1 : 0, 0).sendToTarget();
+                        }
 
                     } else if (TAG_NETWORK_POLICY.equals(tag)) {
                         final int networkTemplate = readIntAttribute(in, ATTR_NETWORK_TEMPLATE);
@@ -1766,20 +1775,25 @@
     }
 
     @Override
+    public void setConnectivityListener(INetworkPolicyListener listener) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        if (mConnectivityListener != null) {
+            throw new IllegalStateException("Connectivity listener already registered");
+        }
+        mConnectivityListener = listener;
+    }
+
+    @Override
     public void registerListener(INetworkPolicyListener listener) {
         // TODO: create permission for observing network policy
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
         mListeners.register(listener);
-
-        // TODO: consider dispatching existing rules to new listeners
     }
 
     @Override
     public void unregisterListener(INetworkPolicyListener listener) {
         // TODO: create permission for observing network policy
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
         mListeners.unregister(listener);
     }
 
@@ -2754,8 +2768,8 @@
         final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
         final boolean isWhitelisted = mRestrictBackgroundWhitelistUids.get(uid);
 
-        int newRule = RULE_ALLOW_ALL;
-        final int oldRule = mUidRules.get(uid);
+        int newRule = RULE_UNKNOWN;
+        final int oldRule = mUidRules.get(uid, RULE_UNKNOWN);
 
         // First step: define the new rule based on user restrictions and foreground state.
         if (isForeground) {
@@ -2777,8 +2791,7 @@
                     + ", oldRule: " + ruleToString(oldRule));
         }
 
-
-        if (newRule == RULE_ALLOW_ALL) {
+        if (newRule == RULE_UNKNOWN) {
             mUidRules.delete(uid);
         } else {
             mUidRules.put(uid, newRule);
@@ -2858,6 +2871,45 @@
         }
     }
 
+    private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) {
+        if (listener != null) {
+            try {
+                listener.onUidRulesChanged(uid, uidRules);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    private void dispatchMeteredIfacesChanged(INetworkPolicyListener listener,
+            String[] meteredIfaces) {
+        if (listener != null) {
+            try {
+                listener.onMeteredIfacesChanged(meteredIfaces);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    private void dispatchRestrictBackgroundChanged(INetworkPolicyListener listener,
+            boolean restrictBackground) {
+        if (listener != null) {
+            try {
+                listener.onRestrictBackgroundChanged(restrictBackground);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    private void dispatchRestrictBackgroundWhitelistChanged(INetworkPolicyListener listener,
+            int uid, boolean whitelisted) {
+        if (listener != null) {
+            try {
+                listener.onRestrictBackgroundWhitelistChanged(uid, whitelisted);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
     private Handler.Callback mHandlerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message msg) {
@@ -2865,30 +2917,22 @@
                 case MSG_RULES_CHANGED: {
                     final int uid = msg.arg1;
                     final int uidRules = msg.arg2;
+                    dispatchUidRulesChanged(mConnectivityListener, uid, uidRules);
                     final int length = mListeners.beginBroadcast();
                     for (int i = 0; i < length; i++) {
                         final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                        if (listener != null) {
-                            try {
-                                listener.onUidRulesChanged(uid, uidRules);
-                            } catch (RemoteException e) {
-                            }
-                        }
+                        dispatchUidRulesChanged(listener, uid, uidRules);
                     }
                     mListeners.finishBroadcast();
                     return true;
                 }
                 case MSG_METERED_IFACES_CHANGED: {
                     final String[] meteredIfaces = (String[]) msg.obj;
+                    dispatchMeteredIfacesChanged(mConnectivityListener, meteredIfaces);
                     final int length = mListeners.beginBroadcast();
                     for (int i = 0; i < length; i++) {
                         final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                        if (listener != null) {
-                            try {
-                                listener.onMeteredIfacesChanged(meteredIfaces);
-                            } catch (RemoteException e) {
-                            }
-                        }
+                        dispatchMeteredIfacesChanged(listener, meteredIfaces);
                     }
                     mListeners.finishBroadcast();
                     return true;
@@ -2915,15 +2959,11 @@
                 }
                 case MSG_RESTRICT_BACKGROUND_CHANGED: {
                     final boolean restrictBackground = msg.arg1 != 0;
+                    dispatchRestrictBackgroundChanged(mConnectivityListener, restrictBackground);
                     final int length = mListeners.beginBroadcast();
                     for (int i = 0; i < length; i++) {
                         final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                        if (listener != null) {
-                            try {
-                                listener.onRestrictBackgroundChanged(restrictBackground);
-                            } catch (RemoteException e) {
-                            }
-                        }
+                        dispatchRestrictBackgroundChanged(listener, restrictBackground);
                     }
                     mListeners.finishBroadcast();
                     final Intent intent =
@@ -2947,18 +2987,16 @@
                     final boolean changed = msg.arg2 == 1;
                     final Boolean whitelisted = (Boolean) msg.obj;
 
+                    // First notify internal listeners...
                     if (whitelisted != null) {
+                        final boolean whitelistedBool = whitelisted.booleanValue();
+                        dispatchRestrictBackgroundWhitelistChanged(mConnectivityListener, uid,
+                                whitelistedBool);
                         final int length = mListeners.beginBroadcast();
                         for (int i = 0; i < length; i++) {
-                            // First notify internal listeners...
                             final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                            if (listener != null) {
-                                try {
-                                    listener.onRestrictBackgroundWhitelistChanged(uid,
-                                            whitelisted.booleanValue());
-                                } catch (RemoteException e) {
-                                }
-                            }
+                            dispatchRestrictBackgroundWhitelistChanged(listener, uid,
+                                    whitelistedBool);
                         }
                         mListeners.finishBroadcast();
                     }