Merge "Add NATT keepalive resources and methods into IpSecService"
diff --git a/Android.bp b/Android.bp
index ec8636d..57f6513 100644
--- a/Android.bp
+++ b/Android.bp
@@ -687,6 +687,7 @@
     static_libs: [
         "apex_aidl_interface-java",
         "framework-protos",
+        "game-driver-protos",
         "android.hidl.base-V1.0-java",
         "android.hardware.cas-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
diff --git a/Android.mk b/Android.mk
index c58f7af..9bda2dc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -32,27 +32,6 @@
 # ============================================================
 include $(CLEAR_VARS)
 
-aidl_parcelables :=
-define stubs-to-aidl-parcelables
-  gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/$1.aidl
-  aidl_parcelables += $$(gen)
-  $$(gen): $(call java-lib-header-files,$1) $(HOST_OUT_EXECUTABLES)/sdkparcelables
-	@echo Extract SDK parcelables: $$@
-	rm -f $$@
-	$(HOST_OUT_EXECUTABLES)/sdkparcelables $$< $$@
-endef
-
-$(foreach stubs,android_stubs_current android_test_stubs_current android_system_stubs_current,\
-  $(eval $(call stubs-to-aidl-parcelables,$(stubs))))
-
-gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
-.KATI_RESTAT: $(gen)
-$(gen): $(aidl_parcelables)
-	@echo Combining SDK parcelables: $@
-	rm -f $@.tmp
-	cat $^ | sort -u > $@.tmp
-	$(call commit-change-for-toc,$@)
-
 # This is used by ide.mk as the list of source files that are
 # always included.
 INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
diff --git a/api/current.txt b/api/current.txt
index 4dcba83..2417d8e 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -8734,8 +8734,8 @@
     method public android.bluetooth.le.ScanFilter.Builder setManufacturerData(int, byte[], byte[]);
     method public android.bluetooth.le.ScanFilter.Builder setServiceData(android.os.ParcelUuid, byte[]);
     method public android.bluetooth.le.ScanFilter.Builder setServiceData(android.os.ParcelUuid, byte[], byte[]);
-    method public android.bluetooth.le.ScanFilter.Builder setServiceSolicitationUuid(android.os.ParcelUuid);
-    method public android.bluetooth.le.ScanFilter.Builder setServiceSolicitationUuid(android.os.ParcelUuid, android.os.ParcelUuid);
+    method @NonNull public android.bluetooth.le.ScanFilter.Builder setServiceSolicitationUuid(@Nullable android.os.ParcelUuid);
+    method @NonNull public android.bluetooth.le.ScanFilter.Builder setServiceSolicitationUuid(@Nullable android.os.ParcelUuid, @Nullable android.os.ParcelUuid);
     method public android.bluetooth.le.ScanFilter.Builder setServiceUuid(android.os.ParcelUuid);
     method public android.bluetooth.le.ScanFilter.Builder setServiceUuid(android.os.ParcelUuid, android.os.ParcelUuid);
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index dc5ff8f..cb9ff493 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -923,6 +923,7 @@
 package android.os {
 
   public static class Build.VERSION {
+    field public static final String[] ACTIVE_CODENAMES;
     field public static final int FIRST_SDK_INT;
     field public static final int RESOURCES_SDK_INT;
   }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 564e918..1ac6ea8 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -134,7 +134,7 @@
         LowMemReported low_mem_reported = 81;
         ThermalThrottlingStateChanged thermal_throttling = 86;
         NetworkDnsEventReported network_dns_event_reported = 116;
-        DataStallEvent data_stall_event = 121;
+        DataStallEvent data_stall_event = 121 [(log_from_module) = "network_stack"];
         BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125;
         BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126;
         BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127;
@@ -165,7 +165,7 @@
         BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167;
         ProcessStartTime process_start_time = 169;
         BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
-        NetworkStackReported network_stack_reported = 182;
+        NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"];
     }
 
     // Pulled events will start at field 10000.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f453289..d9c82ea 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1003,9 +1003,8 @@
         }
 
         public void updateHttpProxy() {
-            final ConnectivityManager cm = ConnectivityManager.from(
+            ActivityThread.updateHttpProxy(
                     getApplication() != null ? getApplication() : getSystemContext());
-            Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
         }
 
         public void processInBackground() {
@@ -6690,6 +6689,11 @@
         return thread;
     }
 
+    public static void updateHttpProxy(@NonNull Context context) {
+        final ConnectivityManager cm = ConnectivityManager.from(context);
+        Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
+    }
+
     @UnsupportedAppUsage
     public final void installSystemProviders(List<ProviderInfo> providers) {
         if (providers != null) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 1e12801..34c7372 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -56,7 +56,8 @@
  * returned by {@link BluetoothAdapter#getBondedDevices()
  * BluetoothAdapter.getBondedDevices()}. You can then open a
  * {@link BluetoothSocket} for communication with the remote device, using
- * {@link #createRfcommSocketToServiceRecord(UUID)}.
+ * {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using
+ * {@link #createL2capChannel(int)} over Bluetooth LE.
  *
  * <p class="note"><strong>Note:</strong>
  * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 4e88625..c06b837 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -35,21 +35,28 @@
  * On the client side, use a single {@link BluetoothSocket} to both initiate
  * an outgoing connection and to manage the connection.
  *
- * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
- * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
- * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
+ * <p>For Bluetooth BR/EDR, the most common type of socket is RFCOMM, which is the type supported by
+ * the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth BR/EDR. It
+ * is also known as the Serial Port Profile (SPP). To create a listening
+ * {@link BluetoothServerSocket} that's ready for incoming Bluetooth BR/EDR connections, use {@link
+ * BluetoothAdapter#listenUsingRfcommWithServiceRecord
+ * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}.
  *
- * <p>To create a listening {@link BluetoothServerSocket} that's ready for
- * incoming connections, use
- * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord
- * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call
- * {@link #accept()} to listen for incoming connection requests. This call
- * will block until a connection is established, at which point, it will return
- * a {@link BluetoothSocket} to manage the connection. Once the {@link
- * BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on
- * the {@link BluetoothServerSocket} when it's no longer needed for accepting
- * connections. Closing the {@link BluetoothServerSocket} will <em>not</em>
- * close the returned {@link BluetoothSocket}.
+ * <p>For Bluetooth LE, the socket uses LE Connection-oriented Channel (CoC). LE CoC is a
+ * connection-oriented, streaming transport over Bluetooth LE and has a credit-based flow control.
+ * Correspondingly, use {@link BluetoothAdapter#listenUsingL2capChannel
+ * BluetoothAdapter.listenUsingL2capChannel()} to create a listening {@link BluetoothServerSocket}
+ * that's ready for incoming Bluetooth LE CoC connections. For LE CoC, you can use {@link #getPsm()}
+ * to get the protocol/service multiplexer (PSM) value that the peer needs to use to connect to your
+ * socket.
+ *
+ * <p> After the listening {@link BluetoothServerSocket} is created, call {@link #accept()} to
+ * listen for incoming connection requests. This call will block until a connection is established,
+ * at which point, it will return a {@link BluetoothSocket} to manage the connection. Once the
+ * {@link BluetoothSocket} is acquired, it's a good idea to call {@link #close()} on the {@link
+ * BluetoothServerSocket} when it's no longer needed for accepting
+ * connections. Closing the {@link BluetoothServerSocket} will <em>not</em> close the returned
+ * {@link BluetoothSocket}.
  *
  * <p>{@link BluetoothServerSocket} is thread
  * safe. In particular, {@link #close} will always immediately abort ongoing
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index c5d435b..66d1da8 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth.le;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
@@ -586,7 +587,8 @@
         /**
          * Set filter on service solicitation uuid.
          */
-        public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid) {
+        public @NonNull Builder setServiceSolicitationUuid(
+                @Nullable ParcelUuid serviceSolicitationUuid) {
             mServiceSolicitationUuid = serviceSolicitationUuid;
             return this;
         }
@@ -601,8 +603,9 @@
          * @throws IllegalArgumentException If {@code serviceSolicitationUuid} is {@code null} but
          *             {@code serviceSolicitationUuidMask} is not {@code null}.
          */
-        public Builder setServiceSolicitationUuid(ParcelUuid serviceSolicitationUuid,
-                ParcelUuid solicitationUuidMask) {
+        public @NonNull Builder setServiceSolicitationUuid(
+                @Nullable ParcelUuid serviceSolicitationUuid,
+                @Nullable ParcelUuid solicitationUuidMask) {
             if (mServiceSolicitationUuidMask != null && mServiceSolicitationUuid == null) {
                 throw new IllegalArgumentException(
                         "SolicitationUuid is null while SolicitationUuidMask is not null!");
diff --git a/core/java/android/net/ITestNetworkManager.aidl b/core/java/android/net/ITestNetworkManager.aidl
index bab6ae8..d586038 100644
--- a/core/java/android/net/ITestNetworkManager.aidl
+++ b/core/java/android/net/ITestNetworkManager.aidl
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.TestNetworkInterface;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -31,7 +32,8 @@
     TestNetworkInterface createTunInterface(in LinkAddress[] linkAddrs);
     TestNetworkInterface createTapInterface();
 
-    void setupTestNetwork(in String iface, in IBinder binder);
+    void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
+            in IBinder binder);
 
     void teardownTestNetwork(int netId);
 }
diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java
index e274005..4ac4a69 100644
--- a/core/java/android/net/TestNetworkManager.java
+++ b/core/java/android/net/TestNetworkManager.java
@@ -56,6 +56,26 @@
     /**
      * Sets up a capability-limited, testing-only network for a given interface
      *
+     * @param lp The LinkProperties for the TestNetworkService to use for this test network. Note
+     *     that the interface name and link addresses will be overwritten, and the passed-in values
+     *     discarded.
+     * @param isMetered Whether or not the network should be considered metered.
+     * @param binder A binder object guarding the lifecycle of this test network.
+     * @hide
+     */
+    public void setupTestNetwork(
+            @NonNull LinkProperties lp, boolean isMetered, @NonNull IBinder binder) {
+        Preconditions.checkNotNull(lp, "Invalid LinkProperties");
+        try {
+            mService.setupTestNetwork(lp.getInterfaceName(), lp, isMetered, binder);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets up a capability-limited, testing-only network for a given interface
+     *
      * @param iface the name of the interface to be used for the Network LinkProperties.
      * @param binder A binder object guarding the lifecycle of this test network.
      * @hide
@@ -63,7 +83,7 @@
     @TestApi
     public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
         try {
-            mService.setupTestNetwork(iface, binder);
+            mService.setupTestNetwork(iface, null, true, binder);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java
index 5b67406..cf08b65 100644
--- a/core/java/android/net/UrlQuerySanitizer.java
+++ b/core/java/android/net/UrlQuerySanitizer.java
@@ -22,6 +22,8 @@
 import java.util.Locale;
 import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  *
@@ -837,15 +839,11 @@
      * @param string the escaped string
      * @return the unescaped string.
      */
+    private static final Pattern plusOrPercent = Pattern.compile("[+%]");
     public String unescape(String string) {
-        // Early exit if no escaped characters.
-        int firstEscape = string.indexOf('%');
-        if ( firstEscape < 0) {
-            firstEscape = string.indexOf('+');
-            if (firstEscape < 0) {
-                return string;
-            }
-        }
+        final Matcher matcher = plusOrPercent.matcher(string);
+        if (!matcher.find()) return string;
+        final int firstEscape = matcher.start();
 
         int length = string.length();
 
@@ -855,8 +853,7 @@
             char c = string.charAt(i);
             if (c == '+') {
                 c = ' ';
-            }
-            else if ( c == '%' && i + 2 < length) {
+            } else if (c == '%' && i + 2 < length) {
                 char c1 = string.charAt(i + 1);
                 char c2 = string.charAt(i + 2);
                 if (isHexDigit(c1) && isHexDigit(c2)) {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1c319fb..d3bed02 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -287,6 +287,7 @@
         /**
          * @hide
          */
+        @TestApi
         @UnsupportedAppUsage
         public static final String[] ACTIVE_CODENAMES = "REL".equals(ALL_CODENAMES[0])
                 ? new String[0] : ALL_CODENAMES;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 606c8f3..1aaee18 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -28,11 +28,11 @@
 
 import dalvik.system.VMRuntime;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /** @hide */
 public class GraphicsEnvironment {
@@ -49,7 +49,7 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
-    private static final String GUP_WHITELIST_FILENAME = "whitelist.txt";
+    private static final String GAME_DRIVER_WHITELIST_ALL = "*";
 
     private ClassLoader mClassLoader;
     private String mLayerPath;
@@ -98,15 +98,15 @@
 
         if (isDebuggable(context)) {
 
-            int enable = Settings.Global.getInt(context.getContentResolver(),
-                                                Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+            final int enable = Settings.Global.getInt(context.getContentResolver(),
+                                                      Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
 
             if (enable != 0) {
 
-                String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
-                                                               Settings.Global.GPU_DEBUG_APP);
+                final String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
+                                                                     Settings.Global.GPU_DEBUG_APP);
 
-                String packageName = context.getPackageName();
+                final String packageName = context.getPackageName();
 
                 if ((gpuDebugApp != null && packageName != null)
                         && (!gpuDebugApp.isEmpty() && !packageName.isEmpty())
@@ -136,37 +136,29 @@
         setLayerPaths(mClassLoader, layerPaths);
     }
 
+    private static List<String> getGlobalSettingsString(Bundle bundle, String globalSetting) {
+        List<String> valueList = null;
+        final String settingsValue = bundle.getString(globalSetting);
+
+        if (settingsValue != null) {
+            valueList = new ArrayList<>(Arrays.asList(settingsValue.split(",")));
+        } else {
+            valueList = new ArrayList<>();
+        }
+
+        return valueList;
+    }
+
     /**
      * Choose whether the current process should use the builtin or an updated driver.
      */
     private static void chooseDriver(Context context, Bundle coreSettings) {
-        String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
+        final String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
         if (driverPackageName == null || driverPackageName.isEmpty()) {
             return;
         }
 
-        // To minimize risk of driver updates crippling the device beyond user repair, never use an
-        // updated driver for privileged or non-updated system apps. Presumably pre-installed apps
-        // were tested thoroughly with the pre-installed driver.
-        ApplicationInfo ai = context.getApplicationInfo();
-        if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
-            if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app");
-            return;
-        }
-
-        String applicationPackageName = context.getPackageName();
-        String devOptInApplicationName = coreSettings.getString(
-                Settings.Global.GUP_DEV_OPT_IN_APPS);
-        boolean devOptIn = applicationPackageName.equals(devOptInApplicationName);
-        boolean whitelisted = onWhitelist(context, driverPackageName, ai.packageName);
-        if (!devOptIn && !whitelisted) {
-            if (DEBUG) {
-                Log.w(TAG, applicationPackageName + " is not on the whitelist.");
-            }
-            return;
-        }
-
-        ApplicationInfo driverInfo;
+        final ApplicationInfo driverInfo;
         try {
             driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
                     PackageManager.MATCH_SYSTEM_ONLY);
@@ -184,7 +176,59 @@
             return;
         }
 
-        String abi = chooseAbi(driverInfo);
+        // To minimize risk of driver updates crippling the device beyond user repair, never use an
+        // updated driver for privileged or non-updated system apps. Presumably pre-installed apps
+        // were tested thoroughly with the pre-installed driver.
+        final ApplicationInfo ai = context.getApplicationInfo();
+        if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
+            if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app");
+            return;
+        }
+
+        // GAME_DRIVER_ALL_APPS
+        // 0: Default (Invalid values fallback to default as well)
+        // 1: All apps use Game Driver
+        // 2: All apps use system graphics driver
+        final int gameDriverAllApps = coreSettings.getInt(Settings.Global.GAME_DRIVER_ALL_APPS, 0);
+        if (gameDriverAllApps == 2) {
+            if (DEBUG) {
+                Log.w(TAG, "Game Driver is turned off on this device");
+            }
+            return;
+        }
+
+        if (gameDriverAllApps != 1) {
+            // GAME_DRIVER_OPT_OUT_APPS has higher priority than GAME_DRIVER_OPT_IN_APPS
+            if (getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_OUT_APPS)
+                            .contains(ai.packageName)) {
+                if (DEBUG) {
+                    Log.w(TAG, ai.packageName + " opts out from Game Driver.");
+                }
+                return;
+            }
+            final boolean isOptIn =
+                    getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS)
+                            .contains(ai.packageName);
+            final List<String> whitelist = getGlobalSettingsString(coreSettings,
+                    Settings.Global.GAME_DRIVER_WHITELIST);
+            if (!isOptIn && whitelist.indexOf(GAME_DRIVER_WHITELIST_ALL) != 0
+                    && !whitelist.contains(ai.packageName)) {
+                if (DEBUG) {
+                    Log.w(TAG, ai.packageName + " is not on the whitelist.");
+                }
+                return;
+            }
+
+            // If the application is not opted-in and check whether it's on the blacklist,
+            // terminate early if it's on the blacklist and fallback to system driver.
+            if (!isOptIn
+                    && getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_BLACKLIST)
+                            .contains(ai.packageName)) {
+                return;
+            }
+        }
+
+        final String abi = chooseAbi(driverInfo);
         if (abi == null) {
             if (DEBUG) {
                 // This is the normal case for the pre-installed empty driver package, don't spam
@@ -195,16 +239,23 @@
             return;
         }
 
-        StringBuilder sb = new StringBuilder();
+        final StringBuilder sb = new StringBuilder();
         sb.append(driverInfo.nativeLibraryDir)
           .append(File.pathSeparator);
         sb.append(driverInfo.sourceDir)
           .append("!/lib/")
           .append(abi);
-        String paths = sb.toString();
+        final String paths = sb.toString();
 
-        if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths);
-        setDriverPath(paths);
+        final String sphalLibraries =
+                coreSettings.getString(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES);
+
+        if (DEBUG) {
+            Log.v(TAG,
+                    "gfx driver package search path: " + paths
+                            + ", required sphal libraries: " + sphalLibraries);
+        }
+        setDriverPathAndSphalLibraries(paths, sphalLibraries);
     }
 
     /**
@@ -218,7 +269,7 @@
      * Should only be called after chooseDriver().
      */
     public static void earlyInitEGL() {
-        Thread eglInitThread = new Thread(
+        final Thread eglInitThread = new Thread(
                 () -> {
                     EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
                 },
@@ -227,7 +278,7 @@
     }
 
     private static String chooseAbi(ApplicationInfo ai) {
-        String isa = VMRuntime.getCurrentInstructionSet();
+        final String isa = VMRuntime.getCurrentInstructionSet();
         if (ai.primaryCpuAbi != null &&
                 isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) {
             return ai.primaryCpuAbi;
@@ -239,32 +290,7 @@
         return null;
     }
 
-    private static boolean onWhitelist(Context context, String driverPackageName,
-            String applicationPackageName) {
-        try {
-            Context driverContext = context.createPackageContext(driverPackageName,
-                                                                 Context.CONTEXT_RESTRICTED);
-            AssetManager assets = driverContext.getAssets();
-            InputStream stream = assets.open(GUP_WHITELIST_FILENAME);
-            BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
-            for (String packageName; (packageName = reader.readLine()) != null; ) {
-                if (packageName.equals(applicationPackageName)) {
-                    return true;
-                }
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            if (DEBUG) {
-                Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
-            }
-        } catch (IOException e) {
-            if (DEBUG) {
-                Log.w(TAG, "Failed to load whitelist driver package, abort.");
-            }
-        }
-        return false;
-    }
-
     private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
     private static native void setDebugLayers(String layers);
-    private static native void setDriverPath(String path);
+    private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
 }
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index ef28f07..0827fd6 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -967,6 +967,23 @@
                 // spam the call log with its own entries, causing entries from Telephony to be
                 // removed.
                 final Uri result = resolver.insert(uri, values);
+                if (result != null) {
+                    String lastPathSegment = result.getLastPathSegment();
+                    // When inserting into the call log, if ContentProvider#insert detect an appops
+                    // denial a non-null "silent rejection" URI is returned which ends in 0.
+                    // Example: content://call_log/calls/0
+                    // The 0 in the last part of the path indicates a fake call id of 0.
+                    // A denial when logging calls from the platform is bad; there is no other
+                    // logging to indicate that this has happened so we will check for that scenario
+                    // here and log a warning so we have a hint as to what is going on.
+                    if (lastPathSegment != null && lastPathSegment.equals("0")) {
+                        Log.w(LOG_TAG, "Failed to insert into call log due to appops denial;"
+                                + " resultUri=" + result);
+                    }
+                } else {
+                    Log.w(LOG_TAG, "Failed to insert into call log; null result uri.");
+                }
+
                 if (values.containsKey(PHONE_ACCOUNT_ID)
                         && !TextUtils.isEmpty(values.getAsString(PHONE_ACCOUNT_ID))
                         && values.containsKey(PHONE_ACCOUNT_COMPONENT_NAME)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bf33e06..f98641d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11446,16 +11446,55 @@
         public static final String GPU_DEBUG_APP = "gpu_debug_app";
 
         /**
-         * Apps that are selected to use Game Update Package.
+         * Game Driver global preference for all Apps.
+         * 0 = Default
+         * 1 = All Apps use Game Driver
+         * 2 = All Apps use system graphics driver
          * @hide
          */
-        public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps";
+        public static final String GAME_DRIVER_ALL_APPS = "game_driver_all_apps";
 
         /**
-         * Apps on the black list that are forbidden to useGame Update Package.
+         * List of Apps selected to use Game Driver.
+         * i.e. <pkg1>,<pkg2>,...,<pkgN>
          * @hide
          */
-        public static final String GUP_BLACK_LIST = "gup_black_list";
+        public static final String GAME_DRIVER_OPT_IN_APPS = "game_driver_opt_in_apps";
+
+        /**
+         * List of Apps selected not to use Game Driver.
+         * i.e. <pkg1>,<pkg2>,...,<pkgN>
+         * @hide
+         */
+        public static final String GAME_DRIVER_OPT_OUT_APPS = "game_driver_opt_out_apps";
+
+        /**
+         * Apps on the blacklist that are forbidden to use Game Driver.
+         * @hide
+         */
+        public static final String GAME_DRIVER_BLACKLIST = "game_driver_blacklist";
+
+        /**
+         * List of blacklists, each blacklist is a blacklist for a specific version of Game Driver.
+         * @hide
+         */
+        public static final String GAME_DRIVER_BLACKLISTS = "game_driver_blacklists";
+
+        /**
+         * Apps on the whitelist that are allowed to use Game Driver.
+         * The string is a list of application package names, seperated by comma.
+         * i.e. <apk1>,<apk2>,...,<apkN>
+         * @hide
+         */
+        public static final String GAME_DRIVER_WHITELIST = "game_driver_whitelist";
+
+        /**
+         * List of libraries in sphal accessible by Game Driver
+         * The string is a list of library names, separated by colon.
+         * i.e. <lib1>:<lib2>:...:<libN>
+         * @hide
+         */
+        public static final String GAME_DRIVER_SPHAL_LIBRARIES = "game_driver_sphal_libraries";
 
         /**
          * Ordered GPU debug layer list
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index dfa5de6..b95d500 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -23,9 +23,12 @@
 
 namespace {
 
-void setDriverPath(JNIEnv* env, jobject clazz, jstring path) {
+void setDriverPathAndSphalLibraries_native(JNIEnv* env, jobject clazz, jstring path,
+                                           jstring sphalLibraries) {
     ScopedUtfChars pathChars(env, path);
-    android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
+    ScopedUtfChars sphalLibrariesChars(env, sphalLibraries);
+    android::GraphicsEnv::getInstance().setDriverPathAndSphalLibraries(pathChars.c_str(),
+                                                                       sphalLibrariesChars.c_str());
 }
 
 void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -43,7 +46,7 @@
 }
 
 const JNINativeMethod g_methods[] = {
-    { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+    { "setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPathAndSphalLibraries_native) },
     { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
     { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
 };
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 7c9176a..a44ec9a 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -384,11 +384,26 @@
         // App allowed to load GPU debug layers.
         optional SettingProto debug_app = 1;
         optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        // Apps opt in to load graphics driver from Game Update Package
-        // instead of native graphcis driver through developer options.
-        optional SettingProto gup_dev_opt_in_apps = 8;
-        // Apps on the black list that are forbidden to useGame Update Package.
-        optional SettingProto gup_black_list = 9;
+        // Game Driver - global preference for all Apps
+        // 0 = Default
+        // 1 = All Apps use Game Driver
+        // 2 = All Apps use system graphics driver
+        optional SettingProto game_driver_all_apps = 8;
+        // Game Driver - List of Apps selected to use Game Driver
+        // i.e. <pkg1>,<pkg2>,...,<pkgN>
+        optional SettingProto game_driver_opt_in_apps = 9;
+        // Game Driver - List of Apps selected not to use Game Driver
+        // i.e. <pkg1>,<pkg2>,...,<pkgN>
+        optional SettingProto game_driver_opt_out_apps = 10;
+        // Game Driver - List of Apps that are forbidden to use Game Driver
+        optional SettingProto game_driver_blacklist = 11;
+        // Game Driver - List of Apps that are allowed to use Game Driver
+        optional SettingProto game_driver_whitelist = 12;
+        // Game Driver - List of blacklists, each blacklist is a blacklist for
+        // a specific Game Driver version
+        optional SettingProto game_driver_blacklists = 14;
+        // Game Driver - List of libraries in sphal accessible by Game Driver
+        optional SettingProto game_driver_sphal_libraries = 16;
     }
     optional Gpu gpu = 59;
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 7b72928..76356ed 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -444,8 +444,13 @@
                     Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
                     Settings.Global.GPU_DEBUG_APP,
                     Settings.Global.GPU_DEBUG_LAYERS,
-                    Settings.Global.GUP_DEV_OPT_IN_APPS,
-                    Settings.Global.GUP_BLACK_LIST,
+                    Settings.Global.GAME_DRIVER_ALL_APPS,
+                    Settings.Global.GAME_DRIVER_OPT_IN_APPS,
+                    Settings.Global.GAME_DRIVER_OPT_OUT_APPS,
+                    Settings.Global.GAME_DRIVER_BLACKLISTS,
+                    Settings.Global.GAME_DRIVER_BLACKLIST,
+                    Settings.Global.GAME_DRIVER_WHITELIST,
+                    Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
diff --git a/graphics/proto/Android.bp b/graphics/proto/Android.bp
new file mode 100644
index 0000000..1d06348
--- /dev/null
+++ b/graphics/proto/Android.bp
@@ -0,0 +1,11 @@
+java_library_static {
+    name: "game-driver-protos",
+    host_supported: true,
+    proto: {
+        type: "lite",
+    },
+    srcs: ["game_driver.proto"],
+    no_framework_libs: true,
+    jarjar_rules: "jarjar-rules.txt",
+    sdk_version: "28",
+}
diff --git a/graphics/proto/game_driver.proto b/graphics/proto/game_driver.proto
new file mode 100644
index 0000000..fd7ffcc
--- /dev/null
+++ b/graphics/proto/game_driver.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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.
+ */
+
+syntax = "proto2";
+
+package android.gamedriver;
+
+option java_package = "android.gamedriver";
+option java_outer_classname = "GameDriverProto";
+
+message Blacklist {
+    optional int64 version_code = 1;
+    repeated string package_names = 2;
+}
+
+message Blacklists {
+    repeated Blacklist blacklists = 1;
+}
diff --git a/graphics/proto/jarjar-rules.txt b/graphics/proto/jarjar-rules.txt
new file mode 100644
index 0000000..4e40637
--- /dev/null
+++ b/graphics/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.** com.android.framework.protobuf.@1
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 5817118..e0bb862 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -37,6 +37,7 @@
         "src/**/*.java",
         ":framework-networkstack-shared-srcs",
         ":services-networkstack-shared-srcs",
+        ":statslog-networkstack-java-gen",
     ],
     static_libs: [
         "androidx.annotation_annotation",
@@ -104,3 +105,11 @@
     certificate: "networkstack",
     manifest: "AndroidManifest.xml",
 }
+
+genrule {
+    name: "statslog-networkstack-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module network_stack" +
+         " --javaPackage com.android.networkstack.metrics --javaClass NetworkStackStatsLog",
+    out: ["com/android/networkstack/metrics/NetworkStackStatsLog.java"],
+}
diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java b/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java
similarity index 99%
rename from packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java
rename to packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java
index 225dc0f..2523ecd 100644
--- a/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java
+++ b/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.metrics;
+package com.android.networkstack.metrics;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java b/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java
similarity index 87%
rename from packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java
rename to packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java
index c96411e..9308901 100644
--- a/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java
+++ b/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.metrics;
+package com.android.networkstack.metrics;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -41,7 +41,6 @@
     private static int probeResultToEnum(@Nullable final CaptivePortalProbeResult result) {
         if (result == null) return DataStallEventProto.INVALID;
 
-        // TODO: Add partial connectivity support.
         if (result.isSuccessful()) {
             return DataStallEventProto.VALID;
         } else if (result.isPortal()) {
@@ -63,6 +62,12 @@
             Log.d(TAG, "write: " + stats + " with result: " + validationResult
                     + ", dns: " + HexDump.toHexString(stats.mDns));
         }
-        // TODO(b/124613085): Send to Statsd once the public StatsLog API is ready.
+        NetworkStackStatsLog.write(NetworkStackStatsLog.DATA_STALL_EVENT,
+                stats.mEvaluationType,
+                validationResult,
+                stats.mNetworkType,
+                stats.mWifiInfo,
+                stats.mCellularInfo,
+                stats.mDns);
     }
 }
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index d88e3dc..093235e 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -69,8 +69,6 @@
 import android.net.Uri;
 import android.net.captiveportal.CaptivePortalProbeResult;
 import android.net.captiveportal.CaptivePortalProbeSpec;
-import android.net.metrics.DataStallDetectionStats;
-import android.net.metrics.DataStallStatsUtils;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.metrics.ValidationProbeEvent;
@@ -106,6 +104,8 @@
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.TrafficStatsConstants;
 import com.android.networkstack.R;
+import com.android.networkstack.metrics.DataStallDetectionStats;
+import com.android.networkstack.metrics.DataStallStatsUtils;
 
 import java.io.IOException;
 import java.net.HttpURLConnection;
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
index 5650f21..bee4bbd9 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
@@ -33,8 +33,8 @@
 import android.net.ipmemorystore.Blob;
 import android.net.ipmemorystore.IOnBlobRetrievedListener;
 import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
-import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
 import android.net.ipmemorystore.IOnStatusListener;
 import android.net.ipmemorystore.NetworkAttributes;
 import android.net.ipmemorystore.NetworkAttributesParcelable;
@@ -297,16 +297,16 @@
      */
     @Override
     public void isSameNetwork(@Nullable final String l2Key1, @Nullable final String l2Key2,
-            @Nullable final IOnSameNetworkResponseListener listener) {
+            @Nullable final IOnSameL3NetworkResponseListener listener) {
         if (null == listener) return;
         mExecutor.execute(() -> {
             try {
                 if (null == l2Key1 || null == l2Key2) {
-                    listener.onSameNetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+                    listener.onSameL3NetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
                     return;
                 }
                 if (null == mDb) {
-                    listener.onSameNetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+                    listener.onSameL3NetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
                     return;
                 }
                 try {
@@ -315,16 +315,16 @@
                     final NetworkAttributes attr2 =
                             IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key2);
                     if (null == attr1 || null == attr2) {
-                        listener.onSameNetworkResponse(makeStatus(SUCCESS),
+                        listener.onSameL3NetworkResponse(makeStatus(SUCCESS),
                                 new SameL3NetworkResponse(l2Key1, l2Key2,
                                         -1f /* never connected */).toParcelable());
                         return;
                     }
                     final float confidence = attr1.getNetworkGroupSamenessConfidence(attr2);
-                    listener.onSameNetworkResponse(makeStatus(SUCCESS),
+                    listener.onSameL3NetworkResponse(makeStatus(SUCCESS),
                             new SameL3NetworkResponse(l2Key1, l2Key2, confidence).toParcelable());
                 } catch (Exception e) {
-                    listener.onSameNetworkResponse(makeStatus(ERROR_GENERIC), null);
+                    listener.onSameL3NetworkResponse(makeStatus(ERROR_GENERIC), null);
                 }
             } catch (final RemoteException e) {
                 // Client at the other end died
@@ -343,7 +343,7 @@
      */
     @Override
     public void retrieveNetworkAttributes(@Nullable final String l2Key,
-            @Nullable final IOnNetworkAttributesRetrieved listener) {
+            @Nullable final IOnNetworkAttributesRetrievedListener listener) {
         if (null == listener) return;
         mExecutor.execute(() -> {
             try {
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 7d9eb9b..594f2ca 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -62,8 +62,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.captiveportal.CaptivePortalProbeResult;
-import android.net.metrics.DataStallDetectionStats;
-import android.net.metrics.DataStallStatsUtils;
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.SharedLog;
 import android.net.wifi.WifiInfo;
@@ -81,6 +79,9 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.networkstack.metrics.DataStallDetectionStats;
+import com.android.networkstack.metrics.DataStallStatsUtils;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
index 94cc589..a00eff7 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -31,8 +31,8 @@
 import android.net.ipmemorystore.Blob;
 import android.net.ipmemorystore.IOnBlobRetrievedListener;
 import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
-import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
 import android.net.ipmemorystore.IOnStatusListener;
 import android.net.ipmemorystore.NetworkAttributes;
 import android.net.ipmemorystore.NetworkAttributesParcelable;
@@ -163,9 +163,9 @@
     private interface OnNetworkAttributesRetrievedListener  {
         void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attr);
     }
-    private IOnNetworkAttributesRetrieved onNetworkAttributesRetrieved(
+    private IOnNetworkAttributesRetrievedListener onNetworkAttributesRetrieved(
             final OnNetworkAttributesRetrievedListener functor) {
-        return new IOnNetworkAttributesRetrieved() {
+        return new IOnNetworkAttributesRetrievedListener() {
             @Override
             public void onNetworkAttributesRetrieved(final StatusParcelable status,
                     final String l2Key, final NetworkAttributesParcelable attributes)
@@ -182,17 +182,17 @@
     }
 
     /** Helper method to make an IOnSameNetworkResponseListener */
-    private interface OnSameNetworkResponseListener {
-        void onSameNetworkResponse(Status status, SameL3NetworkResponse answer);
+    private interface OnSameL3NetworkResponseListener {
+        void onSameL3NetworkResponse(Status status, SameL3NetworkResponse answer);
     }
-    private IOnSameNetworkResponseListener onSameResponse(
-            final OnSameNetworkResponseListener functor) {
-        return new IOnSameNetworkResponseListener() {
+    private IOnSameL3NetworkResponseListener onSameResponse(
+            final OnSameL3NetworkResponseListener functor) {
+        return new IOnSameL3NetworkResponseListener() {
             @Override
-            public void onSameNetworkResponse(final StatusParcelable status,
+            public void onSameL3NetworkResponse(final StatusParcelable status,
                     final SameL3NetworkResponseParcelable sameL3Network)
                     throws RemoteException {
-                functor.onSameNetworkResponse(new Status(status),
+                functor.onSameL3NetworkResponse(new Status(status),
                         null == sameL3Network ? null : new SameL3NetworkResponse(sameL3Network));
             }
 
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 2c4abae..d5b1217 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1131,7 +1131,4 @@
 
     <!-- The notice header of Third-party licenses. not translatable -->
     <string name="notice_header" translatable="false"></string>
-
-    <!-- UI debug setting: opt in to use updated graphics driver? [CHAR LIMIT=100] -->
-    <string name="gup_dev_opt_in_app_summary">Opt in app to use Game Update Package in developement</string>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 759b51c..c60e352 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -648,11 +648,26 @@
                 Settings.Global.GPU_DEBUG_LAYERS,
                 GlobalSettingsProto.Gpu.DEBUG_LAYERS);
         dumpSetting(s, p,
-                Settings.Global.GUP_DEV_OPT_IN_APPS,
-                GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS);
+                Settings.Global.GAME_DRIVER_ALL_APPS,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_ALL_APPS);
         dumpSetting(s, p,
-                Settings.Global.GUP_BLACK_LIST,
-                GlobalSettingsProto.Gpu.GUP_BLACK_LIST);
+                Settings.Global.GAME_DRIVER_OPT_IN_APPS,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_OPT_IN_APPS);
+        dumpSetting(s, p,
+                Settings.Global.GAME_DRIVER_OPT_OUT_APPS,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_OPT_OUT_APPS);
+        dumpSetting(s, p,
+                Settings.Global.GAME_DRIVER_BLACKLIST,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_BLACKLIST);
+        dumpSetting(s, p,
+                Settings.Global.GAME_DRIVER_WHITELIST,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_WHITELIST);
+        dumpSetting(s, p,
+                Settings.Global.GAME_DRIVER_BLACKLISTS,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_BLACKLISTS);
+        dumpSetting(s, p,
+                Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES,
+                GlobalSettingsProto.Gpu.GAME_DRIVER_SPHAL_LIBRARIES);
         p.end(gpuToken);
 
         final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d79d833..6902a70 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -6492,6 +6492,11 @@
     // OS: Q
     ACTION_EMERGENCY_DIALER_FROM_POWER_MENU = 1569;
 
+    // OPEN: Settings > Developer Options > Game Driver Preference
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_GAME_DRIVER_DASHBOARD = 1613;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c1aff75..244fef4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -634,7 +634,8 @@
      *    the first network for a given type changes, or if the default network
      *    changes.
      */
-    private class LegacyTypeTracker {
+    @VisibleForTesting
+    static class LegacyTypeTracker {
 
         private static final boolean DBG = true;
         private static final boolean VDBG = false;
@@ -660,10 +661,12 @@
          *  - dump is thread-safe with respect to concurrent add and remove calls.
          */
         private final ArrayList<NetworkAgentInfo> mTypeLists[];
+        @NonNull
+        private final ConnectivityService mService;
 
-        public LegacyTypeTracker() {
-            mTypeLists = (ArrayList<NetworkAgentInfo>[])
-                    new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
+        LegacyTypeTracker(@NonNull ConnectivityService service) {
+            mService = service;
+            mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
         }
 
         public void addSupportedType(int type) {
@@ -712,10 +715,10 @@
             }
 
             // Send a broadcast if this is the first network of its type or if it's the default.
-            final boolean isDefaultNetwork = isDefaultNetwork(nai);
+            final boolean isDefaultNetwork = mService.isDefaultNetwork(nai);
             if ((list.size() == 1) || isDefaultNetwork) {
                 maybeLogBroadcast(nai, DetailedState.CONNECTED, type, isDefaultNetwork);
-                sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
+                mService.sendLegacyNetworkBroadcast(nai, DetailedState.CONNECTED, type);
             }
         }
 
@@ -737,15 +740,15 @@
 
             if (wasFirstNetwork || wasDefault) {
                 maybeLogBroadcast(nai, state, type, wasDefault);
-                sendLegacyNetworkBroadcast(nai, state, type);
+                mService.sendLegacyNetworkBroadcast(nai, state, type);
             }
 
             if (!list.isEmpty() && wasFirstNetwork) {
                 if (DBG) log("Other network available for type " + type +
                               ", sending connected broadcast");
                 final NetworkAgentInfo replacement = list.get(0);
-                maybeLogBroadcast(replacement, state, type, isDefaultNetwork(replacement));
-                sendLegacyNetworkBroadcast(replacement, state, type);
+                maybeLogBroadcast(replacement, state, type, mService.isDefaultNetwork(replacement));
+                mService.sendLegacyNetworkBroadcast(replacement, state, type);
             }
         }
 
@@ -760,7 +763,7 @@
         // send out another legacy broadcast - currently only used for suspend/unsuspend
         // toggle
         public void update(NetworkAgentInfo nai) {
-            final boolean isDefault = isDefaultNetwork(nai);
+            final boolean isDefault = mService.isDefaultNetwork(nai);
             final DetailedState state = nai.networkInfo.getDetailedState();
             for (int type = 0; type < mTypeLists.length; type++) {
                 final ArrayList<NetworkAgentInfo> list = mTypeLists[type];
@@ -768,7 +771,7 @@
                 final boolean isFirst = contains && (nai == list.get(0));
                 if (isFirst || contains && isDefault) {
                     maybeLogBroadcast(nai, state, type, isDefault);
-                    sendLegacyNetworkBroadcast(nai, state, type);
+                    mService.sendLegacyNetworkBroadcast(nai, state, type);
                 }
             }
         }
@@ -804,7 +807,7 @@
             pw.println();
         }
     }
-    private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker();
+    private final LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(this);
 
     /**
      * Helper class which parses out priority arguments and dumps sections according to their
@@ -5371,7 +5374,8 @@
         }
     }
 
-    private boolean isDefaultNetwork(NetworkAgentInfo nai) {
+    @VisibleForTesting
+    protected boolean isDefaultNetwork(NetworkAgentInfo nai) {
         return nai == getDefaultNetwork();
     }
 
@@ -6671,7 +6675,8 @@
         }
     }
 
-    private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
+    @VisibleForTesting
+    protected void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
         // The NetworkInfo we actually send out has no bearing on the real
         // state of affairs. For example, if the default connection is mobile,
         // and a request for HIPRI has just gone away, we need to pretend that
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 40bf7bc..d19d2dd 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -19,6 +19,7 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetd;
@@ -53,6 +54,7 @@
 import java.net.InterfaceAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
+import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /** @hide */
@@ -226,6 +228,8 @@
             @NonNull Looper looper,
             @NonNull Context context,
             @NonNull String iface,
+            @Nullable LinkProperties lp,
+            boolean isMetered,
             int callingUid,
             @NonNull IBinder binder)
             throws RemoteException, SocketException {
@@ -245,9 +249,19 @@
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
+        if (!isMetered) {
+            nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        }
 
         // Build LinkProperties
-        LinkProperties lp = new LinkProperties();
+        if (lp == null) {
+            lp = new LinkProperties();
+        } else {
+            lp = new LinkProperties(lp);
+            // Use LinkAddress(es) from the interface itself to minimize how much the caller
+            // is trusted.
+            lp.setLinkAddresses(new ArrayList<>());
+        }
         lp.setInterfaceName(iface);
 
         // Find the currently assigned addresses, and add them to LinkProperties
@@ -284,7 +298,11 @@
      * <p>This method provides a Network that is useful only for testing.
      */
     @Override
-    public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
+    public void setupTestNetwork(
+            @NonNull String iface,
+            @Nullable LinkProperties lp,
+            boolean isMetered,
+            @NonNull IBinder binder) {
         enforceTestNetworkPermissions(mContext);
 
         checkNotNull(iface, "missing Iface");
@@ -315,6 +333,8 @@
                                             mHandler.getLooper(),
                                             mContext,
                                             iface,
+                                            lp,
+                                            isMetered,
                                             callingUid,
                                             binder);
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e18f374..5ebd173 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -318,7 +318,6 @@
 import android.media.audiofx.AudioEffect;
 import android.metrics.LogMaker;
 import android.net.Proxy;
-import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -2252,21 +2251,25 @@
                 }
             } break;
             case UPDATE_HTTP_PROXY_MSG: {
+                // Update the HTTP proxy for each application thread.
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
                         ProcessRecord r = mLruProcesses.get(i);
                         // Don't dispatch to isolated processes as they can't access
-                        // ConnectivityManager and don't have network privileges anyway.
-                        if (r.thread != null && !r.isolated) {
+                        // ConnectivityManager and don't have network privileges anyway. Exclude
+                        // system server and update it separately outside the AMS lock, to avoid
+                        // deadlock with Connectivity Service.
+                        if (r.pid != MY_PID && r.thread != null && !r.isolated) {
                             try {
                                 r.thread.updateHttpProxy();
                             } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to update http proxy for: " +
-                                        r.info.processName);
+                                Slog.w(TAG, "Failed to update http proxy for: "
+                                        + r.info.processName);
                             }
                         }
                     }
                 }
+                ActivityThread.updateHttpProxy(mContext);
             } break;
             case PROC_START_TIMEOUT_MSG: {
                 ProcessRecord app = (ProcessRecord)msg.obj;
@@ -2607,7 +2610,7 @@
             } break;
             }
         }
-    };
+    }
 
     static final int COLLECT_PSS_BG_MSG = 1;
 
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 63300a1..9ff47e0 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -55,8 +55,13 @@
         // add other system settings here...
 
         sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class);
-        sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class);
-        sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACK_LIST, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_ALL_APPS, int.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_IN_APPS, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLIST, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_WHITELIST, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLISTS, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class);
         // add other global settings here...
     }
 
diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java
new file mode 100644
index 0000000..647727f
--- /dev/null
+++ b/services/core/java/com/android/server/gpu/GpuService.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 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.
+ */
+
+package com.android.server.gpu;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.gamedriver.GameDriverProto.Blacklist;
+import android.gamedriver.GameDriverProto.Blacklists;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Base64;
+import android.util.Slog;
+
+import com.android.framework.protobuf.InvalidProtocolBufferException;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.SystemService;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Service to manage GPU related features.
+ *
+ * <p>GPU service is a core service that monitors, coordinates all GPU related features,
+ * as well as collect metrics about the GPU and GPU driver.</p>
+ */
+public class GpuService extends SystemService {
+    public static final String TAG = "GpuService";
+    public static final boolean DEBUG = false;
+
+    private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+    private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt";
+    private static final String GAME_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
+    private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
+
+    private final Context mContext;
+    private final String mDriverPackageName;
+    private final PackageManager mPackageManager;
+    private final Object mLock = new Object();
+    private ContentResolver mContentResolver;
+    private long mGameDriverVersionCode;
+    private SettingsObserver mSettingsObserver;
+    @GuardedBy("mLock")
+    private Blacklists mBlacklists;
+
+    public GpuService(Context context) {
+        super(context);
+
+        mContext = context;
+        mDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
+        mGameDriverVersionCode = -1;
+        mPackageManager = context.getPackageManager();
+        if (mDriverPackageName != null && !mDriverPackageName.isEmpty()) {
+            final IntentFilter packageFilter = new IntentFilter();
+            packageFilter.addAction(ACTION_PACKAGE_ADDED);
+            packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+            packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+            packageFilter.addDataScheme("package");
+            getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
+                                                packageFilter, null, null);
+        }
+    }
+
+    @Override
+    public void onStart() {
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_BOOT_COMPLETED) {
+            mContentResolver = mContext.getContentResolver();
+            mSettingsObserver = new SettingsObserver();
+            if (mDriverPackageName == null || mDriverPackageName.isEmpty()) {
+                return;
+            }
+            fetchGameDriverPackageProperties();
+            processBlacklists();
+            setBlacklist();
+        }
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        private final Uri mGameDriverBlackUri =
+                Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS);
+
+        SettingsObserver() {
+            super(new Handler());
+            mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this,
+                    UserHandle.USER_ALL);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (uri == null) {
+                return;
+            }
+
+            if (mGameDriverBlackUri.equals(uri)) {
+                processBlacklists();
+                setBlacklist();
+            }
+        }
+    }
+
+    private final class PackageReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+            final Uri data = intent.getData();
+            if (data == null && DEBUG) {
+                Slog.e(TAG, "Cannot handle package broadcast with null data");
+                return;
+            }
+            final String packageName = data.getSchemeSpecificPart();
+            if (!packageName.equals(mDriverPackageName)) {
+                return;
+            }
+
+            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+            switch (intent.getAction()) {
+                case ACTION_PACKAGE_ADDED:
+                case ACTION_PACKAGE_CHANGED:
+                case ACTION_PACKAGE_REMOVED:
+                    fetchGameDriverPackageProperties();
+                    setBlacklist();
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+    }
+
+    private static void assetToSettingsGlobal(Context context, Context driverContext,
+            String fileName, String settingsGlobal, CharSequence delimiter) {
+        try {
+            final BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(driverContext.getAssets().open(fileName)));
+            final ArrayList<String> assetStrings = new ArrayList<>();
+            for (String assetString; (assetString = reader.readLine()) != null; ) {
+                assetStrings.add(assetString);
+            }
+            Settings.Global.putString(context.getContentResolver(),
+                                      settingsGlobal,
+                                      String.join(delimiter, assetStrings));
+        } catch (IOException e) {
+            if (DEBUG) {
+                Slog.w(TAG, "Failed to load " + fileName + ", abort.");
+            }
+        }
+    }
+
+    private void fetchGameDriverPackageProperties() {
+        final ApplicationInfo driverInfo;
+        try {
+            driverInfo = mPackageManager.getApplicationInfo(mDriverPackageName,
+                                                            PackageManager.MATCH_SYSTEM_ONLY);
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) {
+                Slog.e(TAG, "driver package '" + mDriverPackageName + "' not installed");
+            }
+            return;
+        }
+
+        // O drivers are restricted to the sphal linker namespace, so don't try to use
+        // packages unless they declare they're compatible with that restriction.
+        if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+            if (DEBUG) {
+                Slog.w(TAG, "Driver package is not known to be compatible with O");
+            }
+            return;
+        }
+
+        // Reset the whitelist.
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_WHITELIST, "");
+        // Reset the sphal libraries
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, "");
+        mGameDriverVersionCode = driverInfo.longVersionCode;
+
+        try {
+            final Context driverContext = mContext.createPackageContext(mDriverPackageName,
+                                                                        Context.CONTEXT_RESTRICTED);
+
+            assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_WHITELIST_FILENAME,
+                    Settings.Global.GAME_DRIVER_WHITELIST, ",");
+
+            assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_SPHAL_LIBRARIES_FILENAME,
+                    Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, ":");
+
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) {
+                Slog.w(TAG, "driver package '" + mDriverPackageName + "' not installed");
+            }
+        }
+    }
+
+    private void processBlacklists() {
+        // TODO(b/121350991) Switch to DeviceConfig with property listener.
+        String base64String =
+                Settings.Global.getString(mContentResolver, Settings.Global.GAME_DRIVER_BLACKLISTS);
+        if (base64String == null || base64String.isEmpty()) {
+            return;
+        }
+
+        synchronized (mLock) {
+            // Reset all blacklists
+            mBlacklists = null;
+            try {
+                mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS));
+            } catch (IllegalArgumentException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Can't parse blacklist, skip and continue...");
+                }
+            } catch (InvalidProtocolBufferException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Can't parse blacklist, skip and continue...");
+                }
+            }
+        }
+    }
+
+    private void setBlacklist() {
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_BLACKLIST, "");
+        synchronized (mLock) {
+            if (mBlacklists == null) {
+                return;
+            }
+            List<Blacklist> blacklists = mBlacklists.getBlacklistsList();
+            for (Blacklist blacklist : blacklists) {
+                if (blacklist.getVersionCode() == mGameDriverVersionCode) {
+                    Settings.Global.putString(mContentResolver,
+                            Settings.Global.GAME_DRIVER_BLACKLIST,
+                            String.join(",", blacklist.getPackageNamesList()));
+                    return;
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 4baf70b..62ea95b 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -34,10 +34,10 @@
 per-file UserRestrictionsUtils.java = omakoto@google.com, rubinxu@google.com, sandness@google.com, yamasani@google.com
 
 # security
-per-file KeySetHandle.java = cbrubaker@google.com
-per-file KeySetManagerService.java = cbrubaker@google.com
-per-file PackageKeySetData.java = cbrubaker@google.com
-per-file PackageSignatures.java = cbrubaker@google.com
+per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com
+per-file KeySetManagerService.java = cbrubaker@google.com, nnk@google.com
+per-file PackageKeySetData.java = cbrubaker@google.com, nnk@google.com
+per-file PackageSignatures.java = cbrubaker@google.com, nnk@google.com
 per-file SELinuxMMAC.java = cbrubaker@google.com, jeffv@google.com, jgalenson@google.com, nnk@google.com
 
 # shortcuts
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7be7ab2..f0292aa 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -87,6 +87,7 @@
 import com.android.server.dreams.DreamManagerService;
 import com.android.server.emergency.EmergencyAffordanceService;
 import com.android.server.fingerprint.FingerprintService;
+import com.android.server.gpu.GpuService;
 import com.android.server.hdmi.HdmiControlService;
 import com.android.server.input.InputManagerService;
 import com.android.server.job.JobSchedulerService;
@@ -747,6 +748,11 @@
         traceBeginAndSlog("StartBugreportManagerService");
         mSystemServiceManager.startService(BugreportManagerService.class);
         traceEnd();
+
+        // Serivce for GPU and GPU driver.
+        traceBeginAndSlog("GpuService");
+        mSystemServiceManager.startService(GpuService.class);
+        traceEnd();
     }
 
     /**
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 8f48f5b..f73a285 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -59,6 +59,7 @@
     srcs: ["java/**/*.java"],
     static_libs: [
         "dnsresolver_aidl_interface-java",
+        "ipmemorystore-client",
         "netd_aidl_interface-java",
         "networkstack-aidl-interfaces-java",
     ]
diff --git a/services/net/java/android/net/IIpMemoryStore.aidl b/services/net/java/android/net/IIpMemoryStore.aidl
index 6f88dec..63feae6 100644
--- a/services/net/java/android/net/IIpMemoryStore.aidl
+++ b/services/net/java/android/net/IIpMemoryStore.aidl
@@ -20,8 +20,8 @@
 import android.net.ipmemorystore.NetworkAttributesParcelable;
 import android.net.ipmemorystore.IOnBlobRetrievedListener;
 import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
-import android.net.ipmemorystore.IOnSameNetworkResponseListener;
+import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
 import android.net.ipmemorystore.IOnStatusListener;
 
 /** {@hide} */
@@ -84,7 +84,7 @@
      * @param listener The listener that will be invoked to return the answer.
      * @return (through the listener) A SameL3NetworkResponse containing the answer and confidence.
      */
-    void isSameNetwork(String l2Key1, String l2Key2, IOnSameNetworkResponseListener listener);
+    void isSameNetwork(String l2Key1, String l2Key2, IOnSameL3NetworkResponseListener listener);
 
     /**
      * Retrieve the network attributes for a key.
@@ -95,7 +95,7 @@
      * @return (through the listener) The network attributes and the L2 key associated with
      *         the query.
      */
-    void retrieveNetworkAttributes(String l2Key, IOnNetworkAttributesRetrieved listener);
+    void retrieveNetworkAttributes(String l2Key, IOnNetworkAttributesRetrievedListener listener);
 
     /**
      * Retrieve previously stored private data.
diff --git a/services/net/java/android/net/IpMemoryStoreClient.java b/services/net/java/android/net/IpMemoryStoreClient.java
index 2f4fdbd..379c017 100644
--- a/services/net/java/android/net/IpMemoryStoreClient.java
+++ b/services/net/java/android/net/IpMemoryStoreClient.java
@@ -20,14 +20,13 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.ipmemorystore.Blob;
-import android.net.ipmemorystore.IOnBlobRetrievedListener;
-import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
-import android.net.ipmemorystore.IOnSameNetworkResponseListener;
-import android.net.ipmemorystore.IOnStatusListener;
 import android.net.ipmemorystore.NetworkAttributes;
+import android.net.ipmemorystore.OnBlobRetrievedListener;
+import android.net.ipmemorystore.OnL2KeyResponseListener;
+import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
+import android.net.ipmemorystore.OnSameL3NetworkResponseListener;
+import android.net.ipmemorystore.OnStatusListener;
 import android.net.ipmemorystore.Status;
-import android.net.ipmemorystore.StatusParcelable;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -50,12 +49,6 @@
     @NonNull
     protected abstract IIpMemoryStore getService() throws InterruptedException, ExecutionException;
 
-    protected StatusParcelable internalErrorStatus() {
-        final StatusParcelable error = new StatusParcelable();
-        error.resultCode = Status.ERROR_UNKNOWN;
-        return error;
-    }
-
     /**
      * Store network attributes for a given L2 key.
      * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
@@ -74,12 +67,13 @@
      */
     public void storeNetworkAttributes(@NonNull final String l2Key,
             @NonNull final NetworkAttributes attributes,
-            @Nullable final IOnStatusListener listener) {
+            @Nullable final OnStatusListener listener) {
         try {
             try {
-                getService().storeNetworkAttributes(l2Key, attributes.toParcelable(), listener);
+                getService().storeNetworkAttributes(l2Key, attributes.toParcelable(),
+                        OnStatusListener.toAIDL(listener));
             } catch (InterruptedException | ExecutionException m) {
-                listener.onComplete(internalErrorStatus());
+                listener.onComplete(new Status(Status.ERROR_UNKNOWN));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error storing network attributes", e);
@@ -99,12 +93,13 @@
      */
     public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
             @NonNull final String name, @NonNull final Blob data,
-            @Nullable final IOnStatusListener listener) {
+            @Nullable final OnStatusListener listener) {
         try {
             try {
-                getService().storeBlob(l2Key, clientId, name, data, listener);
+                getService().storeBlob(l2Key, clientId, name, data,
+                        OnStatusListener.toAIDL(listener));
             } catch (InterruptedException | ExecutionException m) {
-                listener.onComplete(internalErrorStatus());
+                listener.onComplete(new Status(Status.ERROR_UNKNOWN));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error storing blob", e);
@@ -126,12 +121,13 @@
      * Through the listener, returns the L2 key if one matched, or null.
      */
     public void findL2Key(@NonNull final NetworkAttributes attributes,
-            @NonNull final IOnL2KeyResponseListener listener) {
+            @NonNull final OnL2KeyResponseListener listener) {
         try {
             try {
-                getService().findL2Key(attributes.toParcelable(), listener);
+                getService().findL2Key(attributes.toParcelable(),
+                        OnL2KeyResponseListener.toAIDL(listener));
             } catch (InterruptedException | ExecutionException m) {
-                listener.onL2KeyResponse(internalErrorStatus(), null);
+                listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error finding L2 Key", e);
@@ -148,12 +144,13 @@
      * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
      */
     public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
-            @NonNull final IOnSameNetworkResponseListener listener) {
+            @NonNull final OnSameL3NetworkResponseListener listener) {
         try {
             try {
-                getService().isSameNetwork(l2Key1, l2Key2, listener);
+                getService().isSameNetwork(l2Key1, l2Key2,
+                        OnSameL3NetworkResponseListener.toAIDL(listener));
             } catch (InterruptedException | ExecutionException m) {
-                listener.onSameNetworkResponse(internalErrorStatus(), null);
+                listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error checking for network sameness", e);
@@ -170,12 +167,13 @@
      *         the query.
      */
     public void retrieveNetworkAttributes(@NonNull final String l2Key,
-            @NonNull final IOnNetworkAttributesRetrieved listener) {
+            @NonNull final OnNetworkAttributesRetrievedListener listener) {
         try {
             try {
-                getService().retrieveNetworkAttributes(l2Key, listener);
+                getService().retrieveNetworkAttributes(l2Key,
+                        OnNetworkAttributesRetrievedListener.toAIDL(listener));
             } catch (InterruptedException | ExecutionException m) {
-                listener.onNetworkAttributesRetrieved(internalErrorStatus(), null, null);
+                listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN), null, null);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error retrieving network attributes", e);
@@ -194,12 +192,13 @@
      *         and the name of the data associated with the query.
      */
     public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
-            @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
+            @NonNull final String name, @NonNull final OnBlobRetrievedListener listener) {
         try {
             try {
-                getService().retrieveBlob(l2Key, clientId, name, listener);
+                getService().retrieveBlob(l2Key, clientId, name,
+                        OnBlobRetrievedListener.toAIDL(listener));
             } catch (InterruptedException | ExecutionException m) {
-                listener.onBlobRetrieved(internalErrorStatus(), null, null, null);
+                listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN), null, null, null);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error retrieving blob", e);
diff --git a/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl b/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
similarity index 94%
rename from services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
rename to services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
index fb4ca3b..870e217 100644
--- a/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
+++ b/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
@@ -20,7 +20,7 @@
 import android.net.ipmemorystore.StatusParcelable;
 
 /** {@hide} */
-oneway interface IOnNetworkAttributesRetrieved {
+oneway interface IOnNetworkAttributesRetrievedListener {
     /**
      * Network attributes were fetched for the specified L2 key. While the L2 key will never
      * be null, the attributes may be if no data is stored about this L2 key.
diff --git a/services/net/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl b/services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
similarity index 89%
rename from services/net/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
rename to services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
index 294bd3b..b8ccfb9 100644
--- a/services/net/java/android/net/ipmemorystore/IOnSameNetworkResponseListener.aidl
+++ b/services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
@@ -20,10 +20,10 @@
 import android.net.ipmemorystore.StatusParcelable;
 
 /** {@hide} */
-oneway interface IOnSameNetworkResponseListener {
+oneway interface IOnSameL3NetworkResponseListener {
     /**
      * The memory store has come up with the answer to a query that was sent.
      */
-     void onSameNetworkResponse(in StatusParcelable status,
+     void onSameL3NetworkResponse(in StatusParcelable status,
              in SameL3NetworkResponseParcelable response);
 }
diff --git a/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
new file mode 100644
index 0000000..22978a2
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return a blob.
+ * @hide
+ */
+public interface OnBlobRetrievedListener {
+    /**
+     * The memory store has come up with the answer to a query that was sent.
+     */
+    void onBlobRetrieved(Status status, String l2Key, String name, Blob blob);
+
+    /** Converts this OnBlobRetrievedListener to a parcelable object */
+    @NonNull
+    static IOnBlobRetrievedListener toAIDL(@NonNull final OnBlobRetrievedListener listener) {
+        return new IOnBlobRetrievedListener.Stub() {
+            @Override
+            public void onBlobRetrieved(final StatusParcelable statusParcelable, final String l2Key,
+                    final String name, final Blob blob) {
+                // NonNull, but still don't crash the system server if null
+                if (null != listener) {
+                    listener.onBlobRetrieved(new Status(statusParcelable), l2Key, name, blob);
+                }
+            }
+        };
+    }
+}
diff --git a/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
new file mode 100644
index 0000000..9e7c1c8
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return a L2 key.
+ * @hide
+ */
+public interface OnL2KeyResponseListener {
+    /**
+     * The operation has completed with the specified status.
+     */
+    void onL2KeyResponse(Status status, String l2Key);
+
+    /** Converts this OnL2KeyResponseListener to a parcelable object */
+    @NonNull
+    static IOnL2KeyResponseListener toAIDL(@NonNull final OnL2KeyResponseListener listener) {
+        return new IOnL2KeyResponseListener.Stub() {
+            @Override
+            public void onL2KeyResponse(final StatusParcelable statusParcelable,
+                    final String l2Key) {
+                // NonNull, but still don't crash the system server if null
+                if (null != listener) {
+                    listener.onL2KeyResponse(new Status(statusParcelable), l2Key);
+                }
+            }
+        };
+    }
+}
diff --git a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
new file mode 100644
index 0000000..59da268
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return network attributes.
+ * @hide
+ */
+public interface OnNetworkAttributesRetrievedListener {
+    /**
+     * The memory store has come up with the answer to a query that was sent.
+     */
+    void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attributes);
+
+    /** Converts this OnNetworkAttributesRetrievedListener to a parcelable object */
+    @NonNull
+    static IOnNetworkAttributesRetrievedListener toAIDL(
+            @NonNull final OnNetworkAttributesRetrievedListener listener) {
+        return new IOnNetworkAttributesRetrievedListener.Stub() {
+            @Override
+            public void onNetworkAttributesRetrieved(final StatusParcelable statusParcelable,
+                    final String l2Key,
+                    final NetworkAttributesParcelable networkAttributesParcelable) {
+                // NonNull, but still don't crash the system server if null
+                if (null != listener) {
+                    listener.onNetworkAttributesRetrieved(
+                            new Status(statusParcelable), l2Key,
+                            new NetworkAttributes(networkAttributesParcelable));
+                }
+            }
+        };
+    }
+}
diff --git a/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
new file mode 100644
index 0000000..0154fd2
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+
+/**
+ * A listener for the IpMemoryStore to return a response about network sameness.
+ * @hide
+ */
+public interface OnSameL3NetworkResponseListener {
+    /**
+     * The memory store has come up with the answer to a query that was sent.
+     */
+    void onSameL3NetworkResponse(Status status, SameL3NetworkResponse response);
+
+    /** Converts this OnSameL3NetworkResponseListener to a parcelable object */
+    @NonNull
+    static IOnSameL3NetworkResponseListener toAIDL(
+            @NonNull final OnSameL3NetworkResponseListener listener) {
+        return new IOnSameL3NetworkResponseListener.Stub() {
+            @Override
+            public void onSameL3NetworkResponse(final StatusParcelable statusParcelable,
+                    final SameL3NetworkResponseParcelable sameL3NetworkResponseParcelable) {
+                // NonNull, but still don't crash the system server if null
+                if (null != listener) {
+                    listener.onSameL3NetworkResponse(
+                            new Status(statusParcelable),
+                            new SameL3NetworkResponse(sameL3NetworkResponseParcelable));
+                }
+            }
+        };
+    }
+}
diff --git a/services/net/java/android/net/ipmemorystore/OnStatusListener.java b/services/net/java/android/net/ipmemorystore/OnStatusListener.java
new file mode 100644
index 0000000..824b7b0
--- /dev/null
+++ b/services/net/java/android/net/ipmemorystore/OnStatusListener.java
@@ -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.
+ */
+
+package android.net.ipmemorystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * A listener for the IpMemoryStore to return a status to a client.
+ * @hide
+ */
+public interface OnStatusListener {
+    /**
+     * The operation has completed with the specified status.
+     */
+    void onComplete(Status status);
+
+    /** Converts this OnStatusListener to a parcelable object */
+    @NonNull
+    static IOnStatusListener toAIDL(@Nullable final OnStatusListener listener) {
+        return new IOnStatusListener.Stub() {
+            @Override
+            public void onComplete(final StatusParcelable statusParcelable) {
+                if (null != listener) {
+                    listener.onComplete(new Status(statusParcelable));
+                }
+            }
+        };
+    }
+}
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index 2cb369d..3dd9318 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -95,6 +95,21 @@
         this.mIsUsingCarrierAggregation = isUsingCarrierAggregation;
     }
 
+    /**
+     * Constructor from another data specific registration info
+     *
+     * @param dsri another data specific registration info
+     * @hide
+     */
+    DataSpecificRegistrationInfo(DataSpecificRegistrationInfo dsri) {
+        maxDataCalls = dsri.maxDataCalls;
+        isDcNrRestricted = dsri.isDcNrRestricted;
+        isNrAvailable = dsri.isNrAvailable;
+        isEnDcAvailable = dsri.isEnDcAvailable;
+        mLteVopsSupportInfo = dsri.mLteVopsSupportInfo;
+        mIsUsingCarrierAggregation = dsri.mIsUsingCarrierAggregation;
+    }
+
     private DataSpecificRegistrationInfo(Parcel source) {
         maxDataCalls = source.readInt();
         isDcNrRestricted = source.readBoolean();
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 7b9f6d5..2fae949 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -280,6 +280,39 @@
     }
 
     /**
+     * Constructor from another network registration info
+     *
+     * @param nri Another network registration info
+     * @hide
+     */
+    public NetworkRegistrationInfo(NetworkRegistrationInfo nri) {
+        mDomain = nri.mDomain;
+        mTransportType = nri.mTransportType;
+        mRegistrationState = nri.mRegistrationState;
+        mRoamingType = nri.mRoamingType;
+        mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+        mRejectCause = nri.mRejectCause;
+        mEmergencyOnly = nri.mEmergencyOnly;
+        mAvailableServices = new ArrayList<>(nri.mAvailableServices);
+        if (nri.mCellIdentity != null) {
+            Parcel p = Parcel.obtain();
+            nri.mCellIdentity.writeToParcel(p, 0);
+            p.setDataPosition(0);
+            // TODO: Instead of doing this, we should create a formal way for cloning cell identity.
+            // Cell identity is not an immutable object so we have to deep copy it.
+            mCellIdentity = CellIdentity.CREATOR.createFromParcel(p);
+        }
+
+        if (nri.mVoiceSpecificInfo != null) {
+            mVoiceSpecificInfo = new VoiceSpecificRegistrationInfo(nri.mVoiceSpecificInfo);
+        }
+        if (nri.mDataSpecificInfo != null) {
+            mDataSpecificInfo = new DataSpecificRegistrationInfo(nri.mDataSpecificInfo);
+        }
+        mNrState = nri.mNrState;
+    }
+
+    /**
      * @return The transport type.
      */
     public @TransportType int getTransportType() { return mTransportType; }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f4a6984..86cdce1 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -418,7 +418,7 @@
                 Arrays.copyOf(s.mCellBandwidths, s.mCellBandwidths.length);
         mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
         mNetworkRegistrationInfos = s.mNetworkRegistrationInfos == null ? null :
-                new ArrayList<>(s.mNetworkRegistrationInfos);
+                s.getNetworkRegistrationInfoList();
         mNrFrequencyRange = s.mNrFrequencyRange;
     }
 
@@ -1113,16 +1113,16 @@
     /** @hide */
     @TestApi
     public void setVoiceRoamingType(@RoamingType int type) {
-        NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
+        NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        if (regState == null) {
-            regState = new NetworkRegistrationInfo.Builder()
+        if (regInfo == null) {
+            regInfo = new NetworkRegistrationInfo.Builder()
                     .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
                     .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                     .build();
-            addNetworkRegistrationInfo(regState);
         }
-        regState.setRoamingType(type);
+        regInfo.setRoamingType(type);
+        addNetworkRegistrationInfo(regInfo);
     }
 
     /** @hide */
@@ -1134,16 +1134,16 @@
     /** @hide */
     @TestApi
     public void setDataRoamingType(@RoamingType int type) {
-        NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
+        NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        if (regState == null) {
-            regState = new NetworkRegistrationInfo.Builder()
+        if (regInfo == null) {
+            regInfo = new NetworkRegistrationInfo.Builder()
                     .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
                     .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                     .build();
-            addNetworkRegistrationInfo(regState);
         }
-        regState.setRoamingType(type);
+        regInfo.setRoamingType(type);
+        addNetworkRegistrationInfo(regInfo);
     }
 
     /**
@@ -1305,16 +1305,16 @@
         Rlog.e(LOG_TAG, "ServiceState.setRilVoiceRadioTechnology() called. It's encouraged to "
                 + "use addNetworkRegistrationInfo() instead *******");
         // Sync to network registration state
-        NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
+        NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        if (regState == null) {
-            regState = new NetworkRegistrationInfo.Builder()
+        if (regInfo == null) {
+            regInfo = new NetworkRegistrationInfo.Builder()
                     .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
                     .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                     .build();
-            addNetworkRegistrationInfo(regState);
         }
-        regState.setAccessNetworkTechnology(rilRadioTechnologyToNetworkType(rt));
+        regInfo.setAccessNetworkTechnology(rilRadioTechnologyToNetworkType(rt));
+        addNetworkRegistrationInfo(regInfo);
     }
 
 
@@ -1326,17 +1326,17 @@
         // Sync to network registration state. Always write down the WWAN transport. For AP-assisted
         // mode device, use addNetworkRegistrationInfo() to set the correct transport if RAT
         // is IWLAN.
-        NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
+        NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
-        if (regState == null) {
-            regState = new NetworkRegistrationInfo.Builder()
+        if (regInfo == null) {
+            regInfo = new NetworkRegistrationInfo.Builder()
                     .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
                     .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                     .build();
-            addNetworkRegistrationInfo(regState);
         }
-        regState.setAccessNetworkTechnology(rilRadioTechnologyToNetworkType(rt));
+        regInfo.setAccessNetworkTechnology(rilRadioTechnologyToNetworkType(rt));
+        addNetworkRegistrationInfo(regInfo);
     }
 
     /** @hide */
@@ -1378,10 +1378,10 @@
      * @hide
      */
     public @NRState int getNrState() {
-        final NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
+        final NetworkRegistrationInfo regInfo = getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        if (regState == null) return NetworkRegistrationInfo.NR_STATE_NONE;
-        return regState.getNrState();
+        if (regInfo == null) return NetworkRegistrationInfo.NR_STATE_NONE;
+        return regInfo.getNrState();
     }
 
     /**
@@ -1775,7 +1775,11 @@
     @SystemApi
     public List<NetworkRegistrationInfo> getNetworkRegistrationInfoList() {
         synchronized (mNetworkRegistrationInfos) {
-            return new ArrayList<>(mNetworkRegistrationInfos);
+            List<NetworkRegistrationInfo> newList = new ArrayList<>();
+            for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) {
+                newList.add(new NetworkRegistrationInfo(nri));
+            }
+            return newList;
         }
     }
 
@@ -1795,7 +1799,7 @@
         synchronized (mNetworkRegistrationInfos) {
             for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
                 if (networkRegistrationInfo.getTransportType() == transportType) {
-                    list.add(networkRegistrationInfo);
+                    list.add(new NetworkRegistrationInfo(networkRegistrationInfo));
                 }
             }
         }
@@ -1819,7 +1823,7 @@
         synchronized (mNetworkRegistrationInfos) {
             for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
                 if (networkRegistrationInfo.getDomain() == domain) {
-                    list.add(networkRegistrationInfo);
+                    list.add(new NetworkRegistrationInfo(networkRegistrationInfo));
                 }
             }
         }
@@ -1844,7 +1848,7 @@
             for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
                 if (networkRegistrationInfo.getTransportType() == transportType
                         && networkRegistrationInfo.getDomain() == domain) {
-                    return networkRegistrationInfo;
+                    return new NetworkRegistrationInfo(networkRegistrationInfo);
                 }
             }
         }
@@ -1856,20 +1860,20 @@
      * @hide
      */
     @TestApi
-    public void addNetworkRegistrationInfo(NetworkRegistrationInfo regState) {
-        if (regState == null) return;
+    public void addNetworkRegistrationInfo(NetworkRegistrationInfo nri) {
+        if (nri == null) return;
 
         synchronized (mNetworkRegistrationInfos) {
             for (int i = 0; i < mNetworkRegistrationInfos.size(); i++) {
                 NetworkRegistrationInfo curRegState = mNetworkRegistrationInfos.get(i);
-                if (curRegState.getTransportType() == regState.getTransportType()
-                        && curRegState.getDomain() == regState.getDomain()) {
+                if (curRegState.getTransportType() == nri.getTransportType()
+                        && curRegState.getDomain() == nri.getDomain()) {
                     mNetworkRegistrationInfos.remove(i);
                     break;
                 }
             }
 
-            mNetworkRegistrationInfos.add(regState);
+            mNetworkRegistrationInfos.add(new NetworkRegistrationInfo(nri));
         }
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 586c815..58c05aa 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -294,6 +294,19 @@
     public static final String SUBSCRIPTION_TYPE = "subscription_type";
 
     /**
+     * TelephonyProvider column name white_listed_apn_data.
+     * It's a bitmask of APN types that will be allowed on this subscription even if it's metered
+     * and mobile data is turned off by the user.
+     * <P>Type: INTEGER (int)</P> For example, if TYPE_MMS is is true, Telephony will allow MMS
+     * data connection to setup even if MMS is metered and mobile_data is turned off on that
+     * subscription.
+     *
+     * Default value is 0.
+     */
+    /** @hide */
+    public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data";
+
+    /**
      * This constant is to designate a subscription as a Local-SIM Subscription.
      * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on the
      * device.
@@ -3087,6 +3100,31 @@
         return subId;
     }
 
+    /**
+     * Set whether a subscription always allows MMS connection. If true, MMS network
+     * request will be accepted by telephony even if user turns "mobile data" off
+     * on this subscription.
+     *
+     * @param subId which subscription it's setting to.
+     * @param alwaysAllow whether Mms data is always allowed.
+     * @return whether operation is successful.
+     *
+     * @hide
+     */
+    public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
+        try {
+            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+            if (iSub != null) {
+                return iSub.setAlwaysAllowMmsData(subId, alwaysAllow);
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+        return false;
+    }
+
     private interface CallISubMethodHelper {
         int callMethod(ISub iSub) throws RemoteException;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e1425b9..82cc1df 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -64,6 +64,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import android.telephony.data.ApnSetting;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
 import android.telephony.ims.aidl.IImsConfig;
@@ -10888,4 +10889,58 @@
         }
         return new Pair<Integer, Integer>(-1, -1);
     }
+
+    /**
+     * Return whether data is enabled for certain APN type. This will tell if framework will accept
+     * corresponding network requests on a subId.
+     *
+     * {@link #isDataEnabled()} is directly associated with users' Mobile data toggle on / off. If
+     * {@link #isDataEnabled()} returns false, it means in general all meter-ed data are disabled.
+     *
+     * This per APN type API gives a better idea whether data is allowed on a specific APN type.
+     * It will return true if:
+     *
+     *  1) User data is turned on, or
+     *  2) APN is un-metered for this subscription, or
+     *  3) APN type is whitelisted. E.g. MMS is whitelisted if
+     *  {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
+     *
+     * @return whether data is enabled for a apn type.
+     *
+     * @hide
+     */
+    public boolean isDataEnabledForApn(@ApnSetting.ApnType int apnType) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isDataEnabledForApn(apnType, getSubId(), pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Whether an APN type is metered or not. It will be evaluated with the subId associated
+     * with the TelephonyManager instance.
+     *
+     * @hide
+     */
+    public boolean isApnMetered(@ApnSetting.ApnType int apnType) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.isApnMetered(apnType, getSubId());
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+        return true;
+    }
 }
diff --git a/telephony/java/android/telephony/VoiceSpecificRegistrationInfo.java b/telephony/java/android/telephony/VoiceSpecificRegistrationInfo.java
index 18a533a..d43181e 100644
--- a/telephony/java/android/telephony/VoiceSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/VoiceSpecificRegistrationInfo.java
@@ -65,6 +65,19 @@
         this.defaultRoamingIndicator = defaultRoamingIndicator;
     }
 
+    /**
+     * Constructor from another voice specific registration info
+     *
+     * @param vsri another voice specific registration info
+     * @hide
+     */
+    VoiceSpecificRegistrationInfo(VoiceSpecificRegistrationInfo vsri) {
+        cssSupported = vsri.cssSupported;
+        roamingIndicator = vsri.roamingIndicator;
+        systemIsInPrl = vsri.systemIsInPrl;
+        defaultRoamingIndicator = vsri.defaultRoamingIndicator;
+    }
+
     private VoiceSpecificRegistrationInfo(Parcel source) {
         this.cssSupported = source.readBoolean();
         this.roamingIndicator = source.readInt();
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 6e8d038..bb5c251 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -94,6 +94,7 @@
     public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
     public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
     public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
+    public static final int EVENT_APN_WHITE_LIST_CHANGE = BASE + 51;
 
     /***** Constants *****/
 
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 118f5e2..f248893 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -283,4 +283,6 @@
     int getSimStateForSlotIndex(int slotIndex);
 
     boolean isActiveSubId(int subId, String callingPackage);
+
+    boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d173cc9..0ad6096 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1958,4 +1958,8 @@
     int getRadioHalVersion();
 
     boolean isModemEnabledForSlot(int slotIndex, String callingPackage);
+
+    boolean isDataEnabledForApn(int apnType, int subId, String callingPackage);
+
+    boolean isApnMetered(int apnType, int subId);
 }
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 9098f90..1fbb658 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -6,6 +6,7 @@
     static_libs: [
         "FrameworksNetCommonTests",
         "frameworks-base-testutils",
+        "frameworks-net-testutils",
         "framework-protos",
         "androidx.test.rules",
         "mockito-target-minus-junit4",
@@ -63,7 +64,7 @@
 android_test {
     name: "FrameworksNetTests",
     defaults: ["FrameworksNetTests-jni-defaults"],
-    srcs: ["java/**/*.java"],
+    srcs: ["java/**/*.java", "java/**/*.kt"],
     platform_apis: true,
     test_suites: ["device-tests"],
     certificate: "platform",
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 0a1ac75..9ee5858 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -21,6 +21,7 @@
     srcs: ["java/**/*.java"],
     static_libs: [
         "androidx.test.rules",
+        "frameworks-net-testutils",
         "junit",
     ],
     libs: [
diff --git a/tests/net/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
similarity index 100%
rename from tests/net/java/android/net/LinkAddressTest.java
rename to tests/net/common/java/android/net/LinkAddressTest.java
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
similarity index 100%
rename from tests/net/java/android/net/LinkPropertiesTest.java
rename to tests/net/common/java/android/net/LinkPropertiesTest.java
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
similarity index 100%
rename from tests/net/java/android/net/NetworkCapabilitiesTest.java
rename to tests/net/common/java/android/net/NetworkCapabilitiesTest.java
diff --git a/tests/net/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java
similarity index 100%
rename from tests/net/java/android/net/NetworkTest.java
rename to tests/net/common/java/android/net/NetworkTest.java
diff --git a/tests/net/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java
similarity index 100%
rename from tests/net/java/android/net/RouteInfoTest.java
rename to tests/net/common/java/android/net/RouteInfoTest.java
diff --git a/tests/net/java/android/net/StaticIpConfigurationTest.java b/tests/net/common/java/android/net/StaticIpConfigurationTest.java
similarity index 100%
rename from tests/net/java/android/net/StaticIpConfigurationTest.java
rename to tests/net/common/java/android/net/StaticIpConfigurationTest.java
diff --git a/tests/net/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
similarity index 96%
rename from tests/net/java/android/net/apf/ApfCapabilitiesTest.java
rename to tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
index 75752c3..7238895 100644
--- a/tests/net/java/android/net/apf/ApfCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
@@ -19,11 +19,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import android.net.shared.ParcelableTestUtil;
-
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.ParcelableTestUtil;
 import com.android.internal.util.TestUtils;
 
 import org.junit.Test;
diff --git a/tests/net/java/android/net/shared/InitialConfigurationTest.java b/tests/net/java/android/net/shared/InitialConfigurationTest.java
index 27bc13d..2fb8b19 100644
--- a/tests/net/java/android/net/shared/InitialConfigurationTest.java
+++ b/tests/net/java/android/net/shared/InitialConfigurationTest.java
@@ -17,7 +17,8 @@
 package android.net.shared;
 
 import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals;
+
+import static com.android.internal.util.ParcelableTestUtil.assertFieldCountEquals;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
diff --git a/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java
index 21a4988..d8f01e9 100644
--- a/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java
+++ b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java
@@ -19,7 +19,8 @@
 import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.shared.IpConfigurationParcelableUtil.fromStableParcelable;
 import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable;
-import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals;
+
+import static com.android.internal.util.ParcelableTestUtil.assertFieldCountEquals;
 
 import static org.junit.Assert.assertEquals;
 
diff --git a/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java b/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java
index 6fad89e..382afe0 100644
--- a/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java
+++ b/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java
@@ -17,9 +17,10 @@
 package android.net.shared;
 
 import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.shared.ParcelableTestUtil.assertFieldCountEquals;
 import static android.net.shared.ProvisioningConfiguration.fromStableParcelable;
 
+import static com.android.internal.util.ParcelableTestUtil.assertFieldCountEquals;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
new file mode 100644
index 0000000..d983b65
--- /dev/null
+++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package com.android.server
+
+import android.net.ConnectivityManager.TYPE_ETHERNET
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.net.ConnectivityManager.TYPE_WIFI
+import android.net.ConnectivityManager.TYPE_WIMAX
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.server.ConnectivityService.LegacyTypeTracker
+import com.android.server.connectivity.NetworkAgentInfo
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertSame
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+
+const val UNSUPPORTED_TYPE = TYPE_WIMAX
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class LegacyTypeTrackerTest {
+    private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET)
+
+    private val mMockService = mock(ConnectivityService::class.java).apply {
+        doReturn(false).`when`(this).isDefaultNetwork(any())
+    }
+    private val mTracker = LegacyTypeTracker(mMockService).apply {
+        supportedTypes.forEach {
+            addSupportedType(it)
+        }
+    }
+
+    @Test
+    fun testSupportedTypes() {
+        try {
+            mTracker.addSupportedType(supportedTypes[0])
+            fail("Expected IllegalStateException")
+        } catch (expected: IllegalStateException) {}
+        supportedTypes.forEach {
+            assertTrue(mTracker.isTypeSupported(it))
+        }
+        assertFalse(mTracker.isTypeSupported(UNSUPPORTED_TYPE))
+    }
+
+    @Test
+    fun testAddNetwork() {
+        val mobileNai = mock(NetworkAgentInfo::class.java)
+        val wifiNai = mock(NetworkAgentInfo::class.java)
+        mTracker.add(TYPE_MOBILE, mobileNai)
+        mTracker.add(TYPE_WIFI, wifiNai)
+        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        // Make sure adding a second NAI does not change the results.
+        val secondMobileNai = mock(NetworkAgentInfo::class.java)
+        mTracker.add(TYPE_MOBILE, secondMobileNai)
+        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        // Make sure removing a network that wasn't added for this type is a no-op.
+        mTracker.remove(TYPE_MOBILE, wifiNai, false /* wasDefault */)
+        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), mobileNai)
+        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        // Remove the top network for mobile and make sure the second one becomes the network
+        // of record for this type.
+        mTracker.remove(TYPE_MOBILE, mobileNai, false /* wasDefault */)
+        assertSame(mTracker.getNetworkForType(TYPE_MOBILE), secondMobileNai)
+        assertSame(mTracker.getNetworkForType(TYPE_WIFI), wifiNai)
+        // Make sure adding a network for an unsupported type does not register it.
+        mTracker.add(UNSUPPORTED_TYPE, mobileNai)
+        assertNull(mTracker.getNetworkForType(UNSUPPORTED_TYPE))
+    }
+}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
index fb84611..a83faf3 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.net.ipmemorystore;
+package com.android.server.connectivity.ipmemorystore;
 
 import static org.junit.Assert.assertEquals;
 
diff --git a/tests/net/util/Android.bp b/tests/net/util/Android.bp
new file mode 100644
index 0000000..d8c502d
--- /dev/null
+++ b/tests/net/util/Android.bp
@@ -0,0 +1,30 @@
+//
+// 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.
+//
+
+// Common utilities for network tests.
+java_library {
+    name: "frameworks-net-testutils",
+    srcs: ["java/**/*.java"],
+    // test_current to be also appropriate for CTS tests
+    sdk_version: "test_current",
+    static_libs: [
+        "androidx.annotation_annotation",
+        "junit",
+    ],
+    libs: [
+        "android.test.base.stubs",
+    ],
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/shared/ParcelableTestUtil.java b/tests/net/util/java/com/android/internal/util/ParcelableTestUtil.java
similarity index 97%
rename from tests/net/java/android/net/shared/ParcelableTestUtil.java
rename to tests/net/util/java/com/android/internal/util/ParcelableTestUtil.java
index 088ea3c..87537b9 100644
--- a/tests/net/java/android/net/shared/ParcelableTestUtil.java
+++ b/tests/net/util/java/com/android/internal/util/ParcelableTestUtil.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.shared;
+package com.android.internal.util;
 
 import static org.junit.Assert.assertEquals;
 
diff --git a/tests/net/java/com/android/internal/util/TestUtils.java b/tests/net/util/java/com/android/internal/util/TestUtils.java
similarity index 96%
rename from tests/net/java/com/android/internal/util/TestUtils.java
rename to tests/net/util/java/com/android/internal/util/TestUtils.java
index 57cc172..75329a8 100644
--- a/tests/net/java/com/android/internal/util/TestUtils.java
+++ b/tests/net/util/java/com/android/internal/util/TestUtils.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
-import android.annotation.NonNull;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -27,6 +26,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import androidx.annotation.NonNull;
+
 import java.util.concurrent.Executor;
 
 public final class TestUtils {
@@ -36,7 +37,7 @@
      * Block until the given Handler thread becomes idle, or until timeoutMs has passed.
      */
     public static void waitForIdleHandler(HandlerThread handlerThread, long timeoutMs) {
-        waitForIdleHandler(handlerThread.getThreadHandler(), timeoutMs);
+        waitForIdleLooper(handlerThread.getLooper(), timeoutMs);
     }
 
     /**
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 99420de..609e366 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -497,9 +497,9 @@
   }
 
   // Try parsing the code name.
-  std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
-  if (entry.first == trimmed_str) {
-    return entry.second;
+  Maybe<int> entry = GetDevelopmentSdkCodeNameVersion(trimmed_str);
+  if (entry) {
+    return entry.value();
   }
   return {};
 }
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 8ebde75..b4b6ff1 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -18,15 +18,17 @@
 
 #include <algorithm>
 #include <string>
-#include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 using android::StringPiece;
 
 namespace aapt {
 
-static const char* sDevelopmentSdkCodeName = "P";
-static ApiVersion sDevelopmentSdkLevel = 28;
+static ApiVersion sDevelopmentSdkLevel = 10000;
+static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
+    "Q", "R"
+});
 
 static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
     {0x021c, 1},
@@ -54,6 +56,7 @@
     {0x0530, SDK_NOUGAT_MR1},
     {0x0568, SDK_O},
     {0x056d, SDK_O_MR1},
+    {0x0586, SDK_P},
 };
 
 static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) {
@@ -71,8 +74,9 @@
   return iter->second;
 }
 
-std::pair<StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion() {
-  return std::make_pair(StringPiece(sDevelopmentSdkCodeName), sDevelopmentSdkLevel);
+Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const StringPiece& code_name) {
+  return (sDevelopmentSdkCodeNames.find(code_name) == sDevelopmentSdkCodeNames.end())
+      ? Maybe<ApiVersion>() : sDevelopmentSdkLevel;
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 9fa29f2..adb034a 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -57,7 +57,7 @@
 };
 
 ApiVersion FindAttributeSdkLevel(const ResourceId& id);
-std::pair<android::StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion();
+Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const android::StringPiece& code_name);
 
 }  // namespace aapt
 
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 4c23bd3..2ef8b99 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -705,35 +705,24 @@
 }
 
 TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
-  static constexpr const char* xml = R"xml(
+  auto doc = test::BuildXmlDom(R"xml(
       <android-sdk
-          label="P"
+          label="Q"
           minSdkVersion="25"
-          targetSdkVersion="%s"
-          maxSdkVersion="%s">
-      </android-sdk>)xml";
-
-  const auto& dev_sdk = GetDevelopmentSdkCodeNameAndVersion();
-  const char* codename = dev_sdk.first.data();
-  const ApiVersion& version = dev_sdk.second;
-
-  auto doc = test::BuildXmlDom(StringPrintf(xml, codename, codename));
+          targetSdkVersion="Q"
+          maxSdkVersion="Q">
+      </android-sdk>)xml");
 
   PostProcessingConfiguration config;
-  bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
-  ASSERT_TRUE(ok);
-
+  ASSERT_TRUE(AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
   ASSERT_EQ(1ul, config.android_sdks.size());
-  ASSERT_EQ(1u, config.android_sdks.count("P"));
-
-  auto& out = config.android_sdks["P"];
+  ASSERT_EQ(1u, config.android_sdks.count("Q"));
 
   AndroidSdk sdk;
   sdk.min_sdk_version = 25;
-  sdk.target_sdk_version = version;
-  sdk.max_sdk_version = version;
-
-  ASSERT_EQ(sdk, out);
+  sdk.target_sdk_version = 10000;
+  sdk.max_sdk_version = 10000;
+  ASSERT_EQ(sdk, config.android_sdks["Q"]);
 }
 
 TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java
index edb9a49..828cce7 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/Main.java
@@ -13,6 +13,9 @@
  */
 package lockedregioncodeinjection;
 
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
 import java.io.BufferedInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -24,8 +27,6 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassWriter;
 
 public class Main {
     public static void main(String[] args) throws IOException {
@@ -74,6 +75,7 @@
         while (srcEntries.hasMoreElements()) {
             ZipEntry entry = srcEntries.nextElement();
             ZipEntry newEntry = new ZipEntry(entry.getName());
+            newEntry.setTime(entry.getTime());
             zos.putNextEntry(newEntry);
             BufferedInputStream bis = new BufferedInputStream(zipSrc.getInputStream(entry));
 
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 49eee07..dc18b8d 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -219,6 +219,14 @@
       errorCount++;
       continue;
     }
+
+    // Doubles are not supported yet.
+    if (javaType == JAVA_TYPE_DOUBLE) {
+        print_error(field, "Doubles are not supported in atoms. Please change field %s to float\n",
+                    field->name().c_str());
+        errorCount++;
+        continue;
+    }
   }
 
   // Check that if there's an attribution chain, it's at position 1.
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index e0ea2077..8e4cb9f 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -48,6 +48,7 @@
   JAVA_TYPE_DOUBLE = 6,
   JAVA_TYPE_STRING = 7,
   JAVA_TYPE_ENUM = 8,
+  JAVA_TYPE_KEY_VALUE_PAIR = 9,
 
   JAVA_TYPE_OBJECT = -1,
   JAVA_TYPE_BYTE_ARRAY = -2,
@@ -118,4 +119,4 @@
 }  // namespace android
 
 
-#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
\ No newline at end of file
+#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 2d9b988..43b79cc 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -27,6 +27,11 @@
 const string DEFAULT_MODULE_NAME = "DEFAULT";
 const string DEFAULT_CPP_NAMESPACE = "android,util";
 const string DEFAULT_CPP_HEADER_IMPORT = "statslog.h";
+const string DEFAULT_JAVA_PACKAGE = "android.util";
+const string DEFAULT_JAVA_CLASS = "StatsLogInternal";
+
+const int JAVA_MODULE_REQUIRES_FLOAT = 0x01;
+const int JAVA_MODULE_REQUIRES_ATTRIBUTION = 0x02;
 
 using android::os::statsd::Atom;
 
@@ -807,11 +812,350 @@
     }
 }
 
+static void write_java_helpers_for_module(
+        FILE * out,
+        const AtomDecl &attributionDecl,
+        const int requiredHelpers) {
+    fprintf(out, "    private static void copyInt(byte[] buff, int pos, int val) {\n");
+    fprintf(out, "        buff[pos] = (byte) (val);\n");
+    fprintf(out, "        buff[pos + 1] = (byte) (val >> 8);\n");
+    fprintf(out, "        buff[pos + 2] = (byte) (val >> 16);\n");
+    fprintf(out, "        buff[pos + 3] = (byte) (val >> 24);\n");
+    fprintf(out, "        return;\n");
+    fprintf(out, "    }\n");
+    fprintf(out, "\n");
+
+    fprintf(out, "    private static void copyLong(byte[] buff, int pos, long val) {\n");
+    fprintf(out, "        buff[pos] = (byte) (val);\n");
+    fprintf(out, "        buff[pos + 1] = (byte) (val >> 8);\n");
+    fprintf(out, "        buff[pos + 2] = (byte) (val >> 16);\n");
+    fprintf(out, "        buff[pos + 3] = (byte) (val >> 24);\n");
+    fprintf(out, "        buff[pos + 4] = (byte) (val >> 32);\n");
+    fprintf(out, "        buff[pos + 5] = (byte) (val >> 40);\n");
+    fprintf(out, "        buff[pos + 6] = (byte) (val >> 48);\n");
+    fprintf(out, "        buff[pos + 7] = (byte) (val >> 56);\n");
+    fprintf(out, "        return;\n");
+    fprintf(out, "    }\n");
+    fprintf(out, "\n");
+
+    if (requiredHelpers & JAVA_MODULE_REQUIRES_FLOAT) {
+        fprintf(out, "    private static void copyFloat(byte[] buff, int pos, float val) {\n");
+        fprintf(out, "        copyInt(buff, pos, Float.floatToIntBits(val));\n");
+        fprintf(out, "        return;\n");
+        fprintf(out, "    }\n");
+        fprintf(out, "\n");
+    }
+
+    if (requiredHelpers & JAVA_MODULE_REQUIRES_ATTRIBUTION) {
+        fprintf(out, "    private static void writeAttributionChain(byte[] buff, int pos");
+        for (auto chainField : attributionDecl.fields) {
+            fprintf(out, ", %s[] %s",
+                java_type_name(chainField.javaType), chainField.name.c_str());
+        }
+        fprintf(out, ") {\n");
+
+        const char* uidName = attributionDecl.fields.front().name.c_str();
+        const char* tagName = attributionDecl.fields.back().name.c_str();
+
+        // Write the first list begin.
+        fprintf(out, "        buff[pos] = LIST_TYPE;\n");
+        fprintf(out, "        buff[pos + 1] = (byte) (%s.length);\n", tagName);
+        fprintf(out, "        pos += LIST_TYPE_OVERHEAD;\n");
+
+        // Iterate through the attribution chain and write the nodes.
+        fprintf(out, "        for (int i = 0; i < %s.length; i++) {\n", tagName);
+        // Write the list begin.
+        fprintf(out, "            buff[pos] = LIST_TYPE;\n");
+        fprintf(out, "            buff[pos + 1] = %lu;\n", attributionDecl.fields.size());
+        fprintf(out, "            pos += LIST_TYPE_OVERHEAD;\n");
+
+        // Write the uid.
+        fprintf(out, "            buff[pos] = INT_TYPE;\n");
+        fprintf(out, "            copyInt(buff, pos + 1, %s[i]);\n", uidName);
+        fprintf(out, "            pos += INT_TYPE_SIZE;\n");
+
+        // Write the tag.
+        fprintf(out, "            String %sStr = (%s[i] == null) ? \"\" : %s[i];\n",
+                tagName, tagName, tagName);
+        fprintf(out, "            byte[] %sByte = %sStr.getBytes(UTF_8);\n", tagName, tagName);
+        fprintf(out, "            buff[pos] = STRING_TYPE;\n");
+        fprintf(out, "            copyInt(buff, pos + 1, %sByte.length);\n", tagName);
+        fprintf(out, "            System.arraycopy("
+                "%sByte, 0, buff, pos + STRING_TYPE_OVERHEAD, %sByte.length);\n",
+                tagName, tagName);
+        fprintf(out, "            pos += STRING_TYPE_OVERHEAD + %sByte.length;\n", tagName);
+        fprintf(out, "        }\n");
+        fprintf(out, "    }\n");
+        fprintf(out, "\n");
+    }
+}
+
+
+static int write_java_non_chained_method_for_module(
+        FILE* out,
+        const map<vector<java_type_t>, set<string>>& signatures_to_modules,
+        const string& moduleName
+        ) {
+    for (auto signature_to_modules_it = signatures_to_modules.begin();
+            signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
+        // Skip if this signature is not needed for the module.
+        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
+            continue;
+        }
+
+        // Print method signature.
+        vector<java_type_t> signature = signature_to_modules_it->first;
+        fprintf(out, "    public static void write_non_chained(int code");
+        int argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature.begin();
+                arg != signature.end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                // Non chained signatures should not have attribution chains.
+                return 1;
+            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+                // Module logging does not yet support key value pair.
+                return 1;
+            } else {
+                fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+            }
+            argIndex++;
+        }
+        fprintf(out, ") {\n");
+
+        fprintf(out, "        write(code");
+        argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature.begin();
+                arg != signature.end(); arg++) {
+            // First two args are uid and tag of attribution chain.
+            if (argIndex == 1) {
+                fprintf(out, ", new int[] {arg%d}", argIndex);
+            } else if (argIndex == 2) {
+                fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
+            } else {
+                fprintf(out, ", arg%d", argIndex);
+            }
+            argIndex++;
+        }
+        fprintf(out, ");\n");
+        fprintf(out, "    }\n");
+        fprintf(out, "\n");
+    }
+    return 0;
+}
+
+static int write_java_method_for_module(
+        FILE* out,
+        const map<vector<java_type_t>, set<string>>& signatures_to_modules,
+        const AtomDecl &attributionDecl,
+        const string& moduleName,
+        int* requiredHelpers
+        ) {
+
+    for (auto signature_to_modules_it = signatures_to_modules.begin();
+            signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
+        // Skip if this signature is not needed for the module.
+        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
+            continue;
+        }
+
+        // Print method signature.
+        vector<java_type_t> signature = signature_to_modules_it->first;
+        fprintf(out, "    public static void write(int code");
+        int argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature.begin();
+                arg != signature.end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, ", %s[] %s",
+                        java_type_name(chainField.javaType), chainField.name.c_str());
+                }
+            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+                // Module logging does not yet support key value pair.
+                return 1;
+            } else {
+                fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+            }
+            argIndex++;
+        }
+        fprintf(out, ") {\n");
+
+        // Calculate the size of the buffer.
+        fprintf(out, "        // Initial overhead of the list, timestamp, and atom tag.\n");
+        fprintf(out, "        int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + INT_TYPE_SIZE;\n");
+        argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature.begin();
+                arg != signature.end(); arg++) {
+            switch (*arg) {
+            case JAVA_TYPE_BOOLEAN:
+            case JAVA_TYPE_INT:
+            case JAVA_TYPE_FLOAT:
+            case JAVA_TYPE_ENUM:
+                fprintf(out, "        needed += INT_TYPE_SIZE;\n");
+                break;
+            case JAVA_TYPE_LONG:
+                // Longs take 9 bytes, 1 for the type and 8 for the value.
+                fprintf(out, "        needed += LONG_TYPE_SIZE;\n");
+                break;
+            case JAVA_TYPE_STRING:
+                // Strings take 5 metadata bytes + length of byte encoded string.
+                fprintf(out, "        if (arg%d == null) {\n", argIndex);
+                fprintf(out, "            arg%d = \"\";\n", argIndex);
+                fprintf(out, "        }\n");
+                fprintf(out, "        byte[] arg%dBytes= arg%d.getBytes(UTF_8);\n",
+                        argIndex, argIndex);
+                fprintf(out, "        needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
+                        argIndex);
+                break;
+            case JAVA_TYPE_BYTE_ARRAY:
+                // Byte arrays take 5 metadata bytes + length of byte array.
+                fprintf(out, "        if (arg%d == null) {\n", argIndex);
+                fprintf(out, "            arg%d = new byte[0];\n", argIndex);
+                fprintf(out, "        }\n");
+                fprintf(out, "        needed += STRING_TYPE_OVERHEAD + arg%d.length;\n", argIndex);
+                break;
+            case JAVA_TYPE_ATTRIBUTION_CHAIN:
+            {
+                const char* uidName = attributionDecl.fields.front().name.c_str();
+                const char* tagName = attributionDecl.fields.back().name.c_str();
+                // Null checks on the params.
+                fprintf(out, "        if (%s == null) {\n", uidName);
+                fprintf(out, "            %s = new %s[0];\n", uidName,
+                        java_type_name(attributionDecl.fields.front().javaType));
+                fprintf(out, "        }\n");
+                fprintf(out, "        if (%s == null) {\n", tagName);
+                fprintf(out, "            %s = new %s[0];\n", tagName,
+                        java_type_name(attributionDecl.fields.back().javaType));
+                fprintf(out, "        }\n");
+
+                // First check that the lengths of the uid and tag arrays are the same.
+                fprintf(out, "        if (%s.length != %s.length) {\n", uidName, tagName);
+                fprintf(out, "            return;\n");
+                fprintf(out, "        }\n");
+                fprintf(out, "        int attrSize = LIST_TYPE_OVERHEAD;\n");
+                fprintf(out, "        for (int i = 0; i < %s.length; i++) {\n", tagName);
+                fprintf(out, "            String str%d = (%s[i] == null) ? \"\" : %s[i];\n",
+                        argIndex, tagName, tagName);
+                fprintf(out, "            int str%dlen = str%d.getBytes(UTF_8).length;\n",
+                        argIndex, argIndex);
+                fprintf(out,
+                        "            attrSize += "
+                        "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + str%dlen;\n",
+                        argIndex);
+                fprintf(out, "        }\n");
+                fprintf(out, "        needed += attrSize;\n");
+                break;
+            }
+            default:
+                // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIR.
+                return 1;
+            }
+            argIndex++;
+        }
+
+        // Now we have the size that is needed. Check for overflow and return if needed.
+        fprintf(out, "        if (needed > MAX_EVENT_PAYLOAD) {\n");
+        fprintf(out, "            return;\n");
+        fprintf(out, "        }\n");
+
+        // Create new buffer, and associated data types.
+        fprintf(out, "        byte[] buff = new byte[needed];\n");
+        fprintf(out, "        int pos = 0;\n");
+
+        // Initialize the buffer with list data type.
+        fprintf(out, "        buff[pos] = LIST_TYPE;\n");
+        fprintf(out, "        buff[pos + 1] = %lu;\n", signature.size() + 2);
+        fprintf(out, "        pos += LIST_TYPE_OVERHEAD;\n");
+
+        // Write timestamp.
+        fprintf(out, "        long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n");
+        fprintf(out, "        buff[pos] = LONG_TYPE;\n");
+        fprintf(out, "        copyLong(buff, pos + 1, elapsedRealtime);\n");
+        fprintf(out, "        pos += LONG_TYPE_SIZE;\n");
+
+        // Write atom code.
+        fprintf(out, "        buff[pos] = INT_TYPE;\n");
+        fprintf(out, "        copyInt(buff, pos + 1, code);\n");
+        fprintf(out, "        pos += INT_TYPE_SIZE;\n");
+
+        // Write the args.
+        argIndex = 1;
+        for (vector<java_type_t>::const_iterator arg = signature.begin();
+                arg != signature.end(); arg++) {
+            switch (*arg) {
+            case JAVA_TYPE_BOOLEAN:
+                fprintf(out, "        buff[pos] = INT_TYPE;\n");
+                fprintf(out, "        copyInt(buff, pos + 1, arg%d? 1 : 0);\n", argIndex);
+                fprintf(out, "        pos += INT_TYPE_SIZE;\n");
+                break;
+            case JAVA_TYPE_INT:
+            case JAVA_TYPE_ENUM:
+                fprintf(out, "        buff[pos] = INT_TYPE;\n");
+                fprintf(out, "        copyInt(buff, pos + 1, arg%d);\n", argIndex);
+                fprintf(out, "        pos += INT_TYPE_SIZE;\n");
+                break;
+            case JAVA_TYPE_FLOAT:
+                *requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
+                fprintf(out, "        buff[pos] = FLOAT_TYPE;\n");
+                fprintf(out, "        copyFloat(buff, pos + 1, arg%d);\n", argIndex);
+                fprintf(out, "        pos += FLOAT_TYPE_SIZE;\n");
+                break;
+            case JAVA_TYPE_LONG:
+                fprintf(out, "        buff[pos] = LONG_TYPE;\n");
+                fprintf(out, "        copyLong(buff, pos + 1, arg%d);\n", argIndex);
+                fprintf(out, "        pos += LONG_TYPE_SIZE;\n");
+                break;
+            case JAVA_TYPE_STRING:
+                fprintf(out, "        buff[pos] = STRING_TYPE;\n");
+                fprintf(out, "        copyInt(buff, pos + 1, arg%dBytes.length);\n", argIndex);
+                fprintf(out, "        System.arraycopy("
+                        "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%dBytes.length);\n",
+                        argIndex, argIndex);
+                fprintf(out, "        pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
+                        argIndex);
+                break;
+            case JAVA_TYPE_BYTE_ARRAY:
+                fprintf(out, "        buff[pos] = STRING_TYPE;\n");
+                fprintf(out, "        copyInt(buff, pos + 1, arg%d.length);\n", argIndex);
+                fprintf(out, "        System.arraycopy("
+                        "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n",
+                        argIndex, argIndex);
+                fprintf(out, "        pos += STRING_TYPE_OVERHEAD + arg%d.length;\n", argIndex);
+                break;
+            case JAVA_TYPE_ATTRIBUTION_CHAIN:
+            {
+                *requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION;
+                const char* uidName = attributionDecl.fields.front().name.c_str();
+                const char* tagName = attributionDecl.fields.back().name.c_str();
+
+                fprintf(out, "        writeAttributionChain(buff, pos, %s, %s);\n",
+                        uidName, tagName);
+                fprintf(out, "        pos += attrSize;\n");
+                break;
+            }
+            default:
+                // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIR.
+                return 1;
+            }
+            argIndex++;
+        }
+
+        fprintf(out, "        StatsLog.writeRaw(buff, pos);\n");
+        fprintf(out, "    }\n");
+        fprintf(out, "\n");
+    }
+    return 0;
+}
+
 static void write_java_work_source_method(FILE* out,
-        const map<vector<java_type_t>, set<string>>& signatures_to_modules) {
+        const map<vector<java_type_t>, set<string>>& signatures_to_modules,
+        const string& moduleName) {
     fprintf(out, "\n    // WorkSource methods.\n");
     for (auto signature_to_modules_it = signatures_to_modules.begin();
             signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
+        // Skip if this signature is not needed for the module.
+        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
+            continue;
+        }
         vector<java_type_t> signature = signature_to_modules_it->first;
         // Determine if there is Attribution in this signature.
         int attributionArg = -1;
@@ -834,7 +1178,9 @@
         }
 
         // Method header (signature)
-        fprintf(out, "    /** @hide */\n");
+        if (moduleName == DEFAULT_MODULE_NAME) {
+            fprintf(out, "    /** @hide */\n");
+        }
         fprintf(out, "    public static void write(int code");
         int argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature.begin();
@@ -859,7 +1205,7 @@
             }
         }
         fprintf(out, ");\n");
-        fprintf(out, "        }\n"); // close flor-loop
+        fprintf(out, "        }\n"); // close for-loop
 
         // write() component.
         fprintf(out, "        ArrayList<WorkSource.WorkChain> workChains = ws.getWorkChains();\n");
@@ -880,23 +1226,7 @@
     }
 }
 
-static int
-write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
-{
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-    fprintf(out, "package android.util;\n");
-    fprintf(out, "\n");
-    fprintf(out, "import android.os.WorkSource;\n");
-    fprintf(out, "import java.util.ArrayList;\n");
-    fprintf(out, "\n");
-    fprintf(out, "\n");
-    fprintf(out, "/**\n");
-    fprintf(out, " * API For logging statistics events.\n");
-    fprintf(out, " * @hide\n");
-    fprintf(out, " */\n");
-    fprintf(out, "public class StatsLogInternal {\n");
+static void write_java_atom_codes(FILE* out, const Atoms& atoms, const string& moduleName) {
     fprintf(out, "    // Constants for atom codes.\n");
 
     std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
@@ -905,6 +1235,10 @@
     // Print constants for the atom codes.
     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
             atom != atoms.decls.end(); atom++) {
+        // Skip if the atom is not needed for the module.
+        if (!atom_needed_for_module(*atom, moduleName)) {
+            continue;
+        }
         string constant = make_constant_name(atom->name);
         fprintf(out, "\n");
         fprintf(out, "    /**\n");
@@ -914,16 +1248,23 @@
         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
             write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second);
         }
-        fprintf(out, "     * @hide\n");
+        if (moduleName == DEFAULT_MODULE_NAME) {
+            fprintf(out, "     * @hide\n");
+        }
         fprintf(out, "     */\n");
         fprintf(out, "    public static final int %s = %d;\n", constant.c_str(), atom->code);
     }
     fprintf(out, "\n");
+}
 
-    // Print constants for the enum values.
+static void write_java_enum_values(FILE* out, const Atoms& atoms, const string& moduleName) {
     fprintf(out, "    // Constants for enum values.\n\n");
     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
         atom != atoms.decls.end(); atom++) {
+        // Skip if the atom is not needed for the module.
+        if (!atom_needed_for_module(*atom, moduleName)) {
+            continue;
+        }
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
             field != atom->fields.end(); field++) {
             if (field->javaType == JAVA_TYPE_ENUM) {
@@ -931,7 +1272,9 @@
                     field->name.c_str());
                 for (map<int, string>::const_iterator value = field->enumValues.begin();
                     value != field->enumValues.end(); value++) {
-                    fprintf(out, "    /** @hide */\n");
+                    if (moduleName == DEFAULT_MODULE_NAME) {
+                        fprintf(out, "    /** @hide */\n");
+                    }
                     fprintf(out, "    public static final int %s__%s__%s = %d;\n",
                         make_constant_name(atom->message).c_str(),
                         make_constant_name(field->name).c_str(),
@@ -942,19 +1285,107 @@
             }
         }
     }
+}
+
+static int
+write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
+{
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+    fprintf(out, "package android.util;\n");
+    fprintf(out, "\n");
+    fprintf(out, "import android.os.WorkSource;\n");
+    fprintf(out, "import android.util.SparseArray;\n");
+    fprintf(out, "import java.util.ArrayList;\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "/**\n");
+    fprintf(out, " * API For logging statistics events.\n");
+    fprintf(out, " * @hide\n");
+    fprintf(out, " */\n");
+    fprintf(out, "public class StatsLogInternal {\n");
+    write_java_atom_codes(out, atoms, DEFAULT_MODULE_NAME);
+
+    write_java_enum_values(out, atoms, DEFAULT_MODULE_NAME);
 
     // Print write methods
     fprintf(out, "    // Write methods\n");
     write_java_method(out, "write", atoms.signatures_to_modules, attributionDecl);
     write_java_method(out, "write_non_chained", atoms.non_chained_signatures_to_modules,
             attributionDecl);
-    write_java_work_source_method(out, atoms.signatures_to_modules);
+    write_java_work_source_method(out, atoms.signatures_to_modules, DEFAULT_MODULE_NAME);
 
     fprintf(out, "}\n");
 
     return 0;
 }
 
+// TODO: Merge this with write_stats_log_java so that we can get rid of StatsLogInternal JNI.
+static int
+write_stats_log_java_for_module(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
+                     const string& moduleName, const string& javaClass, const string& javaPackage)
+{
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+    fprintf(out, "package %s;\n", javaPackage.c_str());
+    fprintf(out, "\n");
+    fprintf(out, "import static java.nio.charset.StandardCharsets.UTF_8;\n");
+    fprintf(out, "\n");
+    fprintf(out, "import android.util.StatsLog;\n");
+    fprintf(out, "import android.os.SystemClock;\n");
+    fprintf(out, "\n");
+    fprintf(out, "import java.util.ArrayList;\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "/**\n");
+    fprintf(out, " * Utility class for logging statistics events.\n");
+    fprintf(out, " */\n");
+    fprintf(out, "public class %s {\n", javaClass.c_str());
+
+    // TODO: ideally these match with the native values (and automatically change if they change).
+    fprintf(out, "    private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;\n");
+    fprintf(out,
+            "    private static final int MAX_EVENT_PAYLOAD = LOGGER_ENTRY_MAX_PAYLOAD - 4;\n");
+    // Value types. Must match with EventLog.java and log.h.
+    fprintf(out, "    private static final byte INT_TYPE = 0;\n");
+    fprintf(out, "    private static final byte LONG_TYPE = 1;\n");
+    fprintf(out, "    private static final byte STRING_TYPE = 2;\n");
+    fprintf(out, "    private static final byte LIST_TYPE = 3;\n");
+    fprintf(out, "    private static final byte FLOAT_TYPE = 4;\n");
+
+    // Size of each value type.
+    // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for the value.
+    fprintf(out, "    private static final int INT_TYPE_SIZE = 5;\n");
+    fprintf(out, "    private static final int FLOAT_TYPE_SIZE = 5;\n");
+    // Longs take 9 bytes, 1 for the type and 8 for the value.
+    fprintf(out, "    private static final int LONG_TYPE_SIZE = 9;\n");
+    // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the length.
+    fprintf(out, "    private static final int STRING_TYPE_OVERHEAD = 5;\n");
+    fprintf(out, "    private static final int LIST_TYPE_OVERHEAD = 2;\n");
+
+    write_java_atom_codes(out, atoms, moduleName);
+
+    write_java_enum_values(out, atoms, moduleName);
+
+    int errors = 0;
+    int requiredHelpers = 0;
+    // Print write methods
+    fprintf(out, "    // Write methods\n");
+    errors += write_java_method_for_module(out, atoms.signatures_to_modules, attributionDecl,
+            moduleName, &requiredHelpers);
+    errors += write_java_non_chained_method_for_module(out, atoms.non_chained_signatures_to_modules,
+            moduleName);
+
+    fprintf(out, "    // Helper methods for copying primitives\n");
+    write_java_helpers_for_module(out, attributionDecl, requiredHelpers);
+
+    fprintf(out, "}\n");
+
+    return errors;
+}
+
 static const char*
 jni_type_name(java_type_t type)
 {
@@ -1346,7 +1777,11 @@
     fprintf(stderr, "  --namespace COMMA,SEP,NAMESPACE   required for cpp/header with module\n");
     fprintf(stderr, "                                    comma separated namespace of the files\n");
     fprintf(stderr, "  --importHeader NAME  required for cpp/jni to say which header to import\n");
-}
+    fprintf(stderr, "  --javaPackage PACKAGE             the package for the java file.\n");
+    fprintf(stderr, "                                    required for java with module\n");
+    fprintf(stderr, "  --javaClass CLASS    the class name of the java class.\n");
+    fprintf(stderr, "                       Optional for Java with module.\n");
+    fprintf(stderr, "                       Default is \"StatsLogInternal\"\n");}
 
 /**
  * Do the argument parsing and execute the tasks.
@@ -1362,6 +1797,8 @@
     string moduleName = DEFAULT_MODULE_NAME;
     string cppNamespace = DEFAULT_CPP_NAMESPACE;
     string cppHeaderImport = DEFAULT_CPP_HEADER_IMPORT;
+    string javaPackage = DEFAULT_JAVA_PACKAGE;
+    string javaClass = DEFAULT_JAVA_CLASS;
 
     int index = 1;
     while (index < argc) {
@@ -1417,6 +1854,20 @@
                 return 1;
             }
             cppHeaderImport = argv[index];
+        } else if (0 == strcmp("--javaPackage", argv[index])) {
+            index++;
+            if (index >= argc) {
+                print_usage();
+                return 1;
+            }
+            javaPackage = argv[index];
+        } else if (0 == strcmp("--javaClass", argv[index])) {
+            index++;
+            if (index >= argc) {
+                print_usage();
+                return 1;
+            }
+            javaClass = argv[index];
         }
         index++;
     }
@@ -1486,8 +1937,18 @@
             fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
             return 1;
         }
-        errorCount = android::stats_log_api_gen::write_stats_log_java(
-            out, atoms, attributionDecl);
+        // If this is for a specific module, the java package must also be provided.
+        if (moduleName != DEFAULT_MODULE_NAME && javaPackage== DEFAULT_JAVA_PACKAGE) {
+            fprintf(stderr, "Must supply --javaPackage if supplying a specific module\n");
+            return 1;
+        }
+        if (moduleName == DEFAULT_MODULE_NAME) {
+            errorCount = android::stats_log_api_gen::write_stats_log_java(
+                    out, atoms, attributionDecl);
+        } else {
+            errorCount = android::stats_log_api_gen::write_stats_log_java_for_module(
+                    out, atoms, attributionDecl, moduleName, javaClass, javaPackage);
+        }
         fclose(out);
     }
 
@@ -1503,7 +1964,7 @@
         fclose(out);
     }
 
-    return 0;
+    return errorCount;
 }
 
 }