Merge "Modify the wake reasons data structure(2/2)" into mm-wireless-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index a03f328..1de5a9d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -20815,6 +20815,7 @@
field public int numberBurst;
field public int preamble;
field public int requestType;
+ field public boolean secure;
}
public static class RttManager.RttResult {
@@ -20846,6 +20847,7 @@
field public deprecated long rtt_sd_ns;
field public deprecated long rtt_spread_ns;
field public int rxRate;
+ field public boolean secure;
field public int status;
field public int successMeasurementFrameNumber;
field public long ts;
@@ -21209,6 +21211,7 @@
method public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000
field public static final int MIN_SCAN_PERIOD_MS = 1000; // 0x3e8
+ field public static final int REASON_DUPLICATE_REQEUST = -5; // 0xfffffffb
field public static final int REASON_INVALID_LISTENER = -2; // 0xfffffffe
field public static final int REASON_INVALID_REQUEST = -3; // 0xfffffffd
field public static final int REASON_NOT_AUTHORIZED = -4; // 0xfffffffc
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 4487cab..c6d919f 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -57,90 +57,6 @@
public native static int resetConnections(String interfaceName, int mask);
/**
- * Start the DHCP client daemon, in order to have it request addresses
- * for the named interface. This returns {@code true} if the DHCPv4 daemon
- * starts, {@code false} otherwise. This call blocks until such time as a
- * result is available or the default discovery timeout has been reached.
- * Callers should check {@link #getDhcpResults} to determine whether DHCP
- * succeeded or failed, and if it succeeded, to fetch the {@link DhcpResults}.
- * @param interfaceName the name of the interface to configure
- * @return {@code true} for success, {@code false} for failure
- */
- public native static boolean startDhcp(String interfaceName);
-
- /**
- * Initiate renewal on the DHCP client daemon for the named interface. This
- * returns {@code true} if the DHCPv4 daemon has been notified, {@code false}
- * otherwise. This call blocks until such time as a result is available or
- * the default renew timeout has been reached. Callers should check
- * {@link #getDhcpResults} to determine whether DHCP succeeded or failed,
- * and if it succeeded, to fetch the {@link DhcpResults}.
- * @param interfaceName the name of the interface to configure
- * @return {@code true} for success, {@code false} for failure
- */
- public native static boolean startDhcpRenew(String interfaceName);
-
- /**
- * Start the DHCP client daemon, in order to have it request addresses
- * for the named interface, and then configure the interface with those
- * addresses. This call blocks until it obtains a result (either success
- * or failure) from the daemon.
- * @param interfaceName the name of the interface to configure
- * @param dhcpResults if the request succeeds, this object is filled in with
- * the IP address information.
- * @return {@code true} for success, {@code false} for failure
- */
- public static boolean runDhcp(String interfaceName, DhcpResults dhcpResults) {
- return startDhcp(interfaceName) && getDhcpResults(interfaceName, dhcpResults);
- }
-
- /**
- * Initiate renewal on the DHCP client daemon. This call blocks until it obtains
- * a result (either success or failure) from the daemon.
- * @param interfaceName the name of the interface to configure
- * @param dhcpResults if the request succeeds, this object is filled in with
- * the IP address information.
- * @return {@code true} for success, {@code false} for failure
- */
- public static boolean runDhcpRenew(String interfaceName, DhcpResults dhcpResults) {
- return startDhcpRenew(interfaceName) && getDhcpResults(interfaceName, dhcpResults);
- }
-
- /**
- * Fetch results from the DHCP client daemon. This call returns {@code true} if
- * if there are results available to be read, {@code false} otherwise.
- * @param interfaceName the name of the interface to configure
- * @param dhcpResults if the request succeeds, this object is filled in with
- * the IP address information.
- * @return {@code true} for success, {@code false} for failure
- */
- public native static boolean getDhcpResults(String interfaceName, DhcpResults dhcpResults);
-
- /**
- * Shut down the DHCP client daemon.
- * @param interfaceName the name of the interface for which the daemon
- * should be stopped
- * @return {@code true} for success, {@code false} for failure
- */
- public native static boolean stopDhcp(String interfaceName);
-
- /**
- * Release the current DHCP lease.
- * @param interfaceName the name of the interface for which the lease should
- * be released
- * @return {@code true} for success, {@code false} for failure
- */
- public native static boolean releaseDhcpLease(String interfaceName);
-
- /**
- * Return the last DHCP-related error message that was recorded.
- * <p/>NOTE: This string is not localized, but currently it is only
- * used in logging.
- * @return the most recent error message, if any
- */
- public native static String getDhcpError();
-
- /**
* Attaches a socket filter that accepts DHCP packets to the given socket.
*/
public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException;
diff --git a/core/java/com/android/internal/util/MessageUtils.java b/core/java/com/android/internal/util/MessageUtils.java
new file mode 100644
index 0000000..184245e
--- /dev/null
+++ b/core/java/com/android/internal/util/MessageUtils.java
@@ -0,0 +1,132 @@
+/*
+ * 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 com.android.internal.util;
+
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * Static utility class for dealing with {@link Message} objects.
+ */
+public class MessageUtils {
+
+ private static final String TAG = MessageUtils.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ /** Thrown when two different constants have the same value. */
+ public static class DuplicateConstantError extends Error {
+ private DuplicateConstantError() {}
+ public DuplicateConstantError(String name1, String name2, int value) {
+ super(String.format("Duplicate constant value: both %s and %s = %d",
+ name1, name2, value));
+ }
+ }
+
+ /**
+ * Finds the names of integer constants. Searches the specified {@code classes}, looking for
+ * accessible static integer fields whose names begin with one of the specified {@prefixes}.
+ *
+ * @param classes the classes to examine.
+ * @prefixes only consider fields names starting with one of these prefixes.
+ * @return a {@link SparseArray} mapping integer constants to their names.
+ */
+ public static SparseArray<String> findMessageNames(Class[] classes, String[] prefixes) {
+ SparseArray<String> messageNames = new SparseArray<>();
+ for (Class c : classes) {
+ String className = c.getName();
+ if (DBG) Log.d(TAG, "Examining class " + className);
+
+ Field[] fields;
+ try {
+ fields = c.getDeclaredFields();
+ } catch (SecurityException e) {
+ Log.e(TAG, "Can't list fields of class " + className);
+ continue;
+ }
+
+ for (Field field : fields) {
+ int modifiers = field.getModifiers();
+ if (!Modifier.isStatic(modifiers) | !Modifier.isFinal(modifiers)) {
+ continue;
+ }
+
+ String name = field.getName();
+ for (String prefix : prefixes) {
+ // Does this look like a constant?
+ if (!name.startsWith(prefix)) {
+ continue;
+ }
+
+ try {
+ // TODO: can we have the caller try to access the field instead, so we don't
+ // expose constants it does not have access to?
+ field.setAccessible(true);
+
+ // Fetch the constant's value.
+ int value;
+ try {
+ value = field.getInt(null);
+ } catch (IllegalArgumentException | ExceptionInInitializerError e) {
+ // The field is not an integer (or short or byte), or c's static
+ // initializer failed and we have no idea what its value is.
+ // Either way, give up on this field.
+ break;
+ }
+
+ // Check for duplicate values.
+ String previousName = messageNames.get(value);
+ if (previousName != null && !previousName.equals(name)) {
+ throw new DuplicateConstantError(name, previousName, value);
+ }
+
+ messageNames.put(value, name);
+ if (DBG) {
+ Log.d(TAG, String.format("Found constant: %s.%s = %d",
+ className, name, value));
+ }
+ } catch (SecurityException | IllegalAccessException e) {
+ // Not allowed to make the field accessible, or no access. Ignore.
+ continue;
+ }
+ }
+ }
+ }
+ return messageNames;
+ }
+
+ /**
+ * Default prefixes for constants.
+ */
+ public static final String[] DEFAULT_PREFIXES = {"CMD_", "EVENT_"};
+
+ /**
+ * Finds the names of integer constants. Searches the specified {@code classes}, looking for
+ * accessible static integer values whose names begin with {@link #DEFAULT_PREFIXES}.
+ *
+ * @param classNames the classes to examine.
+ * @prefixes only consider fields names starting with one of these prefixes.
+ * @return a {@link SparseArray} mapping integer constants to their names.
+ */
+ public static SparseArray<String> findMessageNames(Class[] classNames) {
+ return findMessageNames(classNames, DEFAULT_PREFIXES);
+ }
+}
+
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
new file mode 100644
index 0000000..77859b8
--- /dev/null
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+
+ /**
+ * An AlarmListener that sends the specified message to a Handler and keeps the system awake until
+ * the message is processed.
+ *
+ * This is useful when using the AlarmManager direct callback interface to wake up the system and
+ * request that an object whose API consists of messages (such as a StateMachine) perform some
+ * action.
+ *
+ * In this situation, using AlarmManager.onAlarmListener by itself will wake up the system to send
+ * the message, but does not guarantee that the system will be awake until the target object has
+ * processed it. This is because as soon as the onAlarmListener sends the message and returns, the
+ * AlarmManager releases its wakelock and the system is free to go to sleep again.
+ *
+ */
+public class WakeupMessage implements AlarmManager.OnAlarmListener {
+ private static AlarmManager sAlarmManager;
+ private final Handler mHandler;
+ private final String mCmdName;
+ private final int mCmd, mArg1, mArg2;
+
+ public WakeupMessage(Context context, Handler handler,
+ String cmdName, int cmd, int arg1, int arg2) {
+ if (sAlarmManager == null) {
+ sAlarmManager = context.getSystemService(AlarmManager.class);
+ }
+ mHandler = handler;
+ mCmdName = cmdName;
+ mCmd = cmd;
+ mArg1 = arg1;
+ mArg2 = arg2;
+ }
+
+ public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) {
+ this(context, handler, cmdName, cmd, arg1, 0);
+ }
+
+ public WakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
+ this(context, handler, cmdName, cmd, 0, 0);
+ }
+
+ public void schedule(long when) {
+ sAlarmManager.setExact(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP, when, mCmdName, this, mHandler);
+ }
+
+ public void cancel() {
+ sAlarmManager.cancel(this);
+ }
+
+ @Override
+ public void onAlarm() {
+ Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
+ mHandler.handleMessage(msg);
+ msg.recycle();
+ }
+}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index ba0876d..defb88a 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -39,23 +39,6 @@
int ifc_enable(const char *ifname);
int ifc_disable(const char *ifname);
int ifc_reset_connections(const char *ifname, int reset_mask);
-
-int dhcp_start(const char * const ifname);
-int dhcp_start_renew(const char * const ifname);
-int dhcp_get_results(const char * const ifname,
- const char *ipaddr,
- const char *gateway,
- uint32_t *prefixLength,
- const char *dns[],
- const char *server,
- uint32_t *lease,
- const char *vendorInfo,
- const char *domains,
- const char *mtu);
-
-int dhcp_stop(const char *ifname);
-int dhcp_release_lease(const char *ifname);
-char *dhcp_get_errmsg();
}
#define NETUTILS_PKG_NAME "android/net/NetworkUtils"
@@ -64,22 +47,6 @@
static const uint16_t kDhcpClientPort = 68;
-/*
- * The following remembers the jfieldID's of the fields
- * of the DhcpInfo Java object, so that we don't have
- * to look them up every time.
- */
-static struct fieldIds {
- jmethodID clear;
- jmethodID setIpAddress;
- jmethodID setGateway;
- jmethodID addDns;
- jmethodID setDomains;
- jmethodID setServerAddress;
- jmethodID setLeaseDuration;
- jmethodID setVendorInfo;
-} dhcpResultsFieldIds;
-
static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
jstring ifname, jint mask)
{
@@ -95,137 +62,6 @@
return (jint)result;
}
-static jboolean android_net_utils_getDhcpResults(JNIEnv* env, jobject clazz, jstring ifname,
- jobject dhcpResults)
-{
- int result;
- char ipaddr[PROPERTY_VALUE_MAX];
- uint32_t prefixLength;
- char gateway[PROPERTY_VALUE_MAX];
- char dns1[PROPERTY_VALUE_MAX];
- char dns2[PROPERTY_VALUE_MAX];
- char dns3[PROPERTY_VALUE_MAX];
- char dns4[PROPERTY_VALUE_MAX];
- const char *dns[5] = {dns1, dns2, dns3, dns4, NULL};
- char server[PROPERTY_VALUE_MAX];
- uint32_t lease;
- char vendorInfo[PROPERTY_VALUE_MAX];
- char domains[PROPERTY_VALUE_MAX];
- char mtu[PROPERTY_VALUE_MAX];
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- if (nameStr == NULL) return (jboolean)false;
-
- result = ::dhcp_get_results(nameStr, ipaddr, gateway, &prefixLength,
- dns, server, &lease, vendorInfo, domains, mtu);
- if (result != 0) {
- ALOGD("dhcp_get_results failed : %s (%s)", nameStr, ::dhcp_get_errmsg());
- }
-
- env->ReleaseStringUTFChars(ifname, nameStr);
- if (result == 0) {
- env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.clear);
-
- // set the linkAddress
- // dhcpResults->addLinkAddress(inetAddress, prefixLength)
- result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.setIpAddress,
- env->NewStringUTF(ipaddr), prefixLength);
- }
-
- if (result == 0) {
- // set the gateway
- result = env->CallBooleanMethod(dhcpResults,
- dhcpResultsFieldIds.setGateway, env->NewStringUTF(gateway));
- }
-
- if (result == 0) {
- // dhcpResults->addDns(new InetAddress(dns1))
- result = env->CallBooleanMethod(dhcpResults,
- dhcpResultsFieldIds.addDns, env->NewStringUTF(dns1));
- }
-
- if (result == 0) {
- env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setDomains,
- env->NewStringUTF(domains));
-
- result = env->CallBooleanMethod(dhcpResults,
- dhcpResultsFieldIds.addDns, env->NewStringUTF(dns2));
-
- if (result == 0) {
- result = env->CallBooleanMethod(dhcpResults,
- dhcpResultsFieldIds.addDns, env->NewStringUTF(dns3));
- if (result == 0) {
- result = env->CallBooleanMethod(dhcpResults,
- dhcpResultsFieldIds.addDns, env->NewStringUTF(dns4));
- }
- }
- }
-
- if (result == 0) {
- // dhcpResults->setServerAddress(new InetAddress(server))
- result = env->CallBooleanMethod(dhcpResults, dhcpResultsFieldIds.setServerAddress,
- env->NewStringUTF(server));
- }
-
- if (result == 0) {
- // dhcpResults->setLeaseDuration(lease)
- env->CallVoidMethod(dhcpResults,
- dhcpResultsFieldIds.setLeaseDuration, lease);
-
- // dhcpResults->setVendorInfo(vendorInfo)
- env->CallVoidMethod(dhcpResults, dhcpResultsFieldIds.setVendorInfo,
- env->NewStringUTF(vendorInfo));
- }
- return (jboolean)(result == 0);
-}
-
-static jboolean android_net_utils_startDhcp(JNIEnv* env, jobject clazz, jstring ifname)
-{
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- if (nameStr == NULL) return (jboolean)false;
- if (::dhcp_start(nameStr) != 0) {
- ALOGD("dhcp_start failed : %s", nameStr);
- return (jboolean)false;
- }
- return (jboolean)true;
-}
-
-static jboolean android_net_utils_startDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname)
-{
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- if (nameStr == NULL) return (jboolean)false;
- if (::dhcp_start_renew(nameStr) != 0) {
- ALOGD("dhcp_start_renew failed : %s", nameStr);
- return (jboolean)false;
- }
- return (jboolean)true;
-}
-
-static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::dhcp_stop(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jboolean)(result == 0);
-}
-
-static jboolean android_net_utils_releaseDhcpLease(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::dhcp_release_lease(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jboolean)(result == 0);
-}
-
-static jstring android_net_utils_getDhcpError(JNIEnv* env, jobject clazz)
-{
- return env->NewStringUTF(::dhcp_get_errmsg());
-}
-
static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int fd = jniGetFDFromFileDescriptor(env, javaFd);
@@ -305,12 +141,6 @@
static const JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
{ "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections },
- { "startDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_startDhcp },
- { "startDhcpRenew", "(Ljava/lang/String;)Z", (void *)android_net_utils_startDhcpRenew },
- { "getDhcpResults", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z", (void *)android_net_utils_getDhcpResults },
- { "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp },
- { "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease },
- { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
{ "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
@@ -322,24 +152,6 @@
int register_android_net_NetworkUtils(JNIEnv* env)
{
- jclass dhcpResultsClass = FindClassOrDie(env, "android/net/DhcpResults");
-
- dhcpResultsFieldIds.clear = GetMethodIDOrDie(env, dhcpResultsClass, "clear", "()V");
- dhcpResultsFieldIds.setIpAddress =GetMethodIDOrDie(env, dhcpResultsClass, "setIpAddress",
- "(Ljava/lang/String;I)Z");
- dhcpResultsFieldIds.setGateway = GetMethodIDOrDie(env, dhcpResultsClass, "setGateway",
- "(Ljava/lang/String;)Z");
- dhcpResultsFieldIds.addDns = GetMethodIDOrDie(env, dhcpResultsClass, "addDns",
- "(Ljava/lang/String;)Z");
- dhcpResultsFieldIds.setDomains = GetMethodIDOrDie(env, dhcpResultsClass, "setDomains",
- "(Ljava/lang/String;)V");
- dhcpResultsFieldIds.setServerAddress = GetMethodIDOrDie(env, dhcpResultsClass,
- "setServerAddress", "(Ljava/lang/String;)Z");
- dhcpResultsFieldIds.setLeaseDuration = GetMethodIDOrDie(env, dhcpResultsClass,
- "setLeaseDuration", "(I)V");
- dhcpResultsFieldIds.setVendorInfo = GetMethodIDOrDie(env, dhcpResultsClass, "setVendorInfo",
- "(Ljava/lang/String;)V");
-
return RegisterMethodsOrDie(env, NETUTILS_PKG_NAME, gNetworkUtilMethods,
NELEM(gNetworkUtilMethods));
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index b081568..8a77f82 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -610,6 +610,10 @@
// Grant CAP_WAKE_ALARM and CAP_BLOCK_SUSPEND to the Bluetooth process.
capabilities |= (1LL << CAP_WAKE_ALARM);
capabilities |= (1LL << CAP_BLOCK_SUSPEND);
+ // Allow bluetooth to open packet sockets so it can start the DHCP client.
+ // TODO: consider making such functionality an RPC to netd.
+ capabilities |= (1LL << CAP_NET_RAW);
+ capabilities |= (1LL << CAP_NET_BIND_SERVICE);
// Add the Bluetooth process to the system group.
jsize length = env->GetArrayLength(reinterpret_cast<jarray>(gids));
diff --git a/core/tests/coretests/src/com/android/internal/util/AsyncChannelTest.java b/core/tests/coretests/src/com/android/internal/util/AsyncChannelTest.java
deleted file mode 100644
index 7088650..0000000
--- a/core/tests/coretests/src/com/android/internal/util/AsyncChannelTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.os.Debug;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import junit.framework.TestCase;
-
-/**
- * Test for AsyncChannel.
- */
-public class AsyncChannelTest extends TestCase {
- private static final boolean DBG = true;
- private static final boolean WAIT_FOR_DEBUGGER = false;
- private static final String TAG = "AsyncChannelTest";
-
- @SmallTest
- public void test1() throws Exception {
- if (DBG) log("test1");
- if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
- assertTrue(1 == 1);
- }
-
- protected void log(String s) {
- Log.d(TAG, s);
- }
-}
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
new file mode 100644
index 0000000..f949e1a
--- /dev/null
+++ b/core/tests/utiltests/Android.mk
@@ -0,0 +1,23 @@
+#########################################################################
+# Build FrameworksUtilTests package
+#########################################################################
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := FrameworksUtilTests
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/utiltests/AndroidManifest.xml b/core/tests/utiltests/AndroidManifest.xml
new file mode 100644
index 0000000..fecaf8e
--- /dev/null
+++ b/core/tests/utiltests/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.utiltests">
+
+ <uses-permission android:name="android.permission.READ_LOGS" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+ <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
+ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+ <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.utiltests"
+ android:label="Frameworks Utility Tests" />
+
+</manifest>
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
rename to core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
diff --git a/core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java b/core/tests/utiltests/src/com/android/internal/util/BitwiseStreamsTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java
rename to core/tests/utiltests/src/com/android/internal/util/BitwiseStreamsTest.java
diff --git a/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java b/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java
rename to core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java
diff --git a/core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java
rename to core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
diff --git a/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java b/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
similarity index 94%
rename from core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java
rename to core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
index be7116d..5f36c2d 100644
--- a/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
@@ -42,6 +42,6 @@
out.endDocument();
assertEquals("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<string name=\"meow\"></string>", stream.toString());
+ + "<string name=\"meow\"></string>\n", stream.toString());
}
}
diff --git a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java
rename to core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
diff --git a/core/tests/coretests/src/com/android/internal/util/IndentingPrintWriterTest.java b/core/tests/utiltests/src/com/android/internal/util/IndentingPrintWriterTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/IndentingPrintWriterTest.java
rename to core/tests/utiltests/src/com/android/internal/util/IndentingPrintWriterTest.java
diff --git a/core/tests/utiltests/src/com/android/internal/util/MessageUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/MessageUtilsTest.java
new file mode 100644
index 0000000..32b969a
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/MessageUtilsTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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 com.android.internal.util;
+
+import static org.junit.Assert.*;
+
+import com.android.internal.util.MessageUtils;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.SparseArray;
+
+import org.junit.Test;
+
+
+class A {
+ // Should not see these.
+ private int mMember;
+ public final int CMD_NOT_STATIC = 7;
+ private static final String CMD_NOT_INT = "not an integer";
+ public static int CMD_NOT_FINAL = 34;
+ public static final int kWrongPrefix = 99;
+
+ // Should see these.
+ private static final int CMD_DO_SOMETHING = 12;
+ public static final int EVENT_SOMETHING_HAPPENED = 45;
+}
+
+class B {
+ public static final int CMD_FOO = 56;
+ public static final int EVENT_BAR = 55;
+ public static final int NOTIFICATION_BAZ = 12;
+}
+
+/**
+ * Unit tests for {@link com.android.util.MessageUtils}.
+ */
+@SmallTest
+public class MessageUtilsTest {
+
+ private static final Class[] CLASSES = { A.class, B.class };
+
+ private SparseArray<String> makeSparseArray(int[] keys, String[] values) throws Exception {
+ assertEquals("Must specify same number of keys and values", keys.length, values.length);
+ SparseArray<String> out = new SparseArray<>();
+ for (int i = 0; i < keys.length; i++) {
+ out.put(keys[i], values[i]);
+ }
+ return out;
+ }
+
+ private void assertSparseArrayEquals(
+ SparseArray<String> a1, SparseArray<String> a2) throws Exception {
+ String msg = String.format("%s != %s", a1.toString(), a2.toString());
+ assertEquals(msg, a1.size(), a2.size());
+ int size = a1.size();
+ for (int i = 0; i < size; i++) {
+ assertEquals(msg, a1.keyAt(i), a2.keyAt(i));
+ assertEquals(msg, a1.valueAt(i), a2.valueAt(i));
+ }
+ }
+
+ @Test
+ public void basicOperation() throws Exception {
+ SparseArray<String> expected = makeSparseArray(
+ new int[]{12, 45, 55, 56},
+ new String[]{"CMD_DO_SOMETHING", "EVENT_SOMETHING_HAPPENED", "EVENT_BAR", "CMD_FOO"});
+ assertSparseArrayEquals(expected, MessageUtils.findMessageNames(CLASSES));
+ }
+
+ @Test
+ public void withPrefixes() throws Exception {
+ SparseArray<String> expected = makeSparseArray(
+ new int[]{45, 55},
+ new String[]{"EVENT_SOMETHING_HAPPENED", "EVENT_BAR"});
+ assertSparseArrayEquals(expected, MessageUtils.findMessageNames(CLASSES,
+ new String[]{"EVENT_"}));
+ }
+
+ @Test(expected=MessageUtils.DuplicateConstantError.class)
+ public void duplicateConstants() {
+ MessageUtils.findMessageNames(CLASSES, new String[]{"CMD_", "NOTIFICATION_"});
+ }
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/PredicatesTest.java b/core/tests/utiltests/src/com/android/internal/util/PredicatesTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/PredicatesTest.java
rename to core/tests/utiltests/src/com/android/internal/util/PredicatesTest.java
diff --git a/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java
rename to core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
diff --git a/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
similarity index 99%
rename from core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
rename to core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index 2a2c24e..302aa87 100644
--- a/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -25,6 +25,7 @@
import android.os.Message;
import android.os.SystemClock;
+import android.test.suitebuilder.annotation.Suppress;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.StateMachine.LogRec;
@@ -38,6 +39,7 @@
/**
* Test for StateMachine.
*/
+@Suppress // Failing
public class StateMachineTest extends TestCase {
private static final String ENTER = "enter";
private static final String EXIT = "exit";
diff --git a/core/tests/coretests/src/com/android/internal/util/XmlUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java
similarity index 100%
rename from core/tests/coretests/src/com/android/internal/util/XmlUtilsTest.java
rename to core/tests/utiltests/src/com/android/internal/util/XmlUtilsTest.java
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index 90e26d8..9f1435a 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -59,8 +59,12 @@
protected static final int IPCONFIG_FILE_VERSION = 2;
+ public IpConfigStore(DelayedDiskWrite writer) {
+ mWriter = writer;
+ }
+
public IpConfigStore() {
- mWriter = new DelayedDiskWrite();
+ this(new DelayedDiskWrite());
}
private boolean writeConfig(DataOutputStream out, int configKey,
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 5c7a78a..7fc6c5b 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -19,6 +19,7 @@
import com.android.internal.util.HexDump;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
+import com.android.internal.util.MessageUtils;
import com.android.internal.util.StateMachine;
import android.app.AlarmManager;
@@ -34,7 +35,6 @@
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -42,16 +42,15 @@
import android.system.Os;
import android.system.PacketSocketAddress;
import android.util.Log;
+import android.util.SparseArray;
import android.util.TimeUtils;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.Thread;
import java.net.Inet4Address;
-import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
-import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Random;
@@ -133,6 +132,11 @@
private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
private static final int CMD_ONESHOT_TIMEOUT = PRIVATE_BASE + 4;
+ // For message logging.
+ private static final Class[] sMessageClasses = { DhcpClient.class };
+ private static final SparseArray<String> sMessageNames =
+ MessageUtils.findMessageNames(sMessageClasses);
+
// DHCP parameters that we request.
private static final byte[] REQUESTED_PARAMS = new byte[] {
DHCP_SUBNET_MASK,
@@ -497,30 +501,7 @@
}
private String messageName(int what) {
- switch (what) {
- case CMD_START_DHCP:
- return "CMD_START_DHCP";
- case CMD_STOP_DHCP:
- return "CMD_STOP_DHCP";
- case CMD_RENEW_DHCP:
- return "CMD_RENEW_DHCP";
- case CMD_PRE_DHCP_ACTION:
- return "CMD_PRE_DHCP_ACTION";
- case CMD_PRE_DHCP_ACTION_COMPLETE:
- return "CMD_PRE_DHCP_ACTION_COMPLETE";
- case CMD_POST_DHCP_ACTION:
- return "CMD_POST_DHCP_ACTION";
- case CMD_KICK:
- return "CMD_KICK";
- case CMD_RECEIVED_PACKET:
- return "CMD_RECEIVED_PACKET";
- case CMD_TIMEOUT:
- return "CMD_TIMEOUT";
- case CMD_ONESHOT_TIMEOUT:
- return "CMD_ONESHOT_TIMEOUT";
- default:
- return Integer.toString(what);
- }
+ return sMessageNames.get(what, Integer.toString(what));
}
private String messageToString(Message message) {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index d0d87b1..2a90c60 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -16,12 +16,15 @@
package android.net.ip;
+import com.android.internal.util.MessageUtils;
+
import android.content.Context;
import android.net.DhcpResults;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LinkProperties.ProvisioningChange;
+import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.dhcp.DhcpClient;
@@ -29,7 +32,9 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -61,6 +66,14 @@
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ private static final boolean NO_CALLBACKS = false;
+ private static final boolean SEND_CALLBACKS = true;
+
+ // For message logging.
+ private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class };
+ private static final SparseArray<String> sWhatToString =
+ MessageUtils.findMessageNames(sMessageClasses);
+
/**
* Callbacks for handling IpManager events.
*/
@@ -96,6 +109,35 @@
public void onQuit() {}
}
+ public static class WaitForProvisioningCallback extends Callback {
+ private LinkProperties mCallbackLinkProperties;
+
+ public LinkProperties waitForProvisioning() {
+ synchronized (this) {
+ try {
+ wait();
+ } catch (InterruptedException e) {}
+ return mCallbackLinkProperties;
+ }
+ }
+
+ @Override
+ public void onProvisioningSuccess(LinkProperties newLp) {
+ synchronized (this) {
+ mCallbackLinkProperties = newLp;
+ notify();
+ }
+ }
+
+ @Override
+ public void onProvisioningFailure(LinkProperties newLp) {
+ synchronized (this) {
+ mCallbackLinkProperties = null;
+ notify();
+ }
+ }
+ }
+
/**
* This class encapsulates parameters to be passed to
* IpManager#startProvisioning(). A defensive copy is made by IpManager
@@ -160,6 +202,8 @@
private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4;
// Sent by NetlinkTracker to communicate netlink events.
private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5;
+ private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6;
+ private static final int CMD_UPDATE_HTTP_PROXY = 7;
private static final int MAX_LOG_RECORDS = 1000;
@@ -181,10 +225,12 @@
/**
* Non-final member variables accessed only from within our StateMachine.
*/
+ private ProvisioningConfiguration mConfiguration;
private IpReachabilityMonitor mIpReachabilityMonitor;
private DhcpClient mDhcpClient;
private DhcpResults mDhcpResults;
- private ProvisioningConfiguration mConfiguration;
+ private String mTcpBufferSizes;
+ private ProxyInfo mHttpProxy;
/**
* Member variables accessed both from within the StateMachine thread
@@ -293,6 +339,26 @@
sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
}
+ /**
+ * Set the TCP buffer sizes to use.
+ *
+ * This may be called, repeatedly, at any time before or after a call to
+ * #startProvisioning(). The setting is cleared upon calling #stop().
+ */
+ public void setTcpBufferSizes(String tcpBufferSizes) {
+ sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
+ }
+
+ /**
+ * Set the HTTP Proxy configuration to use.
+ *
+ * This may be called, repeatedly, at any time before or after a call to
+ * #startProvisioning(). The setting is cleared upon calling #stop().
+ */
+ public void setHttpProxy(ProxyInfo proxyInfo) {
+ sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
+ }
+
public LinkProperties getLinkProperties() {
synchronized (mLock) {
return new LinkProperties(mLinkProperties);
@@ -306,26 +372,7 @@
@Override
protected String getWhatToString(int what) {
- // TODO: Investigate switching to reflection.
- switch (what) {
- case CMD_STOP:
- return "CMD_STOP";
- case CMD_START:
- return "CMD_START";
- case CMD_CONFIRM:
- return "CMD_CONFIRM";
- case EVENT_PRE_DHCP_ACTION_COMPLETE:
- return "EVENT_PRE_DHCP_ACTION_COMPLETE";
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
- case DhcpClient.CMD_PRE_DHCP_ACTION:
- return "DhcpClient.CMD_PRE_DHCP_ACTION";
- case DhcpClient.CMD_POST_DHCP_ACTION:
- return "DhcpClient.CMD_POST_DHCP_ACTION";
- case DhcpClient.CMD_ON_QUIT:
- return "DhcpClient.CMD_ON_QUIT";
- }
- return "UNKNOWN:" + Integer.toString(what);
+ return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
}
@Override
@@ -355,8 +402,10 @@
// assigned to the interface, etc.
private void resetLinkProperties() {
mNetlinkTracker.clearLinkProperties();
- mDhcpResults = null;
mConfiguration = null;
+ mDhcpResults = null;
+ mTcpBufferSizes = "";
+ mHttpProxy = null;
synchronized (mLock) {
mLinkProperties = new LinkProperties();
@@ -513,13 +562,33 @@
newLp.setDomains(mDhcpResults.domains);
}
+ // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
+ if (!TextUtils.isEmpty(mTcpBufferSizes)) {
+ newLp.setTcpBufferSizes(mTcpBufferSizes);
+ }
+ if (mHttpProxy != null) {
+ newLp.setHttpProxy(mHttpProxy);
+ }
+
if (VDBG) {
Log.d(mTag, "newLp{" + newLp + "}");
}
-
return newLp;
}
+ // Returns false if we have lost provisioning, true otherwise.
+ private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
+ final LinkProperties newLp = assembleLinkProperties();
+ if (linkPropertiesUnchanged(newLp)) {
+ return true;
+ }
+ final ProvisioningChange delta = setLinkProperties(newLp);
+ if (sendCallbacks) {
+ dispatchCallback(delta, newLp);
+ }
+ return (delta != ProvisioningChange.LOST_PROVISIONING);
+ }
+
private void clearIPv4Address() {
try {
final InterfaceConfiguration ifcg = new InterfaceConfiguration();
@@ -598,7 +667,17 @@
break;
case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- setLinkProperties(assembleLinkProperties());
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
+ break;
+
+ case CMD_UPDATE_TCP_BUFFER_SIZES:
+ mTcpBufferSizes = (String) msg.obj;
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
+ break;
+
+ case CMD_UPDATE_HTTP_PROXY:
+ mHttpProxy = (ProxyInfo) msg.obj;
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
break;
case DhcpClient.CMD_ON_QUIT:
@@ -729,18 +808,23 @@
}
break;
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED: {
- final LinkProperties newLp = assembleLinkProperties();
- if (linkPropertiesUnchanged(newLp)) {
- break;
- }
- final ProvisioningChange delta = setLinkProperties(newLp);
- dispatchCallback(delta, newLp);
- if (delta == ProvisioningChange.LOST_PROVISIONING) {
+ case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+ if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
transitionTo(mStoppedState);
}
break;
- }
+
+ case CMD_UPDATE_TCP_BUFFER_SIZES:
+ mTcpBufferSizes = (String) msg.obj;
+ // This cannot possibly change provisioning state.
+ handleLinkPropertiesUpdate(SEND_CALLBACKS);
+ break;
+
+ case CMD_UPDATE_HTTP_PROXY:
+ mHttpProxy = (ProxyInfo) msg.obj;
+ // This cannot possibly change provisioning state.
+ handleLinkPropertiesUpdate(SEND_CALLBACKS);
+ break;
case DhcpClient.CMD_PRE_DHCP_ACTION:
if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 9137d9d..249f64f 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -324,6 +324,11 @@
public int requestType;
/**
+ * Whether the secure RTT protocol needs to be used for ranging this peer device.
+ */
+ public boolean secure;
+
+ /**
* mac address of the device being ranged
* Default value: null
*/
@@ -478,6 +483,7 @@
for (RttParams params : mParams) {
dest.writeInt(params.deviceType);
dest.writeInt(params.requestType);
+ dest.writeByte(params.secure ? (byte) 1 : 0);
dest.writeString(params.bssid);
dest.writeInt(params.channelWidth);
dest.writeInt(params.frequency);
@@ -515,6 +521,7 @@
params[i] = new RttParams();
params[i].deviceType = in.readInt();
params[i].requestType = in.readInt();
+ params[i].secure = (in.readByte() != 0);
params[i].bssid = in.readString();
params[i].channelWidth = in.readInt();
params[i].frequency = in.readInt();
@@ -690,6 +697,11 @@
/** LCR information Element, only available to double side RTT. */
public WifiInformationElement LCR;
+
+ /**
+ * Whether the secure RTT protocol was used for ranging.
+ */
+ public boolean secure;
}
@@ -742,6 +754,7 @@
dest.writeInt((byte) result.LCR.data.length);
dest.writeByte(result.LCR.id);
}
+ dest.writeByte(result.secure ? (byte) 1 : 0);
}
} else {
dest.writeInt(0);
@@ -796,6 +809,7 @@
results[i].LCR.data = new byte[length];
in.readByteArray(results[i].LCR.data);
}
+ results[i].secure = (in.readByte() != 0);
}
ParcelableRttResults parcelableResults = new ParcelableRttResults(results);
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 31da670..ed12bdf 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -331,6 +331,7 @@
*/
public static class InformationElement {
public static final int EID_SSID = 0;
+ public static final int EID_TIM = 5;
public static final int EID_BSS_LOAD = 11;
public static final int EID_RSN = 48;
public static final int EID_HT_OPERATION = 61;
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a6aad3c..472d1ef 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -402,6 +402,15 @@
/**
* @hide
+ * The number of beacon intervals between Delivery Traffic Indication Maps (DTIM)
+ * This value is populated from scan results that contain Beacon Frames, which are infrequent.
+ * The value is not guaranteed to be set or current (Although it SHOULDNT change once set)
+ * Valid values are from 1 - 255. Initialized here as 0, use this to check if set.
+ */
+ public int dtimInterval = 0;
+
+ /**
+ * @hide
* Uid of app creating the configuration
*/
@SystemApi
@@ -1297,6 +1306,7 @@
lastUpdateUid = -1;
creatorUid = -1;
shared = true;
+ dtimInterval = 0;
}
/**
@@ -2043,4 +2053,4 @@
config.allowedKeyManagement.set(in.readInt());
return config;
}
-}
\ No newline at end of file
+}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index a406fd7..964bfa2 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -20,6 +20,7 @@
import android.os.Parcelable;
import android.security.Credentials;
import android.text.TextUtils;
+import android.util.Log;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
@@ -122,6 +123,22 @@
/** {@hide} */
public static final String DISABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=1\"";
+ // Fields to copy verbatim from wpa_supplicant.
+ private static final String[] SUPPLICANT_CONFIG_KEYS = new String[] {
+ IDENTITY_KEY,
+ ANON_IDENTITY_KEY,
+ PASSWORD_KEY,
+ CLIENT_CERT_KEY,
+ CA_CERT_KEY,
+ SUBJECT_MATCH_KEY,
+ ENGINE_KEY,
+ ENGINE_ID_KEY,
+ PRIVATE_KEY_ID_KEY,
+ ALTSUBJECT_MATCH_KEY,
+ DOM_SUFFIX_MATCH_KEY,
+ CA_PATH_KEY
+ };
+
private HashMap<String, String> mFields = new HashMap<String, String>();
//By default, we enable TLS1.2. However, due to a known bug on some radius, we may disable it to
// fall back to TLS 1.1.
@@ -129,6 +146,10 @@
private X509Certificate[] mCaCerts;
private PrivateKey mClientPrivateKey;
private X509Certificate mClientCertificate;
+ private int mEapMethod = Eap.NONE;
+ private int mPhase2Method = Phase2.NONE;
+
+ private static final String TAG = "WifiEnterpriseConfig";
public WifiEnterpriseConfig() {
// Do not set defaults so that the enterprise fields that are not changed
@@ -143,6 +164,8 @@
for (String key : source.mFields.keySet()) {
mFields.put(key, source.mFields.get(key));
}
+ mEapMethod = source.mEapMethod;
+ mPhase2Method = source.mPhase2Method;
}
@Override
@@ -158,6 +181,8 @@
dest.writeString(entry.getValue());
}
+ dest.writeInt(mEapMethod);
+ dest.writeInt(mPhase2Method);
writeCertificates(dest, mCaCerts);
if (mClientPrivateKey != null) {
@@ -210,6 +235,8 @@
enterpriseConfig.mFields.put(key, value);
}
+ enterpriseConfig.mEapMethod = in.readInt();
+ enterpriseConfig.mPhase2Method = in.readInt();
enterpriseConfig.mCaCerts = readCertificates(in);
PrivateKey userKey = null;
@@ -307,7 +334,8 @@
public static final int MSCHAPV2 = 3;
/** Generic Token Card */
public static final int GTC = 4;
- private static final String PREFIX = "auth=";
+ private static final String AUTH_PREFIX = "auth=";
+ private static final String AUTHEAP_PREFIX = "autheap=";
/** @hide */
public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP",
"MSCHAPV2", "GTC" };
@@ -316,11 +344,98 @@
private Phase2() {}
}
- /** Internal use only
+ // Loader and saver interfaces for exchanging data with wpa_supplicant.
+ // TODO: Decouple this object (which is just a placeholder of the configuration)
+ // from the implementation that knows what wpa_supplicant wants.
+ /**
+ * Interface used for retrieving supplicant configuration from WifiEnterpriseConfig
* @hide
*/
- public HashMap<String, String> getFields() {
- return mFields;
+ public interface SupplicantSaver {
+ /**
+ * Set a value within wpa_supplicant configuration
+ * @param key index to set within wpa_supplciant
+ * @param value the value for the key
+ * @return true if successful; false otherwise
+ */
+ boolean saveValue(String key, String value);
+ }
+
+ /**
+ * Interface used for populating a WifiEnterpriseConfig from supplicant configuration
+ * @hide
+ */
+ public interface SupplicantLoader {
+ /**
+ * Returns a value within wpa_supplicant configuration
+ * @param key index to set within wpa_supplciant
+ * @return string value if successful; null otherwise
+ */
+ String loadValue(String key);
+ }
+
+ /**
+ * Internal use only; supply field values to wpa_supplicant config. The configuration
+ * process aborts on the first failed call on {@code saver}.
+ * @param saver proxy for setting configuration in wpa_supplciant
+ * @return whether the save succeeded on all attempts
+ * @hide
+ */
+ public boolean saveToSupplicant(SupplicantSaver saver) {
+ if (!isEapMethodValid()) {
+ return false;
+ }
+
+ for (String key : mFields.keySet()) {
+ if (!saver.saveValue(key, mFields.get(key))) {
+ return false;
+ }
+ }
+
+ if (!saver.saveValue(EAP_KEY, Eap.strings[mEapMethod])) {
+ return false;
+ }
+
+ if (mEapMethod != Eap.TLS && mPhase2Method != Phase2.NONE) {
+ boolean is_autheap = mEapMethod == Eap.TTLS && mPhase2Method == Phase2.GTC;
+ String prefix = is_autheap ? Phase2.AUTHEAP_PREFIX : Phase2.AUTH_PREFIX;
+ String value = convertToQuotedString(prefix + Phase2.strings[mPhase2Method]);
+ return saver.saveValue(PHASE2_KEY, value);
+ } else if (mPhase2Method == Phase2.NONE) {
+ // By default, send a null phase 2 to clear old configuration values.
+ return saver.saveValue(PHASE2_KEY, null);
+ } else {
+ Log.e(TAG, "WiFi enterprise configuration is invalid as it supplies a "
+ + "phase 2 method but the phase1 method does not support it.");
+ return false;
+ }
+ }
+
+ /**
+ * Internal use only; retrieve configuration from wpa_supplicant config.
+ * @param loader proxy for retrieving configuration keys from wpa_supplicant
+ * @hide
+ */
+ public void loadFromSupplicant(SupplicantLoader loader) {
+ for (String key : SUPPLICANT_CONFIG_KEYS) {
+ String value = loader.loadValue(key);
+ if (value == null) {
+ mFields.put(key, EMPTY_VALUE);
+ } else {
+ mFields.put(key, value);
+ }
+ }
+ String eapMethod = loader.loadValue(EAP_KEY);
+ mEapMethod = getStringIndex(Eap.strings, eapMethod, Eap.NONE);
+
+ String phase2Method = removeDoubleQuotes(loader.loadValue(PHASE2_KEY));
+ // Remove "auth=" or "autheap=" prefix.
+ if (phase2Method.startsWith(Phase2.AUTH_PREFIX)) {
+ phase2Method = phase2Method.substring(Phase2.AUTH_PREFIX.length());
+ } else if (phase2Method.startsWith(Phase2.AUTHEAP_PREFIX)) {
+ phase2Method = phase2Method.substring(Phase2.AUTHEAP_PREFIX.length());
+ }
+ mPhase2Method = getStringIndex(Phase2.strings, phase2Method, Phase2.NONE);
}
/**
@@ -341,7 +456,7 @@
case Eap.SIM:
case Eap.AKA:
case Eap.AKA_PRIME:
- mFields.put(EAP_KEY, Eap.strings[eapMethod]);
+ mEapMethod = eapMethod;
mFields.put(OPP_KEY_CACHING, "1");
break;
default:
@@ -374,8 +489,7 @@
* @return eap method configured
*/
public int getEapMethod() {
- String eapMethod = mFields.get(EAP_KEY);
- return getStringIndex(Eap.strings, eapMethod, Eap.NONE);
+ return mEapMethod;
}
/**
@@ -390,15 +504,11 @@
public void setPhase2Method(int phase2Method) {
switch (phase2Method) {
case Phase2.NONE:
- mFields.put(PHASE2_KEY, EMPTY_VALUE);
- break;
- /** Valid methods */
case Phase2.PAP:
case Phase2.MSCHAP:
case Phase2.MSCHAPV2:
case Phase2.GTC:
- mFields.put(PHASE2_KEY, convertToQuotedString(
- Phase2.PREFIX + Phase2.strings[phase2Method]));
+ mPhase2Method = phase2Method;
break;
default:
throw new IllegalArgumentException("Unknown Phase 2 method");
@@ -410,12 +520,7 @@
* @return a phase 2 method defined at {@link Phase2}
* */
public int getPhase2Method() {
- String phase2Method = removeDoubleQuotes(mFields.get(PHASE2_KEY));
- // Remove auth= prefix
- if (phase2Method.startsWith(Phase2.PREFIX)) {
- phase2Method = phase2Method.substring(Phase2.PREFIX.length());
- }
- return getStringIndex(Phase2.strings, phase2Method, Phase2.NONE);
+ return mPhase2Method;
}
/**
@@ -443,7 +548,8 @@
setFieldValue(ANON_IDENTITY_KEY, anonymousIdentity, "");
}
- /** Get the anonymous identity
+ /**
+ * Get the anonymous identity
* @return anonymous identity
*/
public String getAnonymousIdentity() {
@@ -870,18 +976,15 @@
}
/** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */
- String getKeyId(WifiEnterpriseConfig current) {
- String eap = mFields.get(EAP_KEY);
- String phase2 = mFields.get(PHASE2_KEY);
-
- // If either eap or phase2 are not initialized, use current config details
- if (TextUtils.isEmpty((eap))) {
- eap = current.mFields.get(EAP_KEY);
+ public String getKeyId(WifiEnterpriseConfig current) {
+ // If EAP method is not initialized, use current config details
+ if (mEapMethod == Eap.NONE) {
+ return (current != null) ? current.getKeyId(null) : EMPTY_VALUE;
}
- if (TextUtils.isEmpty(phase2)) {
- phase2 = current.mFields.get(PHASE2_KEY);
+ if (!isEapMethodValid()) {
+ return EMPTY_VALUE;
}
- return eap + "_" + phase2;
+ return Eap.strings[mEapMethod] + "_" + Phase2.strings[mPhase2Method];
}
private String removeDoubleQuotes(String string) {
@@ -898,7 +1001,8 @@
return "\"" + string + "\"";
}
- /** Returns the index at which the toBeFound string is found in the array.
+ /**
+ * Returns the index at which the toBeFound string is found in the array.
* @param arr array of strings
* @param toBeFound string to be found
* @param defaultIndex default index to be returned when string is not found
@@ -912,13 +1016,16 @@
return defaultIndex;
}
- /** Returns the field value for the key.
+ /**
+ * Returns the field value for the key.
* @param key into the hash
* @param prefix is the prefix that the value may have
* @return value
* @hide
*/
public String getFieldValue(String key, String prefix) {
+ // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since
+ // neither of these keys should be retrieved in this manner.
String value = mFields.get(key);
// Uninitialized or known to be empty after reading from supplicant
if (TextUtils.isEmpty(value) || EMPTY_VALUE.equals(value)) return "";
@@ -931,13 +1038,16 @@
}
}
- /** Set a value with an optional prefix at key
+ /**
+ * Set a value with an optional prefix at key
* @param key into the hash
* @param value to be set
* @param prefix an optional value to be prefixed to actual value
* @hide
*/
public void setFieldValue(String key, String value, String prefix) {
+ // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since
+ // neither of these keys should be set in this manner.
if (TextUtils.isEmpty(value)) {
mFields.put(key, EMPTY_VALUE);
} else {
@@ -946,13 +1056,16 @@
}
- /** Set a value with an optional prefix at key
+ /**
+ * Set a value with an optional prefix at key
* @param key into the hash
* @param value to be set
* @param prefix an optional value to be prefixed to actual value
* @hide
*/
public void setFieldValue(String key, String value) {
+ // TODO: Should raise an exception if |key| is EAP_KEY or PHASE2_KEY since
+ // neither of these keys should be set in this manner.
if (TextUtils.isEmpty(value)) {
mFields.put(key, EMPTY_VALUE);
} else {
@@ -968,4 +1081,25 @@
}
return sb.toString();
}
+
+ /**
+ * Returns whether the EAP method data is valid, i.e., whether mEapMethod and mPhase2Method
+ * are valid indices into {@code Eap.strings[]} and {@code Phase2.strings[]} respectively.
+ */
+ private boolean isEapMethodValid() {
+ if (mEapMethod == Eap.NONE) {
+ Log.e(TAG, "WiFi enterprise configuration is invalid as it supplies no EAP method.");
+ return false;
+ }
+ if (mEapMethod < 0 || mEapMethod >= Eap.strings.length) {
+ Log.e(TAG, "mEapMethod is invald for WiFi enterprise configuration: " + mEapMethod);
+ return false;
+ }
+ if (mPhase2Method < 0 || mPhase2Method >= Phase2.strings.length) {
+ Log.e(TAG, "mPhase2Method is invald for WiFi enterprise configuration: "
+ + mPhase2Method);
+ return false;
+ }
+ return true;
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 2373754..69e179d 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -30,7 +30,9 @@
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.Protocol;
import java.util.List;
@@ -78,6 +80,8 @@
public static final int REASON_INVALID_REQUEST = -3;
/** Invalid request */
public static final int REASON_NOT_AUTHORIZED = -4;
+ /** An outstanding request with the same listener hasn't finished yet. */
+ public static final int REASON_DUPLICATE_REQEUST = -5;
/** @hide */
public static final String GET_AVAILABLE_CHANNELS_EXTRA = "Channels";
@@ -460,8 +464,11 @@
* scans should also not share this object.
*/
public void startBackgroundScan(ScanSettings settings, ScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, putListener(listener), settings);
+ sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, settings);
}
/**
* stop an ongoing wifi scan
@@ -469,8 +476,11 @@
* #startBackgroundScan}
*/
public void stopBackgroundScan(ScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = removeListener(listener);
+ if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, removeListener(listener));
+ sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
}
/**
* reports currently available scan results on appropriate listeners
@@ -491,8 +501,11 @@
* scans should also not share this object.
*/
public void startScan(ScanSettings settings, ScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, putListener(listener), settings);
+ sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, settings);
}
/**
@@ -501,8 +514,11 @@
* @param listener
*/
public void stopScan(ScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = removeListener(listener);
+ if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, removeListener(listener));
+ sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
}
/** specifies information about an access point of interest */
@@ -634,8 +650,11 @@
* provided on {@link #stopTrackingWifiChange}
*/
public void startTrackingWifiChange(WifiChangeListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, putListener(listener));
+ sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, key);
}
/**
@@ -644,8 +663,10 @@
* #stopTrackingWifiChange}
*/
public void stopTrackingWifiChange(WifiChangeListener listener) {
+ int key = removeListener(listener);
+ if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, removeListener(listener));
+ sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, key);
}
/** @hide */
@@ -730,11 +751,14 @@
*/
public void startTrackingBssids(BssidInfo[] bssidInfos,
int apLostThreshold, BssidListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
validateChannel();
HotlistSettings settings = new HotlistSettings();
settings.bssidInfos = bssidInfos;
settings.apLostThreshold = apLostThreshold;
- sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, putListener(listener), settings);
+ sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, key, settings);
}
/**
@@ -742,8 +766,11 @@
* @param listener same object provided in {@link #startTrackingBssids}
*/
public void stopTrackingBssids(BssidListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = removeListener(listener);
+ if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener));
+ sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, key);
}
@@ -812,7 +839,7 @@
private static final Object sThreadRefLock = new Object();
private static int sThreadRefCount;
- private static HandlerThread sHandlerThread;
+ private static Handler sInternalHandler;
/**
* Create a new WifiScanner instance.
@@ -824,12 +851,29 @@
* @hide
*/
public WifiScanner(Context context, IWifiScanner service) {
- mContext = context;
- mService = service;
- init();
+ this(context, service, null, true);
}
- private void init() {
+ /**
+ * Create a new WifiScanner instance.
+ *
+ * @param context The application context.
+ * @param service The IWifiScanner Binder interface
+ * @param looper Looper for running WifiScanner operations. If null, a handler thread will be
+ * created for running WifiScanner operations.
+ * @param waitForConnection If true, this will not return until a connection to Wifi Scanner
+ * service is established.
+ * @hide
+ */
+ @VisibleForTesting
+ public WifiScanner(Context context, IWifiScanner service, Looper looper,
+ boolean waitForConnection) {
+ mContext = context;
+ mService = service;
+ init(looper, waitForConnection);
+ }
+
+ private void init(Looper looper, boolean waitForConnection) {
synchronized (sThreadRefLock) {
if (++sThreadRefCount == 1) {
Messenger messenger = null;
@@ -846,17 +890,23 @@
return;
}
- sHandlerThread = new HandlerThread("WifiScanner");
sAsyncChannel = new AsyncChannel();
sConnected = new CountDownLatch(1);
- sHandlerThread.start();
- Handler handler = new ServiceHandler(sHandlerThread.getLooper());
- sAsyncChannel.connect(mContext, handler, messenger);
- try {
- sConnected.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "interrupted wait at init");
+ if (looper == null) {
+ HandlerThread thread = new HandlerThread("WifiScanner");
+ thread.start();
+ sInternalHandler = new ServiceHandler(thread.getLooper());
+ } else {
+ sInternalHandler = new ServiceHandler(looper);
+ }
+ sAsyncChannel.connect(mContext, sInternalHandler, messenger);
+ if (waitForConnection) {
+ try {
+ sConnected.await();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "interrupted wait at init");
+ }
}
}
}
@@ -867,6 +917,30 @@
"No permission to access and change wifi or a bad initialization");
}
+ // Add a listener into listener map. If the listener already exists, return INVALID_KEY and
+ // send an error message to internal handler; Otherwise add the listener to the listener map and
+ // return the key of the listener.
+ private int addListener(ActionListener listener) {
+ synchronized (sListenerMap) {
+ boolean keyExists = (getListenerKey(listener) != INVALID_KEY);
+ // Note we need to put the listener into listener map even if it's a duplicate as the
+ // internal handler will need the key to find the listener. In case of duplicates,
+ // removing duplicate key logic will be handled in internal handler.
+ int key = putListener(listener);
+ if (keyExists) {
+ if (DBG) Log.d(TAG, "listener key already exists");
+ OperationResult operationResult = new OperationResult(REASON_DUPLICATE_REQEUST,
+ "Outstanding request with same key not stopped yet");
+ Message message = Message.obtain(sInternalHandler, CMD_OP_FAILED, 0, key,
+ operationResult);
+ message.sendToTarget();
+ return INVALID_KEY;
+ } else {
+ return key;
+ }
+ }
+ }
+
private static int putListener(Object listener) {
if (listener == null) return INVALID_KEY;
int key;
@@ -910,7 +984,10 @@
private static int removeListener(Object listener) {
int key = getListenerKey(listener);
- if (key == INVALID_KEY) return key;
+ if (key == INVALID_KEY) {
+ Log.e(TAG, "listener cannot be found");
+ return key;
+ }
synchronized (sListenerMapLock) {
sListenerMap.remove(key);
return key;