diff options
36 files changed, 1268 insertions, 480 deletions
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 6f326de76150..80554fff75ac 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -961,7 +961,7 @@ public class ResourcesManager { // TODO(adamlesinski): Make this accept more than just overlay directories. final void applyNewResourceDirsLocked(@NonNull final String baseCodePath, - @NonNull final String[] newResourceDirs) { + @Nullable final String[] newResourceDirs) { try { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#applyNewResourceDirsLocked"); diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 1e41eea925a5..535bf675cd0e 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -21,25 +21,24 @@ import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkStringNotEmpty; import android.annotation.SdkConstant; -import android.annotation.SystemService; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemService; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; -import android.os.RemoteException; import android.os.Messenger; -import android.text.TextUtils; +import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; -import java.util.concurrent.CountDownLatch; - import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; +import java.util.concurrent.CountDownLatch; + /** * The Network Service Discovery Manager class provides the API to discover services * on a network. As an example, if device A and device B are connected over a Wi-Fi @@ -244,7 +243,7 @@ public final class NsdManager { return name; } - private static int FIRST_LISTENER_KEY = 1; + private static final int FIRST_LISTENER_KEY = 1; private final INsdManager mService; private final Context mContext; @@ -278,6 +277,7 @@ public final class NsdManager { @VisibleForTesting public void disconnect() { mAsyncChannel.disconnect(); + mHandler.getLooper().quitSafely(); } /** @@ -650,7 +650,7 @@ public final class NsdManager { private static void checkServiceInfo(NsdServiceInfo serviceInfo) { checkNotNull(serviceInfo, "NsdServiceInfo cannot be null"); - checkStringNotEmpty(serviceInfo.getServiceName(),"Service name cannot be empty"); + checkStringNotEmpty(serviceInfo.getServiceName(), "Service name cannot be empty"); checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty"); } } diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index 8632194f2b02..560b4b31cdc6 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -16,6 +16,8 @@ package android.os; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.util.Log; import android.util.MutableInt; @@ -43,17 +45,12 @@ public class SystemProperties { public static final int PROP_VALUE_MAX = 91; + @GuardedBy("sChangeCallbacks") private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>(); @GuardedBy("sRoReads") - private static final HashMap<String, MutableInt> sRoReads; - static { - if (TRACK_KEY_ACCESS) { - sRoReads = new HashMap<>(); - } else { - sRoReads = null; - } - } + private static final HashMap<String, MutableInt> sRoReads = + TRACK_KEY_ACCESS ? new HashMap<>() : null; private static void onKeyAccess(String key) { if (!TRACK_KEY_ACCESS) return; @@ -85,77 +82,102 @@ public class SystemProperties { private static native void native_report_sysprop_change(); /** - * Get the value for the given key. - * @return an empty string if the key isn't found + * Get the String value for the given {@code key}. + * + * <b>WARNING:</b> Do not use this method if the value may not be a valid UTF string! This + * method will crash in native code. + * + * @param key the key to lookup + * @return an empty string if the {@code key} isn't found */ - public static String get(String key) { + @NonNull + public static String get(@NonNull String key) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get(key); } /** - * Get the value for the given key. - * @return if the key isn't found, return def if it isn't null, or an empty string otherwise + * Get the String value for the given {@code key}. + * + * <b>WARNING:</b> Do not use this method if the value may not be a valid UTF string! This + * method will crash in native code. + * + * @param key the key to lookup + * @param def the default value in case the property is not set or empty + * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty + * string otherwise */ - public static String get(String key, String def) { + @NonNull + public static String get(@NonNull String key, @Nullable String def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get(key, def); } /** - * Get the value for the given key, and return as an integer. + * Get the value for the given {@code key}, and return as an integer. + * * @param key the key to lookup * @param def a default value to return * @return the key parsed as an integer, or def if the key isn't found or * cannot be parsed */ - public static int getInt(String key, int def) { + public static int getInt(@NonNull String key, int def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_int(key, def); } /** - * Get the value for the given key, and return as a long. + * Get the value for the given {@code key}, and return as a long. + * * @param key the key to lookup * @param def a default value to return * @return the key parsed as a long, or def if the key isn't found or * cannot be parsed */ - public static long getLong(String key, long def) { + public static long getLong(@NonNull String key, long def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_long(key, def); } /** - * Get the value for the given key, returned as a boolean. + * Get the value for the given {@code key}, returned as a boolean. * Values 'n', 'no', '0', 'false' or 'off' are considered false. * Values 'y', 'yes', '1', 'true' or 'on' are considered true. * (case sensitive). * If the key does not exist, or has any other value, then the default * result is returned. + * * @param key the key to lookup * @param def a default value to return * @return the key parsed as a boolean, or def if the key isn't found or is * not able to be parsed as a boolean. */ - public static boolean getBoolean(String key, boolean def) { + public static boolean getBoolean(@NonNull String key, boolean def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_boolean(key, def); } /** - * Set the value for the given key. - * @throws IllegalArgumentException if the value exceeds 92 characters + * Set the value for the given {@code key} to {@code val}. + * + * @throws IllegalArgumentException if the {@code val} exceeds 91 characters */ - public static void set(String key, String val) { + public static void set(@NonNull String key, @Nullable String val) { if (val != null && val.length() > PROP_VALUE_MAX) { - throw newValueTooLargeException(key, val); + throw new IllegalArgumentException("value of system property '" + key + + "' is longer than " + PROP_VALUE_MAX + " characters: " + val); } if (TRACK_KEY_ACCESS) onKeyAccess(key); native_set(key, val); } - public static void addChangeCallback(Runnable callback) { + /** + * Add a callback that will be run whenever any system property changes. + * + * @param callback The {@link Runnable} that should be executed when a system property + * changes. + */ + public static void addChangeCallback(@NonNull Runnable callback) { synchronized (sChangeCallbacks) { if (sChangeCallbacks.size() == 0) { native_add_change_callback(); @@ -164,7 +186,8 @@ public class SystemProperties { } } - static void callChangeCallbacks() { + @SuppressWarnings("unused") // Called from native code. + private static void callChangeCallbacks() { synchronized (sChangeCallbacks) { //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!"); if (sChangeCallbacks.size() == 0) { @@ -177,11 +200,6 @@ public class SystemProperties { } } - private static IllegalArgumentException newValueTooLargeException(String key, String value) { - return new IllegalArgumentException("value of system property '" + key + "' is longer than " - + PROP_VALUE_MAX + " characters: " + value); - } - /* * Notifies listeners that a system property has changed */ diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp index 8844fb0a261f..a94cac0f18f5 100644 --- a/core/jni/android_os_SystemProperties.cpp +++ b/core/jni/android_os_SystemProperties.cpp @@ -17,188 +17,109 @@ #define LOG_TAG "SysPropJNI" +#include "android-base/logging.h" +#include "android-base/properties.h" #include "cutils/properties.h" #include "utils/misc.h" #include <utils/Log.h> #include "jni.h" #include "core_jni_helpers.h" #include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/ScopedUtfChars.h> namespace android { -static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz, - jstring keyJ, jstring defJ) -{ - int len; - const char* key; - char buf[PROPERTY_VALUE_MAX]; - jstring rvJ = NULL; - - if (keyJ == NULL) { - jniThrowNullPointerException(env, "key must not be null."); - goto error; - } - - key = env->GetStringUTFChars(keyJ, NULL); - - len = property_get(key, buf, ""); - if ((len <= 0) && (defJ != NULL)) { - rvJ = defJ; - } else if (len >= 0) { - rvJ = env->NewStringUTF(buf); - } else { - rvJ = env->NewStringUTF(""); +namespace { + +template <typename T, typename Handler> +T ConvertKeyAndForward(JNIEnv *env, jstring keyJ, T defJ, Handler handler) { + std::string key; + { + // Scope the String access. If the handler can throw an exception, + // releasing the string characters late would trigger an abort. + ScopedUtfChars key_utf(env, keyJ); + if (key_utf.c_str() == nullptr) { + return defJ; + } + key = key_utf.c_str(); // This will make a copy, but we can't avoid + // with the existing interface in + // android::base. } - - env->ReleaseStringUTFChars(keyJ, key); - -error: - return rvJ; + return handler(key, defJ); } -static jstring SystemProperties_getS(JNIEnv *env, jobject clazz, - jstring keyJ) +jstring SystemProperties_getSS(JNIEnv *env, jclass clazz, jstring keyJ, + jstring defJ) { - return SystemProperties_getSS(env, clazz, keyJ, NULL); + // Using ConvertKeyAndForward is sub-optimal for copying the key string, + // but improves reuse and reasoning over code. + auto handler = [&](const std::string& key, jstring defJ) { + std::string prop_val = android::base::GetProperty(key, ""); + if (!prop_val.empty()) { + return env->NewStringUTF(prop_val.c_str()); + }; + if (defJ != nullptr) { + return defJ; + } + // This function is specified to never return null (or have an + // exception pending). + return env->NewStringUTF(""); + }; + return ConvertKeyAndForward(env, keyJ, defJ, handler); } -static jint SystemProperties_get_int(JNIEnv *env, jobject clazz, - jstring keyJ, jint defJ) +jstring SystemProperties_getS(JNIEnv *env, jclass clazz, jstring keyJ) { - int len; - const char* key; - char buf[PROPERTY_VALUE_MAX]; - char* end; - jint result = defJ; - - if (keyJ == NULL) { - jniThrowNullPointerException(env, "key must not be null."); - goto error; - } - - key = env->GetStringUTFChars(keyJ, NULL); - - len = property_get(key, buf, ""); - if (len > 0) { - result = strtol(buf, &end, 0); - if (end == buf) { - result = defJ; - } - } - - env->ReleaseStringUTFChars(keyJ, key); - -error: - return result; + return SystemProperties_getSS(env, clazz, keyJ, nullptr); } -static jlong SystemProperties_get_long(JNIEnv *env, jobject clazz, - jstring keyJ, jlong defJ) +template <typename T> +T SystemProperties_get_integral(JNIEnv *env, jclass, jstring keyJ, + T defJ) { - int len; - const char* key; - char buf[PROPERTY_VALUE_MAX]; - char* end; - jlong result = defJ; - - if (keyJ == NULL) { - jniThrowNullPointerException(env, "key must not be null."); - goto error; - } - - key = env->GetStringUTFChars(keyJ, NULL); - - len = property_get(key, buf, ""); - if (len > 0) { - result = strtoll(buf, &end, 0); - if (end == buf) { - result = defJ; - } - } - - env->ReleaseStringUTFChars(keyJ, key); - -error: - return result; + auto handler = [](const std::string& key, T defV) { + return android::base::GetIntProperty<T>(key, defV); + }; + return ConvertKeyAndForward(env, keyJ, defJ, handler); } -static jboolean SystemProperties_get_boolean(JNIEnv *env, jobject clazz, - jstring keyJ, jboolean defJ) +jboolean SystemProperties_get_boolean(JNIEnv *env, jclass, jstring keyJ, + jboolean defJ) { - int len; - const char* key; - char buf[PROPERTY_VALUE_MAX]; - jboolean result = defJ; - - if (keyJ == NULL) { - jniThrowNullPointerException(env, "key must not be null."); - goto error; - } - - key = env->GetStringUTFChars(keyJ, NULL); - - len = property_get(key, buf, ""); - if (len == 1) { - char ch = buf[0]; - if (ch == '0' || ch == 'n') - result = false; - else if (ch == '1' || ch == 'y') - result = true; - } else if (len > 1) { - if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) { - result = false; - } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) { - result = true; - } - } - - env->ReleaseStringUTFChars(keyJ, key); - -error: - return result; + auto handler = [](const std::string& key, jboolean defV) -> jboolean { + bool result = android::base::GetBoolProperty(key, defV); + return result ? JNI_TRUE : JNI_FALSE; + }; + return ConvertKeyAndForward(env, keyJ, defJ, handler); } -static void SystemProperties_set(JNIEnv *env, jobject clazz, - jstring keyJ, jstring valJ) +void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, + jstring valJ) { - int err; - const char* key; - const char* val; - - if (keyJ == NULL) { - jniThrowNullPointerException(env, "key must not be null."); - return ; - } - key = env->GetStringUTFChars(keyJ, NULL); - - if (valJ == NULL) { - val = ""; /* NULL pointer not allowed here */ - } else { - val = env->GetStringUTFChars(valJ, NULL); - } - - err = property_set(key, val); - - env->ReleaseStringUTFChars(keyJ, key); - - if (valJ != NULL) { - env->ReleaseStringUTFChars(valJ, val); - } - - if (err < 0) { + auto handler = [&](const std::string& key, bool) { + std::string val; + if (valJ != nullptr) { + ScopedUtfChars key_utf(env, valJ); + val = key_utf.c_str(); + } + return android::base::SetProperty(key, val); + }; + if (!ConvertKeyAndForward(env, keyJ, true, handler)) { + // Must have been a failure in SetProperty. jniThrowException(env, "java/lang/RuntimeException", "failed to set system property"); } } -static JavaVM* sVM = NULL; -static jclass sClazz = NULL; -static jmethodID sCallChangeCallbacks; +JavaVM* sVM = nullptr; +jclass sClazz = nullptr; +jmethodID sCallChangeCallbacks; -static void do_report_sysprop_change() { +void do_report_sysprop_change() { //ALOGI("Java SystemProperties: VM=%p, Clazz=%p", sVM, sClazz); - if (sVM != NULL && sClazz != NULL) { + if (sVM != nullptr && sClazz != nullptr) { JNIEnv* env; if (sVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0) { //ALOGI("Java SystemProperties: calling %p", sCallChangeCallbacks); @@ -207,47 +128,49 @@ static void do_report_sysprop_change() { } } -static void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz) +void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz) { // This is called with the Java lock held. - if (sVM == NULL) { + if (sVM == nullptr) { env->GetJavaVM(&sVM); } - if (sClazz == NULL) { + if (sClazz == nullptr) { sClazz = (jclass) env->NewGlobalRef(clazz); sCallChangeCallbacks = env->GetStaticMethodID(sClazz, "callChangeCallbacks", "()V"); add_sysprop_change_callback(do_report_sysprop_change, -10000); } } -static void SystemProperties_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/) +void SystemProperties_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/) { report_sysprop_change(); } -static const JNINativeMethod method_table[] = { - { "native_get", "(Ljava/lang/String;)Ljava/lang/String;", - (void*) SystemProperties_getS }, - { "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", - (void*) SystemProperties_getSS }, - { "native_get_int", "(Ljava/lang/String;I)I", - (void*) SystemProperties_get_int }, - { "native_get_long", "(Ljava/lang/String;J)J", - (void*) SystemProperties_get_long }, - { "native_get_boolean", "(Ljava/lang/String;Z)Z", - (void*) SystemProperties_get_boolean }, - { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V", - (void*) SystemProperties_set }, - { "native_add_change_callback", "()V", - (void*) SystemProperties_add_change_callback }, - { "native_report_sysprop_change", "()V", - (void*) SystemProperties_report_sysprop_change }, -}; +} // namespace int register_android_os_SystemProperties(JNIEnv *env) { - return RegisterMethodsOrDie(env, "android/os/SystemProperties", method_table, - NELEM(method_table)); + const JNINativeMethod method_table[] = { + { "native_get", "(Ljava/lang/String;)Ljava/lang/String;", + (void*) SystemProperties_getS }, + { "native_get", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void*) SystemProperties_getSS }, + { "native_get_int", "(Ljava/lang/String;I)I", + (void*) SystemProperties_get_integral<jint> }, + { "native_get_long", "(Ljava/lang/String;J)J", + (void*) SystemProperties_get_integral<jlong> }, + { "native_get_boolean", "(Ljava/lang/String;Z)Z", + (void*) SystemProperties_get_boolean }, + { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V", + (void*) SystemProperties_set }, + { "native_add_change_callback", "()V", + (void*) SystemProperties_add_change_callback }, + { "native_report_sysprop_change", "()V", + (void*) SystemProperties_report_sysprop_change }, + }; + return RegisterMethodsOrDie(env, "android/os/SystemProperties", + method_table, NELEM(method_table)); } }; diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 899551371f30..70c39afbc441 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -296,6 +296,17 @@ less than 0x600 --> <bool translatable="false" name="config_apfDrop802_3Frames">true</bool> + <!-- An array of Black listed EtherType, packets with EtherTypes within this array + will be dropped + TODO: need to put proper values, these are for testing purposes only --> + <integer-array translatable="false" name="config_apfEthTypeBlackList"> + <item>0x88A2</item> + <item>0x88A4</item> + <item>0x88B8</item> + <item>0x88CD</item> + <item>0x88E3</item> + </integer-array> + <!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual device behaviour is controlled by Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE. This is the default value of that setting. --> @@ -1493,16 +1504,16 @@ <integer translatable="false" name="config_bluetooth_max_advertisers">0</integer> <!-- Idle current for bluetooth controller. 0 by default--> - <integer translatable="false" name="config_bluetooth_idle_cur_ma">1</integer> + <integer translatable="false" name="config_bluetooth_idle_cur_ma">0</integer> <!-- Rx current for bluetooth controller. 0 by default--> - <integer translatable="false" name="config_bluetooth_rx_cur_ma">2</integer> + <integer translatable="false" name="config_bluetooth_rx_cur_ma">0</integer> <!-- Tx current for bluetooth controller. 0 by default--> - <integer translatable="false" name="config_bluetooth_tx_cur_ma">3</integer> + <integer translatable="false" name="config_bluetooth_tx_cur_ma">0</integer> <!-- Operating volatage for bluetooth controller. 0 by default--> - <integer translatable="false" name="config_bluetooth_operating_voltage_mv">4</integer> + <integer translatable="false" name="config_bluetooth_operating_voltage_mv">0</integer> <!-- Whether supported profiles should be reloaded upon enabling bluetooth --> <bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c5275017a7cf..600c82f87d90 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1837,6 +1837,7 @@ <java-symbol type="integer" name="config_networkWakeupPacketMark" /> <java-symbol type="integer" name="config_networkWakeupPacketMask" /> <java-symbol type="bool" name="config_apfDrop802_3Frames" /> + <java-symbol type="array" name="config_apfEthTypeBlackList" /> <java-symbol type="integer" name="config_networkMeteredMultipathPreference" /> <java-symbol type="integer" name="config_notificationsBatteryFullARGB" /> <java-symbol type="integer" name="config_notificationsBatteryLedOff" /> diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index 7e9f561e68d5..f5b350b053d4 100644 --- a/core/res/res/xml/sms_short_codes.xml +++ b/core/res/res/xml/sms_short_codes.xml @@ -181,7 +181,7 @@ <shortcode country="mk" pattern="\\d{1,6}" free="129005|122" /> <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed --> - <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963" /> + <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963|76551" /> <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf --> <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" /> diff --git a/core/tests/systemproperties/run_core_systemproperties_test.sh b/core/tests/systemproperties/run_core_systemproperties_test.sh index d39adbbfa390..9b1fe4be666b 100755 --- a/core/tests/systemproperties/run_core_systemproperties_test.sh +++ b/core/tests/systemproperties/run_core_systemproperties_test.sh @@ -15,7 +15,7 @@ fi if [[ $rebuild == true ]]; then make -j4 FrameworksCoreSystemPropertiesTests - TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreSystemPropertiesTests.apk + TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreSystemPropertiesTests/FrameworksCoreSystemPropertiesTests.apk COMMAND="adb install -r $TESTAPP" echo $COMMAND $COMMAND diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java index 544a96727217..282b0011eede 100644 --- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java +++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java @@ -51,6 +51,11 @@ public class SystemPropertiesTest extends TestCase { value = SystemProperties.get(KEY, "default"); assertEquals("default", value); + // null default value is the same as "". + SystemProperties.set(KEY, null); + value = SystemProperties.get(KEY, "default"); + assertEquals("default", value); + SystemProperties.set(KEY, "SA"); value = SystemProperties.get(KEY, "default"); assertEquals("SA", value); @@ -62,7 +67,78 @@ public class SystemPropertiesTest extends TestCase { value = SystemProperties.get(KEY, "default"); assertEquals("default", value); + // null value is the same as "". + SystemProperties.set(KEY, "SA"); + SystemProperties.set(KEY, null); + value = SystemProperties.get(KEY, "default"); + assertEquals("default", value); + value = SystemProperties.get(KEY); assertEquals("", value); } + + private static void testInt(String setVal, int defValue, int expected) { + SystemProperties.set(KEY, setVal); + int value = SystemProperties.getInt(KEY, defValue); + assertEquals(expected, value); + } + + private static void testLong(String setVal, long defValue, long expected) { + SystemProperties.set(KEY, setVal); + long value = SystemProperties.getLong(KEY, defValue); + assertEquals(expected, value); + } + + @SmallTest + public void testIntegralProperties() throws Exception { + testInt("", 123, 123); + testInt("", 0, 0); + testInt("", -123, -123); + + testInt("123", 124, 123); + testInt("0", 124, 0); + testInt("-123", 124, -123); + + testLong("", 3147483647L, 3147483647L); + testLong("", 0, 0); + testLong("", -3147483647L, -3147483647L); + + testLong("3147483647", 124, 3147483647L); + testLong("0", 124, 0); + testLong("-3147483647", 124, -3147483647L); + } + + @SmallTest + @SuppressWarnings("null") + public void testNullKey() throws Exception { + try { + SystemProperties.get(null); + fail("Expected NullPointerException"); + } catch (NullPointerException npe) { + } + + try { + SystemProperties.get(null, "default"); + fail("Expected NullPointerException"); + } catch (NullPointerException npe) { + } + + try { + SystemProperties.set(null, "value"); + fail("Expected NullPointerException"); + } catch (NullPointerException npe) { + } + + try { + SystemProperties.getInt(null, 0); + fail("Expected NullPointerException"); + } catch (NullPointerException npe) { + } + + try { + SystemProperties.getLong(null, 0); + fail("Expected NullPointerException"); + } catch (NullPointerException npe) { + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java index 0cf890037f55..fe03fba5bfda 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java @@ -58,9 +58,19 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override public void onSimStateChanged(int subId, int slotId, State simState) { - if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); - resetState(); - }; + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + switch(simState) { + // If the SIM is removed, then we must remove the keyguard. It will be put up + // again when the PUK locked SIM is re-entered. + case ABSENT: { + KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId); + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + break; + } + default: + resetState(); + } + } }; public KeyguardSimPinView(Context context) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java index fb3cee74ab68..826a03d1b658 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java @@ -60,9 +60,23 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override public void onSimStateChanged(int subId, int slotId, State simState) { - if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); - resetState(); - }; + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + switch(simState) { + // If the SIM is removed, then we must remove the keyguard. It will be put up + // again when the PUK locked SIM is re-entered. + case ABSENT: + // intentional fall-through + // If the SIM is unlocked via a key sequence through the emergency dialer, it will + // move into the READY state and the PUK lock keyguard should be removed. + case READY: { + KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId); + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + break; + } + default: + resetState(); + } + } }; public KeyguardSimPukView(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index 2473335e6e15..cef7c8ac9a8c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -170,7 +170,8 @@ public class QSFooter extends FrameLayout implements int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space); mAnimator = new Builder() - .addFloat(mSettingsContainer, "translationX", -(remaining - defSpace), 0) + .addFloat(mSettingsContainer, "translationX", + isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0) .addFloat(mSettingsButton, "rotation", -120, 0) .build(); if (mAlarmShowing) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 0a0d2ce00a1b..bdc5e7d75b48 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -556,7 +556,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta @Override public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) { - if (viewHolder.getItemViewType() == TYPE_EDIT) { + if (viewHolder.getItemViewType() == TYPE_EDIT || viewHolder.getItemViewType() == TYPE_DIVIDER) { return makeMovementFlags(0, 0); } int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index 7e82edaae3e5..b1ac5891dafa 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java @@ -690,6 +690,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { } break; case MotionEvent.ACTION_UP: { + mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); if (!mMagnificationController.magnificationRegionContains( event.getX(), event.getY())) { transitionToDelegatingState(!mShortcutTriggered); @@ -703,7 +704,6 @@ class MagnificationGestureHandler implements EventStreamTransformation { if (mLastDownEvent == null) { return; } - mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop, mTapDistanceSlop, 0)) { transitionToDelegatingState(true); diff --git a/services/core/Android.mk b/services/core/Android.mk index 7fca21f71a6a..f61fd550885b 100644 --- a/services/core/Android.mk +++ b/services/core/Android.mk @@ -4,7 +4,10 @@ include $(CLEAR_VARS) LOCAL_MODULE := services.core -LOCAL_AIDL_INCLUDES := system/netd/server/binder +LOCAL_AIDL_INCLUDES := \ + frameworks/native/aidl/binder \ + system/netd/server/binder + LOCAL_SRC_FILES += \ $(call all-java-files-under,java) \ diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index fb20533837af..60476447ca49 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -458,6 +458,11 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int MAX_WAKELOCK_LOGS = 20; private final LocalLog mWakelockLogs = new LocalLog(MAX_WAKELOCK_LOGS); + private int mTotalWakelockAcquisitions = 0; + private int mTotalWakelockReleases = 0; + private long mTotalWakelockDurationMs = 0; + private long mMaxWakelockDurationMs = 0; + private long mLastWakeLockAcquireTimestamp = 0; // Array of <Network,ReadOnlyLocalLogs> tracking network validation and results private static final int MAX_VALIDATION_LOGS = 10; @@ -1959,6 +1964,14 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(); pw.println("NetTransition WakeLock activity (most recent first):"); pw.increaseIndent(); + pw.println("total acquisitions: " + mTotalWakelockAcquisitions); + pw.println("total releases: " + mTotalWakelockReleases); + pw.println("cumulative duration: " + (mTotalWakelockDurationMs / 1000) + "s"); + pw.println("longest duration: " + (mMaxWakelockDurationMs / 1000) + "s"); + if (mTotalWakelockAcquisitions > mTotalWakelockReleases) { + long duration = SystemClock.elapsedRealtime() - mLastWakeLockAcquireTimestamp; + pw.println("currently holding WakeLock for: " + (duration / 1000) + "s"); + } mWakelockLogs.reverseDump(fd, pw, args); pw.decreaseIndent(); } @@ -3012,6 +3025,8 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } mNetTransitionWakeLock.acquire(); + mLastWakeLockAcquireTimestamp = SystemClock.elapsedRealtime(); + mTotalWakelockAcquisitions++; } mWakelockLogs.log("ACQUIRE for " + forWhom); Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK); @@ -3044,6 +3059,10 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } mNetTransitionWakeLock.release(); + long lockDuration = SystemClock.elapsedRealtime() - mLastWakeLockAcquireTimestamp; + mTotalWakelockDurationMs += lockDuration; + mMaxWakelockDurationMs = Math.max(mMaxWakelockDurationMs, lockDuration); + mTotalWakelockReleases++; } mWakelockLogs.log(String.format("RELEASE (%s)", event)); } @@ -4616,6 +4635,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) { + if (mNetworkForNetId.get(nai.network.netId) != nai) { + // Ignore updates for disconnected networks + return; + } + if (VDBG) { log("Update of LinkProperties for " + nai.name() + "; created=" + nai.created + diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 3e4453204b14..caa2d5112bf0 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -76,6 +76,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.INetworkActivityListener; import android.os.INetworkManagementService; +import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -1897,38 +1898,34 @@ public class NetworkManagementService extends INetworkManagementService.Stub return new NetworkStats(SystemClock.elapsedRealtime(), 0); } - final NativeDaemonEvent[] events; + final PersistableBundle bundle; try { - events = mConnector.executeForList("bandwidth", "gettetherstats"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + bundle = mNetdService.tetherGetStats(); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException("problem parsing tethering stats: ", e); } - final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); - for (NativeDaemonEvent event : events) { - if (event.getCode() != TetheringStatsListResult) continue; - // 114 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets - final StringTokenizer tok = new StringTokenizer(event.getMessage()); - try { - final String ifaceIn = tok.nextToken(); - final String ifaceOut = tok.nextToken(); + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), + bundle.size()); + final NetworkStats.Entry entry = new NetworkStats.Entry(); - final NetworkStats.Entry entry = new NetworkStats.Entry(); - entry.iface = ifaceOut; + for (String iface : bundle.keySet()) { + long[] statsArray = bundle.getLongArray(iface); + try { + entry.iface = iface; entry.uid = UID_TETHERING; entry.set = SET_DEFAULT; entry.tag = TAG_NONE; - entry.rxBytes = Long.parseLong(tok.nextToken()); - entry.rxPackets = Long.parseLong(tok.nextToken()); - entry.txBytes = Long.parseLong(tok.nextToken()); - entry.txPackets = Long.parseLong(tok.nextToken()); + entry.rxBytes = statsArray[INetd.TETHER_STATS_RX_BYTES]; + entry.rxPackets = statsArray[INetd.TETHER_STATS_RX_PACKETS]; + entry.txBytes = statsArray[INetd.TETHER_STATS_TX_BYTES]; + entry.txPackets = statsArray[INetd.TETHER_STATS_TX_PACKETS]; stats.combineValues(entry); - } catch (NoSuchElementException e) { - throw new IllegalStateException("problem parsing tethering stats: " + event); - } catch (NumberFormatException e) { - throw new IllegalStateException("problem parsing tethering stats: " + event); + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalStateException("invalid tethering stats for " + iface, e); } } + return stats; } diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 10c8b8b1e0aa..e6585ad194ec 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -33,7 +33,7 @@ import java.util.Objects; /** * Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated - * from a consistent and unique thread context. It is the responsability of ConnectivityService to + * from a consistent and unique thread context. It is the responsibility of ConnectivityService to * call into this class from its own Handler thread. * * @hide @@ -60,7 +60,9 @@ public class Nat464Xlat extends BaseNetworkObserver { private enum State { IDLE, // start() not called. Base iface and stacked iface names are null. STARTING, // start() called. Base iface and stacked iface names are known. - RUNNING; // start() called, and the stacked iface is known to be up. + RUNNING, // start() called, and the stacked iface is known to be up. + STOPPING; // stop() called, this Nat464Xlat is still registered as a network observer for + // the stacked interface. } private String mBaseIface; @@ -81,6 +83,8 @@ public class Nat464Xlat extends BaseNetworkObserver { // TODO: migrate to NetworkCapabilities.TRANSPORT_*. final int netType = nai.networkInfo.getType(); final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType()); + // TODO: this should also consider if the network is in SUSPENDED state to avoid stopping + // clatd in SUSPENDED state. final boolean connected = nai.networkInfo.isConnected(); // We only run clat on networks that don't have a native IPv4 address. final boolean hasIPv4Address = @@ -111,18 +115,70 @@ public class Nat464Xlat extends BaseNetworkObserver { } /** - * Sets internal state. + * @return true if clatd has been stopped. + */ + public boolean isStopping() { + return mState == State.STOPPING; + } + + /** + * Start clatd, register this Nat464Xlat as a network observer for the stacked interface, + * and set internal state. */ private void enterStartingState(String baseIface) { + try { + mNMService.registerObserver(this); + } catch(RemoteException e) { + Slog.e(TAG, + "startClat: Can't register interface observer for clat on " + mNetwork.name()); + return; + } + try { + mNMService.startClatd(baseIface); + } catch(RemoteException|IllegalStateException e) { + Slog.e(TAG, "Error starting clatd on " + baseIface, e); + } mIface = CLAT_PREFIX + baseIface; mBaseIface = baseIface; mState = State.STARTING; } /** - * Clears internal state. + * Enter running state just after getting confirmation that the stacked interface is up, and + * turn ND offload off if on WiFi. + */ + private void enterRunningState() { + maybeSetIpv6NdOffload(mBaseIface, false); + mState = State.RUNNING; + } + + /** + * Stop clatd, and turn ND offload on if it had been turned off. + */ + private void enterStoppingState() { + if (isRunning()) { + maybeSetIpv6NdOffload(mBaseIface, true); + } + + try { + mNMService.stopClatd(mBaseIface); + } catch(RemoteException|IllegalStateException e) { + Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e); + } + + mState = State.STOPPING; + } + + /** + * Unregister as a base observer for the stacked interface, and clear internal state. */ private void enterIdleState() { + try { + mNMService.unregisterObserver(this); + } catch(RemoteException|IllegalStateException e) { + Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface, e); + } + mIface = null; mBaseIface = null; mState = State.IDLE; @@ -142,27 +198,14 @@ public class Nat464Xlat extends BaseNetworkObserver { return; } - try { - mNMService.registerObserver(this); - } catch(RemoteException e) { - Slog.e(TAG, "startClat: Can't register interface observer for clat on " + mNetwork); - return; - } - String baseIface = mNetwork.linkProperties.getInterfaceName(); if (baseIface == null) { Slog.e(TAG, "startClat: Can't start clat on null interface"); return; } // TODO: should we only do this if mNMService.startClatd() succeeds? + Slog.i(TAG, "Starting clatd on " + baseIface); enterStartingState(baseIface); - - Slog.i(TAG, "Starting clatd on " + mBaseIface); - try { - mNMService.startClatd(mBaseIface); - } catch(RemoteException|IllegalStateException e) { - Slog.e(TAG, "Error starting clatd on " + mBaseIface, e); - } } /** @@ -170,18 +213,15 @@ public class Nat464Xlat extends BaseNetworkObserver { */ public void stop() { if (!isStarted()) { - Slog.e(TAG, "stopClat: already stopped or not started"); return; } - Slog.i(TAG, "Stopping clatd on " + mBaseIface); - try { - mNMService.stopClatd(mBaseIface); - } catch(RemoteException|IllegalStateException e) { - Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e); + + boolean wasStarting = isStarting(); + enterStoppingState(); + if (wasStarting) { + enterIdleState(); } - // When clatd stops and its interface is deleted, handleInterfaceRemoved() will trigger - // ConnectivityService#handleUpdateLinkProperties and call enterIdleState(). } /** @@ -255,55 +295,52 @@ public class Nat464Xlat extends BaseNetworkObserver { if (!isStarting() || !up || !Objects.equals(mIface, iface)) { return; } + LinkAddress clatAddress = getLinkAddress(iface); if (clatAddress == null) { - Slog.e(TAG, "cladAddress was null for stacked iface " + iface); + Slog.e(TAG, "clatAddress was null for stacked iface " + iface); return; } - mState = State.RUNNING; + Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s", mIface, mIface, mBaseIface)); - - maybeSetIpv6NdOffload(mBaseIface, false); + enterRunningState(); LinkProperties lp = new LinkProperties(mNetwork.linkProperties); lp.addStackedLink(makeLinkProperties(clatAddress)); - mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp); + mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp); } /** * Removes stacked link on base link and transitions to IDLE state. */ private void handleInterfaceRemoved(String iface) { - if (!isRunning() || !Objects.equals(mIface, iface)) { + if (!Objects.equals(mIface, iface)) { + return; + } + if (!isRunning() && !isStopping()) { return; } Slog.i(TAG, "interface " + iface + " removed"); - // The interface going away likely means clatd has crashed. Ask netd to stop it, - // because otherwise when we try to start it again on the same base interface netd - // will complain that it's already started. - try { - mNMService.unregisterObserver(this); - // TODO: add STOPPING state to avoid calling stopClatd twice. - mNMService.stopClatd(mBaseIface); - } catch(RemoteException|IllegalStateException e) { - Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e); + if (!isStopping()) { + // Ensure clatd is stopped if stop() has not been called: this likely means that clatd + // has crashed. + enterStoppingState(); } - maybeSetIpv6NdOffload(mBaseIface, true); - LinkProperties lp = new LinkProperties(mNetwork.linkProperties); - lp.removeStackedLink(mIface); enterIdleState(); - mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp); + LinkProperties lp = new LinkProperties(mNetwork.linkProperties); + lp.removeStackedLink(iface); + mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp); } @Override public void interfaceLinkStateChanged(String iface, boolean up) { - mNetwork.handler.post(() -> { handleInterfaceLinkStateChanged(iface, up); }); + mNetwork.handler().post(() -> { handleInterfaceLinkStateChanged(iface, up); }); } @Override public void interfaceRemoved(String iface) { - mNetwork.handler.post(() -> { handleInterfaceRemoved(iface); }); + mNetwork.handler().post(() -> { handleInterfaceRemoved(iface); }); } @Override diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 7c4ef0f0f3b9..e96f4b0383cb 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -249,9 +249,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private static final String TAG = ConnectivityService.class.getSimpleName(); private static final boolean VDBG = false; - public final ConnectivityService connService; + private final ConnectivityService mConnService; private final Context mContext; - final Handler handler; + private final Handler mHandler; public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, @@ -263,13 +263,21 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { linkProperties = lp; networkCapabilities = nc; currentScore = score; - this.connService = connService; + mConnService = connService; mContext = context; - this.handler = handler; - networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest); + mHandler = handler; + networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest); networkMisc = misc; } + public ConnectivityService connService() { + return mConnService; + } + + public Handler handler() { + return mHandler; + } + // Functions for manipulating the requests satisfied by this network. // // These functions must only called on ConnectivityService's main thread. @@ -432,7 +440,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private boolean ignoreWifiUnvalidationPenalty() { boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - boolean avoidBadWifi = connService.avoidBadWifi() || avoidUnvalidated; + boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated; return isWifi && !avoidBadWifi && everValidated; } @@ -516,8 +524,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } if (newExpiry > 0) { - mLingerMessage = connService.makeWakeupMessage( - mContext, handler, + mLingerMessage = mConnService.makeWakeupMessage( + mContext, mHandler, "NETWORK_LINGER_COMPLETE." + network.netId, EVENT_NETWORK_LINGER_COMPLETE, this); mLingerMessage.schedule(newExpiry); diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index 6d5c428e58f8..788867f9137e 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -63,6 +63,8 @@ import java.util.concurrent.TimeUnit; */ public class OffloadController { private static final String TAG = OffloadController.class.getSimpleName(); + private static final String ANYIP = "0.0.0.0"; + private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); private final Handler mHandler; private final OffloadHardwareInterface mHwInterface; @@ -148,6 +150,14 @@ public class OffloadController { public void onStoppedUnsupported() { if (!started()) return; mLog.log("onStoppedUnsupported"); + // Poll for statistics and trigger a sweep of tethering + // stats by observers. This might not succeed, but it's + // worth trying anyway. We need to do this because from + // this point on we continue with software forwarding, + // and we need to synchronize stats and limits between + // software and hardware forwarding. + updateStatsForAllUpstreams(); + forceTetherStatsPoll(); } @Override @@ -155,11 +165,15 @@ public class OffloadController { if (!started()) return; mLog.log("onSupportAvailable"); - // [1] Poll for statistics and notify NetworkStats - // [2] (Re)Push all state: - // [a] push local prefixes - // [b] push downstreams - // [c] push upstream parameters + // [1] Poll for statistics and trigger a sweep of stats + // by observers. We need to do this to ensure that any + // limits set take into account any software tethering + // traffic that has been happening in the meantime. + updateStatsForAllUpstreams(); + forceTetherStatsPoll(); + // [2] (Re)Push all state. + // TODO: computeAndPushLocalPrefixes() + // TODO: push all downstream state. pushUpstreamParameters(null); } @@ -181,12 +195,7 @@ public class OffloadController { // The stats for the previous upstream were already updated on this thread // just after the upstream was changed, so they are also up-to-date. updateStatsForCurrentUpstream(); - - try { - mNms.tetherLimitReached(mStatsProvider); - } catch (RemoteException e) { - mLog.e("Cannot report data limit reached: " + e); - } + forceTetherStatsPoll(); } @Override @@ -305,13 +314,33 @@ public class OffloadController { maybeUpdateStats(currentUpstreamInterface()); } + private void updateStatsForAllUpstreams() { + // In practice, there should only ever be a single digit number of + // upstream interfaces over the lifetime of an active tethering session. + // Roughly speaking, imagine a very ambitious one or two of each of the + // following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ]. + for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { + maybeUpdateStats(kv.getKey()); + } + } + + private void forceTetherStatsPoll() { + try { + mNms.tetherLimitReached(mStatsProvider); + } catch (RemoteException e) { + mLog.e("Cannot report data limit reached: " + e); + } + } + public void setUpstreamLinkProperties(LinkProperties lp) { if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; - String prevUpstream = (mUpstreamLinkProperties != null) ? - mUpstreamLinkProperties.getInterfaceName() : null; + final String prevUpstream = currentUpstreamInterface(); mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; + // Make sure we record this interface in the ForwardedStats map. + final String iface = currentUpstreamInterface(); + if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS); // TODO: examine return code and decide what to do if programming // upstream parameters fails (probably just wait for a subsequent @@ -378,16 +407,20 @@ public class OffloadController { } private boolean pushUpstreamParameters(String prevUpstream) { - if (mUpstreamLinkProperties == null) { + final String iface = currentUpstreamInterface(); + + if (TextUtils.isEmpty(iface)) { + final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null); + // Update stats after we've told the hardware to stop forwarding so + // we don't miss packets. maybeUpdateStats(prevUpstream); - return mHwInterface.setUpstreamParameters(null, null, null, null); + return rval; } // A stacked interface cannot be an upstream for hardware offload. // Consequently, we examine only the primary interface name, look at // getAddresses() rather than getAllAddresses(), and check getRoutes() // rather than getAllRoutes(). - final String iface = mUpstreamLinkProperties.getInterfaceName(); final ArrayList<String> v6gateways = new ArrayList<>(); String v4addr = null; String v4gateway = null; diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 57d2502c6dc7..17adb1a74e30 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -27,6 +27,7 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.ip.InterfaceController; import android.net.ip.RouterAdvertisementDaemon; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.NetdService; @@ -107,8 +108,10 @@ public class TetherInterfaceStateMachine extends StateMachine { private final SharedLog mLog; private final INetworkManagementService mNMService; + private final INetd mNetd; private final INetworkStatsService mStatsService; private final IControlsTethering mTetherController; + private final InterfaceController mInterfaceCtrl; private final String mIfaceName; private final int mInterfaceType; @@ -136,8 +139,11 @@ public class TetherInterfaceStateMachine extends StateMachine { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); mNMService = nMService; + // TODO: This should be passed in for testability. + mNetd = NetdService.getInstance(); mStatsService = statsService; mTetherController = tetherController; + mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog); mIfaceName = ifaceName; mInterfaceType = interfaceType; mLinkProperties = new LinkProperties(); @@ -179,6 +185,7 @@ public class TetherInterfaceStateMachine extends StateMachine { private void stopIPv4() { configureIPv4(false); } + // TODO: Refactor this in terms of calls to InterfaceController. private boolean configureIPv4(boolean enabled) { if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")"); @@ -381,8 +388,8 @@ public class TetherInterfaceStateMachine extends StateMachine { private void configureLocalIPv6Dns( HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) { - final INetd netd = NetdService.getInstance(); - if (netd == null) { + // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located? + if (mNetd == null) { if (newDnses != null) newDnses.clear(); mLog.e("No netd service instance available; not setting local IPv6 addresses"); return; @@ -391,11 +398,8 @@ public class TetherInterfaceStateMachine extends StateMachine { // [1] Remove deprecated local DNS IP addresses. if (!deprecatedDnses.isEmpty()) { for (Inet6Address dns : deprecatedDnses) { - final String dnsString = dns.getHostAddress(); - try { - netd.interfaceDelAddress(mIfaceName, dnsString, RFC7421_PREFIX_LENGTH); - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to remove local dns IP " + dnsString + ": " + e); + if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) { + mLog.e("Failed to remove local dns IP " + dns); } mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH)); @@ -410,11 +414,8 @@ public class TetherInterfaceStateMachine extends StateMachine { } for (Inet6Address dns : addedDnses) { - final String dnsString = dns.getHostAddress(); - try { - netd.interfaceAddAddress(mIfaceName, dnsString, RFC7421_PREFIX_LENGTH); - } catch (ServiceSpecificException | RemoteException e) { - mLog.e("Failed to add local dns IP " + dnsString + ": " + e); + if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) { + mLog.e("Failed to add local dns IP " + dns); newDnses.remove(dns); } @@ -423,7 +424,7 @@ public class TetherInterfaceStateMachine extends StateMachine { } try { - netd.tetherApplyDnsInterfaces(); + mNetd.tetherApplyDnsInterfaces(); } catch (ServiceSpecificException | RemoteException e) { mLog.e("Failed to update local DNS caching server"); if (newDnses != null) newDnses.clear(); diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index 04d91f882d04..807c343d0d10 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -92,26 +92,10 @@ class IdmapManager { return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile(); } - boolean isDangerous(@NonNull final PackageInfo overlayPackage, final int userId) { - // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible - return isDangerous(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())); - } - private String getIdmapPath(@NonNull final String baseCodePath) { final StringBuilder sb = new StringBuilder("/data/resource-cache/"); sb.append(baseCodePath.substring(1).replace('/', '@')); sb.append("@idmap"); return sb.toString(); } - - private boolean isDangerous(@NonNull final String idmapPath) { - try (DataInputStream dis = new DataInputStream(new FileInputStream(idmapPath))) { - final int magic = dis.readInt(); - final int version = dis.readInt(); - final int dangerous = dis.readInt(); - return dangerous != 0; - } catch (IOException e) { - return true; - } - } } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index db6e9749535b..c3957f432f4c 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -169,8 +169,9 @@ final class OverlayManagerServiceImpl { } final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); - updateAllOverlaysForTarget(packageName, userId, targetPackage); - mListener.onOverlaysChanged(packageName, userId); + if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) { + mListener.onOverlaysChanged(packageName, userId); + } } void onTargetPackageChanged(@NonNull final String packageName, final int userId) { @@ -210,7 +211,9 @@ final class OverlayManagerServiceImpl { Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId); } - updateAllOverlaysForTarget(packageName, userId, null); + if (updateAllOverlaysForTarget(packageName, userId, null)) { + mListener.onOverlaysChanged(packageName, userId); + } } /** diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java index 8cb2df761fbe..190b3a613ca1 100644 --- a/services/net/java/android/net/apf/ApfFilter.java +++ b/services/net/java/android/net/apf/ApfFilter.java @@ -180,6 +180,7 @@ public class ApfFilter { private static final int ETH_DEST_ADDR_OFFSET = 0; private static final int ETH_ETHERTYPE_OFFSET = 12; private static final int ETH_TYPE_MIN = 0x0600; + private static final int ETH_TYPE_MAX = 0xFFFF; private static final byte[] ETH_BROADCAST_MAC_ADDRESS = {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN. @@ -232,6 +233,9 @@ public class ApfFilter { private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24; // Do not log ApfProgramEvents whose actual lifetimes was less than this. private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2; + // Limit on the Black List size to cap on program usage for this + // TODO: Select a proper max length + private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20; private final ApfCapabilities mApfCapabilities; private final IpManager.Callback mIpManagerCallback; @@ -247,6 +251,8 @@ public class ApfFilter { @GuardedBy("this") private boolean mMulticastFilter; private final boolean mDrop802_3Frames; + private final int[] mEthTypeBlackList; + // Our IPv4 address, if we have just one, otherwise null. @GuardedBy("this") private byte[] mIPv4Address; @@ -257,12 +263,16 @@ public class ApfFilter { @VisibleForTesting ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface, IpManager.Callback ipManagerCallback, boolean multicastFilter, - boolean ieee802_3Filter, IpConnectivityLog log) { + boolean ieee802_3Filter, int[] ethTypeBlackList, IpConnectivityLog log) { mApfCapabilities = apfCapabilities; mIpManagerCallback = ipManagerCallback; mNetworkInterface = networkInterface; mMulticastFilter = multicastFilter; mDrop802_3Frames = ieee802_3Filter; + + // Now fill the black list from the passed array + mEthTypeBlackList = filterEthTypeBlackList(ethTypeBlackList); + mMetricsLog = log; // TODO: ApfFilter should not generate programs until IpManager sends provisioning success. @@ -278,6 +288,35 @@ public class ApfFilter { return mUniqueCounter++; } + @GuardedBy("this") + private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) { + ArrayList<Integer> bl = new ArrayList<Integer>(); + + for (int p : ethTypeBlackList) { + // Check if the protocol is a valid ether type + if ((p < ETH_TYPE_MIN) || (p > ETH_TYPE_MAX)) { + continue; + } + + // Check if the protocol is not repeated in the passed array + if (bl.contains(p)) { + continue; + } + + // Check if list reach its max size + if (bl.size() == APF_MAX_ETH_TYPE_BLACK_LIST_LEN) { + Log.w(TAG, "Passed EthType Black List size too large (" + bl.size() + + ") using top " + APF_MAX_ETH_TYPE_BLACK_LIST_LEN + " protocols"); + break; + } + + // Now add the protocol to the list + bl.add(p); + } + + return bl.stream().mapToInt(Integer::intValue).toArray(); + } + /** * Attempt to start listening for RAs and, if RAs are received, generating and installing * filters to ignore useless RAs. @@ -891,6 +930,7 @@ public class ApfFilter { * Begin generating an APF program to: * <ul> * <li>Drop/Pass 802.3 frames (based on policy) + * <li>Drop packets with EtherType within the Black List * <li>Drop ARP requests not for us, if mIPv4Address is set, * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC, * <li>Drop IPv4 multicast packets, if mMulticastFilter, @@ -914,6 +954,8 @@ public class ApfFilter { // // if it's a 802.3 Frame (ethtype < 0x0600): // drop or pass based on configurations + // if it has a ether-type that belongs to the black list + // drop // if it's ARP: // insert ARP filter to drop or pass these appropriately // if it's IPv4: @@ -931,6 +973,11 @@ public class ApfFilter { gen.addJumpIfR0LessThan(ETH_TYPE_MIN, gen.DROP_LABEL); } + // Handle ether-type black list + for (int p : mEthTypeBlackList) { + gen.addJumpIfR0Equals(p, gen.DROP_LABEL); + } + // Add ARP filters: String skipArpFiltersLabel = "skipArpFilters"; gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel); @@ -1115,7 +1162,7 @@ public class ApfFilter { */ public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities, NetworkInterface networkInterface, IpManager.Callback ipManagerCallback, - boolean multicastFilter, boolean ieee802_3Filter) { + boolean multicastFilter, boolean ieee802_3Filter, int[] ethTypeBlackList) { if (apfCapabilities == null || networkInterface == null) return null; if (apfCapabilities.apfVersionSupported == 0) return null; if (apfCapabilities.maximumApfProgramSize < 512) { @@ -1132,7 +1179,7 @@ public class ApfFilter { return null; } return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback, - multicastFilter, ieee802_3Filter, new IpConnectivityLog()); + multicastFilter, ieee802_3Filter, ethTypeBlackList, new IpConnectivityLog()); } public synchronized void shutdown() { diff --git a/services/net/java/android/net/ip/InterfaceController.java b/services/net/java/android/net/ip/InterfaceController.java new file mode 100644 index 000000000000..02e4f875230a --- /dev/null +++ b/services/net/java/android/net/ip/InterfaceController.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2017 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 android.net.INetd; +import android.net.InterfaceConfiguration; +import android.net.LinkAddress; +import android.net.util.NetdService; +import android.net.util.SharedLog; +import android.os.INetworkManagementService; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.system.OsConstants; + +import java.net.InetAddress; + + +/** + * Encapsulates the multiple IP configuration operations performed on an interface. + * + * TODO: refactor/eliminate the redundant ways to set and clear addresses. + * + * @hide + */ +public class InterfaceController { + private final static boolean DBG = false; + + private final String mIfName; + private final INetworkManagementService mNMS; + private final INetd mNetd; + private final SharedLog mLog; + + public InterfaceController(String ifname, INetworkManagementService nms, INetd netd, + SharedLog log) { + mIfName = ifname; + mNMS = nms; + mNetd = netd; + mLog = log; + } + + public boolean setIPv4Address(LinkAddress address) { + final InterfaceConfiguration ifcg = new InterfaceConfiguration(); + ifcg.setLinkAddress(address); + try { + mNMS.setInterfaceConfig(mIfName, ifcg); + if (DBG) mLog.log("IPv4 configuration succeeded"); + } catch (IllegalStateException | RemoteException e) { + logError("IPv4 configuration failed: %s", e); + return false; + } + return true; + } + + public boolean clearIPv4Address() { + try { + final InterfaceConfiguration ifcg = new InterfaceConfiguration(); + ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0")); + mNMS.setInterfaceConfig(mIfName, ifcg); + } catch (IllegalStateException | RemoteException e) { + logError("Failed to clear IPv4 address on interface %s: %s", mIfName, e); + return false; + } + return true; + } + + public boolean enableIPv6() { + try { + mNMS.enableIpv6(mIfName); + } catch (IllegalStateException | RemoteException e) { + logError("enabling IPv6 failed: %s", e); + return false; + } + return true; + } + + public boolean disableIPv6() { + try { + mNMS.disableIpv6(mIfName); + } catch (IllegalStateException | RemoteException e) { + logError("disabling IPv6 failed: %s", e); + return false; + } + return true; + } + + public boolean setIPv6PrivacyExtensions(boolean enabled) { + try { + mNMS.setInterfaceIpv6PrivacyExtensions(mIfName, enabled); + } catch (IllegalStateException | RemoteException e) { + logError("error setting IPv6 privacy extensions: %s", e); + return false; + } + return true; + } + + public boolean setIPv6AddrGenModeIfSupported(int mode) { + try { + mNMS.setIPv6AddrGenMode(mIfName, mode); + } catch (RemoteException e) { + logError("Unable to set IPv6 addrgen mode: %s", e); + return false; + } catch (ServiceSpecificException e) { + if (e.errorCode != OsConstants.EOPNOTSUPP) { + logError("Unable to set IPv6 addrgen mode: %s", e); + return false; + } + } + return true; + } + + public boolean addAddress(LinkAddress addr) { + return addAddress(addr.getAddress(), addr.getPrefixLength()); + } + + public boolean addAddress(InetAddress ip, int prefixLen) { + try { + mNetd.interfaceAddAddress(mIfName, ip.getHostAddress(), prefixLen); + } catch (ServiceSpecificException | RemoteException e) { + logError("failed to add %s/%d: %s", ip, prefixLen, e); + return false; + } + return true; + } + + public boolean removeAddress(InetAddress ip, int prefixLen) { + try { + mNetd.interfaceDelAddress(mIfName, ip.getHostAddress(), prefixLen); + } catch (ServiceSpecificException | RemoteException e) { + logError("failed to remove %s/%d: %s", ip, prefixLen, e); + return false; + } + return true; + } + + public boolean clearAllAddresses() { + try { + mNMS.clearInterfaceAddresses(mIfName); + } catch (Exception e) { + logError("Failed to clear addresses: %s", e); + return false; + } + return true; + } + + private void logError(String fmt, Object... args) { + mLog.e(String.format(fmt, args)); + } +} diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index facdb8510759..b1eb0854e334 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -22,7 +22,6 @@ import com.android.internal.util.WakeupMessage; import android.content.Context; import android.net.DhcpResults; import android.net.INetd; -import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties.ProvisioningChange; @@ -43,9 +42,7 @@ import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.ServiceSpecificException; import android.os.SystemClock; -import android.system.OsConstants; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; @@ -94,7 +91,6 @@ import java.util.stream.Collectors; */ public class IpManager extends StateMachine { private static final boolean DBG = false; - private static final boolean VDBG = false; // For message logging. private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class }; @@ -526,17 +522,18 @@ public class IpManager extends StateMachine { public static final String DUMP_ARG = "ipmanager"; public static final String DUMP_ARG_CONFIRM = "confirm"; - private static final int CMD_STOP = 1; - private static final int CMD_START = 2; - private static final int CMD_CONFIRM = 3; - private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4; + private static final int CMD_TERMINATE_AFTER_STOP = 1; + private static final int CMD_STOP = 2; + private static final int CMD_START = 3; + private static final int CMD_CONFIRM = 4; + private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5; // 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 CMD_SET_MULTICAST_FILTER = 8; - private static final int EVENT_PROVISIONING_TIMEOUT = 9; - private static final int EVENT_DHCPACTION_TIMEOUT = 10; + private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6; + private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7; + private static final int CMD_UPDATE_HTTP_PROXY = 8; + private static final int CMD_SET_MULTICAST_FILTER = 9; + private static final int EVENT_PROVISIONING_TIMEOUT = 10; + private static final int EVENT_DHCPACTION_TIMEOUT = 11; private static final int MAX_LOG_RECORDS = 500; private static final int MAX_PACKET_RECORDS = 100; @@ -568,7 +565,7 @@ public class IpManager extends StateMachine { private final LocalLog mConnectivityPacketLog; private final MessageHandlingLogger mMsgStateLogger; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); - private final INetd mNetd; + private final InterfaceController mInterfaceCtrl; private NetworkInterface mNetworkInterface; @@ -612,12 +609,13 @@ public class IpManager extends StateMachine { mClatInterfaceName = CLAT_PREFIX + ifName; mCallback = new LoggingCallbackWrapper(callback); mNwService = nwService; - mNetd = netd; mLog = new SharedLog(MAX_LOG_RECORDS, mTag); mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS); mMsgStateLogger = new MessageHandlingLogger(); + mInterfaceCtrl = new InterfaceController(mInterfaceName, mNwService, netd, mLog); + mNetlinkTracker = new NetlinkTracker( mInterfaceName, new NetlinkTracker.Callback() { @@ -704,6 +702,16 @@ public class IpManager extends StateMachine { mMultinetworkPolicyTracker.start(); } + private void stopStateMachineUpdaters() { + try { + mNwService.unregisterObserver(mNetlinkTracker); + } catch (RemoteException e) { + logError("Couldn't unregister NetlinkTracker: %s", e); + } + + mMultinetworkPolicyTracker.shutdown(); + } + @Override protected void onQuitting() { mCallback.onQuit(); @@ -712,8 +720,7 @@ public class IpManager extends StateMachine { // Shut down this IpManager instance altogether. public void shutdown() { stop(); - mMultinetworkPolicyTracker.shutdown(); - quit(); + sendMessage(CMD_TERMINATE_AFTER_STOP); } public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() { @@ -858,7 +865,7 @@ public class IpManager extends StateMachine { final String richerLogLine = getWhatToString(msg.what) + " " + logLine; mLog.log(richerLogLine); - if (VDBG) { + if (DBG) { Log.d(mTag, richerLogLine); } @@ -1013,19 +1020,19 @@ public class IpManager extends StateMachine { private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { switch (delta) { case GAINED_PROVISIONING: - if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); } + if (DBG) { Log.d(mTag, "onProvisioningSuccess()"); } recordMetric(IpManagerEvent.PROVISIONING_OK); mCallback.onProvisioningSuccess(newLp); break; case LOST_PROVISIONING: - if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); } + if (DBG) { Log.d(mTag, "onProvisioningFailure()"); } recordMetric(IpManagerEvent.PROVISIONING_FAIL); mCallback.onProvisioningFailure(newLp); break; default: - if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); } + if (DBG) { Log.d(mTag, "onLinkPropertiesChange()"); } mCallback.onLinkPropertiesChange(newLp); break; } @@ -1113,7 +1120,7 @@ public class IpManager extends StateMachine { addAllReachableDnsServers(newLp, config.dnsServers); } final LinkProperties oldLp = mLinkProperties; - if (VDBG) { + if (DBG) { Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s", netlinkLinkProperties, newLp, oldLp)); } @@ -1148,35 +1155,12 @@ public class IpManager extends StateMachine { return (delta != ProvisioningChange.LOST_PROVISIONING); } - private boolean setIPv4Address(LinkAddress address) { - final InterfaceConfiguration ifcg = new InterfaceConfiguration(); - ifcg.setLinkAddress(address); - try { - mNwService.setInterfaceConfig(mInterfaceName, ifcg); - if (VDBG) Log.d(mTag, "IPv4 configuration succeeded"); - } catch (IllegalStateException | RemoteException e) { - logError("IPv4 configuration failed: %s", e); - return false; - } - return true; - } - - private void clearIPv4Address() { - try { - final InterfaceConfiguration ifcg = new InterfaceConfiguration(); - ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0")); - mNwService.setInterfaceConfig(mInterfaceName, ifcg); - } catch (IllegalStateException | RemoteException e) { - logError("Failed to clear IPv4 address on interface %s: %s", mInterfaceName, e); - } - } - private void handleIPv4Success(DhcpResults dhcpResults) { mDhcpResults = new DhcpResults(dhcpResults); final LinkProperties newLp = assembleLinkProperties(); final ProvisioningChange delta = setLinkProperties(newLp); - if (VDBG) { + if (DBG) { Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); } mCallback.onNewDhcpResults(dhcpResults); @@ -1190,9 +1174,9 @@ public class IpManager extends StateMachine { // that could trigger a call to this function. If we missed handling // that message in StartedState for some reason we would still clear // any addresses upon entry to StoppedState. - clearIPv4Address(); + mInterfaceCtrl.clearIPv4Address(); mDhcpResults = null; - if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); } + if (DBG) { Log.d(mTag, "onNewDhcpResults(null)"); } mCallback.onNewDhcpResults(null); handleProvisioningFailure(); @@ -1229,7 +1213,7 @@ public class IpManager extends StateMachine { // If we have a StaticIpConfiguration attempt to apply it and // handle the result accordingly. if (mConfiguration.mStaticIpConfig != null) { - if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) { + if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) { handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig)); } else { return false; @@ -1244,46 +1228,16 @@ public class IpManager extends StateMachine { return true; } - private void setIPv6AddrGenModeIfSupported() throws RemoteException { - try { - mNwService.setIPv6AddrGenMode(mInterfaceName, mConfiguration.mIPv6AddrGenMode); - } catch (ServiceSpecificException e) { - if (e.errorCode != OsConstants.EOPNOTSUPP) { - logError("Unable to set IPv6 addrgen mode: %s", e); - } - } - } - private boolean startIPv6() { - // Set privacy extensions. - try { - mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); - - setIPv6AddrGenModeIfSupported(); - mNwService.enableIpv6(mInterfaceName); - } catch (IllegalStateException | RemoteException | ServiceSpecificException e) { - logError("Unable to change interface settings: %s", e); - return false; - } - - return true; + return mInterfaceCtrl.setIPv6PrivacyExtensions(true) && + mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode) && + mInterfaceCtrl.enableIPv6(); } private boolean applyInitialConfig(InitialConfiguration config) { - if (mNetd == null) { - logError("tried to add %s to %s but INetd was null", config, mInterfaceName); - return false; - } - // TODO: also support specifying a static IPv4 configuration in InitialConfiguration. for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIPv6)) { - try { - mNetd.interfaceAddAddress( - mInterfaceName, addr.getAddress().getHostAddress(), addr.getPrefixLength()); - } catch (ServiceSpecificException | RemoteException e) { - logError("failed to add %s to %s: %s", addr, mInterfaceName, e); - return false; - } + if (!mInterfaceCtrl.addAddress(addr)) return false; } return true; @@ -1320,17 +1274,8 @@ public class IpManager extends StateMachine { // - we don't get IPv4 routes from netlink // so we neither react to nor need to wait for changes in either. - try { - mNwService.disableIpv6(mInterfaceName); - } catch (Exception e) { - logError("Failed to disable IPv6: %s", e); - } - - try { - mNwService.clearInterfaceAddresses(mInterfaceName); - } catch (Exception e) { - logError("Failed to clear addresses: %s", e); - } + mInterfaceCtrl.disableIPv6(); + mInterfaceCtrl.clearAllAddresses(); } class StoppedState extends State { @@ -1348,6 +1293,11 @@ public class IpManager extends StateMachine { @Override public boolean processMessage(Message msg) { switch (msg.what) { + case CMD_TERMINATE_AFTER_STOP: + stopStateMachineUpdaters(); + quit(); + break; + case CMD_STOP: break; @@ -1404,7 +1354,7 @@ public class IpManager extends StateMachine { break; case DhcpClient.CMD_CLEAR_LINKADDRESS: - clearIPv4Address(); + mInterfaceCtrl.clearIPv4Address(); break; case DhcpClient.CMD_ON_QUIT: @@ -1494,8 +1444,11 @@ public class IpManager extends StateMachine { boolean filter802_3Frames = mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames); + int[] ethTypeBlackList = mContext.getResources().getIntArray( + R.array.config_apfEthTypeBlackList); + mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface, - mCallback, mMulticastFiltering, filter802_3Frames); + mCallback, mMulticastFiltering, filter802_3Frames, ethTypeBlackList); // TODO: investigate the effects of any multicast filtering racing/interfering with the // rest of this IP configuration startup. if (mApfFilter == null) { @@ -1657,12 +1610,12 @@ public class IpManager extends StateMachine { break; case DhcpClient.CMD_CLEAR_LINKADDRESS: - clearIPv4Address(); + mInterfaceCtrl.clearIPv4Address(); break; case DhcpClient.CMD_CONFIGURE_LINKADDRESS: { final LinkAddress ipAddress = (LinkAddress) msg.obj; - if (setIPv4Address(ipAddress)) { + if (mInterfaceCtrl.setIPv4Address(ipAddress)) { mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED); } else { logError("Failed to set IPv4 address."); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index fe8774fa1f62..2b528101d5d3 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -199,7 +199,7 @@ public class CarrierConfigManager { /** Display carrier settings menu if true */ public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; - /** Does not display additional call seting for IMS phone based on GSM Phone */ + /** Does not display additional call setting for IMS phone based on GSM Phone */ public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool"; /** Show APN Settings for some CDMA carriers */ diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index e7b22bdfadcc..cf4c47bf541f 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -524,6 +524,7 @@ public class ImsConfig { * Defines IMS feature value. */ public static class FeatureValueConstants { + public static final int ERROR = -1; public static final int OFF = 0; public static final int ON = 1; } diff --git a/telephony/java/com/android/internal/telephony/ExponentialBackoff.java b/telephony/java/com/android/internal/telephony/ExponentialBackoff.java new file mode 100644 index 000000000000..80958c077d6a --- /dev/null +++ b/telephony/java/com/android/internal/telephony/ExponentialBackoff.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 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.telephony; + +import android.annotation.NonNull; +import android.os.Handler; +import android.os.Looper; + +/** The implementation of exponential backoff with jitter applied. */ +public class ExponentialBackoff { + private int mRetryCounter; + private long mStartDelayMs; + private long mMaximumDelayMs; + private long mCurrentDelayMs; + private int mMultiplier; + private Runnable mRunnable; + private Handler mHandler; + + public ExponentialBackoff( + long initialDelayMs, + long maximumDelayMs, + int multiplier, + @NonNull Looper looper, + @NonNull Runnable runnable) { + this(initialDelayMs, maximumDelayMs, multiplier, new Handler(looper), runnable); + } + + public ExponentialBackoff( + long initialDelayMs, + long maximumDelayMs, + int multiplier, + @NonNull Handler handler, + @NonNull Runnable runnable) { + mRetryCounter = 0; + mStartDelayMs = initialDelayMs; + mMaximumDelayMs = maximumDelayMs; + mMultiplier = multiplier; + mHandler = handler; + mRunnable = runnable; + } + + /** Starts the backoff, the runnable will be executed after {@link #mStartDelayMs}. */ + public void start() { + mRetryCounter = 0; + mCurrentDelayMs = mStartDelayMs; + mHandler.removeCallbacks(mRunnable); + mHandler.postDelayed(mRunnable, mCurrentDelayMs); + } + + /** Stops the backoff, all pending messages will be removed from the message queue. */ + public void stop() { + mRetryCounter = 0; + mHandler.removeCallbacks(mRunnable); + } + + /** Should call when the retry action has failed and we want to retry after a longer delay. */ + public void notifyFailed() { + mRetryCounter++; + long temp = Math.min( + mMaximumDelayMs, (long) (mStartDelayMs * Math.pow(mMultiplier, mRetryCounter))); + mCurrentDelayMs = (long) (((1 + Math.random()) / 2) * temp); + mHandler.removeCallbacks(mRunnable); + mHandler.postDelayed(mRunnable, mCurrentDelayMs); + } + + /** Returns the delay for the most recently posted message. */ + public long getCurrentDelay() { + return mCurrentDelayMs; + } +} diff --git a/tests/net/Android.mk b/tests/net/Android.mk index 3af0adc36bde..677585cc0c0f 100644 --- a/tests/net/Android.mk +++ b/tests/net/Android.mk @@ -42,6 +42,7 @@ LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \ libui \ libunwind \ libutils \ + libvndksupport \ libcrypto \ libhidl-gen-utils \ libhidlbase \ @@ -52,6 +53,7 @@ LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \ libtinyxml2 \ libvintf \ libhwbinder \ + libunwindstack \ android.hidl.token@1.0 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java index bfbb8cc541d7..5008a4188905 100644 --- a/tests/net/java/android/net/apf/ApfTest.java +++ b/tests/net/java/android/net/apf/ApfTest.java @@ -614,9 +614,10 @@ public class ApfTest extends AndroidTestCase { private final long mFixedTimeMs = SystemClock.elapsedRealtime(); public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter, - boolean ieee802_3Filter, IpConnectivityLog log) throws Exception { + boolean ieee802_3Filter, int[] ethTypeBlackList, + IpConnectivityLog log) throws Exception { super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"), - ipManagerCallback, multicastFilter, ieee802_3Filter, log); + ipManagerCallback, multicastFilter, ieee802_3Filter, ethTypeBlackList, log); } // Pretend an RA packet has been received and show it to ApfFilter. @@ -744,9 +745,10 @@ public class ApfTest extends AndroidTestCase { LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); LinkProperties lp = new LinkProperties(); lp.addLinkAddress(link); + final int[] ethTypeBlackList = {}; ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, - ALLOW_802_3_FRAMES, mLog); + ALLOW_802_3_FRAMES, ethTypeBlackList, mLog); apfFilter.setLinkProperties(lp); byte[] program = ipManagerCallback.getApfProgram(); @@ -796,9 +798,10 @@ public class ApfTest extends AndroidTestCase { @SmallTest public void testApfFilterIPv6() throws Exception { + final int[] ethTypeBlackList = {}; MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, - ALLOW_802_3_FRAMES, mLog); + ALLOW_802_3_FRAMES, ethTypeBlackList, mLog); byte[] program = ipManagerCallback.getApfProgram(); // Verify empty IPv6 packet is passed @@ -833,6 +836,7 @@ public class ApfTest extends AndroidTestCase { final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255}; final byte[] multicastIpv4Addr = {(byte)224,0,0,1}; final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb}; + final int[] ethTypeBlackList = {}; MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24); @@ -840,7 +844,7 @@ public class ApfTest extends AndroidTestCase { lp.addLinkAddress(link); ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, - DROP_802_3_FRAMES, mLog); + DROP_802_3_FRAMES, ethTypeBlackList, mLog); apfFilter.setLinkProperties(lp); byte[] program = ipManagerCallback.getApfProgram(); @@ -903,7 +907,7 @@ public class ApfTest extends AndroidTestCase { ipManagerCallback.resetApfProgramWait(); apfFilter.shutdown(); apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, - DROP_802_3_FRAMES, mLog); + DROP_802_3_FRAMES, ethTypeBlackList, mLog); apfFilter.setLinkProperties(lp); program = ipManagerCallback.getApfProgram(); assertDrop(program, mcastv4packet.array()); @@ -924,9 +928,10 @@ public class ApfTest extends AndroidTestCase { LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); LinkProperties lp = new LinkProperties(); lp.addLinkAddress(link); + final int[] ethTypeBlackList = {}; ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, - ALLOW_802_3_FRAMES, mLog); + ALLOW_802_3_FRAMES, ethTypeBlackList, mLog); apfFilter.setLinkProperties(lp); byte[] program = ipManagerCallback.getApfProgram(); @@ -948,7 +953,7 @@ public class ApfTest extends AndroidTestCase { ipManagerCallback.resetApfProgramWait(); apfFilter.shutdown(); apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, - DROP_802_3_FRAMES, mLog); + DROP_802_3_FRAMES, ethTypeBlackList, mLog); apfFilter.setLinkProperties(lp); program = ipManagerCallback.getApfProgram(); @@ -968,6 +973,70 @@ public class ApfTest extends AndroidTestCase { apfFilter.shutdown(); } + @SmallTest + public void testApfFilterEthTypeBL() throws Exception { + MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); + LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); + LinkProperties lp = new LinkProperties(); + lp.addLinkAddress(link); + final int[] emptyBlackList = {}; + final int[] ipv4BlackList = {ETH_P_IP}; + final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6}; + + ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, + ALLOW_802_3_FRAMES, emptyBlackList, mLog); + apfFilter.setLinkProperties(lp); + + byte[] program = ipManagerCallback.getApfProgram(); + + // Verify empty packet of 100 zero bytes is passed + // Note that eth-type = 0 makes it an IEEE802.3 frame + ByteBuffer packet = ByteBuffer.wrap(new byte[100]); + assertPass(program, packet.array()); + + // Verify empty packet with IPv4 is passed + packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); + assertPass(program, packet.array()); + + // Verify empty IPv6 packet is passed + packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); + assertPass(program, packet.array()); + + // Now add IPv4 to the black list + ipManagerCallback.resetApfProgramWait(); + apfFilter.shutdown(); + apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, + ALLOW_802_3_FRAMES, ipv4BlackList, mLog); + apfFilter.setLinkProperties(lp); + program = ipManagerCallback.getApfProgram(); + + // Verify that IPv4 frame will be dropped + packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); + assertDrop(program, packet.array()); + + // Verify that IPv6 frame will pass + packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); + assertPass(program, packet.array()); + + // Now let us have both IPv4 and IPv6 in the black list + ipManagerCallback.resetApfProgramWait(); + apfFilter.shutdown(); + apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, + ALLOW_802_3_FRAMES, ipv4Ipv6BlackList, mLog); + apfFilter.setLinkProperties(lp); + program = ipManagerCallback.getApfProgram(); + + // Verify that IPv4 frame will be dropped + packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); + assertDrop(program, packet.array()); + + // Verify that IPv6 frame will be dropped + packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); + assertDrop(program, packet.array()); + + apfFilter.shutdown(); + } + private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) { cb.resetApfProgramWait(); filter.setLinkProperties(lp); @@ -991,9 +1060,10 @@ public class ApfTest extends AndroidTestCase { @SmallTest public void testApfFilterArp() throws Exception { + final int[] ethTypeBlackList = {}; MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, - DROP_802_3_FRAMES, mLog); + DROP_802_3_FRAMES, ethTypeBlackList, mLog); // Verify initially ARP request filter is off, and GARP filter is on. verifyArpFilter(ipManagerCallback.getApfProgram(), PASS); @@ -1114,8 +1184,9 @@ public class ApfTest extends AndroidTestCase { @SmallTest public void testApfFilterRa() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); + final int[] ethTypeBlackList = {}; TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, - DROP_802_3_FRAMES, mLog); + DROP_802_3_FRAMES, ethTypeBlackList, mLog); byte[] program = ipManagerCallback.getApfProgram(); final int ROUTER_LIFETIME = 1000; @@ -1256,9 +1327,10 @@ public class ApfTest extends AndroidTestCase { public void testRaParsing() throws Exception { final int maxRandomPacketSize = 512; final Random r = new Random(); + final int[] ethTypeBlackList = {}; MockIpManagerCallback cb = new MockIpManagerCallback(); TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST, - DROP_802_3_FRAMES, mLog); + DROP_802_3_FRAMES, ethTypeBlackList, mLog); for (int i = 0; i < 1000; i++) { byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; r.nextBytes(packet); @@ -1275,9 +1347,10 @@ public class ApfTest extends AndroidTestCase { public void testRaProcessing() throws Exception { final int maxRandomPacketSize = 512; final Random r = new Random(); + final int[] ethTypeBlackList = {}; MockIpManagerCallback cb = new MockIpManagerCallback(); TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST, - DROP_802_3_FRAMES, mLog); + DROP_802_3_FRAMES, ethTypeBlackList, mLog); for (int i = 0; i < 1000; i++) { byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; r.nextBytes(packet); diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java index f77608f95b3e..9115378969ca 100644 --- a/tests/net/java/android/net/nsd/NsdManagerTest.java +++ b/tests/net/java/android/net/nsd/NsdManagerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static com.android.internal.util.TestUtils.waitForIdleHandler; import android.os.HandlerThread; import android.os.Handler; @@ -38,6 +39,7 @@ import android.support.test.runner.AndroidJUnit4; import android.os.Message; import android.os.Messenger; import com.android.internal.util.AsyncChannel; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -56,6 +58,8 @@ public class NsdManagerTest { @Mock INsdManager mService; MockServiceHandler mServiceHandler; + NsdManager mManager; + long mTimeoutMs = 100; // non-final so that tests can adjust the value. @Before @@ -64,11 +68,23 @@ public class NsdManagerTest { mServiceHandler = spy(MockServiceHandler.create(mContext)); when(mService.getMessenger()).thenReturn(new Messenger(mServiceHandler)); + + mManager = makeManager(); + } + + @After + public void tearDown() throws Exception { + waitForIdleHandler(mServiceHandler, mTimeoutMs); + mServiceHandler.chan.disconnect(); + mServiceHandler.stop(); + if (mManager != null) { + mManager.disconnect(); + } } @Test public void testResolveService() { - NsdManager manager = makeManager(); + NsdManager manager = mManager; NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); @@ -88,7 +104,7 @@ public class NsdManagerTest { @Test public void testParallelResolveService() { - NsdManager manager = makeManager(); + NsdManager manager = mManager; NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); @@ -111,7 +127,7 @@ public class NsdManagerTest { @Test public void testRegisterService() { - NsdManager manager = makeManager(); + NsdManager manager = mManager; NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type"); NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type"); @@ -170,7 +186,7 @@ public class NsdManagerTest { @Test public void testDiscoverService() { - NsdManager manager = makeManager(); + NsdManager manager = mManager; NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type"); NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type"); @@ -248,7 +264,7 @@ public class NsdManagerTest { @Test public void testInvalidCalls() { - NsdManager manager = new NsdManager(mContext, mService); + NsdManager manager = mManager; NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); @@ -351,6 +367,10 @@ public class NsdManagerTest { } } + public void stop() { + getLooper().quitSafely(); + } + public static MockServiceHandler create(Context context) { HandlerThread t = new HandlerThread("mock-service-handler"); t.start(); diff --git a/tests/net/java/com/android/server/NsdServiceTest.java b/tests/net/java/com/android/server/NsdServiceTest.java index 68cb251cf87e..2e49c98ca9b6 100644 --- a/tests/net/java/com/android/server/NsdServiceTest.java +++ b/tests/net/java/com/android/server/NsdServiceTest.java @@ -95,6 +95,9 @@ public class NsdServiceTest { client2.disconnect(); verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); + + client1.disconnect(); + client2.disconnect(); } @Test @@ -131,6 +134,8 @@ public class NsdServiceTest { // checks that request are cleaned verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4"); + + client.disconnect(); } NsdService makeService() { diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java new file mode 100644 index 000000000000..e3f46a40e2b1 --- /dev/null +++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.net.ConnectivityManager; +import android.net.InterfaceConfiguration; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.NetworkInfo; +import android.os.Handler; +import android.os.INetworkManagementService; +import android.os.test.TestLooper; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.server.ConnectivityService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class Nat464XlatTest { + + static final String BASE_IFACE = "test0"; + static final String STACKED_IFACE = "v4-test0"; + static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29"); + + @Mock ConnectivityService mConnectivity; + @Mock INetworkManagementService mNms; + @Mock InterfaceConfiguration mConfig; + @Mock NetworkAgentInfo mNai; + + TestLooper mLooper; + Handler mHandler; + + Nat464Xlat makeNat464Xlat() { + return new Nat464Xlat(mNms, mNai); + } + + @Before + public void setUp() throws Exception { + mLooper = new TestLooper(); + mHandler = new Handler(mLooper.getLooper()); + + MockitoAnnotations.initMocks(this); + + mNai.linkProperties = new LinkProperties(); + mNai.linkProperties.setInterfaceName(BASE_IFACE); + mNai.networkInfo = new NetworkInfo(null); + mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI); + when(mNai.connService()).thenReturn(mConnectivity); + when(mNai.handler()).thenReturn(mHandler); + + when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig); + when(mConfig.getLinkAddress()).thenReturn(ADDR); + } + + @Test + public void testNormalStartAndStop() throws Exception { + Nat464Xlat nat = makeNat464Xlat(); + ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); + + // ConnectivityService starts clat. + nat.start(); + + verify(mNms).registerObserver(eq(nat)); + verify(mNms).startClatd(eq(BASE_IFACE)); + + // Stacked interface up notification arrives. + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + verify(mNms).getInterfaceConfig(eq(STACKED_IFACE)); + verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false)); + verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertFalse(c.getValue().getStackedLinks().isEmpty()); + assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertRunning(nat); + + // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). + nat.stop(); + + verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true)); + + // Stacked interface removed notification arrives. + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + + verify(mNms).unregisterObserver(eq(nat)); + verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertIdle(nat); + + verifyNoMoreInteractions(mNms, mConnectivity); + } + + @Test + public void testClatdCrashWhileRunning() throws Exception { + Nat464Xlat nat = makeNat464Xlat(); + ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); + + // ConnectivityService starts clat. + nat.start(); + + verify(mNms).registerObserver(eq(nat)); + verify(mNms).startClatd(eq(BASE_IFACE)); + + // Stacked interface up notification arrives. + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + verify(mNms).getInterfaceConfig(eq(STACKED_IFACE)); + verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false)); + verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertFalse(c.getValue().getStackedLinks().isEmpty()); + assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertRunning(nat); + + // Stacked interface removed notification arrives (clatd crashed, ...). + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + + verify(mNms).unregisterObserver(eq(nat)); + verify(mNms).stopClatd(eq(BASE_IFACE)); + verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true)); + verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertIdle(nat); + + // ConnectivityService stops clat: no-op. + nat.stop(); + + verifyNoMoreInteractions(mNms, mConnectivity); + } + + @Test + public void testStopBeforeClatdStarts() throws Exception { + Nat464Xlat nat = makeNat464Xlat(); + + // ConnectivityService starts clat. + nat.start(); + + verify(mNms).registerObserver(eq(nat)); + verify(mNms).startClatd(eq(BASE_IFACE)); + + // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) + nat.stop(); + + verify(mNms).unregisterObserver(eq(nat)); + verify(mNms).stopClatd(eq(BASE_IFACE)); + assertIdle(nat); + + // In-flight interface up notification arrives: no-op + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + + // Interface removed notification arrives after stopClatd() takes effect: no-op. + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + + assertIdle(nat); + + verifyNoMoreInteractions(mNms, mConnectivity); + } + + @Test + public void testStopAndClatdNeverStarts() throws Exception { + Nat464Xlat nat = makeNat464Xlat(); + + // ConnectivityService starts clat. + nat.start(); + + verify(mNms).registerObserver(eq(nat)); + verify(mNms).startClatd(eq(BASE_IFACE)); + + // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) + nat.stop(); + + verify(mNms).unregisterObserver(eq(nat)); + verify(mNms).stopClatd(eq(BASE_IFACE)); + assertIdle(nat); + + verifyNoMoreInteractions(mNms, mConnectivity); + } + + static void assertIdle(Nat464Xlat nat) { + assertTrue("Nat464Xlat was not IDLE", !nat.isStarted()); + } + + static void assertRunning(Nat464Xlat nat) { + assertTrue("Nat464Xlat was not RUNNING", nat.isRunning()); + } +} diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java index d7739518ef2d..2199a134d657 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -32,12 +32,13 @@ import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.content.Context; @@ -68,6 +69,7 @@ import java.util.Set; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.runner.RunWith; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -144,8 +146,8 @@ public class OffloadControllerTest { return offload; } - // TODO: Restore when FakeSettingsProvider.clearSettingsProvider() is available. - // @Test + @Test + @Ignore("Restore when FakeSettingsProvider.clearSettingsProvider() is available.") public void testNoSettingsValueDefaultDisabledDoesNotStart() throws Exception { setupFunctioningHardwareInterface(); when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1); @@ -165,8 +167,8 @@ public class OffloadControllerTest { inOrder.verifyNoMoreInteractions(); } - // TODO: Restore when FakeSettingsProvider.clearSettingsProvider() is available. - // @Test + @Test + @Ignore("Restore when FakeSettingsProvider.clearSettingsProvider() is available.") public void testNoSettingsValueDefaultEnabledDoesStart() throws Exception { setupFunctioningHardwareInterface(); when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0); @@ -440,6 +442,9 @@ public class OffloadControllerTest { ethernetStats.txBytes = 100000; when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats); offload.setUpstreamLinkProperties(null); + // Expect that we first clear the HAL's upstream parameters. + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(""), eq("0.0.0.0"), eq("0.0.0.0"), eq(null)); // Expect that we fetch stats from the previous upstream. inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); @@ -449,8 +454,6 @@ public class OffloadControllerTest { waitForIdle(); // There is no current upstream, so no stats are fetched. inOrder.verify(mHardware, never()).getForwardedStats(any()); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(null), eq(null), eq(null), eq(null)); inOrder.verifyNoMoreInteractions(); assertEquals(2, stats.size()); @@ -625,6 +628,73 @@ public class OffloadControllerTest { inOrder.verifyNoMoreInteractions(); } + @Test + public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception { + setupFunctioningHardwareInterface(); + enableOffload(); + + final OffloadController offload = makeOffloadController(); + offload.start(); + + // Pretend to set a few different upstreams (only the interface name + // matters for this test; we're ignoring IP and route information). + final LinkProperties upstreamLp = new LinkProperties(); + for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) { + upstreamLp.setInterfaceName(ifname); + offload.setUpstreamLinkProperties(upstreamLp); + } + + // Clear invocation history, especially the getForwardedStats() calls + // that happen with setUpstreamParameters(). + clearInvocations(mHardware); + + OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); + callback.onStoppedUnsupported(); + + // Verify forwarded stats behaviour. + verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); + verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); + verifyNoMoreInteractions(mHardware); + verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); + verifyNoMoreInteractions(mNMService); + } + + @Test + public void testControlCallbackOnSupportAvailableFetchesAllStatsAndPushesAllParameters() + throws Exception { + setupFunctioningHardwareInterface(); + enableOffload(); + + final OffloadController offload = makeOffloadController(); + offload.start(); + + // Pretend to set a few different upstreams (only the interface name + // matters for this test; we're ignoring IP and route information). + final LinkProperties upstreamLp = new LinkProperties(); + for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) { + upstreamLp.setInterfaceName(ifname); + offload.setUpstreamLinkProperties(upstreamLp); + } + + // Clear invocation history, especially the getForwardedStats() calls + // that happen with setUpstreamParameters(). + clearInvocations(mHardware); + + OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); + callback.onSupportAvailable(); + + // Verify forwarded stats behaviour. + verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); + verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); + verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); + verifyNoMoreInteractions(mNMService); + + // TODO: verify local prefixes and downstreams are also pushed to the HAL. + verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any()); + verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong()); + verifyNoMoreInteractions(mHardware); + } + private static void assertArrayListContains(ArrayList<String> list, String... elems) { for (String element : elems) { assertTrue(list.contains(element)); diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 53c66a628568..f0ac467bdd18 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -293,6 +293,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["instrumentation"]["meta-data"] = meta_data_action; manifest_action["original-package"]; + manifest_action["overlay"]; manifest_action["protected-broadcast"]; manifest_action["uses-permission"]; manifest_action["uses-permission-sdk-23"]; |