Merge "Change app selection policy for post-OTA verification" into nyc-dev
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 2be55317..3050ac8 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -510,11 +510,15 @@
         }
 
         // Setup jit profile support.
+        //
         // It is ok to call this multiple times if the application gets updated with new splits.
         // The runtime only keeps track of unique code paths and can handle re-registration of
         // the same code path. There's no need to pass `addedPaths` since any new code paths
         // are already in `mApplicationInfo`.
-        if (needToSetupJitProfiles) {
+        //
+        // It is NOT ok to call this function from the system_server (for any of the packages it
+        // loads code from) so we explicitly disallow it there.
+        if (needToSetupJitProfiles && !ActivityThread.isSystem()) {
             // Temporarily disable logging of disk reads/writes on the Looper thread
             // as this is early and necessary. Write is only needed to create the
             // profile file if it's not already there.
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index f13cbae..5db0f16 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -216,7 +216,7 @@
         }
         if (chosen.versionCode > toUse.versionCode) {
             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
-                    + "version code mismatch, expected: " + chosen.versionCode
+                    + "version code is lower than expected: " + chosen.versionCode
                     + " actual: " + toUse.versionCode);
         }
         if (getWebViewLibrary(toUse.applicationInfo) == null) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7458898..9fc17a2 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -72,6 +72,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -2047,6 +2048,65 @@
         }
     }
 
+    private void closeSocketsForFirewallChain(int chain, String chainName) {
+        // UID ranges to close sockets on.
+        UidRange[] ranges;
+        // UID ranges whose sockets we won't touch.
+        int[] exemptUids;
+
+        SparseIntArray rules = getUidFirewallRules(chain);
+        int numUids = 0;
+
+        if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
+            // Close all sockets on all non-system UIDs...
+            ranges = new UidRange[] {
+                // TODO: is there a better way of finding all existing users? If so, we could
+                // specify their ranges here.
+                new UidRange(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
+            };
+            // ... except for the UIDs that have allow rules.
+            exemptUids = new int[rules.size()];
+            for (int i = 0; i < exemptUids.length; i++) {
+                if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+                    exemptUids[numUids] = rules.keyAt(i);
+                    numUids++;
+                }
+            }
+            // Normally, whitelist chains only contain deny rules, so numUids == exemptUids.length.
+            // But the code does not guarantee this in any way, and at least in one case - if we add
+            // a UID rule to the firewall, and then disable the firewall - the chains can contain
+            // the wrong type of rule. In this case, don't close connections that we shouldn't.
+            //
+            // TODO: tighten up this code by ensuring we never set the wrong type of rule, and
+            // fix setFirewallEnabled to grab mQuotaLock and clear rules.
+            if (numUids != exemptUids.length) {
+                exemptUids = Arrays.copyOf(exemptUids, numUids);
+            }
+        } else {
+            // Close sockets for every UID that has a deny rule...
+            ranges = new UidRange[rules.size()];
+            for (int i = 0; i < ranges.length; i++) {
+                if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+                    int uid = rules.keyAt(i);
+                    ranges[numUids] = new UidRange(uid, uid);
+                    numUids++;
+                }
+            }
+            // As above; usually numUids == ranges.length, but not always.
+            if (numUids != ranges.length) {
+                ranges = Arrays.copyOf(ranges, numUids);
+            }
+            // ... with no exceptions.
+            exemptUids = new int[0];
+        }
+
+        try {
+            mNetdService.socketDestroy(ranges, exemptUids);
+        } catch(RemoteException | ServiceSpecificException e) {
+            Slog.e(TAG, "Error closing sockets after enabling chain " + chainName + ": " + e);
+        }
+    }
+
     @Override
     public void setFirewallChainEnabled(int chain, boolean enable) {
         enforceSystemUid();
@@ -2059,25 +2119,35 @@
             mFirewallChainStates.put(chain, enable);
 
             final String operation = enable ? "enable_chain" : "disable_chain";
+            String chainName;
+            switch(chain) {
+                case FIREWALL_CHAIN_STANDBY:
+                    chainName = FIREWALL_CHAIN_NAME_STANDBY;
+                    break;
+                case FIREWALL_CHAIN_DOZABLE:
+                    chainName = FIREWALL_CHAIN_NAME_DOZABLE;
+                    break;
+                case FIREWALL_CHAIN_POWERSAVE:
+                    chainName = FIREWALL_CHAIN_NAME_POWERSAVE;
+                    break;
+                default:
+                    throw new IllegalArgumentException("Bad child chain: " + chain);
+            }
+
             try {
-                String chainName;
-                switch(chain) {
-                    case FIREWALL_CHAIN_STANDBY:
-                        chainName = FIREWALL_CHAIN_NAME_STANDBY;
-                        break;
-                    case FIREWALL_CHAIN_DOZABLE:
-                        chainName = FIREWALL_CHAIN_NAME_DOZABLE;
-                        break;
-                    case FIREWALL_CHAIN_POWERSAVE:
-                        chainName = FIREWALL_CHAIN_NAME_POWERSAVE;
-                        break;
-                    default:
-                        throw new IllegalArgumentException("Bad child chain: " + chain);
-                }
                 mConnector.execute("firewall", operation, chainName);
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
             }
+
+            // Close any sockets that were opened by the affected UIDs. This has to be done after
+            // disabling network connectivity, in case they react to the socket close by reopening
+            // the connection and race with the iptables commands that enable the firewall. All
+            // whitelist and blacklist chains allow RSTs through.
+            if (enable) {
+                if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
+                closeSocketsForFirewallChain(chain, chainName);
+            }
         }
     }
 
@@ -2376,7 +2446,7 @@
     }
 
     private void dumpUidFirewallRule(PrintWriter pw, String name, SparseIntArray rules) {
-        pw.print("UID firewall");
+        pw.print("UID firewall ");
         pw.print(name);
         pw.print(" rule: [");
         final int size = rules.size();
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8af0af0..92aa8d9 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -405,6 +405,7 @@
     private final SyncHandler mSyncHandler;
 
     private volatile boolean mBootCompleted = false;
+    private volatile boolean mJobServiceReady = false;
 
     private ConnectivityManager getConnectivityManager() {
         synchronized (this) {
@@ -2177,11 +2178,7 @@
         static final int MESSAGE_SCHEDULE_SYNC = 12;
         static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13;
         static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14;
-        /**
-         * Posted delayed in order to expire syncs that are long-running.
-         * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
-         */
-        private static final int MESSAGE_SYNC_EXPIRED = 7;
+
         /**
          * Posted periodically to monitor network process for long-running syncs.
          * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
@@ -2209,7 +2206,7 @@
         }
 
         void checkIfDeviceReady() {
-            if (mProvisioned && mBootCompleted) {
+            if (mProvisioned && mBootCompleted && mJobServiceReady) {
                 synchronized(this) {
                     mSyncStorageEngine.restoreAllPeriodicSyncs();
                     // Dispatch any stashed messages.
@@ -2229,7 +2226,7 @@
          */
         private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
             synchronized (this) {
-                if (!mBootCompleted || !mProvisioned) {
+                if (!mBootCompleted || !mProvisioned || !mJobServiceReady) {
                     // Need to copy the message bc looper will recycle it.
                     Message m = Message.obtain(msg);
                     mUnreadyQueue.add(m);
@@ -2252,6 +2249,8 @@
                 if (msg.what == MESSAGE_JOBSERVICE_OBJECT) {
                     Slog.i(TAG, "Got SyncJobService instance.");
                     mSyncJobService = (SyncJobService) msg.obj;
+                    mJobServiceReady = true;
+                    checkIfDeviceReady();
                 } else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) {
                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
                         Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
@@ -2972,7 +2971,6 @@
                 Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for "
                         + activeSyncContext.toString());
             }
-            mSyncHandler.removeMessages(SyncHandler.MESSAGE_SYNC_EXPIRED, activeSyncContext);
             mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext);
         }
 
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index f6255af..0a25402 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -148,7 +148,7 @@
                     // jobs because PackageDexOptimizer.performDexOpt is synchronized.
                     pm.performDexOpt(pkg,
                             /* instruction set */ null,
-                            /* checkProfiles */ false,
+                            /* checkProfiles */ true,
                             PackageManagerService.REASON_BOOT,
                             /* force */ false);
                 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 547379d..38144e4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7197,7 +7197,7 @@
 
             performDexOpt(pkg.packageName,
                     null /* instructionSet */,
-                    false /* checkProfiles */,
+                    true /* checkProfiles */,
                     causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
                     false /* force */);
         }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index d90d922..91de797 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -536,16 +536,37 @@
         }
 
         /**
+         * Both versionCodes should be from a WebView provider package implemented by Chromium.
+         * VersionCodes from other kinds of packages won't make any sense in this method.
+         *
+         * An introduction to Chromium versionCode scheme:
+         * "BBBBPPPAX"
+         * BBBB: 4 digit branch number. It monotonically increases over time.
+         * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits may
+         * change their meaning in the future.
+         * A: architecture digit.
+         * X: A digit to differentiate APKs for other reasons.
+         *
+         * This method takes the "BBBB" of versionCodes and compare them.
+         *
+         * @return true if versionCode1 is higher than or equal to versionCode2.
+         */
+        private static boolean versionCodeGE(int versionCode1, int versionCode2) {
+            int v1 = versionCode1 / 100000;
+            int v2 = versionCode2 / 100000;
+
+            return v1 >= v2;
+        }
+
+        /**
          * Returns whether this provider is valid for use as a WebView provider.
          */
         public boolean isValidProvider(WebViewProviderInfo configInfo,
                 PackageInfo packageInfo) {
-            if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
-                    && packageInfo.versionCode < getMinimumVersionCode()
+            if (!versionCodeGE(packageInfo.versionCode, getMinimumVersionCode())
                     && !mSystemInterface.systemIsDebuggable()) {
-                // Non-system package webview providers may be downgraded arbitrarily low, prevent
-                // that by enforcing minimum version code. This check is only enforced for user
-                // builds.
+                // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
+                // minimum version code. This check is only enforced for user builds.
                 return false;
             }
             if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) &&