Merge "Move the test of minimum supported keepalive slots to CTS"
diff --git a/Android.bp b/Android.bp
index 7dcafb6..78509ed 100644
--- a/Android.bp
+++ b/Android.bp
@@ -549,6 +549,7 @@
"telephony/java/com/android/ims/ImsConfigListener.aidl",
"telephony/java/com/android/internal/telephony/IApnSourceService.aidl",
"telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl",
+ "telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl",
"telephony/java/com/android/internal/telephony/IMms.aidl",
"telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl",
"telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
diff --git a/api/current.txt b/api/current.txt
index 9bee5ec..015874d 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -29110,7 +29110,6 @@
}
public final class NfcAdapter {
- method public boolean deviceSupportsNfcSecure();
method public void disableForegroundDispatch(android.app.Activity);
method @Deprecated public void disableForegroundNdefPush(android.app.Activity);
method public void disableReaderMode(android.app.Activity);
@@ -29122,7 +29121,8 @@
method @Deprecated public boolean invokeBeam(android.app.Activity);
method public boolean isEnabled();
method @Deprecated public boolean isNdefPushEnabled();
- method public boolean isNfcSecureEnabled();
+ method public boolean isSecureNfcEnabled();
+ method public boolean isSecureNfcSupported();
method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity);
method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
diff --git a/api/system-current.txt b/api/system-current.txt
index 3170a0b..7f29b1d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3958,9 +3958,9 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setNfcSecure(boolean);
field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
}
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index d387531..f925023 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -6,11 +6,11 @@
multilib: {
lib32: {
version_script: ":art_sigchain_version_script32.txt",
- stem: "app_process32",
+ suffix: "32",
},
lib64: {
version_script: ":art_sigchain_version_script64.txt",
- stem: "app_process64",
+ suffix: "64",
},
},
@@ -21,6 +21,7 @@
"libbinder",
"libcutils",
"libdl",
+ "libhidlbase",
"libhwbinder",
"liblog",
"libnativeloader",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f04f017..6506df2 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2818,7 +2818,7 @@
// Only valid for event_type = EVENT_RESNSEND.
optional int32 res_nsend_flags = 5;
- optional android.stats.dnsresolver.Transport network_type = 6;
+ optional android.stats.dnsresolver.NetworkType network_type = 6;
// The DNS over TLS mode on a specific netId.
optional android.stats.dnsresolver.PrivateDnsModes private_dns_modes = 7;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 31bbd16..39d63de 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1028,7 +1028,8 @@
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
@AdapterState
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
+ + "whether you can use BLE & BT classic.")
public int getLeState() {
int state = BluetoothAdapter.STATE_OFF;
@@ -1484,7 +1485,8 @@
* @return true if the scan mode was set, false otherwise
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which "
+ + "shows UI that confirms the user wants to go into discoverable mode.")
public boolean setScanMode(@ScanMode int mode, int duration) {
if (getState() != STATE_ON) {
return false;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 34c7372..ee33103 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1051,7 +1051,7 @@
* @return the Bluetooth alias, or null if no alias or there was a problem
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.")
public String getAlias() {
final IBluetooth service = sService;
if (service == null) {
@@ -1100,7 +1100,7 @@
* @see #getAlias()
* @see #getName()
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.")
public String getAliasName() {
String name = getAlias();
if (name == null) {
@@ -1975,7 +1975,8 @@
* permissions.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use "
+ + "{@link #createInsecureRfcommSocketToServiceRecord} instead.")
public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 9862a63..672174f 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -280,6 +280,7 @@
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
* {@link #ACTION_AUDIO_STATE_CHANGED} intent.
*/
+ public static final int STATE_AUDIO_CONNECTED = 12;
/**
* Intent used to broadcast the headset's indicator status
@@ -322,8 +323,6 @@
public static final String EXTRA_HF_INDICATORS_IND_VALUE =
"android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE";
- public static final int STATE_AUDIO_CONNECTED = 12;
-
private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100;
private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101;
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index c06b837..3a23808 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -77,7 +77,8 @@
private static final String TAG = "BluetoothServerSocket";
private static final boolean DBG = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use public {@link BluetoothServerSocket} API "
+ + "instead.")
/*package*/ final BluetoothSocket mSocket;
private Handler mHandler;
private int mMessage;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 3a1e2f5..a6e3153 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -131,7 +131,7 @@
private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */
private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/
private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.")
private ParcelFileDescriptor mPfd;
@UnsupportedAppUsage
private LocalSocket mSocket;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fbfbfc0..111a8c4 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -655,7 +655,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
+ @UnsupportedAppUsage
public static final int TYPE_WIFI_P2P = 13;
/**
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index 7a85dcb..0b1a845 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -34,6 +34,7 @@
import android.annotation.Nullable;
import android.os.CancellationSignal;
import android.os.Looper;
+import android.os.MessageQueue;
import android.system.ErrnoException;
import android.util.Log;
@@ -466,10 +467,20 @@
private void registerFDListener(@NonNull Executor executor,
@NonNull FileDescriptor queryfd, @NonNull Callback<? super byte[]> answerCallback,
@Nullable CancellationSignal cancellationSignal, @NonNull Object lock) {
- Looper.getMainLooper().getQueue().addOnFileDescriptorEventListener(
+ final MessageQueue mainThreadMessageQueue = Looper.getMainLooper().getQueue();
+ mainThreadMessageQueue.addOnFileDescriptorEventListener(
queryfd,
FD_EVENTS,
(fd, events) -> {
+ // b/134310704
+ // Unregister fd event listener before resNetworkResult is called to prevent
+ // race condition caused by fd reused.
+ // For example when querying v4 and v6, it's possible that the first query ends
+ // and the fd is closed before the second request starts, which might return
+ // the same fd for the second request. By that time, the looper must have
+ // unregistered the fd, otherwise another event listener can't be registered.
+ mainThreadMessageQueue.removeOnFileDescriptorEventListener(fd);
+
executor.execute(() -> {
DnsResponse resp = null;
ErrnoException exception = null;
@@ -490,7 +501,11 @@
}
answerCallback.onAnswer(resp.answerbuf, resp.rcode);
});
- // Unregister this fd listener
+
+ // The file descriptor has already been unregistered, so it does not really
+ // matter what is returned here. In spirit 0 (meaning "unregister this FD")
+ // is still the closest to what the looper needs to do. When returning 0,
+ // Looper knows to ignore the fd if it has already been unregistered.
return 0;
});
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 41efc50..e5f3d26 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -66,7 +66,6 @@
/** Force update of ifaces. */
void forceUpdateIfaces(
in Network[] defaultNetworks,
- in VpnInfo[] vpnArray,
in NetworkState[] networkStates,
in String activeIface);
/** Force update of statistics. */
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index b27b1c0..14a0cbf 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -23,7 +23,6 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
-import android.util.Slog;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -34,10 +33,10 @@
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.function.Predicate;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
+import java.util.function.Predicate;
/**
* Collection of active network statistics. Can contain summary details across
@@ -996,8 +995,8 @@
return;
}
filter(e -> (limitUid == UID_ALL || limitUid == e.uid)
- && (limitTag == TAG_ALL || limitTag == e.tag)
- && (limitIfaces == INTERFACES_ALL
+ && (limitTag == TAG_ALL || limitTag == e.tag)
+ && (limitIfaces == INTERFACES_ALL
|| ArrayUtils.contains(limitIfaces, e.iface)));
}
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index 46eddde..ec73866 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -44,9 +44,11 @@
* {@link SocketKeepalive.Callback#onStopped} if the operation was successful or
* {@link SocketKeepalive.Callback#onError} if an error occurred.
*
- * The device SHOULD support keepalive offload. If it does not, it MUST reply with
+ * For cellular, the device MUST support at least 1 keepalive slot.
+ *
+ * For WiFi, the device SHOULD support keepalive offload. If it does not, it MUST reply with
* {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload
- * request. If it does, it MUST support at least 3 concurrent keepalive slots per transport.
+ * request. If it does, it MUST support at least 3 concurrent keepalive slots.
*/
public abstract class SocketKeepalive implements AutoCloseable {
static final String TAG = "SocketKeepalive";
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index eb347e7..bc698f9 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -1714,11 +1714,12 @@
/**
* Sets Secure NFC feature.
* <p>This API is for the Settings application.
+ * @return True if successful
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
- public boolean setNfcSecure(boolean enable) {
+ public boolean enableSecureNfc(boolean enable) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1736,7 +1737,7 @@
* @return True if device supports Secure NFC, false otherwise
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
*/
- public boolean deviceSupportsNfcSecure() {
+ public boolean isSecureNfcSupported() {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
@@ -1751,12 +1752,12 @@
/**
* Checks Secure NFC feature is enabled.
*
- * @return True if device supports Secure NFC is enabled, false otherwise
+ * @return True if Secure NFC is enabled, false otherwise
* @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
* @throws UnsupportedOperationException if device doesn't support
- * Secure NFC functionality. {@link #deviceSupportsNfcSecure}
+ * Secure NFC functionality. {@link #isSecureNfcSupported}
*/
- public boolean isNfcSecureEnabled() {
+ public boolean isSecureNfcEnabled() {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index fc9c495..69cdf79 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -618,7 +618,7 @@
*
* Returns 0 on success.
*/
-int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
+int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{
JavaVMInitArgs initArgs;
char propBuf[PROPERTY_VALUE_MAX];
@@ -749,6 +749,10 @@
addOption("-verbose:gc");
//addOption("-verbose:class");
+ if (primary_zygote) {
+ addOption("-Xprimaryzygote");
+ }
+
/*
* The default starting and maximum size of the heap. Larger
* values should be specified in a product property override.
@@ -1110,6 +1114,8 @@
className != NULL ? className : "(unknown)", getuid());
static const String8 startSystemServer("start-system-server");
+ // Whether this is the primary zygote, meaning the zygote which will fork system server.
+ bool primary_zygote = false;
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
@@ -1117,6 +1123,7 @@
*/
for (size_t i = 0; i < options.size(); ++i) {
if (options[i] == startSystemServer) {
+ primary_zygote = true;
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
@@ -1152,7 +1159,7 @@
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
- if (startVm(&mJavaVM, &env, zygote) != 0) {
+ if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
return;
}
onVmCreated(env);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 7114dfc..9556333 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -156,8 +156,9 @@
static constexpr int32_t PROXY_WARN_INTERVAL = 5000;
static constexpr uint32_t GC_INTERVAL = 1000;
-static std::atomic<uint32_t> gNumProxies(0);
-static std::atomic<uint32_t> gProxiesWarned(0);
+// Protected by gProxyLock. We warn if this gets too large.
+static int32_t gNumProxies = 0;
+static int32_t gProxiesWarned = 0;
// Number of GlobalRefs held by JavaBBinders.
static std::atomic<uint32_t> gNumLocalRefsCreated(0);
@@ -659,6 +660,12 @@
return (BinderProxyNativeData *) env->GetLongField(obj, gBinderProxyOffsets.mNativeData);
}
+static Mutex gProxyLock;
+
+// We may cache a single BinderProxyNativeData node to avoid repeat allocation.
+// All fields are null. Protected by gProxyLock.
+static BinderProxyNativeData *gNativeDataCache;
+
// If the argument is a JavaBBinder, return the Java object that was used to create it.
// Otherwise return a BinderProxy for the IBinder. If a previous call was passed the
// same IBinder, and the original BinderProxy is still alive, return the same BinderProxy.
@@ -673,31 +680,36 @@
return object;
}
- BinderProxyNativeData* nativeData = new BinderProxyNativeData();
- nativeData->mOrgue = new DeathRecipientList;
- nativeData->mObject = val;
+ // For the rest of the function we will hold this lock, to serialize
+ // looking/creation/destruction of Java proxies for native Binder proxies.
+ AutoMutex _l(gProxyLock);
+ BinderProxyNativeData* nativeData = gNativeDataCache;
+ if (nativeData == nullptr) {
+ nativeData = new BinderProxyNativeData();
+ }
+ // gNativeDataCache is now logically empty.
jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get());
if (env->ExceptionCheck()) {
// In the exception case, getInstance still took ownership of nativeData.
+ gNativeDataCache = nullptr;
return NULL;
}
BinderProxyNativeData* actualNativeData = getBPNativeData(env, object);
if (actualNativeData == nativeData) {
- // Created a new Proxy
- uint32_t numProxies = gNumProxies.fetch_add(1, std::memory_order_relaxed);
- uint32_t numLastWarned = gProxiesWarned.load(std::memory_order_relaxed);
- if (numProxies >= numLastWarned + PROXY_WARN_INTERVAL) {
- // Multiple threads can get here, make sure only one of them gets to
- // update the warn counter.
- if (gProxiesWarned.compare_exchange_strong(numLastWarned,
- numLastWarned + PROXY_WARN_INTERVAL, std::memory_order_relaxed)) {
- ALOGW("Unexpectedly many live BinderProxies: %d\n", numProxies);
- }
+ // New BinderProxy; we still have exclusive access.
+ nativeData->mOrgue = new DeathRecipientList;
+ nativeData->mObject = val;
+ gNativeDataCache = nullptr;
+ ++gNumProxies;
+ if (gNumProxies >= gProxiesWarned + PROXY_WARN_INTERVAL) {
+ ALOGW("Unexpectedly many live BinderProxies: %d\n", gNumProxies);
+ gProxiesWarned = gNumProxies;
}
} else {
- delete nativeData;
+ // nativeData wasn't used. Reuse it the next time.
+ gNativeDataCache = nativeData;
}
return object;
@@ -977,7 +989,8 @@
jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz)
{
- return gNumProxies.load();
+ AutoMutex _l(gProxyLock);
+ return gNumProxies;
}
jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz)
@@ -1372,6 +1385,9 @@
static void BinderProxy_destroy(void* rawNativeData)
{
+ // Don't race with construction/initialization
+ AutoMutex _l(gProxyLock);
+
BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
nativeData->mObject.get(), nativeData->mOrgue.get());
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 2071b98..6d10fc2 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -238,7 +238,7 @@
}
if (!whitelist->IsAllowed(file_path)) {
- fail_fn(std::string("Not whitelisted : ").append(file_path));
+ fail_fn(android::base::StringPrintf("Not whitelisted (%d): %s", fd, file_path.c_str()));
}
// File descriptor flags : currently on FD_CLOEXEC. We can set these
diff --git a/core/jni/include/android_runtime/AndroidRuntime.h b/core/jni/include/android_runtime/AndroidRuntime.h
index 3ec8b1f..9d803f6 100644
--- a/core/jni/include/android_runtime/AndroidRuntime.h
+++ b/core/jni/include/android_runtime/AndroidRuntime.h
@@ -131,7 +131,7 @@
const char* runtimeArg,
const char* quotingArg);
void parseExtraOpts(char* extraOptsBuf, const char* quotingArg);
- int startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote);
+ int startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote);
Vector<JavaVMOption> mOptions;
bool mExitWithoutCleanup;
diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp
index c0ac2cb..4136239 100644
--- a/core/proto/android/server/connectivity/Android.bp
+++ b/core/proto/android/server/connectivity/Android.bp
@@ -21,5 +21,4 @@
"data_stall_event.proto",
],
sdk_version: "system_current",
- no_framework_libs: true,
-}
\ No newline at end of file
+}
diff --git a/core/proto/android/stats/connectivity/Android.bp b/core/proto/android/stats/connectivity/Android.bp
index 5aa4ddb..5d642d38 100644
--- a/core/proto/android/stats/connectivity/Android.bp
+++ b/core/proto/android/stats/connectivity/Android.bp
@@ -21,5 +21,4 @@
"network_stack.proto",
],
sdk_version: "system_current",
- no_framework_libs: true,
-}
\ No newline at end of file
+}
diff --git a/core/proto/android/stats/dnsresolver/Android.bp b/core/proto/android/stats/dnsresolver/Android.bp
index 0b5aa86..1e8c763 100644
--- a/core/proto/android/stats/dnsresolver/Android.bp
+++ b/core/proto/android/stats/dnsresolver/Android.bp
@@ -21,5 +21,4 @@
"dns_resolver.proto",
],
sdk_version: "system_current",
- no_framework_libs: true,
}
diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto
index af6fea0..b7bf384 100644
--- a/core/proto/android/stats/dnsresolver/dns_resolver.proto
+++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto
@@ -1,214 +1,216 @@
-/*
- * Copyright (C) 2019 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.
- */
-syntax = "proto2";
-package android.stats.dnsresolver;
-
-enum EventType {
- EVENT_UNKNOWN = 0;
- EVENT_GETADDRINFO = 1;
- EVENT_GETHOSTBYNAME = 2;
- EVENT_GETHOSTBYADDR = 3;
- EVENT_RES_NSEND = 4;
-}
-
-// The return value of the DNS resolver for each DNS lookups.
-// bionic/libc/include/netdb.h
-// system/netd/resolv/include/netd_resolv/resolv.h
-enum ReturnCode {
- RC_EAI_NO_ERROR = 0;
- RC_EAI_ADDRFAMILY = 1;
- RC_EAI_AGAIN = 2;
- RC_EAI_BADFLAGS = 3;
- RC_EAI_FAIL = 4;
- RC_EAI_FAMILY = 5;
- RC_EAI_MEMORY = 6;
- RC_EAI_NODATA = 7;
- RC_EAI_NONAME = 8;
- RC_EAI_SERVICE = 9;
- RC_EAI_SOCKTYPE = 10;
- RC_EAI_SYSTEM = 11;
- RC_EAI_BADHINTS = 12;
- RC_EAI_PROTOCOL = 13;
- RC_EAI_OVERFLOW = 14;
- RC_RESOLV_TIMEOUT = 255;
- RC_EAI_MAX = 256;
-}
-
-enum NsRcode {
- NS_R_NO_ERROR = 0; // No error occurred.
- NS_R_FORMERR = 1; // Format error.
- NS_R_SERVFAIL = 2; // Server failure.
- NS_R_NXDOMAIN = 3; // Name error.
- NS_R_NOTIMPL = 4; // Unimplemented.
- NS_R_REFUSED = 5; // Operation refused.
- // these are for BIND_UPDATE
- NS_R_YXDOMAIN = 6; // Name exists
- NS_R_YXRRSET = 7; // RRset exists
- NS_R_NXRRSET = 8; // RRset does not exist
- NS_R_NOTAUTH = 9; // Not authoritative for zone
- NS_R_NOTZONE = 10; // Zone of record different from zone section
- NS_R_MAX = 11;
- // The following are EDNS extended rcodes
- NS_R_BADVERS = 16;
- // The following are TSIG errors
- // NS_R_BADSIG = 16,
- NS_R_BADKEY = 17;
- NS_R_BADTIME = 18;
-}
-
-// Currently defined type values for resources and queries.
-enum NsType {
- NS_T_INVALID = 0; // Cookie.
- NS_T_A = 1; // Host address.
- NS_T_NS = 2; // Authoritative server.
- NS_T_MD = 3; // Mail destination.
- NS_T_MF = 4; // Mail forwarder.
- NS_T_CNAME = 5; // Canonical name.
- NS_T_SOA = 6; // Start of authority zone.
- NS_T_MB = 7; // Mailbox domain name.
- NS_T_MG = 8; // Mail group member.
- NS_T_MR = 9; // Mail rename name.
- NS_T_NULL = 10; // Null resource record.
- NS_T_WKS = 11; // Well known service.
- NS_T_PTR = 12; // Domain name pointer.
- NS_T_HINFO = 13; // Host information.
- NS_T_MINFO = 14; // Mailbox information.
- NS_T_MX = 15; // Mail routing information.
- NS_T_TXT = 16; // Text strings.
- NS_T_RP = 17; // Responsible person.
- NS_T_AFSDB = 18; // AFS cell database.
- NS_T_X25 = 19; // X_25 calling address.
- NS_T_ISDN = 20; // ISDN calling address.
- NS_T_RT = 21; // Router.
- NS_T_NSAP = 22; // NSAP address.
- NS_T_NSAP_PTR = 23; // Reverse NSAP lookup (deprecated).
- NS_T_SIG = 24; // Security signature.
- NS_T_KEY = 25; // Security key.
- NS_T_PX = 26; // X.400 mail mapping.
- NS_T_GPOS = 27; // Geographical position (withdrawn).
- NS_T_AAAA = 28; // IPv6 Address.
- NS_T_LOC = 29; // Location Information.
- NS_T_NXT = 30; // Next domain (security).
- NS_T_EID = 31; // Endpoint identifier.
- NS_T_NIMLOC = 32; // Nimrod Locator.
- NS_T_SRV = 33; // Server Selection.
- NS_T_ATMA = 34; // ATM Address
- NS_T_NAPTR = 35; // Naming Authority PoinTeR
- NS_T_KX = 36; // Key Exchange
- NS_T_CERT = 37; // Certification record
- NS_T_A6 = 38; // IPv6 address (experimental)
- NS_T_DNAME = 39; // Non-terminal DNAME
- NS_T_SINK = 40; // Kitchen sink (experimentatl)
- NS_T_OPT = 41; // EDNS0 option (meta-RR)
- NS_T_APL = 42; // Address prefix list (RFC 3123)
- NS_T_DS = 43; // Delegation Signer
- NS_T_SSHFP = 44; // SSH Fingerprint
- NS_T_IPSECKEY = 45; // IPSEC Key
- NS_T_RRSIG = 46; // RRset Signature
- NS_T_NSEC = 47; // Negative security
- NS_T_DNSKEY = 48; // DNS Key
- NS_T_DHCID = 49; // Dynamic host configuratin identifier
- NS_T_NSEC3 = 50; // Negative security type 3
- NS_T_NSEC3PARAM = 51; // Negative security type 3 parameters
- NS_T_HIP = 55; // Host Identity Protocol
- NS_T_SPF = 99; // Sender Policy Framework
- NS_T_TKEY = 249; // Transaction key
- NS_T_TSIG = 250; // Transaction signature.
- NS_T_IXFR = 251; // Incremental zone transfer.
- NS_T_AXFR = 252; // Transfer zone of authority.
- NS_T_MAILB = 253; // Transfer mailbox records.
- NS_T_MAILA = 254; // Transfer mail agent records.
- NS_T_ANY = 255; // Wildcard match.
- NS_T_ZXFR = 256; // BIND-specific, nonstandard.
- NS_T_DLV = 32769; // DNSSEC look-aside validatation.
- NS_T_MAX = 65536;
-}
-
-enum IpVersion {
- IV_UNKNOWN = 0;
- IV_IPV4 = 1;
- IV_IPV6 = 2;
-}
-
-enum TransportType {
- TT_UNKNOWN = 0;
- TT_UDP = 1;
- TT_TCP = 2;
- TT_DOT = 3;
-}
-
-enum PrivateDnsModes {
- PDM_UNKNOWN = 0;
- PDM_OFF = 1;
- PDM_OPPORTUNISTIC = 2;
- PDM_STRICT = 3;
-}
-
-enum Transport {
- // Indicates this network uses a Cellular transport.
- TRANSPORT_DEFAULT = 0; // TRANSPORT_CELLULAR
- // Indicates this network uses a Wi-Fi transport.
- TRANSPORT_WIFI = 1;
- // Indicates this network uses a Bluetooth transport.
- TRANSPORT_BLUETOOTH = 2;
- // Indicates this network uses an Ethernet transport.
- TRANSPORT_ETHERNET = 3;
- // Indicates this network uses a VPN transport.
- TRANSPORT_VPN = 4;
- // Indicates this network uses a Wi-Fi Aware transport.
- TRANSPORT_WIFI_AWARE = 5;
- // Indicates this network uses a LoWPAN transport.
- TRANSPORT_LOWPAN = 6;
-}
-
-enum CacheStatus{
- // the cache can't handle that kind of queries.
- // or the answer buffer is too small.
- CS_UNSUPPORTED = 0;
- // the cache doesn't know about this query.
- CS_NOTFOUND = 1;
- // the cache found the answer.
- CS_FOUND = 2;
- // Don't do anything on cache.
- CS_SKIP = 3;
-}
-
-message DnsQueryEvent {
- optional android.stats.dnsresolver.NsRcode rcode = 1;
-
- optional android.stats.dnsresolver.NsType type = 2;
-
- optional android.stats.dnsresolver.CacheStatus cache_hit = 3;
-
- optional android.stats.dnsresolver.IpVersion ip_version = 4;
-
- optional android.stats.dnsresolver.TransportType transport = 5;
-
- // Number of DNS query retry times
- optional int32 retry_times = 6;
-
- // Ordinal number of name server.
- optional int32 dns_server_count = 7;
-
- // Used only by TCP and DOT. True for new connections.
- optional bool connected = 8;
-
- optional int32 latency_micros = 9;
-}
-
-message DnsQueryEvents {
- repeated DnsQueryEvent dns_query_event = 1;
-}
+/*
+ * Copyright (C) 2019 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.
+ */
+syntax = "proto2";
+package android.stats.dnsresolver;
+
+enum EventType {
+ EVENT_UNKNOWN = 0;
+ EVENT_GETADDRINFO = 1;
+ EVENT_GETHOSTBYNAME = 2;
+ EVENT_GETHOSTBYADDR = 3;
+ EVENT_RES_NSEND = 4;
+}
+
+// The return value of the DNS resolver for each DNS lookups.
+// bionic/libc/include/netdb.h
+// system/netd/resolv/include/netd_resolv/resolv.h
+enum ReturnCode {
+ RC_EAI_NO_ERROR = 0;
+ RC_EAI_ADDRFAMILY = 1;
+ RC_EAI_AGAIN = 2;
+ RC_EAI_BADFLAGS = 3;
+ RC_EAI_FAIL = 4;
+ RC_EAI_FAMILY = 5;
+ RC_EAI_MEMORY = 6;
+ RC_EAI_NODATA = 7;
+ RC_EAI_NONAME = 8;
+ RC_EAI_SERVICE = 9;
+ RC_EAI_SOCKTYPE = 10;
+ RC_EAI_SYSTEM = 11;
+ RC_EAI_BADHINTS = 12;
+ RC_EAI_PROTOCOL = 13;
+ RC_EAI_OVERFLOW = 14;
+ RC_RESOLV_TIMEOUT = 255;
+ RC_EAI_MAX = 256;
+}
+
+enum NsRcode {
+ NS_R_NO_ERROR = 0; // No error occurred.
+ NS_R_FORMERR = 1; // Format error.
+ NS_R_SERVFAIL = 2; // Server failure.
+ NS_R_NXDOMAIN = 3; // Name error.
+ NS_R_NOTIMPL = 4; // Unimplemented.
+ NS_R_REFUSED = 5; // Operation refused.
+ // these are for BIND_UPDATE
+ NS_R_YXDOMAIN = 6; // Name exists
+ NS_R_YXRRSET = 7; // RRset exists
+ NS_R_NXRRSET = 8; // RRset does not exist
+ NS_R_NOTAUTH = 9; // Not authoritative for zone
+ NS_R_NOTZONE = 10; // Zone of record different from zone section
+ NS_R_MAX = 11;
+ // The following are EDNS extended rcodes
+ NS_R_BADVERS = 16;
+ // The following are TSIG errors
+ // NS_R_BADSIG = 16,
+ NS_R_BADKEY = 17;
+ NS_R_BADTIME = 18;
+ NS_R_TIMEOUT = 255;
+}
+
+// Currently defined type values for resources and queries.
+enum NsType {
+ NS_T_INVALID = 0; // Cookie.
+ NS_T_A = 1; // Host address.
+ NS_T_NS = 2; // Authoritative server.
+ NS_T_MD = 3; // Mail destination.
+ NS_T_MF = 4; // Mail forwarder.
+ NS_T_CNAME = 5; // Canonical name.
+ NS_T_SOA = 6; // Start of authority zone.
+ NS_T_MB = 7; // Mailbox domain name.
+ NS_T_MG = 8; // Mail group member.
+ NS_T_MR = 9; // Mail rename name.
+ NS_T_NULL = 10; // Null resource record.
+ NS_T_WKS = 11; // Well known service.
+ NS_T_PTR = 12; // Domain name pointer.
+ NS_T_HINFO = 13; // Host information.
+ NS_T_MINFO = 14; // Mailbox information.
+ NS_T_MX = 15; // Mail routing information.
+ NS_T_TXT = 16; // Text strings.
+ NS_T_RP = 17; // Responsible person.
+ NS_T_AFSDB = 18; // AFS cell database.
+ NS_T_X25 = 19; // X_25 calling address.
+ NS_T_ISDN = 20; // ISDN calling address.
+ NS_T_RT = 21; // Router.
+ NS_T_NSAP = 22; // NSAP address.
+ NS_T_NSAP_PTR = 23; // Reverse NSAP lookup (deprecated).
+ NS_T_SIG = 24; // Security signature.
+ NS_T_KEY = 25; // Security key.
+ NS_T_PX = 26; // X.400 mail mapping.
+ NS_T_GPOS = 27; // Geographical position (withdrawn).
+ NS_T_AAAA = 28; // IPv6 Address.
+ NS_T_LOC = 29; // Location Information.
+ NS_T_NXT = 30; // Next domain (security).
+ NS_T_EID = 31; // Endpoint identifier.
+ NS_T_NIMLOC = 32; // Nimrod Locator.
+ NS_T_SRV = 33; // Server Selection.
+ NS_T_ATMA = 34; // ATM Address
+ NS_T_NAPTR = 35; // Naming Authority PoinTeR
+ NS_T_KX = 36; // Key Exchange
+ NS_T_CERT = 37; // Certification record
+ NS_T_A6 = 38; // IPv6 address (experimental)
+ NS_T_DNAME = 39; // Non-terminal DNAME
+ NS_T_SINK = 40; // Kitchen sink (experimentatl)
+ NS_T_OPT = 41; // EDNS0 option (meta-RR)
+ NS_T_APL = 42; // Address prefix list (RFC 3123)
+ NS_T_DS = 43; // Delegation Signer
+ NS_T_SSHFP = 44; // SSH Fingerprint
+ NS_T_IPSECKEY = 45; // IPSEC Key
+ NS_T_RRSIG = 46; // RRset Signature
+ NS_T_NSEC = 47; // Negative security
+ NS_T_DNSKEY = 48; // DNS Key
+ NS_T_DHCID = 49; // Dynamic host configuratin identifier
+ NS_T_NSEC3 = 50; // Negative security type 3
+ NS_T_NSEC3PARAM = 51; // Negative security type 3 parameters
+ NS_T_HIP = 55; // Host Identity Protocol
+ NS_T_SPF = 99; // Sender Policy Framework
+ NS_T_TKEY = 249; // Transaction key
+ NS_T_TSIG = 250; // Transaction signature.
+ NS_T_IXFR = 251; // Incremental zone transfer.
+ NS_T_AXFR = 252; // Transfer zone of authority.
+ NS_T_MAILB = 253; // Transfer mailbox records.
+ NS_T_MAILA = 254; // Transfer mail agent records.
+ NS_T_ANY = 255; // Wildcard match.
+ NS_T_ZXFR = 256; // BIND-specific, nonstandard.
+ NS_T_DLV = 32769; // DNSSEC look-aside validatation.
+ NS_T_MAX = 65536;
+}
+
+enum IpVersion {
+ IV_UNKNOWN = 0;
+ IV_IPV4 = 1;
+ IV_IPV6 = 2;
+}
+
+enum Protocol {
+ PROTO_UNKNOWN = 0;
+ PROTO_UDP = 1;
+ PROTO_TCP = 2;
+ PROTO_DOT = 3;
+}
+
+enum PrivateDnsModes {
+ PDM_UNKNOWN = 0;
+ PDM_OFF = 1;
+ PDM_OPPORTUNISTIC = 2;
+ PDM_STRICT = 3;
+}
+
+enum NetworkType {
+ NT_UNKNOWN = 0;
+ // Indicates this network uses a Cellular transport.
+ NT_CELLULAR = 1;
+ // Indicates this network uses a Wi-Fi transport.
+ NT_WIFI = 2;
+ // Indicates this network uses a Bluetooth transport.
+ NT_BLUETOOTH = 3;
+ // Indicates this network uses an Ethernet transport.
+ NT_ETHERNET = 4;
+ // Indicates this network uses a VPN transport.
+ NT_VPN = 5;
+ // Indicates this network uses a Wi-Fi Aware transport.
+ NT_WIFI_AWARE = 6;
+ // Indicates this network uses a LoWPAN transport.
+ NT_LOWPAN = 7;
+}
+
+enum CacheStatus{
+ // the cache can't handle that kind of queries.
+ // or the answer buffer is too small.
+ CS_UNSUPPORTED = 0;
+ // the cache doesn't know about this query.
+ CS_NOTFOUND = 1;
+ // the cache found the answer.
+ CS_FOUND = 2;
+ // Don't do anything on cache.
+ CS_SKIP = 3;
+}
+
+message DnsQueryEvent {
+ optional android.stats.dnsresolver.NsRcode rcode = 1;
+
+ optional android.stats.dnsresolver.NsType type = 2;
+
+ optional android.stats.dnsresolver.CacheStatus cache_hit = 3;
+
+ optional android.stats.dnsresolver.IpVersion ip_version = 4;
+
+ optional android.stats.dnsresolver.Protocol protocol = 5;
+
+ // Number of DNS query retry times
+ optional int32 retry_times = 6;
+
+ // Ordinal number of name server.
+ optional int32 dns_server_index = 7;
+
+ // Used only by TCP and DOT. True for new connections.
+ optional bool connected = 8;
+
+ optional int32 latency_micros = 9;
+}
+
+message DnsQueryEvents {
+ repeated DnsQueryEvent dns_query_event = 1;
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f94a5bd..1fce5be 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -353,7 +353,7 @@
overridden by the device to present the capability of creating socket keepalives. -->
<!-- An Array of "[NetworkCapabilities.TRANSPORT_*],[supported keepalives] -->
<string-array translatable="false" name="config_networkSupportedKeepaliveCount">
- <item>0,3</item>
+ <item>0,1</item>
<item>1,3</item>
</string-array>
diff --git a/core/tests/benchmarks/src/android/text/format/AndroidTimeVsOthersBenchmark.java b/core/tests/benchmarks/src/android/text/format/AndroidTimeVsOthersBenchmark.java
new file mode 100644
index 0000000..ea24400
--- /dev/null
+++ b/core/tests/benchmarks/src/android/text/format/AndroidTimeVsOthersBenchmark.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 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.text.format;
+
+import com.google.caliper.Benchmark;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+
+public class AndroidTimeVsOthersBenchmark {
+
+ private static final String[] TIMEZONE_IDS = {
+ "Europe/London",
+ "America/Los_Angeles",
+ "Asia/Shanghai",
+ };
+
+ @Benchmark
+ public void toMillis_androidTime(int reps) {
+ long answer = 0;
+ for (int i = 0; i < reps; i++) {
+ String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length];
+ Time time = new Time(timezoneId);
+ time.set(1, 2, 3, 4, 5, 2010);
+ answer = time.toMillis(false);
+ }
+ // System.out.println(answer);
+ }
+
+ @Benchmark
+ public void toMillis_javaTime(int reps) {
+ long answer = 0;
+ for (int i = 0; i < reps; i++) {
+ String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length];
+ LocalDateTime time = LocalDateTime.of(2010, 5 + 1, 4, 3, 2, 1);
+ ZoneOffset offset = ZoneId.of(timezoneId).getRules().getOffset(time);
+ answer = time.toInstant(offset).toEpochMilli();
+ }
+ // System.out.println(answer);
+ }
+
+ @Benchmark
+ public void toMillis_javaUtil(int reps) {
+ long answer = 0;
+ for (int i = 0; i < reps; i++) {
+ String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length];
+ java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(timezoneId);
+ java.util.Calendar calendar = new java.util.GregorianCalendar(timeZone);
+ calendar.set(2010, 5, 4, 3, 2, 1);
+ calendar.set(java.util.Calendar.MILLISECOND, 0);
+ answer = calendar.getTimeInMillis();
+ }
+ // System.out.println(answer);
+ }
+
+ @Benchmark
+ public void toMillis_androidIucUtil(int reps) {
+ long answer = 0;
+ for (int i = 0; i < reps; i++) {
+ String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length];
+ android.icu.util.TimeZone timeZone =
+ android.icu.util.TimeZone.getTimeZone(timezoneId);
+ android.icu.util.Calendar calendar = new android.icu.util.GregorianCalendar(timeZone);
+ calendar.set(2010, 5, 4, 3, 2, 1);
+ calendar.set(android.icu.util.Calendar.MILLISECOND, 0);
+ answer = calendar.getTimeInMillis();
+ }
+ // System.out.println(answer);
+ }
+}
diff --git a/graphics/proto/Android.bp b/graphics/proto/Android.bp
index 1d06348..ddced59 100644
--- a/graphics/proto/Android.bp
+++ b/graphics/proto/Android.bp
@@ -5,7 +5,6 @@
type: "lite",
},
srcs: ["game_driver.proto"],
- no_framework_libs: true,
jarjar_rules: "jarjar-rules.txt",
sdk_version: "28",
}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 7ba7828..5a8579a 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -211,7 +211,7 @@
return false;
}
- ::ZipString name;
+ std::string name;
::ZipEntry entry;
// We need to hold back directories because many paths will contain them and we want to only
@@ -220,7 +220,7 @@
int32_t result;
while ((result = ::Next(cookie, &entry, &name)) == 0) {
- StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length);
+ StringPiece full_file_path(name);
StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
if (!leaf_file_path.empty()) {
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index ee5f778..e77ac3d 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -39,7 +39,7 @@
class _ZipEntryRO {
public:
ZipEntry entry;
- ZipString name;
+ std::string_view name;
void *cookie;
_ZipEntryRO() : cookie(NULL) {}
@@ -96,7 +96,7 @@
{
_ZipEntryRO* data = new _ZipEntryRO;
- data->name = ZipString(entryName);
+ data->name = entryName;
const int32_t error = FindEntry(mHandle, entryName, &(data->entry));
if (error) {
@@ -194,14 +194,14 @@
const
{
const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
- const uint16_t requiredSize = zipEntry->name.name_length + 1;
+ const uint16_t requiredSize = zipEntry->name.length() + 1;
if (bufLen < requiredSize) {
ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize);
return requiredSize;
}
- memcpy(buffer, zipEntry->name.name, requiredSize - 1);
+ memcpy(buffer, zipEntry->name.data(), requiredSize - 1);
buffer[requiredSize - 1] = '\0';
return 0;
diff --git a/proto/Android.bp b/proto/Android.bp
index e924a4d..5523c53 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -25,6 +25,5 @@
type: "nano",
},
srcs: ["src/metrics_constants.proto"],
- no_framework_libs: true,
sdk_version: "system_current",
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 07c9dc1..e72c9bf 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -192,6 +192,7 @@
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.net.NetworkStatsFactory;
import com.android.server.utils.PriorityDump;
import com.google.android.collect.Lists;
@@ -966,6 +967,8 @@
}
}
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+
mTethering = makeTethering();
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1013,8 +1016,6 @@
final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext);
dataConnectionStats.startMonitoring();
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-
mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);
mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager,
mContext.getSystemService(NotificationManager.class));
@@ -4395,8 +4396,12 @@
List<String> interfaces = new ArrayList<>();
for (Network network : underlyingNetworks) {
LinkProperties lp = getLinkProperties(network);
- if (lp != null && !TextUtils.isEmpty(lp.getInterfaceName())) {
- interfaces.add(lp.getInterfaceName());
+ if (lp != null) {
+ for (String iface : lp.getAllInterfaceNames()) {
+ if (!TextUtils.isEmpty(iface)) {
+ interfaces.add(iface);
+ }
+ }
}
}
if (!interfaces.isEmpty()) {
@@ -6783,8 +6788,8 @@
}
/**
- * Notify NetworkStatsService that the set of active ifaces has changed, or that one of the
- * properties tracked by NetworkStatsService on an active iface has changed.
+ * Notify NetworkStatsService and NetworkStatsFactory that the set of active ifaces has changed,
+ * or that one of the active iface's trackedproperties has changed.
*/
private void notifyIfacesChangedForNetworkStats() {
ensureRunningOnConnectivityServiceThread();
@@ -6793,11 +6798,17 @@
if (activeLinkProperties != null) {
activeIface = activeLinkProperties.getInterfaceName();
}
+
+ // CAUTION: Ordering matters between updateVpnInfos() and forceUpdateIfaces(), which
+ // triggers a new poll. Trigger the poll first to ensure a snapshot is taken before
+ // switching to the new state. This ensures that traffic does not get mis-attributed to
+ // incorrect apps (including VPN app).
try {
mStatsService.forceUpdateIfaces(
- getDefaultNetworks(), getAllVpnInfo(), getAllNetworkState(), activeIface);
+ getDefaultNetworks(), getAllNetworkState(), activeIface);
} catch (Exception ignored) {
}
+ NetworkStatsFactory.updateVpnInfos(getAllVpnInfo());
}
@Override
@@ -6899,8 +6910,10 @@
final int userId = UserHandle.getCallingUserId();
- final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext);
- ipMemoryStore.factoryReset();
+ Binder.withCleanCallingIdentity(() -> {
+ final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext);
+ ipMemoryStore.factoryReset();
+ });
// Turn airplane mode off
setAirplaneMode(false);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 6dbe3ad..6d08b5a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -36,7 +36,6 @@
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_TETHERING;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
@@ -1231,7 +1230,7 @@
public NetworkStats getNetworkStatsDetail() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return mStatsFactory.readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
+ return mStatsFactory.readNetworkStatsDetail();
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -1540,7 +1539,7 @@
public NetworkStats getNetworkStatsUidDetail(int uid, String[] ifaces) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL, null);
+ return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL);
} catch (IOException e) {
throw new IllegalStateException(e);
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 99365de..534d76c 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1315,12 +1315,12 @@
return;
}
if (VDBG) {
- log("notifyUserMobileDataStateChangedForSubscriberPhoneID: subId=" + phoneId
- + " state=" + state);
+ log("notifyUserMobileDataStateChangedForSubscriberPhoneID: PhoneId=" + phoneId
+ + " subId=" + subId + " state=" + state);
}
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
- mMessageWaiting[phoneId] = state;
+ mUserMobileDataState[phoneId] = state;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) &&
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index f6735d9..077c405 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -232,6 +232,11 @@
title = r.getString(R.string.network_switch_metered, toTransport);
details = r.getString(R.string.network_switch_metered_detail, toTransport,
fromTransport);
+ } else if (notifyType == NotificationType.NO_INTERNET
+ || notifyType == NotificationType.PARTIAL_CONNECTIVITY) {
+ // NO_INTERNET and PARTIAL_CONNECTIVITY notification for non-WiFi networks
+ // are sent, but they are not implemented yet.
+ return;
} else {
Slog.wtf(TAG, "Unknown notification type " + notifyType + " on network transport "
+ getTransportName(transportType));
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 2e64965..2d3c66d6 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -16,6 +16,7 @@
package com.android.server.net;
+import static android.net.NetworkStats.INTERFACES_ALL;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
@@ -33,6 +34,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
@@ -70,11 +72,25 @@
private INetd mNetdService;
- // A persistent Snapshot since device start for eBPF stats
- @GuardedBy("mPersistSnapshot")
- private final NetworkStats mPersistSnapshot;
+ /**
+ * Guards persistent data access in this class
+ *
+ * <p>In order to prevent deadlocks, critical sections protected by this lock SHALL NOT call out
+ * to other code that will acquire other locks within the system server. See b/134244752.
+ */
+ private static final Object sPersistentDataLock = new Object();
- // TODO: only do adjustments in NetworkStatsService and remove this.
+ /** Set containing info about active VPNs and their underlying networks. */
+ private static volatile VpnInfo[] sVpnInfos = new VpnInfo[0];
+
+ // A persistent snapshot of cumulative stats since device start
+ @GuardedBy("sPersistentDataLock")
+ private NetworkStats mPersistSnapshot;
+
+ // The persistent snapshot of tun and 464xlat adjusted stats since device start
+ @GuardedBy("sPersistentDataLock")
+ private NetworkStats mTunAnd464xlatAdjustedStats;
+
/**
* (Stacked interface) -> (base interface) association for all connected ifaces since boot.
*
@@ -91,6 +107,24 @@
}
/**
+ * Set active VPN information for data usage migration purposes
+ *
+ * <p>Traffic on TUN-based VPNs inherently all appear to be originated from the VPN providing
+ * app's UID. This method is used to support migration of VPN data usage, ensuring data is
+ * accurately billed to the real owner of the traffic.
+ *
+ * @param vpnArray The snapshot of the currently-running VPNs.
+ */
+ public static void updateVpnInfos(VpnInfo[] vpnArray) {
+ sVpnInfos = vpnArray.clone();
+ }
+
+ @VisibleForTesting
+ public static VpnInfo[] getVpnInfos() {
+ return sVpnInfos.clone();
+ }
+
+ /**
* Get a set of interfaces containing specified ifaces and stacked interfaces.
*
* <p>The added stacked interfaces are ifaces stacked on top of the specified ones, or ifaces
@@ -146,6 +180,7 @@
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats;
mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
+ mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
public NetworkStats readBpfNetworkStatsDev() throws IOException {
@@ -263,28 +298,25 @@
return stats;
}
- /**
- * @deprecated Use NetworkStatsService#getDetailedUidStats which also accounts for
- * VPN traffic
- */
- @Deprecated
public NetworkStats readNetworkStatsDetail() throws IOException {
- return readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
+ return readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
}
- public NetworkStats readNetworkStatsDetail(int limitUid, String[] limitIfaces, int limitTag,
- NetworkStats lastStats) throws IOException {
- final NetworkStats stats =
- readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats);
-
- // No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
- // TODO: remove this and only apply adjustments in NetworkStatsService.
- stats.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats);
-
- return stats;
+ /**
+ * Reads the detailed UID stats based on the provided parameters
+ *
+ * @param limitUid the UID to limit this query to
+ * @param limitIfaces the interfaces to limit this query to. Use {@link
+ * NetworkStats.INTERFACES_ALL} to select all interfaces
+ * @param limitTag the tags to limit this query to
+ * @return the NetworkStats instance containing network statistics at the present time.
+ */
+ public NetworkStats readNetworkStatsDetail(
+ int limitUid, @Nullable String[] limitIfaces, int limitTag) throws IOException {
+ return readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag);
}
- @GuardedBy("mPersistSnapshot")
+ @GuardedBy("sPersistentDataLock")
private void requestSwapActiveStatsMapLocked() throws RemoteException {
// Ask netd to do a active map stats swap. When the binder call successfully returns,
// the system server should be able to safely read and clean the inactive map
@@ -297,19 +329,20 @@
}
}
- // TODO: delete the lastStats parameter
- private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces,
- int limitTag, NetworkStats lastStats) throws IOException {
- if (USE_NATIVE_PARSING) {
- final NetworkStats stats;
- if (lastStats != null) {
- stats = lastStats;
- stats.setElapsedRealtime(SystemClock.elapsedRealtime());
- } else {
- stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
- }
- if (mUseBpfStats) {
- synchronized (mPersistSnapshot) {
+ private NetworkStats readNetworkStatsDetailInternal(
+ int limitUid, String[] limitIfaces, int limitTag) throws IOException {
+ // In order to prevent deadlocks, anything protected by this lock MUST NOT call out to other
+ // code that will acquire other locks within the system server. See b/134244752.
+ synchronized (sPersistentDataLock) {
+ // Take a reference. If this gets swapped out, we still have the old reference.
+ final VpnInfo[] vpnArray = sVpnInfos;
+ // Take a defensive copy. mPersistSnapshot is mutated in some cases below
+ final NetworkStats prev = mPersistSnapshot.clone();
+
+ if (USE_NATIVE_PARSING) {
+ final NetworkStats stats =
+ new NetworkStats(SystemClock.elapsedRealtime(), 0 /* initialSize */);
+ if (mUseBpfStats) {
try {
requestSwapActiveStatsMapLocked();
} catch (RemoteException e) {
@@ -318,32 +351,66 @@
// Stats are always read from the inactive map, so they must be read after the
// swap
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
- null, TAG_ALL, mUseBpfStats) != 0) {
+ INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
throw new IOException("Failed to parse network stats");
}
+
+ // BPF stats are incremental; fold into mPersistSnapshot.
mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
mPersistSnapshot.combineAllValues(stats);
- NetworkStats result = mPersistSnapshot.clone();
- result.filter(limitUid, limitIfaces, limitTag);
- return result;
+ } else {
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
+ INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
+ throw new IOException("Failed to parse network stats");
+ }
+ if (SANITY_CHECK_NATIVE) {
+ final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid,
+ UID_ALL, INTERFACES_ALL, TAG_ALL);
+ assertEquals(javaStats, stats);
+ }
+
+ mPersistSnapshot = stats;
}
} else {
- if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
- limitIfaces, limitTag, mUseBpfStats) != 0) {
- throw new IOException("Failed to parse network stats");
- }
- if (SANITY_CHECK_NATIVE) {
- final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid,
- limitIfaces, limitTag);
- assertEquals(javaStats, stats);
- }
- return stats;
+ mPersistSnapshot = javaReadNetworkStatsDetail(mStatsXtUid, UID_ALL, INTERFACES_ALL,
+ TAG_ALL);
}
- } else {
- return javaReadNetworkStatsDetail(mStatsXtUid, limitUid, limitIfaces, limitTag);
+
+ NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
+
+ // Filter return values
+ adjustedStats.filter(limitUid, limitIfaces, limitTag);
+ return adjustedStats;
}
}
+ @GuardedBy("sPersistentDataLock")
+ private NetworkStats adjustForTunAnd464Xlat(
+ NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) {
+ // Calculate delta from last snapshot
+ final NetworkStats delta = uidDetailStats.subtract(previousStats);
+
+ // Apply 464xlat adjustments before VPN adjustments. If VPNs are using v4 on a v6 only
+ // network, the overhead is their fault.
+ // No locking here: apply464xlatAdjustments behaves fine with an add-only
+ // ConcurrentHashMap.
+ delta.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats);
+
+ // Migrate data usage over a VPN to the TUN network.
+ for (VpnInfo info : vpnArray) {
+ delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
+ }
+
+ // Filter out debug entries as that may lead to over counting.
+ delta.filterDebugEntries();
+
+ // Update mTunAnd464xlatAdjustedStats with migrated delta.
+ mTunAnd464xlatAdjustedStats.combineAllValues(delta);
+ mTunAnd464xlatAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
+
+ return mTunAnd464xlatAdjustedStats.clone();
+ }
+
/**
* Parse and return {@link NetworkStats} with UID-level details. Values are
* expected to monotonically increase since device boot.
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java
index d840873..2564dae 100644
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ b/services/core/java/com/android/server/net/NetworkStatsObservers.java
@@ -39,7 +39,6 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import java.util.concurrent.atomic.AtomicInteger;
@@ -104,9 +103,9 @@
public void updateStats(NetworkStats xtSnapshot, NetworkStats uidSnapshot,
ArrayMap<String, NetworkIdentitySet> activeIfaces,
ArrayMap<String, NetworkIdentitySet> activeUidIfaces,
- VpnInfo[] vpnArray, long currentTime) {
+ long currentTime) {
StatsContext statsContext = new StatsContext(xtSnapshot, uidSnapshot, activeIfaces,
- activeUidIfaces, vpnArray, currentTime);
+ activeUidIfaces, currentTime);
getHandler().sendMessage(mHandler.obtainMessage(MSG_UPDATE_STATS, statsContext));
}
@@ -354,7 +353,7 @@
// thread will update it. We pass a null VPN array because usage is aggregated by uid
// for this snapshot, so VPN traffic can't be reattributed to responsible apps.
mRecorder.recordSnapshotLocked(statsContext.mXtSnapshot, statsContext.mActiveIfaces,
- null /* vpnArray */, statsContext.mCurrentTime);
+ statsContext.mCurrentTime);
}
/**
@@ -396,7 +395,7 @@
// thread will update it. We pass the VPN info so VPN traffic is reattributed to
// responsible apps.
mRecorder.recordSnapshotLocked(statsContext.mUidSnapshot, statsContext.mActiveUidIfaces,
- statsContext.mVpnArray, statsContext.mCurrentTime);
+ statsContext.mCurrentTime);
}
/**
@@ -427,18 +426,16 @@
NetworkStats mUidSnapshot;
ArrayMap<String, NetworkIdentitySet> mActiveIfaces;
ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces;
- VpnInfo[] mVpnArray;
long mCurrentTime;
StatsContext(NetworkStats xtSnapshot, NetworkStats uidSnapshot,
ArrayMap<String, NetworkIdentitySet> activeIfaces,
ArrayMap<String, NetworkIdentitySet> activeUidIfaces,
- VpnInfo[] vpnArray, long currentTime) {
+ long currentTime) {
mXtSnapshot = xtSnapshot;
mUidSnapshot = uidSnapshot;
mActiveIfaces = activeIfaces;
mActiveUidIfaces = activeUidIfaces;
- mVpnArray = vpnArray;
mCurrentTime = currentTime;
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index bdff500..06ec341 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -23,7 +23,6 @@
import static com.android.internal.util.Preconditions.checkNotNull;
-import android.annotation.Nullable;
import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory;
@@ -37,7 +36,6 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
@@ -202,18 +200,12 @@
}
/**
- * Record any delta that occurred since last {@link NetworkStats} snapshot,
- * using the given {@link Map} to identify network interfaces. First
- * snapshot is considered bootstrap, and is not counted as delta.
- *
- * @param vpnArray Optional info about the currently active VPN, if any. This is used to
- * redistribute traffic from the VPN app to the underlying responsible apps.
- * This should always be set to null if the provided snapshot is aggregated
- * across all UIDs (e.g. contains UID_ALL buckets), regardless of VPN state.
+ * Record any delta that occurred since last {@link NetworkStats} snapshot, using the given
+ * {@link Map} to identify network interfaces. First snapshot is considered bootstrap, and is
+ * not counted as delta.
*/
public void recordSnapshotLocked(NetworkStats snapshot,
- Map<String, NetworkIdentitySet> ifaceIdent, @Nullable VpnInfo[] vpnArray,
- long currentTimeMillis) {
+ Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
final HashSet<String> unknownIfaces = Sets.newHashSet();
// skip recording when snapshot missing
@@ -232,12 +224,6 @@
final long end = currentTimeMillis;
final long start = end - delta.getElapsedRealtime();
- if (vpnArray != null) {
- for (VpnInfo info : vpnArray) {
- delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
- }
- }
-
NetworkStats.Entry entry = null;
for (int i = 0; i < delta.size(); i++) {
entry = delta.getValues(i, entry);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 484efd6..2fc78d6 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -130,7 +130,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
@@ -266,10 +265,6 @@
@GuardedBy("mStatsLock")
private Network[] mDefaultNetworks = new Network[0];
- /** Set containing info about active VPNs and their underlying networks. */
- @GuardedBy("mStatsLock")
- private VpnInfo[] mVpnInfos = new VpnInfo[0];
-
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
new DropBoxNonMonotonicObserver();
@@ -292,22 +287,6 @@
/** Data layer operation counters for splicing into other structures. */
private NetworkStats mUidOperations = new NetworkStats(0L, 10);
- /**
- * Snapshot containing most recent network stats for all UIDs across all interfaces and tags
- * since boot.
- *
- * <p>Maintains migrated VPN stats which are result of performing TUN migration on {@link
- * #mLastUidDetailSnapshot}.
- */
- @GuardedBy("mStatsLock")
- private NetworkStats mTunAdjustedStats;
- /**
- * Used by {@link #mTunAdjustedStats} to migrate VPN traffic over delta between this snapshot
- * and latest snapshot.
- */
- @GuardedBy("mStatsLock")
- private NetworkStats mLastUidDetailSnapshot;
-
/** Must be set in factory by calling #setHandler. */
private Handler mHandler;
private Handler.Callback mHandlerCallback;
@@ -821,39 +800,15 @@
@Override
public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
try {
- // Get the latest snapshot from NetworkStatsFactory.
- // TODO: Querying for INTERFACES_ALL may incur performance penalty. Consider restricting
- // this to limited set of ifaces.
- NetworkStats uidDetailStats = getNetworkStatsUidDetail(INTERFACES_ALL);
-
- // Migrate traffic from VPN UID over delta and update mTunAdjustedStats.
- NetworkStats result;
- synchronized (mStatsLock) {
- migrateTunTraffic(uidDetailStats, mVpnInfos);
- result = mTunAdjustedStats.clone();
- }
-
- // Apply filter based on ifacesToQuery.
final String[] ifacesToQuery =
NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
- result.filter(UID_ALL, ifacesToQuery, TAG_ALL);
- return result;
+ return getNetworkStatsUidDetail(ifacesToQuery);
} catch (RemoteException e) {
Log.wtf(TAG, "Error compiling UID stats", e);
return new NetworkStats(0L, 0);
}
}
- @VisibleForTesting
- NetworkStats getTunAdjustedStats() {
- synchronized (mStatsLock) {
- if (mTunAdjustedStats == null) {
- return null;
- }
- return mTunAdjustedStats.clone();
- }
- }
-
@Override
public String[] getMobileIfaces() {
return mMobileIfaces;
@@ -897,7 +852,6 @@
@Override
public void forceUpdateIfaces(
Network[] defaultNetworks,
- VpnInfo[] vpnArray,
NetworkState[] networkStates,
String activeIface) {
checkNetworkStackPermission(mContext);
@@ -905,7 +859,7 @@
final long token = Binder.clearCallingIdentity();
try {
- updateIfaces(defaultNetworks, vpnArray, networkStates, activeIface);
+ updateIfaces(defaultNetworks, networkStates, activeIface);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1171,13 +1125,11 @@
private void updateIfaces(
Network[] defaultNetworks,
- VpnInfo[] vpnArray,
NetworkState[] networkStates,
String activeIface) {
synchronized (mStatsLock) {
mWakeLock.acquire();
try {
- mVpnInfos = vpnArray;
mActiveIface = activeIface;
updateIfacesLocked(defaultNetworks, networkStates);
} finally {
@@ -1187,10 +1139,9 @@
}
/**
- * Inspect all current {@link NetworkState} to derive mapping from {@code
- * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
- * are active on a single {@code iface}, they are combined under a single
- * {@link NetworkIdentitySet}.
+ * Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link
+ * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface},
+ * they are combined under a single {@link NetworkIdentitySet}.
*/
@GuardedBy("mStatsLock")
private void updateIfacesLocked(Network[] defaultNetworks, NetworkState[] states) {
@@ -1307,55 +1258,24 @@
// For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic
// can't be reattributed to responsible apps.
Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev");
- mDevRecorder.recordSnapshotLocked(
- devSnapshot, mActiveIfaces, null /* vpnArray */, currentTime);
+ mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime);
Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "recordXt");
- mXtRecorder.recordSnapshotLocked(
- xtSnapshot, mActiveIfaces, null /* vpnArray */, currentTime);
+ mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime);
Trace.traceEnd(TRACE_TAG_NETWORK);
// For per-UID stats, pass the VPN info so VPN traffic is reattributed to responsible apps.
- VpnInfo[] vpnArray = mVpnInfos;
Trace.traceBegin(TRACE_TAG_NETWORK, "recordUid");
- mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime);
+ mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "recordUidTag");
- mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime);
+ mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime);
Trace.traceEnd(TRACE_TAG_NETWORK);
// We need to make copies of member fields that are sent to the observer to avoid
// a race condition between the service handler thread and the observer's
mStatsObservers.updateStats(xtSnapshot, uidSnapshot, new ArrayMap<>(mActiveIfaces),
- new ArrayMap<>(mActiveUidIfaces), vpnArray, currentTime);
-
- migrateTunTraffic(uidSnapshot, vpnArray);
- }
-
- /**
- * Updates {@link #mTunAdjustedStats} with the delta containing traffic migrated off of VPNs.
- */
- @GuardedBy("mStatsLock")
- private void migrateTunTraffic(NetworkStats uidDetailStats, VpnInfo[] vpnInfoArray) {
- if (mTunAdjustedStats == null) {
- // Either device booted or system server restarted, hence traffic cannot be migrated
- // correctly without knowing the past state of VPN's underlying networks.
- mTunAdjustedStats = uidDetailStats;
- mLastUidDetailSnapshot = uidDetailStats;
- return;
- }
- // Migrate delta traffic from VPN to other apps.
- NetworkStats delta = uidDetailStats.subtract(mLastUidDetailSnapshot);
- for (VpnInfo info : vpnInfoArray) {
- delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
- }
- // Filter out debug entries as that may lead to over counting.
- delta.filterDebugEntries();
- // Update #mTunAdjustedStats with migrated delta.
- mTunAdjustedStats.combineAllValues(delta);
- mTunAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime());
- // Update last snapshot.
- mLastUidDetailSnapshot = uidDetailStats;
+ new ArrayMap<>(mActiveUidIfaces), currentTime);
}
/**
@@ -1728,8 +1648,6 @@
*/
private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
throws RemoteException {
-
- // TODO: remove 464xlat adjustments from NetworkStatsFactory and apply all at once here.
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL,
ifaces);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 58a1dc1..0188f7c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2189,6 +2189,11 @@
@Override
public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
checkCallerIsSystemOrSameApp(pkg);
+ if (UserHandle.getCallingUserId() != UserHandle.getUserId(uid)) {
+ getContext().enforceCallingPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ "canNotifyAsPackage for uid " + uid);
+ }
return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9bb417c..f938b65 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18305,6 +18305,12 @@
@Override
public boolean isPackageDeviceAdminOnAnyUser(String packageName) {
final int callingUid = Binder.getCallingUid();
+ if (checkUidPermission(android.Manifest.permission.MANAGE_USERS, callingUid)
+ != PERMISSION_GRANTED) {
+ EventLog.writeEvent(0x534e4554, "128599183", -1, "");
+ throw new SecurityException(android.Manifest.permission.MANAGE_USERS
+ + " permission is required to call this API");
+ }
if (getInstantAppPackageName(callingUid) != null
&& !isCallerSameApp(packageName, callingUid)) {
return false;
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 296a652..28c171b 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -108,7 +108,7 @@
private static RulesManagerService create(Context context) {
RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
- File baseVersionFile = new File(TimeZoneDataFiles.getRuntimeModuleTzVersionFile());
+ File baseVersionFile = new File(TimeZoneDataFiles.getTimeZoneModuleTzVersionFile());
File tzDataDir = new File(TimeZoneDataFiles.getDataTimeZoneRootDir());
return new RulesManagerService(
helper /* permissionHelper */,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9c6b52f..d3b25fd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3934,6 +3934,9 @@
@Override
public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+ if (!isCallerWithSystemUid()) {
+ throw new SecurityException("Caller must be system");
+ }
ComponentName profileOwner = getProfileOwner(userHandle);
// Profile challenge is supported on N or newer release.
return profileOwner != null &&
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index cf52574..787fda3 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -488,7 +488,9 @@
private void requestConnector(@NonNull NetworkStackCallback request) {
// TODO: PID check.
final int caller = Binder.getCallingUid();
- if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)
+ if (caller != Process.SYSTEM_UID
+ && caller != Process.NETWORK_STACK_UID
+ && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)
&& !UserHandle.isSameApp(caller, Process.PHONE_UID)) {
// Don't even attempt to obtain the connector and give a nice error message
throw new SecurityException(
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 85d0c4c..36103e3 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -49,7 +49,6 @@
import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT;
import static android.telephony.SubscriptionPlan.BYTES_UNLIMITED;
import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED;
-import static android.text.format.Time.TIMEZONE_UTC;
import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS;
import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
@@ -128,7 +127,6 @@
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
-import android.text.format.Time;
import android.util.DataUnit;
import android.util.Log;
import android.util.Pair;
@@ -185,6 +183,7 @@
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.TimeZone;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -223,6 +222,7 @@
* Path on assets where files used by {@link NetPolicyXml} are located.
*/
private static final String NETPOLICY_DIR = "NetworkPolicyManagerServiceTest/netpolicy";
+ private static final String TIMEZONE_UTC = "UTC";
private BroadcastInterceptingContext mServiceContext;
private File mPolicyDir;
@@ -1771,7 +1771,7 @@
private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes,
long limitBytes, boolean inferred){
final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID);
- return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes,
+ return new NetworkPolicy(template, cycleDay, TimeZone.getDefault().getID(), warningBytes,
limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f02c3f0..5622622 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -34,6 +34,7 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
@@ -106,6 +107,7 @@
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.testing.TestablePermissions;
import android.text.Html;
import android.util.ArrayMap;
import android.util.AtomicFile;
@@ -3145,4 +3147,21 @@
assertEquals(0, captor.getValue().getNotification().flags);
}
+
+ @Test
+ public void testAreNotificationsEnabledForPackage_crossUser() throws Exception {
+ try {
+ mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
+ mUid + UserHandle.PER_USER_RANGE);
+ fail("Cannot call cross user without permission");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ // cross user, with permission, no problem
+ TestablePermissions perms = mContext.getTestablePermissions();
+ perms.setPermission(android.Manifest.permission.INTERACT_ACROSS_USERS, PERMISSION_GRANTED);
+ mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
+ mUid + UserHandle.PER_USER_RANGE);
+ }
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index e087f38..44a135d 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1997,4 +1997,18 @@
public void setIwlanPreferred(boolean isIwlanPreferred) {
mIsIwlanPreferred = isIwlanPreferred;
}
+
+ /**
+ * @return {@code true} if any data network is preferred on IWLAN.
+ *
+ * Note only when this value is true, {@link #getDataNetworkType()} will return
+ * {@link TelephonyManager#NETWORK_TYPE_IWLAN} when AP-assisted mode device camps on both
+ * cellular and IWLAN. This value does not affect legacy mode devices as the data network
+ * type is directly reported by the modem.
+ *
+ * @hide
+ */
+ public boolean isIwlanPreferred() {
+ return mIsIwlanPreferred;
+ }
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 0ee08e1..63e3801 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -22,22 +22,25 @@
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.BaseBundle;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.provider.Telephony;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.IMms;
import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.SmsRawData;
import java.util.ArrayList;
@@ -54,22 +57,22 @@
/**
* Manages SMS operations such as sending data, text, and pdu SMS messages.
- * Get this object by calling the static method {@link #getDefault()}.
+ * Get this object by calling the static method {@link #getDefault()}. To create an instance of
+ * {@link SmsManager} associated with a specific subscription ID, call
+ * {@link #getSmsManagerForSubscriptionId(int)}. This is typically used for devices that support
+ * multiple active subscriptions at once.
*
* <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19)
* and higher, see {@link android.provider.Telephony}.
+ *
+ * @see SubscriptionManager#getActiveSubscriptionInfoList()
*/
public final class SmsManager {
private static final String TAG = "SmsManager";
- /**
- * A psuedo-subId that represents the default subId at any given time. The actual subId it
- * represents changes as the default subId is changed.
- */
- private static final int DEFAULT_SUBSCRIPTION_ID = -1002;
-
/** Singleton object constructed during class initialization. */
- private static final SmsManager sInstance = new SmsManager(DEFAULT_SUBSCRIPTION_ID);
+ private static final SmsManager sInstance = new SmsManager(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
private static final Object sLockObject = new Object();
/** @hide */
@@ -110,7 +113,7 @@
* Whether MMS is enabled for the current carrier (boolean type)
*/
public static final String
- MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL;
+ MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL;
/**
* Whether group MMS is enabled for the current carrier (boolean type)
*/
@@ -278,12 +281,6 @@
public static final String MMS_CONFIG_CLOSE_CONNECTION =
CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL;
- /*
- * Forwarded constants from SimDialogActivity.
- */
- private static String DIALOG_TYPE_KEY = "dialog_type";
- private static final int SMS_PICK = 2;
-
/**
* 3gpp2 SMS priority is not specified
* @hide
@@ -296,6 +293,18 @@
public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;
/**
+ * Extra key passed into a PendingIntent when the SMS operation failed due to there being no
+ * default set.
+ */
+ private static final String NO_DEFAULT_EXTRA = "noDefault";
+
+ // result of asking the user for a subscription to perform an operation.
+ private interface SubscriptionResolverResult {
+ void onSuccess(int subId);
+ void onFailure();
+ }
+
+ /**
* Send a text based SMS.
*
* <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -307,6 +316,15 @@
* responsible for writing its sent messages to the SMS Provider). For information about
* how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
*
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+ *
*
* @param destinationAddress the address to send the message to
* @param scAddress is the service center address or null to use
@@ -350,15 +368,51 @@
throw new IllegalArgumentException("Invalid message body");
}
- try {
- // If the subscription is invalid or default, we will use the default phone to send the
- // SMS and possibly fail later in the SMS sending process.
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ // We will only show the SMS disambiguation dialog in the case that the message is being
+ // persisted. This is for two reasons:
+ // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
+ // subscription and require special permissions. These messages are usually not sent by
+ // the device user and should not have an SMS disambiguation dialog associated with them
+ // because the device user did not trigger them.
+ // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS
+ // permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has
+ // the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw
+ // an incorrect SecurityException.
+ if (persistMessage) {
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ ISms iSms = getISmsServiceOrThrow();
+ try {
+ iSms.sendTextForSubscriber(subId, packageName,
+ destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ persistMessage);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
+ + e.getMessage());
+ notifySmsGenericError(sentIntent);
+ }
+ }
+
+ @Override
+ public void onFailure() {
+ notifySmsErrorNoDefaultSet(context, sentIntent);
+ }
+ });
+ } else {
+ // Not persisting the message, used by sendTextMessageWithoutPersisting() and is not
+ // visible to the user.
ISms iSms = getISmsServiceOrThrow();
- iSms.sendTextForSubscriber(getSubscriptionId(), packageName,
- destinationAddress, scAddress, text, sentIntent, deliveryIntent,
- persistMessage);
- } catch (RemoteException ex) {
- // ignore it
+ try {
+ iSms.sendTextForSubscriber(getSubscriptionId(), packageName,
+ destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ persistMessage);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - "
+ + e.getMessage());
+ notifySmsGenericError(sentIntent);
+ }
}
}
@@ -375,6 +429,17 @@
* privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or that the calling app is
* the default IMS app (see
* {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}).
+ * </p>
+ *
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the SMS being sent on the subscription associated with logical
+ * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
+ * correct subscription.
+ * </p>
*
* @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
*/
@@ -394,6 +459,16 @@
* A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is
* for internal use only.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the SMS being sent on the subscription associated with logical
+ * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
+ * correct subscription.
+ * </p>
+ *
* @param persistMessage whether to persist the sent message in the SMS app. the caller must be
* the Phone process if set to false.
*
@@ -417,13 +492,22 @@
destinationAddress,
scAddress, text, sentIntent, deliveryIntent, persistMessage);
} catch (RemoteException ex) {
- // ignore it
+ notifySmsGenericError(sentIntent);
}
}
/**
* Send a text based SMS with messaging options.
*
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+ *
* @param destinationAddress the address to send the message to
* @param scAddress is the service center address or null to use
* the current default SMSC
@@ -494,16 +578,59 @@
validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
}
- try {
- ISms iSms = getISmsServiceOrThrow();
- if (iSms != null) {
- iSms.sendTextForSubscriberWithOptions(getSubscriptionId(),
- ActivityThread.currentPackageName(), destinationAddress, scAddress, text,
- sentIntent, deliveryIntent, persistMessage, priority, expectMore,
- validityPeriod);
+ final int finalPriority = priority;
+ final int finalValidity = validityPeriod;
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ // We will only show the SMS disambiguation dialog in the case that the message is being
+ // persisted. This is for two reasons:
+ // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
+ // subscription and require special permissions. These messages are usually not sent by
+ // the device user and should not have an SMS disambiguation dialog associated with them
+ // because the device user did not trigger them.
+ // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS
+ // permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has
+ // the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw
+ // an incorrect SecurityException.
+ if (persistMessage) {
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ if (iSms != null) {
+ iSms.sendTextForSubscriberWithOptions(subId,
+ ActivityThread.currentPackageName(), destinationAddress,
+ scAddress,
+ text, sentIntent, deliveryIntent, persistMessage, finalPriority,
+ expectMore, finalValidity);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
+ + e.getMessage());
+ notifySmsGenericError(sentIntent);
+ }
+ }
+
+ @Override
+ public void onFailure() {
+ notifySmsErrorNoDefaultSet(context, sentIntent);
+ }
+ });
+ } else {
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ if (iSms != null) {
+ iSms.sendTextForSubscriberWithOptions(getSubscriptionId(),
+ ActivityThread.currentPackageName(), destinationAddress,
+ scAddress,
+ text, sentIntent, deliveryIntent, persistMessage, finalPriority,
+ expectMore, finalValidity);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendTextMessageInternal(no persist): Couldn't send SMS, exception - "
+ + e.getMessage());
+ notifySmsGenericError(sentIntent);
}
- } catch (RemoteException ex) {
- // ignore it
}
}
@@ -515,6 +642,16 @@
* privileges.
* </p>
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the SMS being sent on the subscription associated with logical
+ * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
+ * correct subscription.
+ * </p>
+ *
* @see #sendTextMessage(String, String, String, PendingIntent,
* PendingIntent, int, boolean, int)
* @hide
@@ -535,6 +672,16 @@
* <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
* privileges per {@link android.telephony.TelephonyManager#hasCarrierPrivileges}.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the SMS being injected on the subscription associated with
+ * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is
+ * delivered to the correct subscription.
+ * </p>
+ *
* @param pdu is the byte array of pdu to be injected into android application framework
* @param format is the format of SMS pdu ({@link SmsMessage#FORMAT_3GPP} or
* {@link SmsMessage#FORMAT_3GPP2})
@@ -562,7 +709,13 @@
getSubscriptionId(), pdu, format, receivedIntent);
}
} catch (RemoteException ex) {
- // ignore it
+ try {
+ if (receivedIntent != null) {
+ receivedIntent.send(Telephony.Sms.Intents.RESULT_SMS_GENERIC_ERROR);
+ }
+ } catch (PendingIntent.CanceledException cx) {
+ // Don't worry about it, we do not need to notify the caller if this is the case.
+ }
}
}
@@ -594,6 +747,16 @@
* responsible for writing its sent messages to the SMS Provider). For information about
* how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
*
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+ *
+ *
* @param destinationAddress the address to send the message to
* @param scAddress is the service center address or null to use
* the current default SMSC
@@ -629,11 +792,22 @@
}
/**
- * @hide
* Similar method as #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
- * With an additional argument
- * @param packageName serves as the default package name if ActivityThread.currentpackageName is
- * null.
+ * With an additional argument.
+ *
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use the Telephony
+ * framework and will never trigger an SMS disambiguation dialog. If this method is called on a
+ * device that has multiple active subscriptions, this {@link SmsManager} instance has been
+ * created with {@link #getDefault()}, and no user-defined default subscription is defined, the
+ * subscription ID associated with this message will be INVALID, which will result in the SMS
+ * being sent on the subscription associated with logical slot 0. Use
+ * {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the correct
+ * subscription.
+ * </p>
+ *
+ * @param packageName serves as the default package name if
+ * {@link ActivityThread#currentPackageName()} is null.
+ * @hide
*/
public void sendMultipartTextMessageExternal(
String destinationAddress, String scAddress, ArrayList<String> parts,
@@ -657,13 +831,52 @@
}
if (parts.size() > 1) {
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendMultipartTextForSubscriber(getSubscriptionId(),
- packageName, destinationAddress, scAddress, parts,
- sentIntents, deliveryIntents, persistMessage);
- } catch (RemoteException ex) {
- // ignore it
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ // We will only show the SMS disambiguation dialog in the case that the message is being
+ // persisted. This is for two reasons:
+ // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific
+ // subscription and require special permissions. These messages are usually not sent
+ // by the device user and should not have an SMS disambiguation dialog associated
+ // with them because the device user did not trigger them.
+ // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the
+ // SEND_SMS permission. If we call resolveSubscriptionForOperation from a carrier/OEM
+ // app that has the correct MODIFY_PHONE_STATE or carrier permissions, but no
+ // SEND_SMS, it will throw an incorrect SecurityException.
+ if (persistMessage) {
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ iSms.sendMultipartTextForSubscriber(subId, packageName,
+ destinationAddress, scAddress, parts, sentIntents,
+ deliveryIntents, persistMessage);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+ + e.getMessage());
+ notifySmsGenericError(sentIntents);
+ }
+ }
+
+ @Override
+ public void onFailure() {
+ notifySmsErrorNoDefaultSet(context, sentIntents);
+ }
+ });
+ } else {
+ // Called by apps that are not user facing, don't show disambiguation dialog.
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ if (iSms != null) {
+ iSms.sendMultipartTextForSubscriber(getSubscriptionId(), packageName,
+ destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
+ persistMessage);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+ + e.getMessage());
+ notifySmsGenericError(sentIntents);
+ }
}
} else {
PendingIntent sentIntent = null;
@@ -682,6 +895,15 @@
/**
* Send a multi-part text based SMS without writing it into the SMS Provider.
*
+ * <p>
+ * If this method is called on a device with multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the SMS sent on the subscription associated with slot
+ * 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent using the
+ * correct subscription.
+ * </p>
+ *
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
* privileges.
@@ -713,6 +935,15 @@
* responsible for writing its sent messages to the SMS Provider). For information about
* how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
*
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+
* @param destinationAddress the address to send the message to
* @param scAddress is the service center address or null to use
* the current default SMSC
@@ -780,24 +1011,56 @@
}
if (priority < 0x00 || priority > 0x03) {
- priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
+ priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
}
if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
- validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
+ validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
}
if (parts.size() > 1) {
- try {
- ISms iSms = getISmsServiceOrThrow();
- if (iSms != null) {
- iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
- ActivityThread.currentPackageName(), destinationAddress, scAddress,
- parts, sentIntents, deliveryIntents, persistMessage, priority,
- expectMore, validityPeriod);
+ final int finalPriority = priority;
+ final int finalValidity = validityPeriod;
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ if (persistMessage) {
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ if (iSms != null) {
+ iSms.sendMultipartTextForSubscriberWithOptions(subId,
+ ActivityThread.currentPackageName(), destinationAddress,
+ scAddress, parts, sentIntents, deliveryIntents,
+ persistMessage, finalPriority, expectMore, finalValidity);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+ + e.getMessage());
+ notifySmsGenericError(sentIntents);
+ }
+ }
+
+ @Override
+ public void onFailure() {
+ notifySmsErrorNoDefaultSet(context, sentIntents);
+ }
+ });
+ } else {
+ // Sent by apps that are not user visible, so don't show SIM disambiguation dialog.
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ if (iSms != null) {
+ iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
+ ActivityThread.currentPackageName(), destinationAddress,
+ scAddress, parts, sentIntents, deliveryIntents,
+ persistMessage, finalPriority, expectMore, finalValidity);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMultipartTextMessageInternal (no persist): Couldn't send SMS - "
+ + e.getMessage());
+ notifySmsGenericError(sentIntents);
}
- } catch (RemoteException ex) {
- // ignore it
}
} else {
PendingIntent sentIntent = null;
@@ -822,6 +1085,16 @@
* privileges.
* </p>
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use the Telephony
+ * framework and will never trigger an SMS disambiguation dialog. If this method is called on a
+ * device that has multiple active subscriptions, this {@link SmsManager} instance has been
+ * created with {@link #getDefault()}, and no user-defined default subscription is defined, the
+ * subscription ID associated with this message will be INVALID, which will result in the SMS
+ * being sent on the subscription associated with logical slot 0. Use
+ * {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the correct
+ * subscription.
+ * </p>
+ *
* @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList,
* ArrayList, int, boolean, int)
* @hide
@@ -835,12 +1108,21 @@
validityPeriod);
}
- /**
+ /**
* Send a data based SMS to a specific application port.
*
* <p class="note"><strong>Note:</strong> Using this method requires that your app has the
* {@link android.Manifest.permission#SEND_SMS} permission.</p>
*
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+ *
* @param destinationAddress the address to send the message to
* @param scAddress is the service center address or null to use
* the current default SMSC
@@ -876,20 +1158,41 @@
throw new IllegalArgumentException("Invalid message data");
}
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendDataForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),
- destinationAddress, scAddress, destinationPort & 0xFFFF,
- data, sentIntent, deliveryIntent);
- } catch (RemoteException ex) {
- // ignore it
- }
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ iSms.sendDataForSubscriber(subId, ActivityThread.currentPackageName(),
+ destinationAddress, scAddress, destinationPort & 0xFFFF, data,
+ sentIntent, deliveryIntent);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendDataMessage: Couldn't send SMS - Exception: " + e.getMessage());
+ notifySmsGenericError(sentIntent);
+ }
+ }
+ @Override
+ public void onFailure() {
+ notifySmsErrorNoDefaultSet(context, sentIntent);
+ }
+ });
}
/**
* A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is
* for internal use only.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the SMS being sent on the subscription associated with logical
+ * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
+ * correct subscription.
+ * </p>
+ *
* @hide
*/
public void sendDataMessageWithSelfPermissions(
@@ -908,30 +1211,60 @@
iSms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(),
ActivityThread.currentPackageName(), destinationAddress, scAddress,
destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
- } catch (RemoteException ex) {
- // ignore it
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendDataMessageWithSelfPermissions: Couldn't send SMS - Exception: "
+ + e.getMessage());
+ notifySmsGenericError(sentIntent);
}
}
/**
* Get the SmsManager associated with the default subscription id. The instance will always be
- * associated with the default subscription id, even if the default subscription id is changed.
+ * associated with the default subscription id, even if the default subscription id changes.
*
- * @return the SmsManager associated with the default subscription id
+ * <p class="note"><strong>Note:</strong> For devices that support multiple active subscriptions
+ * at a time, SmsManager will track the subscription set by the user as the default SMS
+ * subscription. If the user has not set a default, {@link SmsManager} may
+ * start an activity to kick off a subscription disambiguation dialog. Most operations will not
+ * complete until the user has chosen the subscription that will be associated with the
+ * operation. If the user cancels the dialog without choosing a subscription, one of the
+ * following will happen, depending on the target SDK version of the application. For
+ * compatibility purposes, if the target SDK level is <= 28, telephony will still send the SMS
+ * over the first available subscription. If the target SDK level is > 28, the operation will
+ * fail to complete.
+ * </p>
+ *
+ * <p class="note"><strong>Note:</strong> If this method is used to perform an operation on a
+ * device that has multiple active subscriptions, the user has not set a default SMS
+ * subscription, and the operation is being performed while the application is not in the
+ * foreground, the SMS disambiguation dialog will not be shown. The result of the operation will
+ * conclude as if the user cancelled the disambiguation dialog and the operation will finish as
+ * outlined above, depending on the target SDK version of the calling application. It is safer
+ * to use {@link #getSmsManagerForSubscriptionId(int)} if the application will perform the
+ * operation while in the background because this can cause unpredictable results, such as the
+ * operation being sent over the wrong subscription or failing completely, depending on the
+ * user's default SMS subscription setting.
+ * </p>
+ *
+ * @return the {@link SmsManager} associated with the default subscription id.
+ *
+ * @see SubscriptionManager#getDefaultSmsSubscriptionId()
*/
public static SmsManager getDefault() {
return sInstance;
}
/**
- * Get the the instance of the SmsManager associated with a particular subscription id
+ * Get the instance of the SmsManager associated with a particular subscription ID.
*
- * @param subId an SMS subscription id, typically accessed using
- * {@link android.telephony.SubscriptionManager}
- * @return the instance of the SmsManager associated with subId
+ * <p class="note"><strong>Note:</strong> Constructing an {@link SmsManager} in this manner will
+ * never cause an SMS disambiguation dialog to appear, unlike {@link #getDefault()}.
+ * </p>
+ *
+ * @see SubscriptionManager#getActiveSubscriptionInfoList()
+ * @see SubscriptionManager#getDefaultSmsSubscriptionId()
*/
public static SmsManager getSmsManagerForSubscriptionId(int subId) {
- // TODO(shri): Add javadoc link once SubscriptionManager is made public api
synchronized(sLockObject) {
SmsManager smsManager = sSubInstances.get(subId);
if (smsManager == null) {
@@ -949,60 +1282,188 @@
/**
* Get the associated subscription id. If the instance was returned by {@link #getDefault()},
* then this method may return different values at different points in time (if the user
- * changes the default subscription id). It will return < 0 if the default subscription id
- * cannot be determined.
+ * changes the default subscription id).
*
- * Additionally, to support legacy applications that are not multi-SIM aware,
- * if the following are true:
- * - We are using a multi-SIM device
- * - A default SMS SIM has not been selected
- * - At least one SIM subscription is available
- * then ask the user to set the default SMS SIM.
+ * <p class="note"><strong>Note:</strong> This method used to display a disambiguation dialog to
+ * the user asking them to choose a default subscription to send SMS messages over if they
+ * haven't chosen yet. Starting in API level 29, we allow the user to not have a default set as
+ * a valid option for the default SMS subscription on multi-SIM devices. We no longer show the
+ * disambiguation dialog and return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if the
+ * device has multiple active subscriptions and no default is set.
+ * </p>
*
- * @return associated subscription id
+ * @return associated subscription ID or {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if
+ * the default subscription id cannot be determined or the device has multiple active
+ * subscriptions and and no default is set ("ask every time") by the user.
*/
public int getSubscriptionId() {
- final int subId = getSubIdOrDefault();
+ try {
+ return (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
+ ? getISmsServiceOrThrow().getPreferredSmsSubscription() : mSubId;
+ } catch (RemoteException e) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ }
+
+ /**
+ * Resolves the subscription id to use for the associated operation if
+ * {@link #getSubscriptionId()} returns {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ *
+ * If app targets API level 28 or below and they are either sending the SMS from the background
+ * or the device has more than one active subscription available and no default is set, we will
+ * use the first logical slot to send the SMS and possibly fail later in the SMS sending
+ * process.
+ *
+ * Regardless of the API level, if the app is the foreground app, then we will show the SMS
+ * disambiguation dialog. If the app is in the background and tries to perform an operation, we
+ * will not show the disambiguation dialog.
+ *
+ * See {@link #getDefault()} for a detailed explanation of how this method operates.
+ *
+ * @param resolverResult The callback that will be called when the subscription is resolved or
+ * fails to be resolved.
+ */
+ private void resolveSubscriptionForOperation(SubscriptionResolverResult resolverResult) {
+ int subId = getSubscriptionId();
boolean isSmsSimPickActivityNeeded = false;
final Context context = ActivityThread.currentApplication().getApplicationContext();
try {
ISms iSms = getISmsService();
if (iSms != null) {
+ // Determines if the SMS SIM pick activity should be shown. This is only shown if:
+ // 1) The device has multiple active subscriptions and an SMS default subscription
+ // hasn't been set, and
+ // 2) SmsManager is being called from the foreground app.
+ // Android does not allow background activity starts, so we need to block this.
+ // if Q+, do not perform requested operation if these two operations are not set. If
+ // <P, perform these operations on phone 0 (for compatibility purposes, since we
+ // used to not wait for the result of this activity).
isSmsSimPickActivityNeeded = iSms.isSmsSimPickActivityNeeded(subId);
}
} catch (RemoteException ex) {
- Log.e(TAG, "Exception in getSubscriptionId");
+ Log.e(TAG, "resolveSubscriptionForOperation", ex);
}
-
- if (isSmsSimPickActivityNeeded) {
- Log.d(TAG, "getSubscriptionId isSmsSimPickActivityNeeded is true");
- // ask the user for a default SMS SIM.
- Intent intent = new Intent();
- intent.setClassName("com.android.settings",
- "com.android.settings.sim.SimDialogActivity");
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK);
- try {
- context.startActivity(intent);
- } catch (ActivityNotFoundException anfe) {
- // If Settings is not installed, only log the error as we do not want to break
- // legacy applications.
- Log.e(TAG, "Unable to launch Settings application.");
- }
+ if (!isSmsSimPickActivityNeeded) {
+ sendResolverResult(resolverResult, subId, false /*pickActivityShown*/);
+ return;
}
-
- return subId;
+ // We need to ask the user pick an appropriate subid for the operation.
+ Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for package "
+ + context.getPackageName());
+ try {
+ // Create the SMS pick activity and call back once the activity is complete. Can't do
+ // it here because we do not have access to the activity context that is performing this
+ // operation.
+ // Requires that the calling process has the SEND_SMS permission.
+ getITelephony().enqueueSmsPickResult(context.getOpPackageName(),
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int subId) {
+ // Runs on binder thread attached to this app's process.
+ sendResolverResult(resolverResult, subId, true /*pickActivityShown*/);
+ }
+ });
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to launch activity", ex);
+ // pickActivityShown is true here because we want to call sendResolverResult and always
+ // have this operation fail. This is because we received a RemoteException here, which
+ // means that telephony is not available and the next operation to Telephony will fail
+ // as well anyways, so we might as well shortcut fail here first.
+ sendResolverResult(resolverResult, subId, true /*pickActivityShown*/);
+ }
}
- /**
- * @return the subscription ID associated with this {@link SmsManager} or the default set by the
- * user if this instance was created using {@link SmsManager#getDefault}.
- *
- * If there is no default set by the user, this method returns
- * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
- */
- private int getSubIdOrDefault() {
- return (mSubId == DEFAULT_SUBSCRIPTION_ID) ? getDefaultSmsSubscriptionId() : mSubId;
+ private void sendResolverResult(SubscriptionResolverResult resolverResult, int subId,
+ boolean pickActivityShown) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ resolverResult.onSuccess(subId);
+ return;
+ }
+
+ if (getTargetSdkVersion() <= Build.VERSION_CODES.P && !pickActivityShown) {
+ // Do not fail, return a success with an INVALID subid for apps targeting P or below
+ // that tried to perform an operation and the SMS disambiguation dialog was never shown,
+ // as these applications may not have been written to handle the failure case properly.
+ // This will resolve to performing the operation on phone 0 in telephony.
+ resolverResult.onSuccess(subId);
+ } else {
+ // Fail if the app targets Q or above or it targets P and below and the disambiguation
+ // dialog was shown and the user clicked out of it.
+ resolverResult.onFailure();
+ }
+ }
+
+ private static int getTargetSdkVersion() {
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ int targetSdk;
+ try {
+ targetSdk = context.getPackageManager().getApplicationInfo(
+ context.getOpPackageName(), 0).targetSdkVersion;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Default to old behavior if we can not find this.
+ targetSdk = -1;
+ }
+ return targetSdk;
+ }
+
+ private static ITelephony getITelephony() {
+ ITelephony binder = ITelephony.Stub.asInterface(
+ ServiceManager.getService(Context.TELEPHONY_SERVICE));
+ if (binder == null) {
+ throw new RuntimeException("Could not find Telephony Service.");
+ }
+ return binder;
+ }
+
+ private static void notifySmsErrorNoDefaultSet(Context context, PendingIntent pendingIntent) {
+ if (pendingIntent != null) {
+ Intent errorMessage = new Intent();
+ errorMessage.putExtra(NO_DEFAULT_EXTRA, true);
+ try {
+ pendingIntent.send(context, RESULT_ERROR_GENERIC_FAILURE, errorMessage);
+ } catch (PendingIntent.CanceledException e) {
+ // Don't worry about it, we do not need to notify the caller if this is the case.
+ }
+ }
+ }
+
+ private static void notifySmsErrorNoDefaultSet(Context context,
+ List<PendingIntent> pendingIntents) {
+ if (pendingIntents != null) {
+ for (PendingIntent pendingIntent : pendingIntents) {
+ Intent errorMessage = new Intent();
+ errorMessage.putExtra(NO_DEFAULT_EXTRA, true);
+ try {
+ pendingIntent.send(context, RESULT_ERROR_GENERIC_FAILURE, errorMessage);
+ } catch (PendingIntent.CanceledException e) {
+ // Don't worry about it, we do not need to notify the caller if this is the
+ // case.
+ }
+ }
+ }
+ }
+
+ private static void notifySmsGenericError(PendingIntent pendingIntent) {
+ if (pendingIntent != null) {
+ try {
+ pendingIntent.send(RESULT_ERROR_GENERIC_FAILURE);
+ } catch (PendingIntent.CanceledException e) {
+ // Don't worry about it, we do not need to notify the caller if this is the case.
+ }
+ }
+ }
+
+ private static void notifySmsGenericError(List<PendingIntent> pendingIntents) {
+ if (pendingIntents != null) {
+ for (PendingIntent pendingIntent : pendingIntents) {
+ try {
+ pendingIntent.send(RESULT_ERROR_GENERIC_FAILURE);
+ } catch (PendingIntent.CanceledException e) {
+ // Don't worry about it, we do not need to notify the caller if this is the
+ // case.
+ }
+ }
+ }
}
/**
@@ -1026,6 +1487,16 @@
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param smsc the SMSC for this message, or NULL for the default SMSC
* @param pdu the raw PDU to store
* @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
@@ -1061,6 +1532,16 @@
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param messageIndex is the record index of the message on ICC
* @return true for success
*
@@ -1092,6 +1573,16 @@
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param messageIndex record index of message to update
* @param newStatus new message status (STATUS_ON_ICC_READ,
* STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
@@ -1124,6 +1615,16 @@
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects
*
* {@hide}
@@ -1156,6 +1657,16 @@
* Note: This call is blocking, callers may want to avoid calling it from
* the main thread of an application.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
* or C.R1001-G (3GPP2)
* @param ranType as defined in class SmsManager, the value can be one of these:
@@ -1172,8 +1683,9 @@
try {
ISms iSms = getISmsService();
if (iSms != null) {
- // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
- success = iSms.enableCellBroadcastForSubscriber(getSubIdOrDefault(),
+ // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
+ // the default phone internally.
+ success = iSms.enableCellBroadcastForSubscriber(getSubscriptionId(),
messageIdentifier, ranType);
}
} catch (RemoteException ex) {
@@ -1192,6 +1704,16 @@
* Note: This call is blocking, callers may want to avoid calling it from
* the main thread of an application.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
* or C.R1001-G (3GPP2)
* @param ranType as defined in class SmsManager, the value can be one of these:
@@ -1209,8 +1731,9 @@
try {
ISms iSms = getISmsService();
if (iSms != null) {
- // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
- success = iSms.disableCellBroadcastForSubscriber(getSubIdOrDefault(),
+ // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
+ // the default phone internally.
+ success = iSms.disableCellBroadcastForSubscriber(getSubscriptionId(),
messageIdentifier, ranType);
}
} catch (RemoteException ex) {
@@ -1230,6 +1753,16 @@
* Note: This call is blocking, callers may want to avoid calling it from
* the main thread of an application.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
* or C.R1001-G (3GPP2)
* @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
@@ -1253,8 +1786,9 @@
try {
ISms iSms = getISmsService();
if (iSms != null) {
- // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
- success = iSms.enableCellBroadcastRangeForSubscriber(getSubIdOrDefault(),
+ // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
+ // the default phone internally.
+ success = iSms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(),
startMessageId, endMessageId, ranType);
}
} catch (RemoteException ex) {
@@ -1273,6 +1807,16 @@
* Note: This call is blocking, callers may want to avoid calling it from
* the main thread of an application.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
* or C.R1001-G (3GPP2)
* @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
@@ -1297,8 +1841,9 @@
try {
ISms iSms = getISmsService();
if (iSms != null) {
- // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
- success = iSms.disableCellBroadcastRangeForSubscriber(getSubIdOrDefault(),
+ // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
+ // the default phone internally.
+ success = iSms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(),
startMessageId, endMessageId, ranType);
}
} catch (RemoteException ex) {
@@ -1312,6 +1857,16 @@
* Create a list of <code>SmsMessage</code>s from a list of RawSmsData
* records returned by <code>getAllMessagesFromIcc()</code>
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param records SMS EF records, returned by
* <code>getAllMessagesFromIcc</code>
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
@@ -1339,6 +1894,16 @@
* SMS over IMS is supported if IMS is registered and SMS is supported
* on IMS.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @return true if SMS over IMS is supported, false otherwise
*
* @see #getImsSmsFormat()
@@ -1359,8 +1924,17 @@
}
/**
- * Gets SMS format supported on IMS. SMS over IMS format is
- * either 3GPP or 3GPP2.
+ * Gets SMS format supported on IMS. SMS over IMS format is either 3GPP or 3GPP2.
+ *
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
*
* @return SmsMessage.FORMAT_3GPP,
* SmsMessage.FORMAT_3GPP2
@@ -1384,19 +1958,24 @@
}
/**
- * Get default sms subscription id
+ * Get default sms subscription id.
*
- * @return the default SMS subscription id
+ * <p class="note"><strong>Note:</strong>This returns a value different from
+ * {@link SubscriptionManager#getDefaultSmsSubscriptionId} if the user has not chosen a default.
+ * In this case it returns the active subscription id if there's only one active subscription
+ * available.
+ *
+ * @return the user-defined default SMS subscription id, or the active subscription id if
+ * there's only one active subscription available, otherwise
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
*/
public static int getDefaultSmsSubscriptionId() {
- ISms iSms = null;
try {
- iSms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
- return iSms.getPreferredSmsSubscription();
- } catch (RemoteException ex) {
- return -1;
- } catch (NullPointerException ex) {
- return -1;
+ return getISmsService().getPreferredSmsSubscription();
+ } catch (RemoteException e) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ } catch (NullPointerException e) {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
}
@@ -1567,6 +2146,15 @@
/**
* Send an MMS message
*
+ * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param context application context
* @param contentUri the content Uri from which the message pdu will be read
* @param locationUrl the optional location url where message should be sent to
@@ -1597,6 +2185,15 @@
/**
* Download an MMS message from carrier by a given location URL
*
+ * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param context application context
* @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
* from the MMS WAP push notification
@@ -1620,9 +2217,8 @@
if (iMms == null) {
return;
}
- iMms.downloadMessage(
- getSubscriptionId(), ActivityThread.currentPackageName(), locationUrl,
- contentUri, configOverrides, downloadedIntent);
+ iMms.downloadMessage(getSubscriptionId(), ActivityThread.currentPackageName(),
+ locationUrl, contentUri, configOverrides, downloadedIntent);
} catch (RemoteException e) {
// Ignore it
}
@@ -1860,6 +2456,15 @@
*
* You can only send a failed text message or a draft text message.
*
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+ *
* @param messageUri the URI of the stored message
* @param scAddress is the service center address or null to use the current default SMSC
* @param sentIntent if not NULL this <code>PendingIntent</code> is
@@ -1887,14 +2492,25 @@
if (messageUri == null) {
throw new IllegalArgumentException("Empty message URI");
}
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendStoredText(
- getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
- scAddress, sentIntent, deliveryIntent);
- } catch (RemoteException ex) {
- // ignore it
- }
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ iSms.sendStoredText(subId, ActivityThread.currentPackageName(), messageUri,
+ scAddress, sentIntent, deliveryIntent);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendStoredTextMessage: Couldn't send SMS - Exception: "
+ + e.getMessage());
+ notifySmsGenericError(sentIntent);
+ }
+ }
+ @Override
+ public void onFailure() {
+ notifySmsErrorNoDefaultSet(context, sentIntent);
+ }
+ });
}
/**
@@ -1904,6 +2520,15 @@
* The provided <code>PendingIntent</code> lists should match the part number of the
* divided text of the stored message by using <code>divideMessage</code>
*
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+ *
* @param messageUri the URI of the stored message
* @param scAddress is the service center address or null to use
* the current default SMSC
@@ -1935,14 +2560,25 @@
if (messageUri == null) {
throw new IllegalArgumentException("Empty message URI");
}
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendStoredMultipartText(
- getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
- scAddress, sentIntents, deliveryIntents);
- } catch (RemoteException ex) {
- // ignore it
- }
+ final Context context = ActivityThread.currentApplication().getApplicationContext();
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ try {
+ ISms iSms = getISmsServiceOrThrow();
+ iSms.sendStoredMultipartText(subId, ActivityThread.currentPackageName(),
+ messageUri, scAddress, sentIntents, deliveryIntents);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendStoredTextMessage: Couldn't send SMS - Exception: "
+ + e.getMessage());
+ notifySmsGenericError(sentIntents);
+ }
+ }
+ @Override
+ public void onFailure() {
+ notifySmsErrorNoDefaultSet(context, sentIntents);
+ }
+ });
}
/**
@@ -1951,6 +2587,15 @@
* This is used for sending a previously sent, but failed-to-send, message or
* for sending a text message that has been stored as a draft.
*
+ * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @param messageUri the URI of the stored message
* @param configOverrides the carrier-specific messaging configuration values to override for
* sending the message.
@@ -2024,6 +2669,16 @@
/**
* Get carrier-dependent configuration values.
*
+ * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
+ * applications or the Telephony framework and will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @return bundle key/values pairs of configuration values
*/
public Bundle getCarrierConfigValues() {
@@ -2039,7 +2694,7 @@
}
/**
- * Create a single use app specific incoming SMS request for the the calling package.
+ * Create a single use app specific incoming SMS request for the calling package.
*
* This method returns a token that if included in a subsequent incoming SMS message will cause
* {@code intent} to be sent with the SMS data.
@@ -2050,6 +2705,15 @@
* An app can only have one request at a time, if the app already has a request pending it will
* be replaced with a new request.
*
+ * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
+ * dialog. If this method is called on a device that has multiple active subscriptions, this
+ * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
+ * default subscription is defined, the subscription ID associated with this message will be
+ * INVALID, which will result in the operation being completed on the subscription associated
+ * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
+ * operation is performed on the correct subscription.
+ * </p>
+ *
* @return Token to include in an SMS message. The token will be 11 characters long.
* @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
*/
@@ -2134,5 +2798,4 @@
config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER));
return filtered;
}
-
}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 7d4bcb7..b705d71 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -843,20 +843,16 @@
}
/**
- * GSM:
- * For an SMS-STATUS-REPORT message, this returns the status field from
- * the status report. This field indicates the status of a previously
- * submitted SMS, if requested. See TS 23.040, 9.2.3.15 TP-Status for a
- * description of values.
- * CDMA:
- * For not interfering with status codes from GSM, the value is
- * shifted to the bits 31-16.
- * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
- * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+ * GSM: For an SMS-STATUS-REPORT message, this returns the status field from the status report.
+ * This field indicates the status of a previously submitted SMS, if requested.
+ * See TS 23.040, 9.2.3.15 TP-Status for a description of values.
+ * CDMA: For not interfering with status codes from GSM, the value is shifted to the bits 31-16.
+ * The value is composed of an error class (bits 25-24) and a status code (bits 23-16). Possible
+ * codes are described in C.S0015-B, v2.0, 4.5.21.
*
- * @return 0 indicates the previously sent message was received.
- * See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
- * for a description of other possible values.
+ * @return 0 for GSM or 2 shifted left by 16 for CDMA indicates the previously sent message was
+ * received. See TS 23.040, 9.2.3.15 and C.S0015-B, v2.0, 4.5.21 for a description of
+ * other possible values.
*/
public int getStatus() {
return mWrappedSmsMessage.getStatus();
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 70471d7..994d705 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -734,7 +734,7 @@
return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
+ " carrierId=" + mCarrierId + " displayName=" + mDisplayName
+ " carrierName=" + mCarrierName + " nameSource=" + mNameSource
- + " iconTint=" + mIconTint + " mNumber=" + mNumber
+ + " iconTint=" + mIconTint + " mNumber=" + Rlog.pii(Build.IS_DEBUGGABLE, mNumber)
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
+ " accessRules " + Arrays.toString(mAccessRules)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 65db458..12422c67 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3175,4 +3175,24 @@
return result;
}
+
+ /**
+ * Get active data subscription id.
+ * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details.
+ *
+ * @return Active data subscription id
+ *
+ * //TODO: Refactor this API in b/134702460
+ * @hide
+ */
+ public static int getActiveDataSubscriptionId() {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.getActiveDataSubscriptionId();
+ }
+ } catch (RemoteException ex) {
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 87ab175..c955efb 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2418,7 +2418,7 @@
@Deprecated
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public @NetworkType int getNetworkType() {
- return getNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ return getNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
}
/**
@@ -2501,7 +2501,7 @@
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public @NetworkType int getDataNetworkType() {
- return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
}
/**
@@ -4733,7 +4733,8 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return DATA_ACTIVITY_NONE;
- return telephony.getDataActivity();
+ return telephony.getDataActivity(
+ getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
} catch (RemoteException ex) {
// the phone process is restarting.
return DATA_ACTIVITY_NONE;
@@ -4781,7 +4782,8 @@
ITelephony telephony = getITelephony();
if (telephony == null)
return DATA_DISCONNECTED;
- return telephony.getDataState();
+ return telephony.getDataState(
+ getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
} catch (RemoteException ex) {
// the phone process is restarting.
return DATA_DISCONNECTED;
@@ -7245,7 +7247,7 @@
* @hide
*/
public boolean getTetherApnRequired() {
- return getTetherApnRequired(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ return getTetherApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
}
/**
@@ -8011,7 +8013,7 @@
ITelephony telephony = getITelephony();
if (telephony != null)
return telephony.isDataConnectivityPossible(getSubId(SubscriptionManager
- .getDefaultDataSubscriptionId()));
+ .getActiveDataSubscriptionId()));
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isDataAllowed", e);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index a9f10b1..9f22d0a 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -549,4 +549,22 @@
*/
public void onAppCallbackDied(int uid, int subscriptionId) {
}
+
+ // Following two methods exist to workaround b/124210145
+ /** @hide */
+ @SystemApi
+ @TestApi
+ @Override
+ public android.os.IBinder asBinder() {
+ return super.asBinder();
+ }
+
+ /** @hide */
+ @SystemApi
+ @TestApi
+ @Override
+ public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
+ int flags) throws RemoteException {
+ return super.onTransact(code, data, reply, flags);
+ }
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 5ce612d..cced447 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -294,4 +294,23 @@
*/
public void onAppCallbackDied(int uid, int subscriptionId) {
}
+
+
+ // Following two methods exist to workaround b/124210145
+ /** @hide */
+ @SystemApi
+ @TestApi
+ @Override
+ public android.os.IBinder asBinder() {
+ return super.asBinder();
+ }
+
+ /** @hide */
+ @SystemApi
+ @TestApi
+ @Override
+ public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
+ int flags) throws RemoteException {
+ return super.onTransact(code, data, reply, flags);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl b/telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl
new file mode 100644
index 0000000..252460e
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+// Copies consumer pattern for an operation that requires an integer result from another process to
+// finish.
+oneway interface IIntegerConsumer {
+ void accept(int result);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index f248893..a481c29 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -285,4 +285,6 @@
boolean isActiveSubId(int subId, String callingPackage);
boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow);
+
+ int getActiveDataSubscriptionId();
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index bd26e1a..4de1917 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.content.Intent;
+import android.content.IntentSender;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Messenger;
@@ -52,6 +53,7 @@
import android.telephony.ims.aidl.IImsRegistrationCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
+import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.OperatorInfo;
@@ -306,18 +308,36 @@
*/
List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg);
- @UnsupportedAppUsage
- int getCallState();
+ @UnsupportedAppUsage
+ int getCallState();
/**
* Returns the call state for a slot.
*/
int getCallStateForSlot(int slotIndex);
- @UnsupportedAppUsage
- int getDataActivity();
- @UnsupportedAppUsage
- int getDataState();
+ /**
+ * Returns a constant indicating the type of activity on a data connection
+ * (cellular).
+ *
+ * @see #DATA_ACTIVITY_NONE
+ * @see #DATA_ACTIVITY_IN
+ * @see #DATA_ACTIVITY_OUT
+ * @see #DATA_ACTIVITY_INOUT
+ * @see #DATA_ACTIVITY_DORMANT
+ */
+ int getDataActivity(int subId);
+
+ /**
+ * Returns a constant indicating the current data connection state
+ * (cellular).
+ *
+ * @see #DATA_DISCONNECTED
+ * @see #DATA_CONNECTING
+ * @see #DATA_CONNECTED
+ * @see #DATA_SUSPENDED
+ */
+ int getDataState(int subId);
/**
* Returns the current active phone type as integer.
@@ -1975,6 +1995,12 @@
boolean isApnMetered(int apnType, int subId);
/**
+ * Enqueue a pending sms Consumer, which will answer with the user specified selection for an
+ * outgoing SmsManager operation.
+ */
+ oneway void enqueueSmsPickResult(String callingPackage, IIntegerConsumer subIdResult);
+
+ /**
* Returns the MMS user agent.
*/
String getMmsUserAgent(int subId);
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 1bd054b..9ad8bef 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -726,12 +726,12 @@
// being reported refers to. The MsgStatus subparameter
// is primarily useful to indicate error conditions -- a
// message without this subparameter is assumed to
- // indicate successful delivery (status == 0).
- if (! mBearerData.messageStatusSet) {
+ // indicate successful delivery.
+ if (!mBearerData.messageStatusSet) {
Rlog.d(LOG_TAG, "DELIVERY_ACK message without msgStatus (" +
(mUserData == null ? "also missing" : "does have") +
" userData).");
- status = 0;
+ status = (BearerData.ERROR_NONE << 8) | BearerData.STATUS_DELIVERED;
} else {
status = mBearerData.errorClass << 8;
status |= mBearerData.messageStatus;
diff --git a/tests/CanvasCompare/res/drawable/sunset1.jpg b/tests/CanvasCompare/res/drawable/sunset1.jpg
index 31da357..3b4e056 100644
--- a/tests/CanvasCompare/res/drawable/sunset1.jpg
+++ b/tests/CanvasCompare/res/drawable/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
index 31da357..086c055 100644
--- a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset1.jpg b/tests/HwAccelerationTest/res/drawable/sunset1.jpg
index 31da357..3b4e056 100644
--- a/tests/HwAccelerationTest/res/drawable/sunset1.jpg
+++ b/tests/HwAccelerationTest/res/drawable/sunset1.jpg
Binary files differ
diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
new file mode 100644
index 0000000..9c7cfb0
--- /dev/null
+++ b/tests/net/common/java/android/net/util/SocketUtilsTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 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.system.NetlinkSocketAddress
+import android.system.Os
+import android.system.OsConstants.AF_INET
+import android.system.OsConstants.ETH_P_ALL
+import android.system.OsConstants.IPPROTO_UDP
+import android.system.OsConstants.RTMGRP_NEIGH
+import android.system.OsConstants.SOCK_DGRAM
+import android.system.PacketSocketAddress
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TEST_INDEX = 123
+private const val TEST_PORT = 555
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class SocketUtilsTest {
+ @Test
+ fun testMakeNetlinkSocketAddress() {
+ val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH)
+ if (nlAddress is NetlinkSocketAddress) {
+ assertEquals(TEST_PORT, nlAddress.getPortId())
+ assertEquals(RTMGRP_NEIGH, nlAddress.getGroupsMask())
+ } else {
+ fail("Not NetlinkSocketAddress object")
+ }
+ }
+
+ @Test
+ fun testMakePacketSocketAddress() {
+ val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX)
+ assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress)
+
+ val ff = 0xff.toByte()
+ val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX,
+ byteArrayOf(ff, ff, ff, ff, ff, ff))
+ assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress)
+ }
+
+ @Test
+ fun testCloseSocket() {
+ // Expect no exception happening with null object.
+ SocketUtils.closeSocket(null)
+
+ val fd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+ assertTrue(fd.valid())
+ SocketUtils.closeSocket(fd)
+ assertFalse(fd.valid())
+ // Expecting socket should be still invalid even closed socket again.
+ SocketUtils.closeSocket(fd)
+ assertFalse(fd.valid())
+ }
+}
diff --git a/tests/net/deflake/Android.bp b/tests/net/deflake/Android.bp
new file mode 100644
index 0000000..1c48c74
--- /dev/null
+++ b/tests/net/deflake/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 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.
+//
+
+java_test_host {
+ name: "FrameworksNetDeflakeTest",
+ srcs: ["src/**/*.kt"],
+ libs: [
+ "junit",
+ "tradefed",
+ ],
+ static_libs: [
+ "kotlin-test",
+ "net-host-tests-utils",
+ ],
+ data: [":FrameworksNetTests"],
+}
\ No newline at end of file
diff --git a/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt b/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt
new file mode 100644
index 0000000..6285525
--- /dev/null
+++ b/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.net
+
+import com.android.testutils.host.DeflakeHostTestBase
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import org.junit.runner.RunWith
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class FrameworksNetDeflakeTest: DeflakeHostTestBase() {
+ override val runCount = 20
+ override val testApkFilename = "FrameworksNetTests.apk"
+ override val testClasses = listOf("com.android.server.ConnectivityServiceTest")
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1a8d905..f14eced 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -192,6 +192,7 @@
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.net.NetworkStatsFactory;
import com.android.testutils.HandlerUtilsKt;
import org.junit.After;
@@ -4901,9 +4902,9 @@
verify(mStatsService, atLeastOnce())
.forceUpdateIfaces(
eq(onlyCell),
- eq(new VpnInfo[0]),
any(NetworkState[].class),
eq(MOBILE_IFNAME));
+ assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService);
// Default network switch should update ifaces.
@@ -4914,9 +4915,9 @@
verify(mStatsService, atLeastOnce())
.forceUpdateIfaces(
eq(onlyWifi),
- eq(new VpnInfo[0]),
any(NetworkState[].class),
eq(WIFI_IFNAME));
+ assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService);
// Disconnect should update ifaces.
@@ -4925,9 +4926,9 @@
verify(mStatsService, atLeastOnce())
.forceUpdateIfaces(
eq(onlyCell),
- eq(new VpnInfo[0]),
any(NetworkState[].class),
eq(MOBILE_IFNAME));
+ assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService);
// Metered change should update ifaces
@@ -4936,9 +4937,9 @@
verify(mStatsService, atLeastOnce())
.forceUpdateIfaces(
eq(onlyCell),
- eq(new VpnInfo[0]),
any(NetworkState[].class),
eq(MOBILE_IFNAME));
+ assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
@@ -4946,9 +4947,9 @@
verify(mStatsService, atLeastOnce())
.forceUpdateIfaces(
eq(onlyCell),
- eq(new VpnInfo[0]),
any(NetworkState[].class),
eq(MOBILE_IFNAME));
+ assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService);
// Captive portal change shouldn't update ifaces
@@ -4957,9 +4958,9 @@
verify(mStatsService, never())
.forceUpdateIfaces(
eq(onlyCell),
- eq(new VpnInfo[0]),
any(NetworkState[].class),
eq(MOBILE_IFNAME));
+ assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService);
// Roaming change should update ifaces
@@ -4968,9 +4969,9 @@
verify(mStatsService, atLeastOnce())
.forceUpdateIfaces(
eq(onlyCell),
- eq(new VpnInfo[0]),
any(NetworkState[].class),
eq(MOBILE_IFNAME));
+ assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos());
reset(mStatsService);
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
new file mode 100644
index 0000000..28785f7
--- /dev/null
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
+import static android.net.NetworkStats.SET_ALL;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.NetworkStats;
+
+import com.android.internal.net.VpnInfo;
+
+/** Superclass with utilities for NetworkStats(Service|Factory)Test */
+abstract class NetworkStatsBaseTest {
+ static final String TEST_IFACE = "test0";
+ static final String TEST_IFACE2 = "test1";
+ static final String TUN_IFACE = "test_nss_tun0";
+
+ static final int UID_RED = 1001;
+ static final int UID_BLUE = 1002;
+ static final int UID_GREEN = 1003;
+ static final int UID_VPN = 1004;
+
+ void assertValues(NetworkStats stats, String iface, int uid, long rxBytes,
+ long rxPackets, long txBytes, long txPackets) {
+ assertValues(
+ stats, iface, uid, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
+ rxBytes, rxPackets, txBytes, txPackets, 0);
+ }
+
+ void assertValues(NetworkStats stats, String iface, int uid, int set, int tag,
+ int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
+ long txBytes, long txPackets, long operations) {
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ final int[] sets;
+ if (set == SET_ALL) {
+ sets = new int[] {SET_ALL, SET_DEFAULT, SET_FOREGROUND};
+ } else {
+ sets = new int[] {set};
+ }
+
+ final int[] roamings;
+ if (roaming == ROAMING_ALL) {
+ roamings = new int[] {ROAMING_ALL, ROAMING_YES, ROAMING_NO};
+ } else {
+ roamings = new int[] {roaming};
+ }
+
+ final int[] meterings;
+ if (metered == METERED_ALL) {
+ meterings = new int[] {METERED_ALL, METERED_YES, METERED_NO};
+ } else {
+ meterings = new int[] {metered};
+ }
+
+ final int[] defaultNetworks;
+ if (defaultNetwork == DEFAULT_NETWORK_ALL) {
+ defaultNetworks =
+ new int[] {DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES, DEFAULT_NETWORK_NO};
+ } else {
+ defaultNetworks = new int[] {defaultNetwork};
+ }
+
+ for (int s : sets) {
+ for (int r : roamings) {
+ for (int m : meterings) {
+ for (int d : defaultNetworks) {
+ final int i = stats.findIndex(iface, uid, s, tag, m, r, d);
+ if (i != -1) {
+ entry.add(stats.getValues(i, null));
+ }
+ }
+ }
+ }
+ }
+
+ assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+ assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
+ assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+ assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+ assertEquals("unexpected operations", operations, entry.operations);
+ }
+
+ VpnInfo createVpnInfo(String[] underlyingIfaces) {
+ VpnInfo info = new VpnInfo();
+ info.ownerUid = UID_VPN;
+ info.vpnIface = TUN_IFACE;
+ info.underlyingIfaces = underlyingIfaces;
+ return info;
+ }
+}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index 95bc7d9..7329474 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -16,8 +16,11 @@
package com.android.server.net;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
@@ -39,6 +42,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
+import com.android.internal.net.VpnInfo;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -54,12 +58,12 @@
import java.io.InputStream;
import java.io.OutputStream;
-/**
- * Tests for {@link NetworkStatsFactory}.
- */
+/** Tests for {@link NetworkStatsFactory}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class NetworkStatsFactoryTest {
+public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
+ private static final String CLAT_PREFIX = "v4-";
+
private File mTestProc;
private NetworkStatsFactory mFactory;
@@ -75,6 +79,8 @@
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni");
mFactory = new NetworkStatsFactory(mTestProc, false);
+ NetworkStatsFactory.updateVpnInfos(new VpnInfo[0]);
+ NetworkStatsFactory.clearStackedIfaces();
}
@After
@@ -99,6 +105,236 @@
}
@Test
+ public void vpnRewriteTrafficThroughItself() throws Exception {
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ //
+ // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+ // over VPN.
+ // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+ // over VPN.
+ //
+ // VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic
+
+ final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_rewrite_through_self);
+
+ assertValues(tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 0);
+ assertValues(tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 0);
+ assertValues(tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 0);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
+ assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 260L, 26L);
+ }
+
+ @Test
+ public void vpnWithClat() throws Exception {
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+ NetworkStatsFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+ // over VPN.
+ // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+ // over VPN.
+ // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over v4-WiFi, and clat
+ // added 20 bytes per packet of extra overhead
+ //
+ // For 1650 bytes sent over v4-WiFi, 4650 bytes were actually sent over WiFi, which is
+ // expected to be split as follows:
+ // UID_RED: 1000 bytes, 100 packets
+ // UID_BLUE: 500 bytes, 50 packets
+ // UID_VPN: 3150 bytes, 0 packets
+ //
+ // For 3300 bytes received over v4-WiFi, 9300 bytes were actually sent over WiFi, which is
+ // expected to be split as follows:
+ // UID_RED: 2000 bytes, 200 packets
+ // UID_BLUE: 1000 bytes, 100 packets
+ // UID_VPN: 6300 bytes, 0 packets
+ final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_with_clat);
+
+ assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_RED, 2000L, 200L, 1000, 100L);
+ assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
+ assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_VPN, 6300L, 0L, 3150L, 0L);
+ }
+
+ @Test
+ public void vpnWithOneUnderlyingIface() throws Exception {
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+ // over VPN.
+ // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+ // over VPN.
+ // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over WiFi.
+ // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
+ // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN.
+ // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
+ // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN.
+ final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
+ assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L);
+ }
+
+ @Test
+ public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
+ // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
+ // over VPN.
+ // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
+ // over VPN.
+ // Additionally, the VPN sends 6000 bytes (600 packets) of its own traffic into the tun
+ // interface (passing that traffic to the VPN endpoint), and receives 5000 bytes (500
+ // packets) from it. Including overhead that is 6600/5500 bytes.
+ // VPN sent 8250 bytes (750 packets), and received 8800 (800 packets) over WiFi.
+ // Of 8250 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
+ // attributed to UID_BLUE, and 6750 bytes attributed to UID_VPN.
+ // Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
+ // attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN.
+ final NetworkStats tunStats =
+ parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_own_traffic);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L);
+ assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 5800L, 500L, 6750L, 600L);
+ }
+
+ @Test
+ public void vpnWithOneUnderlyingIface_withCompression() throws Exception {
+ // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
+ // 3000 bytes (300 packets) were sent/received by UID_BLUE over VPN.
+ // VPN sent/received 1000 bytes (100 packets) over WiFi.
+ // Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE,
+ // with nothing attributed to UID_VPN for both rx/tx traffic.
+ final NetworkStats tunStats =
+ parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_compression);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 250L, 25L, 250L, 25L);
+ assertValues(tunStats, TEST_IFACE, UID_BLUE, 750L, 75L, 750L, 75L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L);
+ }
+
+ @Test
+ public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is duplicating traffic across both WiFi and Cell.
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN.
+ // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total).
+ // Of 8800 bytes over WiFi/Cell, expect:
+ // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE.
+ // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID.
+ final NetworkStats tunStats =
+ parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_duplication);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 500L, 50L, 500L, 50L);
+ assertValues(tunStats, TEST_IFACE, UID_BLUE, 500L, 50L, 500L, 50L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 1200L, 100L, 1200L, 100L);
+ assertValues(tunStats, TEST_IFACE2, UID_RED, 500L, 50L, 500L, 50L);
+ assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 500L, 50L);
+ assertValues(tunStats, TEST_IFACE2, UID_VPN, 1200L, 100L, 1200L, 100L);
+ }
+
+ @Test
+ public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent, and 500 bytes (50 packets) received by UID_RED over
+ // VPN.
+ // VPN sent 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell.
+ // And, it received 330 bytes (30 packets) over WiFi and 220 bytes (20 packets) over Cell.
+ // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for sent (tx)
+ // traffic. For received (rx) traffic, expect 300 bytes over WiFi and 200 bytes over Cell.
+ //
+ // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic.
+ // And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic.
+ final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 300L, 30L, 600L, 60L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 30L, 0L, 60L, 0L);
+ assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 400L, 40L);
+ assertValues(tunStats, TEST_IFACE2, UID_VPN, 20L, 0L, 40L, 0L);
+ }
+
+ @Test
+ public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface:
+ // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
+ // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell.
+ // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both
+ // rx/tx.
+ // UID_VPN gets nothing attributed to it (avoiding negative stats).
+ final NetworkStats tunStats =
+ parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split_compression);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 600L, 60L, 600L, 60L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L);
+ assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 200L, 20L);
+ assertValues(tunStats, TEST_IFACE2, UID_VPN, 0L, 0L, 0L, 0L);
+ }
+
+ @Test
+ public void vpnWithIncorrectUnderlyingIface() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
+ // but has declared only WiFi (TEST_IFACE) in its underlying network set.
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ NetworkStatsFactory.updateVpnInfos(vpnInfos);
+
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
+ // VPN sent/received 1100 bytes (100 packets) over Cell.
+ // Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic.
+ final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_incorrect_iface);
+
+ assertValues(tunStats, TEST_IFACE, UID_RED, 0L, 0L, 0L, 0L);
+ assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L);
+ assertValues(tunStats, TEST_IFACE2, UID_RED, 0L, 0L, 0L, 0L);
+ assertValues(tunStats, TEST_IFACE2, UID_VPN, 1100L, 100L, 1100L, 100L);
+ }
+
+ @Test
public void testKernelTags() throws Exception {
assertEquals(0, kernelToTag("0x0000000000000000"));
assertEquals(0x32, kernelToTag("0x0000003200000000"));
@@ -146,7 +382,7 @@
}
@Test
- public void testDoubleClatAccounting() throws Exception {
+ public void testDoubleClatAccountingSimple() throws Exception {
NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
// xt_qtaguid_with_clat_simple is a synthetic file that simulates
@@ -154,12 +390,17 @@
// - 41 sent 464xlat packets of size 100 bytes
// - no other traffic on base interface for root uid.
NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_simple);
- assertEquals(4, stats.size());
+ assertEquals(3, stats.size());
assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 46860L, 4920L);
assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L);
+ }
- stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat);
+ @Test
+ public void testDoubleClatAccounting() throws Exception {
+ NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0");
+
+ NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat);
assertEquals(42, stats.size());
assertStatsEntry(stats, "v4-wlan0", 0, SET_DEFAULT, 0x0, 356L, 276L);
@@ -272,11 +513,19 @@
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO);
+ assertStatsEntry(stats, iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+ rxBytes, rxPackets, txBytes, txPackets);
+ }
+
+ private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
+ int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
+ long txBytes, long txPackets) {
+ final int i = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork);
+
if (i < 0) {
- fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)",
- iface, uid, set, tag));
+ fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d, metered:"
+ + " %d, roaming: %d, defaultNetwork: %d)",
+ iface, uid, set, tag, metered, roaming, defaultNetwork));
}
final NetworkStats.Entry entry = stats.getValues(i, null);
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index 9a47f35..c0f9dc1 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -52,7 +52,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.net.VpnInfo;
import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
import com.android.testutils.HandlerUtilsKt;
@@ -93,8 +92,6 @@
private static final long BASE_BYTES = 7 * MB_IN_BYTES;
private static final int INVALID_TYPE = -1;
- private static final VpnInfo[] VPN_INFO = new VpnInfo[0];
-
private long mElapsedRealtime;
private HandlerThread mObserverHandlerThread;
@@ -247,8 +244,7 @@
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
}
@@ -271,15 +267,13 @@
.addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
.addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
}
@@ -303,16 +297,14 @@
.addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
.addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
BASE_BYTES + THRESHOLD_BYTES, 22L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@@ -337,8 +329,7 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
@@ -346,8 +337,7 @@
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@@ -372,8 +362,7 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
@@ -381,8 +370,7 @@
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
}
@@ -406,8 +394,7 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
@@ -415,8 +402,7 @@
DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@@ -441,8 +427,7 @@
.addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
@@ -450,8 +435,7 @@
ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
- xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
- VPN_INFO, TEST_START);
+ xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 31df9f3..1682dd1 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -23,7 +23,6 @@
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.INTERFACES_ALL;
@@ -42,7 +41,6 @@
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
@@ -56,7 +54,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -98,7 +95,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
@@ -129,13 +125,9 @@
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class NetworkStatsServiceTest {
+public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
private static final String TAG = "NetworkStatsServiceTest";
- private static final String TEST_IFACE = "test0";
- private static final String TEST_IFACE2 = "test1";
- private static final String TUN_IFACE = "test_nss_tun0";
-
private static final long TEST_START = 1194220800000L;
private static final String IMSI_1 = "310004";
@@ -146,11 +138,6 @@
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
- private static final int UID_RED = 1001;
- private static final int UID_BLUE = 1002;
- private static final int UID_GREEN = 1003;
- private static final int UID_VPN = 1004;
-
private static final Network WIFI_NETWORK = new Network(100);
private static final Network MOBILE_NETWORK = new Network(101);
private static final Network VPN_NETWORK = new Network(102);
@@ -216,12 +203,9 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectSystemReady();
- assertNull(mService.getTunAdjustedStats());
mService.systemReady();
- // Verify that system ready fetches realtime stats and initializes tun adjusted stats.
+ // Verify that system ready fetches realtime stats
verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
- assertNotNull("failed to initialize TUN adjusted stats", mService.getTunAdjustedStats());
- assertEquals(0, mService.getTunAdjustedStats().size());
mSession = mService.openSession();
assertNotNull("openSession() failed", mSession);
@@ -257,7 +241,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -301,7 +285,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -375,8 +359,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// modify some number on wifi, and trigger poll event
incrementCurrentTime(2 * HOUR_IN_MILLIS);
@@ -417,8 +400,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
// create some traffic on first network
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -453,7 +435,7 @@
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
forcePollAndWaitForIdle();
@@ -493,8 +475,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -552,8 +533,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
// create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -580,7 +560,7 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
forcePollAndWaitForIdle();
@@ -610,8 +590,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// create some traffic for two apps
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -669,7 +648,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
NetworkStats.Entry entry1 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
@@ -713,7 +692,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
NetworkStats.Entry uidStats = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
@@ -741,8 +720,12 @@
// mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations:
// 1) NetworkStatsService#systemReady from #setUp.
// 2) mService#forceUpdateIfaces in the test above.
- // 3) Finally, mService#getDetailedUidStats.
- verify(mNetManager, times(3)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
+ //
+ // Additionally, we should have one call from the above call to mService#getDetailedUidStats
+ // with the augmented ifaceFilter
+ verify(mNetManager, times(2)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
+ verify(mNetManager, times(1)).getNetworkStatsUidDetail(
+ eq(UID_ALL), eq(NetworkStatsFactory.augmentWithStackedInterfaces(ifaceFilter)));
assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE));
assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface));
assertEquals(2, stats.size());
@@ -759,8 +742,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -817,8 +799,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -858,8 +839,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
// Create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -897,8 +877,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states));
-
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states));
// create some tethering traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -930,481 +909,6 @@
}
@Test
- public void vpnRewriteTrafficThroughItself() throws Exception {
- // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
- expectDefaultSettings();
- NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
-
- mService.forceUpdateIfaces(
- new Network[] {WIFI_NETWORK, VPN_NETWORK},
- vpnInfos,
- networkStates,
- getActiveIface(networkStates));
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- //
- // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
- // over VPN.
- // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
- // over VPN.
- //
- // VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic
- // (100 bytes).
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 5)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L)
- .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L)
- // VPN rewrites all the packets read from TUN + 100 additional bytes of VPN's
- // own traffic.
- .addValues(TUN_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 0L, 0L, 1600L, 160L, 2L)
- // VPN sent 1760 bytes over WiFi in foreground (SET_FOREGROUND) i.e. 1600
- // bytes (160 packets) + 1 byte/packet overhead (=160 bytes).
- .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1760L, 176L, 1L)
- // VPN received 3300 bytes over WiFi in background (SET_DEFAULT) i.e. 3000 bytes
- // (300 packets) + 1 byte/packet encryption overhead (=300 bytes).
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 3300L, 300L, 0L, 0L, 1L));
-
- forcePollAndWaitForIdle();
-
- // Verify increased TUN usage by UID_VPN does not get attributed to other apps.
- NetworkStats tunStats =
- mService.getDetailedUidStats(new String[] {TUN_IFACE});
- assertValues(
- tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 1);
- assertValues(
- tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 1);
- assertValues(
- tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 2);
-
- // Verify correct attribution over WiFi.
- assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1);
- assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1);
- assertUidTotal(sTemplateWifi, UID_VPN, 300L, 0L, 260L, 26L, 2);
- }
-
- @Test
- public void vpnWithOneUnderlyingIface() throws Exception {
- // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
- expectDefaultSettings();
- NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
-
- mService.forceUpdateIfaces(
- new Network[] {WIFI_NETWORK, VPN_NETWORK},
- vpnInfos,
- networkStates,
- getActiveIface(networkStates));
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
- // over VPN.
- // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
- // over VPN.
- // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over WiFi.
- // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
- // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN.
- // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
- // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L)
- .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L)
- // VPN received 3300 bytes over WiFi in background (SET_DEFAULT).
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 3300L, 300L, 0L, 0L, 1L)
- // VPN sent 1650 bytes over WiFi in foreground (SET_FOREGROUND).
- .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1650L, 150L, 1L));
-
- forcePollAndWaitForIdle();
-
- assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1);
- assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1);
- assertUidTotal(sTemplateWifi, UID_VPN, 300L, 0L, 150L, 0L, 2);
- }
-
- @Test
- public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
- // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
- expectDefaultSettings();
- NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
-
- mService.forceUpdateIfaces(
- new Network[] {WIFI_NETWORK, VPN_NETWORK},
- vpnInfos,
- networkStates,
- getActiveIface(networkStates));
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED
- // over VPN.
- // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE
- // over VPN.
- // Additionally, the VPN sends 6000 bytes (600 packets) of its own traffic into the tun
- // interface (passing that traffic to the VPN endpoint), and receives 5000 bytes (500
- // packets) from it. Including overhead that is 6600/5500 bytes.
- // VPN sent 8250 bytes (750 packets), and received 8800 (800 packets) over WiFi.
- // Of 8250 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes
- // attributed to UID_BLUE, and 6750 bytes attributed to UID_VPN.
- // Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes
- // attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L)
- .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L)
- .addValues(TUN_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 5000L, 500L, 6000L, 600L, 1L)
- // VPN received 8800 bytes over WiFi in background (SET_DEFAULT).
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 8800L, 800L, 0L, 0L, 1L)
- // VPN sent 8250 bytes over WiFi in foreground (SET_FOREGROUND).
- .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 8250L, 750L, 1L));
-
- forcePollAndWaitForIdle();
-
- assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1);
- assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1);
- assertUidTotal(sTemplateWifi, UID_VPN, 5800L, 500L, 6750L, 600L, 2);
- }
-
- @Test
- public void vpnWithOneUnderlyingIface_withCompression() throws Exception {
- // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
- expectDefaultSettings();
- NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
-
- mService.forceUpdateIfaces(
- new Network[] {WIFI_NETWORK, VPN_NETWORK},
- vpnInfos,
- networkStates,
- getActiveIface(networkStates));
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
- // 3000 bytes (300 packets) were sent/received by UID_BLUE over VPN.
- // VPN sent/received 1000 bytes (100 packets) over WiFi.
- // Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE,
- // with nothing attributed to UID_VPN for both rx/tx traffic.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
- .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 3000L, 300L, 3000L, 300L, 1L)
- .addValues(
- TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 0L));
-
- forcePollAndWaitForIdle();
-
- assertUidTotal(sTemplateWifi, UID_RED, 250L, 25L, 250L, 25L, 0);
- assertUidTotal(sTemplateWifi, UID_BLUE, 750L, 75L, 750L, 75L, 0);
- assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0);
- }
-
- @Test
- public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
- // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
- // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
- // Additionally, VPN is duplicating traffic across both WiFi and Cell.
- expectDefaultSettings();
- NetworkState[] networkStates =
- new NetworkState[] {
- buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
- };
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
-
- mService.forceUpdateIfaces(
- new Network[] {WIFI_NETWORK, VPN_NETWORK},
- vpnInfos,
- networkStates,
- getActiveIface(networkStates));
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN.
- // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total).
- // Of 8800 bytes over WiFi/Cell, expect:
- // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE.
- // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L)
- .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L)
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L)
- .addValues(
- TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L));
-
- forcePollAndWaitForIdle();
-
- assertUidTotal(sTemplateWifi, UID_RED, 500L, 50L, 500L, 50L, 1);
- assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1);
- assertUidTotal(sTemplateWifi, UID_VPN, 1200L, 100L, 1200L, 100L, 2);
-
- assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 500L, 50L, 500L, 50L, 1);
- assertUidTotal(buildTemplateMobileWildcard(), UID_BLUE, 500L, 50L, 500L, 50L, 1);
- assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1200L, 100L, 1200L, 100L, 2);
- }
-
- @Test
- public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
- // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
- // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
- // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
- expectDefaultSettings();
- NetworkState[] networkStates =
- new NetworkState[] {
- buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
- };
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
-
- mService.forceUpdateIfaces(
- new Network[] {WIFI_NETWORK, VPN_NETWORK},
- vpnInfos,
- networkStates,
- getActiveIface(networkStates));
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were sent, and 500 bytes (50 packets) received by UID_RED over
- // VPN.
- // VPN sent 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell.
- // And, it received 330 bytes (30 packets) over WiFi and 220 bytes (20 packets) over Cell.
- // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for sent (tx)
- // traffic. For received (rx) traffic, expect 300 bytes over WiFi and 200 bytes over Cell.
- //
- // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic.
- // And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 500L, 50L, 1000L, 100L, 2L)
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 330L, 30L, 660L, 60L, 1L)
- .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 220L, 20L, 440L, 40L, 1L));
-
- forcePollAndWaitForIdle();
-
- assertUidTotal(sTemplateWifi, UID_RED, 300L, 30L, 600L, 60L, 1);
- assertUidTotal(sTemplateWifi, UID_VPN, 30L, 0L, 60L, 0L, 1);
-
- assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 400L, 40L, 1);
- assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 20L, 0L, 40L, 0L, 1);
- }
-
- @Test
- public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
- // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
- // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
- // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
- expectDefaultSettings();
- NetworkState[] networkStates =
- new NetworkState[] {
- buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
- };
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
-
- mService.forceUpdateIfaces(
- new Network[] {WIFI_NETWORK, VPN_NETWORK},
- vpnInfos,
- networkStates,
- getActiveIface(networkStates));
- // create some traffic (assume 10 bytes of MTU for VPN interface:
- // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
- // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell.
- // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both
- // rx/tx.
- // UID_VPN gets nothing attributed to it (avoiding negative stats).
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 600L, 60L, 600L, 60L, 0L)
- .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 200L, 20L, 200L, 20L, 0L));
-
- forcePollAndWaitForIdle();
-
- assertUidTotal(sTemplateWifi, UID_RED, 600L, 60L, 600L, 60L, 0);
- assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0);
-
- assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 200L, 20L, 0);
- assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 0L, 0L, 0L, 0L, 0);
- }
-
- @Test
- public void vpnWithIncorrectUnderlyingIface() throws Exception {
- // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
- // but has declared only WiFi (TEST_IFACE) in its underlying network set.
- expectDefaultSettings();
- NetworkState[] networkStates =
- new NetworkState[] {
- buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
- };
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectBandwidthControlCheck();
-
- mService.forceUpdateIfaces(
- new Network[] {WIFI_NETWORK, VPN_NETWORK},
- vpnInfos,
- networkStates,
- getActiveIface(networkStates));
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
- // VPN sent/received 1100 bytes (100 packets) over Cell.
- // Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
- .addValues(
- TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 1100L, 100L, 1L));
-
- forcePollAndWaitForIdle();
-
- assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L, 0L, 0L, 0);
- assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0);
- assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 0L, 0L, 0L, 0L, 0);
- assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1100L, 100L, 1100L, 100L, 1);
- }
-
- @Test
- public void recordSnapshot_migratesTunTrafficAndUpdatesTunAdjustedStats() throws Exception {
- assertEquals(0, mService.getTunAdjustedStats().size());
- // VPN using WiFi (TEST_IFACE).
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- expectBandwidthControlCheck();
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
- // VPN received 1100 bytes (100 packets) over WiFi.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
-
- // this should lead to NSS#recordSnapshotLocked
- mService.forceUpdateIfaces(
- new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
-
- // Verify TUN adjusted stats have traffic migrated correctly.
- // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100
- // bytes attributed to UID_VPN.
- NetworkStats tunAdjStats = mService.getTunAdjustedStats();
- assertValues(
- tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
- assertValues(
- tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
- }
-
- @Test
- public void getDetailedUidStats_migratesTunTrafficAndUpdatesTunAdjustedStats()
- throws Exception {
- assertEquals(0, mService.getTunAdjustedStats().size());
- // VPN using WiFi (TEST_IFACE).
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(
- new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
- // VPN received 1100 bytes (100 packets) over WiFi.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
-
- mService.getDetailedUidStats(INTERFACES_ALL);
-
- // Verify internally maintained TUN adjusted stats
- NetworkStats tunAdjStats = mService.getTunAdjustedStats();
- // Verify stats for TEST_IFACE (WiFi):
- // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100
- // bytes attributed to UID_VPN.
- assertValues(
- tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
- assertValues(
- tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
- // Verify stats for TUN_IFACE; only UID_RED should have usage on it.
- assertValues(
- tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
- assertValues(
- tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0);
-
- // lets assume that since last time, VPN received another 1100 bytes (same assumptions as
- // before i.e. UID_RED downloaded another 1000 bytes).
- incrementCurrentTime(HOUR_IN_MILLIS);
- // Note - NetworkStatsFactory returns counters that are monotonically increasing.
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 0L, 0L, 0L));
-
- mService.getDetailedUidStats(INTERFACES_ALL);
-
- tunAdjStats = mService.getTunAdjustedStats();
- // verify TEST_IFACE stats:
- assertValues(
- tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0);
- assertValues(
- tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 200L, 0L, 0L, 0L, 0);
- // verify TUN_IFACE stats:
- assertValues(
- tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0);
- assertValues(
- tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0);
- }
-
- @Test
- public void getDetailedUidStats_returnsCorrectStatsWithVpnRunning() throws Exception {
- // VPN using WiFi (TEST_IFACE).
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- expectBandwidthControlCheck();
- mService.forceUpdateIfaces(
- new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
- // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
- // overhead per packet):
- // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
- // VPN received 1100 bytes (100 packets) over WiFi.
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
- .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
- .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
-
- // Query realtime stats for TEST_IFACE.
- NetworkStats queriedStats =
- mService.getDetailedUidStats(new String[] {TEST_IFACE});
-
- assertEquals(HOUR_IN_MILLIS, queriedStats.getElapsedRealtime());
- // verify that returned stats are only for TEST_IFACE and VPN traffic is migrated correctly.
- assertEquals(new String[] {TEST_IFACE}, queriedStats.getUniqueIfaces());
- assertValues(
- queriedStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
- assertValues(
- queriedStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
- }
-
- @Test
public void testRegisterUsageCallback() throws Exception {
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
@@ -1414,7 +918,7 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
- mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states));
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states));
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -1634,59 +1138,6 @@
}
}
- private static void assertValues(NetworkStats stats, String iface, int uid, int set,
- int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
- long txBytes, long txPackets, int operations) {
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- final int[] sets;
- if (set == SET_ALL) {
- sets = new int[] { SET_ALL, SET_DEFAULT, SET_FOREGROUND };
- } else {
- sets = new int[] { set };
- }
-
- final int[] roamings;
- if (roaming == ROAMING_ALL) {
- roamings = new int[] { ROAMING_ALL, ROAMING_YES, ROAMING_NO };
- } else {
- roamings = new int[] { roaming };
- }
-
- final int[] meterings;
- if (metered == METERED_ALL) {
- meterings = new int[] { METERED_ALL, METERED_YES, METERED_NO };
- } else {
- meterings = new int[] { metered };
- }
-
- final int[] defaultNetworks;
- if (defaultNetwork == DEFAULT_NETWORK_ALL) {
- defaultNetworks = new int[] { DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES,
- DEFAULT_NETWORK_NO };
- } else {
- defaultNetworks = new int[] { defaultNetwork };
- }
-
- for (int s : sets) {
- for (int r : roamings) {
- for (int m : meterings) {
- for (int d : defaultNetworks) {
- final int i = stats.findIndex(iface, uid, s, tag, m, r, d);
- if (i != -1) {
- entry.add(stats.getValues(i, null));
- }
- }
- }
- }
- }
-
- assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
- assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
- assertEquals("unexpected txBytes", txBytes, entry.txBytes);
- assertEquals("unexpected txPackets", txPackets, entry.txPackets);
- assertEquals("unexpected operations", operations, entry.operations);
- }
-
private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes,
long rxPackets, long txBytes, long txPackets, int operations) {
final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
@@ -1752,14 +1203,6 @@
return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
}
- private static VpnInfo createVpnInfo(String[] underlyingIfaces) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = UID_VPN;
- info.vpnIface = TUN_IFACE;
- info.underlyingIfaces = underlyingIfaces;
- return info;
- }
-
private long getElapsedRealtime() {
return mElapsedRealtime;
}
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface b/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface
new file mode 100644
index 0000000..fc92715
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface
@@ -0,0 +1,3 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test1 0x0 1004 0 1100 100 1100 100 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying
new file mode 100644
index 0000000..1ef1889
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying
@@ -0,0 +1,5 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
+4 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+5 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression
new file mode 100644
index 0000000..6d6bf55
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression
@@ -0,0 +1,4 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test_nss_tun0 0x0 1002 0 3000 300 3000 300 0 0 0 0 0 0 0 0 0 0 0 0
+4 test0 0x0 1004 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
new file mode 100644
index 0000000..2c2e5d2
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic
@@ -0,0 +1,6 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
+4 test_nss_tun0 0x0 1004 0 5000 500 6000 600 0 0 0 0 0 0 0 0 0 0 0 0
+5 test0 0x0 1004 0 8800 800 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+6 test0 0x0 1004 1 0 0 8250 750 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self b/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self
new file mode 100644
index 0000000..afcdd71
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self
@@ -0,0 +1,6 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
+4 test_nss_tun0 0x0 1004 0 0 0 1600 160 0 0 0 0 0 0 0 0 0 0 0 0
+5 test0 0x0 1004 1 0 0 1760 176 0 0 0 0 0 0 0 0 0 0 0 0
+6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication
new file mode 100644
index 0000000..d7c7eb9
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication
@@ -0,0 +1,5 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test_nss_tun0 0x0 1002 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+4 test0 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0
+5 test1 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split
new file mode 100644
index 0000000..38a3dce
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split
@@ -0,0 +1,4 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 500 50 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test0 0x0 1004 0 330 30 660 60 0 0 0 0 0 0 0 0 0 0 0 0
+4 test1 0x0 1004 0 220 20 440 40 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
new file mode 100644
index 0000000..d35244b
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression
@@ -0,0 +1,4 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test0 0x0 1004 0 600 60 600 60 0 0 0 0 0 0 0 0 0 0 0 0
+4 test1 0x0 1004 0 200 20 200 20 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_vpn_with_clat b/tests/net/res/raw/xt_qtaguid_vpn_with_clat
new file mode 100644
index 0000000..0d893d5
--- /dev/null
+++ b/tests/net/res/raw/xt_qtaguid_vpn_with_clat
@@ -0,0 +1,8 @@
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
+3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0
+4 v4-test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+5 v4-test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
+6 test0 0x0 0 0 9300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+7 test0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+8 test0 0x0 1029 0 0 0 4650 150 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file
diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple
index 8c132e7..b37fae6 100644
--- a/tests/net/res/raw/xt_qtaguid_with_clat_simple
+++ b/tests/net/res/raw/xt_qtaguid_with_clat_simple
@@ -2,5 +2,4 @@
2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0
3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0
-5 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-6 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0
+5 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index 550edd3..77ecf62 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -115,12 +115,9 @@
using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
IterationEnder iteration_ender(cookie, EndIteration);
- ZipString zip_entry_name;
+ std::string zip_entry_path;
ZipEntry zip_data;
- while ((result = Next(cookie, &zip_data, &zip_entry_name)) == 0) {
- std::string zip_entry_path =
- std::string(reinterpret_cast<const char*>(zip_entry_name.name),
- zip_entry_name.name_length);
+ while ((result = Next(cookie, &zip_data, &zip_entry_path)) == 0) {
std::string nested_path = path.to_string() + "@" + zip_entry_path;
std::unique_ptr<IFile> file =
util::make_unique<ZipFile>(collection->handle_, zip_data, Source(nested_path));