Merge "Extend support for requestRouteToHostAddress for backward compatibility"
diff --git a/api/system-current.txt b/api/system-current.txt
index 82bf26c..cf55fbe 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7706,7 +7706,7 @@
 
 package android.telephony.mbms.vendor {
 
-  public class MbmsDownloadServiceBase extends android.os.Binder {
+  public class MbmsDownloadServiceBase extends android.os.Binder implements android.os.IInterface {
     ctor public MbmsDownloadServiceBase();
     method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
     method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
@@ -7739,7 +7739,7 @@
     method public void updateGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>);
   }
 
-  public class MbmsStreamingServiceBase extends android.os.Binder {
+  public class MbmsStreamingServiceBase extends android.os.Binder implements android.os.IInterface {
     ctor public MbmsStreamingServiceBase();
     method public android.os.IBinder asBinder();
     method public void dispose(int) throws android.os.RemoteException;
diff --git a/api/test-current.txt b/api/test-current.txt
index e9dae4e..e813523 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1589,7 +1589,7 @@
 
 package android.telephony.mbms.vendor {
 
-  public class MbmsDownloadServiceBase extends android.os.Binder {
+  public class MbmsDownloadServiceBase extends android.os.Binder implements android.os.IInterface {
     ctor public MbmsDownloadServiceBase();
     method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
     method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
@@ -1622,7 +1622,7 @@
     method public void updateGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>);
   }
 
-  public class MbmsStreamingServiceBase extends android.os.Binder {
+  public class MbmsStreamingServiceBase extends android.os.Binder implements android.os.IInterface {
     ctor public MbmsStreamingServiceBase();
     method public android.os.IBinder asBinder();
     method public void dispose(int) throws android.os.RemoteException;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 946d386..8c4d9be 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7319,15 +7319,6 @@
                 "call_screening_default_component";
 
         /**
-         * Specifies the component name currently configured to be the default application to
-         * perform the user-defined call redirection service with Telecom.
-         * @hide
-         */
-        @UnsupportedAppUsage
-        public static final String CALL_REDIRECTION_DEFAULT_APPLICATION =
-                "call_redirection_default_application";
-
-        /**
          * Specifies the package name currently configured to be the emergency assistance application
          *
          * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 6024f68..e508b02a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -196,7 +196,6 @@
         "android_content_res_Configuration.cpp",
         "android_animation_PropertyValuesHolder.cpp",
         "android_security_Scrypt.cpp",
-        "com_android_internal_net_NetworkStatsFactory.cpp",
         "com_android_internal_os_ClassLoaderFactory.cpp",
         "com_android_internal_os_FuseAppLoop.cpp",
         "com_android_internal_os_Zygote.cpp",
@@ -288,6 +287,7 @@
         "libnativewindow",
         "libhwui",
         "libdl",
+        "libdl_android",
         "libstatslog",
         "server_configurable_flags",
     ],
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 79850d0..d04295f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -213,7 +213,6 @@
 extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
 extern int register_android_security_Scrypt(JNIEnv *env);
 extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
-extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
@@ -1538,7 +1537,6 @@
     REG_JNI(register_android_animation_PropertyValuesHolder),
     REG_JNI(register_android_security_Scrypt),
     REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
-    REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
     REG_JNI(register_com_android_internal_os_FuseAppLoop),
 };
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index af03aac..64dc178 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -537,7 +537,6 @@
                  Settings.Secure.BACKUP_ENABLED,
                  Settings.Secure.BACKUP_PROVISIONED,
                  Settings.Secure.BACKUP_TRANSPORT,
-                 Settings.Secure.CALL_REDIRECTION_DEFAULT_APPLICATION,
                  Settings.Secure.CALL_SCREENING_DEFAULT_COMPONENT,
                  Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, // Candidate for backup?
                  Settings.Secure.CARRIER_APPS_HANDLED,
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index 3838a999..b5c4cca 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -24,6 +24,7 @@
 import android.os.StrictMode;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -37,7 +38,6 @@
 import java.net.UnknownServiceException;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
 
 /** @hide */
 public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@@ -47,23 +47,42 @@
     // connection timeout - 30 sec
     private static final int CONNECT_TIMEOUT_MS = 30 * 1000;
 
+    @GuardedBy("this")
+    @UnsupportedAppUsage
+    private long mCurrentOffset = -1;
+
+    @GuardedBy("this")
+    @UnsupportedAppUsage
+    private URL mURL = null;
+
+    @GuardedBy("this")
+    @UnsupportedAppUsage
+    private Map<String, String> mHeaders = null;
+
+    // volatile so that disconnect() can be called without acquiring a lock.
+    // All other access is @GuardedBy("this").
+    @UnsupportedAppUsage
+    private volatile HttpURLConnection mConnection = null;
+
+    @GuardedBy("this")
+    @UnsupportedAppUsage
+    private long mTotalSize = -1;
+
+    @GuardedBy("this")
+    private InputStream mInputStream = null;
+
+    @GuardedBy("this")
+    @UnsupportedAppUsage
+    private boolean mAllowCrossDomainRedirect = true;
+
+    @GuardedBy("this")
+    @UnsupportedAppUsage
+    private boolean mAllowCrossProtocolRedirect = true;
+
     // from com.squareup.okhttp.internal.http
     private final static int HTTP_TEMP_REDIRECT = 307;
     private final static int MAX_REDIRECTS = 20;
 
-    class ConnectionState {
-        public HttpURLConnection mConnection = null;
-        public InputStream mInputStream = null;
-        public long mCurrentOffset = -1;
-        public Map<String, String> mHeaders = null;
-        public URL mURL = null;
-        public long mTotalSize = -1;
-        public boolean mAllowCrossDomainRedirect = true;
-        public boolean mAllowCrossProtocolRedirect = true;
-    }
-    private final AtomicReference<ConnectionState> mConnectionStateHolder =
-            new AtomicReference<ConnectionState>();
-
     @UnsupportedAppUsage
     public MediaHTTPConnection() {
         CookieHandler cookieHandler = CookieHandler.getDefault();
@@ -76,34 +95,24 @@
 
     @Override
     @UnsupportedAppUsage
-    public IBinder connect(String uri, String headers) {
+    public synchronized IBinder connect(String uri, String headers) {
         if (VERBOSE) {
             Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers);
         }
 
-        ConnectionState connectionState = mConnectionStateHolder.get();
-        synchronized (this) {
-            if (connectionState == null) {
-                connectionState = new ConnectionState();
-                mConnectionStateHolder.set(connectionState);
-            }
-        }
-
         try {
             disconnect();
-            connectionState.mAllowCrossDomainRedirect = true;
-            connectionState.mURL = new URL(uri);
-            connectionState.mHeaders = convertHeaderStringToMap(headers, connectionState);
+            mAllowCrossDomainRedirect = true;
+            mURL = new URL(uri);
+            mHeaders = convertHeaderStringToMap(headers);
         } catch (MalformedURLException e) {
             return null;
-        } finally {
-            mConnectionStateHolder.set(connectionState);
         }
 
         return native_getIMemory();
     }
 
-    private boolean parseBoolean(String val) {
+    private static boolean parseBoolean(String val) {
         try {
             return Long.parseLong(val) != 0;
         } catch (NumberFormatException e) {
@@ -113,21 +122,18 @@
     }
 
     /* returns true iff header is internal */
-    private boolean filterOutInternalHeaders(
-            String key, String val, ConnectionState connectionState) {
+    private synchronized boolean filterOutInternalHeaders(String key, String val) {
         if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) {
-            connectionState.mAllowCrossDomainRedirect = parseBoolean(val);
+            mAllowCrossDomainRedirect = parseBoolean(val);
             // cross-protocol redirects are also controlled by this flag
-            connectionState.mAllowCrossProtocolRedirect =
-                    connectionState.mAllowCrossDomainRedirect;
+            mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect;
         } else {
             return false;
         }
         return true;
     }
 
-    private Map<String, String> convertHeaderStringToMap(String headers,
-            ConnectionState connectionState) {
+    private synchronized Map<String, String> convertHeaderStringToMap(String headers) {
         HashMap<String, String> map = new HashMap<String, String>();
 
         String[] pairs = headers.split("\r\n");
@@ -137,7 +143,7 @@
                 String key = pair.substring(0, colonPos);
                 String val = pair.substring(colonPos + 1);
 
-                if (!filterOutInternalHeaders(key, val, connectionState)) {
+                if (!filterOutInternalHeaders(key, val)) {
                     map.put(key, val);
                 }
             }
@@ -149,28 +155,36 @@
     @Override
     @UnsupportedAppUsage
     public void disconnect() {
-        ConnectionState connectionState = mConnectionStateHolder.getAndSet(null);
-        if (connectionState != null) {
-            teardownConnection(connectionState);
-            connectionState.mHeaders = null;
-            connectionState.mURL = null;
+        HttpURLConnection connectionToDisconnect = mConnection;
+        // Call disconnect() before blocking for the lock in order to ensure that any
+        // other thread that is blocked in readAt() will return quickly.
+        if (connectionToDisconnect != null) {
+            connectionToDisconnect.disconnect();
+        }
+        synchronized (this) {
+            // It's unlikely but possible that while we were waiting to acquire the lock, another
+            // thread concurrently started a new connection; if so, we're disconnecting that one
+            // here, too.
+            teardownConnection();
+            mHeaders = null;
+            mURL = null;
         }
     }
 
-    private void teardownConnection(ConnectionState connectionState) {
-        if (connectionState.mConnection != null) {
-            if (connectionState.mInputStream != null) {
+    private synchronized void teardownConnection() {
+        if (mConnection != null) {
+            if (mInputStream != null) {
                 try {
-                    connectionState.mInputStream.close();
+                    mInputStream.close();
                 } catch (IOException e) {
                 }
-                connectionState.mInputStream = null;
+                mInputStream = null;
             }
 
-            connectionState.mConnection.disconnect();
-            connectionState.mConnection = null;
+            mConnection.disconnect();
+            mConnection = null;
 
-            connectionState.mCurrentOffset = -1;
+            mCurrentOffset = -1;
         }
     }
 
@@ -197,44 +211,42 @@
         return false;
     }
 
-    private void seekTo(long offset, ConnectionState connectionState) throws IOException {
-        teardownConnection(connectionState);
+    private synchronized void seekTo(long offset) throws IOException {
+        teardownConnection();
 
         try {
             int response;
             int redirectCount = 0;
 
-            URL url = connectionState.mURL;
+            URL url = mURL;
 
             // do not use any proxy for localhost (127.0.0.1)
             boolean noProxy = isLocalHost(url);
 
             while (true) {
                 if (noProxy) {
-                    connectionState.mConnection =
-                            (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
+                    mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
                 } else {
-                    connectionState.mConnection = (HttpURLConnection) url.openConnection();
+                    mConnection = (HttpURLConnection)url.openConnection();
                 }
-                connectionState.mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
+                mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
 
                 // handle redirects ourselves if we do not allow cross-domain redirect
-                connectionState.mConnection.setInstanceFollowRedirects(
-                        connectionState.mAllowCrossDomainRedirect);
+                mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect);
 
-                if (connectionState.mHeaders != null) {
-                    for (Map.Entry<String, String> entry : connectionState.mHeaders.entrySet()) {
-                        connectionState.mConnection.setRequestProperty(
+                if (mHeaders != null) {
+                    for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
+                        mConnection.setRequestProperty(
                                 entry.getKey(), entry.getValue());
                     }
                 }
 
                 if (offset > 0) {
-                    connectionState.mConnection.setRequestProperty(
+                    mConnection.setRequestProperty(
                             "Range", "bytes=" + offset + "-");
                 }
 
-                response = connectionState.mConnection.getResponseCode();
+                response = mConnection.getResponseCode();
                 if (response != HttpURLConnection.HTTP_MULT_CHOICE &&
                         response != HttpURLConnection.HTTP_MOVED_PERM &&
                         response != HttpURLConnection.HTTP_MOVED_TEMP &&
@@ -248,7 +260,7 @@
                     throw new NoRouteToHostException("Too many redirects: " + redirectCount);
                 }
 
-                String method = connectionState.mConnection.getRequestMethod();
+                String method = mConnection.getRequestMethod();
                 if (response == HTTP_TEMP_REDIRECT &&
                         !method.equals("GET") && !method.equals("HEAD")) {
                     // "If the 307 status code is received in response to a
@@ -256,35 +268,34 @@
                     // automatically redirect the request"
                     throw new NoRouteToHostException("Invalid redirect");
                 }
-                String location = connectionState.mConnection.getHeaderField("Location");
+                String location = mConnection.getHeaderField("Location");
                 if (location == null) {
                     throw new NoRouteToHostException("Invalid redirect");
                 }
-                url = new URL(connectionState.mURL /* TRICKY: don't use url! */, location);
+                url = new URL(mURL /* TRICKY: don't use url! */, location);
                 if (!url.getProtocol().equals("https") &&
                         !url.getProtocol().equals("http")) {
                     throw new NoRouteToHostException("Unsupported protocol redirect");
                 }
-                boolean sameProtocol =
-                        connectionState.mURL.getProtocol().equals(url.getProtocol());
-                if (!connectionState.mAllowCrossProtocolRedirect && !sameProtocol) {
+                boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol());
+                if (!mAllowCrossProtocolRedirect && !sameProtocol) {
                     throw new NoRouteToHostException("Cross-protocol redirects are disallowed");
                 }
-                boolean sameHost = connectionState.mURL.getHost().equals(url.getHost());
-                if (!connectionState.mAllowCrossDomainRedirect && !sameHost) {
+                boolean sameHost = mURL.getHost().equals(url.getHost());
+                if (!mAllowCrossDomainRedirect && !sameHost) {
                     throw new NoRouteToHostException("Cross-domain redirects are disallowed");
                 }
 
                 if (response != HTTP_TEMP_REDIRECT) {
                     // update effective URL, unless it is a Temporary Redirect
-                    connectionState.mURL = url;
+                    mURL = url;
                 }
             }
 
-            if (connectionState.mAllowCrossDomainRedirect) {
+            if (mAllowCrossDomainRedirect) {
                 // remember the current, potentially redirected URL if redirects
                 // were handled by HttpURLConnection
-                connectionState.mURL = connectionState.mConnection.getURL();
+                mURL = mConnection.getURL();
             }
 
             if (response == HttpURLConnection.HTTP_PARTIAL) {
@@ -292,9 +303,10 @@
                 // because what we want is not just the length of the range
                 // returned but the size of the full content if available.
 
-                String contentRange = connectionState.mConnection.getHeaderField("Content-Range");
+                String contentRange =
+                    mConnection.getHeaderField("Content-Range");
 
-                connectionState.mTotalSize = -1;
+                mTotalSize = -1;
                 if (contentRange != null) {
                     // format is "bytes xxx-yyy/zzz
                     // where "zzz" is the total number of bytes of the
@@ -306,7 +318,7 @@
                             contentRange.substring(lastSlashPos + 1);
 
                         try {
-                            connectionState.mTotalSize = Long.parseLong(total);
+                            mTotalSize = Long.parseLong(total);
                         } catch (NumberFormatException e) {
                         }
                     }
@@ -314,7 +326,7 @@
             } else if (response != HttpURLConnection.HTTP_OK) {
                 throw new IOException();
             } else {
-                connectionState.mTotalSize = connectionState.mConnection.getContentLength();
+                mTotalSize = mConnection.getContentLength();
             }
 
             if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) {
@@ -323,14 +335,14 @@
                 throw new ProtocolException();
             }
 
-            connectionState.mInputStream =
-                new BufferedInputStream(connectionState.mConnection.getInputStream());
+            mInputStream =
+                new BufferedInputStream(mConnection.getInputStream());
 
-            connectionState.mCurrentOffset = offset;
+            mCurrentOffset = offset;
         } catch (IOException e) {
-            connectionState.mTotalSize = -1;
-            teardownConnection(connectionState);
-            connectionState.mCurrentOffset = -1;
+            mTotalSize = -1;
+            teardownConnection();
+            mCurrentOffset = -1;
 
             throw e;
         }
@@ -338,28 +350,22 @@
 
     @Override
     @UnsupportedAppUsage
-    public int readAt(long offset, int size) {
-        ConnectionState connectionState = mConnectionStateHolder.get();
-        if (connectionState != null) {
-            return native_readAt(offset, size, connectionState);
-        }
-        return -1;
+    public synchronized int readAt(long offset, int size) {
+        return native_readAt(offset, size);
     }
 
-    private int readAt(long offset, byte[] data, int size, ConnectionState connectionState) {
+    private synchronized int readAt(long offset, byte[] data, int size) {
         StrictMode.ThreadPolicy policy =
             new StrictMode.ThreadPolicy.Builder().permitAll().build();
 
         StrictMode.setThreadPolicy(policy);
 
         try {
-            synchronized(this) {
-                if (offset != connectionState.mCurrentOffset) {
-                    seekTo(offset, connectionState);
-                }
+            if (offset != mCurrentOffset) {
+                seekTo(offset);
             }
 
-            int n = connectionState.mInputStream.read(data, 0, size);
+            int n = mInputStream.read(data, 0, size);
 
             if (n == -1) {
                 // InputStream signals EOS using a -1 result, our semantics
@@ -367,7 +373,7 @@
                 n = 0;
             }
 
-            connectionState.mCurrentOffset += n;
+            mCurrentOffset += n;
 
             if (VERBOSE) {
                 Log.d(TAG, "readAt " + offset + " / " + size + " => " + n);
@@ -399,47 +405,35 @@
 
     @Override
     public synchronized long getSize() {
-        ConnectionState connectionState = mConnectionStateHolder.get();
-        if (connectionState != null) {
-            if (connectionState.mConnection == null) {
-                try {
-                    seekTo(0, connectionState);
-                } catch (IOException e) {
-                    return -1;
-                }
+        if (mConnection == null) {
+            try {
+                seekTo(0);
+            } catch (IOException e) {
+                return -1;
             }
-            return connectionState.mTotalSize;
         }
 
-        return -1;
+        return mTotalSize;
     }
 
     @Override
     @UnsupportedAppUsage
     public synchronized String getMIMEType() {
-        ConnectionState connectionState = mConnectionStateHolder.get();
-        if (connectionState != null) {
-            if (connectionState.mConnection == null) {
-                try {
-                    seekTo(0, connectionState);
-                } catch (IOException e) {
-                    return "application/octet-stream";
-                }
+        if (mConnection == null) {
+            try {
+                seekTo(0);
+            } catch (IOException e) {
+                return "application/octet-stream";
             }
-            return connectionState.mConnection.getContentType();
         }
 
-        return null;
+        return mConnection.getContentType();
     }
 
     @Override
     @UnsupportedAppUsage
-    public String getUri() {
-        ConnectionState connectionState = mConnectionStateHolder.get();
-        if (connectionState != null) {
-            return connectionState.mURL.toString();
-        }
-        return null;
+    public synchronized String getUri() {
+        return mURL.toString();
     }
 
     @Override
@@ -452,7 +446,7 @@
     private native final void native_finalize();
 
     private native final IBinder native_getIMemory();
-    private native int native_readAt(long offset, int size, ConnectionState connectionState);
+    private native final int native_readAt(long offset, int size);
 
     static {
         System.loadLibrary("media_jni");
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index d28c15c..365e045 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -109,8 +109,7 @@
     gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
     CHECK(gFields.context != NULL);
 
-    gFields.readAtMethodID = env->GetMethodID(
-            clazz.get(), "readAt", "(J[BILandroid/media/MediaHTTPConnection$ConnectionState;)I");
+    gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I");
 }
 
 static void android_media_MediaHTTPConnection_native_setup(
@@ -133,7 +132,7 @@
 }
 
 static jint android_media_MediaHTTPConnection_native_readAt(
-        JNIEnv *env, jobject thiz, jlong offset, jint size, jobject connectionState) {
+        JNIEnv *env, jobject thiz, jlong offset, jint size) {
     sp<JMediaHTTPConnection> conn = getObject(env, thiz);
     if (size > JMediaHTTPConnection::kBufferSize) {
         size = JMediaHTTPConnection::kBufferSize;
@@ -142,7 +141,7 @@
     jbyteArray byteArrayObj = conn->getByteArrayObj();
 
     jint n = env->CallIntMethod(
-            thiz, gFields.readAtMethodID, offset, byteArrayObj, size, connectionState);
+            thiz, gFields.readAtMethodID, offset, byteArrayObj, size);
 
     if (n > 0) {
         env->GetByteArrayRegion(
@@ -159,7 +158,7 @@
     { "native_getIMemory", "()Landroid/os/IBinder;",
       (void *)android_media_MediaHTTPConnection_native_getIMemory },
 
-    { "native_readAt", "(JILandroid/media/MediaHTTPConnection$ConnectionState;)I",
+    { "native_readAt", "(JI)I",
       (void *)android_media_MediaHTTPConnection_native_readAt },
 
     { "native_init", "()V",
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 0a91721..7875e93 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -88,10 +88,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
-import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.Preconditions;
+import com.android.server.net.NetworkStatsFactory;
 
 import com.google.android.collect.Maps;
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 7a8d23a..85787f2 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -248,6 +248,8 @@
 
     private final LocalLog mLocalLog = new LocalLog(100);
 
+    private final LocalLog mListenLog = new LocalLog(100);
+
     private PreciseDataConnectionState mPreciseDataConnectionState =
                 new PreciseDataConnectionState();
 
@@ -305,6 +307,8 @@
                     }
                     mDefaultSubId = newDefaultSubId;
                     mDefaultPhoneId = newDefaultPhoneId;
+                    mLocalLog.log("Default subscription updated: mDefaultPhoneId="
+                            + mDefaultPhoneId + ", mDefaultSubId" + mDefaultSubId);
                 }
             }
         }
@@ -598,10 +602,12 @@
             boolean notifyNow, int subId) {
         int callerUserId = UserHandle.getCallingUserId();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
-        if (VDBG) {
-            log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
+        String str = "listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
                 + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
-                + UserHandle.myUserId() + " callerUserId=" + callerUserId);
+                + UserHandle.myUserId() + " callerUserId=" + callerUserId;
+        mListenLog.log(str);
+        if (VDBG) {
+            log(str);
         }
 
         if (events != PhoneStateListener.LISTEN_NONE) {
@@ -1399,8 +1405,10 @@
                 if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)
                         && (mDataConnectionState[phoneId] != state
                         || mDataConnectionNetworkType[phoneId] != networkType)) {
-                    String str = "onDataConnectionStateChanged(" + state
-                            + ", " + networkType + ")";
+                    String str = "onDataConnectionStateChanged("
+                            + TelephonyManager.dataStateToString(state)
+                            + ", " + TelephonyManager.getNetworkTypeName(networkType)
+                            + ") subId=" + subId + ", phoneId=" + phoneId;
                     log(str);
                     mLocalLog.log(str);
                     for (Record r : mRecords) {
@@ -1916,12 +1924,16 @@
             pw.println("mEmergencyNumberList=" + mEmergencyNumberList);
             pw.println("mCallQuality=" + mCallQuality);
             pw.println("mCallAttributes=" + mCallAttributes);
+            pw.println("mDefaultPhoneId" + mDefaultPhoneId);
+            pw.println("mDefaultSubId" + mDefaultSubId);
 
             pw.decreaseIndent();
 
             pw.println("local logs:");
             pw.increaseIndent();
             mLocalLog.dump(fd, pw, args);
+            pw.println("listen logs:");
+            mListenLog.dump(fd, pw, args);
             pw.decreaseIndent();
             pw.println("registrations: count=" + recordCount);
             pw.increaseIndent();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 863ef67..ca0fbe1 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3462,9 +3462,9 @@
     /**
      * Process state of UID changed; if needed, will trigger
      * {@link #updateRulesForDataUsageRestrictionsUL(int)} and
-     * {@link #updateRulesForPowerRestrictionsUL(int)}
+     * {@link #updateRulesForPowerRestrictionsUL(int)}. Returns true if the state was updated.
      */
-    private void updateUidStateUL(int uid, int uidState) {
+    private boolean updateUidStateUL(int uid, int uidState) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
         try {
             final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
@@ -3483,14 +3483,15 @@
                     }
                     updateRulesForPowerRestrictionsUL(uid);
                 }
-                updateNetworkStats(uid, isUidStateForeground(uidState));
+                return true;
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
+        return false;
     }
 
-    private void removeUidStateUL(int uid) {
+    private boolean removeUidStateUL(int uid) {
         final int index = mUidState.indexOfKey(uid);
         if (index >= 0) {
             final int oldUidState = mUidState.valueAt(index);
@@ -3505,9 +3506,10 @@
                     updateRuleForRestrictPowerUL(uid);
                 }
                 updateRulesForPowerRestrictionsUL(uid);
-                updateNetworkStats(uid, false);
+                return true;
             }
         }
+        return false;
     }
 
     // adjust stats accounting based on foreground status
@@ -4419,21 +4421,26 @@
                 }
             }
         }
-
     };
 
     void handleUidChanged(int uid, int procState, long procStateSeq) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged");
         try {
+            boolean updated;
             synchronized (mUidRulesFirstLock) {
                 // We received a uid state change callback, add it to the history so that it
                 // will be useful for debugging.
                 mLogger.uidStateChanged(uid, procState, procStateSeq);
                 // Now update the network policy rules as per the updated uid state.
-                updateUidStateUL(uid, procState);
+                updated = updateUidStateUL(uid, procState);
                 // Updating the network rules is done, so notify AMS about this.
                 mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq);
             }
+            // Do this without the lock held. handleUidChanged() and handleUidGone() are
+            // called from the handler, so there's no multi-threading issue.
+            if (updated) {
+                updateNetworkStats(uid, isUidStateForeground(procState));
+            }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
@@ -4442,8 +4449,14 @@
     void handleUidGone(int uid) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidGone");
         try {
+            boolean updated;
             synchronized (mUidRulesFirstLock) {
-                removeUidStateUL(uid);
+                updated = removeUidStateUL(uid);
+            }
+            // Do this without the lock held. handleUidChanged() and handleUidGone() are
+            // called from the handler, so there's no multi-threading issue.
+            if (updated) {
+                updateNetworkStats(uid, false);
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
similarity index 99%
rename from core/java/com/android/internal/net/NetworkStatsFactory.java
rename to services/core/java/com/android/server/net/NetworkStatsFactory.java
index 1f3b4c4..bf34d8f 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.net;
+package com.android.server.net;
 
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.TAG_ALL;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4c07678..f237c4b 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -129,7 +129,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.net.VpnInfo;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 07a6c70..b5381ae 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -51,6 +51,7 @@
         "com_android_server_PersistentDataBlockService.cpp",
         "com_android_server_GraphicsStatsService.cpp",
         "onload.cpp",
+        ":lib_networkStatsFactory_native",
     ],
 
     include_dirs: [
@@ -140,3 +141,10 @@
         }
     }
 }
+
+filegroup {
+    name: "lib_networkStatsFactory_native",
+    srcs: [
+        "com_android_server_net_NetworkStatsFactory.cpp",
+    ],
+}
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp
similarity index 87%
rename from core/jni/com_android_internal_net_NetworkStatsFactory.cpp
rename to services/core/jni/com_android_server_net_NetworkStatsFactory.cpp
index 8259ffc..9cd743b 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp
@@ -21,9 +21,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <core_jni_helpers.h>
 #include <jni.h>
 
+#include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
@@ -333,29 +333,27 @@
                 (void*) readNetworkStatsDev },
 };
 
-int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) {
-    int err = RegisterMethodsOrDie(env,
-            "com/android/internal/net/NetworkStatsFactory", gMethods,
+int register_android_server_net_NetworkStatsFactory(JNIEnv* env) {
+    int err = jniRegisterNativeMethods(env, "com/android/server/net/NetworkStatsFactory", gMethods,
             NELEM(gMethods));
+    gStringClass = env->FindClass("java/lang/String");
+    gStringClass = static_cast<jclass>(env->NewGlobalRef(gStringClass));
 
-    gStringClass = FindClassOrDie(env, "java/lang/String");
-    gStringClass = MakeGlobalRefOrDie(env, gStringClass);
-
-    jclass clazz = FindClassOrDie(env, "android/net/NetworkStats");
-    gNetworkStatsClassInfo.size = GetFieldIDOrDie(env, clazz, "size", "I");
-    gNetworkStatsClassInfo.capacity = GetFieldIDOrDie(env, clazz, "capacity", "I");
-    gNetworkStatsClassInfo.iface = GetFieldIDOrDie(env, clazz, "iface", "[Ljava/lang/String;");
-    gNetworkStatsClassInfo.uid = GetFieldIDOrDie(env, clazz, "uid", "[I");
-    gNetworkStatsClassInfo.set = GetFieldIDOrDie(env, clazz, "set", "[I");
-    gNetworkStatsClassInfo.tag = GetFieldIDOrDie(env, clazz, "tag", "[I");
-    gNetworkStatsClassInfo.metered = GetFieldIDOrDie(env, clazz, "metered", "[I");
-    gNetworkStatsClassInfo.roaming = GetFieldIDOrDie(env, clazz, "roaming", "[I");
-    gNetworkStatsClassInfo.defaultNetwork = GetFieldIDOrDie(env, clazz, "defaultNetwork", "[I");
-    gNetworkStatsClassInfo.rxBytes = GetFieldIDOrDie(env, clazz, "rxBytes", "[J");
-    gNetworkStatsClassInfo.rxPackets = GetFieldIDOrDie(env, clazz, "rxPackets", "[J");
-    gNetworkStatsClassInfo.txBytes = GetFieldIDOrDie(env, clazz, "txBytes", "[J");
-    gNetworkStatsClassInfo.txPackets = GetFieldIDOrDie(env, clazz, "txPackets", "[J");
-    gNetworkStatsClassInfo.operations = GetFieldIDOrDie(env, clazz, "operations", "[J");
+    jclass clazz = env->FindClass("android/net/NetworkStats");
+    gNetworkStatsClassInfo.size = env->GetFieldID(clazz, "size", "I");
+    gNetworkStatsClassInfo.capacity = env->GetFieldID(clazz, "capacity", "I");
+    gNetworkStatsClassInfo.iface = env->GetFieldID(clazz, "iface", "[Ljava/lang/String;");
+    gNetworkStatsClassInfo.uid = env->GetFieldID(clazz, "uid", "[I");
+    gNetworkStatsClassInfo.set = env->GetFieldID(clazz, "set", "[I");
+    gNetworkStatsClassInfo.tag = env->GetFieldID(clazz, "tag", "[I");
+    gNetworkStatsClassInfo.metered = env->GetFieldID(clazz, "metered", "[I");
+    gNetworkStatsClassInfo.roaming = env->GetFieldID(clazz, "roaming", "[I");
+    gNetworkStatsClassInfo.defaultNetwork = env->GetFieldID(clazz, "defaultNetwork", "[I");
+    gNetworkStatsClassInfo.rxBytes = env->GetFieldID(clazz, "rxBytes", "[J");
+    gNetworkStatsClassInfo.rxPackets = env->GetFieldID(clazz, "rxPackets", "[J");
+    gNetworkStatsClassInfo.txBytes = env->GetFieldID(clazz, "txBytes", "[J");
+    gNetworkStatsClassInfo.txPackets = env->GetFieldID(clazz, "txPackets", "[J");
+    gNetworkStatsClassInfo.operations = env->GetFieldID(clazz, "operations", "[J");
 
     return err;
 }
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 83347bb..4b0bbae 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -54,6 +54,7 @@
 int register_android_server_SyntheticPasswordManager(JNIEnv* env);
 int register_android_server_GraphicsStatsService(JNIEnv* env);
 int register_android_hardware_display_DisplayViewport(JNIEnv* env);
+int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
 };
 
@@ -102,6 +103,7 @@
     register_android_server_SyntheticPasswordManager(env);
     register_android_server_GraphicsStatsService(env);
     register_android_hardware_display_DisplayViewport(env);
+    register_android_server_net_NetworkStatsFactory(env);
     register_android_server_net_NetworkStatsService(env);
     return JNI_VERSION_1_4;
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1bb1bc4..0dd6614 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2557,6 +2557,48 @@
     public static final String KEY_5G_ICON_CONFIGURATION_STRING =
             "5g_icon_configuration_string";
 
+    /**
+     * Passing this value as {@link KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
+     * subscription from a group instead of adding it to a group.
+     *
+     * TODO: Expose in a future release.
+     *
+     * @hide
+     */
+    public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
+
+    /**
+     * The UUID of a Group of related subscriptions in which to place the current subscription.
+     *
+     * A grouped subscription will behave for billing purposes and other UI purposes as though it
+     * is a transparent extension of other subscriptions in the group.
+     *
+     * <p>If set to {@link #REMOVE_GROUP_UUID_STRING}, then the subscription will be removed from
+     * its current group.
+     *
+     * TODO: unhide this key.
+     *
+     * @hide
+     */
+    public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING =
+            "key_subscription_group_uuid_string";
+
+    /**
+    * A boolean property indicating whether this subscription should be managed as an opportunistic
+    * subscription.
+    *
+    * If true, then this subscription will be selected based on available coverage and will not be
+    * available for a user in settings menus for selecting macro network providers. If unset,
+    * defaults to “false”.
+    *
+    * TODO: unhide this key.
+    *
+    * @hide
+    */
+    public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL =
+            "key_is_opportunistic_subscription_bool";
+
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -2948,6 +2990,8 @@
         sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN,
                 false);
+        sDefaults.putString(KEY_SUBSCRIPTION_GROUP_UUID_STRING, "");
+        sDefaults.putBoolean(KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e6e54ea..f9d9bab 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2647,7 +2647,7 @@
      */
     /** {@hide} */
     @UnsupportedAppUsage
-    public static String getNetworkTypeName(int type) {
+    public static String getNetworkTypeName(@NetworkType int type) {
         switch (type) {
             case NETWORK_TYPE_GPRS:
                 return "GPRS";
@@ -4788,6 +4788,22 @@
         }
     }
 
+    /**
+     * Convert data state to string
+     *
+     * @return The data state in string format.
+     * @hide
+     */
+    public static String dataStateToString(@DataState int state) {
+        switch (state) {
+            case DATA_DISCONNECTED: return "DISCONNECTED";
+            case DATA_CONNECTING: return "CONNECTING";
+            case DATA_CONNECTED: return "CONNECTED";
+            case DATA_SUSPENDED: return "SUSPENDED";
+        }
+        return "UNKNOWN(" + state + ")";
+    }
+
    /**
     * @hide
     */
diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp
new file mode 100644
index 0000000..f16ddb9
--- /dev/null
+++ b/tests/benchmarks/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2015 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.
+
+// build framework base core benchmarks
+// ============================================================
+
+java_library {
+    name: "networkStatsFactory-benchmarks",
+    installable: true,
+
+    srcs: ["src/**/*.java"],
+
+    libs: [
+        "caliper-api-target",
+        "services.core",
+    ],
+
+}
diff --git a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java b/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java
similarity index 95%
rename from core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
rename to tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java
index c213464..ef014f0 100644
--- a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
+++ b/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.internal.net;
+package com.android.server.net;
 
 import android.net.NetworkStats;
 import android.os.SystemClock;
+import com.android.server.net.NetworkStatsFactory;
 import com.google.caliper.AfterExperiment;
 import com.google.caliper.BeforeExperiment;
 import java.io.File;
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 176f541..45eeb2a 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -1,11 +1,8 @@
 //########################################################################
 // Build FrameworksNetTests package
 //########################################################################
-
-android_test {
-    name: "FrameworksNetTests",
-    // Include all test java files.
-    srcs: ["java/**/*.java"],
+java_defaults {
+    name: "FrameworksNetTests-jni-defaults",
     static_libs: [
         "FrameworksNetCommonTests",
         "frameworks-base-testutils",
@@ -21,6 +18,52 @@
         "android.test.base",
         "android.test.mock",
     ],
+    jni_libs: [
+        "ld-android",
+        "libartbase",
+        "libbacktrace",
+        "libbase",
+        "libbinder",
+        "libbinderthreadstate",
+        "libbpf",
+        "libbpf_android",
+        "libc++",
+        "libcrypto",
+        "libcutils",
+        "libdexfile",
+        "libdl_android",
+        "libhidl-gen-utils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libjsoncpp",
+        "liblog",
+        "liblzma",
+        "libnativehelper",
+        "libnetdbpf",
+        "libnetdutils",
+        "libpackagelistparser",
+        "libpcre2",
+        "libprocessgroup",
+        "libselinux",
+        "libui",
+        "libutils",
+        "libvintf",
+        "libvndksupport",
+        "libtinyxml2",
+        "libunwindstack",
+        "libutilscallstack",
+        "libziparchive",
+        "libz",
+        "netd_aidl_interface-cpp",
+        "libnetworkstatsfactorytestjni",
+    ],
+}
+
+android_test {
+    name: "FrameworksNetTests",
+    defaults: ["FrameworksNetTests-jni-defaults"],
+    srcs: ["java/**/*.java"],
     platform_apis: true,
     test_suites: ["device-tests"],
     certificate: "platform",
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
index 8b2b4e3..2adbb06 100644
--- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -45,6 +45,7 @@
 import libcore.util.HexEncoding;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -189,6 +190,8 @@
         udp.close();
     }
 
+    @Ignore
+    @Test
     public void testGetConnectionOwnerUid() throws Exception {
         checkGetConnectionOwnerUid("::", null);
         checkGetConnectionOwnerUid("::", "::");
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
similarity index 96%
rename from tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
rename to tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index 4ec4fdd..95bc7d9 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.net;
+package com.android.server.net;
 
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.METERED_NO;
@@ -70,6 +70,10 @@
             IoUtils.deleteContents(mTestProc);
         }
 
+        // The libandroid_servers which have the native method is not available to
+        // applications. So in order to have a test support native library, the native code
+        // related to networkStatsFactory is compiled to a minimal native library and loaded here.
+        System.loadLibrary("networkstatsfactorytestjni");
         mFactory = new NetworkStatsFactory(mTestProc, false);
     }
 
diff --git a/tests/net/jni/Android.bp b/tests/net/jni/Android.bp
new file mode 100644
index 0000000..9225ffb
--- /dev/null
+++ b/tests/net/jni/Android.bp
@@ -0,0 +1,23 @@
+cc_library_shared {
+    name: "libnetworkstatsfactorytestjni",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wthread-safety",
+    ],
+
+    srcs: [
+        ":lib_networkStatsFactory_native",
+        "test_onload.cpp",
+    ],
+
+    shared_libs: [
+        "libbpf_android",
+        "liblog",
+        "libnativehelper",
+        "libnetdbpf",
+        "libnetdutils",
+    ],
+}
diff --git a/tests/net/jni/test_onload.cpp b/tests/net/jni/test_onload.cpp
new file mode 100644
index 0000000..5194ddb
--- /dev/null
+++ b/tests/net/jni/test_onload.cpp
@@ -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.
+ */
+
+/*
+ * this is a mini native libaray for NetworkStatsFactoryTest to run properly. It
+ * load all the native method related to NetworkStatsFactory when test run
+ */
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+namespace android {
+int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
+};
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("GetEnv failed!");
+        return result;
+    }
+    ALOG_ASSERT(env, "Could not retrieve the env!");
+    register_android_server_net_NetworkStatsFactory(env);
+    return JNI_VERSION_1_4;
+}