Improve how battery stats collects network stats.
This optimizes the path for battery stats to collect per-uid network usage. It now collects wifi and mobile usage separately, with a path that allows it to recycle all data structures and filter out stats it isn't interested in before they come back to java. This is setting us up for the actual goal, to collect mobile stats independently each time the mobile radio goes down, allowing us to distribute mobile radio usage across uids based on the number of packets they transferred during a session. Change-Id: I21a0f517cf087ea5aa8b8dd535e20b46e361a52b
This commit is contained in:
@@ -44,6 +44,8 @@ public class NetworkStats implements Parcelable {
|
|||||||
public static final String IFACE_ALL = null;
|
public static final String IFACE_ALL = null;
|
||||||
/** {@link #uid} value when UID details unavailable. */
|
/** {@link #uid} value when UID details unavailable. */
|
||||||
public static final int UID_ALL = -1;
|
public static final int UID_ALL = -1;
|
||||||
|
/** {@link #tag} value matching any tag. */
|
||||||
|
public static final int TAG_ALL = -1;
|
||||||
/** {@link #set} value when all sets combined. */
|
/** {@link #set} value when all sets combined. */
|
||||||
public static final int SET_ALL = -1;
|
public static final int SET_ALL = -1;
|
||||||
/** {@link #set} value where background data is accounted. */
|
/** {@link #set} value where background data is accounted. */
|
||||||
@@ -59,8 +61,9 @@ public class NetworkStats implements Parcelable {
|
|||||||
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
|
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
|
||||||
* generated.
|
* generated.
|
||||||
*/
|
*/
|
||||||
private final long elapsedRealtime;
|
private long elapsedRealtime;
|
||||||
private int size;
|
private int size;
|
||||||
|
private int capacity;
|
||||||
private String[] iface;
|
private String[] iface;
|
||||||
private int[] uid;
|
private int[] uid;
|
||||||
private int[] set;
|
private int[] set;
|
||||||
@@ -152,20 +155,27 @@ public class NetworkStats implements Parcelable {
|
|||||||
public NetworkStats(long elapsedRealtime, int initialSize) {
|
public NetworkStats(long elapsedRealtime, int initialSize) {
|
||||||
this.elapsedRealtime = elapsedRealtime;
|
this.elapsedRealtime = elapsedRealtime;
|
||||||
this.size = 0;
|
this.size = 0;
|
||||||
this.iface = new String[initialSize];
|
if (initialSize >= 0) {
|
||||||
this.uid = new int[initialSize];
|
this.capacity = initialSize;
|
||||||
this.set = new int[initialSize];
|
this.iface = new String[initialSize];
|
||||||
this.tag = new int[initialSize];
|
this.uid = new int[initialSize];
|
||||||
this.rxBytes = new long[initialSize];
|
this.set = new int[initialSize];
|
||||||
this.rxPackets = new long[initialSize];
|
this.tag = new int[initialSize];
|
||||||
this.txBytes = new long[initialSize];
|
this.rxBytes = new long[initialSize];
|
||||||
this.txPackets = new long[initialSize];
|
this.rxPackets = new long[initialSize];
|
||||||
this.operations = new long[initialSize];
|
this.txBytes = new long[initialSize];
|
||||||
|
this.txPackets = new long[initialSize];
|
||||||
|
this.operations = new long[initialSize];
|
||||||
|
} else {
|
||||||
|
// Special case for use by NetworkStatsFactory to start out *really* empty.
|
||||||
|
this.capacity = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkStats(Parcel parcel) {
|
public NetworkStats(Parcel parcel) {
|
||||||
elapsedRealtime = parcel.readLong();
|
elapsedRealtime = parcel.readLong();
|
||||||
size = parcel.readInt();
|
size = parcel.readInt();
|
||||||
|
capacity = parcel.readInt();
|
||||||
iface = parcel.createStringArray();
|
iface = parcel.createStringArray();
|
||||||
uid = parcel.createIntArray();
|
uid = parcel.createIntArray();
|
||||||
set = parcel.createIntArray();
|
set = parcel.createIntArray();
|
||||||
@@ -181,6 +191,7 @@ public class NetworkStats implements Parcelable {
|
|||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeLong(elapsedRealtime);
|
dest.writeLong(elapsedRealtime);
|
||||||
dest.writeInt(size);
|
dest.writeInt(size);
|
||||||
|
dest.writeInt(capacity);
|
||||||
dest.writeStringArray(iface);
|
dest.writeStringArray(iface);
|
||||||
dest.writeIntArray(uid);
|
dest.writeIntArray(uid);
|
||||||
dest.writeIntArray(set);
|
dest.writeIntArray(set);
|
||||||
@@ -222,8 +233,8 @@ public class NetworkStats implements Parcelable {
|
|||||||
* object can be recycled across multiple calls.
|
* object can be recycled across multiple calls.
|
||||||
*/
|
*/
|
||||||
public NetworkStats addValues(Entry entry) {
|
public NetworkStats addValues(Entry entry) {
|
||||||
if (size >= this.iface.length) {
|
if (size >= capacity) {
|
||||||
final int newLength = Math.max(iface.length, 10) * 3 / 2;
|
final int newLength = Math.max(size, 10) * 3 / 2;
|
||||||
iface = Arrays.copyOf(iface, newLength);
|
iface = Arrays.copyOf(iface, newLength);
|
||||||
uid = Arrays.copyOf(uid, newLength);
|
uid = Arrays.copyOf(uid, newLength);
|
||||||
set = Arrays.copyOf(set, newLength);
|
set = Arrays.copyOf(set, newLength);
|
||||||
@@ -233,6 +244,7 @@ public class NetworkStats implements Parcelable {
|
|||||||
txBytes = Arrays.copyOf(txBytes, newLength);
|
txBytes = Arrays.copyOf(txBytes, newLength);
|
||||||
txPackets = Arrays.copyOf(txPackets, newLength);
|
txPackets = Arrays.copyOf(txPackets, newLength);
|
||||||
operations = Arrays.copyOf(operations, newLength);
|
operations = Arrays.copyOf(operations, newLength);
|
||||||
|
capacity = newLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
iface[size] = entry.iface;
|
iface[size] = entry.iface;
|
||||||
@@ -270,6 +282,10 @@ public class NetworkStats implements Parcelable {
|
|||||||
return elapsedRealtime;
|
return elapsedRealtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setElapsedRealtime(long time) {
|
||||||
|
elapsedRealtime = time;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return age of this {@link NetworkStats} object with respect to
|
* Return age of this {@link NetworkStats} object with respect to
|
||||||
* {@link SystemClock#elapsedRealtime()}.
|
* {@link SystemClock#elapsedRealtime()}.
|
||||||
@@ -284,7 +300,7 @@ public class NetworkStats implements Parcelable {
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public int internalSize() {
|
public int internalSize() {
|
||||||
return iface.length;
|
return capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@@ -507,8 +523,25 @@ public class NetworkStats implements Parcelable {
|
|||||||
* If counters have rolled backwards, they are clamped to {@code 0} and
|
* If counters have rolled backwards, they are clamped to {@code 0} and
|
||||||
* reported to the given {@link NonMonotonicObserver}.
|
* reported to the given {@link NonMonotonicObserver}.
|
||||||
*/
|
*/
|
||||||
public static <C> NetworkStats subtract(
|
public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
|
||||||
NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie) {
|
NonMonotonicObserver<C> observer, C cookie) {
|
||||||
|
return subtract(left, right, observer, cookie, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract the two given {@link NetworkStats} objects, returning the delta
|
||||||
|
* between two snapshots in time. Assumes that statistics rows collect over
|
||||||
|
* time, and that none of them have disappeared.
|
||||||
|
* <p>
|
||||||
|
* If counters have rolled backwards, they are clamped to {@code 0} and
|
||||||
|
* reported to the given {@link NonMonotonicObserver}.
|
||||||
|
* <p>
|
||||||
|
* If <var>recycle</var> is supplied, this NetworkStats object will be
|
||||||
|
* reused (and returned) as the result if it is large enough to contain
|
||||||
|
* the data.
|
||||||
|
*/
|
||||||
|
public static <C> NetworkStats subtract(NetworkStats left, NetworkStats right,
|
||||||
|
NonMonotonicObserver<C> observer, C cookie, NetworkStats recycle) {
|
||||||
long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
|
long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
|
||||||
if (deltaRealtime < 0) {
|
if (deltaRealtime < 0) {
|
||||||
if (observer != null) {
|
if (observer != null) {
|
||||||
@@ -519,7 +552,14 @@ public class NetworkStats implements Parcelable {
|
|||||||
|
|
||||||
// result will have our rows, and elapsed time between snapshots
|
// result will have our rows, and elapsed time between snapshots
|
||||||
final Entry entry = new Entry();
|
final Entry entry = new Entry();
|
||||||
final NetworkStats result = new NetworkStats(deltaRealtime, left.size);
|
final NetworkStats result;
|
||||||
|
if (recycle != null && recycle.capacity >= left.size) {
|
||||||
|
result = recycle;
|
||||||
|
result.size = 0;
|
||||||
|
result.elapsedRealtime = deltaRealtime;
|
||||||
|
} else {
|
||||||
|
result = new NetworkStats(deltaRealtime, left.size);
|
||||||
|
}
|
||||||
for (int i = 0; i < left.size; i++) {
|
for (int i = 0; i < left.size; i++) {
|
||||||
entry.iface = left.iface[i];
|
entry.iface = left.iface[i];
|
||||||
entry.uid = left.uid[i];
|
entry.uid = left.uid[i];
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.internal.net;
|
package com.android.internal.net;
|
||||||
|
|
||||||
import static android.net.NetworkStats.SET_ALL;
|
import static android.net.NetworkStats.SET_ALL;
|
||||||
|
import static android.net.NetworkStats.TAG_ALL;
|
||||||
import static android.net.NetworkStats.TAG_NONE;
|
import static android.net.NetworkStats.TAG_NONE;
|
||||||
import static android.net.NetworkStats.UID_ALL;
|
import static android.net.NetworkStats.UID_ALL;
|
||||||
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
|
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
|
||||||
@@ -26,6 +27,7 @@ import android.os.StrictMode;
|
|||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.util.ProcFileReader;
|
import com.android.internal.util.ProcFileReader;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -165,22 +167,32 @@ public class NetworkStatsFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public NetworkStats readNetworkStatsDetail() throws IOException {
|
public NetworkStats readNetworkStatsDetail() throws IOException {
|
||||||
return readNetworkStatsDetail(UID_ALL);
|
return readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkStats readNetworkStatsDetail(int limitUid) throws IOException {
|
public NetworkStats readNetworkStatsDetail(int limitUid, String[] limitIfaces, int limitTag,
|
||||||
|
NetworkStats lastStats)
|
||||||
|
throws IOException {
|
||||||
if (USE_NATIVE_PARSING) {
|
if (USE_NATIVE_PARSING) {
|
||||||
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
|
final NetworkStats stats;
|
||||||
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid) != 0) {
|
if (lastStats != null) {
|
||||||
|
stats = lastStats;
|
||||||
|
stats.setElapsedRealtime(SystemClock.elapsedRealtime());
|
||||||
|
} else {
|
||||||
|
stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
|
||||||
|
}
|
||||||
|
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
|
||||||
|
limitIfaces, limitTag) != 0) {
|
||||||
throw new IOException("Failed to parse network stats");
|
throw new IOException("Failed to parse network stats");
|
||||||
}
|
}
|
||||||
if (SANITY_CHECK_NATIVE) {
|
if (SANITY_CHECK_NATIVE) {
|
||||||
final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
|
final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid,
|
||||||
|
limitIfaces, limitTag);
|
||||||
assertEquals(javaStats, stats);
|
assertEquals(javaStats, stats);
|
||||||
}
|
}
|
||||||
return stats;
|
return stats;
|
||||||
} else {
|
} else {
|
||||||
return javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
|
return javaReadNetworkStatsDetail(mStatsXtUid, limitUid, limitIfaces, limitTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +201,8 @@ public class NetworkStatsFactory {
|
|||||||
* expected to monotonically increase since device boot.
|
* expected to monotonically increase since device boot.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid)
|
public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid,
|
||||||
|
String[] limitIfaces, int limitTag)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
|
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
|
||||||
|
|
||||||
@@ -222,7 +235,9 @@ public class NetworkStatsFactory {
|
|||||||
entry.txBytes = reader.nextLong();
|
entry.txBytes = reader.nextLong();
|
||||||
entry.txPackets = reader.nextLong();
|
entry.txPackets = reader.nextLong();
|
||||||
|
|
||||||
if (limitUid == UID_ALL || limitUid == entry.uid) {
|
if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
|
||||||
|
&& (limitUid == UID_ALL || limitUid == entry.uid)
|
||||||
|
&& (limitTag == TAG_ALL || limitTag == entry.tag)) {
|
||||||
stats.addValues(entry);
|
stats.addValues(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,5 +279,5 @@ public class NetworkStatsFactory {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static native int nativeReadNetworkStatsDetail(
|
public static native int nativeReadNetworkStatsDetail(
|
||||||
NetworkStats stats, String path, int limitUid);
|
NetworkStats stats, String path, int limitUid, String[] limitIfaces, int limitTag);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ static jclass gStringClass;
|
|||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
jfieldID size;
|
jfieldID size;
|
||||||
|
jfieldID capacity;
|
||||||
jfieldID iface;
|
jfieldID iface;
|
||||||
jfieldID uid;
|
jfieldID uid;
|
||||||
jfieldID set;
|
jfieldID set;
|
||||||
@@ -49,7 +50,6 @@ static struct {
|
|||||||
} gNetworkStatsClassInfo;
|
} gNetworkStatsClassInfo;
|
||||||
|
|
||||||
struct stats_line {
|
struct stats_line {
|
||||||
int32_t idx;
|
|
||||||
char iface[32];
|
char iface[32];
|
||||||
int32_t uid;
|
int32_t uid;
|
||||||
int32_t set;
|
int32_t set;
|
||||||
@@ -60,8 +60,41 @@ struct stats_line {
|
|||||||
int64_t txPackets;
|
int64_t txPackets;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static jobjectArray get_string_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
|
||||||
|
{
|
||||||
|
if (!grow) {
|
||||||
|
jobjectArray array = (jobjectArray)env->GetObjectField(obj, field);
|
||||||
|
if (array != NULL) {
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return env->NewObjectArray(size, gStringClass, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jintArray get_int_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
|
||||||
|
{
|
||||||
|
if (!grow) {
|
||||||
|
jintArray array = (jintArray)env->GetObjectField(obj, field);
|
||||||
|
if (array != NULL) {
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return env->NewIntArray(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlongArray get_long_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
|
||||||
|
{
|
||||||
|
if (!grow) {
|
||||||
|
jlongArray array = (jlongArray)env->GetObjectField(obj, field);
|
||||||
|
if (array != NULL) {
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return env->NewLongArray(size);
|
||||||
|
}
|
||||||
|
|
||||||
static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
|
static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
|
||||||
jstring path, jint limitUid) {
|
jstring path, jint limitUid, jobjectArray limitIfacesObj, jint limitTag) {
|
||||||
ScopedUtfChars path8(env, path);
|
ScopedUtfChars path8(env, path);
|
||||||
if (path8.c_str() == NULL) {
|
if (path8.c_str() == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -72,50 +105,146 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<String8> limitIfaces;
|
||||||
|
if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
|
||||||
|
int num = env->GetArrayLength(limitIfacesObj);
|
||||||
|
limitIfaces.setCapacity(num);
|
||||||
|
for (int i=0; i<num; i++) {
|
||||||
|
jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
|
||||||
|
ScopedUtfChars string8(env, string);
|
||||||
|
if (string8.c_str() != NULL) {
|
||||||
|
limitIfaces.add(String8(string8.c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vector<stats_line> lines;
|
Vector<stats_line> lines;
|
||||||
|
|
||||||
int lastIdx = 1;
|
int lastIdx = 1;
|
||||||
|
int idx;
|
||||||
char buffer[384];
|
char buffer[384];
|
||||||
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
|
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
|
||||||
stats_line s;
|
stats_line s;
|
||||||
int64_t rawTag;
|
int64_t rawTag;
|
||||||
if (sscanf(buffer, "%d %31s 0x%llx %u %u %llu %llu %llu %llu", &s.idx,
|
char* pos = buffer;
|
||||||
s.iface, &rawTag, &s.uid, &s.set, &s.rxBytes, &s.rxPackets,
|
char* endPos;
|
||||||
&s.txBytes, &s.txPackets) == 9) {
|
// First field is the index.
|
||||||
if (s.idx != lastIdx + 1) {
|
idx = (int)strtol(pos, &endPos, 10);
|
||||||
ALOGE("inconsistent idx=%d after lastIdx=%d", s.idx, lastIdx);
|
//ALOGI("Index #%d: %s", idx, buffer);
|
||||||
return -1;
|
if (pos == endPos) {
|
||||||
|
// Skip lines that don't start with in index. In particular,
|
||||||
|
// this will skip the initial header line.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (idx != lastIdx + 1) {
|
||||||
|
ALOGE("inconsistent idx=%d after lastIdx=%d: %s", idx, lastIdx, buffer);
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
lastIdx = idx;
|
||||||
|
pos = endPos;
|
||||||
|
// Skip whitespace.
|
||||||
|
while (*pos == ' ') {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
// Next field is iface.
|
||||||
|
int ifaceIdx = 0;
|
||||||
|
while (*pos != ' ' && *pos != 0 && ifaceIdx < (int)(sizeof(s.iface)-1)) {
|
||||||
|
s.iface[ifaceIdx] = *pos;
|
||||||
|
ifaceIdx++;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if (*pos != ' ') {
|
||||||
|
ALOGE("bad iface: %s", buffer);
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
s.iface[ifaceIdx] = 0;
|
||||||
|
if (limitIfaces.size() > 0) {
|
||||||
|
// Is this an iface the caller is interested in?
|
||||||
|
int i = 0;
|
||||||
|
while (i < (int)limitIfaces.size()) {
|
||||||
|
if (limitIfaces[i] == s.iface) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= (int)limitIfaces.size()) {
|
||||||
|
// Nothing matched; skip this line.
|
||||||
|
//ALOGI("skipping due to iface: %s", buffer);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Skip whitespace.
|
||||||
|
while (*pos == ' ') {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
// Next field is tag.
|
||||||
|
rawTag = strtoll(pos, &endPos, 16);
|
||||||
|
//ALOGI("Index #%d: %s", idx, buffer);
|
||||||
|
if (pos == endPos) {
|
||||||
|
ALOGE("bad tag: %s", pos);
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
s.tag = rawTag >> 32;
|
||||||
|
if (limitTag != -1 && s.tag != limitTag) {
|
||||||
|
//ALOGI("skipping due to tag: %s", buffer);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pos = endPos;
|
||||||
|
// Skip whitespace.
|
||||||
|
while (*pos == ' ') {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
// Parse remaining fields.
|
||||||
|
if (sscanf(pos, "%u %u %llu %llu %llu %llu",
|
||||||
|
&s.uid, &s.set, &s.rxBytes, &s.rxPackets,
|
||||||
|
&s.txBytes, &s.txPackets) == 6) {
|
||||||
|
if (limitUid != -1 && limitUid != s.uid) {
|
||||||
|
//ALOGI("skipping due to uid: %s", buffer);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
lastIdx = s.idx;
|
|
||||||
|
|
||||||
s.tag = rawTag >> 32;
|
|
||||||
lines.push_back(s);
|
lines.push_back(s);
|
||||||
|
} else {
|
||||||
|
//ALOGI("skipping due to bad remaining fields: %s", pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fclose(fp) != 0) {
|
if (fclose(fp) != 0) {
|
||||||
|
ALOGE("Failed to close netstats file");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int size = lines.size();
|
int size = lines.size();
|
||||||
|
bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity);
|
||||||
|
|
||||||
ScopedLocalRef<jobjectArray> iface(env, env->NewObjectArray(size, gStringClass, NULL));
|
ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.iface, size, grow));
|
||||||
if (iface.get() == NULL) return -1;
|
if (iface.get() == NULL) return -1;
|
||||||
ScopedIntArrayRW uid(env, env->NewIntArray(size));
|
ScopedIntArrayRW uid(env, get_int_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.uid, size, grow));
|
||||||
if (uid.get() == NULL) return -1;
|
if (uid.get() == NULL) return -1;
|
||||||
ScopedIntArrayRW set(env, env->NewIntArray(size));
|
ScopedIntArrayRW set(env, get_int_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.set, size, grow));
|
||||||
if (set.get() == NULL) return -1;
|
if (set.get() == NULL) return -1;
|
||||||
ScopedIntArrayRW tag(env, env->NewIntArray(size));
|
ScopedIntArrayRW tag(env, get_int_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.tag, size, grow));
|
||||||
if (tag.get() == NULL) return -1;
|
if (tag.get() == NULL) return -1;
|
||||||
ScopedLongArrayRW rxBytes(env, env->NewLongArray(size));
|
ScopedLongArrayRW rxBytes(env, get_long_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.rxBytes, size, grow));
|
||||||
if (rxBytes.get() == NULL) return -1;
|
if (rxBytes.get() == NULL) return -1;
|
||||||
ScopedLongArrayRW rxPackets(env, env->NewLongArray(size));
|
ScopedLongArrayRW rxPackets(env, get_long_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.rxPackets, size, grow));
|
||||||
if (rxPackets.get() == NULL) return -1;
|
if (rxPackets.get() == NULL) return -1;
|
||||||
ScopedLongArrayRW txBytes(env, env->NewLongArray(size));
|
ScopedLongArrayRW txBytes(env, get_long_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.txBytes, size, grow));
|
||||||
if (txBytes.get() == NULL) return -1;
|
if (txBytes.get() == NULL) return -1;
|
||||||
ScopedLongArrayRW txPackets(env, env->NewLongArray(size));
|
ScopedLongArrayRW txPackets(env, get_long_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.txPackets, size, grow));
|
||||||
if (txPackets.get() == NULL) return -1;
|
if (txPackets.get() == NULL) return -1;
|
||||||
ScopedLongArrayRW operations(env, env->NewLongArray(size));
|
ScopedLongArrayRW operations(env, get_long_array(env, stats,
|
||||||
|
gNetworkStatsClassInfo.operations, size, grow));
|
||||||
if (operations.get() == NULL) return -1;
|
if (operations.get() == NULL) return -1;
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
@@ -132,15 +261,18 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
|
|||||||
}
|
}
|
||||||
|
|
||||||
env->SetIntField(stats, gNetworkStatsClassInfo.size, size);
|
env->SetIntField(stats, gNetworkStatsClassInfo.size, size);
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get());
|
if (grow) {
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray());
|
env->SetIntField(stats, gNetworkStatsClassInfo.capacity, size);
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray());
|
env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get());
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
|
env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray());
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
|
env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray());
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
|
env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
|
env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray());
|
env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
|
||||||
env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray());
|
env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
|
||||||
|
env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray());
|
||||||
|
env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray());
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -157,7 +289,7 @@ static jclass findClass(JNIEnv* env, const char* name) {
|
|||||||
|
|
||||||
static JNINativeMethod gMethods[] = {
|
static JNINativeMethod gMethods[] = {
|
||||||
{ "nativeReadNetworkStatsDetail",
|
{ "nativeReadNetworkStatsDetail",
|
||||||
"(Landroid/net/NetworkStats;Ljava/lang/String;I)I",
|
"(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I",
|
||||||
(void*) readNetworkStatsDetail }
|
(void*) readNetworkStatsDetail }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -170,6 +302,7 @@ int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) {
|
|||||||
|
|
||||||
jclass clazz = env->FindClass("android/net/NetworkStats");
|
jclass clazz = env->FindClass("android/net/NetworkStats");
|
||||||
gNetworkStatsClassInfo.size = env->GetFieldID(clazz, "size", "I");
|
gNetworkStatsClassInfo.size = env->GetFieldID(clazz, "size", "I");
|
||||||
|
gNetworkStatsClassInfo.capacity = env->GetFieldID(clazz, "capacity", "I");
|
||||||
gNetworkStatsClassInfo.iface = env->GetFieldID(clazz, "iface", "[Ljava/lang/String;");
|
gNetworkStatsClassInfo.iface = env->GetFieldID(clazz, "iface", "[Ljava/lang/String;");
|
||||||
gNetworkStatsClassInfo.uid = env->GetFieldID(clazz, "uid", "[I");
|
gNetworkStatsClassInfo.uid = env->GetFieldID(clazz, "uid", "[I");
|
||||||
gNetworkStatsClassInfo.set = env->GetFieldID(clazz, "set", "[I");
|
gNetworkStatsClassInfo.set = env->GetFieldID(clazz, "set", "[I");
|
||||||
|
|||||||
Reference in New Issue
Block a user