diff options
43 files changed, 1293 insertions, 445 deletions
diff --git a/Android.mk b/Android.mk index 5f759cbd843c..aec1bde15439 100644 --- a/Android.mk +++ b/Android.mk @@ -550,7 +550,6 @@ include $(CLEAR_VARS) aidl_files := \ frameworks/base/telephony/java/android/telephony/mbms/DownloadRequest.aidl \ - frameworks/base/telephony/java/android/telephony/mbms/DownloadStatus.aidl \ frameworks/base/telephony/java/android/telephony/mbms/FileInfo.aidl \ frameworks/base/telephony/java/android/telephony/mbms/FileServiceInfo.aidl \ frameworks/base/telephony/java/android/telephony/mbms/ServiceInfo.aidl \ diff --git a/api/current.txt b/api/current.txt index 1deb3adbeecb..40fd30837fbb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -114,6 +114,7 @@ package android { field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"; field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; + field public static final java.lang.String SEND_EMBMS_INTENTS = "android.permission.SEND_EMBMS_INTENTS"; field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS"; field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM"; @@ -36684,14 +36685,18 @@ package android.system { public final class StructStat { ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long); + ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long); + field public final android.system.StructTimespec st_atim; field public final long st_atime; field public final long st_blksize; field public final long st_blocks; + field public final android.system.StructTimespec st_ctim; field public final long st_ctime; field public final long st_dev; field public final int st_gid; field public final long st_ino; field public final int st_mode; + field public final android.system.StructTimespec st_mtim; field public final long st_mtime; field public final long st_nlink; field public final long st_rdev; @@ -36714,6 +36719,13 @@ package android.system { field public final long f_namemax; } + public final class StructTimespec implements java.lang.Comparable { + ctor public StructTimespec(long, long); + method public int compareTo(android.system.StructTimespec); + field public final long tv_nsec; + field public final long tv_sec; + } + public final class StructUtsname { ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); field public final java.lang.String machine; diff --git a/api/system-current.txt b/api/system-current.txt index 48d2aba3cd21..13e283c46761 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -206,6 +206,7 @@ package android { field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT"; field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS"; field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS"; + field public static final java.lang.String SEND_EMBMS_INTENTS = "android.permission.SEND_EMBMS_INTENTS"; field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS"; field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION"; @@ -39635,14 +39636,18 @@ package android.system { public final class StructStat { ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long); + ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long); + field public final android.system.StructTimespec st_atim; field public final long st_atime; field public final long st_blksize; field public final long st_blocks; + field public final android.system.StructTimespec st_ctim; field public final long st_ctime; field public final long st_dev; field public final int st_gid; field public final long st_ino; field public final int st_mode; + field public final android.system.StructTimespec st_mtim; field public final long st_mtime; field public final long st_nlink; field public final long st_rdev; @@ -39665,6 +39670,13 @@ package android.system { field public final long f_namemax; } + public final class StructTimespec implements java.lang.Comparable { + ctor public StructTimespec(long, long); + method public int compareTo(android.system.StructTimespec); + field public final long tv_nsec; + field public final long tv_sec; + } + public final class StructUtsname { ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); field public final java.lang.String machine; diff --git a/api/test-current.txt b/api/test-current.txt index 5dab3d11f306..9d23dc83ec66 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -114,6 +114,7 @@ package android { field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"; field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; + field public static final java.lang.String SEND_EMBMS_INTENTS = "android.permission.SEND_EMBMS_INTENTS"; field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS"; field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM"; @@ -36767,14 +36768,18 @@ package android.system { public final class StructStat { ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long); + ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long); + field public final android.system.StructTimespec st_atim; field public final long st_atime; field public final long st_blksize; field public final long st_blocks; + field public final android.system.StructTimespec st_ctim; field public final long st_ctime; field public final long st_dev; field public final int st_gid; field public final long st_ino; field public final int st_mode; + field public final android.system.StructTimespec st_mtim; field public final long st_mtime; field public final long st_nlink; field public final long st_rdev; @@ -36797,6 +36802,13 @@ package android.system { field public final long f_namemax; } + public final class StructTimespec implements java.lang.Comparable { + ctor public StructTimespec(long, long); + method public int compareTo(android.system.StructTimespec); + field public final long tv_nsec; + field public final long tv_sec; + } + public final class StructUtsname { ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); field public final java.lang.String machine; diff --git a/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl index feccdce57b98..0da4e8843282 100644 --- a/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl @@ -21,7 +21,7 @@ package android.bluetooth; * * {@hide} */ -interface IBluetoothStateChangeCallback +oneway interface IBluetoothStateChangeCallback { void onBluetoothStateChange(boolean on); } diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java index 4795e979f644..5f01e300bf42 100644 --- a/core/java/android/database/ContentObserver.java +++ b/core/java/android/database/ContentObserver.java @@ -193,11 +193,6 @@ public abstract class ContentObserver { */ private void dispatchChange(boolean selfChange, Uri uri, int userId) { if (mHandler == null) { - synchronized (mLock) { - if (mTransport == null) { - return; - } - } onChange(selfChange, uri, userId); } else { mHandler.post(new NotificationRunnable(selfChange, uri, userId)); @@ -218,11 +213,6 @@ public abstract class ContentObserver { @Override public void run() { - synchronized (mLock) { - if (mTransport == null) { - return; - } - } ContentObserver.this.onChange(mSelfChange, mUri, mUserId); } } diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index 6e74f14bd138..62de9911bc48 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -16,6 +16,15 @@ package android.net; +import static android.system.OsConstants.IFA_F_DADFAILED; +import static android.system.OsConstants.IFA_F_DEPRECATED; +import static android.system.OsConstants.IFA_F_OPTIMISTIC; +import static android.system.OsConstants.IFA_F_TENTATIVE; +import static android.system.OsConstants.RT_SCOPE_HOST; +import static android.system.OsConstants.RT_SCOPE_LINK; +import static android.system.OsConstants.RT_SCOPE_SITE; +import static android.system.OsConstants.RT_SCOPE_UNIVERSE; + import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; @@ -26,15 +35,6 @@ import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.UnknownHostException; -import static android.system.OsConstants.IFA_F_DADFAILED; -import static android.system.OsConstants.IFA_F_DEPRECATED; -import static android.system.OsConstants.IFA_F_OPTIMISTIC; -import static android.system.OsConstants.IFA_F_TENTATIVE; -import static android.system.OsConstants.RT_SCOPE_HOST; -import static android.system.OsConstants.RT_SCOPE_LINK; -import static android.system.OsConstants.RT_SCOPE_SITE; -import static android.system.OsConstants.RT_SCOPE_UNIVERSE; - /** * Identifies an IP address on a network link. * @@ -101,7 +101,7 @@ public class LinkAddress implements Parcelable { * Per RFC 4193 section 8, fc00::/7 identifies these addresses. */ private boolean isIPv6ULA() { - if (address != null && address instanceof Inet6Address) { + if (address instanceof Inet6Address) { byte[] bytes = address.getAddress(); return ((bytes[0] & (byte)0xfe) == (byte)0xfc); } diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java index e0a026ed678d..007846e8c1ae 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -42,10 +42,13 @@ public final class IpManagerEvent implements Parcelable { /** @hide */ public static final int ERROR_STARTING_IPV6 = 5; /** @hide */ public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; + /** @hide */ public static final int ERROR_INVALID_PROVISIONING = 7; + /** {@hide} */ @IntDef(value = { PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE, ERROR_STARTING_IPV4, ERROR_STARTING_IPV6, ERROR_STARTING_IPREACHABILITYMONITOR, + ERROR_INVALID_PROVISIONING, }) @Retention(RetentionPolicy.SOURCE) public @interface EventType {} diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index e05bd89079af..55b6dc817e5c 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -221,28 +221,69 @@ public final class Debug /** @hide */ public static final int OTHER_OTHER_MEMTRACK = 16; + // Needs to be declared here for the DVK_STAT ranges below. + /** @hide */ + public static final int NUM_OTHER_STATS = 17; + + // Dalvik subsections. /** @hide */ public static final int OTHER_DALVIK_NORMAL = 17; /** @hide */ public static final int OTHER_DALVIK_LARGE = 18; /** @hide */ - public static final int OTHER_DALVIK_LINEARALLOC = 19; + public static final int OTHER_DALVIK_ZYGOTE = 19; + /** @hide */ + public static final int OTHER_DALVIK_NON_MOVING = 20; + // Section begins and ends for dumpsys, relative to the DALVIK categories. + /** @hide */ + public static final int OTHER_DVK_STAT_DALVIK_START = + OTHER_DALVIK_NORMAL - NUM_OTHER_STATS; /** @hide */ - public static final int OTHER_DALVIK_ACCOUNTING = 20; + public static final int OTHER_DVK_STAT_DALVIK_END = + OTHER_DALVIK_NON_MOVING - NUM_OTHER_STATS; + + // Dalvik Other subsections. + /** @hide */ + public static final int OTHER_DALVIK_OTHER_LINEARALLOC = 21; + /** @hide */ + public static final int OTHER_DALVIK_OTHER_ACCOUNTING = 22; /** @hide */ - public static final int OTHER_DALVIK_CODE_CACHE = 21; + public static final int OTHER_DALVIK_OTHER_CODE_CACHE = 23; /** @hide */ - public static final int OTHER_DALVIK_ZYGOTE = 22; + public static final int OTHER_DALVIK_OTHER_COMPILER_METADATA = 24; /** @hide */ - public static final int OTHER_DALVIK_NON_MOVING = 23; + public static final int OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE = 25; /** @hide */ - public static final int OTHER_DALVIK_INDIRECT_REFERENCE_TABLE = 24; + public static final int OTHER_DVK_STAT_DALVIK_OTHER_START = + OTHER_DALVIK_OTHER_LINEARALLOC - NUM_OTHER_STATS; + /** @hide */ + public static final int OTHER_DVK_STAT_DALVIK_OTHER_END = + OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE - NUM_OTHER_STATS; + // Dex subsections (Boot vdex, App dex, and App vdex). /** @hide */ - public static final int NUM_OTHER_STATS = 17; + public static final int OTHER_DEX_BOOT_VDEX = 26; + /** @hide */ + public static final int OTHER_DEX_APP_DEX = 27; + /** @hide */ + public static final int OTHER_DEX_APP_VDEX = 28; + /** @hide */ + public static final int OTHER_DVK_STAT_DEX_START = OTHER_DEX_BOOT_VDEX - NUM_OTHER_STATS; + /** @hide */ + public static final int OTHER_DVK_STAT_DEX_END = OTHER_DEX_APP_VDEX - NUM_OTHER_STATS; + + // Art subsections (App image, boot image). + /** @hide */ + public static final int OTHER_ART_APP = 29; + /** @hide */ + public static final int OTHER_ART_BOOT = 30; + /** @hide */ + public static final int OTHER_DVK_STAT_ART_START = OTHER_ART_APP - NUM_OTHER_STATS; + /** @hide */ + public static final int OTHER_DVK_STAT_ART_END = OTHER_ART_BOOT - NUM_OTHER_STATS; /** @hide */ - public static final int NUM_DVK_STATS = 8; + public static final int NUM_DVK_STATS = 14; /** @hide */ public static final int NUM_CATEGORIES = 8; @@ -406,12 +447,18 @@ public final class Debug case OTHER_OTHER_MEMTRACK: return "Other mtrack"; case OTHER_DALVIK_NORMAL: return ".Heap"; case OTHER_DALVIK_LARGE: return ".LOS"; - case OTHER_DALVIK_LINEARALLOC: return ".LinearAlloc"; - case OTHER_DALVIK_ACCOUNTING: return ".GC"; - case OTHER_DALVIK_CODE_CACHE: return ".JITCache"; case OTHER_DALVIK_ZYGOTE: return ".Zygote"; case OTHER_DALVIK_NON_MOVING: return ".NonMoving"; - case OTHER_DALVIK_INDIRECT_REFERENCE_TABLE: return ".IndirectRef"; + case OTHER_DALVIK_OTHER_LINEARALLOC: return ".LinearAlloc"; + case OTHER_DALVIK_OTHER_ACCOUNTING: return ".GC"; + case OTHER_DALVIK_OTHER_CODE_CACHE: return ".JITCache"; + case OTHER_DALVIK_OTHER_COMPILER_METADATA: return ".CompilerMetadata"; + case OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE: return ".IndirectRef"; + case OTHER_DEX_BOOT_VDEX: return ".Boot vdex"; + case OTHER_DEX_APP_DEX: return ".App dex"; + case OTHER_DEX_APP_VDEX: return ".App vdex"; + case OTHER_ART_APP: return ".App art"; + case OTHER_ART_BOOT: return ".Boot art"; default: return "????"; } } diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index b09c51c50d26..866e20c26a0c 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -51,6 +51,11 @@ public abstract class HwBinder implements IHwBinder { String serviceName) throws RemoteException, NoSuchElementException; + public static native final void configureRpcThreadpool( + long maxThreads, boolean callerWillJoin); + + public static native final void joinRpcThreadpool(); + // Returns address of the "freeFunction". private static native final long native_init(); diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 821d0e515d09..120962390ef3 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -75,14 +75,27 @@ enum { HEAP_GL, HEAP_OTHER_MEMTRACK, + // Dalvik extra sections (heap). HEAP_DALVIK_NORMAL, HEAP_DALVIK_LARGE, - HEAP_DALVIK_LINEARALLOC, - HEAP_DALVIK_ACCOUNTING, - HEAP_DALVIK_CODE_CACHE, HEAP_DALVIK_ZYGOTE, HEAP_DALVIK_NON_MOVING, - HEAP_DALVIK_INDIRECT_REFERENCE_TABLE, + + // Dalvik other extra sections. + HEAP_DALVIK_OTHER_LINEARALLOC, + HEAP_DALVIK_OTHER_ACCOUNTING, + HEAP_DALVIK_OTHER_CODE_CACHE, + HEAP_DALVIK_OTHER_COMPILER_METADATA, + HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE, + + // Boot vdex / app dex / app vdex + HEAP_DEX_BOOT_VDEX, + HEAP_DEX_APP_DEX, + HEAP_DEX_APP_VDEX, + + // App art, boot art. + HEAP_ART_APP, + HEAP_ART_BOOT, _NUM_HEAP, _NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1, @@ -297,15 +310,30 @@ static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss) whichHeap = HEAP_TTF; is_swappable = true; } else if ((nameLen > 4 && strstr(name, ".dex") != NULL) || - (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0) || - (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0)) { + (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) { whichHeap = HEAP_DEX; + subHeap = HEAP_DEX_APP_DEX; + is_swappable = true; + } else if (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0) { + whichHeap = HEAP_DEX; + // Handle system@framework@boot* and system/framework/boot* + if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) { + subHeap = HEAP_DEX_BOOT_VDEX; + } else { + subHeap = HEAP_DEX_APP_VDEX; + } is_swappable = true; } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) { whichHeap = HEAP_OAT; is_swappable = true; } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) { whichHeap = HEAP_ART; + // Handle system@framework@boot* and system/framework/boot* + if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) { + subHeap = HEAP_ART_BOOT; + } else { + subHeap = HEAP_ART_APP; + } is_swappable = true; } else if (strncmp(name, "/dev/", 5) == 0) { if (strncmp(name, "/dev/kgsl-3d0", 13) == 0) { @@ -314,7 +342,7 @@ static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss) if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) { whichHeap = HEAP_DALVIK_OTHER; if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) { - subHeap = HEAP_DALVIK_LINEARALLOC; + subHeap = HEAP_DALVIK_OTHER_LINEARALLOC; } else if ((strstr(name, "/dev/ashmem/dalvik-alloc space") == name) || (strstr(name, "/dev/ashmem/dalvik-main space") == name)) { // This is the regular Dalvik heap. @@ -332,13 +360,14 @@ static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss) whichHeap = HEAP_DALVIK; subHeap = HEAP_DALVIK_ZYGOTE; } else if (strstr(name, "/dev/ashmem/dalvik-indirect ref") == name) { - subHeap = HEAP_DALVIK_INDIRECT_REFERENCE_TABLE; + subHeap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE; } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name || - strstr(name, "/dev/ashmem/dalvik-data-code-cache") == name || - strstr(name, "/dev/ashmem/dalvik-CompilerMetadata") == name) { - subHeap = HEAP_DALVIK_CODE_CACHE; + strstr(name, "/dev/ashmem/dalvik-data-code-cache") == name) { + subHeap = HEAP_DALVIK_OTHER_CODE_CACHE; + } else if (strstr(name, "/dev/ashmem/dalvik-CompilerMetadata") == name) { + subHeap = HEAP_DALVIK_OTHER_COMPILER_METADATA; } else { - subHeap = HEAP_DALVIK_ACCOUNTING; // Default to accounting. + subHeap = HEAP_DALVIK_OTHER_ACCOUNTING; // Default to accounting. } } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) { whichHeap = HEAP_CURSOR; @@ -423,7 +452,8 @@ static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss) stats[whichHeap].sharedClean += shared_clean; stats[whichHeap].swappedOut += swapped_out; stats[whichHeap].swappedOutPss += swapped_out_pss; - if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) { + if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER || + whichHeap == HEAP_DEX || whichHeap == HEAP_ART) { stats[subHeap].pss += pss; stats[subHeap].swappablePss += swappable_pss; stats[subHeap].privateDirty += private_dirty; diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index 4fc8e1109bc0..dc16220e7f84 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -42,6 +42,8 @@ using android::AndroidRuntime; using android::hardware::hidl_vec; using android::hardware::hidl_string; +using android::hardware::IPCThreadState; +using android::hardware::ProcessState; template<typename T> using Return = android::hardware::Return<T>; @@ -393,6 +395,15 @@ static jobject JHwBinder_native_getService( return JHwRemoteBinder::NewObject(env, service); } +void JHwBinder_native_configureRpcThreadpool(jlong maxThreads, jboolean callerWillJoin) { + CHECK(maxThreads > 0); + ProcessState::self()->setThreadPoolConfiguration(maxThreads, callerWillJoin /*callerJoinsPool*/); +} + +void JHwBinder_native_joinRpcThreadpool() { + IPCThreadState::self()->joinThreadPool(); +} + static JNINativeMethod gMethods[] = { { "native_init", "()J", (void *)JHwBinder_native_init }, { "native_setup", "()V", (void *)JHwBinder_native_setup }, @@ -406,6 +417,12 @@ static JNINativeMethod gMethods[] = { { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;", (void *)JHwBinder_native_getService }, + + { "configureRpcThreadpool", "(JZ)V", + (void *)JHwBinder_native_configureRpcThreadpool }, + + { "joinRpcThreadpool", "()V", + (void *)JHwBinder_native_joinRpcThreadpool }, }; namespace android { diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp index 2a3f0361a9cd..3b97a5e6442e 100644 --- a/core/jni/android_text_AndroidBidi.cpp +++ b/core/jni/android_text_AndroidBidi.cpp @@ -38,7 +38,7 @@ static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray, if (info != NULL) { UErrorCode status = U_ZERO_ERROR; UBiDi* bidi = ubidi_openSized(n, 0, &status); - ubidi_setPara(bidi, chs, n, dir, NULL, &status); + ubidi_setPara(bidi, reinterpret_cast<const UChar*>(chs), n, dir, NULL, &status); if (U_SUCCESS(status)) { for (int i = 0; i < n; ++i) { info[i] = ubidi_getLevelAt(bidi, i); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d2154168eef2..387eb1d4201d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1605,6 +1605,10 @@ <permission android:name="android.permission.RECEIVE_STK_COMMANDS" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to send EMBMS download intents to apps--> + <permission android:name="android.permission.SEND_EMBMS_INTENTS" + android:protectionLevel="signature|privileged" /> + <!-- Must be required by an ImsService to ensure that only the system can bind to it. <p>Protection level: signature|privileged diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index b88bbc1bdd69..2f9b8618cba6 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -206,7 +206,11 @@ public class IpSecService extends IIpSecService.Stub { T get(int key) { T val = mArray.get(key); - val.checkOwnerOrSystemAndThrow(); + // The value should never be null unless the resource doesn't exist + // (since we do not allow null resources to be added). + if (val != null) { + val.checkOwnerOrSystemAndThrow(); + } return val; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c5fc038028e2..784a7104bba4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16447,23 +16447,41 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<MemItem> catMems = new ArrayList<MemItem>(); catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1)); - final MemItem dalvikItem = - new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, -2); - if (dalvikSubitemPss.length > 0) { - dalvikItem.subitems = new ArrayList<MemItem>(); - for (int j=0; j<dalvikSubitemPss.length; j++) { - final String name = Debug.MemoryInfo.getOtherLabel( - Debug.MemoryInfo.NUM_OTHER_STATS + j); - dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], - dalvikSubitemSwapPss[j], j)); - } - } - catMems.add(dalvikItem); + final int dalvikId = -2; + catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikId)); catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3)); for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { String label = Debug.MemoryInfo.getOtherLabel(j); catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j)); } + if (dalvikSubitemPss.length > 0) { + // Add dalvik subitems. + for (MemItem memItem : catMems) { + int memItemStart = 0, memItemEnd = 0; + if (memItem.id == dalvikId) { + memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_START; + memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_END; + } else if (memItem.id == Debug.MemoryInfo.OTHER_DALVIK_OTHER) { + memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_START; + memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_END; + } else if (memItem.id == Debug.MemoryInfo.OTHER_DEX) { + memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_START; + memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_END; + } else if (memItem.id == Debug.MemoryInfo.OTHER_ART) { + memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_ART_START; + memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_ART_END; + } else { + continue; // No subitems, continue. + } + memItem.subitems = new ArrayList<MemItem>(); + for (int j=memItemStart; j<=memItemEnd; j++) { + final String name = Debug.MemoryInfo.getOtherLabel( + Debug.MemoryInfo.NUM_OTHER_STATS + j); + memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], + dalvikSubitemSwapPss[j], j)); + } + } + } ArrayList<MemItem> oomMems = new ArrayList<MemItem>(); for (int j=0; j<oomPss.length; j++) { diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index ae385c114afe..3a4e07e96ad7 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -57,6 +57,7 @@ import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.net.wifi.WifiManager; import android.os.Binder; @@ -213,10 +214,10 @@ public class Tethering extends BaseNetworkObserver { mContext.getContentResolver(), mLog); mUpstreamNetworkMonitor = new UpstreamNetworkMonitor( - mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK ); + mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); mSimChange = new SimChangeListener( - mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning()); + mContext, smHandler, () -> reevaluateSimCardProvisioning()); mStateReceiver = new StateReceiver(); IntentFilter filter = new IntentFilter(); @@ -224,13 +225,13 @@ public class Tethering extends BaseNetworkObserver { filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler()); + mContext.registerReceiver(mStateReceiver, filter, null, smHandler); filter = new IntentFilter(); filter.addAction(Intent.ACTION_MEDIA_SHARED); filter.addAction(Intent.ACTION_MEDIA_UNSHARED); filter.addDataScheme("file"); - mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler()); + mContext.registerReceiver(mStateReceiver, filter, null, smHandler); // load device config info updateConfiguration(); @@ -252,6 +253,12 @@ public class Tethering extends BaseNetworkObserver { mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); } + private void maybeUpdateConfiguration() { + final int dunCheck = TetheringConfiguration.checkDunRequired(mContext); + if (dunCheck == mConfig.dunCheck) return; + updateConfiguration(); + } + @Override public void interfaceStatusChanged(String iface, boolean up) { // Never called directly: only called from interfaceLinkStateChanged. @@ -1126,12 +1133,6 @@ public class Tethering extends BaseNetworkObserver { } } - private void startOffloadController() { - mOffloadController.start(); - mOffloadController.updateExemptPrefixes( - mUpstreamNetworkMonitor.getOffloadExemptPrefixes()); - } - class TetherMasterSM extends StateMachine { private static final int BASE_MASTER = Protocol.BASE_TETHERING; // an interface SM has requested Tethering/Local Hotspot @@ -1149,14 +1150,14 @@ public class Tethering extends BaseNetworkObserver { static final int CMD_CLEAR_ERROR = BASE_MASTER + 6; static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7; - private State mInitialState; - private State mTetherModeAliveState; + private final State mInitialState; + private final State mTetherModeAliveState; - private State mSetIpForwardingEnabledErrorState; - private State mSetIpForwardingDisabledErrorState; - private State mStartTetheringErrorState; - private State mStopTetheringErrorState; - private State mSetDnsForwardersErrorState; + private final State mSetIpForwardingEnabledErrorState; + private final State mSetIpForwardingDisabledErrorState; + private final State mStartTetheringErrorState; + private final State mStopTetheringErrorState; + private final State mSetDnsForwardersErrorState; // This list is a little subtle. It contains all the interfaces that currently are // requesting tethering, regardless of whether these interfaces are still members of @@ -1196,22 +1197,46 @@ public class Tethering extends BaseNetworkObserver { mNotifyList = new ArrayList<>(); mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog); + setInitialState(mInitialState); } + private void startOffloadController() { + mOffloadController.start(); + sendOffloadExemptPrefixes(); + } + + private void sendOffloadExemptPrefixes() { + sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes()); + } + + private void sendOffloadExemptPrefixes(Set<IpPrefix> localPrefixes) { + // Add in well-known minimum set. + PrefixUtils.addNonForwardablePrefixes(localPrefixes); + // Add tragically hardcoded prefixes. + localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX); + + // Add prefixes for all downstreams, regardless of IP serving mode. + for (TetherInterfaceStateMachine tism : mNotifyList) { + localPrefixes.addAll(PrefixUtils.localPrefixesFrom(tism.linkProperties())); + } + + mOffloadController.setLocalPrefixes(localPrefixes); + } + class InitialState extends State { @Override public boolean processMessage(Message message) { logMessage(this, message.what); switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); transitionTo(mTetherModeAliveState); break; case EVENT_IFACE_SERVING_STATE_INACTIVE: - who = (TetherInterfaceStateMachine)message.obj; + who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); break; @@ -1273,7 +1298,9 @@ public class Tethering extends BaseNetworkObserver { } protected void chooseUpstreamType(boolean tryCell) { - updateConfiguration(); // TODO - remove? + // We rebuild configuration on ACTION_CONFIGURATION_CHANGED, but we + // do not currently know how to watch for changes in DUN settings. + maybeUpdateConfiguration(); final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType( mConfig.preferredUpstreamIfaceTypes); @@ -1404,8 +1431,8 @@ public class Tethering extends BaseNetworkObserver { } private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { - if (arg1 == UpstreamNetworkMonitor.NOTIFY_EXEMPT_PREFIXES) { - mOffloadController.updateExemptPrefixes((Set<IpPrefix>) o); + if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { + sendOffloadExemptPrefixes((Set<IpPrefix>) o); return; } @@ -1509,7 +1536,7 @@ public class Tethering extends BaseNetworkObserver { boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: { - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, @@ -1523,7 +1550,7 @@ public class Tethering extends BaseNetworkObserver { break; } case EVENT_IFACE_SERVING_STATE_INACTIVE: { - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); @@ -1555,6 +1582,9 @@ public class Tethering extends BaseNetworkObserver { mOffloadController.notifyDownstreamLinkProperties(newLp); } else { mOffloadController.removeDownstreamInterface(newLp.getInterfaceName()); + // Another interface might be in local-only hotspot mode; + // resend all local prefixes to the OffloadController. + sendOffloadExemptPrefixes(); } break; } @@ -1596,7 +1626,7 @@ public class Tethering extends BaseNetworkObserver { boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; who.sendMessage(mErrorNotification); break; case CMD_CLEAR_ERROR: 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 a69e4caebd5d..b47386705a36 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -20,6 +20,7 @@ import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import android.content.ContentResolver; import android.net.IpPrefix; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; import android.net.util.SharedLog; @@ -27,8 +28,11 @@ import android.os.Handler; import android.provider.Settings; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Objects; import java.util.Set; /** @@ -47,7 +51,13 @@ public class OffloadController { private boolean mConfigInitialized; private boolean mControlInitialized; private LinkProperties mUpstreamLinkProperties; + // The complete set of offload-exempt prefixes passed in via Tethering from + // all upstream and downstream sources. private Set<IpPrefix> mExemptPrefixes; + // A strictly "smaller" set of prefixes, wherein offload-approved prefixes + // (e.g. downstream on-link prefixes) have been removed and replaced with + // prefixes representing only the locally-assigned IP addresses. + private Set<String> mLastLocalPrefixStrs; public OffloadController(Handler h, OffloadHardwareInterface hwi, ContentResolver contentResolver, SharedLog log) { @@ -55,6 +65,8 @@ public class OffloadController { mHwInterface = hwi; mContentResolver = contentResolver; mLog = log.forSubComponent(TAG); + mExemptPrefixes = new HashSet<>(); + mLastLocalPrefixStrs = new HashSet<>(); } public void start() { @@ -77,8 +89,36 @@ public class OffloadController { mControlInitialized = mHwInterface.initOffloadControl( new OffloadHardwareInterface.ControlCallback() { @Override - public void onOffloadEvent(int event) { - mLog.log("got offload event: " + event); + public void onStarted() { + mLog.log("onStarted"); + } + + @Override + public void onStoppedError() { + mLog.log("onStoppedError"); + } + + @Override + public void onStoppedUnsupported() { + mLog.log("onStoppedUnsupported"); + } + + @Override + public void onSupportAvailable() { + 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 + pushUpstreamParameters(); + } + + @Override + public void onStoppedLimitReached() { + mLog.log("onStoppedLimitReached"); + // Poll for statistics and notify NetworkStats } @Override @@ -106,25 +146,22 @@ public class OffloadController { } public void setUpstreamLinkProperties(LinkProperties lp) { - if (!started()) return; + if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; // TODO: examine return code and decide what to do if programming // upstream parameters fails (probably just wait for a subsequent // onOffloadEvent() callback to tell us offload is available again and // then reapply all state). + computeAndPushLocalPrefixes(); pushUpstreamParameters(); } - public void updateExemptPrefixes(Set<IpPrefix> exemptPrefixes) { + public void setLocalPrefixes(Set<IpPrefix> localPrefixes) { if (!started()) return; - mExemptPrefixes = exemptPrefixes; - // TODO: - // - add IP addresses from all downstream link properties - // - add routes from all non-tethering downstream link properties - // - remove any 64share prefixes - // - push this to the HAL + mExemptPrefixes = localPrefixes; + computeAndPushLocalPrefixes(); } public void notifyDownstreamLinkProperties(LinkProperties lp) { @@ -187,4 +224,42 @@ public class OffloadController { return mHwInterface.setUpstreamParameters( iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways)); } + + private boolean computeAndPushLocalPrefixes() { + final Set<String> localPrefixStrs = computeLocalPrefixStrings( + mExemptPrefixes, mUpstreamLinkProperties); + if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; + + mLastLocalPrefixStrs = localPrefixStrs; + return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs)); + } + + // TODO: Factor in downstream LinkProperties once that information is available. + private static Set<String> computeLocalPrefixStrings( + Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) { + // Create an editable copy. + final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes); + + // TODO: If a downstream interface (not currently passed in) is reusing + // the /64 of the upstream (64share) then: + // + // [a] remove that /64 from the local prefixes + // [b] add in /128s for IP addresses on the downstream interface + // [c] add in /128s for IP addresses on the upstream interface + // + // Until downstream information is available here, simply add /128s from + // the upstream network; they'll just be redundant with their /64. + if (upstreamLinkProperties != null) { + for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) { + if (!linkAddr.isGlobalPreferred()) continue; + final InetAddress ip = linkAddr.getAddress(); + if (!(ip instanceof Inet6Address)) continue; + prefixSet.add(new IpPrefix(ip, 128)); + } + } + + final HashSet<String> localPrefixStrs = new HashSet<>(); + for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString()); + return localPrefixStrs; + } } diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index 913096cb5bef..4df566f03d6d 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -21,6 +21,7 @@ import static com.android.internal.util.BitUtils.uint16; import android.hardware.tetheroffload.control.V1_0.IOffloadControl; import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; +import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; import android.os.Handler; import android.os.RemoteException; import android.net.util.SharedLog; @@ -53,7 +54,11 @@ public class OffloadHardwareInterface { private ControlCallback mControlCallback; public static class ControlCallback { - public void onOffloadEvent(int event) {} + public void onStarted() {} + public void onStoppedError() {} + public void onStoppedUnsupported() {} + public void onSupportAvailable() {} + public void onStoppedLimitReached() {} public void onNatTimeoutUpdate(int proto, String srcAddr, int srcPort, @@ -108,7 +113,7 @@ public class OffloadHardwareInterface { (controlCb == null) ? "null" : "0x" + Integer.toHexString(System.identityHashCode(controlCb))); - mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback); + mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog); final CbResults results = new CbResults(); try { mOffloadControl.initOffload( @@ -163,6 +168,26 @@ public class OffloadHardwareInterface { return stats; } + public boolean setLocalPrefixes(ArrayList<String> localPrefixes) { + final String logmsg = String.format("setLocalPrefixes([%s])", + String.join(",", localPrefixes)); + + final CbResults results = new CbResults(); + try { + mOffloadControl.setLocalPrefixes(localPrefixes, + (boolean success, String errMsg) -> { + results.success = success; + results.errMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.success; + } + public boolean setUpstreamParameters( String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) { iface = (iface != null) ? iface : NO_INTERFACE_NAME; @@ -206,15 +231,37 @@ public class OffloadHardwareInterface { private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { public final Handler handler; public final ControlCallback controlCb; + public final SharedLog log; - public TetheringOffloadCallback(Handler h, ControlCallback cb) { + public TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) { handler = h; controlCb = cb; + log = sharedLog; } @Override public void onEvent(int event) { - handler.post(() -> { controlCb.onOffloadEvent(event); }); + handler.post(() -> { + switch (event) { + case OffloadCallbackEvent.OFFLOAD_STARTED: + controlCb.onStarted(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: + controlCb.onStoppedError(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: + controlCb.onStoppedUnsupported(); + break; + case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: + controlCb.onSupportAvailable(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: + controlCb.onStoppedLimitReached(); + break; + default: + log.e("Unsupported OffloadCallbackEvent: " + event); + } + }); } @Override 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 4bac69ce7495..69678df4543d 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -161,6 +161,8 @@ public class TetherInterfaceStateMachine extends StateMachine { public int lastError() { return mLastError; } + public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); } + public void stop() { sendMessage(CMD_INTERFACE_DOWN); } public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java index 9b034aef1e95..eb18b1220f5b 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -70,6 +70,7 @@ public class TetheringConfiguration { public final String[] tetherableUsbRegexs; public final String[] tetherableWifiRegexs; public final String[] tetherableBluetoothRegexs; + public final int dunCheck; public final boolean isDunRequired; public final Collection<Integer> preferredUpstreamIfaceTypes; public final String[] dhcpRanges; @@ -85,7 +86,7 @@ public class TetheringConfiguration { tetherableBluetoothRegexs = ctx.getResources().getStringArray( com.android.internal.R.array.config_tether_bluetooth_regexs); - final int dunCheck = checkDunRequired(ctx); + dunCheck = checkDunRequired(ctx); configLog.log("DUN check returned: " + dunCheckString(dunCheck)); preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck); @@ -172,7 +173,7 @@ public class TetheringConfiguration { return upstreamNames; } - private static int checkDunRequired(Context ctx) { + public static int checkDunRequired(Context ctx) { final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); return (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED; } diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index eb66767bfdfa..c5f752807cb7 100644 --- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -34,6 +34,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.NetworkState; import android.net.util.NetworkConstants; +import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.util.Log; @@ -72,16 +73,11 @@ public class UpstreamNetworkMonitor { private static final boolean DBG = false; private static final boolean VDBG = false; - private static final IpPrefix[] MINIMUM_LOCAL_PREFIXES_SET = { - prefix("127.0.0.0/8"), prefix("169.254.0.0/16"), - prefix("::/3"), prefix("fe80::/64"), prefix("fc00::/7"), prefix("ff00::/8"), - }; - public static final int EVENT_ON_AVAILABLE = 1; public static final int EVENT_ON_CAPABILITIES = 2; public static final int EVENT_ON_LINKPROPERTIES = 3; public static final int EVENT_ON_LOST = 4; - public static final int NOTIFY_EXEMPT_PREFIXES = 10; + public static final int NOTIFY_LOCAL_PREFIXES = 10; private static final int CALLBACK_LISTEN_ALL = 1; private static final int CALLBACK_TRACK_DEFAULT = 2; @@ -93,7 +89,7 @@ public class UpstreamNetworkMonitor { private final Handler mHandler; private final int mWhat; private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>(); - private HashSet<IpPrefix> mOffloadExemptPrefixes; + private HashSet<IpPrefix> mLocalPrefixes; private ConnectivityManager mCM; private NetworkCallback mListenAllCallback; private NetworkCallback mDefaultNetworkCallback; @@ -107,7 +103,7 @@ public class UpstreamNetworkMonitor { mHandler = mTarget.getHandler(); mLog = log.forSubComponent(TAG); mWhat = what; - mOffloadExemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values()); + mLocalPrefixes = new HashSet<>(); } @VisibleForTesting @@ -223,8 +219,8 @@ public class UpstreamNetworkMonitor { return typeStatePair.ns; } - public Set<IpPrefix> getOffloadExemptPrefixes() { - return (Set<IpPrefix>) mOffloadExemptPrefixes.clone(); + public Set<IpPrefix> getLocalPrefixes() { + return (Set<IpPrefix>) mLocalPrefixes.clone(); } private void handleAvailable(int callbackType, Network network) { @@ -360,11 +356,11 @@ public class UpstreamNetworkMonitor { notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network)); } - private void recomputeOffloadExemptPrefixes() { - final HashSet<IpPrefix> exemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values()); - if (!mOffloadExemptPrefixes.equals(exemptPrefixes)) { - mOffloadExemptPrefixes = exemptPrefixes; - notifyTarget(NOTIFY_EXEMPT_PREFIXES, exemptPrefixes.clone()); + private void recomputeLocalPrefixes() { + final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values()); + if (!mLocalPrefixes.equals(localPrefixes)) { + mLocalPrefixes = localPrefixes; + notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone()); } } @@ -402,7 +398,7 @@ public class UpstreamNetworkMonitor { @Override public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { handleLinkProp(network, newLp); - recomputeOffloadExemptPrefixes(); + recomputeLocalPrefixes(); } // TODO: Handle onNetworkSuspended(); @@ -411,7 +407,7 @@ public class UpstreamNetworkMonitor { @Override public void onLost(Network network) { handleLost(mCallbackType, network); - recomputeOffloadExemptPrefixes(); + recomputeLocalPrefixes(); } } @@ -460,35 +456,15 @@ public class UpstreamNetworkMonitor { return result; } - private static HashSet<IpPrefix> allOffloadExemptPrefixes(Iterable<NetworkState> netStates) { + private static HashSet<IpPrefix> allLocalPrefixes(Iterable<NetworkState> netStates) { final HashSet<IpPrefix> prefixSet = new HashSet<>(); - addDefaultLocalPrefixes(prefixSet); - for (NetworkState ns : netStates) { - addOffloadExemptPrefixes(prefixSet, ns.linkProperties); + final LinkProperties lp = ns.linkProperties; + if (lp == null) continue; + prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp)); } return prefixSet; } - - private static void addDefaultLocalPrefixes(Set<IpPrefix> prefixSet) { - Collections.addAll(prefixSet, MINIMUM_LOCAL_PREFIXES_SET); - } - - private static void addOffloadExemptPrefixes(Set<IpPrefix> prefixSet, LinkProperties lp) { - if (lp == null) return; - - for (LinkAddress linkAddr : lp.getAllLinkAddresses()) { - prefixSet.add(new IpPrefix(linkAddr.getAddress(), linkAddr.getPrefixLength())); - } - - // TODO: Consider adding other non-default routes associated with this - // network. Traffic to these destinations should perhaps not go through - // the Internet (upstream). - } - - private static IpPrefix prefix(String prefixStr) { - return new IpPrefix(prefixStr); - } } diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java index e8dfd779a715..9b4999667c88 100644 --- a/services/core/java/com/android/server/timezone/PackageTracker.java +++ b/services/core/java/com/android/server/timezone/PackageTracker.java @@ -164,33 +164,29 @@ public class PackageTracker implements IntentHelper.Listener { } // Validate the updater application package. - // TODO(nfuller) Uncomment or remove the code below. Currently an app stops being a priv-app - // after it is replaced by one in data so this check fails. http://b/35995024 - // try { - // if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) { - // throw failWithException( - // "Update app " + mUpdateAppPackageName + " must be a priv-app.", null); - // } - // } catch (PackageManager.NameNotFoundException e) { - // throw failWithException("Could not determine update app package details for " - // + mUpdateAppPackageName, e); - // } + try { + if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) { + throw logAndThrowRuntimeException( + "Update app " + mUpdateAppPackageName + " must be a priv-app.", null); + } + } catch (PackageManager.NameNotFoundException e) { + throw logAndThrowRuntimeException("Could not determine update app package details for " + + mUpdateAppPackageName, e); + } // TODO(nfuller) Consider permission checks. While an updated system app retains permissions // obtained by the system version it's not clear how to check them. Slog.d(TAG, "Update app " + mUpdateAppPackageName + " is valid."); // Validate the data application package. - // TODO(nfuller) Uncomment or remove the code below. Currently an app stops being a priv-app - // after it is replaced by one in data. http://b/35995024 - // try { - // if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) { - // throw failWithException( - // "Data app " + mDataAppPackageName + " must be a priv-app.", null); - // } - // } catch (PackageManager.NameNotFoundException e) { - // throw failWithException("Could not determine data app package details for " - // + mDataAppPackageName, e); - // } + try { + if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) { + throw logAndThrowRuntimeException( + "Data app " + mDataAppPackageName + " must be a priv-app.", null); + } + } catch (PackageManager.NameNotFoundException e) { + throw logAndThrowRuntimeException("Could not determine data app package details for " + + mDataAppPackageName, e); + } // TODO(nfuller) Consider permission checks. While an updated system app retains permissions // obtained by the system version it's not clear how to check them. Slog.d(TAG, "Data app " + mDataAppPackageName + " is valid."); diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index b2930a4d536d..adaf59974caf 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -23,6 +23,7 @@ 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; import android.net.LinkProperties; @@ -35,6 +36,7 @@ import android.net.dhcp.DhcpClient; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpManagerEvent; import android.net.util.MultinetworkPolicyTracker; +import android.net.util.NetworkConstants; import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.Message; @@ -51,17 +53,25 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IState; +import com.android.internal.util.Preconditions; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.net.NetlinkTracker; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; +import java.util.Collection; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import java.util.StringJoiner; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** @@ -308,6 +318,11 @@ public class IpManager extends StateMachine { return this; } + public Builder withInitialConfiguration(InitialConfiguration initialConfig) { + mConfig.mInitialConfig = initialConfig; + return this; + } + public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { mConfig.mStaticIpConfig = staticConfig; return this; @@ -342,18 +357,20 @@ public class IpManager extends StateMachine { /* package */ boolean mEnableIPv6 = true; /* package */ boolean mUsingIpReachabilityMonitor = true; /* package */ int mRequestedPreDhcpActionMs; + /* package */ InitialConfiguration mInitialConfig; /* package */ StaticIpConfiguration mStaticIpConfig; /* package */ ApfCapabilities mApfCapabilities; /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; - public ProvisioningConfiguration() {} + public ProvisioningConfiguration() {} // used by Builder public ProvisioningConfiguration(ProvisioningConfiguration other) { mEnableIPv4 = other.mEnableIPv4; mEnableIPv6 = other.mEnableIPv6; mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs; + mInitialConfig = InitialConfiguration.copy(other.mInitialConfig); mStaticIpConfig = other.mStaticIpConfig; mApfCapabilities = other.mApfCapabilities; mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; @@ -366,12 +383,124 @@ public class IpManager extends StateMachine { .add("mEnableIPv6: " + mEnableIPv6) .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor) .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs) + .add("mInitialConfig: " + mInitialConfig) .add("mStaticIpConfig: " + mStaticIpConfig) .add("mApfCapabilities: " + mApfCapabilities) .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) .toString(); } + + public boolean isValid() { + return (mInitialConfig == null) || mInitialConfig.isValid(); + } + } + + public static class InitialConfiguration { + public final Set<LinkAddress> ipAddresses = new HashSet<>(); + public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>(); + public final Set<InetAddress> dnsServers = new HashSet<>(); + public Inet4Address gateway; // WiFi legacy behavior with static ipv4 config + + public static InitialConfiguration copy(InitialConfiguration config) { + if (config == null) { + return null; + } + InitialConfiguration configCopy = new InitialConfiguration(); + configCopy.ipAddresses.addAll(config.ipAddresses); + configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes); + configCopy.dnsServers.addAll(config.dnsServers); + return configCopy; + } + + @Override + public String toString() { + return String.format( + "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s}, v4 gateway: %s)", + join(", ", ipAddresses), join(", ", directlyConnectedRoutes), + join(", ", dnsServers), gateway); + } + + public boolean isValid() { + // For every IP address, there must be at least one prefix containing that address. + for (LinkAddress addr : ipAddresses) { + if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) { + return false; + } + } + // For every dns server, there must be at least one prefix containing that address. + for (InetAddress addr : dnsServers) { + if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) { + return false; + } + } + // All IPv6 LinkAddresses have an RFC7421-suitable prefix length + // (read: compliant with RFC4291#section2.5.4). + if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) { + return false; + } + // If directlyConnectedRoutes contains an IPv6 default route + // then ipAddresses MUST contain at least one non-ULA GUA. + if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute) + && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) { + return false; + } + // The prefix length of routes in directlyConnectedRoutes be within reasonable + // bounds for IPv6: /48-/64 just as we’d accept in RIOs. + if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) { + return false; + } + // There no more than one IPv4 address + if (ipAddresses.stream().filter(Inet4Address.class::isInstance).count() > 1) { + return false; + } + + return true; + } + + private static boolean isPrefixLengthCompliant(LinkAddress addr) { + return (addr.getAddress() instanceof Inet4Address) + || isCompliantIPv6PrefixLength(addr.getPrefixLength()); + } + + private static boolean isPrefixLengthCompliant(IpPrefix prefix) { + return (prefix.getAddress() instanceof Inet4Address) + || isCompliantIPv6PrefixLength(prefix.getPrefixLength()); + } + + private static boolean isCompliantIPv6PrefixLength(int prefixLength) { + return (NetworkConstants.RFC6177_MIN_PREFIX_LENGTH <= prefixLength) + && (prefixLength <= NetworkConstants.RFC7421_PREFIX_LENGTH); + } + + private static boolean isIPv6DefaultRoute(IpPrefix prefix) { + return prefix.getAddress().equals(Inet6Address.ANY); + } + + private static boolean isIPv6GUA(LinkAddress addr) { + return (addr.getAddress() instanceof Inet6Address) && addr.isGlobalPreferred(); + } + + private static <T> boolean any(Iterable<T> coll, Predicate<T> fn) { + for (T t : coll) { + if (fn.test(t)) { + return true; + } + } + return false; + } + + private static <T> boolean all(Iterable<T> coll, Predicate<T> fn) { + return !any(coll, not(fn)); + } + + private static <T> Predicate<T> not(Predicate<T> fn) { + return (t) -> !fn.test(t); + } + + private static <T> String join(String delimiter, Collection<T> coll) { + return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter)); + } } public static final String DUMP_ARG = "ipmanager"; @@ -436,8 +565,7 @@ public class IpManager extends StateMachine { private boolean mMulticastFiltering; private long mStartTimeMillis; - public IpManager(Context context, String ifName, Callback callback) - throws IllegalArgumentException { + public IpManager(Context context, String ifName, Callback callback) { this(context, ifName, callback, INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE))); } @@ -446,7 +574,7 @@ public class IpManager extends StateMachine { * An expanded constructor, useful for dependency injection. */ public IpManager(Context context, String ifName, Callback callback, - INetworkManagementService nwService) throws IllegalArgumentException { + INetworkManagementService nwService) { super(IpManager.class.getSimpleName() + "." + ifName); mTag = getName(); @@ -563,6 +691,11 @@ public class IpManager extends StateMachine { } public void startProvisioning(ProvisioningConfiguration req) { + if (!req.isValid()) { + doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); + return; + } + getNetworkInterface(); mCallback.setNeighborDiscoveryOffload(true); diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java index a012e0cb0547..9b3bc3f0301a 100644 --- a/services/net/java/android/net/util/NetworkConstants.java +++ b/services/net/java/android/net/util/NetworkConstants.java @@ -102,6 +102,7 @@ public final class NetworkConstants { public static final int IPV6_ADDR_LEN = 16; public static final int IPV6_MIN_MTU = 1280; public static final int RFC7421_PREFIX_LENGTH = 64; + public static final int RFC6177_MIN_PREFIX_LENGTH = 48; /** * ICMPv6 constants. diff --git a/services/net/java/android/net/util/PrefixUtils.java b/services/net/java/android/net/util/PrefixUtils.java new file mode 100644 index 000000000000..962aab459a19 --- /dev/null +++ b/services/net/java/android/net/util/PrefixUtils.java @@ -0,0 +1,74 @@ +/* + * 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.util; + +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + + +/** + * @hide + */ +public class PrefixUtils { + private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = { + pfx("127.0.0.0/8"), // IPv4 loopback + pfx("169.254.0.0/16"), // IPv4 link-local, RFC3927#section-8 + pfx("::/3"), + pfx("fe80::/64"), // IPv6 link-local + pfx("fc00::/7"), // IPv6 ULA + pfx("ff02::/8"), // IPv6 link-local multicast + }; + + public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24"); + + public static Set<IpPrefix> getNonForwardablePrefixes() { + final HashSet<IpPrefix> prefixes = new HashSet<>(); + addNonForwardablePrefixes(prefixes); + return prefixes; + } + + public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) { + Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES); + } + + public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) { + final HashSet<IpPrefix> localPrefixes = new HashSet<>(); + if (lp == null) return localPrefixes; + + for (LinkAddress addr : lp.getAllLinkAddresses()) { + if (addr.getAddress().isLinkLocalAddress()) continue; + localPrefixes.add(asIpPrefix(addr)); + } + // TODO: Add directly-connected routes as well (ones from which we did + // not also form a LinkAddress)? + + return localPrefixes; + } + + public static IpPrefix asIpPrefix(LinkAddress addr) { + return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); + } + + private static IpPrefix pfx(String prefixStr) { + return new IpPrefix(prefixStr); + } +} diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java index 231f2c8660e9..4eeabb078a64 100644 --- a/telephony/java/android/telephony/MbmsDownloadManager.java +++ b/telephony/java/android/telephony/MbmsDownloadManager.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntDef; import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; @@ -26,9 +27,9 @@ import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; +import android.telephony.mbms.FileInfo; import android.telephony.mbms.IDownloadCallback; import android.telephony.mbms.DownloadRequest; -import android.telephony.mbms.DownloadStatus; import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadManagerCallback; import android.telephony.mbms.MbmsDownloadReceiver; @@ -40,6 +41,8 @@ import android.util.Log; import java.io.File; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -192,6 +195,18 @@ public class MbmsDownloadManager { public static final int RESULT_EXPIRED = 3; // TODO - more results! + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({STATUS_UNKNOWN, STATUS_ACTIVELY_DOWNLOADING, STATUS_PENDING_DOWNLOAD, + STATUS_PENDING_REPAIR, STATUS_PENDING_DOWNLOAD_WINDOW}) + public @interface DownloadStatus {} + + public static final int STATUS_UNKNOWN = 0; + public static final int STATUS_ACTIVELY_DOWNLOADING = 1; + public static final int STATUS_PENDING_DOWNLOAD = 2; + public static final int STATUS_PENDING_REPAIR = 3; + public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4; + private final Context mContext; private int mSubscriptionId = INVALID_SUBSCRIPTION_ID; @@ -271,17 +286,19 @@ public class MbmsDownloadManager { * The serviceClasses argument lets the app filter on types of programming and is opaque data * negotiated beforehand between the app and the carrier. * - * Multiple calls replace the list of serviceClasses of interest. - * * This may throw an {@link MbmsException} containing one of the following errors: * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} - * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED} - * {@link MbmsException#ERROR_SERVICE_LOST} + * {@link MbmsException#ERROR_MIDDLEWARE_LOST} * * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)} * callback can include any of the errors except: - * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE} - * {@link MbmsException#ERROR_END_OF_SESSION} + * {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE} + * + * @param classList A list of service classes which the app wishes to receive + * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks + * about. Subsequent calls to this method will replace this list of service + * classes (i.e. the middleware will no longer send updates for services + * matching classes only in the old list). */ public void getFileServices(List<String> classList) throws MbmsException { IMbmsDownloadService downloadService = mService.get(); @@ -296,7 +313,7 @@ public class MbmsDownloadManager { } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService.set(null); - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } } @@ -312,9 +329,10 @@ public class MbmsDownloadManager { * will default to a directory formed by the concatenation of the app's files directory and * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}. * - * This method may not be called while any download requests are still active. If this is - * the case, an {@link MbmsException} will be thrown with code - * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} + * Before calling this method, the app must cancel all of its pending + * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done, + * an {@link MbmsException} will be thrown with code + * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} * * The {@link File} supplied as a root temp file directory must already exist. If not, an * {@link IllegalArgumentException} will be thrown. @@ -346,7 +364,7 @@ public class MbmsDownloadManager { } } catch (RemoteException e) { mService.set(null); - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } SharedPreferences prefs = mContext.getSharedPreferences( @@ -397,29 +415,36 @@ public class MbmsDownloadManager { downloadService.download(request, callback); } catch (RemoteException e) { mService.set(null); - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } } /** - * Returns a list DownloadRequests that originated from this application (UID). - * - * May throw a RemoteException. - * - * Asynchronous errors through the listener include any of the errors except - * <li>ERROR_UNABLED_TO_START_SERVICE</li> - * <li>ERROR_MSDC_INVALID_SERVICE_ID</li> - * <li>ERROR_MSDC_END_OF_SESSION</li> + * Returns a list of pending {@link DownloadRequest}s that originated from this application. + * A pending request is one that was issued via + * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through + * {@link #cancelDownload(DownloadRequest)}. + * @return A list, possibly empty, of {@link DownloadRequest}s */ - public List<DownloadRequest> listPendingDownloads() { - return null; + public @NonNull List<DownloadRequest> listPendingDownloads() throws MbmsException { + IMbmsDownloadService downloadService = mService.get(); + if (downloadService == null) { + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); + } + + try { + return downloadService.listPendingDownloads(mSubscriptionId); + } catch (RemoteException e) { + mService.set(null); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); + } } /** * Attempts to cancel the specified {@link DownloadRequest}. * * If the middleware is not aware of the specified download request, an MbmsException will be - * thrown with error code {@link MbmsException#ERROR_UNKNOWN_DOWNLOAD_REQUEST}. + * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}. * * If this method returns without throwing an exception, you may assume that cancellation * was successful. @@ -438,45 +463,71 @@ public class MbmsDownloadManager { } } catch (RemoteException e) { mService.set(null); - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } deleteDownloadRequestToken(downloadRequest); } /** - * Gets information about current and known upcoming downloads. + * Gets information about the status of a file pending download. * - * Current is a straightforward count of the files being downloaded "now" - * for some definition of now (may be racey). - * Future downloads include counts of files with pending repair operations, counts of - * files with future downloads and indication of scheduled download times with unknown - * file details. + * If the middleware has not yet been properly initialized or if it has no records of the + * file indicated by {@code fileInfo} being associated with {@code downloadRequest}, + * {@link #STATUS_UNKNOWN} will be returned. * - * May throw an IllegalArgumentException or RemoteException. - * - * If the DownloadRequest is unknown the results will be null. + * @param downloadRequest The download request to query. + * @param fileInfo The particular file within the request to get information on. + * @return The status of the download. */ - public DownloadStatus getDownloadStatus(DownloadRequest downloadRequest) { - return null; + @DownloadStatus + public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) + throws MbmsException { + IMbmsDownloadService downloadService = mService.get(); + if (downloadService == null) { + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); + } + + try { + return downloadService.getDownloadStatus(downloadRequest, fileInfo); + } catch (RemoteException e) { + mService.set(null); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); + } } /** - * Resets middleware knowledge regarding this download request. + * Resets the middleware's knowledge of previously-downloaded files in this download request. * - * This state consists of knowledge of what files have already been downloaded. - * Normally the middleware won't download files who's hash matches previously downloaded - * content, even if that content has since been deleted. If this function is called - * repeated content will be downloaded again when available. This does not interrupt - * in-progress downloads. + * Normally, the middleware keeps track of the hashes of downloaded files and won't re-download + * files whose server-reported hash matches one of the already-downloaded files. This means + * that if the file is accidentally deleted by the user or by the app, the middleware will + * not try to download it again. + * This method will reset the middleware's cache of hashes for the provided + * {@link DownloadRequest}, so that previously downloaded content will be downloaded again + * when available. + * This will not interrupt in-progress downloads. * - * May throw an IllegalArgumentException or RemoteException. + * If the middleware is not aware of the specified download request, an MbmsException will be + * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}. * - * <li>SUCCESS</li> - * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li> - * <li>ERROR_MSDC_UNKNOWN_REQUEST</li> + * May throw a {@link MbmsException} with error code + * @param downloadRequest The request to re-download files for. */ - public int resetDownloadKnowledge(DownloadRequest downloadRequest) { - return 0; + public void resetDownloadKnowledge(DownloadRequest downloadRequest) throws MbmsException { + IMbmsDownloadService downloadService = mService.get(); + if (downloadService == null) { + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND); + } + + try { + int result = downloadService.resetDownloadKnowledge(downloadRequest); + if (result != MbmsException.SUCCESS) { + throw new MbmsException(result); + } + } catch (RemoteException e) { + mService.set(null); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); + } } public void dispose() { diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java index 8cc447e4db7a..5b3503a1b163 100644 --- a/telephony/java/android/telephony/MbmsStreamingManager.java +++ b/telephony/java/android/telephony/MbmsStreamingManager.java @@ -35,7 +35,10 @@ import java.util.concurrent.atomic.AtomicReference; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -/** @hide */ +/** + * This class provides functionality for streaming media over MBMS. + * @hide + */ public class MbmsStreamingManager { private static final String LOG_TAG = "MbmsStreamingManager"; public static final String MBMS_STREAMING_SERVICE_ACTION = @@ -88,6 +91,8 @@ public class MbmsStreamingManager { /** * Terminates this instance, ending calls to the registered listener. Also terminates * any streaming services spawned from this instance. + * + * May throw an {@link IllegalStateException} */ public void dispose() { IMbmsStreamingService streamingService = mService.get(); @@ -111,15 +116,15 @@ public class MbmsStreamingManager { * * Multiple calls replace the list of serviceClasses of interest. * - * This may throw an {@link MbmsException} containing one of the following errors: - * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} - * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED} - * {@link MbmsException#ERROR_SERVICE_LOST} + * This may throw an {@link MbmsException} containing any error in + * {@link android.telephony.mbms.MbmsException.GeneralErrors}, + * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or + * {@link MbmsException#ERROR_MIDDLEWARE_LOST}. * - * Asynchronous error codes via the {@link MbmsStreamingManagerCallback#error(int, String)} - * callback can include any of the errors except: - * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE} - * {@link MbmsException#ERROR_END_OF_SESSION} + * May also throw an unchecked {@link IllegalArgumentException} or an + * {@link IllegalStateException} + * + * @param classList A list of streaming service classes that the app would like updates on. */ public void getStreamingServices(List<String> classList) throws MbmsException { IMbmsStreamingService streamingService = mService.get(); @@ -134,7 +139,7 @@ public class MbmsStreamingManager { } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService.set(null); - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } } @@ -145,14 +150,21 @@ public class MbmsStreamingManager { * reported via * {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int, int)} * - * May throw an {@link MbmsException} containing any of the following error codes: - * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} - * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED} - * {@link MbmsException#ERROR_SERVICE_LOST} + * May throw an + * {@link MbmsException} containing any of the error codes in + * {@link android.telephony.mbms.MbmsException.GeneralErrors}, + * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or + * {@link MbmsException#ERROR_MIDDLEWARE_LOST}. * * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException} * - * Asynchronous errors through the listener include any of the errors + * Asynchronous errors through the listener include any of the errors in + * {@link android.telephony.mbms.MbmsException.GeneralErrors} or + * {@link android.telephony.mbms.MbmsException.StreamingErrors}. + * + * @param serviceInfo The information about the service to stream. + * @param listener A listener that'll be called when something about the stream changes. + * @return An instance of {@link StreamingService} through which the stream can be controlled. */ public StreamingService startStreaming(StreamingServiceInfo serviceInfo, StreamingServiceCallback listener) throws MbmsException { @@ -170,7 +182,7 @@ public class MbmsStreamingManager { } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService.set(null); - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } return new StreamingService(mSubscriptionId, streamingService, serviceInfo, listener); diff --git a/telephony/java/android/telephony/mbms/DownloadStatus.aidl b/telephony/java/android/telephony/mbms/DownloadStatus.aidl deleted file mode 100755 index e7cfd391dade..000000000000 --- a/telephony/java/android/telephony/mbms/DownloadStatus.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 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.telephony.mbms; - -parcelable DownloadStatus; diff --git a/telephony/java/android/telephony/mbms/DownloadStatus.java b/telephony/java/android/telephony/mbms/DownloadStatus.java deleted file mode 100644 index 90eb53f3f59e..000000000000 --- a/telephony/java/android/telephony/mbms/DownloadStatus.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.telephony.mbms; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * A Parcelable class describing the status of a Cell-Broadcast download request - * @hide - */ -public class DownloadStatus implements Parcelable { - // includes downloads and active repair work - public final int activelyDownloading; - - // files scheduled for future broadcast - public final int pendingDownloads; - - // files scheduled for future repairs - public final int pendingRepairs; - - // is a future download window scheduled with unknown - // number of files - public final boolean windowPending; - - public DownloadStatus(int downloading, int downloads, int repairs, boolean window) { - activelyDownloading = downloading; - pendingDownloads = downloads; - pendingRepairs = repairs; - windowPending = window; - } - - public static final Parcelable.Creator<DownloadStatus> CREATOR = - new Parcelable.Creator<DownloadStatus>() { - @Override - public DownloadStatus createFromParcel(Parcel in) { - return new DownloadStatus(in); - } - - @Override - public DownloadStatus[] newArray(int size) { - return new DownloadStatus[size]; - } - }; - - DownloadStatus(Parcel in) { - activelyDownloading = in.readInt(); - pendingDownloads = in.readInt(); - pendingRepairs = in.readInt(); - windowPending = (in.readInt() == 1); - } - - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(activelyDownloading); - dest.writeInt(pendingDownloads); - dest.writeInt(pendingRepairs); - dest.writeInt((windowPending ? 1 : 0)); - } - - public int describeContents() { - return 0; - } -} diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl index 8116a7f0b7c4..007aee7cf3f2 100755 --- a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl +++ b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl @@ -30,7 +30,5 @@ oneway interface IMbmsStreamingManagerCallback void streamingServicesUpdated(in List<StreamingServiceInfo> services); - void activeStreamingServicesUpdated(in List<StreamingServiceInfo> services); - void middlewareReady(); } diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java index 5b22199bea1c..ba25f663ffb4 100644 --- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java +++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java @@ -55,7 +55,7 @@ public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.St * Before this method is called, calling any method on an instance of * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException} * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} - * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY} + * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} */ @Override public void middlewareReady() { diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java index e190623f5529..8888119f90e6 100644 --- a/telephony/java/android/telephony/mbms/MbmsException.java +++ b/telephony/java/android/telephony/mbms/MbmsException.java @@ -18,27 +18,112 @@ package android.telephony.mbms; /** @hide */ public class MbmsException extends Exception { + /** Indicates that the operation was successful. */ public static final int SUCCESS = 0; - public static final int ERROR_NO_SERVICE_INSTALLED = 1; - public static final int ERROR_MULTIPLE_SERVICES_INSTALLED = 2; - public static final int ERROR_BIND_TIMEOUT_OR_FAILURE = 3; - public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 4; - public static final int ERROR_ALREADY_INITIALIZED = 5; - public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 6; - public static final int ERROR_MIDDLEWARE_NOT_BOUND = 7; - public static final int ERROR_UNABLE_TO_START_SERVICE = 8; - public static final int ERROR_STREAM_ALREADY_STARTED = 9; - public static final int ERROR_END_OF_SESSION = 10; - public static final int ERROR_SERVICE_LOST = 11; - public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 12; - public static final int ERROR_IN_E911 = 13; - public static final int ERROR_OUT_OF_MEMORY = 14; - public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 15; - public static final int ERROR_UNABLE_TO_READ_SIM = 16; - public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 17; - public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 18; - public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 19; - public static final int ERROR_UNABLE_TO_INITIALIZE = 20; + + // Following errors are generated in the manager and should not be returned from the + // middleware + /** + * Indicates that either no MBMS middleware app is installed on the device or multiple + * middleware apps are installed on the device. + */ + public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1; + + /** + * Indicates that the app attempted to perform an operation on an instance of + * {@link android.telephony.MbmsDownloadManager} or + * {@link android.telephony.MbmsStreamingManager} without being bound to the middleware. + */ + public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; + + /** Indicates that the middleware has died and the requested operation was not completed.*/ + public static final int ERROR_MIDDLEWARE_LOST = 3; + + /** + * Indicates errors that may be generated during initialization by the + * middleware. They are applicable to both streaming and file-download use-cases. + */ + public static class InitializationErrors { + /** + * Indicates that the app tried to create more than one instance each of + * {@link android.telephony.MbmsStreamingManager} or + * {@link android.telephony.MbmsDownloadManager}. + */ + public static final int ERROR_DUPLICATE_INITIALIZE = 101; + /** Indicates that the app is not authorized to access media via MBMS.*/ + public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; + /** Indicates that the middleware was unable to initialize for this app. */ + public static final int ERROR_UNABLE_TO_INITIALIZE = 103; + } + + /** + * Indicates the errors that may occur at any point and are applicable to both + * streaming and file-download. + */ + public static class GeneralErrors { + /** + * Indicates that the app attempted to perform an operation before receiving notification + * that the middleware is ready via {@link MbmsStreamingManagerCallback#middlewareReady()} + * or {@link MbmsDownloadManagerCallback#middlewareReady()}. + */ + public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201; + /** + * Indicates that the middleware ran out of memory and was unable to complete the requested + * operation. + */ + public static final int ERROR_OUT_OF_MEMORY = 202; + /** + * Indicates that the requested operation failed due to the middleware being unavailable due + * to a transient condition. The app may retry the operation at a later time. + */ + public static final int ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE = 203; + /** + * Indicates that the requested operation was not performed due to being in emergency + * callback mode. + */ + public static final int ERROR_IN_E911 = 204; + /** Indicates that MBMS is not available due to the device being in roaming. */ + public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 205; + /** Indicates that MBMS is not available due to a SIM read error. */ + public static final int ERROR_UNABLE_TO_READ_SIM = 206; + /** + * Indicates that MBMS is not available due to the inserted SIM being from an unsupported + * carrier. + */ + public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 207; + } + + /** + * Indicates the errors that are applicable only to the streaming use-case + */ + public static class StreamingErrors { + /** Indicates that the middleware cannot start a stream due to too many ongoing streams */ + public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301; + + /** Indicates that the middleware was unable to start the streaming service */ + public static final int ERROR_UNABLE_TO_START_SERVICE = 302; + + /** + * Indicates that the app called + * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)} + * more than once for the same {@link StreamingServiceInfo}. + */ + public static final int ERROR_DUPLICATE_START_STREAM = 303; + } + + /** + * Indicates the errors that are applicable only to the file-download use-case + */ + public static class DownloadErrors { + /** + * Indicates that the app is not allowed to change the temp file root at this time due to + * outstanding download requests. + */ + public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401; + + /** Indicates that the middleware has no record of the supplied {@link DownloadRequest}. */ + public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402; + } private final int mErrorCode; diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java index 27d9878a1966..2e91be9acaf7 100644 --- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java +++ b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java @@ -50,25 +50,12 @@ public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback. } /** - * Called to indicate the active Streaming Services have changed. - * - * This will be caused whenever a new service starts streaming or whenever - * MbmsStreamServiceManager.getActiveStreamingServices is called. - * - * @param services a list of StreamingServiceInfos. May be empty if - * there are no active StreamingServices - */ - public void activeStreamingServicesUpdated(List<StreamingServiceInfo> services) { - // default implementation empty - } - - /** * Called to indicate that the middleware has been initialized and is ready. * * Before this method is called, calling any method on an instance of * {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException} * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND} - * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY} + * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} */ @Override public void middlewareReady() { diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java index 1e03fb9584b4..4b913f825231 100644 --- a/telephony/java/android/telephony/mbms/MbmsUtils.java +++ b/telephony/java/android/telephony/mbms/MbmsUtils.java @@ -22,14 +22,11 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.*; import android.content.pm.ServiceInfo; -import android.telephony.MbmsDownloadManager; import android.util.Log; import java.io.File; import java.io.IOException; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; /** * @hide @@ -78,7 +75,7 @@ public class MbmsUtils { MbmsUtils.getMiddlewareServiceInfo(context, serviceAction); if (mbmsServiceInfo == null) { - throw new MbmsException(MbmsException.ERROR_NO_SERVICE_INSTALLED); + throw new MbmsException(MbmsException.ERROR_NO_UNIQUE_MIDDLEWARE); } bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo)); diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java index 475c93aa0eb5..1a6418969a90 100644 --- a/telephony/java/android/telephony/mbms/StreamingService.java +++ b/telephony/java/android/telephony/mbms/StreamingService.java @@ -18,7 +18,6 @@ package android.telephony.mbms; import android.annotation.IntDef; import android.net.Uri; -import android.os.DeadObjectException; import android.os.RemoteException; import android.telephony.mbms.vendor.IMbmsStreamingService; import android.util.Log; @@ -50,15 +49,42 @@ public class StreamingService { */ @Retention(RetentionPolicy.SOURCE) @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT, - REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE}) + REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE, + REASON_LEFT_MBMS_BROADCAST_AREA}) public @interface StreamingStateChangeReason {} + + /** + * State changed due to a call to {@link #stopStreaming()} or + * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)} + */ public static final int REASON_BY_USER_REQUEST = 1; + + /** + * State changed due to the streaming session ending at the carrier. + */ public static final int REASON_END_OF_SESSION = 2; + + /** + * State changed due to a frequency conflict with another requested stream. + */ public static final int REASON_FREQUENCY_CONFLICT = 3; + + /** + * State changed due to the middleware running out of memory + */ public static final int REASON_OUT_OF_MEMORY = 4; + + /** + * State changed due to the device leaving the home carrier's LTE network. + */ public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5; /** + * State changed due to the device leaving the where this stream is being broadcast. + */ + public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 5; + + /** * The method of transmission currently used for a stream, * reported via {@link StreamingServiceCallback#streamMethodUpdated} */ @@ -87,7 +113,9 @@ public class StreamingService { * Retreive the Uri used to play this stream. * * This may throw a {@link MbmsException} with the error code - * {@link MbmsException#ERROR_SERVICE_LOST} + * {@link MbmsException#ERROR_MIDDLEWARE_LOST} + * + * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException} * * @return The {@link Uri} to pass to the streaming client. */ @@ -101,7 +129,7 @@ public class StreamingService { } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService = null; - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } } @@ -115,7 +143,9 @@ public class StreamingService { /** * Stop streaming this service. * This may throw a {@link MbmsException} with the error code - * {@link MbmsException#ERROR_SERVICE_LOST} + * {@link MbmsException#ERROR_MIDDLEWARE_LOST} + * + * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException} */ public void stopStreaming() throws MbmsException { if (mService == null) { @@ -127,10 +157,18 @@ public class StreamingService { } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); mService = null; - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); } } + /** + * Disposes of this stream. Further operations on this object will fail with an + * {@link IllegalStateException}. + * + * This may throw a {@link MbmsException} with the error code + * {@link MbmsException#ERROR_MIDDLEWARE_LOST} + * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException} + */ public void dispose() throws MbmsException { if (mService == null) { throw new IllegalStateException("No streaming service attached"); @@ -140,8 +178,9 @@ public class StreamingService { mService.disposeStream(mSubscriptionId, mServiceInfo.getServiceId()); } catch (RemoteException e) { Log.w(LOG_TAG, "Remote process died"); + throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST); + } finally { mService = null; - throw new MbmsException(MbmsException.ERROR_SERVICE_LOST); } } } diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl index 7112e13d5650..725d11c880b2 100755 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl @@ -19,12 +19,11 @@ package android.telephony.mbms.vendor; import android.app.PendingIntent; import android.net.Uri; import android.telephony.mbms.DownloadRequest; -import android.telephony.mbms.DownloadStatus; +import android.telephony.mbms.FileInfo; import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.IDownloadCallback; /** - * The interface the opaque MbmsStreamingService will satisfy. * @hide */ interface IMbmsDownloadService @@ -41,14 +40,9 @@ interface IMbmsDownloadService int cancelDownload(in DownloadRequest downloadRequest); - DownloadStatus getDownloadStatus(in DownloadRequest downloadRequest); + int getDownloadStatus(in DownloadRequest downloadRequest, in FileInfo fileInfo); - /* - * named this for 2 reasons: - * 1 don't want 'State' here as it conflicts with 'Status' of the previous function - * 2 want to perfect typing 'Knowledge' - */ - void resetDownloadKnowledge(in DownloadRequest downloadRequest); + int resetDownloadKnowledge(in DownloadRequest downloadRequest); void dispose(int subId); } diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl index 1370b83857ec..04a53cbe0d00 100755 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl @@ -22,12 +22,11 @@ import android.telephony.mbms.IStreamingServiceCallback; import android.telephony.mbms.StreamingServiceInfo; /** - * The interface the opaque MbmsStreamingService will satisfy. * @hide */ interface IMbmsStreamingService { - int initialize(IMbmsStreamingManagerCallback listener, int subId); + void initialize(IMbmsStreamingManagerCallback listener, int subId); int getStreamingServices(int subId, in List<String> serviceClasses); diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java index 58bda6480999..8fbd4481cfc2 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -16,9 +16,10 @@ package android.telephony.mbms.vendor; +import android.annotation.NonNull; import android.os.RemoteException; import android.telephony.mbms.DownloadRequest; -import android.telephony.mbms.DownloadStatus; +import android.telephony.mbms.FileInfo; import android.telephony.mbms.IDownloadCallback; import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.MbmsException; @@ -35,7 +36,9 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { /** * Initialize the download service for this app and subId, registering the listener. * - * May throw an {@link IllegalArgumentException} or a {@link SecurityException} + * Exceptions should not be thrown through this method -- this method is called from within a + * {@link android.content.ServiceConnection} defined by the framework, so apps have no way of + * catching them. Call {@link IMbmsDownloadManagerCallback#error(int, String)} instead. * * @param listener The callback to use to communicate with the app. * @param subscriptionId The subscription ID to use. @@ -59,9 +62,8 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { * @param serviceClasses The service classes that the app wishes to get info on. The strings * may contain arbitrary data as negotiated between the app and the * carrier. - * @return One of {@link MbmsException#SUCCESS}, - * {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}, - * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED} + * @return One of {@link MbmsException#SUCCESS} or + * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}, */ @Override public int getFileServices(int subscriptionId, List<String> serviceClasses) @@ -73,11 +75,16 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { * Sets the temp file root directory for this app/subscriptionId combination. The middleware * should persist {@code rootDirectoryPath} and send it back when sending intents to the * app's {@link android.telephony.mbms.MbmsDownloadReceiver}. + * + * If the calling app (as identified by the calling UID) currently has any pending download + * requests that have not been canceled, the middleware must return + * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here. + * * @param subscriptionId The subscription id the download is operating under. * @param rootDirectoryPath The path to the app's temp file root directory. - * @return {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}, - * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}, - * or {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED} + * @return {@link MbmsException#SUCCESS}, + * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or + * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} */ @Override public int setTempFileRootDirectory(int subscriptionId, @@ -87,6 +94,11 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { /** * Issues a request to download a set of files. + * + * The middleware should expect that {@link #setTempFileRootDirectory(int, String)} has been + * called for this app between when the app was installed and when this method is called. If + * this is not the case, an {@link IllegalStateException} may be thrown. + * * @param downloadRequest An object describing the set of files to be downloaded. * @param listener A listener through which the middleware can provide progress updates to * the app while both are still running. @@ -98,8 +110,18 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { return 0; } + + /** + * Returns a list of pending {@link DownloadRequest}s that originated from the calling + * application, identified by its uid. A pending request is one that was issued via + * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through + * {@link #cancelDownload(DownloadRequest)}. + * The middleware must return a non-null result synchronously or throw an exception + * inheriting from {@link RuntimeException}. + * @return A list, possibly empty, of {@link DownloadRequest}s + */ @Override - public List<DownloadRequest> listPendingDownloads(int subscriptionId) + public @NonNull List<DownloadRequest> listPendingDownloads(int subscriptionId) throws RemoteException { return null; } @@ -113,23 +135,47 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { * {@link DownloadRequest}. * @param downloadRequest The request to cancel * @return {@link MbmsException#SUCCESS}, - * {@link MbmsException#ERROR_UNKNOWN_DOWNLOAD_REQUEST}, - * {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY} + * {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}, + * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} */ @Override public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException { return 0; } + /** + * Gets information about the status of a file pending download. + * + * If the middleware has not yet been properly initialized or if it has no records of the + * file indicated by {@code fileInfo} being associated with {@code downloadRequest}, + * {@link android.telephony.MbmsDownloadManager#STATUS_UNKNOWN} must be returned. + * + * @param downloadRequest The download request to query. + * @param fileInfo The particular file within the request to get information on. + * @return The status of the download. + */ @Override - public DownloadStatus getDownloadStatus(DownloadRequest downloadRequest) + public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) throws RemoteException { - return null; + return 0; } + /** + * Resets the middleware's knowledge of previously-downloaded files in this download request. + * + * When this method is called, the middleware must attempt to re-download all the files + * specified by the {@link DownloadRequest}, even if the files have not changed on the server. + * In addition, current in-progress downloads must not be interrupted. + * + * If the middleware is not aware of the specified download request, return + * {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}. + * + * @param downloadRequest The request to re-download files for. + */ @Override - public void resetDownloadKnowledge(DownloadRequest downloadRequest) + public int resetDownloadKnowledge(DownloadRequest downloadRequest) throws RemoteException { + return 0; } /** diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java index c97e754d1dd5..f072c46171d6 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java @@ -33,16 +33,17 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { /** * Initialize streaming service for this app and subId, registering the listener. * - * May throw an {@link IllegalArgumentException} or a {@link SecurityException} + * Exceptions should not be thrown through this method -- this method is called from within a + * {@link android.content.ServiceConnection} defined by the framework, so apps have no way of + * catching them. Call {@link IMbmsStreamingManagerCallback#error(int, String)} instead. * * @param listener The callback to use to communicate with the app. * @param subscriptionId The subscription ID to use. - * @return {@link MbmsException#SUCCESS} or {@link MbmsException#ERROR_ALREADY_INITIALIZED} */ @Override - public int initialize(IMbmsStreamingManagerCallback listener, int subscriptionId) + public void initialize(IMbmsStreamingManagerCallback listener, int subscriptionId) throws RemoteException { - return 0; + return; } /** @@ -59,9 +60,8 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { * @param serviceClasses The service classes that the app wishes to get info on. The strings * may contain arbitrary data as negotiated between the app and the * carrier. - * @return One of {@link MbmsException#SUCCESS}, - * {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}, - * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED} + * @return {@link MbmsException#SUCCESS} or any of the errors in + * {@link android.telephony.mbms.MbmsException.GeneralErrors} */ @Override public int getStreamingServices(int subscriptionId, @@ -79,8 +79,7 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { * @param subscriptionId The subscription id to use. * @param serviceId The ID of the streaming service that the app has requested. * @param listener The listener object on which the app wishes to receive updates. - * @return {@link MbmsException#SUCCESS}, {@link MbmsException#ERROR_STREAM_ALREADY_STARTED}, - * or {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}. + * @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors} */ @Override public int startStreaming(int subscriptionId, String serviceId, diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java index 025b01740aad..e7dbfe3f5044 100644 --- a/tests/net/java/android/net/ip/IpManagerTest.java +++ b/tests/net/java/android/net/ip/IpManagerTest.java @@ -16,11 +16,26 @@ package android.net.ip; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +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 android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.ip.IpManager.Callback; +import android.net.ip.IpManager.InitialConfiguration; +import android.net.ip.IpManager.ProvisioningConfiguration; import android.os.INetworkManagementService; import android.provider.Settings; import android.support.test.filters.SmallTest; @@ -31,11 +46,17 @@ import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.R; import org.junit.Before; -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.HashSet; +import java.util.Set; + /** * Tests for IpManager. */ @@ -44,14 +65,20 @@ import org.mockito.MockitoAnnotations; public class IpManagerTest { private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1; + private static final String VALID = "VALID"; + private static final String INVALID = "INVALID"; + @Mock private Context mContext; @Mock private INetworkManagementService mNMService; @Mock private Resources mResources; + @Mock private Callback mCb; + @Mock private AlarmManager mAlarm; private MockContentResolver mContentResolver; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); when(mContext.getResources()).thenReturn(mResources); when(mResources.getInteger(R.integer.config_networkAvoidBadWifi)) .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE); @@ -68,7 +95,152 @@ public class IpManagerTest { @Test public void testInvalidInterfaceDoesNotThrow() throws Exception { - final IpManager.Callback cb = new IpManager.Callback(); - final IpManager ipm = new IpManager(mContext, "test_wlan0", cb, mNMService); + final IpManager ipm = new IpManager(mContext, "test_wlan0", mCb, mNMService); + } + + @Test + public void testDefaultProvisioningConfiguration() throws Exception { + final String iface = "test_wlan0"; + final IpManager ipm = new IpManager(mContext, iface, mCb, mNMService); + ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIPv4() + // TODO: mock IpReachabilityMonitor's dependencies (NetworkInterface, PowerManager) + // and enable it in this test + .withoutIpReachabilityMonitor() + .build(); + + ipm.startProvisioning(config); + verify(mCb, times(1)).setNeighborDiscoveryOffload(true); + verify(mCb, timeout(100).times(1)).setFallbackMulticastFilter(false); + verify(mCb, never()).onProvisioningFailure(any()); + + ipm.stop(); + verify(mNMService, timeout(100).times(1)).disableIpv6(iface); + verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(iface); + } + + @Test + public void testInitialConfigurations() throws Exception { + InitialConfigurationTestCase[] testcases = { + validConf("valid IPv4 configuration", + links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("192.0.2.2")), + validConf("another valid IPv4 configuration", + links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns()), + validConf("valid IPv6 configurations", + links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), + prefixes("2001:db8:dead:beef::/64", "fe80::/64"), + dns("2001:db8:dead:beef:f00::02")), + validConf("valid IPv6 configurations", + links("fe80::1/64"), prefixes("fe80::/64"), dns()), + validConf("valid IPv6/v4 configuration", + links("2001:db8:dead:beef:f00::a0/48", "192.0.2.12/24"), + prefixes("2001:db8:dead:beef::/64", "192.0.2.0/24"), + dns("192.0.2.2", "2001:db8:dead:beef:f00::02")), + validConf("valid IPv6 configuration without any GUA.", + links("fd00:1234:5678::1/48"), + prefixes("fd00:1234:5678::/48"), + dns("fd00:1234:5678::1000")), + + invalidConf("v4 addr and dns not in any prefix", + links("192.0.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")), + invalidConf("v4 addr not in any prefix", + links("198.51.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")), + invalidConf("v4 dns addr not in any prefix", + links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("198.51.100.2")), + invalidConf("v6 addr not in any prefix", + links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), + prefixes("2001:db8:dead:beef::/64"), + dns("2001:db8:dead:beef:f00::02")), + invalidConf("v6 dns addr not in any prefix", + links("fe80::1/64"), prefixes("fe80::/64"), dns("2001:db8:dead:beef:f00::02")), + invalidConf("default ipv6 route and no GUA", + links("fd01:1111:2222:3333::a0/128"), prefixes("::/0"), dns()), + invalidConf("invalid v6 prefix length", + links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/32"), + dns()), + invalidConf("another invalid v6 prefix length", + links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/72"), + dns()) + }; + + for (InitialConfigurationTestCase testcase : testcases) { + if (testcase.config.isValid() != testcase.isValid) { + fail(testcase.errorMessage()); + } + } + } + + static class InitialConfigurationTestCase { + String descr; + boolean isValid; + InitialConfiguration config; + public String errorMessage() { + return String.format("%s: expected configuration %s to be %s, but was %s", + descr, config, validString(isValid), validString(!isValid)); + } + } + + static String validString(boolean isValid) { + return isValid ? VALID : INVALID; + } + + static InitialConfigurationTestCase validConf(String descr, Set<LinkAddress> links, + Set<IpPrefix> prefixes, Set<InetAddress> dns) { + return confTestCase(descr, true, conf(links, prefixes, dns)); + } + + static InitialConfigurationTestCase invalidConf(String descr, Set<LinkAddress> links, + Set<IpPrefix> prefixes, Set<InetAddress> dns) { + return confTestCase(descr, false, conf(links, prefixes, dns)); + } + + static InitialConfigurationTestCase confTestCase( + String descr, boolean isValid, InitialConfiguration config) { + InitialConfigurationTestCase testcase = new InitialConfigurationTestCase(); + testcase.descr = descr; + testcase.isValid = isValid; + testcase.config = config; + return testcase; + } + + static InitialConfiguration conf( + Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns) { + InitialConfiguration conf = new InitialConfiguration(); + conf.ipAddresses.addAll(links); + conf.directlyConnectedRoutes.addAll(prefixes); + conf.dnsServers.addAll(dns); + return conf; + } + + static Set<IpPrefix> prefixes(String... prefixes) { + return mapIntoSet(prefixes, IpPrefix::new); + } + + static Set<LinkAddress> links(String... addresses) { + return mapIntoSet(addresses, LinkAddress::new); + } + + static Set<InetAddress> ips(String... addresses) { + return mapIntoSet(addresses, InetAddress::getByName); + } + + static Set<InetAddress> dns(String... addresses) { + return ips(addresses); + } + + static <A, B> Set<B> mapIntoSet(A[] in, Fn<A, B> fn) { + Set<B> out = new HashSet<>(in.length); + for (A item : in) { + try { + out.add(fn.call(item)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return out; + } + + interface Fn<A,B> { + B call(A a) throws Exception; } } 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 c3313f8cb2ce..789ce6c3092a 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; @@ -45,6 +46,8 @@ import com.android.internal.util.test.FakeSettingsProvider; import java.net.InetAddress; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; import org.junit.After; import org.junit.Before; @@ -186,17 +189,43 @@ public class OffloadControllerTest { any(OffloadHardwareInterface.ControlCallback.class)); inOrder.verifyNoMoreInteractions(); + // In reality, the UpstreamNetworkMonitor would have passed down to us + // a covering set of local prefixes representing a minimum essential + // set plus all the prefixes on networks with network agents. + // + // We simulate that there, and then add upstream elements one by one + // and watch what happens. + final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>(); + for (String s : new String[]{ + "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { + minimumLocalPrefixes.add(new IpPrefix(s)); + } + offload.setLocalPrefixes(minimumLocalPrefixes); + inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); + ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); + assertEquals(4, localPrefixes.size()); + assertTrue(localPrefixes.contains("127.0.0.0/8")); + assertTrue(localPrefixes.contains("192.0.2.0/24")); + assertTrue(localPrefixes.contains("fe80::/64")); + assertTrue(localPrefixes.contains("2001:db8::/64")); + inOrder.verifyNoMoreInteractions(); + offload.setUpstreamLinkProperties(null); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(null), eq(null), eq(null), eq(null)); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); + // This LinkProperties value does not differ from the default upstream. + // There should be no extraneous call to setUpstreamParameters(). + inOrder.verify(mHardware, never()).setUpstreamParameters( + anyObject(), anyObject(), anyObject(), anyObject()); inOrder.verifyNoMoreInteractions(); - reset(mHardware); final LinkProperties lp = new LinkProperties(); final String testIfName = "rmnet_data17"; lp.setInterfaceName(testIfName); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(null), eq(null), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -204,7 +233,15 @@ public class OffloadControllerTest { final String ipv4Addr = "192.0.2.5"; final String linkAddr = ipv4Addr + "/24"; lp.addLinkAddress(new LinkAddress(linkAddr)); + lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"))); offload.setUpstreamLinkProperties(lp); + // IPv4 prefixes and addresses on the upstream are simply left as whole + // prefixes (already passed in from UpstreamNetworkMonitor code). If a + // tethering client sends traffic to the IPv4 default router or other + // clients on the upstream this will not be hardware-forwarded, and that + // should be fine for now. Ergo: no change in local addresses, no call + // to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(null), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -212,6 +249,8 @@ public class OffloadControllerTest { final String ipv4Gateway = "192.0.2.1"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -219,6 +258,8 @@ public class OffloadControllerTest { final String ipv6Gw1 = "fe80::cafe"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); ArrayList<String> v6gws = mStringArrayCaptor.getValue(); @@ -229,6 +270,8 @@ public class OffloadControllerTest { final String ipv6Gw2 = "fe80::d00d"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); v6gws = mStringArrayCaptor.getValue(); @@ -244,6 +287,8 @@ public class OffloadControllerTest { stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00"))); assertTrue(lp.addStackedLink(stacked)); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); v6gws = mStringArrayCaptor.getValue(); @@ -251,5 +296,43 @@ public class OffloadControllerTest { assertTrue(v6gws.contains(ipv6Gw1)); assertTrue(v6gws.contains(ipv6Gw2)); inOrder.verifyNoMoreInteractions(); + + // Add in some IPv6 upstream info. When there is a tethered downstream + // making use of the IPv6 prefix we would expect to see the /64 route + // removed from "local prefixes" and /128s added for the upstream IPv6 + // addresses. This is not yet implemented, and for now we simply + // expect to see these /128s. + lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"))); + // "2001:db8::/64" plus "assigned" ASCII in hex + lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64")); + // "2001:db8::/64" plus "random" ASCII in hex + lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64")); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); + localPrefixes = mStringArrayCaptor.getValue(); + assertEquals(6, localPrefixes.size()); + assertTrue(localPrefixes.contains("127.0.0.0/8")); + assertTrue(localPrefixes.contains("192.0.2.0/24")); + assertTrue(localPrefixes.contains("fe80::/64")); + assertTrue(localPrefixes.contains("2001:db8::/64")); + assertTrue(localPrefixes.contains("2001:db8::6173:7369:676e:6564/128")); + assertTrue(localPrefixes.contains("2001:db8::7261:6e64:6f6d/128")); + // The relevant parts of the LinkProperties have not changed, but at the + // moment we do not de-dup upstream LinkProperties this carefully. + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + v6gws = mStringArrayCaptor.getValue(); + assertEquals(2, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + assertTrue(v6gws.contains(ipv6Gw2)); + inOrder.verifyNoMoreInteractions(); + + // Completely identical LinkProperties updates are de-duped. + offload.setUpstreamLinkProperties(lp); + // This LinkProperties value does not differ from the default upstream. + // There should be no extraneous call to setUpstreamParameters(). + inOrder.verify(mHardware, never()).setUpstreamParameters( + anyObject(), anyObject(), anyObject(), anyObject()); + inOrder.verifyNoMoreInteractions(); } } diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index 69c93b14a486..c3b9defdec4e 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -324,19 +324,14 @@ public class UpstreamNetworkMonitorTest { } @Test - public void testOffloadExemptPrefixes() throws Exception { + public void testLocalPrefixes() throws Exception { mUNM.start(); - // [0] Test minimum set of exempt prefixes. - Set<IpPrefix> exempt = mUNM.getOffloadExemptPrefixes(); - final String[] MINSET = { - "127.0.0.0/8", "169.254.0.0/16", - "::/3", "fe80::/64", "fc00::/7", "ff00::/8", - }; - assertPrefixSet(exempt, INCLUDES, MINSET); + // [0] Test minimum set of local prefixes. + Set<IpPrefix> local = mUNM.getLocalPrefixes(); + assertTrue(local.isEmpty()); + final Set<String> alreadySeen = new HashSet<>(); - Collections.addAll(alreadySeen, MINSET); - assertEquals(alreadySeen.size(), exempt.size()); // [1] Pretend Wi-Fi connects. final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); @@ -355,15 +350,15 @@ public class UpstreamNetworkMonitorTest { wifiAgent.fakeConnect(); wifiAgent.sendLinkProperties(wifiLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] wifiLinkPrefixes = { - // Excludes link-local as that's already tested within MINSET. + // Link-local prefixes are excluded and dealt with elsewhere. "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64", }; - assertPrefixSet(exempt, INCLUDES, wifiLinkPrefixes); + assertPrefixSet(local, INCLUDES, wifiLinkPrefixes); Collections.addAll(alreadySeen, wifiLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [2] Pretend mobile connects. final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); @@ -379,12 +374,12 @@ public class UpstreamNetworkMonitorTest { cellAgent.fakeConnect(); cellAgent.sendLinkProperties(cellLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" }; - assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes); + assertPrefixSet(local, INCLUDES, cellLinkPrefixes); Collections.addAll(alreadySeen, cellLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [3] Pretend DUN connects. final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); @@ -401,21 +396,20 @@ public class UpstreamNetworkMonitorTest { dunAgent.fakeConnect(); dunAgent.sendLinkProperties(dunLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" }; - assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes); + assertPrefixSet(local, INCLUDES, dunLinkPrefixes); Collections.addAll(alreadySeen, dunLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no // longer be included (should be properly removed). wifiAgent.fakeDisconnect(); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, MINSET); - assertPrefixSet(exempt, EXCLUDES, wifiLinkPrefixes); - assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes); - assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); + assertPrefixSet(local, INCLUDES, cellLinkPrefixes); + assertPrefixSet(local, INCLUDES, dunLinkPrefixes); } private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) { |