Merge "Fixing issue in current and max duration calculations." into nyc-mr2-dev
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 10e6fb2..a7a8615 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -27,189 +27,25 @@
 import android.util.Slog;
 
 /**
- * Base class for code that will receive intents sent by sendBroadcast().
- *
- * <p>If you don't need to send broadcasts across applications, consider using
- * this class with {@link android.support.v4.content.LocalBroadcastManager} instead
- * of the more general facilities described below.  This will give you a much
- * more efficient implementation (no cross-process communication needed) and allow
- * you to avoid thinking about any security issues related to other applications
- * being able to receive or send your broadcasts.
+ * Base class for code that receives and handles broadcast intents sent by
+ * {@link android.content.Context#sendBroadcast(Intent)}.
  *
  * <p>You can either dynamically register an instance of this class with
  * {@link Context#registerReceiver Context.registerReceiver()}
- * or statically publish an implementation through the
+ * or statically declare an implementation with the
  * {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
  * tag in your <code>AndroidManifest.xml</code>.
- * 
- * <p><em><strong>Note:</strong></em>
- * &nbsp;&nbsp;&nbsp;If registering a receiver in your
- * {@link android.app.Activity#onResume() Activity.onResume()}
- * implementation, you should unregister it in 
- * {@link android.app.Activity#onPause() Activity.onPause()}.
- * (You won't receive intents when paused, 
- * and this will cut down on unnecessary system overhead). Do not unregister in 
- * {@link android.app.Activity#onSaveInstanceState(android.os.Bundle) Activity.onSaveInstanceState()},
- * because this won't be called if the user moves back in the history
- * stack.
- * 
- * <p>There are two major classes of broadcasts that can be received:</p>
- * <ul>
- * <li> <b>Normal broadcasts</b> (sent with {@link Context#sendBroadcast(Intent)
- * Context.sendBroadcast}) are completely asynchronous.  All receivers of the
- * broadcast are run in an undefined order, often at the same time.  This is
- * more efficient, but means that receivers cannot use the result or abort
- * APIs included here.
- * <li> <b>Ordered broadcasts</b> (sent with {@link Context#sendOrderedBroadcast(Intent, String)
- * Context.sendOrderedBroadcast}) are delivered to one receiver at a time.
- * As each receiver executes in turn, it can propagate a result to the next
- * receiver, or it can completely abort the broadcast so that it won't be passed
- * to other receivers.  The order receivers run in can be controlled with the
- * {@link android.R.styleable#AndroidManifestIntentFilter_priority
- * android:priority} attribute of the matching intent-filter; receivers with
- * the same priority will be run in an arbitrary order.
- * </ul>
- * 
- * <p>Even in the case of normal broadcasts, the system may in some
- * situations revert to delivering the broadcast one receiver at a time.  In
- * particular, for receivers that may require the creation of a process, only
- * one will be run at a time to avoid overloading the system with new processes.
- * In this situation, however, the non-ordered semantics hold: these receivers still
- * cannot return results or abort their broadcast.</p>
- * 
- * <p>Note that, although the Intent class is used for sending and receiving
- * these broadcasts, the Intent broadcast mechanism here is completely separate
- * from Intents that are used to start Activities with
- * {@link Context#startActivity Context.startActivity()}.
- * There is no way for a BroadcastReceiver
- * to see or capture Intents used with startActivity(); likewise, when
- * you broadcast an Intent, you will never find or start an Activity.
- * These two operations are semantically very different: starting an
- * Activity with an Intent is a foreground operation that modifies what the
- * user is currently interacting with; broadcasting an Intent is a background
- * operation that the user is not normally aware of.
- * 
- * <p>The BroadcastReceiver class (when launched as a component through
- * a manifest's {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
- * tag) is an important part of an
- * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">application's overall lifecycle</a>.</p>
- * 
- * <p>Topics covered here:
- * <ol>
- * <li><a href="#Security">Security</a>
- * <li><a href="#ReceiverLifecycle">Receiver Lifecycle</a>
- * <li><a href="#ProcessLifecycle">Process Lifecycle</a>
- * </ol>
  *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
- * <p>For information about how to use this class to receive and resolve intents, read the
- * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
- * developer guide.</p>
- * </div>
+ * <p>For more information about using BroadcastReceiver, read the
+ * <a href="{@docRoot}guide/components/broadcasts.html">Broadcasts</a> developer guide.</p></div>
  *
- * <a name="Security"></a>
- * <h3>Security</h3>
- *
- * <p>Receivers used with the {@link Context} APIs are by their nature a
- * cross-application facility, so you must consider how other applications
- * may be able to abuse your use of them.  Some things to consider are:
- *
- * <ul>
- * <li><p>The Intent namespace is global.  Make sure that Intent action names and
- * other strings are written in a namespace you own, or else you may inadvertently
- * conflict with other applications.
- * <li><p>When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)},
- * <em>any</em> application may send broadcasts to that registered receiver.  You can
- * control who can send broadcasts to it through permissions described below.
- * <li><p>When you publish a receiver in your application's manifest and specify
- * intent-filters for it, any other application can send broadcasts to it regardless
- * of the filters you specify.  To prevent others from sending to it, make it
- * unavailable to them with <code>android:exported="false"</code>.
- * <li><p>When you use {@link Context#sendBroadcast(Intent)} or related methods,
- * normally any other application can receive these broadcasts.  You can control who
- * can receive such broadcasts through permissions described below.  Alternatively,
- * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you
- * can also safely restrict the broadcast to a single application with
- * {@link Intent#setPackage(String) Intent.setPackage}
- * </ul>
- *
- * <p>None of these issues exist when using
- * {@link android.support.v4.content.LocalBroadcastManager}, since intents
- * broadcast it never go outside of the current process.
- *
- * <p>Access permissions can be enforced by either the sender or receiver
- * of a broadcast.
- *
- * <p>To enforce a permission when sending, you supply a non-null
- * <var>permission</var> argument to
- * {@link Context#sendBroadcast(Intent, String)} or
- * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}.
- * Only receivers who have been granted this permission
- * (by requesting it with the
- * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
- * tag in their <code>AndroidManifest.xml</code>) will be able to receive
- * the broadcast.
- *
- * <p>To enforce a permission when receiving, you supply a non-null
- * <var>permission</var> when registering your receiver -- either when calling
- * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)}
- * or in the static
- * {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
- * tag in your <code>AndroidManifest.xml</code>.  Only broadcasters who have
- * been granted this permission (by requesting it with the
- * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
- * tag in their <code>AndroidManifest.xml</code>) will be able to send an
- * Intent to the receiver.
- *
- * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
- * document for more information on permissions and security in general.
- *
- * <a name="ReceiverLifecycle"></a>
- * <h3>Receiver Lifecycle</h3>
- * 
- * <p>A BroadcastReceiver object is only valid for the duration of the call
- * to {@link #onReceive}.  Once your code returns from this function,
- * the system considers the object to be finished and no longer active.
- * 
- * <p>This has important repercussions to what you can do in an
- * {@link #onReceive} implementation: anything that requires asynchronous
- * operation is not available, because you will need to return from the
- * function to handle the asynchronous operation, but at that point the
- * BroadcastReceiver is no longer active and thus the system is free to kill
- * its process before the asynchronous operation completes.
- * 
- * <p>In particular, you may <i>not</i> show a dialog or bind to a service from
- * within a BroadcastReceiver.  For the former, you should instead use the
- * {@link android.app.NotificationManager} API.  For the latter, you can
- * use {@link android.content.Context#startService Context.startService()} to
- * send a command to the service.
- *
- * <a name="ProcessLifecycle"></a>
- * <h3>Process Lifecycle</h3>
- * 
- * <p>A process that is currently executing a BroadcastReceiver (that is,
- * currently running the code in its {@link #onReceive} method) is
- * considered to be a foreground process and will be kept running by the
- * system except under cases of extreme memory pressure.
- * 
- * <p>Once you return from onReceive(), the BroadcastReceiver is no longer
- * active, and its hosting process is only as important as any other application
- * components that are running in it.  This is especially important because if
- * that process was only hosting the BroadcastReceiver (a common case for
- * applications that the user has never or not recently interacted with), then
- * upon returning from onReceive() the system will consider its process
- * to be empty and aggressively kill it so that resources are available for other
- * more important processes.
- * 
- * <p>This means that for longer-running operations you will often use
- * a {@link android.app.Service} in conjunction with a BroadcastReceiver to keep
- * the containing process active for the entire time of your operation.
  */
 public abstract class BroadcastReceiver {
     private PendingResult mPendingResult;
     private boolean mDebugUnregister;
-    
+
     /**
      * State for a result that is pending for a broadcast receiver.  Returned
      * by {@link BroadcastReceiver#goAsync() goAsync()}
@@ -218,7 +54,7 @@
      * terminate; you must call {@link #finish()} once you are done with the
      * broadcast.  This allows you to process the broadcast off of the main
      * thread of your app.
-     * 
+     *
      * <p>Note on threading: the state inside of this class is not itself
      * thread-safe, however you can use it from any thread if you properly
      * sure that you do not have races.  Typically this means you will hand
@@ -232,14 +68,14 @@
         public static final int TYPE_REGISTERED = 1;
         /** @hide */
         public static final int TYPE_UNREGISTERED = 2;
-        
+
         final int mType;
         final boolean mOrderedHint;
         final boolean mInitialStickyHint;
         final IBinder mToken;
         final int mSendingUser;
         final int mFlags;
-        
+
         int mResultCode;
         String mResultData;
         Bundle mResultExtras;
@@ -259,7 +95,7 @@
             mSendingUser = userId;
             mFlags = flags;
         }
-        
+
         /**
          * Version of {@link BroadcastReceiver#setResultCode(int)
          * BroadcastReceiver.setResultCode(int)} for
@@ -331,7 +167,7 @@
             mResultData = data;
             mResultExtras = extras;
         }
-     
+
         /**
          * Version of {@link BroadcastReceiver#getAbortBroadcast()
          * BroadcastReceiver.getAbortBroadcast()} for
@@ -350,7 +186,7 @@
             checkSynchronousHint();
             mAbortBroadcast = true;
         }
-        
+
         /**
          * Version of {@link BroadcastReceiver#clearAbortBroadcast()
          * BroadcastReceiver.clearAbortBroadcast()} for
@@ -359,7 +195,7 @@
         public final void clearAbortBroadcast() {
             mAbortBroadcast = false;
         }
-        
+
         /**
          * Finish the broadcast.  The current result will be sent and the
          * next broadcast will proceed.
@@ -397,14 +233,14 @@
                 sendFinished(mgr);
             }
         }
-        
+
         /** @hide */
         public void setExtrasClassLoader(ClassLoader cl) {
             if (mResultExtras != null) {
                 mResultExtras.setClassLoader(cl);
             }
         }
-        
+
         /** @hide */
         public void sendFinished(IActivityManager am) {
             synchronized (this) {
@@ -412,7 +248,7 @@
                     throw new IllegalStateException("Broadcast already finished");
                 }
                 mFinished = true;
-            
+
                 try {
                     if (mResultExtras != null) {
                         mResultExtras.setAllowFds(false);
@@ -448,7 +284,7 @@
             Log.e("BroadcastReceiver", e.getMessage(), e);
         }
     }
-    
+
     public BroadcastReceiver() {
     }
 
@@ -468,14 +304,15 @@
      *
      * <p><b>If this BroadcastReceiver was launched through a &lt;receiver&gt; tag,
      * then the object is no longer alive after returning from this
-     * function.</b>  This means you should not perform any operations that
-     * return a result to you asynchronously -- in particular, for interacting
-     * with services, you should use
-     * {@link Context#startService(Intent)} instead of
-     * {@link Context#bindService(Intent, ServiceConnection, int)}.  If you wish
-     * to interact with a service that is already running, you can use
-     * {@link #peekService}.
-     * 
+     * function.</b> This means you should not perform any operations that
+     * return a result to you asynchronously. If you need to perform any follow up
+     * background work, schedule a {@link android.app.job.JobService} with
+     * {@link android.app.job.JobScheduler}.
+     *
+     * If you wish to interact with a service that is already running and previously
+     * bound using {@link android.content.Context#bindService(Intent, ServiceConnection, int) bindService()},
+     * you can use {@link #peekService}.
+     *
      * <p>The Intent filters used in {@link android.content.Context#registerReceiver}
      * and in application manifests are <em>not</em> guaranteed to be exclusive. They
      * are hints to the operating system about how to find suitable recipients. It is
@@ -483,7 +320,7 @@
      * resolution.  For this reason, {@link #onReceive(Context, Intent) onReceive()}
      * implementations should respond only to known actions, ignoring any unexpected
      * Intents that they may receive.
-     * 
+     *
      * @param context The Context in which the receiver is running.
      * @param intent The Intent being received.
      */
@@ -496,7 +333,7 @@
      * responsive to the broadcast (finishing it within 10s), but does allow
      * the implementation to move work related to it over to another thread
      * to avoid glitching the main UI thread due to disk IO.
-     * 
+     *
      * @return Returns a {@link PendingResult} representing the result of
      * the active broadcast.  The BroadcastRecord itself is no longer active;
      * all data and other interaction must go through {@link PendingResult}
@@ -508,15 +345,20 @@
         mPendingResult = null;
         return res;
     }
-    
+
     /**
-     * Provide a binder to an already-running service.  This method is synchronous
+     * Provide a binder to an already-bound service.  This method is synchronous
      * and will not start the target service if it is not present, so it is safe
      * to call from {@link #onReceive}.
-     * 
+     *
+     * For peekService() to return a non null {@link android.os.IBinder} interface
+     * the service must have published it before. In other words some component
+     * must have called {@link android.content.Context#bindService(Intent, ServiceConnection, int)} on it.
+     *
      * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)}
-     * @param service The Intent indicating the service you wish to use.  See {@link
-     * Context#startService(Intent)} for more information.
+     * @param service Identifies the already-bound service you wish to use. See
+     * {@link android.content.Context#bindService(Intent, ServiceConnection, int)}
+     * for more information.
      */
     public IBinder peekService(Context myContext, Intent service) {
         IActivityManager am = ActivityManagerNative.getDefault();
@@ -538,13 +380,13 @@
      * Activity {@link android.app.Activity#RESULT_CANCELED} and
      * {@link android.app.Activity#RESULT_OK} constants, though the
      * actual meaning of this value is ultimately up to the broadcaster.
-     * 
+     *
      * <p class="note">This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</p>
-     * 
+     *
      * @param code The new result code.
-     * 
+     *
      * @see #setResult(int, String, Bundle)
      */
     public final void setResultCode(int code) {
@@ -554,7 +396,7 @@
 
     /**
      * Retrieve the current result code, as set by the previous receiver.
-     * 
+     *
      * @return int The current result code.
      */
     public final int getResultCode() {
@@ -567,13 +409,13 @@
      * {@link Context#sendOrderedBroadcast(Intent, String)
      * Context.sendOrderedBroadcast}.  This is an arbitrary
      * string whose interpretation is up to the broadcaster.
-     * 
+     *
      * <p><strong>This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</strong></p>
-     * 
+     *
      * @param data The new result data; may be null.
-     * 
+     *
      * @see #setResult(int, String, Bundle)
      */
     public final void setResultData(String data) {
@@ -584,7 +426,7 @@
     /**
      * Retrieve the current result data, as set by the previous receiver.
      * Often this is null.
-     * 
+     *
      * @return String The current result data; may be null.
      */
     public final String getResultData() {
@@ -599,13 +441,13 @@
      * holding arbitrary data, whose interpretation is up to the
      * broadcaster.  Can be set to null.  Calling this method completely
      * replaces the current map (if any).
-     * 
+     *
      * <p><strong>This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</strong></p>
-     * 
+     *
      * @param extras The new extra data map; may be null.
-     * 
+     *
      * @see #setResult(int, String, Bundle)
      */
     public final void setResultExtras(Bundle extras) {
@@ -617,11 +459,11 @@
      * Retrieve the current result extra data, as set by the previous receiver.
      * Any changes you make to the returned Map will be propagated to the next
      * receiver.
-     * 
+     *
      * @param makeMap If true then a new empty Map will be made for you if the
      *                current Map is null; if false you should be prepared to
      *                receive a null Map.
-     * 
+     *
      * @return Map The current extras map.
      */
     public final Bundle getResultExtras(boolean makeMap) {
@@ -640,11 +482,11 @@
      * {@link Context#sendOrderedBroadcast(Intent, String)
      * Context.sendOrderedBroadcast}.  All current result data is replaced
      * by the value given to this method.
-     * 
+     *
      * <p><strong>This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</strong></p>
-     * 
+     *
      * @param code The new result code.  Often uses the
      * Activity {@link android.app.Activity#RESULT_CANCELED} and
      * {@link android.app.Activity#RESULT_OK} constants, though the
@@ -662,11 +504,11 @@
         mPendingResult.mResultData = data;
         mPendingResult.mResultExtras = extras;
     }
- 
+
     /**
      * Returns the flag indicating whether or not this receiver should
      * abort the current broadcast.
-     * 
+     *
      * @return True if the broadcast should be aborted.
      */
     public final boolean getAbortBroadcast() {
@@ -679,10 +521,10 @@
      * {@link Context#sendOrderedBroadcast(Intent, String)
      * Context.sendOrderedBroadcast}.  This will prevent
      * any other broadcast receivers from receiving the broadcast. It will still
-     * call {@link #onReceive} of the BroadcastReceiver that the caller of 
+     * call {@link #onReceive} of the BroadcastReceiver that the caller of
      * {@link Context#sendOrderedBroadcast(Intent, String)
      * Context.sendOrderedBroadcast} passed in.
-     * 
+     *
      * <p><strong>This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</strong></p>
@@ -691,7 +533,7 @@
         checkSynchronousHint();
         mPendingResult.mAbortBroadcast = true;
     }
-    
+
     /**
      * Clears the flag indicating that this receiver should abort the current
      * broadcast.
@@ -701,7 +543,7 @@
             mPendingResult.mAbortBroadcast = false;
         }
     }
-    
+
     /**
      * Returns true if the receiver is currently processing an ordered
      * broadcast.
@@ -709,7 +551,7 @@
     public final boolean isOrderedBroadcast() {
         return mPendingResult != null ? mPendingResult.mOrderedHint : false;
     }
-    
+
     /**
      * Returns true if the receiver is currently processing the initial
      * value of a sticky broadcast -- that is, the value that was last
@@ -719,7 +561,7 @@
     public final boolean isInitialStickyBroadcast() {
         return mPendingResult != null ? mPendingResult.mInitialStickyHint : false;
     }
-    
+
     /**
      * For internal use, sets the hint about whether this BroadcastReceiver is
      * running in ordered mode.
@@ -727,21 +569,21 @@
     public final void setOrderedHint(boolean isOrdered) {
         // Accidentally left in the SDK.
     }
-    
+
     /**
      * For internal use to set the result data that is active. @hide
      */
     public final void setPendingResult(PendingResult result) {
         mPendingResult = result;
     }
-    
+
     /**
      * For internal use to set the result data that is active. @hide
      */
     public final PendingResult getPendingResult() {
         return mPendingResult;
     }
-    
+
     /** @hide */
     public int getSendingUserId() {
         return mPendingResult.mSendingUser;
@@ -761,19 +603,19 @@
     public final void setDebugUnregister(boolean debug) {
         mDebugUnregister = debug;
     }
-    
+
     /**
      * Return the last value given to {@link #setDebugUnregister}.
      */
     public final boolean getDebugUnregister() {
         return mDebugUnregister;
     }
-    
+
     void checkSynchronousHint() {
         if (mPendingResult == null) {
             throw new IllegalStateException("Call while result is not pending");
         }
-        
+
         // Note that we don't assert when receiving the initial sticky value,
         // since that may have come from an ordered broadcast.  We'll catch
         // them later when the real broadcast happens again.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0afb546..f3c7817 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -87,6 +87,13 @@
      * sent as an extra; it should be consulted to see what kind of
      * connectivity event occurred.
      * <p/>
+     * Apps targeting Android 7.0 (API level 24) and higher do not receive this
+     * broadcast if they declare the broadcast receiver in their manifest. Apps
+     * will still receive broadcasts if they register their
+     * {@link android.content.BroadcastReceiver} with
+     * {@link android.content.Context#registerReceiver Context.registerReceiver()}
+     * and that context is still valid.
+     * <p/>
      * If this is a connection that was the result of failing over from a
      * disconnected network, then the FAILOVER_CONNECTION boolean extra is
      * set to true.
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 35e3065..a677d73 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -52,6 +52,17 @@
     public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
 
     /**
+     * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity.
+     *
+     * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges.
+     *
+     * @param fd the socket's {@link FileDescriptor}.
+     * @param packetType the hardware address type, one of ARPHRD_*.
+     */
+    public native static void attachControlPacketFilter(FileDescriptor fd, int packetType)
+            throws SocketException;
+
+    /**
      * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
      * @param fd the socket's {@link FileDescriptor}.
      * @param ifIndex the interface index.
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 2dfa8cd..286f5f7 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -353,7 +353,18 @@
                 return;
             }
             if (!mIsEnabled) {
-                throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+                Looper myLooper = Looper.myLooper();
+                if (myLooper == Looper.getMainLooper()) {
+                    throw new IllegalStateException(
+                            "Accessibility off. Did you forget to check that?");
+                } else {
+                    // If we're not running on the thread with the main looper, it's possible for
+                    // the state of accessibility to change between checking isEnabled and
+                    // calling this method. So just log the error rather than throwing the
+                    // exception.
+                    Log.e(LOG_TAG, "Interrupt called with accessibility disabled");
+                    return;
+                }
             }
             userId = mUserId;
         }
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index bdcfff2..e80d812 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -330,18 +330,16 @@
         mVelocityTracker.addMovement(ev);
         mVelocityTracker.computeCurrentVelocity(1000);
         if (!mDismissed) {
-
-            if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
-                    ev.getRawX() >= mLastX) {
+            if ((deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
+                    ev.getRawX() >= mLastX)
+                    || mVelocityTracker.getXVelocity() >= mMinFlingVelocity) {
                 mDismissed = true;
             }
         }
         // Check if the user tried to undo this.
         if (mDismissed && mSwiping) {
-            // Check if the user's finger is actually back
-            if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) ||
-                    // or user is flinging back left
-                    mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
+            // Check if the user's finger is actually flinging back to left
+            if (mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
                 mDismissed = false;
             }
         }
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 679e882..eb105d2 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -47,28 +47,33 @@
 
 namespace android {
 
+static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type);
+static const uint32_t kEtherHeaderLen = sizeof(ether_header);
+static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol);
+static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off);
+static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt);
+static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr);
+static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
+static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source);
+static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
 static const uint16_t kDhcpClientPort = 68;
 
 static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
 {
-    uint32_t ip_offset = sizeof(ether_header);
-    uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
-    uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
-    uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
     struct sock_filter filter_code[] = {
         // Check the protocol is UDP.
-        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  proto_offset),
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kIPv4Protocol),
         BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_UDP, 0, 6),
 
         // Check this is not a fragment.
-        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, flags_offset),
-        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   0x1fff, 4, 0),
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, kIPv4FlagsOffset),
+        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   IP_OFFMASK, 4, 0),
 
         // Get the IP header length.
-        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, ip_offset),
+        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, kEtherHeaderLen),
 
         // Check the destination port.
-        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, dport_indirect_offset),
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, kUDPDstPortIndirectOffset),
         BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 0, 1),
 
         // Accept or reject.
@@ -96,17 +101,13 @@
         return;
     }
 
-    uint32_t ipv6_offset = sizeof(ether_header);
-    uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt);
-    uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr);
-    uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type);
     struct sock_filter filter_code[] = {
         // Check IPv6 Next Header is ICMPv6.
-        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  ipv6_next_header_offset),
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kIPv6NextHeader),
         BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_ICMPV6, 0, 3),
 
         // Check ICMPv6 type is Router Advertisement.
-        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  icmp6_type_offset),
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kICMPv6TypeOffset),
         BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    ND_ROUTER_ADVERT, 0, 1),
 
         // Accept or reject.
@@ -125,6 +126,81 @@
     }
 }
 
+// TODO: Move all this filter code into libnetutils.
+static void android_net_utils_attachControlPacketFilter(
+        JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) {
+    if (hardwareAddressType != ARPHRD_ETHER) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "attachControlPacketFilter only supports ARPHRD_ETHER");
+        return;
+    }
+
+    // Capture all:
+    //     - ARPs
+    //     - DHCPv4 packets
+    //     - Router Advertisements & Solicitations
+    //     - Neighbor Advertisements & Solicitations
+    //
+    // tcpdump:
+    //     arp or
+    //     '(ip and udp port 68)' or
+    //     '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)'
+    struct sock_filter filter_code[] = {
+        // Load the link layer next payload field.
+        BPF_STMT(BPF_LD  | BPF_H   | BPF_ABS,  kEtherTypeOffset),
+
+        // Accept all ARP.
+        // TODO: Figure out how to better filter ARPs on noisy networks.
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0),
+
+        // If IPv4:
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9),
+
+        // Check the protocol is UDP.
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kIPv4Protocol),
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_UDP, 0, 14),
+
+        // Check this is not a fragment.
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, kIPv4FlagsOffset),
+        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   IP_OFFMASK, 12, 0),
+
+        // Get the IP header length.
+        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, kEtherHeaderLen),
+
+        // Check the source port.
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, kUDPSrcPortIndirectOffset),
+        BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 8, 0),
+
+        // Check the destination port.
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, kUDPDstPortIndirectOffset),
+        BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 6, 7),
+
+        // IPv6 ...
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6),
+        // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ...
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kIPv6NextHeader),
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_ICMPV6, 0, 4),
+        // ... and check the ICMPv6 type is one of RS/RA/NS/NA.
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kICMPv6TypeOffset),
+        BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K,    ND_ROUTER_SOLICIT, 0, 2),
+        BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K,    ND_NEIGHBOR_ADVERT, 1, 0),
+
+        // Accept or reject.
+        BPF_STMT(BPF_RET | BPF_K,              0xffff),
+        BPF_STMT(BPF_RET | BPF_K,              0)
+    };
+    struct sock_fprog filter = {
+        sizeof(filter_code) / sizeof(filter_code[0]),
+        filter_code,
+    };
+
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+    }
+}
+
 static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
         jint ifIndex)
 {
@@ -266,6 +342,7 @@
     { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
     { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
     { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
+    { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
     { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
 };
 
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c182831..cff3f38 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1208,7 +1208,7 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ermöglicht der App, Installationssitzungen zu lesen. Dadurch kann sie Details aktiver Paketinstallationen abrufen."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Installation von Paketen anfordern"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ermöglicht der App, die Installation von Paketen anzufordern"</string>
-    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"Fragen, ob Akku-Leistungsoptimierungen ignoriert werden können"</string>
+    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"fragen, ob Akku-Leistungsoptimierungen ignoriert werden können"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Erlaubt einer App, nach der Berechtigung zum Ignorieren der Akku-Leistungsoptimierungen zu fragen."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Für Zoomeinstellung zweimal berühren"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget konnte nicht hinzugefügt werden."</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index b0aaecf..b85ddf2 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -1208,8 +1208,8 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que unha aplicación consulte as sesións de instalación. Desta forma, pode ver os detalles acerca das instalacións de paquetes activas."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar instalación de paquetes"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite a unha aplicación solicitar a instalación dos paquetes."</string>
-    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"solicitar ignorar optimizacións da batería"</string>
-    <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Permite que unha aplicación solicite permiso para ignorar optimizacións da batería para esa aplicación."</string>
+    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"pedir que se ignore a optimización da batería"</string>
+    <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Fai que unha aplicación poida solicitar permiso para ignorar as optimizacións da batería."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Toca dúas veces para controlar o zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Non se puido engadir o widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 40c35fa..e4af8f8 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -248,9 +248,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"위치"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"이 기기의 위치정보에 액세스"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"캘린더"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"일정에 접근할 수 있도록"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"캘린더에 액세스"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
-    <string name="permgroupdesc_sms" msgid="4656988620100940350">"문자 메시지를 보내고 확인할 수 있도록"</string>
+    <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS 메시지 전송 및 보기"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"저장"</string>
     <string name="permgroupdesc_storage" msgid="637758554581589203">"기기 사진, 미디어, 파일 액세스"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"마이크"</string>
@@ -258,7 +258,7 @@
     <string name="permgrouplab_camera" msgid="4820372495894586615">"카메라"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"사진 및 동영상 촬영"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"전화"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"통화 상태를 관리하거나 전화를 걸 수 있도록"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"전화 걸기 및 관리"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"신체 센서"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"생체 신호에 관한 센서 데이터에 액세스"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"창 콘텐츠 가져오기"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index ecbf649..e7458ae 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -269,8 +269,8 @@
     <string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"යෙදුම් අන්තර්ගතයට ප්‍රවේශ්‍යතාවය වැඩිවන ලෙස සකස් කිරීමට ඇතැම් විට ස්ක්‍රිප්ට් ස්ථාපනය කර ඇත."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ඔබ ටයිප් කළ පෙළ බලන්න"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ණයවරපත් අංක සහ මුරපද වැනි පුද්ගලික දත්ත ඇතුළත් වේ."</string>
-    <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"සංදර්ශන විශාලන මට්ටම පාලනය කිරීම"</string>
-    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"සංදර්ශනයේ විශාලන මට්ටම සහ පිහිටීම පාලනය කිරීම."</string>
+    <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"සංදර්ශනයේ විශාලනය පාලනය කරන්න"</string>
+    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"සංදර්ශනයේ විශාලන මට්ටම සහ පිහිටීම පාලනය කරන්න."</string>
     <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"අභින සිදු කරන්න"</string>
     <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"තට්ටු කිරීමට, ස්වයිප් කිරීමට, පින්ච් කිරීමට, සහ වෙනත් අභින සිදු කිරීමට හැකිය."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"තත්ව තීරුව අබල කරන්න හෝ වෙනස් කරන්න"</string>
@@ -457,13 +457,13 @@
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"සමමුහුර්ත සැකසීම් කියවන්න"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ගිණුම සඳහා සමමුහුර්ත සැකසීම් කියවීමට යෙදුමට අවසර දෙන්න. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත දැයි මෙයට හඳුනා ගත හැක."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"සමමුහුර්ත කිරීම සක්‍රිය කරන්න සහ අක්‍රිය කරන්න"</string>
-    <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ගිණුම සඳහා සමමුහුර්ත සැකසීම් වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. උදාහරණයක් ලෙස, ගිණුම සමඟ පුද්ගල යෙදුම සමමුහුර්ත කිරීම සක්‍රිය කිරීමට භාවිත කල හැක."</string>
+    <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ගිණුමක් සඳහා සමමුහුර්ත සැකසීම් විකරණය කිරීමට යෙදුමකට ඉඩ දෙයි. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත කිරීම සබල කිරීමට මෙය භාවිත කළ හැක."</string>
     <string name="permlab_readSyncStats" msgid="7396577451360202448">"සමමුහුර්ත කිරීමේ සංඛ්‍යාන කියවීම"</string>
     <string name="permdesc_readSyncStats" msgid="1510143761757606156">"සමමුහුර්ත කිරීමේ සිදුවීම් ඉතිහාසය සහ කෙතරම් දත්ත සමමුහුර්ත වී ඇතිදැයි ඇතුලත් ගිණුම සඳහා සමමුහුර්ත කිරීමේ සංඛ්‍යාන කියවීමට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ඔබගේ USB ආචයනය හි අන්තර්ගතය කියවන්න"</string>
     <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ඔබගේ SD කාඩ් පතෙහි අන්තර්ගතය කියවන්න"</string>
     <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"යෙදුමට ඔබගේ USB ආචයනය අන්තර්ගතය කියවීමට අවසර දෙන්න."</string>
-    <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"යෙදුමට ඔබගේ SD කාඩ් පතින් අන්තර්ගත කියවීමට අවසර දෙන්න."</string>
+    <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"යෙදුමට ඔබගේ SD කාඩ්පතේ අන්තර්ගතය කියවීමට ඉඩ දෙයි."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ඔබගේ USB ආචයනයේ අන්තර්ගත වෙනස් කිරීම හෝ මැකීම"</string>
     <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ඔබගේ SD පතේ අන්තර්ගත වෙනස් කිරීම හෝ මැකීම"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB ආචයනය වෙත ලිවීමට යෙදුමට අවසර දෙන්න."</string>
@@ -737,7 +737,7 @@
     <!-- String.format failed for translation -->
     <!-- no translation found for keyguard_accessibility_widget_changed (5678624624681400191) -->
     <skip />
-    <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"විජටය එකතු කරන්න."</string>
+    <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"විජටය එක් කරන්න."</string>
     <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"හිස්"</string>
     <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"අගුළු අරින ප්‍රදේශය විදහා ඇත."</string>
     <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"අගුළු අරින ප්‍රදේශය හැකිලී ඇත."</string>
@@ -950,7 +950,7 @@
     <string name="undo" msgid="7905788502491742328">"අස් කරන්න"</string>
     <string name="redo" msgid="7759464876566803888">"යළි කරන්න"</string>
     <string name="textSelectionCABTitle" msgid="5236850394370820357">"පෙළ තේරීම"</string>
-    <string name="addToDictionary" msgid="4352161534510057874">"ශබ්ද කෝෂයට එකතු කරන්න"</string>
+    <string name="addToDictionary" msgid="4352161534510057874">"ශබ්ද කෝෂයට එක් කරන්න"</string>
     <string name="deleteText" msgid="6979668428458199034">"මකන්න"</string>
     <string name="inputMethod" msgid="1653630062304567879">"ආදාන ක්‍රමය"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"පෙළ ක්‍රියාවන්"</string>
@@ -1286,7 +1286,7 @@
     <string name="sync_undo_deletes" msgid="2941317360600338602">"මැකීම් අස් කරන්න"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"දැනට කිසිවක් නොකරන්න"</string>
     <string name="choose_account_label" msgid="5655203089746423927">"ගිණුමක් තෝරන්න"</string>
-    <string name="add_account_label" msgid="2935267344849993553">"ගිණුමක් එකතු කරන්න"</string>
+    <string name="add_account_label" msgid="2935267344849993553">"ගිණුමක් එක් කරන්න"</string>
     <string name="add_account_button_label" msgid="3611982894853435874">"ගිණුමක් එක් කරන්න"</string>
     <string name="number_picker_increment_button" msgid="2412072272832284313">"වැඩි කරන්න"</string>
     <string name="number_picker_decrement_button" msgid="476050778386779067">"අඩු කරන්න"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1c06cc8..ebfb837 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2620,6 +2620,9 @@
     <!-- Component that is the default launcher when demo mode is enabled. -->
     <string name="config_demoModeLauncherComponent">com.android.retaildemo/.DemoPlayer</string>
 
+    <!-- Hashed password (SHA-256) used to restrict demo mode operation -->
+    <string name="config_demoModePassword" translatable="false"></string>
+
     <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
     <bool name="config_useRoundIcon">false</bool>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5ae03a9..edd8b78 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -202,6 +202,12 @@
     <!-- Displayed to tell the user that all service is blocked by access control. -->
     <string name="RestrictedOnAll">All voice/data/SMS services are blocked.</string>
 
+    <!-- Displayed to tell the user that they should switch their network preference. -->
+    <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string>
+    <!-- Displayed to tell the user that they should switch their network preference. -->
+    <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings > Cellular networks > Preferred network type."</string>
+
+
     <!-- Displayed to tell the user that peer changed TTY mode -->
     <string name="peerTtyModeFull">Peer requested TTY Mode FULL</string>
     <string name="peerTtyModeHco">Peer requested TTY Mode HCO</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 225a20a..86eea99 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -486,6 +486,8 @@
   <java-symbol type="string" name="RestrictedOnData" />
   <java-symbol type="string" name="RestrictedOnEmergency" />
   <java-symbol type="string" name="RestrictedOnNormal" />
+  <java-symbol type="string" name="NetworkPreferenceSwitchSummary" />
+  <java-symbol type="string" name="NetworkPreferenceSwitchTitle" />
   <java-symbol type="string" name="SetupCallDefault" />
   <java-symbol type="string" name="accept" />
   <java-symbol type="string" name="accessibility_enabled" />
@@ -1113,6 +1115,7 @@
   <java-symbol type="string" name="config_ethernet_tcp_buffers" />
   <java-symbol type="string" name="config_wifi_tcp_buffers" />
   <java-symbol type="string" name="config_demoModeLauncherComponent" />
+  <java-symbol type="string" name="config_demoModePassword" />
   <java-symbol type="string" name="demo_starting_message" />
   <java-symbol type="string" name="demo_restarting_message" />
   <java-symbol type="string" name="conference_call" />
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 9eceeac..c965067 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -31,6 +31,7 @@
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.IVoiceInteractionSessionListener;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
@@ -282,7 +283,7 @@
 
     @Nullable
     private ComponentName getAssistInfo() {
-        return mAssistUtils.getAssistComponentForUser(UserHandle.USER_CURRENT);
+        return mAssistUtils.getAssistComponentForUser(KeyguardUpdateMonitor.getCurrentUser());
     }
 
     public void showDisclosure() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
index 1338d9d..71ddba5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
@@ -84,6 +84,11 @@
         log("I", tag, s);
     }
 
+    public static void wLogcat(String tag, String s) {
+        Log.w(TAG, tag + "\t" + s);
+        log("W", tag, s);
+    }
+
     public static void w(String tag, String s) {
         if (LOGCAT) {
             Log.w(TAG, tag + "\t" + s);
@@ -133,7 +138,7 @@
         pw.println();
     }
 
-    public static synchronized void wtf(String tag, String s) {
+    public static synchronized void wtf(String tag, String s, Throwable here) {
         if (!ENABLED) {
             return;
         }
@@ -161,6 +166,6 @@
             Log.e(TAG, "Unable to write log, build must be debuggable.");
         }
 
-        Log.wtf(TAG, tag + " " + s + "; " + fileMessage);
+        Log.wtf(TAG, tag + " " + s + "; " + fileMessage, here);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 8fc555f..1abea37 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -71,6 +71,7 @@
     private boolean mSessionActive = false;
     private int mState = StatusBarState.SHADE;
     private boolean mScreenOn;
+    private Runnable mPendingWtf;
 
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
@@ -136,6 +137,7 @@
     private void onSessionStart() {
         if (FalsingLog.ENABLED) {
             FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
+            clearPendingWtf();
         }
         mBouncerOn = false;
         mSessionActive = true;
@@ -172,13 +174,35 @@
         if (FalsingLog.ENABLED) {
             // We're getting some false wtfs from touches that happen after the device went
             // to sleep. Only report missing sessions that happen when the device is interactive.
-            if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()) {
-                FalsingLog.wtf("isFalseTouch", new StringBuilder()
+            if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()
+                    && mPendingWtf == null) {
+                int enabled = isEnabled() ? 1 : 0;
+                int screenOn = mScreenOn ? 1 : 0;
+                String state = StatusBarState.toShortString(mState);
+                Throwable here = new Throwable("here");
+                FalsingLog.wLogcat("isFalseTouch", new StringBuilder()
                         .append("Session is not active, yet there's a query for a false touch.")
-                        .append(" enabled=").append(isEnabled() ? 1 : 0)
-                        .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
-                        .append(" mState=").append(StatusBarState.toShortString(mState))
+                        .append(" enabled=").append(enabled)
+                        .append(" mScreenOn=").append(screenOn)
+                        .append(" mState=").append(state)
+                        .append(". Escalating to WTF if screen does not turn on soon.")
                         .toString());
+
+                // Unfortunately we're also getting false positives for touches that happen right
+                // after the screen turns on, but before that notification has made it to us.
+                // Unfortunately there's no good way to catch that, except to wait and see if we get
+                // the screen on notification soon.
+                mPendingWtf = () -> FalsingLog.wtf("isFalseTouch", new StringBuilder()
+                        .append("Session did not become active after query for a false touch.")
+                        .append(" enabled=").append(enabled)
+                        .append('/').append(isEnabled() ? 1 : 0)
+                        .append(" mScreenOn=").append(screenOn)
+                        .append('/').append(mScreenOn ? 1 : 0)
+                        .append(" mState=").append(state)
+                        .append('/').append(StatusBarState.toShortString(mState))
+                        .append(". Look for warnings ~1000ms earlier to see root cause.")
+                        .toString(), here);
+                mHandler.postDelayed(mPendingWtf, 1000);
             }
         }
         if (mAccessibilityManager.isTouchExplorationEnabled()) {
@@ -189,6 +213,13 @@
         return mHumanInteractionClassifier.isFalseTouch();
     }
 
+    private void clearPendingWtf() {
+        if (mPendingWtf != null) {
+            mHandler.removeCallbacks(mPendingWtf);
+            mPendingWtf = null;
+        }
+    }
+
     @Override
     public synchronized void onSensorChanged(SensorEvent event) {
         mDataCollector.onSensorChanged(event);
@@ -224,6 +255,7 @@
             FalsingLog.i("onScreenTurningOn", new StringBuilder()
                     .append("from=").append(mScreenOn ? 1 : 0)
                     .toString());
+            clearPendingWtf();
         }
         mScreenOn = true;
         if (sessionEntrypoint()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 30613bc..556aac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2913,7 +2913,7 @@
 
         runPostCollapseRunnables();
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
-        showBouncer();
+        showBouncerIfKeyguard();
         recomputeDisableFlags(true /* animate */);
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
@@ -4438,13 +4438,17 @@
         return false;
     }
 
-    private void showBouncer() {
+    private void showBouncerIfKeyguard() {
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
-            mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
-            mStatusBarKeyguardViewManager.dismiss();
+            showBouncer();
         }
     }
 
+    private void showBouncer() {
+        mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
+        mStatusBarKeyguardViewManager.dismiss();
+    }
+
     private void instantExpandNotificationsPanel() {
 
         // Make our window larger and the panel expanded.
@@ -4541,7 +4545,7 @@
     public void onTrackingStopped(boolean expand) {
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
             if (!expand && !mUnlockMethodCache.canSkipBouncer()) {
-                showBouncer();
+                showBouncerIfKeyguard();
             }
         }
     }
@@ -4632,7 +4636,7 @@
                 || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
         if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
             mLeaveOpenOnKeyguardHide = true;
-            showBouncer();
+            showBouncerIfKeyguard();
             mDraggedDownRow = row;
             mPendingRemoteInputView = null;
         } else {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 58bb5f3..34ccb7b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -538,7 +538,7 @@
 
     @Override
     public void interrupt(int userId) {
-        CopyOnWriteArrayList<Service> services;
+        List<IAccessibilityServiceClient> interfacesToInterrupt;
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
@@ -549,15 +549,24 @@
             if (resolvedUserId != mCurrentUserId) {
                 return;
             }
-            services = getUserStateLocked(resolvedUserId).mBoundServices;
+            List<Service> services = getUserStateLocked(resolvedUserId).mBoundServices;
+            int numServices = services.size();
+            interfacesToInterrupt = new ArrayList<>(numServices);
+            for (int i = 0; i < numServices; i++) {
+                Service service = services.get(i);
+                IBinder a11yServiceBinder = service.mService;
+                IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
+                if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) {
+                    interfacesToInterrupt.add(a11yServiceInterface);
+                }
+            }
         }
-        for (int i = 0, count = services.size(); i < count; i++) {
-            Service service = services.get(i);
+        for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
             try {
-                service.mServiceInterface.onInterrupt();
+                interfacesToInterrupt.get(i).onInterrupt();
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error during sending interrupt request to "
-                    + service.mService, re);
+                Slog.e(LOG_TAG, "Error sending interrupt request to "
+                        + interfacesToInterrupt.get(i), re);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d00783e..b68af43 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1547,6 +1547,7 @@
     static final int NOTIFY_FORCED_RESIZABLE_MSG = 67;
     static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 68;
     static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 69;
+    static final int NOTIFY_VR_SLEEPING_MSG = 70;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2362,6 +2363,8 @@
                     }
                 }
                 vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
+            } case NOTIFY_VR_SLEEPING_MSG: {
+                notifyVrManagerOfSleepState(msg.arg1 != 0);
             } break;
             }
         }
@@ -3164,6 +3167,11 @@
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
     }
 
+    private void sendNotifyVrManagerOfSleepState(boolean isSleeping) {
+        mHandler.sendMessage(
+                mHandler.obtainMessage(NOTIFY_VR_SLEEPING_MSG, isSleeping ? 1 : 0, 0));
+    }
+
     private void notifyVrManagerOfSleepState(boolean isSleeping) {
         final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
         if (vrService == null) {
@@ -11690,7 +11698,7 @@
             startTimeTrackingFocusedActivityLocked();
             mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
             mStackSupervisor.comeOutOfSleepIfNeededLocked();
-            notifyVrManagerOfSleepState(false);
+            sendNotifyVrManagerOfSleepState(false);
             updateOomAdjLocked();
         } else if (!mSleeping && shouldSleepLocked()) {
             mSleeping = true;
@@ -11699,7 +11707,7 @@
             }
             mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
             mStackSupervisor.goingToSleepLocked();
-            notifyVrManagerOfSleepState(true);
+            sendNotifyVrManagerOfSleepState(true);
             updateOomAdjLocked();
 
             // Initialize the wake times of all processes.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 8cb13da..5f464bd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -34,6 +34,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * A class for managing network logging.
@@ -49,13 +50,13 @@
     private IIpConnectivityMetrics mIpConnectivityMetrics;
     private ServiceThread mHandlerThread;
     private NetworkLoggingHandler mNetworkLoggingHandler;
-    private boolean mIsLoggingEnabled;
+    private AtomicBoolean mIsLoggingEnabled;
 
     private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
         @Override
         public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
                 long timestamp, int uid) {
-            if (!mIsLoggingEnabled) {
+            if (!mIsLoggingEnabled.get()) {
                 return;
             }
             DnsEvent dnsEvent = new DnsEvent(hostname, ipAddresses, ipAddressesCount,
@@ -65,7 +66,7 @@
 
         @Override
         public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
-            if (!mIsLoggingEnabled) {
+            if (!mIsLoggingEnabled.get()) {
                 return;
             }
             ConnectEvent connectEvent = new ConnectEvent(ipAddr, port, mPm.getNameForUid(uid),
@@ -116,7 +117,7 @@
                         mDpm);
                 mNetworkLoggingHandler.scheduleBatchFinalization(
                         NetworkLoggingHandler.BATCH_FINALIZATION_TIMEOUT_MS);
-                mIsLoggingEnabled = true;
+                mIsLoggingEnabled.set(true);
                 return true;
             } else {
                 return false;
@@ -130,7 +131,7 @@
     boolean stopNetworkLogging() {
         Log.d(TAG, "Stopping network logging");
         // stop the logging regardless of whether we fail to unregister listener
-        mIsLoggingEnabled = false;
+        mIsLoggingEnabled.set(false);
         try {
             if (!checkIpConnectivityMetricsService()) {
                 // the IIpConnectivityMetrics service should have been present at this point
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index ef4bc02..060ded6 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -25,8 +25,10 @@
  * Defines basic data and operations needed to build and use packets for the
  * DHCP protocol.  Subclasses create the specific packets used at each
  * stage of the negotiation.
+ *
+ * @hide
  */
-abstract class DhcpPacket {
+public abstract class DhcpPacket {
     protected static final String TAG = "DhcpPacket";
 
     // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
new file mode 100644
index 0000000..884a8a7
--- /dev/null
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 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.ip;
+
+import static android.system.OsConstants.*;
+
+import android.net.NetworkUtils;
+import android.net.util.BlockingSocketReader;
+import android.net.util.ConnectivityPacketSummary;
+import android.os.Handler;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.PacketSocketAddress;
+import android.util.Log;
+import android.util.LocalLog;
+
+import libcore.io.IoBridge;
+import libcore.util.HexEncoding;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+
+
+/**
+ * Critical connectivity packet tracking daemon.
+ *
+ * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
+ *
+ * This class's constructor, start() and stop() methods must only be called
+ * from the same thread on which the passed in |log| is accessed.
+ *
+ * Log lines include a hexdump of the packet, which can be decoded via:
+ *
+ *     echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /'
+ *                       | text2pcap - -
+ *                       | tcpdump -n -vv -e -r -
+ *
+ * @hide
+ */
+public class ConnectivityPacketTracker {
+    private static final String TAG = ConnectivityPacketTracker.class.getSimpleName();
+    private static final boolean DBG = false;
+    private static final String MARK_START = "--- START ---";
+    private static final String MARK_STOP = "--- STOP ---";
+
+    private final String mTag;
+    private final Handler mHandler;
+    private final LocalLog mLog;
+    private final BlockingSocketReader mPacketListener;
+
+    public ConnectivityPacketTracker(NetworkInterface netif, LocalLog log) {
+        final String ifname;
+        final int ifindex;
+        final byte[] hwaddr;
+        final int mtu;
+
+        try {
+            ifname = netif.getName();
+            ifindex = netif.getIndex();
+            hwaddr = netif.getHardwareAddress();
+            mtu = netif.getMTU();
+        } catch (NullPointerException|SocketException e) {
+            throw new IllegalArgumentException("bad network interface", e);
+        }
+
+        mTag = TAG + "." + ifname;
+        mHandler = new Handler();
+        mLog = log;
+        mPacketListener = new PacketListener(ifindex, hwaddr, mtu);
+    }
+
+    public void start() {
+        mLog.log(MARK_START);
+        mPacketListener.start();
+    }
+
+    public void stop() {
+        mPacketListener.stop();
+        mLog.log(MARK_STOP);
+    }
+
+    private final class PacketListener extends BlockingSocketReader {
+        private final int mIfIndex;
+        private final byte mHwAddr[];
+
+        PacketListener(int ifindex, byte[] hwaddr, int mtu) {
+            super(mtu);
+            mIfIndex = ifindex;
+            mHwAddr = hwaddr;
+        }
+
+        @Override
+        protected FileDescriptor createSocket() {
+            FileDescriptor s = null;
+            try {
+                // TODO: Evaluate switching to SOCK_DGRAM and changing the
+                // BlockingSocketReader's read() to recvfrom(), so that this
+                // might work on non-ethernet-like links (via SLL).
+                s = Os.socket(AF_PACKET, SOCK_RAW, 0);
+                NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
+                Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex));
+            } catch (ErrnoException | IOException e) {
+                logError("Failed to create packet tracking socket: ", e);
+                closeSocket(s);
+                return null;
+            }
+            return s;
+        }
+
+        @Override
+        protected void handlePacket(byte[] recvbuf, int length) {
+            final String summary = ConnectivityPacketSummary.summarize(
+                    mHwAddr, recvbuf, length);
+            if (summary == null) return;
+
+            if (DBG) Log.d(mTag, summary);
+            addLogEntry(summary +
+                        "\n[" + new String(HexEncoding.encode(recvbuf, 0, length)) + "]");
+        }
+
+        @Override
+        protected void logError(String msg, Exception e) {
+            Log.e(mTag, msg, e);
+            addLogEntry(msg + e);
+        }
+
+        private void addLogEntry(String entry) {
+            mHandler.post(() -> mLog.log(entry));
+        }
+    }
+}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 58b2dec..87018ec 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -374,6 +374,7 @@
     private static final int EVENT_DHCPACTION_TIMEOUT = 10;
 
     private static final int MAX_LOG_RECORDS = 500;
+    private static final int MAX_PACKET_RECORDS = 100;
 
     private static final boolean NO_CALLBACKS = false;
     private static final boolean SEND_CALLBACKS = true;
@@ -399,6 +400,7 @@
     private final WakeupMessage mDhcpActionTimeoutAlarm;
     private final AvoidBadWifiTracker mAvoidBadWifiTracker;
     private final LocalLog mLocalLog;
+    private final LocalLog mConnectivityPacketLog;
     private final MessageHandlingLogger mMsgStateLogger;
     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
 
@@ -439,6 +441,7 @@
         mNwService = nwService;
 
         mLocalLog = new LocalLog(MAX_LOG_RECORDS);
+        mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS);
         mMsgStateLogger = new MessageHandlingLogger();
 
         mNetlinkTracker = new NetlinkTracker(
@@ -609,7 +612,7 @@
         }
 
         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
-        pw.println("APF dump:");
+        pw.println(mTag + " APF dump:");
         pw.increaseIndent();
         // Thread-unsafe access to mApfFilter but just used for debugging.
         ApfFilter apfFilter = mApfFilter;
@@ -625,6 +628,12 @@
         pw.increaseIndent();
         mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
         pw.decreaseIndent();
+
+        pw.println();
+        pw.println(mTag + " connectivity packet log:");
+        pw.increaseIndent();
+        mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
+        pw.decreaseIndent();
     }
 
 
@@ -1220,6 +1229,7 @@
     }
 
     class RunningState extends State {
+        private ConnectivityPacketTracker mPacketTracker;
         private boolean mDhcpActionInFlight;
 
         @Override
@@ -1232,6 +1242,9 @@
                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
             }
 
+            mPacketTracker = createPacketTracker();
+            if (mPacketTracker != null) mPacketTracker.start();
+
             if (mConfiguration.mEnableIPv6 && !startIPv6()) {
                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
                 transitionTo(mStoppingState);
@@ -1266,6 +1279,11 @@
                 mDhcpClient.doQuit();
             }
 
+            if (mPacketTracker != null) {
+                mPacketTracker.stop();
+                mPacketTracker = null;
+            }
+
             if (mApfFilter != null) {
                 mApfFilter.shutdown();
                 mApfFilter = null;
@@ -1274,6 +1292,14 @@
             resetLinkProperties();
         }
 
+        private ConnectivityPacketTracker createPacketTracker() {
+            try {
+                return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog);
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+        }
+
         private void ensureDhcpAction() {
             if (!mDhcpActionInFlight) {
                 mCallback.onPreDhcpAction();
diff --git a/services/net/java/android/net/util/BlockingSocketReader.java b/services/net/java/android/net/util/BlockingSocketReader.java
new file mode 100644
index 0000000..12fa1e5
--- /dev/null
+++ b/services/net/java/android/net/util/BlockingSocketReader.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import android.annotation.Nullable;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import libcore.io.IoBridge;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+
+
+/**
+ * A thread that reads from a socket and passes the received packets to a
+ * subclass's handlePacket() method.  The packet receive buffer is recycled
+ * on every read call, so subclasses should make any copies they would like
+ * inside their handlePacket() implementation.
+ *
+ * All public methods may be called from any thread.
+ *
+ * @hide
+ */
+public abstract class BlockingSocketReader {
+    public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
+
+    private final byte[] mPacket;
+    private final Thread mThread;
+    private volatile FileDescriptor mSocket;
+    private volatile boolean mRunning;
+    private volatile long mPacketsReceived;
+
+    // Make it slightly easier for subclasses to properly close a socket
+    // without having to know this incantation.
+    public static final void closeSocket(@Nullable FileDescriptor fd) {
+        try {
+            IoBridge.closeAndSignalBlockedThreads(fd);
+        } catch (IOException ignored) {}
+    }
+
+    protected BlockingSocketReader() {
+        this(DEFAULT_RECV_BUF_SIZE);
+    }
+
+    protected BlockingSocketReader(int recvbufsize) {
+        if (recvbufsize < DEFAULT_RECV_BUF_SIZE) {
+            recvbufsize = DEFAULT_RECV_BUF_SIZE;
+        }
+        mPacket = new byte[recvbufsize];
+        mThread = new Thread(() -> { mainLoop(); });
+    }
+
+    public final boolean start() {
+        if (mSocket != null) return false;
+
+        try {
+            mSocket = createSocket();
+        } catch (Exception e) {
+            logError("Failed to create socket: ", e);
+            return false;
+        }
+
+        if (mSocket == null) return false;
+
+        mRunning = true;
+        mThread.start();
+        return true;
+    }
+
+    public final void stop() {
+        mRunning = false;
+        closeSocket(mSocket);
+        mSocket = null;
+    }
+
+    public final boolean isRunning() { return mRunning; }
+
+    public final long numPacketsReceived() { return mPacketsReceived; }
+
+    /**
+     * Subclasses MUST create the listening socket here, including setting
+     * all desired socket options, interface or address/port binding, etc.
+     */
+    protected abstract FileDescriptor createSocket();
+
+    /**
+     * Called by the main loop for every packet.  Any desired copies of
+     * |recvbuf| should be made in here, and the underlying byte array is
+     * reused across all reads.
+     */
+    protected void handlePacket(byte[] recvbuf, int length) {}
+
+    /**
+     * Called by the main loop to log errors.  In some cases |e| may be null.
+     */
+    protected void logError(String msg, Exception e) {}
+
+    /**
+     * Called by the main loop just prior to exiting.
+     */
+    protected void onExit() {}
+
+    private final void mainLoop() {
+        while (isRunning()) {
+            final int bytesRead;
+
+            try {
+                // Blocking read.
+                // TODO: See if this can be converted to recvfrom.
+                bytesRead = Os.read(mSocket, mPacket, 0, mPacket.length);
+                if (bytesRead < 1) {
+                    if (isRunning()) logError("Socket closed, exiting", null);
+                    break;
+                }
+                mPacketsReceived++;
+            } catch (ErrnoException e) {
+                if (e.errno != OsConstants.EINTR) {
+                    if (isRunning()) logError("read error: ", e);
+                    break;
+                }
+                continue;
+            } catch (IOException ioe) {
+                if (isRunning()) logError("read error: ", ioe);
+                continue;
+            }
+
+            try {
+                handlePacket(mPacket, bytesRead);
+            } catch (Exception e) {
+                logError("Unexpected exception: ", e);
+                break;
+            }
+        }
+
+        stop();
+        onExit();
+    }
+}
diff --git a/services/net/java/android/net/util/ConnectivityPacketSummary.java b/services/net/java/android/net/util/ConnectivityPacketSummary.java
new file mode 100644
index 0000000..699ba5b
--- /dev/null
+++ b/services/net/java/android/net/util/ConnectivityPacketSummary.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import android.net.dhcp.DhcpPacket;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.StringJoiner;
+
+import static android.system.OsConstants.*;
+import static android.net.util.NetworkConstants.*;
+
+
+/**
+ * Critical connectivity packet summarizing class.
+ *
+ * Outputs short descriptions of ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
+ *
+ * @hide
+ */
+public class ConnectivityPacketSummary {
+    private static final String TAG = ConnectivityPacketSummary.class.getSimpleName();
+
+    private final byte[] mHwAddr;
+    private final byte[] mBytes;
+    private final int mLength;
+    private final ByteBuffer mPacket;
+    private final String mSummary;
+
+    public static String summarize(byte[] hwaddr, byte[] buffer) {
+        return summarize(hwaddr, buffer, buffer.length);
+    }
+
+    // Methods called herein perform some but by no means all error checking.
+    // They may throw runtime exceptions on malformed packets.
+    public static String summarize(byte[] hwaddr, byte[] buffer, int length) {
+        if ((hwaddr == null) || (hwaddr.length != ETHER_ADDR_LEN)) return null;
+        if (buffer == null) return null;
+        length = Math.min(length, buffer.length);
+        return (new ConnectivityPacketSummary(hwaddr, buffer, length)).toString();
+    }
+
+    private ConnectivityPacketSummary(byte[] hwaddr, byte[] buffer, int length) {
+        mHwAddr = hwaddr;
+        mBytes = buffer;
+        mLength = Math.min(length, mBytes.length);
+        mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
+        mPacket.order(ByteOrder.BIG_ENDIAN);
+
+        final StringJoiner sj = new StringJoiner(" ");
+        // TODO: support other link-layers, or even no link-layer header.
+        parseEther(sj);
+        mSummary = sj.toString();
+    }
+
+    public String toString() {
+        return mSummary;
+    }
+
+    private void parseEther(StringJoiner sj) {
+        if (mPacket.remaining() < ETHER_HEADER_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        mPacket.position(ETHER_SRC_ADDR_OFFSET);
+        final ByteBuffer srcMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
+        sj.add(ByteBuffer.wrap(mHwAddr).equals(srcMac) ? "TX" : "RX");
+        sj.add(getMacAddressString(srcMac));
+
+        mPacket.position(ETHER_DST_ADDR_OFFSET);
+        final ByteBuffer dstMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
+        sj.add(">").add(getMacAddressString(dstMac));
+
+        mPacket.position(ETHER_TYPE_OFFSET);
+        final int etherType = asUint(mPacket.getShort());
+        switch (etherType) {
+            case ETHER_TYPE_ARP:
+                sj.add("arp");
+                parseARP(sj);
+                break;
+            case ETHER_TYPE_IPV4:
+                sj.add("ipv4");
+                parseIPv4(sj);
+                break;
+            case ETHER_TYPE_IPV6:
+                sj.add("ipv6");
+                parseIPv6(sj);
+                break;
+            default:
+                // Unknown ether type.
+                sj.add("ethtype").add(asString(etherType));
+                break;
+        }
+    }
+
+    private void parseARP(StringJoiner sj) {
+        if (mPacket.remaining() < ARP_PAYLOAD_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        if (asUint(mPacket.getShort()) != ARP_HWTYPE_ETHER ||
+            asUint(mPacket.getShort()) != ETHER_TYPE_IPV4 ||
+            asUint(mPacket.get()) != ETHER_ADDR_LEN ||
+            asUint(mPacket.get()) != IPV4_ADDR_LEN) {
+            sj.add("unexpected header");
+            return;
+        }
+
+        final int opCode = asUint(mPacket.getShort());
+
+        final String senderHwAddr = getMacAddressString(mPacket);
+        final String senderIPv4 = getIPv4AddressString(mPacket);
+        getMacAddressString(mPacket);  // target hardware address, unused
+        final String targetIPv4 = getIPv4AddressString(mPacket);
+
+        if (opCode == ARP_REQUEST) {
+            sj.add("who-has").add(targetIPv4);
+        } else if (opCode == ARP_REPLY) {
+            sj.add("reply").add(senderIPv4).add(senderHwAddr);
+        } else {
+            sj.add("unknown opcode").add(asString(opCode));
+        }
+    }
+
+    private void parseIPv4(StringJoiner sj) {
+        if (!mPacket.hasRemaining()) {
+            sj.add("runt");
+            return;
+        }
+
+        final int startOfIpLayer = mPacket.position();
+        final int ipv4HeaderLength = (mPacket.get(startOfIpLayer) & IPV4_IHL_MASK) * 4;
+        if (mPacket.remaining() < ipv4HeaderLength ||
+            mPacket.remaining() < IPV4_HEADER_MIN_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+        final int startOfTransportLayer = startOfIpLayer + ipv4HeaderLength;
+
+        mPacket.position(startOfIpLayer + IPV4_FLAGS_OFFSET);
+        final int flagsAndFragment = asUint(mPacket.getShort());
+        final boolean isFragment = (flagsAndFragment & IPV4_FRAGMENT_MASK) != 0;
+
+        mPacket.position(startOfIpLayer + IPV4_PROTOCOL_OFFSET);
+        final int protocol = asUint(mPacket.get());
+
+        mPacket.position(startOfIpLayer + IPV4_SRC_ADDR_OFFSET);
+        final String srcAddr = getIPv4AddressString(mPacket);
+
+        mPacket.position(startOfIpLayer + IPV4_DST_ADDR_OFFSET);
+        final String dstAddr = getIPv4AddressString(mPacket);
+
+        sj.add(srcAddr).add(">").add(dstAddr);
+
+        mPacket.position(startOfTransportLayer);
+        if (protocol == IPPROTO_UDP) {
+            sj.add("udp");
+            if (isFragment) sj.add("fragment");
+            else parseUDP(sj);
+        } else {
+            sj.add("proto").add(asString(protocol));
+            if (isFragment) sj.add("fragment");
+        }
+    }
+
+    private void parseIPv6(StringJoiner sj) {
+        if (mPacket.remaining() < IPV6_HEADER_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        final int startOfIpLayer = mPacket.position();
+
+        mPacket.position(startOfIpLayer + IPV6_PROTOCOL_OFFSET);
+        final int protocol = asUint(mPacket.get());
+
+        mPacket.position(startOfIpLayer + IPV6_SRC_ADDR_OFFSET);
+        final String srcAddr = getIPv6AddressString(mPacket);
+        final String dstAddr = getIPv6AddressString(mPacket);
+
+        sj.add(srcAddr).add(">").add(dstAddr);
+
+        mPacket.position(startOfIpLayer + IPV6_HEADER_LEN);
+        if (protocol == IPPROTO_ICMPV6) {
+            sj.add("icmp6");
+            parseICMPv6(sj);
+        } else {
+            sj.add("proto").add(asString(protocol));
+        }
+    }
+
+    private void parseICMPv6(StringJoiner sj) {
+        if (mPacket.remaining() < ICMPV6_HEADER_MIN_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        final int icmp6Type = asUint(mPacket.get());
+        final int icmp6Code = asUint(mPacket.get());
+        mPacket.getShort();  // checksum, unused
+
+        switch (icmp6Type) {
+            case ICMPV6_ROUTER_SOLICITATION:
+                sj.add("rs");
+                parseICMPv6RouterSolicitation(sj);
+                break;
+            case ICMPV6_ROUTER_ADVERTISEMENT:
+                sj.add("ra");
+                parseICMPv6RouterAdvertisement(sj);
+                break;
+            case ICMPV6_NEIGHBOR_SOLICITATION:
+                sj.add("ns");
+                parseICMPv6NeighborMessage(sj);
+                break;
+            case ICMPV6_NEIGHBOR_ADVERTISEMENT:
+                sj.add("na");
+                parseICMPv6NeighborMessage(sj);
+                break;
+            default:
+                sj.add("type").add(asString(icmp6Type));
+                sj.add("code").add(asString(icmp6Code));
+                break;
+        }
+    }
+
+    private void parseICMPv6RouterSolicitation(StringJoiner sj) {
+        final int RESERVED = 4;
+        if (mPacket.remaining() < RESERVED) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        mPacket.position(mPacket.position() + RESERVED);
+        parseICMPv6NeighborDiscoveryOptions(sj);
+    }
+
+    private void parseICMPv6RouterAdvertisement(StringJoiner sj) {
+        final int FLAGS_AND_TIMERS = 3 * 4;
+        if (mPacket.remaining() < FLAGS_AND_TIMERS) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        mPacket.position(mPacket.position() + FLAGS_AND_TIMERS);
+        parseICMPv6NeighborDiscoveryOptions(sj);
+    }
+
+    private void parseICMPv6NeighborMessage(StringJoiner sj) {
+        final int RESERVED = 4;
+        final int minReq = RESERVED + IPV6_ADDR_LEN;
+        if (mPacket.remaining() < minReq) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        mPacket.position(mPacket.position() + RESERVED);
+        sj.add(getIPv6AddressString(mPacket));
+        parseICMPv6NeighborDiscoveryOptions(sj);
+    }
+
+    private void parseICMPv6NeighborDiscoveryOptions(StringJoiner sj) {
+        // All ND options are TLV, where T is one byte and L is one byte equal
+        // to the length of T + L + V in units of 8 octets.
+        while (mPacket.remaining() >= ICMPV6_ND_OPTION_MIN_LENGTH) {
+            final int ndType = asUint(mPacket.get());
+            final int ndLength = asUint(mPacket.get());
+            final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2;
+            if (mPacket.remaining() < ndBytes) break;
+            final int position = mPacket.position();
+
+            switch (ndType) {
+                    case ICMPV6_ND_OPTION_SLLA:
+                        sj.add("slla");
+                        sj.add(getMacAddressString(mPacket));
+                        break;
+                    case ICMPV6_ND_OPTION_TLLA:
+                        sj.add("tlla");
+                        sj.add(getMacAddressString(mPacket));
+                        break;
+                    case ICMPV6_ND_OPTION_MTU:
+                        sj.add("mtu");
+                        final short reserved = mPacket.getShort();
+                        sj.add(asString(mPacket.getInt()));
+                        break;
+                    default:
+                        // Skip.
+                        break;
+            }
+
+            mPacket.position(position + ndBytes);
+        }
+    }
+
+    private void parseUDP(StringJoiner sj) {
+        if (mPacket.remaining() < UDP_HEADER_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        final int previous = mPacket.position();
+        final int srcPort = asUint(mPacket.getShort());
+        final int dstPort = asUint(mPacket.getShort());
+        sj.add(asString(srcPort)).add(">").add(asString(dstPort));
+
+        mPacket.position(previous + UDP_HEADER_LEN);
+        if (srcPort == DHCP4_CLIENT_PORT || dstPort == DHCP4_CLIENT_PORT) {
+            sj.add("dhcp4");
+            parseDHCPv4(sj);
+        }
+    }
+
+    private void parseDHCPv4(StringJoiner sj) {
+        final DhcpPacket dhcpPacket;
+        try {
+            dhcpPacket = DhcpPacket.decodeFullPacket(mBytes, mLength, DhcpPacket.ENCAP_L2);
+            sj.add(dhcpPacket.toString());
+        } catch (DhcpPacket.ParseException e) {
+            sj.add("parse error: " + e);
+        }
+    }
+
+    private static String getIPv4AddressString(ByteBuffer ipv4) {
+        return getIpAddressString(ipv4, IPV4_ADDR_LEN);
+    }
+
+    private static String getIPv6AddressString(ByteBuffer ipv6) {
+        return getIpAddressString(ipv6, IPV6_ADDR_LEN);
+    }
+
+    private static String getIpAddressString(ByteBuffer ip, int byteLength) {
+        if (ip == null || ip.remaining() < byteLength) return "invalid";
+
+        byte[] bytes = new byte[byteLength];
+        ip.get(bytes, 0, byteLength);
+        try {
+            InetAddress addr = InetAddress.getByAddress(bytes);
+            return addr.getHostAddress();
+        } catch (UnknownHostException uhe) {
+            return "unknown";
+        }
+    }
+
+    private static String getMacAddressString(ByteBuffer mac) {
+        if (mac == null || mac.remaining() < ETHER_ADDR_LEN) return "invalid";
+
+        byte[] bytes = new byte[ETHER_ADDR_LEN];
+        mac.get(bytes, 0, bytes.length);
+        Byte[] printableBytes = new Byte[bytes.length];
+        int i = 0;
+        for (byte b : bytes) printableBytes[i++] = b;
+
+        final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x";
+        return String.format(MAC48_FORMAT, printableBytes);
+    }
+}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
new file mode 100644
index 0000000..362f757
--- /dev/null
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import java.nio.ByteBuffer;
+
+
+/**
+ * Networking protocol constants.
+ *
+ * Includes:
+ *     - constants that describe packet layout
+ *     - various helper functions
+ *
+ * @hide
+ */
+public final class NetworkConstants {
+    private NetworkConstants() { throw new RuntimeException("no instance permitted"); }
+
+    /**
+     * Ethernet constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc894
+     *     - https://tools.ietf.org/html/rfc7042
+     *     - http://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml
+     *     - http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
+     */
+    public static final int ETHER_DST_ADDR_OFFSET = 0;
+    public static final int ETHER_SRC_ADDR_OFFSET = 6;
+    public static final int ETHER_ADDR_LEN = 6;
+
+    public static final int ETHER_TYPE_OFFSET = 12;
+    public static final int ETHER_TYPE_LENGTH = 2;
+    public static final int ETHER_TYPE_ARP  = 0x0806;
+    public static final int ETHER_TYPE_IPV4 = 0x0800;
+    public static final int ETHER_TYPE_IPV6 = 0x86dd;
+
+    public static final int ETHER_HEADER_LEN = 14;
+
+    private static final byte FF = asByte(0xff);
+    public static final byte[] ETHER_ADDR_BROADCAST = {
+        FF, FF, FF, FF, FF, FF
+    };
+
+    /**
+     * ARP constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc826
+     *     - http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
+     */
+    public static final int ARP_PAYLOAD_LEN = 28;  // For Ethernet+IPv4.
+    public static final int ARP_REQUEST = 1;
+    public static final int ARP_REPLY   = 2;
+    public static final int ARP_HWTYPE_RESERVED_LO = 0;
+    public static final int ARP_HWTYPE_ETHER       = 1;
+    public static final int ARP_HWTYPE_RESERVED_HI = 0xffff;
+
+    /**
+     * IPv4 constants.
+     *
+     * See als:
+     *     - https://tools.ietf.org/html/rfc791
+     */
+    public static final int IPV4_HEADER_MIN_LEN = 20;
+    public static final int IPV4_IHL_MASK = 0xf;
+    public static final int IPV4_FLAGS_OFFSET = 6;
+    public static final int IPV4_FRAGMENT_MASK = 0x1fff;
+    public static final int IPV4_PROTOCOL_OFFSET = 9;
+    public static final int IPV4_SRC_ADDR_OFFSET = 12;
+    public static final int IPV4_DST_ADDR_OFFSET = 16;
+    public static final int IPV4_ADDR_LEN = 4;
+
+    /**
+     * IPv6 constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc2460
+     */
+    public static final int IPV6_HEADER_LEN = 40;
+    public static final int IPV6_PROTOCOL_OFFSET = 6;
+    public static final int IPV6_SRC_ADDR_OFFSET = 8;
+    public static final int IPV6_DST_ADDR_OFFSET = 24;
+    public static final int IPV6_ADDR_LEN = 16;
+
+    /**
+     * ICMPv6 constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc4443
+     *     - https://tools.ietf.org/html/rfc4861
+     */
+    public static final int ICMPV6_HEADER_MIN_LEN = 4;
+    public static final int ICMPV6_ROUTER_SOLICITATION    = 133;
+    public static final int ICMPV6_ROUTER_ADVERTISEMENT   = 134;
+    public static final int ICMPV6_NEIGHBOR_SOLICITATION  = 135;
+    public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
+
+    public static final int ICMPV6_ND_OPTION_MIN_LENGTH = 8;
+    public static final int ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR = 8;
+    public static final int ICMPV6_ND_OPTION_SLLA = 1;
+    public static final int ICMPV6_ND_OPTION_TLLA = 2;
+    public static final int ICMPV6_ND_OPTION_MTU  = 5;
+
+    /**
+     * UDP constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc768
+     */
+    public static final int UDP_HEADER_LEN = 8;
+
+    /**
+     * DHCP(v4) constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc2131
+     */
+    public static final int DHCP4_SERVER_PORT = 67;
+    public static final int DHCP4_CLIENT_PORT = 68;
+
+    /**
+     * Utility functions.
+     */
+    public static byte asByte(int i) { return (byte) i; }
+
+    public static String asString(int i) { return Integer.toString(i); }
+
+    public static int asUint(byte b) { return (b & 0xff); }
+    public static int asUint(short s) { return (s & 0xffff); }
+}
diff --git a/services/tests/servicestests/src/android/net/util/BlockingSocketReaderTest.java b/services/tests/servicestests/src/android/net/util/BlockingSocketReaderTest.java
new file mode 100644
index 0000000..e03350f
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/util/BlockingSocketReaderTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import static android.system.OsConstants.*;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
+
+import libcore.io.IoBridge;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests for BlockingSocketReader.
+ *
+ * @hide
+ */
+public class BlockingSocketReaderTest extends TestCase {
+    static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
+    static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
+
+    protected CountDownLatch mLatch;
+    protected FileDescriptor mLocalSocket;
+    protected InetSocketAddress mLocalSockName;
+    protected byte[] mLastRecvBuf;
+    protected boolean mExited;
+    protected BlockingSocketReader mReceiver;
+
+    @Override
+    public void setUp() {
+        resetLatch();
+        mLocalSocket = null;
+        mLocalSockName = null;
+        mLastRecvBuf = null;
+        mExited = false;
+
+        mReceiver = new BlockingSocketReader() {
+            @Override
+            protected FileDescriptor createSocket() {
+                FileDescriptor s = null;
+                try {
+                    s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+                    Os.bind(s, LOOPBACK6, 0);
+                    mLocalSockName = (InetSocketAddress) Os.getsockname(s);
+                    Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
+                } catch (ErrnoException|SocketException e) {
+                    closeSocket(s);
+                    fail();
+                    return null;
+                }
+
+                mLocalSocket = s;
+                return s;
+            }
+
+            @Override
+            protected void handlePacket(byte[] recvbuf, int length) {
+                mLastRecvBuf = Arrays.copyOf(recvbuf, length);
+                mLatch.countDown();
+            }
+
+            @Override
+            protected void onExit() {
+                mExited = true;
+                mLatch.countDown();
+            }
+        };
+    }
+
+    @Override
+    public void tearDown() {
+        if (mReceiver != null) mReceiver.stop();
+        mReceiver = null;
+    }
+
+    void resetLatch() { mLatch = new CountDownLatch(1); }
+
+    void waitForActivity() throws Exception {
+        assertTrue(mLatch.await(500, TimeUnit.MILLISECONDS));
+        resetLatch();
+    }
+
+    void sendPacket(byte[] contents) throws Exception {
+        final DatagramSocket sender = new DatagramSocket();
+        sender.connect(mLocalSockName);
+        sender.send(new DatagramPacket(contents, contents.length));
+        sender.close();
+    }
+
+    public void testBasicWorking() throws Exception {
+        assertTrue(mReceiver.start());
+        assertTrue(mLocalSockName != null);
+        assertEquals(LOOPBACK6, mLocalSockName.getAddress());
+        assertTrue(0 < mLocalSockName.getPort());
+        assertTrue(mLocalSocket != null);
+        assertFalse(mExited);
+
+        final byte[] one = "one 1".getBytes("UTF-8");
+        sendPacket(one);
+        waitForActivity();
+        assertEquals(1, mReceiver.numPacketsReceived());
+        assertTrue(Arrays.equals(one, mLastRecvBuf));
+        assertFalse(mExited);
+
+        final byte[] two = "two 2".getBytes("UTF-8");
+        sendPacket(two);
+        waitForActivity();
+        assertEquals(2, mReceiver.numPacketsReceived());
+        assertTrue(Arrays.equals(two, mLastRecvBuf));
+        assertFalse(mExited);
+
+        mReceiver.stop();
+        waitForActivity();
+        assertEquals(2, mReceiver.numPacketsReceived());
+        assertTrue(Arrays.equals(two, mLastRecvBuf));
+        assertTrue(mExited);
+    }
+}
diff --git a/services/tests/servicestests/src/android/net/util/ConnectivityPacketSummaryTest.java b/services/tests/servicestests/src/android/net/util/ConnectivityPacketSummaryTest.java
new file mode 100644
index 0000000..766e5c0
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/util/ConnectivityPacketSummaryTest.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2016 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.util;
+
+import static android.net.util.NetworkConstants.*;
+
+import libcore.util.HexEncoding;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests for ConnectivityPacketSummary.
+ *
+ * @hide
+ */
+public class ConnectivityPacketSummaryTest extends TestCase {
+    private static final byte[] MYHWADDR = {
+        asByte(0x80), asByte(0x7a), asByte(0xbf), asByte(0x6f), asByte(0x48), asByte(0xf3)
+    };
+
+    private String getSummary(String hexBytes) {
+        hexBytes = hexBytes.replaceAll("\\s+", "");
+        final byte[] bytes = HexEncoding.decode(hexBytes.toCharArray(), false);
+        return ConnectivityPacketSummary.summarize(MYHWADDR, bytes);
+    }
+
+    public void testParseICMPv6DADProbe() {
+        final String packet =
+                // Ethernet
+                "3333FF6F48F3 807ABF6F48F3 86DD" +
+                // IPv6
+                "600000000018 3A FF" +
+                "00000000000000000000000000000000" +
+                "FF0200000000000000000001FF6F48F3" +
+                // ICMPv6
+                "87 00 A8E7" +
+                "00000000" +
+                "FE80000000000000827ABFFFFE6F48F3";
+
+        final String expected =
+                "TX 80:7a:bf:6f:48:f3 > 33:33:ff:6f:48:f3 ipv6" +
+                " :: > ff02::1:ff6f:48f3 icmp6" +
+                " ns fe80::827a:bfff:fe6f:48f3";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseICMPv6RS() {
+        final String packet =
+                // Ethernet
+                "333300000002 807ABF6F48F3 86DD" +
+                // IPv6
+                "600000000010 3A FF" +
+                "FE80000000000000827ABFFFFE6F48F3" +
+                "FF020000000000000000000000000002" +
+                // ICMPv6 RS
+                "85 00 6973" +
+                "00000000" +
+                "01 01 807ABF6F48F3";
+
+        final String expected =
+                "TX 80:7a:bf:6f:48:f3 > 33:33:00:00:00:02 ipv6" +
+                " fe80::827a:bfff:fe6f:48f3 > ff02::2 icmp6" +
+                " rs slla 80:7a:bf:6f:48:f3";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseICMPv6RA() {
+        final String packet =
+                // Ethernet
+                "807ABF6F48F3 100E7E263FC1 86DD" +
+                // IPv6
+                "600000000068 3A FF" +
+                "FE80000000000000FA000004FD000001" +
+                "FE80000000000000827ABFFFFE6F48F3" +
+                // ICMPv6 RA
+                "86 00 8141" +
+                "40 00 0E10" +
+                "00000000" +
+                "00000000" +
+                "01 01 00005E000265" +
+                "05 01 0000000005DC" +
+                "19 05 000000000E10" +
+                "      20014860486000000000000000008844" +
+                "      20014860486000000000000000008888" +
+                "03 04 40 C0" +
+                "      00278D00" +
+                "      00093A80" +
+                "      00000000" +
+                "      2401FA000004FD000000000000000000";
+
+        final String expected =
+                "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
+                " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" +
+                " ra slla 00:00:5e:00:02:65 mtu 1500";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseICMPv6NS() {
+        final String packet =
+                // Ethernet
+                  "807ABF6F48F3 100E7E263FC1 86DD" +
+                  // IPv6
+                  "6C0000000020 3A FF" +
+                  "FE80000000000000FA000004FD000001" +
+                  "FF0200000000000000000001FF01C146" +
+                  // ICMPv6 NS
+                  "87 00 8AD4" +
+                  "00000000" +
+                  "2401FA000004FD0015EA6A5C7B01C146" +
+                  "01 01 00005E000265";
+
+        final String expected =
+                "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
+                " fe80::fa00:4:fd00:1 > ff02::1:ff01:c146 icmp6" +
+                " ns 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 slla 00:00:5e:00:02:65";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseICMPv6NA() {
+        final String packet =
+                // Ethernet
+                "00005E000265 807ABF6F48F3 86DD" +
+                "600000000020 3A FF" +
+                "2401FA000004FD0015EA6A5C7B01C146" +
+                "FE80000000000000FA000004FD000001" +
+                "88 00 E8126" +
+                "0000000" +
+                "2401FA000004FD0015EA6A5C7B01C146" +
+                "02 01 807ABF6F48F3";
+
+        final String expected =
+                "TX 80:7a:bf:6f:48:f3 > 00:00:5e:00:02:65 ipv6" +
+                " 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 > fe80::fa00:4:fd00:1 icmp6" +
+                " na 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 tlla 80:7a:bf:6f:48:f3";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseARPRequest() {
+        final String packet =
+                // Ethernet
+                  "FFFFFFFFFFFF 807ABF6F48F3 0806" +
+                  // ARP
+                  "0001 0800 06 04" +
+                  // Request
+                  "0001" +
+                  "807ABF6F48F3 64706ADB" +
+                  "000000000000 64706FFD";
+
+        final String expected =
+                "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff arp" +
+                " who-has 100.112.111.253";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseARPReply() {
+        final String packet =
+                // Ethernet
+                  "807ABF6F48F3 288A1CA8DFC1 0806" +
+                  // ARP
+                  "0001 0800 06 04" +
+                  // Reply
+                  "0002" +
+                  "288A1CA8DFC1 64706FFD"+
+                  "807ABF6F48F3 64706ADB" +
+                  // Ethernet padding to packet min size.
+                  "0000000000000000000000000000";
+
+        final String expected =
+                "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 arp" +
+                " reply 100.112.111.253 28:8a:1c:a8:df:c1";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseDHCPv4Discover() {
+        final String packet =
+                // Ethernet
+                "FFFFFFFFFFFF 807ABF6F48F3 0800" +
+                // IPv4
+                "451001580000400040113986" +
+                "00000000" +
+                "FFFFFFFF" +
+                // UDP
+                "0044 0043" +
+                "0144 5559" +
+                // DHCPv4
+                "01 01 06 00" +
+                "79F7ACA4" +
+                "0000 0000" +
+                "00000000" +
+                "00000000" +
+                "00000000" +
+                "00000000" +
+                "807ABF6F48F300000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "63 82 53 63" +
+                "35 01 01" +
+                "3D 07 01807ABF6F48F3" +
+                "39 02 05DC" +
+                "3C 12 616E64726F69642D646863702D372E312E32" +
+                "0C 18 616E64726F69642D36623030366333313333393835343139" +
+                "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" +
+                "FF" +
+                "00";
+
+        final String expectedPrefix =
+                "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" +
+                " 0.0.0.0 > 255.255.255.255 udp" +
+                " 68 > 67 dhcp4" +
+                " 80:7a:bf:6f:48:f3 DISCOVER";
+
+        assertTrue(getSummary(packet).startsWith(expectedPrefix));
+    }
+
+    public void testParseDHCPv4Offer() {
+        final String packet =
+                // Ethernet
+                "807ABF6F48F3 288A1CA8DFC1 0800" +
+                // IPv4
+                "4500013D4D2C0000401188CB" +
+                "64706FFD" +
+                "64706ADB" +
+                // UDP
+                "0043 0044" +
+                "0129 371D" +
+                // DHCPv4
+                "02 01 06 01" +
+                "79F7ACA4" +
+                "0000 0000" +
+                "00000000" +
+                "64706ADB" +
+                "00000000" +
+                "00000000" +
+                "807ABF6F48F300000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "63 82 53 63" +
+                "35 01 02" +
+                "36 04 AC188A0B" +
+                "33 04 00000708" +
+                "01 04 FFFFF000" +
+                "03 04 64706FFE" +
+                "06 08 08080808" +
+                "      08080404" +
+                "FF0001076165313A363636FF";
+
+        final String expectedPrefix =
+                "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" +
+                " 100.112.111.253 > 100.112.106.219 udp" +
+                " 67 > 68 dhcp4" +
+                " 80:7a:bf:6f:48:f3 OFFER";
+
+        assertTrue(getSummary(packet).startsWith(expectedPrefix));
+    }
+
+    public void testParseDHCPv4Request() {
+        final String packet =
+                // Ethernet
+                "FFFFFFFFFFFF 807ABF6F48F3 0800" +
+                // IPv4
+                "45100164000040004011397A" +
+                "00000000" +
+                "FFFFFFFF" +
+                // UDP
+                "0044 0043" +
+                "0150 E5C7" +
+                // DHCPv4
+                "01 01 06 00" +
+                "79F7ACA4" +
+                "0001 0000" +
+                "00000000" +
+                "00000000" +
+                "00000000" +
+                "00000000" +
+                "807ABF6F48F300000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "63 82 53 63" +
+                "35 01 03" +
+                "3D 07 01807ABF6F48F3" +
+                "32 04 64706ADB" +
+                "36 04 AC188A0B" +
+                "39 02 05DC" +
+                "3C 12 616E64726F69642D646863702D372E312E32" +
+                "0C 18 616E64726F69642D36623030366333313333393835343139" +
+                "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" +
+                "FF" +
+                "00";
+
+        final String expectedPrefix =
+                "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" +
+                " 0.0.0.0 > 255.255.255.255 udp" +
+                " 68 > 67 dhcp4" +
+                " 80:7a:bf:6f:48:f3 REQUEST";
+
+        assertTrue(getSummary(packet).startsWith(expectedPrefix));
+    }
+
+    public void testParseDHCPv4Ack() {
+        final String packet =
+                // Ethernet
+                "807ABF6F48F3 288A1CA8DFC1 0800" +
+                // IPv4
+                "4500013D4D3B0000401188BC" +
+                "64706FFD" +
+                "64706ADB" +
+                // UDP
+                "0043 0044" +
+                "0129 341C" +
+                // DHCPv4
+                "02 01 06 01" +
+                "79F7ACA4" +
+                "0001 0000" +
+                "00000000" +
+                "64706ADB" +
+                "00000000" +
+                "00000000" +
+                "807ABF6F48F300000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "63 82 53 63" +
+                "35 01 05" +
+                "36 04 AC188A0B" +
+                "33 04 00000708" +
+                "01 04 FFFFF000" +
+                "03 04 64706FFE" +
+                "06 08 08080808" +
+                "      08080404" +
+                "FF0001076165313A363636FF";
+
+        final String expectedPrefix =
+                "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" +
+                " 100.112.111.253 > 100.112.106.219 udp" +
+                " 67 > 68 dhcp4" +
+                " 80:7a:bf:6f:48:f3 ACK";
+
+        assertTrue(getSummary(packet).startsWith(expectedPrefix));
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index eb5b288..a240bb1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -599,6 +599,15 @@
     public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
 
     /**
+     * Determines whether High Definition audio property is displayed in the dialer UI.
+     * If {@code false}, remove the HD audio property from the connection so that HD audio related
+     * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
+     * @hide
+     */
+    public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL =
+            "display_hd_audio_property_bool";
+
+    /**
      * Determines whether video conference calls are supported by a carrier.  When {@code true},
      * video calls can be merged into conference calls, {@code false} otherwiwse.
      * <p>
@@ -1027,6 +1036,17 @@
     public static final String KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT =
             "network_notification_delay_int";
 
+    /**
+     * Determine whether user edited tether APN (type dun) has effect
+     * {@code false} - Default. APN with dun type in telephony database has no effect.
+     *
+     * {@code true}  - DUN APN added/edited in ApnEditor will be used for tethering data call.
+     *
+     * @hide
+     */
+    public static final String KEY_EDITABLE_TETHER_APN_BOOL =
+            "editable_tether_apn_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1130,6 +1150,7 @@
         sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);
         sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
         sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
@@ -1207,6 +1228,7 @@
         sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
         sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
         sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
+        sDefaults.putBoolean(KEY_EDITABLE_TETHER_APN_BOOL, false);
     }
 
     /**