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 <receiver>}
* tag in your <code>AndroidManifest.xml</code>.
- *
- * <p><em><strong>Note:</strong></em>
- * 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 <receiver>}
- * 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 <uses-permission>}
- * 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 <receiver>}
- * 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 <uses-permission>}
- * 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 <receiver> 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);
}
/**