Merge changes from topic "conn_t_move_stageaosp" into stage-aosp-master

* changes:
  Remove stale aidl-export references
  Move ConnectivityT to packages/modules
This commit is contained in:
Remi NGUYEN VAN
2022-03-25 14:52:26 +00:00
committed by Android (Google) Code Review
87 changed files with 0 additions and 26489 deletions

View File

@@ -271,8 +271,6 @@ java_defaults {
include_dirs: [ include_dirs: [
"frameworks/av/aidl", "frameworks/av/aidl",
"frameworks/native/libs/permission/aidl", "frameworks/native/libs/permission/aidl",
// TODO: remove when moved to the below package
"frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
"packages/modules/Connectivity/framework/aidl-export", "packages/modules/Connectivity/framework/aidl-export",
"packages/modules/Media/apex/aidl/stable", "packages/modules/Media/apex/aidl/stable",
], ],
@@ -519,8 +517,6 @@ stubs_defaults {
include_dirs: [ include_dirs: [
"frameworks/av/aidl", "frameworks/av/aidl",
"frameworks/native/libs/permission/aidl", "frameworks/native/libs/permission/aidl",
// TODO: remove when moved to the below package
"frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
"packages/modules/Connectivity/framework/aidl-export", "packages/modules/Connectivity/framework/aidl-export",
], ],
}, },

View File

@@ -1,5 +0,0 @@
# OWNERS block for code move: b/222234190
reminv@google.com
# file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
# per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS

View File

@@ -1,205 +0,0 @@
//
// Copyright (C) 2021 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 {
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
// NetworkStats related libraries.
filegroup {
name: "framework-connectivity-netstats-internal-sources",
srcs: [
"src/android/app/usage/*.java",
"src/android/net/DataUsageRequest.*",
"src/android/net/INetworkStatsService.aidl",
"src/android/net/INetworkStatsSession.aidl",
"src/android/net/NetworkIdentity.java",
"src/android/net/NetworkIdentitySet.java",
"src/android/net/NetworkStateSnapshot.*",
"src/android/net/NetworkStats.*",
"src/android/net/NetworkStatsAccess.*",
"src/android/net/NetworkStatsCollection.*",
"src/android/net/NetworkStatsHistory.*",
"src/android/net/NetworkTemplate.*",
"src/android/net/TrafficStats.java",
"src/android/net/UnderlyingNetworkInfo.*",
"src/android/net/netstats/**/*.*",
],
path: "src",
visibility: [
"//visibility:private",
],
}
filegroup {
name: "framework-connectivity-netstats-aidl-export-sources",
srcs: [
"aidl-export/android/net/NetworkStats.aidl",
"aidl-export/android/net/NetworkTemplate.aidl",
],
path: "aidl-export",
visibility: [
"//visibility:private",
],
}
filegroup {
name: "framework-connectivity-netstats-sources",
srcs: [
":framework-connectivity-netstats-internal-sources",
":framework-connectivity-netstats-aidl-export-sources",
],
visibility: [
"//visibility:private",
],
}
// Nsd related libraries.
filegroup {
name: "framework-connectivity-nsd-internal-sources",
srcs: [
"src/android/net/nsd/*.aidl",
"src/android/net/nsd/*.java",
],
path: "src",
visibility: [
"//visibility:private",
],
}
filegroup {
name: "framework-connectivity-nsd-aidl-export-sources",
srcs: [
"aidl-export/android/net/nsd/*.aidl",
],
path: "aidl-export",
visibility: [
"//visibility:private",
],
}
filegroup {
name: "framework-connectivity-nsd-sources",
srcs: [
":framework-connectivity-nsd-internal-sources",
":framework-connectivity-nsd-aidl-export-sources",
],
visibility: [
"//visibility:private",
],
}
// IpSec related libraries.
filegroup {
name: "framework-connectivity-ipsec-sources",
srcs: [
"src/android/net/IIpSecService.aidl",
"src/android/net/IpSec*.*",
],
path: "src",
visibility: [
"//visibility:private",
],
}
// Ethernet related libraries.
filegroup {
name: "framework-connectivity-ethernet-sources",
srcs: [
"src/android/net/EthernetManager.java",
"src/android/net/EthernetNetworkManagementException.java",
"src/android/net/EthernetNetworkManagementException.aidl",
"src/android/net/EthernetNetworkSpecifier.java",
"src/android/net/EthernetNetworkUpdateRequest.java",
"src/android/net/EthernetNetworkUpdateRequest.aidl",
"src/android/net/IEthernetManager.aidl",
"src/android/net/IEthernetServiceListener.aidl",
"src/android/net/INetworkInterfaceOutcomeReceiver.aidl",
"src/android/net/ITetheredInterfaceCallback.aidl",
],
path: "src",
visibility: [
"//visibility:private",
],
}
// Connectivity-T common libraries.
filegroup {
name: "framework-connectivity-tiramisu-internal-sources",
srcs: [
"src/android/net/ConnectivityFrameworkInitializerTiramisu.java",
],
path: "src",
visibility: [
"//visibility:private",
],
}
// TODO: remove this empty filegroup.
filegroup {
name: "framework-connectivity-tiramisu-sources",
srcs: [],
visibility: ["//frameworks/base"],
}
filegroup {
name: "framework-connectivity-tiramisu-updatable-sources",
srcs: [
":framework-connectivity-ethernet-sources",
":framework-connectivity-ipsec-sources",
":framework-connectivity-netstats-sources",
":framework-connectivity-nsd-sources",
":framework-connectivity-tiramisu-internal-sources",
],
visibility: [
"//frameworks/base",
"//packages/modules/Connectivity:__subpackages__",
],
}
cc_library_shared {
name: "libframework-connectivity-tiramisu-jni",
min_sdk_version: "30",
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
// Don't warn about S API usage even with
// min_sdk 30: the library is only loaded
// on S+ devices
"-Wno-unguarded-availability",
"-Wthread-safety",
],
srcs: [
"jni/android_net_TrafficStats.cpp",
"jni/onload.cpp",
],
shared_libs: [
"libandroid",
"liblog",
"libnativehelper",
],
stl: "none",
apex_available: [
"com.android.tethering",
],
}

View File

@@ -1,19 +0,0 @@
/**
* 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 android.net;
parcelable NetworkStats;

View File

@@ -1,19 +0,0 @@
/**
* 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 android.net;
parcelable NetworkTemplate;

View File

@@ -1,19 +0,0 @@
/*
* Copyright (C) 2021 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.nsd;
@JavaOnlyStableParcelable parcelable NsdServiceInfo;

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) 2022 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.
*/
#include <android/file_descriptor_jni.h>
#include <android/multinetwork.h>
#include <nativehelper/JNIHelp.h>
namespace android {
static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor, jint tag, jint uid) {
int fd = AFileDescriptor_getFd(env, fileDescriptor);
if (fd == -1) return -EBADF;
return android_tag_socket_with_uid(fd, tag, uid);
}
static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) {
int fd = AFileDescriptor_getFd(env, fileDescriptor);
if (fd == -1) return -EBADF;
return android_untag_socket(fd);
}
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*) tagSocketFd },
{ "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*) untagSocketFd },
};
int register_android_net_TrafficStats(JNIEnv* env) {
return jniRegisterNativeMethods(env, "android/net/TrafficStats", gMethods, NELEM(gMethods));
}
}; // namespace android

View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) 2022 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.
*/
#define LOG_TAG "FrameworkConnectivityJNI"
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
namespace android {
int register_android_net_TrafficStats(JNIEnv* env);
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
return JNI_ERR;
}
if (register_android_net_TrafficStats(env) < 0) return JNI_ERR;
return JNI_VERSION_1_6;
}
}; // namespace android

View File

@@ -1,742 +0,0 @@
/**
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package android.app.usage;
import android.annotation.IntDef;
import android.content.Context;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.RemoteException;
import android.util.Log;
import com.android.net.module.util.CollectionUtils;
import dalvik.system.CloseGuard;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
* Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} objects
* are returned as results to various queries in {@link NetworkStatsManager}.
*/
public final class NetworkStats implements AutoCloseable {
private final static String TAG = "NetworkStats";
private final CloseGuard mCloseGuard = CloseGuard.get();
/**
* Start timestamp of stats collected
*/
private final long mStartTimeStamp;
/**
* End timestamp of stats collected
*/
private final long mEndTimeStamp;
/**
* Non-null array indicates the query enumerates over uids.
*/
private int[] mUids;
/**
* Index of the current uid in mUids when doing uid enumeration or a single uid value,
* depending on query type.
*/
private int mUidOrUidIndex;
/**
* Tag id in case if was specified in the query.
*/
private int mTag = android.net.NetworkStats.TAG_NONE;
/**
* State in case it was not specified in the query.
*/
private int mState = Bucket.STATE_ALL;
/**
* The session while the query requires it, null if all the stats have been collected or close()
* has been called.
*/
private INetworkStatsSession mSession;
private NetworkTemplate mTemplate;
/**
* Results of a summary query.
*/
private android.net.NetworkStats mSummary = null;
/**
* Results of detail queries.
*/
private NetworkStatsHistory mHistory = null;
/**
* Where we are in enumerating over the current result.
*/
private int mEnumerationIndex = 0;
/**
* Recycling entry objects to prevent heap fragmentation.
*/
private android.net.NetworkStats.Entry mRecycledSummaryEntry = null;
private NetworkStatsHistory.Entry mRecycledHistoryEntry = null;
/** @hide */
NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp,
long endTimestamp, INetworkStatsService statsService)
throws RemoteException, SecurityException {
// Open network stats session
mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName());
mCloseGuard.open("close");
mTemplate = template;
mStartTimeStamp = startTimestamp;
mEndTimeStamp = endTimestamp;
}
@Override
protected void finalize() throws Throwable {
try {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
close();
} finally {
super.finalize();
}
}
// -------------------------BEGINNING OF PUBLIC API-----------------------------------
/**
* Buckets are the smallest elements of a query result. As some dimensions of a result may be
* aggregated (e.g. time or state) some values may be equal across all buckets.
*/
public static class Bucket {
/** @hide */
@IntDef(prefix = { "STATE_" }, value = {
STATE_ALL,
STATE_DEFAULT,
STATE_FOREGROUND
})
@Retention(RetentionPolicy.SOURCE)
public @interface State {}
/**
* Combined usage across all states.
*/
public static final int STATE_ALL = -1;
/**
* Usage not accounted for in any other state.
*/
public static final int STATE_DEFAULT = 0x1;
/**
* Foreground usage.
*/
public static final int STATE_FOREGROUND = 0x2;
/**
* Special UID value for aggregate/unspecified.
*/
public static final int UID_ALL = android.net.NetworkStats.UID_ALL;
/**
* Special UID value for removed apps.
*/
public static final int UID_REMOVED = TrafficStats.UID_REMOVED;
/**
* Special UID value for data usage by tethering.
*/
public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
/** @hide */
@IntDef(prefix = { "METERED_" }, value = {
METERED_ALL,
METERED_NO,
METERED_YES
})
@Retention(RetentionPolicy.SOURCE)
public @interface Metered {}
/**
* Combined usage across all metered states. Covers metered and unmetered usage.
*/
public static final int METERED_ALL = -1;
/**
* Usage that occurs on an unmetered network.
*/
public static final int METERED_NO = 0x1;
/**
* Usage that occurs on a metered network.
*
* <p>A network is classified as metered when the user is sensitive to heavy data usage on
* that connection.
*/
public static final int METERED_YES = 0x2;
/** @hide */
@IntDef(prefix = { "ROAMING_" }, value = {
ROAMING_ALL,
ROAMING_NO,
ROAMING_YES
})
@Retention(RetentionPolicy.SOURCE)
public @interface Roaming {}
/**
* Combined usage across all roaming states. Covers both roaming and non-roaming usage.
*/
public static final int ROAMING_ALL = -1;
/**
* Usage that occurs on a home, non-roaming network.
*
* <p>Any cellular usage in this bucket was incurred while the device was connected to a
* tower owned or operated by the user's wireless carrier, or a tower that the user's
* wireless carrier has indicated should be treated as a home network regardless.
*
* <p>This is also the default value for network types that do not support roaming.
*/
public static final int ROAMING_NO = 0x1;
/**
* Usage that occurs on a roaming network.
*
* <p>Any cellular usage in this bucket as incurred while the device was roaming on another
* carrier's network, for which additional charges may apply.
*/
public static final int ROAMING_YES = 0x2;
/** @hide */
@IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
DEFAULT_NETWORK_ALL,
DEFAULT_NETWORK_NO,
DEFAULT_NETWORK_YES
})
@Retention(RetentionPolicy.SOURCE)
public @interface DefaultNetworkStatus {}
/**
* Combined usage for this network regardless of default network status.
*/
public static final int DEFAULT_NETWORK_ALL = -1;
/**
* Usage that occurs while this network is not a default network.
*
* <p>This implies that the app responsible for this usage requested that it occur on a
* specific network different from the one(s) the system would have selected for it.
*/
public static final int DEFAULT_NETWORK_NO = 0x1;
/**
* Usage that occurs while this network is a default network.
*
* <p>This implies that the app either did not select a specific network for this usage,
* or it selected a network that the system could have selected for app traffic.
*/
public static final int DEFAULT_NETWORK_YES = 0x2;
/**
* Special TAG value for total data across all tags
*/
public static final int TAG_NONE = android.net.NetworkStats.TAG_NONE;
private int mUid;
private int mTag;
private int mState;
private int mDefaultNetworkStatus;
private int mMetered;
private int mRoaming;
private long mBeginTimeStamp;
private long mEndTimeStamp;
private long mRxBytes;
private long mRxPackets;
private long mTxBytes;
private long mTxPackets;
private static int convertSet(@State int state) {
switch (state) {
case STATE_ALL: return android.net.NetworkStats.SET_ALL;
case STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT;
case STATE_FOREGROUND: return android.net.NetworkStats.SET_FOREGROUND;
}
return 0;
}
private static @State int convertState(int networkStatsSet) {
switch (networkStatsSet) {
case android.net.NetworkStats.SET_ALL : return STATE_ALL;
case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND;
}
return 0;
}
private static int convertUid(int uid) {
switch (uid) {
case TrafficStats.UID_REMOVED: return UID_REMOVED;
case TrafficStats.UID_TETHERING: return UID_TETHERING;
}
return uid;
}
private static int convertTag(int tag) {
switch (tag) {
case android.net.NetworkStats.TAG_NONE: return TAG_NONE;
}
return tag;
}
private static @Metered int convertMetered(int metered) {
switch (metered) {
case android.net.NetworkStats.METERED_ALL : return METERED_ALL;
case android.net.NetworkStats.METERED_NO: return METERED_NO;
case android.net.NetworkStats.METERED_YES: return METERED_YES;
}
return 0;
}
private static @Roaming int convertRoaming(int roaming) {
switch (roaming) {
case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL;
case android.net.NetworkStats.ROAMING_NO: return ROAMING_NO;
case android.net.NetworkStats.ROAMING_YES: return ROAMING_YES;
}
return 0;
}
private static @DefaultNetworkStatus int convertDefaultNetworkStatus(
int defaultNetworkStatus) {
switch (defaultNetworkStatus) {
case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
}
return 0;
}
public Bucket() {
}
/**
* Key of the bucket. Usually an app uid or one of the following special values:<p />
* <ul>
* <li>{@link #UID_REMOVED}</li>
* <li>{@link #UID_TETHERING}</li>
* <li>{@link android.os.Process#SYSTEM_UID}</li>
* </ul>
* @return Bucket key.
*/
public int getUid() {
return mUid;
}
/**
* Tag of the bucket.<p />
* @return Bucket tag.
*/
public int getTag() {
return mTag;
}
/**
* Usage state. One of the following values:<p/>
* <ul>
* <li>{@link #STATE_ALL}</li>
* <li>{@link #STATE_DEFAULT}</li>
* <li>{@link #STATE_FOREGROUND}</li>
* </ul>
* @return Usage state.
*/
public @State int getState() {
return mState;
}
/**
* Metered state. One of the following values:<p/>
* <ul>
* <li>{@link #METERED_ALL}</li>
* <li>{@link #METERED_NO}</li>
* <li>{@link #METERED_YES}</li>
* </ul>
* <p>A network is classified as metered when the user is sensitive to heavy data usage on
* that connection. Apps may warn before using these networks for large downloads. The
* metered state can be set by the user within data usage network restrictions.
*/
public @Metered int getMetered() {
return mMetered;
}
/**
* Roaming state. One of the following values:<p/>
* <ul>
* <li>{@link #ROAMING_ALL}</li>
* <li>{@link #ROAMING_NO}</li>
* <li>{@link #ROAMING_YES}</li>
* </ul>
*/
public @Roaming int getRoaming() {
return mRoaming;
}
/**
* Default network status. One of the following values:<p/>
* <ul>
* <li>{@link #DEFAULT_NETWORK_ALL}</li>
* <li>{@link #DEFAULT_NETWORK_NO}</li>
* <li>{@link #DEFAULT_NETWORK_YES}</li>
* </ul>
*/
public @DefaultNetworkStatus int getDefaultNetworkStatus() {
return mDefaultNetworkStatus;
}
/**
* Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return Start of interval.
*/
public long getStartTimeStamp() {
return mBeginTimeStamp;
}
/**
* End timestamp of the bucket's time interval. Defined in terms of "Unix time", see
* {@link java.lang.System#currentTimeMillis}.
* @return End of interval.
*/
public long getEndTimeStamp() {
return mEndTimeStamp;
}
/**
* Number of bytes received during the bucket's time interval. Statistics are measured at
* the network layer, so they include both TCP and UDP usage.
* @return Number of bytes.
*/
public long getRxBytes() {
return mRxBytes;
}
/**
* Number of bytes transmitted during the bucket's time interval. Statistics are measured at
* the network layer, so they include both TCP and UDP usage.
* @return Number of bytes.
*/
public long getTxBytes() {
return mTxBytes;
}
/**
* Number of packets received during the bucket's time interval. Statistics are measured at
* the network layer, so they include both TCP and UDP usage.
* @return Number of packets.
*/
public long getRxPackets() {
return mRxPackets;
}
/**
* Number of packets transmitted during the bucket's time interval. Statistics are measured
* at the network layer, so they include both TCP and UDP usage.
* @return Number of packets.
*/
public long getTxPackets() {
return mTxPackets;
}
}
/**
* Fills the recycled bucket with data of the next bin in the enumeration.
* @param bucketOut Bucket to be filled with data.
* @return true if successfully filled the bucket, false otherwise.
*/
public boolean getNextBucket(Bucket bucketOut) {
if (mSummary != null) {
return getNextSummaryBucket(bucketOut);
} else {
return getNextHistoryBucket(bucketOut);
}
}
/**
* Check if it is possible to ask for a next bucket in the enumeration.
* @return true if there is at least one more bucket.
*/
public boolean hasNextBucket() {
if (mSummary != null) {
return mEnumerationIndex < mSummary.size();
} else if (mHistory != null) {
return mEnumerationIndex < mHistory.size()
|| hasNextUid();
}
return false;
}
/**
* Closes the enumeration. Call this method before this object gets out of scope.
*/
@Override
public void close() {
if (mSession != null) {
try {
mSession.close();
} catch (RemoteException e) {
Log.w(TAG, e);
// Otherwise, meh
}
}
mSession = null;
if (mCloseGuard != null) {
mCloseGuard.close();
}
}
// -------------------------END OF PUBLIC API-----------------------------------
/**
* Collects device summary results into a Bucket.
* @throws RemoteException
*/
Bucket getDeviceSummaryForNetwork() throws RemoteException {
mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, mStartTimeStamp, mEndTimeStamp);
// Setting enumeration index beyond end to avoid accidental enumeration over data that does
// not belong to the calling user.
mEnumerationIndex = mSummary.size();
return getSummaryAggregate();
}
/**
* Collects summary results and sets summary enumeration mode.
* @throws RemoteException
*/
void startSummaryEnumeration() throws RemoteException {
mSummary = mSession.getSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp,
false /* includeTags */);
mEnumerationIndex = 0;
}
/**
* Collects tagged summary results and sets summary enumeration mode.
* @throws RemoteException
*/
void startTaggedSummaryEnumeration() throws RemoteException {
mSummary = mSession.getTaggedSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp);
mEnumerationIndex = 0;
}
/**
* Collects history results for uid and resets history enumeration index.
*/
void startHistoryUidEnumeration(int uid, int tag, int state) {
mHistory = null;
try {
mHistory = mSession.getHistoryIntervalForUid(mTemplate, uid,
Bucket.convertSet(state), tag, NetworkStatsHistory.FIELD_ALL,
mStartTimeStamp, mEndTimeStamp);
setSingleUidTagState(uid, tag, state);
} catch (RemoteException e) {
Log.w(TAG, e);
// Leaving mHistory null
}
mEnumerationIndex = 0;
}
/**
* Collects history results for network and resets history enumeration index.
*/
void startHistoryDeviceEnumeration() {
try {
mHistory = mSession.getHistoryIntervalForNetwork(
mTemplate, NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
} catch (RemoteException e) {
Log.w(TAG, e);
mHistory = null;
}
mEnumerationIndex = 0;
}
/**
* Starts uid enumeration for current user.
* @throws RemoteException
*/
void startUserUidEnumeration() throws RemoteException {
// TODO: getRelevantUids should be sensitive to time interval. When that's done,
// the filtering logic below can be removed.
int[] uids = mSession.getRelevantUids();
// Filtering of uids with empty history.
final ArrayList<Integer> filteredUids = new ArrayList<>();
for (int uid : uids) {
try {
NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid,
android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
if (history != null && history.size() > 0) {
filteredUids.add(uid);
}
} catch (RemoteException e) {
Log.w(TAG, "Error while getting history of uid " + uid, e);
}
}
mUids = CollectionUtils.toIntArray(filteredUids);
mUidOrUidIndex = -1;
stepHistory();
}
/**
* Steps to next uid in enumeration and collects history for that.
*/
private void stepHistory(){
if (hasNextUid()) {
stepUid();
mHistory = null;
try {
mHistory = mSession.getHistoryIntervalForUid(mTemplate, getUid(),
android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE,
NetworkStatsHistory.FIELD_ALL, mStartTimeStamp, mEndTimeStamp);
} catch (RemoteException e) {
Log.w(TAG, e);
// Leaving mHistory null
}
mEnumerationIndex = 0;
}
}
private void fillBucketFromSummaryEntry(Bucket bucketOut) {
bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
bucketOut.mDefaultNetworkStatus = Bucket.convertDefaultNetworkStatus(
mRecycledSummaryEntry.defaultNetwork);
bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
bucketOut.mBeginTimeStamp = mStartTimeStamp;
bucketOut.mEndTimeStamp = mEndTimeStamp;
bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets;
bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes;
bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets;
}
/**
* Getting the next item in summary enumeration.
* @param bucketOut Next item will be set here.
* @return true if a next item could be set.
*/
private boolean getNextSummaryBucket(Bucket bucketOut) {
if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
fillBucketFromSummaryEntry(bucketOut);
return true;
}
return false;
}
Bucket getSummaryAggregate() {
if (mSummary == null) {
return null;
}
Bucket bucket = new Bucket();
if (mRecycledSummaryEntry == null) {
mRecycledSummaryEntry = new android.net.NetworkStats.Entry();
}
mSummary.getTotal(mRecycledSummaryEntry);
fillBucketFromSummaryEntry(bucket);
return bucket;
}
/**
* Getting the next item in a history enumeration.
* @param bucketOut Next item will be set here.
* @return true if a next item could be set.
*/
private boolean getNextHistoryBucket(Bucket bucketOut) {
if (bucketOut != null && mHistory != null) {
if (mEnumerationIndex < mHistory.size()) {
mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
mRecycledHistoryEntry);
bucketOut.mUid = Bucket.convertUid(getUid());
bucketOut.mTag = Bucket.convertTag(mTag);
bucketOut.mState = mState;
bucketOut.mDefaultNetworkStatus = Bucket.DEFAULT_NETWORK_ALL;
bucketOut.mMetered = Bucket.METERED_ALL;
bucketOut.mRoaming = Bucket.ROAMING_ALL;
bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
mRecycledHistoryEntry.bucketDuration;
bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes;
bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets;
bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes;
bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets;
return true;
} else if (hasNextUid()) {
stepHistory();
return getNextHistoryBucket(bucketOut);
}
}
return false;
}
// ------------------ UID LOGIC------------------------
private boolean isUidEnumeration() {
return mUids != null;
}
private boolean hasNextUid() {
return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
}
private int getUid() {
// Check if uid enumeration.
if (isUidEnumeration()) {
if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) {
throw new IndexOutOfBoundsException(
"Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length);
}
return mUids[mUidOrUidIndex];
}
// Single uid mode.
return mUidOrUidIndex;
}
private void setSingleUidTagState(int uid, int tag, int state) {
mUidOrUidIndex = uid;
mTag = tag;
mState = state;
}
private void stepUid() {
if (mUids != null) {
++mUidOrUidIndex;
}
}
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright (C) 2021 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;
import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
/**
* Class for performing registration for Connectivity services which are exposed via updatable APIs
* since Android T.
*
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class ConnectivityFrameworkInitializerTiramisu {
private ConnectivityFrameworkInitializerTiramisu() {}
/**
* Called by {@link SystemServiceRegistry}'s static initializer and registers NetworkStats, nsd,
* ipsec and ethernet services to {@link Context}, so that {@link Context#getSystemService} can
* return them.
*
* @throws IllegalStateException if this is called anywhere besides
* {@link SystemServiceRegistry}.
*/
public static void registerServiceWrappers() {
SystemServiceRegistry.registerContextAwareService(
Context.NSD_SERVICE,
NsdManager.class,
(context, serviceBinder) -> {
INsdManager service = INsdManager.Stub.asInterface(serviceBinder);
return new NsdManager(context, service);
}
);
SystemServiceRegistry.registerContextAwareService(
Context.IPSEC_SERVICE,
IpSecManager.class,
(context, serviceBinder) -> {
IIpSecService service = IIpSecService.Stub.asInterface(serviceBinder);
return new IpSecManager(context, service);
}
);
SystemServiceRegistry.registerContextAwareService(
Context.NETWORK_STATS_SERVICE,
NetworkStatsManager.class,
(context, serviceBinder) -> {
INetworkStatsService service =
INetworkStatsService.Stub.asInterface(serviceBinder);
return new NetworkStatsManager(context, service);
}
);
SystemServiceRegistry.registerContextAwareService(
Context.ETHERNET_SERVICE,
EthernetManager.class,
(context, serviceBinder) -> {
IEthernetManager service = IEthernetManager.Stub.asInterface(serviceBinder);
return new EthernetManager(context, service);
}
);
}
}

View File

@@ -1,19 +0,0 @@
/**
* Copyright (c) 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
parcelable DataUsageRequest;

View File

@@ -1,112 +0,0 @@
/**
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package android.net;
import android.annotation.Nullable;
import android.net.NetworkTemplate;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* Defines a request to register a callbacks. Used to be notified on data usage via
* {@link android.app.usage.NetworkStatsManager#registerDataUsageCallback}.
* If no {@code uid}s are set, callbacks are restricted to device-owners,
* carrier-privileged apps, or system apps.
*
* @hide
*/
public final class DataUsageRequest implements Parcelable {
public static final String PARCELABLE_KEY = "DataUsageRequest";
public static final int REQUEST_ID_UNSET = 0;
/**
* Identifies the request. {@link DataUsageRequest}s should only be constructed by
* the Framework and it is used internally to identify the request.
*/
public final int requestId;
/**
* {@link NetworkTemplate} describing the network to monitor.
*/
public final NetworkTemplate template;
/**
* Threshold in bytes to be notified on.
*/
public final long thresholdInBytes;
public DataUsageRequest(int requestId, NetworkTemplate template, long thresholdInBytes) {
this.requestId = requestId;
this.template = template;
this.thresholdInBytes = thresholdInBytes;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(requestId);
dest.writeParcelable(template, flags);
dest.writeLong(thresholdInBytes);
}
public static final @android.annotation.NonNull Creator<DataUsageRequest> CREATOR =
new Creator<DataUsageRequest>() {
@Override
public DataUsageRequest createFromParcel(Parcel in) {
int requestId = in.readInt();
NetworkTemplate template = in.readParcelable(null);
long thresholdInBytes = in.readLong();
DataUsageRequest result = new DataUsageRequest(requestId, template,
thresholdInBytes);
return result;
}
@Override
public DataUsageRequest[] newArray(int size) {
return new DataUsageRequest[size];
}
};
@Override
public String toString() {
return "DataUsageRequest [ requestId=" + requestId
+ ", networkTemplate=" + template
+ ", thresholdInBytes=" + thresholdInBytes + " ]";
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof DataUsageRequest == false) return false;
DataUsageRequest that = (DataUsageRequest) obj;
return that.requestId == this.requestId
&& Objects.equals(that.template, this.template)
&& that.thresholdInBytes == this.thresholdInBytes;
}
@Override
public int hashCode() {
return Objects.hash(requestId, template, thresholdInBytes);
}
}

View File

@@ -1,729 +0,0 @@
/*
* Copyright (C) 2014 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;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
import com.android.internal.annotations.GuardedBy;
import com.android.modules.utils.BackgroundThread;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.IntConsumer;
/**
* A class that manages and configures Ethernet interfaces.
*
* @hide
*/
@SystemApi
@SystemService(Context.ETHERNET_SERVICE)
public class EthernetManager {
private static final String TAG = "EthernetManager";
private final IEthernetManager mService;
@GuardedBy("mListenerLock")
private final ArrayList<ListenerInfo<InterfaceStateListener>> mIfaceListeners =
new ArrayList<>();
@GuardedBy("mListenerLock")
private final ArrayList<ListenerInfo<IntConsumer>> mEthernetStateListeners =
new ArrayList<>();
final Object mListenerLock = new Object();
private final IEthernetServiceListener.Stub mServiceListener =
new IEthernetServiceListener.Stub() {
@Override
public void onEthernetStateChanged(int state) {
synchronized (mListenerLock) {
for (ListenerInfo<IntConsumer> li : mEthernetStateListeners) {
li.executor.execute(() -> {
li.listener.accept(state);
});
}
}
}
@Override
public void onInterfaceStateChanged(String iface, int state, int role,
IpConfiguration configuration) {
synchronized (mListenerLock) {
for (ListenerInfo<InterfaceStateListener> li : mIfaceListeners) {
li.executor.execute(() ->
li.listener.onInterfaceStateChanged(iface, state, role,
configuration));
}
}
}
};
/**
* Indicates that Ethernet is disabled.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int ETHERNET_STATE_DISABLED = 0;
/**
* Indicates that Ethernet is enabled.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int ETHERNET_STATE_ENABLED = 1;
private static class ListenerInfo<T> {
@NonNull
public final Executor executor;
@NonNull
public final T listener;
private ListenerInfo(@NonNull Executor executor, @NonNull T listener) {
this.executor = executor;
this.listener = listener;
}
}
/**
* The interface is absent.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int STATE_ABSENT = 0;
/**
* The interface is present but link is down.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int STATE_LINK_DOWN = 1;
/**
* The interface is present and link is up.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int STATE_LINK_UP = 2;
/** @hide */
@IntDef(prefix = "STATE_", value = {STATE_ABSENT, STATE_LINK_DOWN, STATE_LINK_UP})
@Retention(RetentionPolicy.SOURCE)
public @interface InterfaceState {}
/**
* The interface currently does not have any specific role.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int ROLE_NONE = 0;
/**
* The interface is in client mode (e.g., connected to the Internet).
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int ROLE_CLIENT = 1;
/**
* Ethernet interface is in server mode (e.g., providing Internet access to tethered devices).
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int ROLE_SERVER = 2;
/** @hide */
@IntDef(prefix = "ROLE_", value = {ROLE_NONE, ROLE_CLIENT, ROLE_SERVER})
@Retention(RetentionPolicy.SOURCE)
public @interface Role {}
/**
* A listener that receives notifications about the state of Ethernet interfaces on the system.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public interface InterfaceStateListener {
/**
* Called when an Ethernet interface changes state.
*
* @param iface the name of the interface.
* @param state the current state of the interface, or {@link #STATE_ABSENT} if the
* interface was removed.
* @param role whether the interface is in client mode or server mode.
* @param configuration the current IP configuration of the interface.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
void onInterfaceStateChanged(@NonNull String iface, @InterfaceState int state,
@Role int role, @Nullable IpConfiguration configuration);
}
/**
* A listener interface to receive notification on changes in Ethernet.
* This has never been a supported API. Use {@link InterfaceStateListener} instead.
* @hide
*/
public interface Listener extends InterfaceStateListener {
/**
* Called when Ethernet port's availability is changed.
* @param iface Ethernet interface name
* @param isAvailable {@code true} if Ethernet port exists.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void onAvailabilityChanged(String iface, boolean isAvailable);
/** Default implementation for backwards compatibility. Only calls the legacy listener. */
default void onInterfaceStateChanged(@NonNull String iface, @InterfaceState int state,
@Role int role, @Nullable IpConfiguration configuration) {
onAvailabilityChanged(iface, (state >= STATE_LINK_UP));
}
}
/**
* Create a new EthernetManager instance.
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#ETHERNET_SERVICE Context.ETHERNET_SERVICE}.
* @hide
*/
public EthernetManager(Context context, IEthernetManager service) {
mService = service;
}
/**
* Get Ethernet configuration.
* @return the Ethernet Configuration, contained in {@link IpConfiguration}.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public IpConfiguration getConfiguration(String iface) {
try {
return mService.getConfiguration(iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Set Ethernet configuration.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setConfiguration(@NonNull String iface, @NonNull IpConfiguration config) {
try {
mService.setConfiguration(iface, config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Indicates whether the system currently has one or more Ethernet interfaces.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isAvailable() {
return getAvailableInterfaces().length > 0;
}
/**
* Indicates whether the system has given interface.
*
* @param iface Ethernet interface name
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isAvailable(String iface) {
try {
return mService.isAvailable(iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Adds a listener.
* This has never been a supported API. Use {@link #addInterfaceStateListener} instead.
*
* @param listener A {@link Listener} to add.
* @throws IllegalArgumentException If the listener is null.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void addListener(@NonNull Listener listener) {
addListener(listener, BackgroundThread.getExecutor());
}
/**
* Adds a listener.
* This has never been a supported API. Use {@link #addInterfaceStateListener} instead.
*
* @param listener A {@link Listener} to add.
* @param executor Executor to run callbacks on.
* @throws IllegalArgumentException If the listener or executor is null.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void addListener(@NonNull Listener listener, @NonNull Executor executor) {
addInterfaceStateListener(executor, listener);
}
/**
* Listen to changes in the state of Ethernet interfaces.
*
* Adds a listener to receive notification for any state change of all existing Ethernet
* interfaces.
* <p>{@link Listener#onInterfaceStateChanged} will be triggered immediately for all
* existing interfaces upon adding a listener. The same method will be called on the
* listener every time any of the interface changes state. In particular, if an
* interface is removed, it will be called with state {@link #STATE_ABSENT}.
* <p>Use {@link #removeInterfaceStateListener} with the same object to stop listening.
*
* @param executor Executor to run callbacks on.
* @param listener A {@link Listener} to add.
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@SystemApi(client = MODULE_LIBRARIES)
public void addInterfaceStateListener(@NonNull Executor executor,
@NonNull InterfaceStateListener listener) {
if (listener == null || executor == null) {
throw new NullPointerException("listener and executor must not be null");
}
synchronized (mListenerLock) {
maybeAddServiceListener();
mIfaceListeners.add(new ListenerInfo<InterfaceStateListener>(executor, listener));
}
}
@GuardedBy("mListenerLock")
private void maybeAddServiceListener() {
if (!mIfaceListeners.isEmpty() || !mEthernetStateListeners.isEmpty()) return;
try {
mService.addListener(mServiceListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns an array of available Ethernet interface names.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String[] getAvailableInterfaces() {
try {
return mService.getAvailableInterfaces();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/**
* Removes a listener.
*
* @param listener A {@link Listener} to remove.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public void removeInterfaceStateListener(@NonNull InterfaceStateListener listener) {
Objects.requireNonNull(listener);
synchronized (mListenerLock) {
mIfaceListeners.removeIf(l -> l.listener == listener);
maybeRemoveServiceListener();
}
}
@GuardedBy("mListenerLock")
private void maybeRemoveServiceListener() {
if (!mIfaceListeners.isEmpty() || !mEthernetStateListeners.isEmpty()) return;
try {
mService.removeListener(mServiceListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes a listener.
* This has never been a supported API. Use {@link #removeInterfaceStateListener} instead.
* @param listener A {@link Listener} to remove.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void removeListener(@NonNull Listener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
removeInterfaceStateListener(listener);
}
/**
* Whether to treat interfaces created by {@link TestNetworkManager#createTapInterface}
* as Ethernet interfaces. The effects of this method apply to any test interfaces that are
* already present on the system.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public void setIncludeTestInterfaces(boolean include) {
try {
mService.setIncludeTestInterfaces(include);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* A request for a tethered interface.
*/
public static class TetheredInterfaceRequest {
private final IEthernetManager mService;
private final ITetheredInterfaceCallback mCb;
private TetheredInterfaceRequest(@NonNull IEthernetManager service,
@NonNull ITetheredInterfaceCallback cb) {
this.mService = service;
this.mCb = cb;
}
/**
* Release the request, causing the interface to revert back from tethering mode if there
* is no other requestor.
*/
public void release() {
try {
mService.releaseTetheredInterface(mCb);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
}
/**
* Callback for {@link #requestTetheredInterface(TetheredInterfaceCallback)}.
*/
public interface TetheredInterfaceCallback {
/**
* Called when the tethered interface is available.
* @param iface The name of the interface.
*/
void onAvailable(@NonNull String iface);
/**
* Called when the tethered interface is now unavailable.
*/
void onUnavailable();
}
/**
* Request a tethered interface in tethering mode.
*
* <p>When this method is called and there is at least one ethernet interface available, the
* system will designate one to act as a tethered interface. If there is already a tethered
* interface, the existing interface will be used.
* @param callback A callback to be called once the request has been fulfilled.
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_STACK,
android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
})
@NonNull
public TetheredInterfaceRequest requestTetheredInterface(@NonNull final Executor executor,
@NonNull final TetheredInterfaceCallback callback) {
Objects.requireNonNull(callback, "Callback must be non-null");
Objects.requireNonNull(executor, "Executor must be non-null");
final ITetheredInterfaceCallback cbInternal = new ITetheredInterfaceCallback.Stub() {
@Override
public void onAvailable(String iface) {
executor.execute(() -> callback.onAvailable(iface));
}
@Override
public void onUnavailable() {
executor.execute(() -> callback.onUnavailable());
}
};
try {
mService.requestTetheredInterface(cbInternal);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return new TetheredInterfaceRequest(mService, cbInternal);
}
private static final class NetworkInterfaceOutcomeReceiver
extends INetworkInterfaceOutcomeReceiver.Stub {
@NonNull
private final Executor mExecutor;
@NonNull
private final OutcomeReceiver<String, EthernetNetworkManagementException> mCallback;
NetworkInterfaceOutcomeReceiver(
@NonNull final Executor executor,
@NonNull final OutcomeReceiver<String, EthernetNetworkManagementException>
callback) {
Objects.requireNonNull(executor, "Pass a non-null executor");
Objects.requireNonNull(callback, "Pass a non-null callback");
mExecutor = executor;
mCallback = callback;
}
@Override
public void onResult(@NonNull String iface) {
mExecutor.execute(() -> mCallback.onResult(iface));
}
@Override
public void onError(@NonNull EthernetNetworkManagementException e) {
mExecutor.execute(() -> mCallback.onError(e));
}
}
private NetworkInterfaceOutcomeReceiver makeNetworkInterfaceOutcomeReceiver(
@Nullable final Executor executor,
@Nullable final OutcomeReceiver<String, EthernetNetworkManagementException> callback) {
if (null != callback) {
Objects.requireNonNull(executor, "Pass a non-null executor, or a null callback");
}
final NetworkInterfaceOutcomeReceiver proxy;
if (null == callback) {
proxy = null;
} else {
proxy = new NetworkInterfaceOutcomeReceiver(executor, callback);
}
return proxy;
}
/**
* Updates the configuration of an automotive device's ethernet network.
*
* The {@link EthernetNetworkUpdateRequest} {@code request} argument describes how to update the
* configuration for this network.
* Use {@link StaticIpConfiguration.Builder} to build a {@code StaticIpConfiguration} object for
* this network to put inside the {@code request}.
* Similarly, use {@link NetworkCapabilities.Builder} to build a {@code NetworkCapabilities}
* object for this network to put inside the {@code request}.
*
* This function accepts an {@link OutcomeReceiver} that is called once the operation has
* finished execution.
*
* @param iface the name of the interface to act upon.
* @param request the {@link EthernetNetworkUpdateRequest} used to set an ethernet network's
* {@link StaticIpConfiguration} and {@link NetworkCapabilities} values.
* @param executor an {@link Executor} to execute the callback on. Optional if callback is null.
* @param callback an optional {@link OutcomeReceiver} to listen for completion of the
* operation. On success, {@link OutcomeReceiver#onResult} is called with the
* interface name. On error, {@link OutcomeReceiver#onError} is called with more
* information about the error.
* @throws SecurityException if the process doesn't hold
* {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
* @throws UnsupportedOperationException if called on a non-automotive device or on an
* unsupported interface.
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK,
android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
public void updateConfiguration(
@NonNull String iface,
@NonNull EthernetNetworkUpdateRequest request,
@Nullable @CallbackExecutor Executor executor,
@Nullable OutcomeReceiver<String, EthernetNetworkManagementException> callback) {
Objects.requireNonNull(iface, "iface must be non-null");
Objects.requireNonNull(request, "request must be non-null");
final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver(
executor, callback);
try {
mService.updateConfiguration(iface, request, proxy);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Enable a network interface.
*
* Enables a previously disabled network interface.
* This function accepts an {@link OutcomeReceiver} that is called once the operation has
* finished execution.
*
* @param iface the name of the interface to enable.
* @param executor an {@link Executor} to execute the callback on. Optional if callback is null.
* @param callback an optional {@link OutcomeReceiver} to listen for completion of the
* operation. On success, {@link OutcomeReceiver#onResult} is called with the
* interface name. On error, {@link OutcomeReceiver#onError} is called with more
* information about the error.
* @throws SecurityException if the process doesn't hold
* {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK,
android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
@RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
public void enableInterface(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
@Nullable OutcomeReceiver<String, EthernetNetworkManagementException> callback) {
Objects.requireNonNull(iface, "iface must be non-null");
final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver(
executor, callback);
try {
mService.connectNetwork(iface, proxy);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Disable a network interface.
*
* Disables the use of a network interface to fulfill network requests. If the interface
* currently serves a request, the network will be torn down.
* This function accepts an {@link OutcomeReceiver} that is called once the operation has
* finished execution.
*
* @param iface the name of the interface to disable.
* @param executor an {@link Executor} to execute the callback on. Optional if callback is null.
* @param callback an optional {@link OutcomeReceiver} to listen for completion of the
* operation. On success, {@link OutcomeReceiver#onResult} is called with the
* interface name. On error, {@link OutcomeReceiver#onError} is called with more
* information about the error.
* @throws SecurityException if the process doesn't hold
* {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK,
android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
@RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
public void disableInterface(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
@Nullable OutcomeReceiver<String, EthernetNetworkManagementException> callback) {
Objects.requireNonNull(iface, "iface must be non-null");
final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver(
executor, callback);
try {
mService.disconnectNetwork(iface, proxy);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Change ethernet setting.
*
* @param enabled enable or disable ethernet settings.
*
* @hide
*/
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK,
android.Manifest.permission.NETWORK_SETTINGS})
@SystemApi(client = MODULE_LIBRARIES)
public void setEthernetEnabled(boolean enabled) {
try {
mService.setEthernetEnabled(enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Listen to changes in the state of ethernet.
*
* @param executor to run callbacks on.
* @param listener to listen ethernet state changed.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@SystemApi(client = MODULE_LIBRARIES)
public void addEthernetStateListener(@NonNull Executor executor,
@NonNull IntConsumer listener) {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
synchronized (mListenerLock) {
maybeAddServiceListener();
mEthernetStateListeners.add(new ListenerInfo<IntConsumer>(executor, listener));
}
}
/**
* Removes a listener.
*
* @param listener to listen ethernet state changed.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@SystemApi(client = MODULE_LIBRARIES)
public void removeEthernetStateListener(@NonNull IntConsumer listener) {
Objects.requireNonNull(listener);
synchronized (mListenerLock) {
mEthernetStateListeners.removeIf(l -> l.listener == listener);
maybeRemoveServiceListener();
}
}
/**
* Returns an array of existing Ethernet interface names regardless whether the interface
* is available or not currently.
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@SystemApi(client = MODULE_LIBRARIES)
@NonNull
public List<String> getInterfaceList() {
try {
return mService.getInterfaceList();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
}

View File

@@ -1,19 +0,0 @@
/*
* Copyright (C) 2021 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;
parcelable EthernetNetworkManagementException;

View File

@@ -1,73 +0,0 @@
/*
* Copyright (C) 2021 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;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/** @hide */
@SystemApi
public final class EthernetNetworkManagementException
extends RuntimeException implements Parcelable {
/* @hide */
public EthernetNetworkManagementException(@NonNull final String errorMessage) {
super(errorMessage);
}
@Override
public int hashCode() {
return Objects.hash(getMessage());
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
final EthernetNetworkManagementException that = (EthernetNetworkManagementException) obj;
return Objects.equals(getMessage(), that.getMessage());
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(getMessage());
}
@Override
public int describeContents() {
return 0;
}
@NonNull
public static final Parcelable.Creator<EthernetNetworkManagementException> CREATOR =
new Parcelable.Creator<EthernetNetworkManagementException>() {
@Override
public EthernetNetworkManagementException[] newArray(int size) {
return new EthernetNetworkManagementException[size];
}
@Override
public EthernetNetworkManagementException createFromParcel(@NonNull Parcel source) {
return new EthernetNetworkManagementException(source.readString());
}
};
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright (C) 2021 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;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.util.Objects;
/**
* A {@link NetworkSpecifier} used to identify ethernet interfaces.
*
* @see EthernetManager
*/
public final class EthernetNetworkSpecifier extends NetworkSpecifier implements Parcelable {
/**
* Name of the network interface.
*/
@NonNull
private final String mInterfaceName;
/**
* Create a new EthernetNetworkSpecifier.
* @param interfaceName Name of the ethernet interface the specifier refers to.
*/
public EthernetNetworkSpecifier(@NonNull String interfaceName) {
if (TextUtils.isEmpty(interfaceName)) {
throw new IllegalArgumentException();
}
mInterfaceName = interfaceName;
}
/**
* Get the name of the ethernet interface the specifier refers to.
*/
@Nullable
public String getInterfaceName() {
// This may be null in the future to support specifiers based on data other than the
// interface name.
return mInterfaceName;
}
/** @hide */
@Override
public boolean canBeSatisfiedBy(@Nullable NetworkSpecifier other) {
return equals(other);
}
@Override
public boolean equals(@Nullable Object o) {
if (!(o instanceof EthernetNetworkSpecifier)) return false;
return TextUtils.equals(mInterfaceName, ((EthernetNetworkSpecifier) o).mInterfaceName);
}
@Override
public int hashCode() {
return Objects.hashCode(mInterfaceName);
}
@Override
public String toString() {
return "EthernetNetworkSpecifier (" + mInterfaceName + ")";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mInterfaceName);
}
public static final @NonNull Parcelable.Creator<EthernetNetworkSpecifier> CREATOR =
new Parcelable.Creator<EthernetNetworkSpecifier>() {
public EthernetNetworkSpecifier createFromParcel(Parcel in) {
return new EthernetNetworkSpecifier(in.readString());
}
public EthernetNetworkSpecifier[] newArray(int size) {
return new EthernetNetworkSpecifier[size];
}
};
}

View File

@@ -1,19 +0,0 @@
/*
* Copyright (C) 2021 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;
parcelable EthernetNetworkUpdateRequest;

View File

@@ -1,185 +0,0 @@
/*
* Copyright (C) 2021 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;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* Represents a request to update an existing Ethernet interface.
*
* @see EthernetManager#updateConfiguration
*
* @hide
*/
@SystemApi
public final class EthernetNetworkUpdateRequest implements Parcelable {
@Nullable
private final IpConfiguration mIpConfig;
@Nullable
private final NetworkCapabilities mNetworkCapabilities;
/**
* Setting the {@link IpConfiguration} is optional in {@link EthernetNetworkUpdateRequest}.
* When set to null, the existing IpConfiguration is not updated.
*
* @return the new {@link IpConfiguration} or null.
*/
@Nullable
public IpConfiguration getIpConfiguration() {
return mIpConfig == null ? null : new IpConfiguration(mIpConfig);
}
/**
* Setting the {@link NetworkCapabilities} is optional in {@link EthernetNetworkUpdateRequest}.
* When set to null, the existing NetworkCapabilities are not updated.
*
* @return the new {@link NetworkCapabilities} or null.
*/
@Nullable
public NetworkCapabilities getNetworkCapabilities() {
return mNetworkCapabilities == null ? null : new NetworkCapabilities(mNetworkCapabilities);
}
private EthernetNetworkUpdateRequest(@Nullable final IpConfiguration ipConfig,
@Nullable final NetworkCapabilities networkCapabilities) {
mIpConfig = ipConfig;
mNetworkCapabilities = networkCapabilities;
}
private EthernetNetworkUpdateRequest(@NonNull final Parcel source) {
Objects.requireNonNull(source);
mIpConfig = source.readParcelable(IpConfiguration.class.getClassLoader(),
IpConfiguration.class);
mNetworkCapabilities = source.readParcelable(NetworkCapabilities.class.getClassLoader(),
NetworkCapabilities.class);
}
/**
* Builder used to create {@link EthernetNetworkUpdateRequest} objects.
*/
public static final class Builder {
@Nullable
private IpConfiguration mBuilderIpConfig;
@Nullable
private NetworkCapabilities mBuilderNetworkCapabilities;
public Builder(){}
/**
* Constructor to populate the builder's values with an already built
* {@link EthernetNetworkUpdateRequest}.
* @param request the {@link EthernetNetworkUpdateRequest} to populate with.
*/
public Builder(@NonNull final EthernetNetworkUpdateRequest request) {
Objects.requireNonNull(request);
mBuilderIpConfig = null == request.mIpConfig
? null : new IpConfiguration(request.mIpConfig);
mBuilderNetworkCapabilities = null == request.mNetworkCapabilities
? null : new NetworkCapabilities(request.mNetworkCapabilities);
}
/**
* Set the {@link IpConfiguration} to be used with the {@code Builder}.
* @param ipConfig the {@link IpConfiguration} to set.
* @return The builder to facilitate chaining.
*/
@NonNull
public Builder setIpConfiguration(@Nullable final IpConfiguration ipConfig) {
mBuilderIpConfig = ipConfig == null ? null : new IpConfiguration(ipConfig);
return this;
}
/**
* Set the {@link NetworkCapabilities} to be used with the {@code Builder}.
* @param nc the {@link NetworkCapabilities} to set.
* @return The builder to facilitate chaining.
*/
@NonNull
public Builder setNetworkCapabilities(@Nullable final NetworkCapabilities nc) {
mBuilderNetworkCapabilities = nc == null ? null : new NetworkCapabilities(nc);
return this;
}
/**
* Build {@link EthernetNetworkUpdateRequest} return the current update request.
*
* @throws IllegalStateException when both mBuilderNetworkCapabilities and mBuilderIpConfig
* are null.
*/
@NonNull
public EthernetNetworkUpdateRequest build() {
if (mBuilderIpConfig == null && mBuilderNetworkCapabilities == null) {
throw new IllegalStateException(
"Cannot construct an empty EthernetNetworkUpdateRequest");
}
return new EthernetNetworkUpdateRequest(mBuilderIpConfig, mBuilderNetworkCapabilities);
}
}
@Override
public String toString() {
return "EthernetNetworkUpdateRequest{"
+ "mIpConfig=" + mIpConfig
+ ", mNetworkCapabilities=" + mNetworkCapabilities + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o;
return Objects.equals(that.getIpConfiguration(), mIpConfig)
&& Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
}
@Override
public int hashCode() {
return Objects.hash(mIpConfig, mNetworkCapabilities);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mIpConfig, flags);
dest.writeParcelable(mNetworkCapabilities, flags);
}
@Override
public int describeContents() {
return 0;
}
@NonNull
public static final Parcelable.Creator<EthernetNetworkUpdateRequest> CREATOR =
new Parcelable.Creator<EthernetNetworkUpdateRequest>() {
@Override
public EthernetNetworkUpdateRequest[] newArray(int size) {
return new EthernetNetworkUpdateRequest[size];
}
@Override
public EthernetNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
return new EthernetNetworkUpdateRequest(source);
}
};
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright (C) 2014 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;
import android.net.IpConfiguration;
import android.net.IEthernetServiceListener;
import android.net.EthernetNetworkManagementException;
import android.net.EthernetNetworkUpdateRequest;
import android.net.INetworkInterfaceOutcomeReceiver;
import android.net.ITetheredInterfaceCallback;
import java.util.List;
/**
* Interface that answers queries about, and allows changing
* ethernet configuration.
*/
/** {@hide} */
interface IEthernetManager
{
String[] getAvailableInterfaces();
IpConfiguration getConfiguration(String iface);
void setConfiguration(String iface, in IpConfiguration config);
boolean isAvailable(String iface);
void addListener(in IEthernetServiceListener listener);
void removeListener(in IEthernetServiceListener listener);
void setIncludeTestInterfaces(boolean include);
void requestTetheredInterface(in ITetheredInterfaceCallback callback);
void releaseTetheredInterface(in ITetheredInterfaceCallback callback);
void updateConfiguration(String iface, in EthernetNetworkUpdateRequest request,
in INetworkInterfaceOutcomeReceiver listener);
void connectNetwork(String iface, in INetworkInterfaceOutcomeReceiver listener);
void disconnectNetwork(String iface, in INetworkInterfaceOutcomeReceiver listener);
void setEthernetEnabled(boolean enabled);
List<String> getInterfaceList();
}

View File

@@ -1,27 +0,0 @@
/*
* Copyright (C) 2014 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;
import android.net.IpConfiguration;
/** @hide */
oneway interface IEthernetServiceListener
{
void onEthernetStateChanged(int state);
void onInterfaceStateChanged(String iface, int state, int role,
in IpConfiguration configuration);
}

View File

@@ -1,78 +0,0 @@
/*
** Copyright 2017, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
package android.net;
import android.net.LinkAddress;
import android.net.Network;
import android.net.IpSecConfig;
import android.net.IpSecUdpEncapResponse;
import android.net.IpSecSpiResponse;
import android.net.IpSecTransformResponse;
import android.net.IpSecTunnelInterfaceResponse;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
/**
* @hide
*/
interface IIpSecService
{
IpSecSpiResponse allocateSecurityParameterIndex(
in String destinationAddress, int requestedSpi, in IBinder binder);
void releaseSecurityParameterIndex(int resourceId);
IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, in IBinder binder);
void closeUdpEncapsulationSocket(int resourceId);
IpSecTunnelInterfaceResponse createTunnelInterface(
in String localAddr,
in String remoteAddr,
in Network underlyingNetwork,
in IBinder binder,
in String callingPackage);
void addAddressToTunnelInterface(
int tunnelResourceId,
in LinkAddress localAddr,
in String callingPackage);
void removeAddressFromTunnelInterface(
int tunnelResourceId,
in LinkAddress localAddr,
in String callingPackage);
void setNetworkForTunnelInterface(
int tunnelResourceId, in Network underlyingNetwork, in String callingPackage);
void deleteTunnelInterface(int resourceId, in String callingPackage);
IpSecTransformResponse createTransform(
in IpSecConfig c, in IBinder binder, in String callingPackage);
void deleteTransform(int transformId);
void applyTransportModeTransform(
in ParcelFileDescriptor socket, int direction, int transformId);
void applyTunnelModeTransform(
int tunnelResourceId, int direction, int transformResourceId, in String callingPackage);
void removeTransportModeTransforms(in ParcelFileDescriptor socket);
}

View File

@@ -1,25 +0,0 @@
/**
* Copyright (c) 2021, 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;
import android.net.EthernetNetworkManagementException;
/** @hide */
oneway interface INetworkInterfaceOutcomeReceiver {
void onResult(in String iface);
void onError(in EthernetNetworkManagementException e);
}

View File

@@ -1,104 +0,0 @@
/*
* 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 android.net;
import android.net.DataUsageRequest;
import android.net.INetworkStatsSession;
import android.net.Network;
import android.net.NetworkStateSnapshot;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.IUsageCallback;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
import android.os.Messenger;
/** {@hide} */
interface INetworkStatsService {
/** Start a statistics query session. */
@UnsupportedAppUsage
INetworkStatsSession openSession();
/** Start a statistics query session. If calling package is profile or device owner then it is
* granted automatic access if apiLevel is NetworkStatsManager.API_LEVEL_DPC_ALLOWED. If
* apiLevel is at least NetworkStatsManager.API_LEVEL_REQUIRES_PACKAGE_USAGE_STATS then
* PACKAGE_USAGE_STATS permission is always checked. If PACKAGE_USAGE_STATS is not granted
* READ_NETWORK_USAGE_STATS is checked for.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
INetworkStatsSession openSessionForUsageStats(int flags, String callingPackage);
/** Return data layer snapshot of UID network usage. */
@UnsupportedAppUsage
NetworkStats getDataLayerSnapshotForUid(int uid);
/** Get the transport NetworkStats for all UIDs since boot. */
NetworkStats getUidStatsForTransport(int transport);
/** Return set of any ifaces associated with mobile networks since boot. */
@UnsupportedAppUsage
String[] getMobileIfaces();
/** Increment data layer count of operations performed for UID and tag. */
void incrementOperationCount(int uid, int tag, int operationCount);
/** Notify {@code NetworkStatsService} about network status changed. */
void notifyNetworkStatus(
in Network[] defaultNetworks,
in NetworkStateSnapshot[] snapshots,
in String activeIface,
in UnderlyingNetworkInfo[] underlyingNetworkInfos);
/** Force update of statistics. */
@UnsupportedAppUsage
void forceUpdate();
/** Registers a callback on data usage. */
DataUsageRequest registerUsageCallback(String callingPackage,
in DataUsageRequest request, in IUsageCallback callback);
/** Unregisters a callback on data usage. */
void unregisterUsageRequest(in DataUsageRequest request);
/** Get the uid stats information since boot */
long getUidStats(int uid, int type);
/** Get the iface stats information since boot */
long getIfaceStats(String iface, int type);
/** Get the total network stats information since boot */
long getTotalStats(int type);
/** Registers a network stats provider */
INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
in INetworkStatsProvider provider);
/** Mark given UID as being in foreground for stats purposes. */
void noteUidForeground(int uid, boolean uidForeground);
/** Advise persistence threshold; may be overridden internally. */
void advisePersistThreshold(long thresholdBytes);
/**
* Set the warning and limit to all registered custom network stats providers.
* Note that invocation of any interface will be sent to all providers.
*/
void setStatsProviderWarningAndLimitAsync(String iface, long warning, long limit);
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright (C) 2012 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;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
/** {@hide} */
interface INetworkStatsSession {
/** Return device aggregated network layer usage summary for traffic that matches template. */
NetworkStats getDeviceSummaryForNetwork(in NetworkTemplate template, long start, long end);
/** Return network layer usage summary for traffic that matches template. */
@UnsupportedAppUsage
NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
/** Return historical network layer stats for traffic that matches template. */
@UnsupportedAppUsage
NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
/**
* Return historical network layer stats for traffic that matches template, start and end
* timestamp.
*/
NetworkStatsHistory getHistoryIntervalForNetwork(in NetworkTemplate template, int fields, long start, long end);
/**
* Return network layer usage summary per UID for traffic that matches template.
*
* <p>The resulting {@code NetworkStats#getElapsedRealtime()} contains time delta between
* {@code start} and {@code end}.
*
* @param template - a predicate to filter netstats.
* @param start - start of the range, timestamp in milliseconds since the epoch.
* @param end - end of the range, timestamp in milliseconds since the epoch.
* @param includeTags - includes data usage tags if true.
*/
@UnsupportedAppUsage
NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
/** Return network layer usage summary per UID for tagged traffic that matches template. */
NetworkStats getTaggedSummaryForAllUid(in NetworkTemplate template, long start, long end);
/** Return historical network layer stats for specific UID traffic that matches template. */
@UnsupportedAppUsage
NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
/** Return historical network layer stats for specific UID traffic that matches template. */
NetworkStatsHistory getHistoryIntervalForUid(in NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end);
/** Return array of uids that have stats and are accessible to the calling user */
int[] getRelevantUids();
@UnsupportedAppUsage
void close();
}

View File

@@ -1,23 +0,0 @@
/*
* Copyright (C) 2020 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;
/** @hide */
oneway interface ITetheredInterfaceCallback {
void onAvailable(in String iface);
void onUnavailable();
}

View File

@@ -1,491 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.annotation.NonNull;
import android.annotation.StringDef;
import android.content.res.Resources;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* This class represents a single algorithm that can be used by an {@link IpSecTransform}.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
* Internet Protocol</a>
*/
public final class IpSecAlgorithm implements Parcelable {
private static final String TAG = "IpSecAlgorithm";
/**
* Null cipher.
*
* @hide
*/
public static final String CRYPT_NULL = "ecb(cipher_null)";
/**
* AES-CBC Encryption/Ciphering Algorithm.
*
* <p>Valid lengths for this key are {128, 192, 256}.
*/
public static final String CRYPT_AES_CBC = "cbc(aes)";
/**
* AES-CTR Encryption/Ciphering Algorithm.
*
* <p>Valid lengths for keying material are {160, 224, 288}.
*
* <p>As per <a href="https://tools.ietf.org/html/rfc3686#section-5.1">RFC3686 (Section
* 5.1)</a>, keying material consists of a 128, 192, or 256 bit AES key followed by a 32-bit
* nonce. RFC compliance requires that the nonce must be unique per security association.
*
* <p>This algorithm may be available on the device. Caller MUST check if it is supported before
* using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
* included in the returned algorithm set. The returned algorithm set will not change unless the
* device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
* requested on an unsupported device.
*
* <p>@see {@link #getSupportedAlgorithms()}
*/
// This algorithm may be available on devices released before Android 12, and is guaranteed
// to be available on devices first shipped with Android 12 or later.
public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
/**
* MD5 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
* new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
*
* <p>Keys for this algorithm must be 128 bits in length.
*
* <p>Valid truncation lengths are multiples of 8 bits from 96 to 128.
*/
public static final String AUTH_HMAC_MD5 = "hmac(md5)";
/**
* SHA1 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
* new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
*
* <p>Keys for this algorithm must be 160 bits in length.
*
* <p>Valid truncation lengths are multiples of 8 bits from 96 to 160.
*/
public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
/**
* SHA256 HMAC Authentication/Integrity Algorithm.
*
* <p>Keys for this algorithm must be 256 bits in length.
*
* <p>Valid truncation lengths are multiples of 8 bits from 96 to 256.
*/
public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
/**
* SHA384 HMAC Authentication/Integrity Algorithm.
*
* <p>Keys for this algorithm must be 384 bits in length.
*
* <p>Valid truncation lengths are multiples of 8 bits from 192 to 384.
*/
public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
/**
* SHA512 HMAC Authentication/Integrity Algorithm.
*
* <p>Keys for this algorithm must be 512 bits in length.
*
* <p>Valid truncation lengths are multiples of 8 bits from 256 to 512.
*/
public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
/**
* AES-XCBC Authentication/Integrity Algorithm.
*
* <p>Keys for this algorithm must be 128 bits in length.
*
* <p>The only valid truncation length is 96 bits.
*
* <p>This algorithm may be available on the device. Caller MUST check if it is supported before
* using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
* included in the returned algorithm set. The returned algorithm set will not change unless the
* device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
* requested on an unsupported device.
*
* <p>@see {@link #getSupportedAlgorithms()}
*/
// This algorithm may be available on devices released before Android 12, and is guaranteed
// to be available on devices first shipped with Android 12 or later.
public static final String AUTH_AES_XCBC = "xcbc(aes)";
/**
* AES-CMAC Authentication/Integrity Algorithm.
*
* <p>Keys for this algorithm must be 128 bits in length.
*
* <p>The only valid truncation length is 96 bits.
*
* <p>This algorithm may be available on the device. Caller MUST check if it is supported before
* using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
* included in the returned algorithm set. The returned algorithm set will not change unless the
* device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
* requested on an unsupported device.
*
* <p>@see {@link #getSupportedAlgorithms()}
*/
// This algorithm may be available on devices released before Android 12, and is guaranteed
// to be available on devices first shipped with Android 12 or later.
public static final String AUTH_AES_CMAC = "cmac(aes)";
/**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
*
* <p>Valid lengths for keying material are {160, 224, 288}.
*
* <p>As per <a href="https://tools.ietf.org/html/rfc4106#section-8.1">RFC4106 (Section
* 8.1)</a>, keying material consists of a 128, 192, or 256 bit AES key followed by a 32-bit
* salt. RFC compliance requires that the salt must be unique per invocation with the same key.
*
* <p>Valid ICV (truncation) lengths are {64, 96, 128}.
*/
public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
/**
* ChaCha20-Poly1305 Authentication/Integrity + Encryption/Ciphering Algorithm.
*
* <p>Keys for this algorithm must be 288 bits in length.
*
* <p>As per <a href="https://tools.ietf.org/html/rfc7634#section-2">RFC7634 (Section 2)</a>,
* keying material consists of a 256 bit key followed by a 32-bit salt. The salt is fixed per
* security association.
*
* <p>The only valid ICV (truncation) length is 128 bits.
*
* <p>This algorithm may be available on the device. Caller MUST check if it is supported before
* using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
* included in the returned algorithm set. The returned algorithm set will not change unless the
* device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
* requested on an unsupported device.
*
* <p>@see {@link #getSupportedAlgorithms()}
*/
// This algorithm may be available on devices released before Android 12, and is guaranteed
// to be available on devices first shipped with Android 12 or later.
public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
/** @hide */
@StringDef({
CRYPT_AES_CBC,
CRYPT_AES_CTR,
AUTH_HMAC_MD5,
AUTH_HMAC_SHA1,
AUTH_HMAC_SHA256,
AUTH_HMAC_SHA384,
AUTH_HMAC_SHA512,
AUTH_AES_XCBC,
AUTH_AES_CMAC,
AUTH_CRYPT_AES_GCM,
AUTH_CRYPT_CHACHA20_POLY1305
})
@Retention(RetentionPolicy.SOURCE)
public @interface AlgorithmName {}
/** @hide */
@VisibleForTesting
public static final Map<String, Integer> ALGO_TO_REQUIRED_FIRST_SDK = new HashMap<>();
private static final int SDK_VERSION_ZERO = 0;
static {
ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CBC, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_MD5, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA1, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA256, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA384, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
}
private static final Set<String> ENABLED_ALGOS =
Collections.unmodifiableSet(loadAlgos(Resources.getSystem()));
private final String mName;
private final byte[] mKey;
private final int mTruncLenBits;
/**
* Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
* defined as constants in this class.
*
* <p>For algorithms that produce an integrity check value, the truncation length is a required
* parameter. See {@link #IpSecAlgorithm(String algorithm, byte[] key, int truncLenBits)}
*
* @param algorithm name of the algorithm.
* @param key key padded to a multiple of 8 bits.
* @throws IllegalArgumentException if algorithm or key length is invalid.
*/
public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
this(algorithm, key, 0);
}
/**
* Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
* defined as constants in this class.
*
* <p>This constructor only supports algorithms that use a truncation length. i.e.
* Authentication and Authenticated Encryption algorithms.
*
* @param algorithm name of the algorithm.
* @param key key padded to a multiple of 8 bits.
* @param truncLenBits number of bits of output hash to use.
* @throws IllegalArgumentException if algorithm, key length or truncation length is invalid.
*/
public IpSecAlgorithm(
@NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
mName = algorithm;
mKey = key.clone();
mTruncLenBits = truncLenBits;
checkValidOrThrow(mName, mKey.length * 8, mTruncLenBits);
}
/** Get the algorithm name */
@NonNull
public String getName() {
return mName;
}
/** Get the key for this algorithm */
@NonNull
public byte[] getKey() {
return mKey.clone();
}
/** Get the truncation length of this algorithm, in bits */
public int getTruncationLengthBits() {
return mTruncLenBits;
}
/** Parcelable Implementation */
public int describeContents() {
return 0;
}
/** Write to parcel */
public void writeToParcel(Parcel out, int flags) {
out.writeString(mName);
out.writeByteArray(mKey);
out.writeInt(mTruncLenBits);
}
/** Parcelable Creator */
public static final @android.annotation.NonNull Parcelable.Creator<IpSecAlgorithm> CREATOR =
new Parcelable.Creator<IpSecAlgorithm>() {
public IpSecAlgorithm createFromParcel(Parcel in) {
final String name = in.readString();
final byte[] key = in.createByteArray();
final int truncLenBits = in.readInt();
return new IpSecAlgorithm(name, key, truncLenBits);
}
public IpSecAlgorithm[] newArray(int size) {
return new IpSecAlgorithm[size];
}
};
/**
* Returns supported IPsec algorithms for the current device.
*
* <p>Some algorithms may not be supported on old devices. Callers MUST check if an algorithm is
* supported before using it.
*/
@NonNull
public static Set<String> getSupportedAlgorithms() {
return ENABLED_ALGOS;
}
/** @hide */
@VisibleForTesting
public static Set<String> loadAlgos(Resources systemResources) {
final Set<String> enabledAlgos = new HashSet<>();
// Load and validate the optional algorithm resource. Undefined or duplicate algorithms in
// the resource are not allowed.
final String[] resourceAlgos = systemResources.getStringArray(
android.R.array.config_optionalIpSecAlgorithms);
for (String str : resourceAlgos) {
if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) {
// This error should be caught by CTS and never be thrown to API callers
throw new IllegalArgumentException("Invalid or repeated algorithm " + str);
}
}
for (Entry<String, Integer> entry : ALGO_TO_REQUIRED_FIRST_SDK.entrySet()) {
if (Build.VERSION.DEVICE_INITIAL_SDK_INT >= entry.getValue()) {
enabledAlgos.add(entry.getKey());
}
}
return enabledAlgos;
}
private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
final boolean isValidLen;
final boolean isValidTruncLen;
if (!getSupportedAlgorithms().contains(name)) {
throw new IllegalArgumentException("Unsupported algorithm: " + name);
}
switch (name) {
case CRYPT_AES_CBC:
isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256;
isValidTruncLen = true;
break;
case CRYPT_AES_CTR:
// The keying material for AES-CTR is a key plus a 32-bit salt
isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
isValidTruncLen = true;
break;
case AUTH_HMAC_MD5:
isValidLen = keyLen == 128;
isValidTruncLen = truncLen >= 96 && truncLen <= 128;
break;
case AUTH_HMAC_SHA1:
isValidLen = keyLen == 160;
isValidTruncLen = truncLen >= 96 && truncLen <= 160;
break;
case AUTH_HMAC_SHA256:
isValidLen = keyLen == 256;
isValidTruncLen = truncLen >= 96 && truncLen <= 256;
break;
case AUTH_HMAC_SHA384:
isValidLen = keyLen == 384;
isValidTruncLen = truncLen >= 192 && truncLen <= 384;
break;
case AUTH_HMAC_SHA512:
isValidLen = keyLen == 512;
isValidTruncLen = truncLen >= 256 && truncLen <= 512;
break;
case AUTH_AES_XCBC:
isValidLen = keyLen == 128;
isValidTruncLen = truncLen == 96;
break;
case AUTH_AES_CMAC:
isValidLen = keyLen == 128;
isValidTruncLen = truncLen == 96;
break;
case AUTH_CRYPT_AES_GCM:
// The keying material for GCM is a key plus a 32-bit salt
isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
break;
case AUTH_CRYPT_CHACHA20_POLY1305:
// The keying material for ChaCha20Poly1305 is a key plus a 32-bit salt
isValidLen = keyLen == 256 + 32;
isValidTruncLen = truncLen == 128;
break;
default:
// Should never hit here.
throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
}
if (!isValidLen) {
throw new IllegalArgumentException("Invalid key material keyLength: " + keyLen);
}
if (!isValidTruncLen) {
throw new IllegalArgumentException("Invalid truncation keyLength: " + truncLen);
}
}
/** @hide */
public boolean isAuthentication() {
switch (getName()) {
// Fallthrough
case AUTH_HMAC_MD5:
case AUTH_HMAC_SHA1:
case AUTH_HMAC_SHA256:
case AUTH_HMAC_SHA384:
case AUTH_HMAC_SHA512:
case AUTH_AES_XCBC:
case AUTH_AES_CMAC:
return true;
default:
return false;
}
}
/** @hide */
public boolean isEncryption() {
switch (getName()) {
case CRYPT_AES_CBC: // fallthrough
case CRYPT_AES_CTR:
return true;
default:
return false;
}
}
/** @hide */
public boolean isAead() {
switch (getName()) {
case AUTH_CRYPT_AES_GCM: // fallthrough
case AUTH_CRYPT_CHACHA20_POLY1305:
return true;
default:
return false;
}
}
@Override
@NonNull
public String toString() {
return new StringBuilder()
.append("{mName=")
.append(mName)
.append(", mTruncLenBits=")
.append(mTruncLenBits)
.append("}")
.toString();
}
/** @hide */
@VisibleForTesting
public static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) {
if (lhs == null || rhs == null) return (lhs == rhs);
return (lhs.mName.equals(rhs.mName)
&& Arrays.equals(lhs.mKey, rhs.mKey)
&& lhs.mTruncLenBits == rhs.mTruncLenBits);
}
};

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/** @hide */
parcelable IpSecConfig;

View File

@@ -1,358 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
/**
* This class encapsulates all the configuration parameters needed to create IPsec transforms and
* policies.
*
* @hide
*/
public final class IpSecConfig implements Parcelable {
private static final String TAG = "IpSecConfig";
// MODE_TRANSPORT or MODE_TUNNEL
private int mMode = IpSecTransform.MODE_TRANSPORT;
// Preventing this from being null simplifies Java->Native binder
private String mSourceAddress = "";
// Preventing this from being null simplifies Java->Native binder
private String mDestinationAddress = "";
// The underlying Network that represents the "gateway" Network
// for outbound packets. It may also be used to select packets.
private Network mNetwork;
// Minimum requirements for identifying a transform
// SPI identifying the IPsec SA in packet processing
// and a destination IP address
private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID;
// Encryption Algorithm
private IpSecAlgorithm mEncryption;
// Authentication Algorithm
private IpSecAlgorithm mAuthentication;
// Authenticated Encryption Algorithm
private IpSecAlgorithm mAuthenticatedEncryption;
// For tunnel mode IPv4 UDP Encapsulation
// IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
private int mEncapType = IpSecTransform.ENCAP_NONE;
private int mEncapSocketResourceId = IpSecManager.INVALID_RESOURCE_ID;
private int mEncapRemotePort;
// An interval, in seconds between the NattKeepalive packets
private int mNattKeepaliveInterval;
// XFRM mark and mask; defaults to 0 (no mark/mask)
private int mMarkValue;
private int mMarkMask;
// XFRM interface id
private int mXfrmInterfaceId;
/** Set the mode for this IPsec transform */
public void setMode(int mode) {
mMode = mode;
}
/** Set the source IP addres for this IPsec transform */
public void setSourceAddress(String sourceAddress) {
mSourceAddress = sourceAddress;
}
/** Set the destination IP address for this IPsec transform */
public void setDestinationAddress(String destinationAddress) {
mDestinationAddress = destinationAddress;
}
/** Set the SPI by resource ID */
public void setSpiResourceId(int resourceId) {
mSpiResourceId = resourceId;
}
/** Set the encryption algorithm */
public void setEncryption(IpSecAlgorithm encryption) {
mEncryption = encryption;
}
/** Set the authentication algorithm */
public void setAuthentication(IpSecAlgorithm authentication) {
mAuthentication = authentication;
}
/** Set the authenticated encryption algorithm */
public void setAuthenticatedEncryption(IpSecAlgorithm authenticatedEncryption) {
mAuthenticatedEncryption = authenticatedEncryption;
}
/** Set the underlying network that will carry traffic for this transform */
public void setNetwork(Network network) {
mNetwork = network;
}
public void setEncapType(int encapType) {
mEncapType = encapType;
}
public void setEncapSocketResourceId(int resourceId) {
mEncapSocketResourceId = resourceId;
}
public void setEncapRemotePort(int port) {
mEncapRemotePort = port;
}
public void setNattKeepaliveInterval(int interval) {
mNattKeepaliveInterval = interval;
}
/**
* Sets the mark value
*
* <p>Internal (System server) use only. Marks passed in by users will be overwritten or
* ignored.
*/
public void setMarkValue(int mark) {
mMarkValue = mark;
}
/**
* Sets the mark mask
*
* <p>Internal (System server) use only. Marks passed in by users will be overwritten or
* ignored.
*/
public void setMarkMask(int mask) {
mMarkMask = mask;
}
public void setXfrmInterfaceId(int xfrmInterfaceId) {
mXfrmInterfaceId = xfrmInterfaceId;
}
// Transport or Tunnel
public int getMode() {
return mMode;
}
public String getSourceAddress() {
return mSourceAddress;
}
public int getSpiResourceId() {
return mSpiResourceId;
}
public String getDestinationAddress() {
return mDestinationAddress;
}
public IpSecAlgorithm getEncryption() {
return mEncryption;
}
public IpSecAlgorithm getAuthentication() {
return mAuthentication;
}
public IpSecAlgorithm getAuthenticatedEncryption() {
return mAuthenticatedEncryption;
}
public Network getNetwork() {
return mNetwork;
}
public int getEncapType() {
return mEncapType;
}
public int getEncapSocketResourceId() {
return mEncapSocketResourceId;
}
public int getEncapRemotePort() {
return mEncapRemotePort;
}
public int getNattKeepaliveInterval() {
return mNattKeepaliveInterval;
}
public int getMarkValue() {
return mMarkValue;
}
public int getMarkMask() {
return mMarkMask;
}
public int getXfrmInterfaceId() {
return mXfrmInterfaceId;
}
// Parcelable Methods
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mMode);
out.writeString(mSourceAddress);
out.writeString(mDestinationAddress);
out.writeParcelable(mNetwork, flags);
out.writeInt(mSpiResourceId);
out.writeParcelable(mEncryption, flags);
out.writeParcelable(mAuthentication, flags);
out.writeParcelable(mAuthenticatedEncryption, flags);
out.writeInt(mEncapType);
out.writeInt(mEncapSocketResourceId);
out.writeInt(mEncapRemotePort);
out.writeInt(mNattKeepaliveInterval);
out.writeInt(mMarkValue);
out.writeInt(mMarkMask);
out.writeInt(mXfrmInterfaceId);
}
@VisibleForTesting
public IpSecConfig() {}
/** Copy constructor */
@VisibleForTesting
public IpSecConfig(IpSecConfig c) {
mMode = c.mMode;
mSourceAddress = c.mSourceAddress;
mDestinationAddress = c.mDestinationAddress;
mNetwork = c.mNetwork;
mSpiResourceId = c.mSpiResourceId;
mEncryption = c.mEncryption;
mAuthentication = c.mAuthentication;
mAuthenticatedEncryption = c.mAuthenticatedEncryption;
mEncapType = c.mEncapType;
mEncapSocketResourceId = c.mEncapSocketResourceId;
mEncapRemotePort = c.mEncapRemotePort;
mNattKeepaliveInterval = c.mNattKeepaliveInterval;
mMarkValue = c.mMarkValue;
mMarkMask = c.mMarkMask;
mXfrmInterfaceId = c.mXfrmInterfaceId;
}
private IpSecConfig(Parcel in) {
mMode = in.readInt();
mSourceAddress = in.readString();
mDestinationAddress = in.readString();
mNetwork = (Network) in.readParcelable(Network.class.getClassLoader());
mSpiResourceId = in.readInt();
mEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
mAuthentication =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
mAuthenticatedEncryption =
(IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
mEncapType = in.readInt();
mEncapSocketResourceId = in.readInt();
mEncapRemotePort = in.readInt();
mNattKeepaliveInterval = in.readInt();
mMarkValue = in.readInt();
mMarkMask = in.readInt();
mXfrmInterfaceId = in.readInt();
}
@Override
public String toString() {
StringBuilder strBuilder = new StringBuilder();
strBuilder
.append("{mMode=")
.append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT")
.append(", mSourceAddress=")
.append(mSourceAddress)
.append(", mDestinationAddress=")
.append(mDestinationAddress)
.append(", mNetwork=")
.append(mNetwork)
.append(", mEncapType=")
.append(mEncapType)
.append(", mEncapSocketResourceId=")
.append(mEncapSocketResourceId)
.append(", mEncapRemotePort=")
.append(mEncapRemotePort)
.append(", mNattKeepaliveInterval=")
.append(mNattKeepaliveInterval)
.append("{mSpiResourceId=")
.append(mSpiResourceId)
.append(", mEncryption=")
.append(mEncryption)
.append(", mAuthentication=")
.append(mAuthentication)
.append(", mAuthenticatedEncryption=")
.append(mAuthenticatedEncryption)
.append(", mMarkValue=")
.append(mMarkValue)
.append(", mMarkMask=")
.append(mMarkMask)
.append(", mXfrmInterfaceId=")
.append(mXfrmInterfaceId)
.append("}");
return strBuilder.toString();
}
public static final @android.annotation.NonNull Parcelable.Creator<IpSecConfig> CREATOR =
new Parcelable.Creator<IpSecConfig>() {
public IpSecConfig createFromParcel(Parcel in) {
return new IpSecConfig(in);
}
public IpSecConfig[] newArray(int size) {
return new IpSecConfig[size];
}
};
@Override
public boolean equals(@Nullable Object other) {
if (!(other instanceof IpSecConfig)) return false;
final IpSecConfig rhs = (IpSecConfig) other;
return (mMode == rhs.mMode
&& mSourceAddress.equals(rhs.mSourceAddress)
&& mDestinationAddress.equals(rhs.mDestinationAddress)
&& ((mNetwork != null && mNetwork.equals(rhs.mNetwork))
|| (mNetwork == rhs.mNetwork))
&& mEncapType == rhs.mEncapType
&& mEncapSocketResourceId == rhs.mEncapSocketResourceId
&& mEncapRemotePort == rhs.mEncapRemotePort
&& mNattKeepaliveInterval == rhs.mNattKeepaliveInterval
&& mSpiResourceId == rhs.mSpiResourceId
&& IpSecAlgorithm.equals(mEncryption, rhs.mEncryption)
&& IpSecAlgorithm.equals(mAuthenticatedEncryption, rhs.mAuthenticatedEncryption)
&& IpSecAlgorithm.equals(mAuthentication, rhs.mAuthentication)
&& mMarkValue == rhs.mMarkValue
&& mMarkMask == rhs.mMarkMask
&& mXfrmInterfaceId == rhs.mXfrmInterfaceId);
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/** @hide */
parcelable IpSecSpiResponse;

View File

@@ -1,78 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.os.Parcel;
import android.os.Parcelable;
/**
* This class is used to return an SPI and corresponding status from the IpSecService to an
* IpSecManager.SecurityParameterIndex.
*
* @hide
*/
public final class IpSecSpiResponse implements Parcelable {
private static final String TAG = "IpSecSpiResponse";
public final int resourceId;
public final int status;
public final int spi;
// Parcelable Methods
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(status);
out.writeInt(resourceId);
out.writeInt(spi);
}
public IpSecSpiResponse(int inStatus, int inResourceId, int inSpi) {
status = inStatus;
resourceId = inResourceId;
spi = inSpi;
}
public IpSecSpiResponse(int inStatus) {
if (inStatus == IpSecManager.Status.OK) {
throw new IllegalArgumentException("Valid status implies other args must be provided");
}
status = inStatus;
resourceId = IpSecManager.INVALID_RESOURCE_ID;
spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
}
private IpSecSpiResponse(Parcel in) {
status = in.readInt();
resourceId = in.readInt();
spi = in.readInt();
}
public static final @android.annotation.NonNull Parcelable.Creator<IpSecSpiResponse> CREATOR =
new Parcelable.Creator<IpSecSpiResponse>() {
public IpSecSpiResponse createFromParcel(Parcel in) {
return new IpSecSpiResponse(in);
}
public IpSecSpiResponse[] newArray(int size) {
return new IpSecSpiResponse[size];
}
};
}

View File

@@ -1,405 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.ServiceSpecificException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import dalvik.system.CloseGuard;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.util.Objects;
/**
* This class represents a transform, which roughly corresponds to an IPsec Security Association.
*
* <p>Transforms are created using {@link IpSecTransform.Builder}. Each {@code IpSecTransform}
* object encapsulates the properties and state of an IPsec security association. That includes,
* but is not limited to, algorithm choice, key material, and allocated system resources.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
* Internet Protocol</a>
*/
public final class IpSecTransform implements AutoCloseable {
private static final String TAG = "IpSecTransform";
/** @hide */
public static final int MODE_TRANSPORT = 0;
/** @hide */
public static final int MODE_TUNNEL = 1;
/** @hide */
public static final int ENCAP_NONE = 0;
/**
* IPsec traffic will be encapsulated within UDP, but with 8 zero-value bytes between the UDP
* header and payload. This prevents traffic from being interpreted as ESP or IKEv2.
*
* @hide
*/
public static final int ENCAP_ESPINUDP_NON_IKE = 1;
/**
* IPsec traffic will be encapsulated within UDP as per
* <a href="https://tools.ietf.org/html/rfc3948">RFC 3498</a>.
*
* @hide
*/
public static final int ENCAP_ESPINUDP = 2;
/** @hide */
@IntDef(value = {ENCAP_NONE, ENCAP_ESPINUDP, ENCAP_ESPINUDP_NON_IKE})
@Retention(RetentionPolicy.SOURCE)
public @interface EncapType {}
/** @hide */
@VisibleForTesting
public IpSecTransform(Context context, IpSecConfig config) {
mContext = context;
mConfig = new IpSecConfig(config);
mResourceId = INVALID_RESOURCE_ID;
}
private IpSecManager getIpSecManager(Context context) {
return context.getSystemService(IpSecManager.class);
}
/**
* Checks the result status and throws an appropriate exception if the status is not Status.OK.
*/
private void checkResultStatus(int status)
throws IOException, IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException {
switch (status) {
case IpSecManager.Status.OK:
return;
// TODO: Pass Error string back from bundle so that errors can be more specific
case IpSecManager.Status.RESOURCE_UNAVAILABLE:
throw new IpSecManager.ResourceUnavailableException(
"Failed to allocate a new IpSecTransform");
case IpSecManager.Status.SPI_UNAVAILABLE:
Log.wtf(TAG, "Attempting to use an SPI that was somehow not reserved");
// Fall through
default:
throw new IllegalStateException(
"Failed to Create a Transform with status code " + status);
}
}
private IpSecTransform activate()
throws IOException, IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException {
synchronized (this) {
try {
IpSecTransformResponse result = getIpSecManager(mContext).createTransform(
mConfig, new Binder(), mContext.getOpPackageName());
int status = result.status;
checkResultStatus(status);
mResourceId = result.resourceId;
Log.d(TAG, "Added Transform with Id " + mResourceId);
mCloseGuard.open("build");
} catch (ServiceSpecificException e) {
throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e);
}
}
return this;
}
/**
* Standard equals.
*/
public boolean equals(@Nullable Object other) {
if (this == other) return true;
if (!(other instanceof IpSecTransform)) return false;
final IpSecTransform rhs = (IpSecTransform) other;
return getConfig().equals(rhs.getConfig()) && mResourceId == rhs.mResourceId;
}
/**
* Deactivate this {@code IpSecTransform} and free allocated resources.
*
* <p>Deactivating a transform while it is still applied to a socket will result in errors on
* that socket. Make sure to remove transforms by calling {@link
* IpSecManager#removeTransportModeTransforms}. Note, removing an {@code IpSecTransform} from a
* socket will not deactivate it (because one transform may be applied to multiple sockets).
*
* <p>It is safe to call this method on a transform that has already been deactivated.
*/
public void close() {
Log.d(TAG, "Removing Transform with Id " + mResourceId);
// Always safe to attempt cleanup
if (mResourceId == INVALID_RESOURCE_ID) {
mCloseGuard.close();
return;
}
try {
getIpSecManager(mContext).deleteTransform(mResourceId);
} catch (Exception e) {
// On close we swallow all random exceptions since failure to close is not
// actionable by the user.
Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
} finally {
mResourceId = INVALID_RESOURCE_ID;
mCloseGuard.close();
}
}
/** Check that the transform was closed properly. */
@Override
protected void finalize() throws Throwable {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
close();
}
/* Package */
IpSecConfig getConfig() {
return mConfig;
}
private final IpSecConfig mConfig;
private int mResourceId;
private final Context mContext;
private final CloseGuard mCloseGuard = CloseGuard.get();
/** @hide */
@VisibleForTesting
public int getResourceId() {
return mResourceId;
}
/**
* A callback class to provide status information regarding a NAT-T keepalive session
*
* <p>Use this callback to receive status information regarding a NAT-T keepalive session
* by registering it when calling {@link #startNattKeepalive}.
*
* @hide
*/
public static class NattKeepaliveCallback {
/** The specified {@code Network} is not connected. */
public static final int ERROR_INVALID_NETWORK = 1;
/** The hardware does not support this request. */
public static final int ERROR_HARDWARE_UNSUPPORTED = 2;
/** The hardware returned an error. */
public static final int ERROR_HARDWARE_ERROR = 3;
/** The requested keepalive was successfully started. */
public void onStarted() {}
/** The keepalive was successfully stopped. */
public void onStopped() {}
/** An error occurred. */
public void onError(int error) {}
}
/** This class is used to build {@link IpSecTransform} objects. */
public static class Builder {
private Context mContext;
private IpSecConfig mConfig;
/**
* Set the encryption algorithm.
*
* <p>Encryption is mutually exclusive with authenticated encryption.
*
* @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
*/
@NonNull
public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
Objects.requireNonNull(algo);
mConfig.setEncryption(algo);
return this;
}
/**
* Set the authentication (integrity) algorithm.
*
* <p>Authentication is mutually exclusive with authenticated encryption.
*
* @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
*/
@NonNull
public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
// TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
Objects.requireNonNull(algo);
mConfig.setAuthentication(algo);
return this;
}
/**
* Set the authenticated encryption algorithm.
*
* <p>The Authenticated Encryption (AE) class of algorithms are also known as
* Authenticated Encryption with Associated Data (AEAD) algorithms, or Combined mode
* algorithms (as referred to in
* <a href="https://tools.ietf.org/html/rfc4301">RFC 4301</a>).
*
* <p>Authenticated encryption is mutually exclusive with encryption and authentication.
*
* @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
* be applied.
*/
@NonNull
public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
Objects.requireNonNull(algo);
mConfig.setAuthenticatedEncryption(algo);
return this;
}
/**
* Add UDP encapsulation to an IPv4 transform.
*
* <p>This allows IPsec traffic to pass through a NAT.
*
* @see <a href="https://tools.ietf.org/html/rfc3948">RFC 3948, UDP Encapsulation of IPsec
* ESP Packets</a>
* @see <a href="https://tools.ietf.org/html/rfc7296#section-2.23">RFC 7296 section 2.23,
* NAT Traversal of IKEv2</a>
* @param localSocket a socket for sending and receiving encapsulated traffic
* @param remotePort the UDP port number of the remote host that will send and receive
* encapsulated traffic. In the case of IKEv2, this should be port 4500.
*/
@NonNull
public IpSecTransform.Builder setIpv4Encapsulation(
@NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
Objects.requireNonNull(localSocket);
mConfig.setEncapType(ENCAP_ESPINUDP);
if (localSocket.getResourceId() == INVALID_RESOURCE_ID) {
throw new IllegalArgumentException("Invalid UdpEncapsulationSocket");
}
mConfig.setEncapSocketResourceId(localSocket.getResourceId());
mConfig.setEncapRemotePort(remotePort);
return this;
}
/**
* Build a transport mode {@link IpSecTransform}.
*
* <p>This builds and activates a transport mode transform. Note that an active transform
* will not affect any network traffic until it has been applied to one or more sockets.
*
* @see IpSecManager#applyTransportModeTransform
* @param sourceAddress the source {@code InetAddress} of traffic on sockets that will use
* this transform; this address must belong to the Network used by all sockets that
* utilize this transform; if provided, then only traffic originating from the
* specified source address will be processed.
* @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
* traffic
* @throws IllegalArgumentException indicating that a particular combination of transform
* properties is invalid
* @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
* are active
* @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
* collides with an existing transform
* @throws IOException indicating other errors
*/
@NonNull
public IpSecTransform buildTransportModeTransform(
@NonNull InetAddress sourceAddress,
@NonNull IpSecManager.SecurityParameterIndex spi)
throws IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException, IOException {
Objects.requireNonNull(sourceAddress);
Objects.requireNonNull(spi);
if (spi.getResourceId() == INVALID_RESOURCE_ID) {
throw new IllegalArgumentException("Invalid SecurityParameterIndex");
}
mConfig.setMode(MODE_TRANSPORT);
mConfig.setSourceAddress(sourceAddress.getHostAddress());
mConfig.setSpiResourceId(spi.getResourceId());
// FIXME: modifying a builder after calling build can change the built transform.
return new IpSecTransform(mContext, mConfig).activate();
}
/**
* Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
* parameters have interdependencies that are checked at build time.
*
* @param sourceAddress the {@link InetAddress} that provides the source address for this
* IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
* that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
* @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
* traffic
* @throws IllegalArgumentException indicating that a particular combination of transform
* properties is invalid.
* @throws IpSecManager.ResourceUnavailableException indicating that too many transforms
* are active
* @throws IpSecManager.SpiUnavailableException indicating the rare case where an SPI
* collides with an existing transform
* @throws IOException indicating other errors
* @hide
*/
@SystemApi
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public IpSecTransform buildTunnelModeTransform(
@NonNull InetAddress sourceAddress,
@NonNull IpSecManager.SecurityParameterIndex spi)
throws IpSecManager.ResourceUnavailableException,
IpSecManager.SpiUnavailableException, IOException {
Objects.requireNonNull(sourceAddress);
Objects.requireNonNull(spi);
if (spi.getResourceId() == INVALID_RESOURCE_ID) {
throw new IllegalArgumentException("Invalid SecurityParameterIndex");
}
mConfig.setMode(MODE_TUNNEL);
mConfig.setSourceAddress(sourceAddress.getHostAddress());
mConfig.setSpiResourceId(spi.getResourceId());
return new IpSecTransform(mContext, mConfig).activate();
}
/**
* Create a new IpSecTransform.Builder.
*
* @param context current context
*/
public Builder(@NonNull Context context) {
Objects.requireNonNull(context);
mContext = context;
mConfig = new IpSecConfig();
}
}
@Override
public String toString() {
return new StringBuilder()
.append("IpSecTransform{resourceId=")
.append(mResourceId)
.append("}")
.toString();
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/** @hide */
parcelable IpSecTransformResponse;

View File

@@ -1,74 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.os.Parcel;
import android.os.Parcelable;
/**
* This class is used to return an IpSecTransform resource Id and and corresponding status from the
* IpSecService to an IpSecTransform object.
*
* @hide
*/
public final class IpSecTransformResponse implements Parcelable {
private static final String TAG = "IpSecTransformResponse";
public final int resourceId;
public final int status;
// Parcelable Methods
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(status);
out.writeInt(resourceId);
}
public IpSecTransformResponse(int inStatus) {
if (inStatus == IpSecManager.Status.OK) {
throw new IllegalArgumentException("Valid status implies other args must be provided");
}
status = inStatus;
resourceId = IpSecManager.INVALID_RESOURCE_ID;
}
public IpSecTransformResponse(int inStatus, int inResourceId) {
status = inStatus;
resourceId = inResourceId;
}
private IpSecTransformResponse(Parcel in) {
status = in.readInt();
resourceId = in.readInt();
}
@android.annotation.NonNull
public static final Parcelable.Creator<IpSecTransformResponse> CREATOR =
new Parcelable.Creator<IpSecTransformResponse>() {
public IpSecTransformResponse createFromParcel(Parcel in) {
return new IpSecTransformResponse(in);
}
public IpSecTransformResponse[] newArray(int size) {
return new IpSecTransformResponse[size];
}
};
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2018 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;
/** @hide */
parcelable IpSecTunnelInterfaceResponse;

View File

@@ -1,79 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.os.Parcel;
import android.os.Parcelable;
/**
* This class is used to return an IpSecTunnelInterface resource Id and and corresponding status
* from the IpSecService to an IpSecTunnelInterface object.
*
* @hide
*/
public final class IpSecTunnelInterfaceResponse implements Parcelable {
private static final String TAG = "IpSecTunnelInterfaceResponse";
public final int resourceId;
public final String interfaceName;
public final int status;
// Parcelable Methods
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(status);
out.writeInt(resourceId);
out.writeString(interfaceName);
}
public IpSecTunnelInterfaceResponse(int inStatus) {
if (inStatus == IpSecManager.Status.OK) {
throw new IllegalArgumentException("Valid status implies other args must be provided");
}
status = inStatus;
resourceId = IpSecManager.INVALID_RESOURCE_ID;
interfaceName = "";
}
public IpSecTunnelInterfaceResponse(int inStatus, int inResourceId, String inInterfaceName) {
status = inStatus;
resourceId = inResourceId;
interfaceName = inInterfaceName;
}
private IpSecTunnelInterfaceResponse(Parcel in) {
status = in.readInt();
resourceId = in.readInt();
interfaceName = in.readString();
}
@android.annotation.NonNull
public static final Parcelable.Creator<IpSecTunnelInterfaceResponse> CREATOR =
new Parcelable.Creator<IpSecTunnelInterfaceResponse>() {
public IpSecTunnelInterfaceResponse createFromParcel(Parcel in) {
return new IpSecTunnelInterfaceResponse(in);
}
public IpSecTunnelInterfaceResponse[] newArray(int size) {
return new IpSecTunnelInterfaceResponse[size];
}
};
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
/** @hide */
parcelable IpSecUdpEncapResponse;

View File

@@ -1,98 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import java.io.FileDescriptor;
import java.io.IOException;
/**
* This class is used to return a UDP Socket and corresponding status from the IpSecService to an
* IpSecManager.UdpEncapsulationSocket.
*
* @hide
*/
public final class IpSecUdpEncapResponse implements Parcelable {
private static final String TAG = "IpSecUdpEncapResponse";
public final int resourceId;
public final int port;
public final int status;
// There is a weird asymmetry with FileDescriptor: you can write a FileDescriptor
// but you read a ParcelFileDescriptor. To circumvent this, when we receive a FD
// from the user, we immediately create a ParcelFileDescriptor DUP, which we invalidate
// on writeParcel() by setting the flag to do close-on-write.
// TODO: tests to ensure this doesn't leak
public final ParcelFileDescriptor fileDescriptor;
// Parcelable Methods
@Override
public int describeContents() {
return (fileDescriptor != null) ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(status);
out.writeInt(resourceId);
out.writeInt(port);
out.writeParcelable(fileDescriptor, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
public IpSecUdpEncapResponse(int inStatus) {
if (inStatus == IpSecManager.Status.OK) {
throw new IllegalArgumentException("Valid status implies other args must be provided");
}
status = inStatus;
resourceId = IpSecManager.INVALID_RESOURCE_ID;
port = -1;
fileDescriptor = null; // yes I know it's redundant, but readability
}
public IpSecUdpEncapResponse(int inStatus, int inResourceId, int inPort, FileDescriptor inFd)
throws IOException {
if (inStatus == IpSecManager.Status.OK && inFd == null) {
throw new IllegalArgumentException("Valid status implies FD must be non-null");
}
status = inStatus;
resourceId = inResourceId;
port = inPort;
fileDescriptor = (status == IpSecManager.Status.OK) ? ParcelFileDescriptor.dup(inFd) : null;
}
private IpSecUdpEncapResponse(Parcel in) {
status = in.readInt();
resourceId = in.readInt();
port = in.readInt();
fileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
}
@android.annotation.NonNull
public static final Parcelable.Creator<IpSecUdpEncapResponse> CREATOR =
new Parcelable.Creator<IpSecUdpEncapResponse>() {
public IpSecUdpEncapResponse createFromParcel(Parcel in) {
return new IpSecUdpEncapResponse(in);
}
public IpSecUdpEncapResponse[] newArray(int size) {
return new IpSecUdpEncapResponse[size];
}
};
}

View File

@@ -1,594 +0,0 @@
/*
* 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 android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.service.NetworkIdentityProto;
import android.telephony.TelephonyManager;
import android.util.proto.ProtoOutputStream;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
/**
* Network definition that includes strong identity. Analogous to combining
* {@link NetworkCapabilities} and an IMSI.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public class NetworkIdentity {
private static final String TAG = "NetworkIdentity";
/** @hide */
// TODO: Remove this after migrating all callers to use
// {@link NetworkTemplate#NETWORK_TYPE_ALL} instead.
public static final int SUBTYPE_COMBINED = -1;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "OEM_MANAGED_" }, flag = true, value = {
NetworkTemplate.OEM_MANAGED_NO,
NetworkTemplate.OEM_MANAGED_PAID,
NetworkTemplate.OEM_MANAGED_PRIVATE
})
public @interface OemManaged{}
/**
* Network has no {@code NetworkCapabilities#NET_CAPABILITY_OEM_*}.
* @hide
*/
public static final int OEM_NONE = 0x0;
/**
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}.
* @hide
*/
public static final int OEM_PAID = 1 << 0;
/**
* Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}.
* @hide
*/
public static final int OEM_PRIVATE = 1 << 1;
private static final long SUPPORTED_OEM_MANAGED_TYPES = OEM_PAID | OEM_PRIVATE;
final int mType;
final int mRatType;
final int mSubId;
final String mSubscriberId;
final String mWifiNetworkKey;
final boolean mRoaming;
final boolean mMetered;
final boolean mDefaultNetwork;
final int mOemManaged;
/** @hide */
public NetworkIdentity(
int type, int ratType, @Nullable String subscriberId, @Nullable String wifiNetworkKey,
boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged, int subId) {
mType = type;
mRatType = ratType;
mSubscriberId = subscriberId;
mWifiNetworkKey = wifiNetworkKey;
mRoaming = roaming;
mMetered = metered;
mDefaultNetwork = defaultNetwork;
mOemManaged = oemManaged;
mSubId = subId;
}
@Override
public int hashCode() {
return Objects.hash(mType, mRatType, mSubscriberId, mWifiNetworkKey, mRoaming, mMetered,
mDefaultNetwork, mOemManaged, mSubId);
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkIdentity) {
final NetworkIdentity ident = (NetworkIdentity) obj;
return mType == ident.mType && mRatType == ident.mRatType && mRoaming == ident.mRoaming
&& Objects.equals(mSubscriberId, ident.mSubscriberId)
&& Objects.equals(mWifiNetworkKey, ident.mWifiNetworkKey)
&& mMetered == ident.mMetered
&& mDefaultNetwork == ident.mDefaultNetwork
&& mOemManaged == ident.mOemManaged
&& mSubId == ident.mSubId;
}
return false;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder("{");
builder.append("type=").append(mType);
builder.append(", ratType=");
if (mRatType == NETWORK_TYPE_ALL) {
builder.append("COMBINED");
} else {
builder.append(mRatType);
}
if (mSubscriberId != null) {
builder.append(", subscriberId=")
.append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mWifiNetworkKey != null) {
builder.append(", wifiNetworkKey=").append(mWifiNetworkKey);
}
if (mRoaming) {
builder.append(", ROAMING");
}
builder.append(", metered=").append(mMetered);
builder.append(", defaultNetwork=").append(mDefaultNetwork);
builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
builder.append(", subId=").append(mSubId);
return builder.append("}").toString();
}
/**
* Get the human readable representation of a bitfield representing the OEM managed state of a
* network.
*/
static String getOemManagedNames(int oemManaged) {
if (oemManaged == OEM_NONE) {
return "OEM_NONE";
}
final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
final ArrayList<String> oemManagedNames = new ArrayList<String>();
for (int position : bitPositions) {
oemManagedNames.add(nameOfOemManaged(1 << position));
}
return String.join(",", oemManagedNames);
}
private static String nameOfOemManaged(int oemManagedBit) {
switch (oemManagedBit) {
case OEM_PAID:
return "OEM_PAID";
case OEM_PRIVATE:
return "OEM_PRIVATE";
default:
return "Invalid(" + oemManagedBit + ")";
}
}
/** @hide */
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
proto.write(NetworkIdentityProto.TYPE, mType);
// TODO: dump mRatType as well.
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
proto.write(NetworkIdentityProto.METERED, mMetered);
proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged);
proto.end(start);
}
/** Get the network type of this instance. */
public int getType() {
return mType;
}
/** Get the Radio Access Technology(RAT) type of this instance. */
public int getRatType() {
return mRatType;
}
/** Get the Subscriber Id of this instance. */
@Nullable
public String getSubscriberId() {
return mSubscriberId;
}
/** Get the Wifi Network Key of this instance. See {@link WifiInfo#getNetworkKey()}. */
@Nullable
public String getWifiNetworkKey() {
return mWifiNetworkKey;
}
/** @hide */
// TODO: Remove this function after all callers are removed.
public boolean getRoaming() {
return mRoaming;
}
/** Return whether this network is roaming. */
public boolean isRoaming() {
return mRoaming;
}
/** @hide */
// TODO: Remove this function after all callers are removed.
public boolean getMetered() {
return mMetered;
}
/** Return whether this network is metered. */
public boolean isMetered() {
return mMetered;
}
/** @hide */
// TODO: Remove this function after all callers are removed.
public boolean getDefaultNetwork() {
return mDefaultNetwork;
}
/** Return whether this network is the default network. */
public boolean isDefaultNetwork() {
return mDefaultNetwork;
}
/** Get the OEM managed type of this instance. */
public int getOemManaged() {
return mOemManaged;
}
/** Get the SubId of this instance. */
public int getSubId() {
return mSubId;
}
/**
* Assemble a {@link NetworkIdentity} from the passed arguments.
*
* This methods builds an identity based on the capabilities of the network in the
* snapshot and other passed arguments. The identity is used as a key to record data usage.
*
* @param snapshot the snapshot of network state. See {@link NetworkStateSnapshot}.
* @param defaultNetwork whether the network is a default network.
* @param ratType the Radio Access Technology(RAT) type of the network. Or
* {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if not applicable.
* See {@code TelephonyManager.NETWORK_TYPE_*}.
* @hide
* @deprecated See {@link NetworkIdentity.Builder}.
*/
// TODO: Remove this after all callers are migrated to use new Api.
@Deprecated
@NonNull
public static NetworkIdentity buildNetworkIdentity(Context context,
@NonNull NetworkStateSnapshot snapshot, boolean defaultNetwork, int ratType) {
final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
.setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork)
.setSubId(snapshot.getSubId());
if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
builder.setRatType(ratType);
}
return builder.build();
}
/**
* Builds a bitfield of {@code NetworkIdentity.OEM_*} based on {@link NetworkCapabilities}.
* @hide
*/
public static int getOemBitfield(@NonNull NetworkCapabilities nc) {
int oemManaged = OEM_NONE;
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)) {
oemManaged |= OEM_PAID;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)) {
oemManaged |= OEM_PRIVATE;
}
return oemManaged;
}
/** @hide */
public static int compare(@NonNull NetworkIdentity left, @NonNull NetworkIdentity right) {
Objects.requireNonNull(right);
int res = Integer.compare(left.mType, right.mType);
if (res == 0) {
res = Integer.compare(left.mRatType, right.mRatType);
}
if (res == 0 && left.mSubscriberId != null && right.mSubscriberId != null) {
res = left.mSubscriberId.compareTo(right.mSubscriberId);
}
if (res == 0 && left.mWifiNetworkKey != null && right.mWifiNetworkKey != null) {
res = left.mWifiNetworkKey.compareTo(right.mWifiNetworkKey);
}
if (res == 0) {
res = Boolean.compare(left.mRoaming, right.mRoaming);
}
if (res == 0) {
res = Boolean.compare(left.mMetered, right.mMetered);
}
if (res == 0) {
res = Boolean.compare(left.mDefaultNetwork, right.mDefaultNetwork);
}
if (res == 0) {
res = Integer.compare(left.mOemManaged, right.mOemManaged);
}
if (res == 0) {
res = Integer.compare(left.mSubId, right.mSubId);
}
return res;
}
/**
* Builder class for {@link NetworkIdentity}.
*/
public static final class Builder {
// Need to be synchronized with ConnectivityManager.
// TODO: Use {@link ConnectivityManager#MAX_NETWORK_TYPE} when this file is in the module.
private static final int MAX_NETWORK_TYPE = 18; // TYPE_TEST
private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
private int mType;
private int mRatType;
private String mSubscriberId;
private String mWifiNetworkKey;
private boolean mRoaming;
private boolean mMetered;
private boolean mDefaultNetwork;
private int mOemManaged;
private int mSubId;
/**
* Creates a new Builder.
*/
public Builder() {
// Initialize with default values. Will be overwritten by setters.
mType = ConnectivityManager.TYPE_NONE;
mRatType = NetworkTemplate.NETWORK_TYPE_ALL;
mSubscriberId = null;
mWifiNetworkKey = null;
mRoaming = false;
mMetered = false;
mDefaultNetwork = false;
mOemManaged = NetworkTemplate.OEM_MANAGED_NO;
mSubId = INVALID_SUBSCRIPTION_ID;
}
/**
* Add an {@link NetworkStateSnapshot} into the {@link NetworkIdentity} instance.
* This is a useful shorthand that will read from the snapshot and set the
* following fields, if they are set in the snapshot :
* - type
* - subscriberId
* - roaming
* - metered
* - oemManaged
* - wifiNetworkKey
*
* @param snapshot The target {@link NetworkStateSnapshot} object.
* @return The builder object.
*/
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setNetworkStateSnapshot(@NonNull NetworkStateSnapshot snapshot) {
setType(snapshot.getLegacyType());
setSubscriberId(snapshot.getSubscriberId());
setRoaming(!snapshot.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING));
setMetered(!(snapshot.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
|| snapshot.getNetworkCapabilities().hasCapability(
NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)));
setOemManaged(getOemBitfield(snapshot.getNetworkCapabilities()));
if (mType == TYPE_WIFI) {
final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
.getTransportInfo();
if (transportInfo instanceof WifiInfo) {
final WifiInfo info = (WifiInfo) transportInfo;
setWifiNetworkKey(info.getNetworkKey());
}
}
return this;
}
/**
* Set the network type of the network.
*
* @param type the network type. See {@link ConnectivityManager#TYPE_*}.
*
* @return this builder.
*/
@NonNull
public Builder setType(int type) {
// Include TYPE_NONE for compatibility, type field might not be filled by some
// networks such as test networks.
if ((type < MIN_NETWORK_TYPE || MAX_NETWORK_TYPE < type)
&& type != ConnectivityManager.TYPE_NONE) {
throw new IllegalArgumentException("Invalid network type: " + type);
}
mType = type;
return this;
}
/**
* Set the Radio Access Technology(RAT) type of the network.
*
* No RAT type is specified by default. Call clearRatType to reset.
*
* @param ratType the Radio Access Technology(RAT) type if applicable. See
* {@code TelephonyManager.NETWORK_TYPE_*}.
*
* @return this builder.
*/
@NonNull
public Builder setRatType(int ratType) {
if (!CollectionUtils.contains(TelephonyManager.getAllNetworkTypes(), ratType)
&& ratType != TelephonyManager.NETWORK_TYPE_UNKNOWN
&& ratType != NetworkStatsManager.NETWORK_TYPE_5G_NSA) {
throw new IllegalArgumentException("Invalid ratType " + ratType);
}
mRatType = ratType;
return this;
}
/**
* Clear the Radio Access Technology(RAT) type of the network.
*
* @return this builder.
*/
@NonNull
public Builder clearRatType() {
mRatType = NetworkTemplate.NETWORK_TYPE_ALL;
return this;
}
/**
* Set the Subscriber Id.
*
* @param subscriberId the Subscriber Id of the network. Or null if not applicable.
* @return this builder.
*/
@NonNull
public Builder setSubscriberId(@Nullable String subscriberId) {
mSubscriberId = subscriberId;
return this;
}
/**
* Set the Wifi Network Key.
*
* @param wifiNetworkKey Wifi Network Key of the network,
* see {@link WifiInfo#getNetworkKey()}.
* Or null if not applicable.
* @return this builder.
*/
@NonNull
public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) {
mWifiNetworkKey = wifiNetworkKey;
return this;
}
/**
* Set whether this network is roaming.
*
* This field is false by default. Call with false to reset.
*
* @param roaming the roaming status of the network.
* @return this builder.
*/
@NonNull
public Builder setRoaming(boolean roaming) {
mRoaming = roaming;
return this;
}
/**
* Set whether this network is metered.
*
* This field is false by default. Call with false to reset.
*
* @param metered the meteredness of the network.
* @return this builder.
*/
@NonNull
public Builder setMetered(boolean metered) {
mMetered = metered;
return this;
}
/**
* Set whether this network is the default network.
*
* This field is false by default. Call with false to reset.
*
* @param defaultNetwork the default network status of the network.
* @return this builder.
*/
@NonNull
public Builder setDefaultNetwork(boolean defaultNetwork) {
mDefaultNetwork = defaultNetwork;
return this;
}
/**
* Set the OEM managed type.
*
* @param oemManaged Type of OEM managed network or unmanaged networks.
* See {@code NetworkTemplate#OEM_MANAGED_*}.
* @return this builder.
*/
@NonNull
public Builder setOemManaged(@OemManaged int oemManaged) {
// Assert input does not contain illegal oemManage bits.
if ((~SUPPORTED_OEM_MANAGED_TYPES & oemManaged) != 0) {
throw new IllegalArgumentException("Invalid value for OemManaged : " + oemManaged);
}
mOemManaged = oemManaged;
return this;
}
/**
* Set the Subscription Id.
*
* @param subId the Subscription Id of the network. Or INVALID_SUBSCRIPTION_ID if not
* applicable.
* @return this builder.
*/
@NonNull
public Builder setSubId(int subId) {
mSubId = subId;
return this;
}
private void ensureValidParameters() {
// Assert non-mobile network cannot have a ratType.
if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) {
throw new IllegalArgumentException(
"Invalid ratType " + mRatType + " for type " + mType);
}
// Assert non-wifi network cannot have a wifi network key.
if (mType != TYPE_WIFI && mWifiNetworkKey != null) {
throw new IllegalArgumentException("Invalid wifi network key for type " + mType);
}
}
/**
* Builds the instance of the {@link NetworkIdentity}.
*
* @return the built instance of {@link NetworkIdentity}.
*/
@NonNull
public NetworkIdentity build() {
ensureValidParameters();
return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey,
mRoaming, mMetered, mDefaultNetwork, mOemManaged, mSubId);
}
}
}

View File

@@ -1,230 +0,0 @@
/*
* 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 android.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.NonNull;
import android.service.NetworkIdentitySetProto;
import android.util.proto.ProtoOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
* active on that interface.
*
* @hide
*/
public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_ROAMING = 2;
private static final int VERSION_ADD_NETWORK_ID = 3;
private static final int VERSION_ADD_METERED = 4;
private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
private static final int VERSION_ADD_SUB_ID = 7;
/**
* Construct a {@link NetworkIdentitySet} object.
*/
public NetworkIdentitySet() {
super();
}
/** @hide */
public NetworkIdentitySet(@NonNull Set<NetworkIdentity> ident) {
super(ident);
}
/** @hide */
public NetworkIdentitySet(DataInput in) throws IOException {
final int version = in.readInt();
final int size = in.readInt();
for (int i = 0; i < size; i++) {
if (version <= VERSION_INIT) {
final int ignored = in.readInt();
}
final int type = in.readInt();
final int ratType = in.readInt();
final String subscriberId = readOptionalString(in);
final String networkId;
if (version >= VERSION_ADD_NETWORK_ID) {
networkId = readOptionalString(in);
} else {
networkId = null;
}
final boolean roaming;
if (version >= VERSION_ADD_ROAMING) {
roaming = in.readBoolean();
} else {
roaming = false;
}
final boolean metered;
if (version >= VERSION_ADD_METERED) {
metered = in.readBoolean();
} else {
// If this is the old data and the type is mobile, treat it as metered. (Note that
// if this is a mobile network, TYPE_MOBILE is the only possible type that could be
// used.)
metered = (type == TYPE_MOBILE);
}
final boolean defaultNetwork;
if (version >= VERSION_ADD_DEFAULT_NETWORK) {
defaultNetwork = in.readBoolean();
} else {
defaultNetwork = true;
}
final int oemNetCapabilities;
if (version >= VERSION_ADD_OEM_MANAGED_NETWORK) {
oemNetCapabilities = in.readInt();
} else {
oemNetCapabilities = NetworkIdentity.OEM_NONE;
}
final int subId;
if (version >= VERSION_ADD_SUB_ID) {
subId = in.readInt();
} else {
subId = INVALID_SUBSCRIPTION_ID;
}
add(new NetworkIdentity(type, ratType, subscriberId, networkId, roaming, metered,
defaultNetwork, oemNetCapabilities, subId));
}
}
/**
* Method to serialize this object into a {@code DataOutput}.
* @hide
*/
public void writeToStream(DataOutput out) throws IOException {
out.writeInt(VERSION_ADD_SUB_ID);
out.writeInt(size());
for (NetworkIdentity ident : this) {
out.writeInt(ident.getType());
out.writeInt(ident.getRatType());
writeOptionalString(out, ident.getSubscriberId());
writeOptionalString(out, ident.getWifiNetworkKey());
out.writeBoolean(ident.isRoaming());
out.writeBoolean(ident.isMetered());
out.writeBoolean(ident.isDefaultNetwork());
out.writeInt(ident.getOemManaged());
out.writeInt(ident.getSubId());
}
}
/**
* @return whether any {@link NetworkIdentity} in this set is considered metered.
* @hide
*/
public boolean isAnyMemberMetered() {
if (isEmpty()) {
return false;
}
for (NetworkIdentity ident : this) {
if (ident.isMetered()) {
return true;
}
}
return false;
}
/**
* @return whether any {@link NetworkIdentity} in this set is considered roaming.
* @hide
*/
public boolean isAnyMemberRoaming() {
if (isEmpty()) {
return false;
}
for (NetworkIdentity ident : this) {
if (ident.isRoaming()) {
return true;
}
}
return false;
}
/**
* @return whether any {@link NetworkIdentity} in this set is considered on the default
* network.
* @hide
*/
public boolean areAllMembersOnDefaultNetwork() {
if (isEmpty()) {
return true;
}
for (NetworkIdentity ident : this) {
if (!ident.isDefaultNetwork()) {
return false;
}
}
return true;
}
private static void writeOptionalString(DataOutput out, String value) throws IOException {
if (value != null) {
out.writeByte(1);
out.writeUTF(value);
} else {
out.writeByte(0);
}
}
private static String readOptionalString(DataInput in) throws IOException {
if (in.readByte() != 0) {
return in.readUTF();
} else {
return null;
}
}
public static int compare(@NonNull NetworkIdentitySet left, @NonNull NetworkIdentitySet right) {
Objects.requireNonNull(left);
Objects.requireNonNull(right);
if (left.isEmpty()) return -1;
if (right.isEmpty()) return 1;
final NetworkIdentity leftIdent = left.iterator().next();
final NetworkIdentity rightIdent = right.iterator().next();
return NetworkIdentity.compare(leftIdent, rightIdent);
}
/**
* Method to dump this object into proto debug file.
* @hide
*/
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
for (NetworkIdentity ident : this) {
ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES);
}
proto.end(start);
}
}

View File

@@ -1,192 +0,0 @@
/*
* Copyright (C) 2021 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;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.net.module.util.NetworkIdentityUtils;
import java.util.Objects;
/**
* Snapshot of network state.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public final class NetworkStateSnapshot implements Parcelable {
/** The network associated with this snapshot. */
@NonNull
private final Network mNetwork;
/** The {@link NetworkCapabilities} of the network associated with this snapshot. */
@NonNull
private final NetworkCapabilities mNetworkCapabilities;
/** The {@link LinkProperties} of the network associated with this snapshot. */
@NonNull
private final LinkProperties mLinkProperties;
/**
* The Subscriber Id of the network associated with this snapshot. See
* {@link android.telephony.TelephonyManager#getSubscriberId()}.
*/
@Nullable
private final String mSubscriberId;
/**
* The legacy type of the network associated with this snapshot. See
* {@code ConnectivityManager#TYPE_*}.
*/
private final int mLegacyType;
public NetworkStateSnapshot(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
@Nullable String subscriberId, int legacyType) {
mNetwork = Objects.requireNonNull(network);
mNetworkCapabilities = Objects.requireNonNull(networkCapabilities);
mLinkProperties = Objects.requireNonNull(linkProperties);
mSubscriberId = subscriberId;
mLegacyType = legacyType;
}
/** @hide */
public NetworkStateSnapshot(@NonNull Parcel in) {
mNetwork = in.readParcelable(null);
mNetworkCapabilities = in.readParcelable(null);
mLinkProperties = in.readParcelable(null);
mSubscriberId = in.readString();
mLegacyType = in.readInt();
}
/** Get the network associated with this snapshot */
@NonNull
public Network getNetwork() {
return mNetwork;
}
/** Get {@link NetworkCapabilities} of the network associated with this snapshot. */
@NonNull
public NetworkCapabilities getNetworkCapabilities() {
return mNetworkCapabilities;
}
/** Get the {@link LinkProperties} of the network associated with this snapshot. */
@NonNull
public LinkProperties getLinkProperties() {
return mLinkProperties;
}
/**
* Get the Subscriber Id of the network associated with this snapshot.
* @deprecated Please use #getSubId, which doesn't return personally identifiable
* information.
*/
@Deprecated
@Nullable
public String getSubscriberId() {
return mSubscriberId;
}
/** Get the subId of the network associated with this snapshot. */
public int getSubId() {
if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
final NetworkSpecifier spec = mNetworkCapabilities.getNetworkSpecifier();
if (spec instanceof TelephonyNetworkSpecifier) {
return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
}
}
return INVALID_SUBSCRIPTION_ID;
}
/**
* Get the legacy type of the network associated with this snapshot.
* @return the legacy network type. See {@code ConnectivityManager#TYPE_*}.
*/
public int getLegacyType() {
return mLegacyType;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(mNetwork, flags);
out.writeParcelable(mNetworkCapabilities, flags);
out.writeParcelable(mLinkProperties, flags);
out.writeString(mSubscriberId);
out.writeInt(mLegacyType);
}
@NonNull
public static final Creator<NetworkStateSnapshot> CREATOR =
new Creator<NetworkStateSnapshot>() {
@NonNull
@Override
public NetworkStateSnapshot createFromParcel(@NonNull Parcel in) {
return new NetworkStateSnapshot(in);
}
@NonNull
@Override
public NetworkStateSnapshot[] newArray(int size) {
return new NetworkStateSnapshot[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NetworkStateSnapshot)) return false;
NetworkStateSnapshot that = (NetworkStateSnapshot) o;
return mLegacyType == that.mLegacyType
&& Objects.equals(mNetwork, that.mNetwork)
&& Objects.equals(mNetworkCapabilities, that.mNetworkCapabilities)
&& Objects.equals(mLinkProperties, that.mLinkProperties)
&& Objects.equals(mSubscriberId, that.mSubscriberId);
}
@Override
public int hashCode() {
return Objects.hash(mNetwork,
mNetworkCapabilities, mLinkProperties, mSubscriberId, mLegacyType);
}
@Override
public String toString() {
return "NetworkStateSnapshot{"
+ "network=" + mNetwork
+ ", networkCapabilities=" + mNetworkCapabilities
+ ", linkProperties=" + mLinkProperties
+ ", subscriberId='" + NetworkIdentityUtils.scrubSubscriberId(mSubscriberId) + '\''
+ ", legacyType=" + mLegacyType
+ '}';
}
}

View File

@@ -1,208 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
import android.Manifest;
import android.annotation.IntDef;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Utility methods for controlling access to network stats APIs.
*
* @hide
*/
public final class NetworkStatsAccess {
private NetworkStatsAccess() {}
/**
* Represents an access level for the network usage history and statistics APIs.
*
* <p>Access levels are in increasing order; that is, it is reasonable to check access by
* verifying that the caller's access level is at least the minimum required level.
*/
@IntDef({
Level.DEFAULT,
Level.USER,
Level.DEVICESUMMARY,
Level.DEVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Level {
/**
* Default, unprivileged access level.
*
* <p>Can only access usage for one's own UID.
*
* <p>Every app will have at least this access level.
*/
int DEFAULT = 0;
/**
* Access level for apps which can access usage for any app running in the same user.
*
* <p>Granted to:
* <ul>
* <li>Profile owners.
* </ul>
*/
int USER = 1;
/**
* Access level for apps which can access usage summary of device. Device summary includes
* usage by apps running in any profiles/users, however this access level does not
* allow querying usage of individual apps running in other profiles/users.
*
* <p>Granted to:
* <ul>
* <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit
* so it is not necessarily sufficient to declare this in the manifest.
* <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission.
* </ul>
*/
int DEVICESUMMARY = 2;
/**
* Access level for apps which can access usage for any app on the device, including apps
* running on other users/profiles.
*
* <p>Granted to:
* <ul>
* <li>Device owners.
* <li>Carrier-privileged applications.
* <li>The system UID.
* </ul>
*/
int DEVICE = 3;
}
/** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
public static @NetworkStatsAccess.Level int checkAccessLevel(
Context context, int callingPid, int callingUid, String callingPackage) {
final DevicePolicyManager mDpm = context.getSystemService(DevicePolicyManager.class);
final TelephonyManager tm = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
boolean hasCarrierPrivileges;
final long token = Binder.clearCallingIdentity();
try {
hasCarrierPrivileges = tm != null
&& tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
} finally {
Binder.restoreCallingIdentity(token);
}
final boolean isDeviceOwner = mDpm != null && mDpm.isDeviceOwnerApp(callingPackage);
final int appId = UserHandle.getAppId(callingUid);
final boolean isNetworkStack = context.checkPermission(
android.Manifest.permission.NETWORK_STACK, callingPid, callingUid)
== PERMISSION_GRANTED;
if (hasCarrierPrivileges || isDeviceOwner
|| appId == Process.SYSTEM_UID || isNetworkStack) {
// Carrier-privileged apps and device owners, and the system (including the
// network stack) can access data usage for all apps on the device.
return NetworkStatsAccess.Level.DEVICE;
}
boolean hasAppOpsPermission = hasAppOpsPermission(context, callingUid, callingPackage);
if (hasAppOpsPermission || context.checkCallingOrSelfPermission(
READ_NETWORK_USAGE_HISTORY) == PackageManager.PERMISSION_GRANTED) {
return NetworkStatsAccess.Level.DEVICESUMMARY;
}
//TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
boolean isProfileOwner = mDpm != null && (mDpm.isProfileOwnerApp(callingPackage)
|| mDpm.isDeviceOwnerApp(callingPackage));
if (isProfileOwner) {
// Apps with the AppOps permission, profile owners, and apps with the privileged
// permission can access data usage for all apps in this user/profile.
return NetworkStatsAccess.Level.USER;
}
// Everyone else gets default access (only to their own UID).
return NetworkStatsAccess.Level.DEFAULT;
}
/**
* Returns whether the given caller should be able to access the given UID when the caller has
* the given {@link NetworkStatsAccess.Level}.
*/
public static boolean isAccessibleToUser(int uid, int callerUid,
@NetworkStatsAccess.Level int accessLevel) {
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
switch (accessLevel) {
case NetworkStatsAccess.Level.DEVICE:
// Device-level access - can access usage for any uid.
return true;
case NetworkStatsAccess.Level.DEVICESUMMARY:
// Can access usage for any app running in the same user, along
// with some special uids (system, removed, or tethering) and
// anonymized uids
return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
|| uid == UID_TETHERING || uid == UID_ALL
|| userId == callerUserId;
case NetworkStatsAccess.Level.USER:
// User-level access - can access usage for any app running in the same user, along
// with some special uids (system, removed, or tethering).
return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
|| uid == UID_TETHERING
|| userId == callerUserId;
case NetworkStatsAccess.Level.DEFAULT:
default:
// Default access level - can only access one's own usage.
return uid == callerUid;
}
}
private static boolean hasAppOpsPermission(
Context context, int callingUid, String callingPackage) {
if (callingPackage != null) {
AppOpsManager appOps = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
final int mode = appOps.noteOp(AppOpsManager.OPSTR_GET_USAGE_STATS,
callingUid, callingPackage, null /* attributionTag */, null /* message */);
if (mode == AppOpsManager.MODE_DEFAULT) {
// The default behavior here is to check if PackageManager has given the app
// permission.
final int permissionCheck = context.checkCallingPermission(
Manifest.permission.PACKAGE_USAGE_STATS);
return permissionCheck == PackageManager.PERMISSION_GRANTED;
}
return (mode == AppOpsManager.MODE_ALLOWED);
}
return false;
}
}

View File

@@ -1,956 +0,0 @@
/*
* Copyright (C) 2012 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;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
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.METERED_NO;
import static android.net.NetworkStats.METERED_YES;
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.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.NetworkStats.State;
import android.net.NetworkStatsHistory.Entry;
import android.os.Binder;
import android.service.NetworkStatsCollectionKeyProto;
import android.service.NetworkStatsCollectionProto;
import android.service.NetworkStatsCollectionStatsProto;
import android.telephony.SubscriptionPlan;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Range;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FileRotator;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStatsUtils;
import libcore.io.IoUtils;
import java.io.BufferedInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ProtocolException;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* Collection of {@link NetworkStatsHistory}, stored based on combined key of
* {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
private static final String TAG = NetworkStatsCollection.class.getSimpleName();
/** File header magic number: "ANET" */
private static final int FILE_MAGIC = 0x414E4554;
private static final int VERSION_NETWORK_INIT = 1;
private static final int VERSION_UID_INIT = 1;
private static final int VERSION_UID_WITH_IDENT = 2;
private static final int VERSION_UID_WITH_TAG = 3;
private static final int VERSION_UID_WITH_SET = 4;
private static final int VERSION_UNIFIED_INIT = 16;
private ArrayMap<Key, NetworkStatsHistory> mStats = new ArrayMap<>();
private final long mBucketDurationMillis;
private long mStartMillis;
private long mEndMillis;
private long mTotalBytes;
private boolean mDirty;
/**
* Construct a {@link NetworkStatsCollection} object.
*
* @param bucketDuration duration of the buckets in this object, in milliseconds.
* @hide
*/
public NetworkStatsCollection(long bucketDurationMillis) {
mBucketDurationMillis = bucketDurationMillis;
reset();
}
/** @hide */
public void clear() {
reset();
}
/** @hide */
public void reset() {
mStats.clear();
mStartMillis = Long.MAX_VALUE;
mEndMillis = Long.MIN_VALUE;
mTotalBytes = 0;
mDirty = false;
}
/** @hide */
public long getStartMillis() {
return mStartMillis;
}
/**
* Return first atomic bucket in this collection, which is more conservative
* than {@link #mStartMillis}.
* @hide
*/
public long getFirstAtomicBucketMillis() {
if (mStartMillis == Long.MAX_VALUE) {
return Long.MAX_VALUE;
} else {
return mStartMillis + mBucketDurationMillis;
}
}
/** @hide */
public long getEndMillis() {
return mEndMillis;
}
/** @hide */
public long getTotalBytes() {
return mTotalBytes;
}
/** @hide */
public boolean isDirty() {
return mDirty;
}
/** @hide */
public void clearDirty() {
mDirty = false;
}
/** @hide */
public boolean isEmpty() {
return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
}
/** @hide */
@VisibleForTesting
public long roundUp(long time) {
if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
|| time == SubscriptionPlan.TIME_UNKNOWN) {
return time;
} else {
final long mod = time % mBucketDurationMillis;
if (mod > 0) {
time -= mod;
time += mBucketDurationMillis;
}
return time;
}
}
/** @hide */
@VisibleForTesting
public long roundDown(long time) {
if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
|| time == SubscriptionPlan.TIME_UNKNOWN) {
return time;
} else {
final long mod = time % mBucketDurationMillis;
if (mod > 0) {
time -= mod;
}
return time;
}
}
/** @hide */
public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
return getRelevantUids(accessLevel, Binder.getCallingUid());
}
/** @hide */
public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
final int callerUid) {
final ArrayList<Integer> uids = new ArrayList<>();
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
int j = Collections.binarySearch(uids, new Integer(key.uid));
if (j < 0) {
j = ~j;
uids.add(j, key.uid);
}
}
}
return CollectionUtils.toIntArray(uids);
}
/**
* Combine all {@link NetworkStatsHistory} in this collection which match
* the requested parameters.
* @hide
*/
public NetworkStatsHistory getHistory(NetworkTemplate template, SubscriptionPlan augmentPlan,
int uid, int set, int tag, int fields, long start, long end,
@NetworkStatsAccess.Level int accessLevel, int callerUid) {
if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
throw new SecurityException("Network stats history of uid " + uid
+ " is forbidden for caller " + callerUid);
}
// 180 days of history should be enough for anyone; if we end up needing
// more, we'll dynamically grow the history object.
final int bucketEstimate = (int) NetworkStatsUtils.constrain(
((end - start) / mBucketDurationMillis), 0,
(180 * DateUtils.DAY_IN_MILLIS) / mBucketDurationMillis);
final NetworkStatsHistory combined = new NetworkStatsHistory(
mBucketDurationMillis, bucketEstimate, fields);
// shortcut when we know stats will be empty
if (start == end) return combined;
// Figure out the window of time that we should be augmenting (if any)
long augmentStart = SubscriptionPlan.TIME_UNKNOWN;
long augmentEnd = (augmentPlan != null) ? augmentPlan.getDataUsageTime()
: SubscriptionPlan.TIME_UNKNOWN;
// And if augmenting, we might need to collect more data to adjust with
long collectStart = start;
long collectEnd = end;
if (augmentEnd != SubscriptionPlan.TIME_UNKNOWN) {
final Iterator<Range<ZonedDateTime>> it = augmentPlan.cycleIterator();
while (it.hasNext()) {
final Range<ZonedDateTime> cycle = it.next();
final long cycleStart = cycle.getLower().toInstant().toEpochMilli();
final long cycleEnd = cycle.getUpper().toInstant().toEpochMilli();
if (cycleStart <= augmentEnd && augmentEnd < cycleEnd) {
augmentStart = cycleStart;
collectStart = Long.min(collectStart, augmentStart);
collectEnd = Long.max(collectEnd, augmentEnd);
break;
}
}
}
if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) {
// Shrink augmentation window so we don't risk undercounting.
augmentStart = roundUp(augmentStart);
augmentEnd = roundDown(augmentEnd);
// Grow collection window so we get all the stats needed.
collectStart = roundDown(collectStart);
collectEnd = roundUp(collectEnd);
}
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
&& templateMatches(template, key.ident)) {
final NetworkStatsHistory value = mStats.valueAt(i);
combined.recordHistory(value, collectStart, collectEnd);
}
}
if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) {
final NetworkStatsHistory.Entry entry = combined.getValues(
augmentStart, augmentEnd, null);
// If we don't have any recorded data for this time period, give
// ourselves something to scale with.
if (entry.rxBytes == 0 || entry.txBytes == 0) {
combined.recordData(augmentStart, augmentEnd,
new NetworkStats.Entry(1, 0, 1, 0, 0));
combined.getValues(augmentStart, augmentEnd, entry);
}
final long rawBytes = (entry.rxBytes + entry.txBytes) == 0 ? 1 :
(entry.rxBytes + entry.txBytes);
final long rawRxBytes = entry.rxBytes == 0 ? 1 : entry.rxBytes;
final long rawTxBytes = entry.txBytes == 0 ? 1 : entry.txBytes;
final long targetBytes = augmentPlan.getDataUsageBytes();
final long targetRxBytes = multiplySafeByRational(targetBytes, rawRxBytes, rawBytes);
final long targetTxBytes = multiplySafeByRational(targetBytes, rawTxBytes, rawBytes);
// Scale all matching buckets to reach anchor target
final long beforeTotal = combined.getTotalBytes();
for (int i = 0; i < combined.size(); i++) {
combined.getValues(i, entry);
if (entry.bucketStart >= augmentStart
&& entry.bucketStart + entry.bucketDuration <= augmentEnd) {
entry.rxBytes = multiplySafeByRational(
targetRxBytes, entry.rxBytes, rawRxBytes);
entry.txBytes = multiplySafeByRational(
targetTxBytes, entry.txBytes, rawTxBytes);
// We purposefully clear out packet counters to indicate
// that this data has been augmented.
entry.rxPackets = 0;
entry.txPackets = 0;
combined.setValues(i, entry);
}
}
final long deltaTotal = combined.getTotalBytes() - beforeTotal;
if (deltaTotal != 0) {
Log.d(TAG, "Augmented network usage by " + deltaTotal + " bytes");
}
// Finally we can slice data as originally requested
final NetworkStatsHistory sliced = new NetworkStatsHistory(
mBucketDurationMillis, bucketEstimate, fields);
sliced.recordHistory(combined, start, end);
return sliced;
} else {
return combined;
}
}
/**
* Summarize all {@link NetworkStatsHistory} in this collection which match
* the requested parameters across the requested range.
*
* @param template - a predicate for filtering netstats.
* @param start - start of the range, timestamp in milliseconds since the epoch.
* @param end - end of the range, timestamp in milliseconds since the epoch.
* @param accessLevel - caller access level.
* @param callerUid - caller UID.
* @hide
*/
public NetworkStats getSummary(NetworkTemplate template, long start, long end,
@NetworkStatsAccess.Level int accessLevel, int callerUid) {
final long now = System.currentTimeMillis();
final NetworkStats stats = new NetworkStats(end - start, 24);
// shortcut when we know stats will be empty
if (start == end) return stats;
final NetworkStats.Entry entry = new NetworkStats.Entry();
NetworkStatsHistory.Entry historyEntry = null;
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
if (templateMatches(template, key.ident)
&& NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)
&& key.set < NetworkStats.SET_DEBUG_START) {
final NetworkStatsHistory value = mStats.valueAt(i);
historyEntry = value.getValues(start, end, now, historyEntry);
entry.iface = IFACE_ALL;
entry.uid = key.uid;
entry.set = key.set;
entry.tag = key.tag;
entry.defaultNetwork = key.ident.areAllMembersOnDefaultNetwork()
? DEFAULT_NETWORK_YES : DEFAULT_NETWORK_NO;
entry.metered = key.ident.isAnyMemberMetered() ? METERED_YES : METERED_NO;
entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
entry.rxBytes = historyEntry.rxBytes;
entry.rxPackets = historyEntry.rxPackets;
entry.txBytes = historyEntry.txBytes;
entry.txPackets = historyEntry.txPackets;
entry.operations = historyEntry.operations;
if (!entry.isEmpty()) {
stats.combineValues(entry);
}
}
}
return stats;
}
/**
* Record given {@link android.net.NetworkStats.Entry} into this collection.
* @hide
*/
public void recordData(NetworkIdentitySet ident, int uid, int set, int tag, long start,
long end, NetworkStats.Entry entry) {
final NetworkStatsHistory history = findOrCreateHistory(ident, uid, set, tag);
history.recordData(start, end, entry);
noteRecordedHistory(history.getStart(), history.getEnd(), entry.rxBytes + entry.txBytes);
}
/**
* Record given {@link NetworkStatsHistory} into this collection.
*
* @hide
*/
public void recordHistory(@NonNull Key key, @NonNull NetworkStatsHistory history) {
Objects.requireNonNull(key);
Objects.requireNonNull(history);
if (history.size() == 0) return;
noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes());
NetworkStatsHistory target = mStats.get(key);
if (target == null) {
target = new NetworkStatsHistory(history.getBucketDuration());
mStats.put(key, target);
}
target.recordEntireHistory(history);
}
/**
* Record all {@link NetworkStatsHistory} contained in the given collection
* into this collection.
*
* @hide
*/
public void recordCollection(@NonNull NetworkStatsCollection another) {
Objects.requireNonNull(another);
for (int i = 0; i < another.mStats.size(); i++) {
final Key key = another.mStats.keyAt(i);
final NetworkStatsHistory value = another.mStats.valueAt(i);
recordHistory(key, value);
}
}
private NetworkStatsHistory findOrCreateHistory(
NetworkIdentitySet ident, int uid, int set, int tag) {
final Key key = new Key(ident, uid, set, tag);
final NetworkStatsHistory existing = mStats.get(key);
// update when no existing, or when bucket duration changed
NetworkStatsHistory updated = null;
if (existing == null) {
updated = new NetworkStatsHistory(mBucketDurationMillis, 10);
} else if (existing.getBucketDuration() != mBucketDurationMillis) {
updated = new NetworkStatsHistory(existing, mBucketDurationMillis);
}
if (updated != null) {
mStats.put(key, updated);
return updated;
} else {
return existing;
}
}
/** @hide */
@Override
public void read(InputStream in) throws IOException {
read((DataInput) new DataInputStream(in));
}
private void read(DataInput in) throws IOException {
// verify file magic header intact
final int magic = in.readInt();
if (magic != FILE_MAGIC) {
throw new ProtocolException("unexpected magic: " + magic);
}
final int version = in.readInt();
switch (version) {
case VERSION_UNIFIED_INIT: {
// uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
final int identSize = in.readInt();
for (int i = 0; i < identSize; i++) {
final NetworkIdentitySet ident = new NetworkIdentitySet(in);
final int size = in.readInt();
for (int j = 0; j < size; j++) {
final int uid = in.readInt();
final int set = in.readInt();
final int tag = in.readInt();
final Key key = new Key(ident, uid, set, tag);
final NetworkStatsHistory history = new NetworkStatsHistory(in);
recordHistory(key, history);
}
}
break;
}
default: {
throw new ProtocolException("unexpected version: " + version);
}
}
}
/** @hide */
@Override
public void write(OutputStream out) throws IOException {
write((DataOutput) new DataOutputStream(out));
out.flush();
}
private void write(DataOutput out) throws IOException {
// cluster key lists grouped by ident
final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = new HashMap<>();
for (Key key : mStats.keySet()) {
ArrayList<Key> keys = keysByIdent.get(key.ident);
if (keys == null) {
keys = new ArrayList<>();
keysByIdent.put(key.ident, keys);
}
keys.add(key);
}
out.writeInt(FILE_MAGIC);
out.writeInt(VERSION_UNIFIED_INIT);
out.writeInt(keysByIdent.size());
for (NetworkIdentitySet ident : keysByIdent.keySet()) {
final ArrayList<Key> keys = keysByIdent.get(ident);
ident.writeToStream(out);
out.writeInt(keys.size());
for (Key key : keys) {
final NetworkStatsHistory history = mStats.get(key);
out.writeInt(key.uid);
out.writeInt(key.set);
out.writeInt(key.tag);
history.writeToStream(out);
}
}
}
/**
* Read legacy network summary statistics file format into the collection,
* See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
*
* @deprecated
* @hide
*/
@Deprecated
public void readLegacyNetwork(File file) throws IOException {
final AtomicFile inputFile = new AtomicFile(file);
DataInputStream in = null;
try {
in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
// verify file magic header intact
final int magic = in.readInt();
if (magic != FILE_MAGIC) {
throw new ProtocolException("unexpected magic: " + magic);
}
final int version = in.readInt();
switch (version) {
case VERSION_NETWORK_INIT: {
// network := size *(NetworkIdentitySet NetworkStatsHistory)
final int size = in.readInt();
for (int i = 0; i < size; i++) {
final NetworkIdentitySet ident = new NetworkIdentitySet(in);
final NetworkStatsHistory history = new NetworkStatsHistory(in);
final Key key = new Key(ident, UID_ALL, SET_ALL, TAG_NONE);
recordHistory(key, history);
}
break;
}
default: {
throw new ProtocolException("unexpected version: " + version);
}
}
} catch (FileNotFoundException e) {
// missing stats is okay, probably first boot
} finally {
IoUtils.closeQuietly(in);
}
}
/**
* Read legacy Uid statistics file format into the collection,
* See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
*
* @deprecated
* @hide
*/
@Deprecated
public void readLegacyUid(File file, boolean onlyTags) throws IOException {
final AtomicFile inputFile = new AtomicFile(file);
DataInputStream in = null;
try {
in = new DataInputStream(new BufferedInputStream(inputFile.openRead()));
// verify file magic header intact
final int magic = in.readInt();
if (magic != FILE_MAGIC) {
throw new ProtocolException("unexpected magic: " + magic);
}
final int version = in.readInt();
switch (version) {
case VERSION_UID_INIT: {
// uid := size *(UID NetworkStatsHistory)
// drop this data version, since we don't have a good
// mapping into NetworkIdentitySet.
break;
}
case VERSION_UID_WITH_IDENT: {
// uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
// drop this data version, since this version only existed
// for a short time.
break;
}
case VERSION_UID_WITH_TAG:
case VERSION_UID_WITH_SET: {
// uid := size *(NetworkIdentitySet size *(uid set tag NetworkStatsHistory))
final int identSize = in.readInt();
for (int i = 0; i < identSize; i++) {
final NetworkIdentitySet ident = new NetworkIdentitySet(in);
final int size = in.readInt();
for (int j = 0; j < size; j++) {
final int uid = in.readInt();
final int set = (version >= VERSION_UID_WITH_SET) ? in.readInt()
: SET_DEFAULT;
final int tag = in.readInt();
final Key key = new Key(ident, uid, set, tag);
final NetworkStatsHistory history = new NetworkStatsHistory(in);
if ((tag == TAG_NONE) != onlyTags) {
recordHistory(key, history);
}
}
}
break;
}
default: {
throw new ProtocolException("unexpected version: " + version);
}
}
} catch (FileNotFoundException e) {
// missing stats is okay, probably first boot
} finally {
IoUtils.closeQuietly(in);
}
}
/**
* Remove any {@link NetworkStatsHistory} attributed to the requested UID,
* moving any {@link NetworkStats#TAG_NONE} series to
* {@link TrafficStats#UID_REMOVED}.
* @hide
*/
public void removeUids(int[] uids) {
final ArrayList<Key> knownKeys = new ArrayList<>();
knownKeys.addAll(mStats.keySet());
// migrate all UID stats into special "removed" bucket
for (Key key : knownKeys) {
if (CollectionUtils.contains(uids, key.uid)) {
// only migrate combined TAG_NONE history
if (key.tag == TAG_NONE) {
final NetworkStatsHistory uidHistory = mStats.get(key);
final NetworkStatsHistory removedHistory = findOrCreateHistory(
key.ident, UID_REMOVED, SET_DEFAULT, TAG_NONE);
removedHistory.recordEntireHistory(uidHistory);
}
mStats.remove(key);
mDirty = true;
}
}
}
private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
if (startMillis < mStartMillis) mStartMillis = startMillis;
if (endMillis > mEndMillis) mEndMillis = endMillis;
mTotalBytes += totalBytes;
mDirty = true;
}
private int estimateBuckets() {
return (int) (Math.min(mEndMillis - mStartMillis, WEEK_IN_MILLIS * 5)
/ mBucketDurationMillis);
}
private ArrayList<Key> getSortedKeys() {
final ArrayList<Key> keys = new ArrayList<>();
keys.addAll(mStats.keySet());
Collections.sort(keys, (left, right) -> Key.compare(left, right));
return keys;
}
/** @hide */
public void dump(IndentingPrintWriter pw) {
for (Key key : getSortedKeys()) {
pw.print("ident="); pw.print(key.ident.toString());
pw.print(" uid="); pw.print(key.uid);
pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
pw.print(" tag="); pw.println(NetworkStats.tagToString(key.tag));
final NetworkStatsHistory history = mStats.get(key);
pw.increaseIndent();
history.dump(pw, true);
pw.decreaseIndent();
}
}
/** @hide */
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
for (Key key : getSortedKeys()) {
final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
// Key
final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY);
proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
proto.end(startKey);
// Value
final NetworkStatsHistory history = mStats.get(key);
history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY);
proto.end(startStats);
}
proto.end(start);
}
/** @hide */
public void dumpCheckin(PrintWriter pw, long start, long end) {
dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateEthernet(), "eth");
dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateBluetooth(), "bt");
}
/**
* Dump all contained stats that match requested parameters, but group
* together all matching {@link NetworkTemplate} under a single prefix.
*/
private void dumpCheckin(PrintWriter pw, long start, long end, NetworkTemplate groupTemplate,
String groupPrefix) {
final ArrayMap<Key, NetworkStatsHistory> grouped = new ArrayMap<>();
// Walk through all history, grouping by matching network templates
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
final NetworkStatsHistory value = mStats.valueAt(i);
if (!templateMatches(groupTemplate, key.ident)) continue;
if (key.set >= NetworkStats.SET_DEBUG_START) continue;
final Key groupKey = new Key(null, key.uid, key.set, key.tag);
NetworkStatsHistory groupHistory = grouped.get(groupKey);
if (groupHistory == null) {
groupHistory = new NetworkStatsHistory(value.getBucketDuration());
grouped.put(groupKey, groupHistory);
}
groupHistory.recordHistory(value, start, end);
}
for (int i = 0; i < grouped.size(); i++) {
final Key key = grouped.keyAt(i);
final NetworkStatsHistory value = grouped.valueAt(i);
if (value.size() == 0) continue;
pw.print("c,");
pw.print(groupPrefix); pw.print(',');
pw.print(key.uid); pw.print(',');
pw.print(NetworkStats.setToCheckinString(key.set)); pw.print(',');
pw.print(key.tag);
pw.println();
value.dumpCheckin(pw);
}
}
/**
* Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
* in the given {@link NetworkIdentitySet}.
*/
private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
for (NetworkIdentity ident : identSet) {
if (template.matches(ident)) {
return true;
}
}
return false;
}
/**
* Get the all historical stats of the collection {@link NetworkStatsCollection}.
*
* @return All {@link NetworkStatsHistory} in this collection.
*/
@NonNull
public Map<Key, NetworkStatsHistory> getEntries() {
return new ArrayMap(mStats);
}
/**
* Builder class for {@link NetworkStatsCollection}.
*/
public static final class Builder {
private final long mBucketDurationMillis;
private final ArrayMap<Key, NetworkStatsHistory> mEntries = new ArrayMap<>();
/**
* Creates a new Builder with given bucket duration.
*
* @param bucketDuration Duration of the buckets of the object, in milliseconds.
*/
public Builder(long bucketDurationMillis) {
mBucketDurationMillis = bucketDurationMillis;
}
/**
* Add association of the history with the specified key in this map.
*
* @param key The object used to identify a network, see {@link Key}.
* @param history {@link NetworkStatsHistory} instance associated to the given {@link Key}.
* @return The builder object.
*/
@NonNull
public NetworkStatsCollection.Builder addEntry(@NonNull Key key,
@NonNull NetworkStatsHistory history) {
Objects.requireNonNull(key);
Objects.requireNonNull(history);
final List<Entry> historyEntries = history.getEntries();
final NetworkStatsHistory.Builder historyBuilder =
new NetworkStatsHistory.Builder(mBucketDurationMillis, historyEntries.size());
for (Entry entry : historyEntries) {
historyBuilder.addEntry(entry);
}
mEntries.put(key, historyBuilder.build());
return this;
}
/**
* Builds the instance of the {@link NetworkStatsCollection}.
*
* @return the built instance of {@link NetworkStatsCollection}.
*/
@NonNull
public NetworkStatsCollection build() {
final NetworkStatsCollection collection =
new NetworkStatsCollection(mBucketDurationMillis);
for (int i = 0; i < mEntries.size(); i++) {
collection.recordHistory(mEntries.keyAt(i), mEntries.valueAt(i));
}
return collection;
}
}
/**
* the identifier that associate with the {@link NetworkStatsHistory} object to identify
* a certain record in the {@link NetworkStatsCollection} object.
*/
public static final class Key {
/** @hide */
public final NetworkIdentitySet ident;
/** @hide */
public final int uid;
/** @hide */
public final int set;
/** @hide */
public final int tag;
private final int mHashCode;
/**
* Construct a {@link Key} object.
*
* @param ident a Set of {@link NetworkIdentity} that associated with the record.
* @param uid Uid of the record.
* @param set Set of the record, see {@code NetworkStats#SET_*}.
* @param tag Tag of the record, see {@link TrafficStats#setThreadStatsTag(int)}.
*/
public Key(@NonNull Set<NetworkIdentity> ident, int uid, @State int set, int tag) {
this(new NetworkIdentitySet(Objects.requireNonNull(ident)), uid, set, tag);
}
/** @hide */
public Key(@NonNull NetworkIdentitySet ident, int uid, int set, int tag) {
this.ident = Objects.requireNonNull(ident);
this.uid = uid;
this.set = set;
this.tag = tag;
mHashCode = Objects.hash(ident, uid, set, tag);
}
@Override
public int hashCode() {
return mHashCode;
}
@Override
public boolean equals(@Nullable Object obj) {
if (obj instanceof Key) {
final Key key = (Key) obj;
return uid == key.uid && set == key.set && tag == key.tag
&& Objects.equals(ident, key.ident);
}
return false;
}
/** @hide */
public static int compare(@NonNull Key left, @NonNull Key right) {
Objects.requireNonNull(left);
Objects.requireNonNull(right);
int res = 0;
if (left.ident != null && right.ident != null) {
res = NetworkIdentitySet.compare(left.ident, right.ident);
}
if (res == 0) {
res = Integer.compare(left.uid, right.uid);
}
if (res == 0) {
res = Integer.compare(left.set, right.set);
}
if (res == 0) {
res = Integer.compare(left.tag, right.tag);
}
return res;
}
}
}

View File

@@ -1,19 +0,0 @@
/**
* 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 android.net;
parcelable NetworkStatsHistory;

View File

@@ -1,19 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
parcelable UnderlyingNetworkInfo;

View File

@@ -1,135 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* A lightweight container used to carry information on the networks that underly a given
* virtual network.
*
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public final class UnderlyingNetworkInfo implements Parcelable {
/** The owner of this network. */
private final int mOwnerUid;
/** The interface name of this network. */
@NonNull
private final String mIface;
/** The names of the interfaces underlying this network. */
@NonNull
private final List<String> mUnderlyingIfaces;
public UnderlyingNetworkInfo(int ownerUid, @NonNull String iface,
@NonNull List<String> underlyingIfaces) {
Objects.requireNonNull(iface);
Objects.requireNonNull(underlyingIfaces);
mOwnerUid = ownerUid;
mIface = iface;
mUnderlyingIfaces = Collections.unmodifiableList(new ArrayList<>(underlyingIfaces));
}
private UnderlyingNetworkInfo(@NonNull Parcel in) {
mOwnerUid = in.readInt();
mIface = in.readString();
List<String> underlyingIfaces = new ArrayList<>();
in.readList(underlyingIfaces, null /*classLoader*/);
mUnderlyingIfaces = Collections.unmodifiableList(underlyingIfaces);
}
/** Get the owner of this network. */
public int getOwnerUid() {
return mOwnerUid;
}
/** Get the interface name of this network. */
@NonNull
public String getInterface() {
return mIface;
}
/** Get the names of the interfaces underlying this network. */
@NonNull
public List<String> getUnderlyingInterfaces() {
return mUnderlyingIfaces;
}
@Override
public String toString() {
return "UnderlyingNetworkInfo{"
+ "ownerUid=" + mOwnerUid
+ ", iface='" + mIface + '\''
+ ", underlyingIfaces='" + mUnderlyingIfaces.toString() + '\''
+ '}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mOwnerUid);
dest.writeString(mIface);
dest.writeList(mUnderlyingIfaces);
}
@NonNull
public static final Parcelable.Creator<UnderlyingNetworkInfo> CREATOR =
new Parcelable.Creator<UnderlyingNetworkInfo>() {
@NonNull
@Override
public UnderlyingNetworkInfo createFromParcel(@NonNull Parcel in) {
return new UnderlyingNetworkInfo(in);
}
@NonNull
@Override
public UnderlyingNetworkInfo[] newArray(int size) {
return new UnderlyingNetworkInfo[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof UnderlyingNetworkInfo)) return false;
final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
return mOwnerUid == that.getOwnerUid()
&& Objects.equals(mIface, that.getInterface())
&& Objects.equals(mUnderlyingIfaces, that.getUnderlyingInterfaces());
}
@Override
public int hashCode() {
return Objects.hash(mOwnerUid, mIface, mUnderlyingIfaces);
}
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (C) 2022 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.netstats;
import android.net.DataUsageRequest;
/**
* Interface for NetworkStatsService to notify events to the callers of registerUsageCallback.
*
* @hide
*/
oneway interface IUsageCallback {
void onThresholdReached(in DataUsageRequest request);
void onCallbackReleased(in DataUsageRequest request);
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (C) 2020 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.netstats.provider;
/**
* Interface for NetworkStatsService to query network statistics and set data limits.
*
* @hide
*/
oneway interface INetworkStatsProvider {
void onRequestStatsUpdate(int token);
void onSetAlert(long quotaBytes);
void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes);
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright (C) 2020 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.netstats.provider;
import android.net.NetworkStats;
/**
* Interface for implementor of {@link INetworkStatsProviderCallback} to push events
* such as network statistics update or notify limit reached.
* @hide
*/
oneway interface INetworkStatsProviderCallback {
void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
void notifyAlertReached();
void notifyWarningReached();
void notifyLimitReached();
void unregister();
}

View File

@@ -1,232 +0,0 @@
/*
* Copyright (C) 2020 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.netstats.provider;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.NetworkStats;
import android.os.RemoteException;
/**
* A base class that allows external modules to implement a custom network statistics provider.
* @hide
*/
@SystemApi
public abstract class NetworkStatsProvider {
/**
* A value used by {@link #onSetLimit}, {@link #onSetAlert} and {@link #onSetWarningAndLimit}
* indicates there is no limit.
*/
public static final int QUOTA_UNLIMITED = -1;
@NonNull private final INetworkStatsProvider mProviderBinder =
new INetworkStatsProvider.Stub() {
@Override
public void onRequestStatsUpdate(int token) {
NetworkStatsProvider.this.onRequestStatsUpdate(token);
}
@Override
public void onSetAlert(long quotaBytes) {
NetworkStatsProvider.this.onSetAlert(quotaBytes);
}
@Override
public void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes) {
NetworkStatsProvider.this.onSetWarningAndLimit(iface, warningBytes, limitBytes);
}
};
// The binder given by the service when successfully registering. Only null before registering,
// never null once non-null.
@Nullable
private INetworkStatsProviderCallback mProviderCbBinder;
/**
* Return the binder invoked by the service and redirect function calls to the overridden
* methods.
* @hide
*/
@NonNull
public INetworkStatsProvider getProviderBinder() {
return mProviderBinder;
}
/**
* Store the binder that was returned by the service when successfully registering. Note that
* the provider cannot be re-registered. Hence this method can only be called once per provider.
*
* @hide
*/
public void setProviderCallbackBinder(@NonNull INetworkStatsProviderCallback binder) {
if (mProviderCbBinder != null) {
throw new IllegalArgumentException("provider is already registered");
}
mProviderCbBinder = binder;
}
/**
* Get the binder that was returned by the service when successfully registering. Or null if the
* provider was never registered.
*
* @hide
*/
@Nullable
public INetworkStatsProviderCallback getProviderCallbackBinder() {
return mProviderCbBinder;
}
/**
* Get the binder that was returned by the service when successfully registering. Throw an
* {@link IllegalStateException} if the provider is not registered.
*
* @hide
*/
@NonNull
public INetworkStatsProviderCallback getProviderCallbackBinderOrThrow() {
if (mProviderCbBinder == null) {
throw new IllegalStateException("the provider is not registered");
}
return mProviderCbBinder;
}
/**
* Notify the system of new network statistics.
*
* Send the network statistics recorded since the last call to {@link #notifyStatsUpdated}. Must
* be called as soon as possible after {@link NetworkStatsProvider#onRequestStatsUpdate(int)}
* being called. Responding later increases the probability stats will be dropped. The
* provider can also call this whenever it wants to reports new stats for any reason.
* Note that the system will not necessarily immediately propagate the statistics to
* reflect the update.
*
* @param token the token under which these stats were gathered. Providers can call this method
* with the current token as often as they want, until the token changes.
* {@see NetworkStatsProvider#onRequestStatsUpdate()}
* @param ifaceStats the {@link NetworkStats} per interface to be reported.
* The provider should not include any traffic that is already counted by
* kernel interface counters.
* @param uidStats the same stats as above, but counts {@link NetworkStats}
* per uid.
*/
public void notifyStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
@NonNull NetworkStats uidStats) {
try {
getProviderCallbackBinderOrThrow().notifyStatsUpdated(token, ifaceStats, uidStats);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Notify system that the quota set by {@code onSetAlert} has been reached.
*/
public void notifyAlertReached() {
try {
getProviderCallbackBinderOrThrow().notifyAlertReached();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Notify system that the warning set by {@link #onSetWarningAndLimit} has been reached.
*/
public void notifyWarningReached() {
try {
// Reuse the code path to notify warning reached with limit reached
// since framework handles them in the same way.
getProviderCallbackBinderOrThrow().notifyWarningReached();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Notify system that the limit set by {@link #onSetLimit} or limit set by
* {@link #onSetWarningAndLimit} has been reached.
*/
public void notifyLimitReached() {
try {
getProviderCallbackBinderOrThrow().notifyLimitReached();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Called by {@code NetworkStatsService} when it requires to know updated stats.
* The provider MUST respond by calling {@link #notifyStatsUpdated} as soon as possible.
* Responding later increases the probability stats will be dropped. Memory allowing, the
* system will try to take stats into account up to one minute after calling
* {@link #onRequestStatsUpdate}.
*
* @param token a positive number identifying the new state of the system under which
* {@link NetworkStats} have to be gathered from now on. When this is called,
* custom implementations of providers MUST tally and report the latest stats with
* the previous token, under which stats were being gathered so far.
*/
public abstract void onRequestStatsUpdate(int token);
/**
* Called by {@code NetworkStatsService} when setting the interface quota for the specified
* upstream interface. When this is called, the custom implementation should block all egress
* packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
* been reached, and MUST respond to it by calling
* {@link NetworkStatsProvider#notifyLimitReached()}.
*
* @param iface the interface requiring the operation.
* @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
* from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
*/
public abstract void onSetLimit(@NonNull String iface, long quotaBytes);
/**
* Called by {@code NetworkStatsService} when setting the interface quotas for the specified
* upstream interface. If a provider implements {@link #onSetWarningAndLimit}, the system
* will not call {@link #onSetLimit}. When this method is called, the implementation
* should behave as follows:
* 1. If {@code warningBytes} is reached on {@code iface}, block all further traffic on
* {@code iface} and call {@link NetworkStatsProvider@notifyWarningReached()}.
* 2. If {@code limitBytes} is reached on {@code iface}, block all further traffic on
* {@code iface} and call {@link NetworkStatsProvider#notifyLimitReached()}.
*
* @param iface the interface requiring the operation.
* @param warningBytes the warning defined as the number of bytes, starting from zero and
* counting from now. A value of {@link #QUOTA_UNLIMITED} indicates
* there is no warning.
* @param limitBytes the limit defined as the number of bytes, starting from zero and counting
* from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
*/
public void onSetWarningAndLimit(@NonNull String iface, long warningBytes, long limitBytes) {
// Backward compatibility for those who didn't override this function.
onSetLimit(iface, limitBytes);
}
/**
* Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
* MUST call {@link NetworkStatsProvider#notifyAlertReached()} when {@code quotaBytes} bytes
* have been reached. Unlike {@link #onSetLimit(String, long)}, the custom implementation should
* not block all egress packets.
*
* @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
* from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
*/
public abstract void onSetAlert(long quotaBytes);
}

View File

@@ -1,30 +0,0 @@
/**
* Copyright (c) 2021, 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.nsd;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
import android.os.Messenger;
/**
* Interface that NsdService implements to connect NsdManager clients.
*
* {@hide}
*/
interface INsdManager {
INsdServiceConnector connect(INsdManagerCallback cb);
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2021, 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.nsd;
import android.os.Messenger;
import android.net.nsd.NsdServiceInfo;
/**
* Callbacks from NsdService to NsdManager
* @hide
*/
oneway interface INsdManagerCallback {
void onDiscoverServicesStarted(int listenerKey, in NsdServiceInfo info);
void onDiscoverServicesFailed(int listenerKey, int error);
void onServiceFound(int listenerKey, in NsdServiceInfo info);
void onServiceLost(int listenerKey, in NsdServiceInfo info);
void onStopDiscoveryFailed(int listenerKey, int error);
void onStopDiscoverySucceeded(int listenerKey);
void onRegisterServiceFailed(int listenerKey, int error);
void onRegisterServiceSucceeded(int listenerKey, in NsdServiceInfo info);
void onUnregisterServiceFailed(int listenerKey, int error);
void onUnregisterServiceSucceeded(int listenerKey);
void onResolveServiceFailed(int listenerKey, int error);
void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info);
}

View File

@@ -1,35 +0,0 @@
/**
* Copyright (c) 2021, 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.nsd;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.NsdServiceInfo;
import android.os.Messenger;
/**
* Interface that NsdService implements for each NsdManager client.
*
* {@hide}
*/
interface INsdServiceConnector {
void registerService(int listenerKey, in NsdServiceInfo serviceInfo);
void unregisterService(int listenerKey);
void discoverServices(int listenerKey, in NsdServiceInfo serviceInfo);
void stopDiscovery(int listenerKey);
void resolveService(int listenerKey, in NsdServiceInfo serviceInfo);
void startDaemon();
}

View File

@@ -1,418 +0,0 @@
/*
* Copyright (C) 2012 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.nsd;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.Network;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Base64;
import android.util.Log;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;
/**
* A class representing service information for network service discovery
* {@see NsdManager}
*/
public final class NsdServiceInfo implements Parcelable {
private static final String TAG = "NsdServiceInfo";
private String mServiceName;
private String mServiceType;
private final ArrayMap<String, byte[]> mTxtRecord = new ArrayMap<>();
private InetAddress mHost;
private int mPort;
@Nullable
private Network mNetwork;
public NsdServiceInfo() {
}
/** @hide */
public NsdServiceInfo(String sn, String rt) {
mServiceName = sn;
mServiceType = rt;
}
/** Get the service name */
public String getServiceName() {
return mServiceName;
}
/** Set the service name */
public void setServiceName(String s) {
mServiceName = s;
}
/** Get the service type */
public String getServiceType() {
return mServiceType;
}
/** Set the service type */
public void setServiceType(String s) {
mServiceType = s;
}
/** Get the host address. The host address is valid for a resolved service. */
public InetAddress getHost() {
return mHost;
}
/** Set the host address */
public void setHost(InetAddress s) {
mHost = s;
}
/** Get port number. The port number is valid for a resolved service. */
public int getPort() {
return mPort;
}
/** Set port number */
public void setPort(int p) {
mPort = p;
}
/**
* Unpack txt information from a base-64 encoded byte array.
*
* @param rawRecords The raw base64 encoded records string read from netd.
*
* @hide
*/
public void setTxtRecords(@NonNull String rawRecords) {
byte[] txtRecordsRawBytes = Base64.decode(rawRecords, Base64.DEFAULT);
// There can be multiple TXT records after each other. Each record has to following format:
//
// byte type required meaning
// ------------------- ------------------- -------- ----------------------------------
// 0 unsigned 8 bit yes size of record excluding this byte
// 1 - n ASCII but not '=' yes key
// n + 1 '=' optional separator of key and value
// n + 2 - record size uninterpreted bytes optional value
//
// Example legal records:
// [11, 'm', 'y', 'k', 'e', 'y', '=', 0x0, 0x4, 0x65, 0x7, 0xff]
// [17, 'm', 'y', 'K', 'e', 'y', 'W', 'i', 't', 'h', 'N', 'o', 'V', 'a', 'l', 'u', 'e', '=']
// [12, 'm', 'y', 'B', 'o', 'o', 'l', 'e', 'a', 'n', 'K', 'e', 'y']
//
// Example corrupted records
// [3, =, 1, 2] <- key is empty
// [3, 0, =, 2] <- key contains non-ASCII character. We handle this by replacing the
// invalid characters instead of skipping the record.
// [30, 'a', =, 2] <- length exceeds total left over bytes in the TXT records array, we
// handle this by reducing the length of the record as needed.
int pos = 0;
while (pos < txtRecordsRawBytes.length) {
// recordLen is an unsigned 8 bit value
int recordLen = txtRecordsRawBytes[pos] & 0xff;
pos += 1;
try {
if (recordLen == 0) {
throw new IllegalArgumentException("Zero sized txt record");
} else if (pos + recordLen > txtRecordsRawBytes.length) {
Log.w(TAG, "Corrupt record length (pos = " + pos + "): " + recordLen);
recordLen = txtRecordsRawBytes.length - pos;
}
// Decode key-value records
String key = null;
byte[] value = null;
int valueLen = 0;
for (int i = pos; i < pos + recordLen; i++) {
if (key == null) {
if (txtRecordsRawBytes[i] == '=') {
key = new String(txtRecordsRawBytes, pos, i - pos,
StandardCharsets.US_ASCII);
}
} else {
if (value == null) {
value = new byte[recordLen - key.length() - 1];
}
value[valueLen] = txtRecordsRawBytes[i];
valueLen++;
}
}
// If '=' was not found we have a boolean record
if (key == null) {
key = new String(txtRecordsRawBytes, pos, recordLen, StandardCharsets.US_ASCII);
}
if (TextUtils.isEmpty(key)) {
// Empty keys are not allowed (RFC6763 6.4)
throw new IllegalArgumentException("Invalid txt record (key is empty)");
}
if (getAttributes().containsKey(key)) {
// When we have a duplicate record, the later ones are ignored (RFC6763 6.4)
throw new IllegalArgumentException("Invalid txt record (duplicate key \"" + key + "\")");
}
setAttribute(key, value);
} catch (IllegalArgumentException e) {
Log.e(TAG, "While parsing txt records (pos = " + pos + "): " + e.getMessage());
}
pos += recordLen;
}
}
/** @hide */
@UnsupportedAppUsage
public void setAttribute(String key, byte[] value) {
if (TextUtils.isEmpty(key)) {
throw new IllegalArgumentException("Key cannot be empty");
}
// Key must be printable US-ASCII, excluding =.
for (int i = 0; i < key.length(); ++i) {
char character = key.charAt(i);
if (character < 0x20 || character > 0x7E) {
throw new IllegalArgumentException("Key strings must be printable US-ASCII");
} else if (character == 0x3D) {
throw new IllegalArgumentException("Key strings must not include '='");
}
}
// Key length + value length must be < 255.
if (key.length() + (value == null ? 0 : value.length) >= 255) {
throw new IllegalArgumentException("Key length + value length must be < 255 bytes");
}
// Warn if key is > 9 characters, as recommended by RFC 6763 section 6.4.
if (key.length() > 9) {
Log.w(TAG, "Key lengths > 9 are discouraged: " + key);
}
// Check against total TXT record size limits.
// Arbitrary 400 / 1300 byte limits taken from RFC 6763 section 6.2.
int txtRecordSize = getTxtRecordSize();
int futureSize = txtRecordSize + key.length() + (value == null ? 0 : value.length) + 2;
if (futureSize > 1300) {
throw new IllegalArgumentException("Total length of attributes must be < 1300 bytes");
} else if (futureSize > 400) {
Log.w(TAG, "Total length of all attributes exceeds 400 bytes; truncation may occur");
}
mTxtRecord.put(key, value);
}
/**
* Add a service attribute as a key/value pair.
*
* <p> Service attributes are included as DNS-SD TXT record pairs.
*
* <p> The key must be US-ASCII printable characters, excluding the '=' character. Values may
* be UTF-8 strings or null. The total length of key + value must be less than 255 bytes.
*
* <p> Keys should be short, ideally no more than 9 characters, and unique per instance of
* {@link NsdServiceInfo}. Calling {@link #setAttribute} twice with the same key will overwrite
* first value.
*/
public void setAttribute(String key, String value) {
try {
setAttribute(key, value == null ? (byte []) null : value.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Value must be UTF-8");
}
}
/** Remove an attribute by key */
public void removeAttribute(String key) {
mTxtRecord.remove(key);
}
/**
* Retrieve attributes as a map of String keys to byte[] values. The attributes map is only
* valid for a resolved service.
*
* <p> The returned map is unmodifiable; changes must be made through {@link #setAttribute} and
* {@link #removeAttribute}.
*/
public Map<String, byte[]> getAttributes() {
return Collections.unmodifiableMap(mTxtRecord);
}
private int getTxtRecordSize() {
int txtRecordSize = 0;
for (Map.Entry<String, byte[]> entry : mTxtRecord.entrySet()) {
txtRecordSize += 2; // One for the length byte, one for the = between key and value.
txtRecordSize += entry.getKey().length();
byte[] value = entry.getValue();
txtRecordSize += value == null ? 0 : value.length;
}
return txtRecordSize;
}
/** @hide */
public @NonNull byte[] getTxtRecord() {
int txtRecordSize = getTxtRecordSize();
if (txtRecordSize == 0) {
return new byte[]{};
}
byte[] txtRecord = new byte[txtRecordSize];
int ptr = 0;
for (Map.Entry<String, byte[]> entry : mTxtRecord.entrySet()) {
String key = entry.getKey();
byte[] value = entry.getValue();
// One byte to record the length of this key/value pair.
txtRecord[ptr++] = (byte) (key.length() + (value == null ? 0 : value.length) + 1);
// The key, in US-ASCII.
// Note: use the StandardCharsets const here because it doesn't raise exceptions and we
// already know the key is ASCII at this point.
System.arraycopy(key.getBytes(StandardCharsets.US_ASCII), 0, txtRecord, ptr,
key.length());
ptr += key.length();
// US-ASCII '=' character.
txtRecord[ptr++] = (byte)'=';
// The value, as any raw bytes.
if (value != null) {
System.arraycopy(value, 0, txtRecord, ptr, value.length);
ptr += value.length;
}
}
return txtRecord;
}
/**
* Get the network where the service can be found.
*
* This is never null if this {@link NsdServiceInfo} was obtained from
* {@link NsdManager#discoverServices} or {@link NsdManager#resolveService}.
*/
@Nullable
public Network getNetwork() {
return mNetwork;
}
/**
* Set the network where the service can be found.
* @param network The network, or null to search for, or to announce, the service on all
* connected networks.
*/
public void setNetwork(@Nullable Network network) {
mNetwork = network;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("name: ").append(mServiceName)
.append(", type: ").append(mServiceType)
.append(", host: ").append(mHost)
.append(", port: ").append(mPort)
.append(", network: ").append(mNetwork);
byte[] txtRecord = getTxtRecord();
sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8));
return sb.toString();
}
/** Implement the Parcelable interface */
public int describeContents() {
return 0;
}
/** Implement the Parcelable interface */
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mServiceName);
dest.writeString(mServiceType);
if (mHost != null) {
dest.writeInt(1);
dest.writeByteArray(mHost.getAddress());
} else {
dest.writeInt(0);
}
dest.writeInt(mPort);
// TXT record key/value pairs.
dest.writeInt(mTxtRecord.size());
for (String key : mTxtRecord.keySet()) {
byte[] value = mTxtRecord.get(key);
if (value != null) {
dest.writeInt(1);
dest.writeInt(value.length);
dest.writeByteArray(value);
} else {
dest.writeInt(0);
}
dest.writeString(key);
}
dest.writeParcelable(mNetwork, 0);
}
/** Implement the Parcelable interface */
public static final @android.annotation.NonNull Creator<NsdServiceInfo> CREATOR =
new Creator<NsdServiceInfo>() {
public NsdServiceInfo createFromParcel(Parcel in) {
NsdServiceInfo info = new NsdServiceInfo();
info.mServiceName = in.readString();
info.mServiceType = in.readString();
if (in.readInt() == 1) {
try {
info.mHost = InetAddress.getByAddress(in.createByteArray());
} catch (java.net.UnknownHostException e) {}
}
info.mPort = in.readInt();
// TXT record key/value pairs.
int recordCount = in.readInt();
for (int i = 0; i < recordCount; ++i) {
byte[] valueArray = null;
if (in.readInt() == 1) {
int valueLength = in.readInt();
valueArray = new byte[valueLength];
in.readByteArray(valueArray);
}
info.mTxtRecord.put(in.readString(), valueArray);
}
info.mNetwork = in.readParcelable(null, Network.class);
return info;
}
public NsdServiceInfo[] newArray(int size) {
return new NsdServiceInfo[size];
}
};
}

View File

@@ -1,156 +0,0 @@
//
// Copyright (C) 2021 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 {
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
// NetworkStats related libraries.
filegroup {
name: "services.connectivity-netstats-sources",
srcs: [
"src/com/android/server/net/NetworkIdentity*.java",
"src/com/android/server/net/NetworkStats*.java",
"src/com/android/server/net/BpfInterfaceMapUpdater.java",
"src/com/android/server/net/InterfaceMapValue.java",
"src/com/android/server/net/CookieTagMapKey.java",
"src/com/android/server/net/CookieTagMapValue.java",
"src/com/android/server/net/StatsMapKey.java",
"src/com/android/server/net/StatsMapValue.java",
"src/com/android/server/net/UidStatsMapKey.java",
],
path: "src",
visibility: [
"//visibility:private",
],
}
// For test code only.
filegroup {
name: "lib_networkStatsFactory_native",
srcs: [
"jni/com_android_server_net_NetworkStatsFactory.cpp",
],
path: "jni",
visibility: [
"//packages/modules/Connectivity:__subpackages__",
],
}
filegroup {
name: "services.connectivity-netstats-jni-sources",
srcs: [
"jni/com_android_server_net_NetworkStatsFactory.cpp",
"jni/com_android_server_net_NetworkStatsService.cpp",
],
path: "jni",
visibility: [
"//packages/modules/Connectivity:__subpackages__",
],
}
// Nsd related libraries.
filegroup {
name: "services.connectivity-nsd-sources",
srcs: [
"src/com/android/server/INativeDaemon*.java",
"src/com/android/server/NativeDaemon*.java",
"src/com/android/server/Nsd*.java",
],
path: "src",
visibility: [
"//visibility:private",
],
}
// IpSec related libraries.
filegroup {
name: "services.connectivity-ipsec-sources",
srcs: [
"src/com/android/server/IpSecService.java",
],
path: "src",
visibility: [
"//visibility:private",
],
}
// Ethernet related libraries.
filegroup {
name: "services.connectivity-ethernet-sources",
srcs: [
"src/com/android/server/net/DelayedDiskWrite.java",
"src/com/android/server/net/IpConfigStore.java",
],
path: "src",
visibility: [
"//frameworks/opt/net/ethernet/tests",
],
}
// Connectivity-T common libraries.
// TODO: remove this empty filegroup.
filegroup {
name: "services.connectivity-tiramisu-sources",
srcs: [],
path: "src",
visibility: ["//frameworks/base/services/core"],
}
filegroup {
name: "services.connectivity-tiramisu-updatable-sources",
srcs: [
":services.connectivity-ethernet-sources",
":services.connectivity-ipsec-sources",
":services.connectivity-netstats-sources",
":services.connectivity-nsd-sources",
],
path: "src",
visibility: [
"//packages/modules/Connectivity:__subpackages__",
],
}
cc_library_shared {
name: "libcom_android_net_module_util_jni",
min_sdk_version: "30",
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
],
srcs: [
"jni/onload.cpp",
],
stl: "libc++_static",
static_libs: [
"libnet_utils_device_common_bpfjni",
],
shared_libs: [
"liblog",
"libnativehelper",
],
apex_available: [
"//apex_available:platform",
],
}

View File

@@ -1,362 +0,0 @@
/*
* Copyright (C) 2013 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.
*/
#define LOG_TAG "NetworkStats"
#include <errno.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <vector>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <utils/Log.h>
#include <utils/misc.h>
#include "android-base/unique_fd.h"
#include "bpf/BpfUtils.h"
#include "netdbpf/BpfNetworkStats.h"
using android::bpf::parseBpfNetworkStatsDetail;
using android::bpf::stats_line;
namespace android {
static jclass gStringClass;
static struct {
jfieldID size;
jfieldID capacity;
jfieldID iface;
jfieldID uid;
jfieldID set;
jfieldID tag;
jfieldID metered;
jfieldID roaming;
jfieldID defaultNetwork;
jfieldID rxBytes;
jfieldID rxPackets;
jfieldID txBytes;
jfieldID txPackets;
jfieldID operations;
} gNetworkStatsClassInfo;
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 legacyReadNetworkStatsDetail(std::vector<stats_line>* lines,
const std::vector<std::string>& limitIfaces,
int limitTag, int limitUid, const char* path) {
FILE* fp = fopen(path, "re");
if (fp == NULL) {
return -1;
}
int lastIdx = 1;
int idx;
char buffer[384];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
stats_line s;
int64_t rawTag;
char* pos = buffer;
char* endPos;
// First field is the index.
idx = (int)strtol(pos, &endPos, 10);
//ALOGI("Index #%d: %s", idx, buffer);
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;
}
}
// Ignore whitespace
while (*pos == ' ') pos++;
// Find end of tag field
endPos = pos;
while (*endPos != ' ') endPos++;
// Three digit field is always 0x0, otherwise parse
if (endPos - pos == 3) {
rawTag = 0;
} else {
if (sscanf(pos, "%" PRIx64, &rawTag) != 1) {
ALOGE("bad tag: %s", pos);
fclose(fp);
return -1;
}
}
s.tag = rawTag >> 32;
if (limitTag != -1 && s.tag != static_cast<uint32_t>(limitTag)) {
//ALOGI("skipping due to tag: %s", buffer);
continue;
}
pos = endPos;
// Ignore whitespace
while (*pos == ' ') pos++;
// Parse remaining fields.
if (sscanf(pos, "%u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64,
&s.uid, &s.set, &s.rxBytes, &s.rxPackets,
&s.txBytes, &s.txPackets) == 6) {
if (limitUid != -1 && static_cast<uint32_t>(limitUid) != s.uid) {
//ALOGI("skipping due to uid: %s", buffer);
continue;
}
lines->push_back(s);
} else {
//ALOGI("skipping due to bad remaining fields: %s", pos);
}
}
if (fclose(fp) != 0) {
ALOGE("Failed to close netstats file");
return -1;
}
return 0;
}
static int statsLinesToNetworkStats(JNIEnv* env, jclass clazz, jobject stats,
std::vector<stats_line>& lines) {
int size = lines.size();
bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity);
ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats,
gNetworkStatsClassInfo.iface, size, grow));
if (iface.get() == NULL) return -1;
ScopedIntArrayRW uid(env, get_int_array(env, stats,
gNetworkStatsClassInfo.uid, size, grow));
if (uid.get() == NULL) return -1;
ScopedIntArrayRW set(env, get_int_array(env, stats,
gNetworkStatsClassInfo.set, size, grow));
if (set.get() == NULL) return -1;
ScopedIntArrayRW tag(env, get_int_array(env, stats,
gNetworkStatsClassInfo.tag, size, grow));
if (tag.get() == NULL) return -1;
ScopedIntArrayRW metered(env, get_int_array(env, stats,
gNetworkStatsClassInfo.metered, size, grow));
if (metered.get() == NULL) return -1;
ScopedIntArrayRW roaming(env, get_int_array(env, stats,
gNetworkStatsClassInfo.roaming, size, grow));
if (roaming.get() == NULL) return -1;
ScopedIntArrayRW defaultNetwork(env, get_int_array(env, stats,
gNetworkStatsClassInfo.defaultNetwork, size, grow));
if (defaultNetwork.get() == NULL) return -1;
ScopedLongArrayRW rxBytes(env, get_long_array(env, stats,
gNetworkStatsClassInfo.rxBytes, size, grow));
if (rxBytes.get() == NULL) return -1;
ScopedLongArrayRW rxPackets(env, get_long_array(env, stats,
gNetworkStatsClassInfo.rxPackets, size, grow));
if (rxPackets.get() == NULL) return -1;
ScopedLongArrayRW txBytes(env, get_long_array(env, stats,
gNetworkStatsClassInfo.txBytes, size, grow));
if (txBytes.get() == NULL) return -1;
ScopedLongArrayRW txPackets(env, get_long_array(env, stats,
gNetworkStatsClassInfo.txPackets, size, grow));
if (txPackets.get() == NULL) return -1;
ScopedLongArrayRW operations(env, get_long_array(env, stats,
gNetworkStatsClassInfo.operations, size, grow));
if (operations.get() == NULL) return -1;
for (int i = 0; i < size; i++) {
ScopedLocalRef<jstring> ifaceString(env, env->NewStringUTF(lines[i].iface));
env->SetObjectArrayElement(iface.get(), i, ifaceString.get());
uid[i] = lines[i].uid;
set[i] = lines[i].set;
tag[i] = lines[i].tag;
// Metered, roaming and defaultNetwork are populated in Java-land.
rxBytes[i] = lines[i].rxBytes;
rxPackets[i] = lines[i].rxPackets;
txBytes[i] = lines[i].txBytes;
txPackets[i] = lines[i].txPackets;
}
env->SetIntField(stats, gNetworkStatsClassInfo.size, size);
if (grow) {
env->SetIntField(stats, gNetworkStatsClassInfo.capacity, size);
env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get());
env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.metered, metered.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.roaming, roaming.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.defaultNetwork,
defaultNetwork.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray());
env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray());
}
return 0;
}
static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
jboolean useBpfStats) {
std::vector<std::string> limitIfaces;
if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
int num = env->GetArrayLength(limitIfacesObj);
for (int i = 0; i < num; i++) {
jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
ScopedUtfChars string8(env, string);
if (string8.c_str() != NULL) {
limitIfaces.push_back(std::string(string8.c_str()));
}
}
}
std::vector<stats_line> lines;
if (useBpfStats) {
if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
return -1;
} else {
ScopedUtfChars path8(env, path);
if (path8.c_str() == NULL) {
ALOGE("the qtaguid legacy path is invalid: %s", path8.c_str());
return -1;
}
if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
limitUid, path8.c_str()) < 0)
return -1;
}
return statsLinesToNetworkStats(env, clazz, stats, lines);
}
static int readNetworkStatsDev(JNIEnv* env, jclass clazz, jobject stats) {
std::vector<stats_line> lines;
if (parseBpfNetworkStatsDev(&lines) < 0)
return -1;
return statsLinesToNetworkStats(env, clazz, stats, lines);
}
static const JNINativeMethod gMethods[] = {
{ "nativeReadNetworkStatsDetail",
"(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;IZ)I",
(void*) readNetworkStatsDetail },
{ "nativeReadNetworkStatsDev", "(Landroid/net/NetworkStats;)I",
(void*) readNetworkStatsDev },
};
int register_android_server_net_NetworkStatsFactory(JNIEnv* env) {
int err = jniRegisterNativeMethods(env, "com/android/server/net/NetworkStatsFactory", gMethods,
NELEM(gMethods));
gStringClass = env->FindClass("java/lang/String");
gStringClass = static_cast<jclass>(env->NewGlobalRef(gStringClass));
jclass clazz = env->FindClass("android/net/NetworkStats");
gNetworkStatsClassInfo.size = env->GetFieldID(clazz, "size", "I");
gNetworkStatsClassInfo.capacity = env->GetFieldID(clazz, "capacity", "I");
gNetworkStatsClassInfo.iface = env->GetFieldID(clazz, "iface", "[Ljava/lang/String;");
gNetworkStatsClassInfo.uid = env->GetFieldID(clazz, "uid", "[I");
gNetworkStatsClassInfo.set = env->GetFieldID(clazz, "set", "[I");
gNetworkStatsClassInfo.tag = env->GetFieldID(clazz, "tag", "[I");
gNetworkStatsClassInfo.metered = env->GetFieldID(clazz, "metered", "[I");
gNetworkStatsClassInfo.roaming = env->GetFieldID(clazz, "roaming", "[I");
gNetworkStatsClassInfo.defaultNetwork = env->GetFieldID(clazz, "defaultNetwork", "[I");
gNetworkStatsClassInfo.rxBytes = env->GetFieldID(clazz, "rxBytes", "[J");
gNetworkStatsClassInfo.rxPackets = env->GetFieldID(clazz, "rxPackets", "[J");
gNetworkStatsClassInfo.txBytes = env->GetFieldID(clazz, "txBytes", "[J");
gNetworkStatsClassInfo.txPackets = env->GetFieldID(clazz, "txPackets", "[J");
gNetworkStatsClassInfo.operations = env->GetFieldID(clazz, "operations", "[J");
return err;
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "NetworkStatsNative"
#include <cutils/qtaguid.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <jni.h>
#include <nativehelper/ScopedUtfChars.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <utils/Log.h>
#include <utils/misc.h>
#include "bpf/BpfUtils.h"
#include "netdbpf/BpfNetworkStats.h"
using android::bpf::bpfGetUidStats;
using android::bpf::bpfGetIfaceStats;
namespace android {
// NOTE: keep these in sync with TrafficStats.java
static const uint64_t UNKNOWN = -1;
enum StatsType {
RX_BYTES = 0,
RX_PACKETS = 1,
TX_BYTES = 2,
TX_PACKETS = 3,
TCP_RX_PACKETS = 4,
TCP_TX_PACKETS = 5
};
static uint64_t getStatsType(Stats* stats, StatsType type) {
switch (type) {
case RX_BYTES:
return stats->rxBytes;
case RX_PACKETS:
return stats->rxPackets;
case TX_BYTES:
return stats->txBytes;
case TX_PACKETS:
return stats->txPackets;
case TCP_RX_PACKETS:
return stats->tcpRxPackets;
case TCP_TX_PACKETS:
return stats->tcpTxPackets;
default:
return UNKNOWN;
}
}
static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
Stats stats = {};
if (bpfGetIfaceStats(NULL, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
}
}
static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
ScopedUtfChars iface8(env, iface);
if (iface8.c_str() == NULL) {
return UNKNOWN;
}
Stats stats = {};
if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
}
}
static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
Stats stats = {};
if (bpfGetUidStats(uid, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
}
}
static const JNINativeMethod gMethods[] = {
{"nativeGetTotalStat", "(I)J", (void*)getTotalStat},
{"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat},
{"nativeGetUidStat", "(II)J", (void*)getUidStat},
};
int register_android_server_net_NetworkStatsService(JNIEnv* env) {
return jniRegisterNativeMethods(env, "com/android/server/net/NetworkStatsService", gMethods,
NELEM(gMethods));
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2022 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.
*/
#include <nativehelper/JNIHelp.h>
#include <log/log.h>
namespace android {
int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name);
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
ALOGE("GetEnv failed");
return JNI_ERR;
}
if (register_com_android_net_module_util_BpfMap(env,
"com/android/net/module/util/BpfMap") < 0) return JNI_ERR;
return JNI_VERSION_1_6;
}
};

View File

@@ -1,25 +0,0 @@
/*
* Copyright (C) 2007 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;
interface INativeDaemonConnectorCallbacks {
void onDaemonConnected();
boolean onCheckHoldWakeLock(int code);
boolean onEvent(int code, String raw, String[] cooked);
}

View File

@@ -1,704 +0,0 @@
/*
* Copyright (C) 2007 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;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.LocalLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Generic connector class for interfacing with a native daemon which uses the
* {@code libsysutils} FrameworkListener protocol.
*/
final class NativeDaemonConnector implements Runnable, Handler.Callback {
private final static boolean VDBG = false;
private final String TAG;
private String mSocket;
private OutputStream mOutputStream;
private LocalLog mLocalLog;
private volatile boolean mDebug = false;
private volatile Object mWarnIfHeld;
private final ResponseQueue mResponseQueue;
private final PowerManager.WakeLock mWakeLock;
private final Looper mLooper;
private INativeDaemonConnectorCallbacks mCallbacks;
private Handler mCallbackHandler;
private AtomicInteger mSequenceNumber;
private static final long DEFAULT_TIMEOUT = 1 * 60 * 1000; /* 1 minute */
private static final long WARN_EXECUTE_DELAY_MS = 500; /* .5 sec */
/** Lock held whenever communicating with native daemon. */
private final Object mDaemonLock = new Object();
private final int BUFFER_SIZE = 4096;
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) {
mCallbacks = callbacks;
mSocket = socket;
mResponseQueue = new ResponseQueue(responseQueueSize);
mWakeLock = wl;
if (mWakeLock != null) {
mWakeLock.setReferenceCounted(true);
}
mSequenceNumber = new AtomicInteger(0);
TAG = logTag != null ? logTag : "NativeDaemonConnector";
mLocalLog = new LocalLog(maxLogSize);
final HandlerThread thread = new HandlerThread(TAG);
thread.start();
mLooper = thread.getLooper();
}
/**
* Enable Set debugging mode, which causes messages to also be written to both
* {@link Log} in addition to internal log.
*/
public void setDebug(boolean debug) {
mDebug = debug;
}
/**
* Like SystemClock.uptimeMillis, except truncated to an int so it will fit in a message arg.
* Inaccurate across 49.7 days of uptime, but only used for debugging.
*/
private int uptimeMillisInt() {
return (int) SystemClock.uptimeMillis() & Integer.MAX_VALUE;
}
/**
* Yell loudly if someone tries making future {@link #execute(Command)}
* calls while holding a lock on the given object.
*/
public void setWarnIfHeld(Object warnIfHeld) {
if (mWarnIfHeld != null) {
throw new IllegalStateException("warnIfHeld is already set.");
}
mWarnIfHeld = Objects.requireNonNull(warnIfHeld);
}
@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);
while (true) {
try {
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
SystemClock.sleep(5000);
}
}
}
@Override
public boolean handleMessage(Message msg) {
final String event = (String) msg.obj;
final int start = uptimeMillisInt();
final int sent = msg.arg1;
try {
if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
log(String.format("Unhandled event '%s'", event));
}
} catch (Exception e) {
loge("Error handling '" + event + "': " + e);
} finally {
if (mCallbacks.onCheckHoldWakeLock(msg.what) && mWakeLock != null) {
mWakeLock.release();
}
final int end = uptimeMillisInt();
if (start > sent && start - sent > WARN_EXECUTE_DELAY_MS) {
loge(String.format("NDC event {%s} processed too late: %dms", event, start - sent));
}
if (end > start && end - start > WARN_EXECUTE_DELAY_MS) {
loge(String.format("NDC event {%s} took too long: %dms", event, end - start));
}
}
return true;
}
private LocalSocketAddress determineSocketAddress() {
// If we're testing, set up a socket in a namespace that's accessible to test code.
// In order to ensure that unprivileged apps aren't able to impersonate native daemons on
// production devices, even if said native daemons ill-advisedly pick a socket name that
// starts with __test__, only allow this on debug builds.
if (mSocket.startsWith("__test__") && Build.isDebuggable()) {
return new LocalSocketAddress(mSocket);
} else {
return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED);
}
}
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
socket.connect(address);
InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}
mCallbacks.onDaemonConnected();
FileDescriptor[] fdList = null;
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;
while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
if (count < 0) {
loge("got " + count + " reading with start = " + start);
break;
}
fdList = socket.getAncillaryFileDescriptors();
// Add our starting point to the count and reset the start.
count += start;
start = 0;
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
// Note - do not log this raw message since it may contain
// sensitive data
final String rawEvent = new String(
buffer, start, i - start, StandardCharsets.UTF_8);
boolean releaseWl = false;
try {
final NativeDaemonEvent event =
NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
log("RCV <- {" + event + "}");
if (event.isClassUnsolicited()) {
// TODO: migrate to sending NativeDaemonEvent instances
if (mCallbacks.onCheckHoldWakeLock(event.getCode())
&& mWakeLock != null) {
mWakeLock.acquire();
releaseWl = true;
}
Message msg = mCallbackHandler.obtainMessage(
event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
if (mCallbackHandler.sendMessage(msg)) {
releaseWl = false;
}
} else {
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message " + e);
} finally {
if (releaseWl) {
mWakeLock.release();
}
}
start = i + 1;
}
}
if (start == 0) {
log("RCV incomplete");
}
// We should end at the amount we read. If not, compact then
// buffer and read again.
if (start != count) {
final int remaining = BUFFER_SIZE - start;
System.arraycopy(buffer, start, buffer, 0, remaining);
start = remaining;
} else {
start = 0;
}
}
} catch (IOException ex) {
loge("Communications error: " + ex);
throw ex;
} finally {
synchronized (mDaemonLock) {
if (mOutputStream != null) {
try {
loge("closing stream for " + mSocket);
mOutputStream.close();
} catch (IOException e) {
loge("Failed closing output stream: " + e);
}
mOutputStream = null;
}
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
loge("Failed closing socket: " + ex);
}
}
}
/**
* Wrapper around argument that indicates it's sensitive and shouldn't be
* logged.
*/
public static class SensitiveArg {
private final Object mArg;
public SensitiveArg(Object arg) {
mArg = arg;
}
@Override
public String toString() {
return String.valueOf(mArg);
}
}
/**
* Make command for daemon, escaping arguments as needed.
*/
@VisibleForTesting
static void makeCommand(StringBuilder rawBuilder, StringBuilder logBuilder, int sequenceNumber,
String cmd, Object... args) {
if (cmd.indexOf('\0') >= 0) {
throw new IllegalArgumentException("Unexpected command: " + cmd);
}
if (cmd.indexOf(' ') >= 0) {
throw new IllegalArgumentException("Arguments must be separate from command");
}
rawBuilder.append(sequenceNumber).append(' ').append(cmd);
logBuilder.append(sequenceNumber).append(' ').append(cmd);
for (Object arg : args) {
final String argString = String.valueOf(arg);
if (argString.indexOf('\0') >= 0) {
throw new IllegalArgumentException("Unexpected argument: " + arg);
}
rawBuilder.append(' ');
logBuilder.append(' ');
appendEscaped(rawBuilder, argString);
if (arg instanceof SensitiveArg) {
logBuilder.append("[scrubbed]");
} else {
appendEscaped(logBuilder, argString);
}
}
rawBuilder.append('\0');
}
/**
* Method that waits until all asychronous notifications sent by the native daemon have
* been processed. This method must not be called on the notification thread or an
* exception will be thrown.
*/
public void waitForCallbacks() {
if (Thread.currentThread() == mLooper.getThread()) {
throw new IllegalStateException("Must not call this method on callback thread");
}
final CountDownLatch latch = new CountDownLatch(1);
mCallbackHandler.post(new Runnable() {
@Override
public void run() {
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
Log.wtf(TAG, "Interrupted while waiting for unsolicited response handling", e);
}
}
/**
* Issue the given command to the native daemon and return a single expected
* response.
*
* @throws NativeDaemonConnectorException when problem communicating with
* native daemon, or if the response matches
* {@link NativeDaemonEvent#isClassClientError()} or
* {@link NativeDaemonEvent#isClassServerError()}.
*/
public NativeDaemonEvent execute(Command cmd) throws NativeDaemonConnectorException {
return execute(cmd.mCmd, cmd.mArguments.toArray());
}
/**
* Issue the given command to the native daemon and return a single expected
* response. Any arguments must be separated from base command so they can
* be properly escaped.
*
* @throws NativeDaemonConnectorException when problem communicating with
* native daemon, or if the response matches
* {@link NativeDaemonEvent#isClassClientError()} or
* {@link NativeDaemonEvent#isClassServerError()}.
*/
public NativeDaemonEvent execute(String cmd, Object... args)
throws NativeDaemonConnectorException {
return execute(DEFAULT_TIMEOUT, cmd, args);
}
public NativeDaemonEvent execute(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
final NativeDaemonEvent[] events = executeForList(timeoutMs, cmd, args);
if (events.length != 1) {
throw new NativeDaemonConnectorException(
"Expected exactly one response, but received " + events.length);
}
return events[0];
}
/**
* Issue the given command to the native daemon and return any
* {@link NativeDaemonEvent#isClassContinue()} responses, including the
* final terminal response.
*
* @throws NativeDaemonConnectorException when problem communicating with
* native daemon, or if the response matches
* {@link NativeDaemonEvent#isClassClientError()} or
* {@link NativeDaemonEvent#isClassServerError()}.
*/
public NativeDaemonEvent[] executeForList(Command cmd) throws NativeDaemonConnectorException {
return executeForList(cmd.mCmd, cmd.mArguments.toArray());
}
/**
* Issue the given command to the native daemon and return any
* {@link NativeDaemonEvent#isClassContinue()} responses, including the
* final terminal response. Any arguments must be separated from base
* command so they can be properly escaped.
*
* @throws NativeDaemonConnectorException when problem communicating with
* native daemon, or if the response matches
* {@link NativeDaemonEvent#isClassClientError()} or
* {@link NativeDaemonEvent#isClassServerError()}.
*/
public NativeDaemonEvent[] executeForList(String cmd, Object... args)
throws NativeDaemonConnectorException {
return executeForList(DEFAULT_TIMEOUT, cmd, args);
}
/**
* Issue the given command to the native daemon and return any {@linke
* NativeDaemonEvent@isClassContinue()} responses, including the final
* terminal response. Note that the timeout does not count time in deep
* sleep. Any arguments must be separated from base command so they can be
* properly escaped.
*
* @throws NativeDaemonConnectorException when problem communicating with
* native daemon, or if the response matches
* {@link NativeDaemonEvent#isClassClientError()} or
* {@link NativeDaemonEvent#isClassServerError()}.
*/
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
Log.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
}
final long startTime = SystemClock.elapsedRealtime();
final ArrayList<NativeDaemonEvent> events = new ArrayList<>();
final StringBuilder rawBuilder = new StringBuilder();
final StringBuilder logBuilder = new StringBuilder();
final int sequenceNumber = mSequenceNumber.incrementAndGet();
makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
final String rawCmd = rawBuilder.toString();
final String logCmd = logBuilder.toString();
log("SND -> {" + logCmd + "}");
synchronized (mDaemonLock) {
if (mOutputStream == null) {
throw new NativeDaemonConnectorException("missing output stream");
} else {
try {
mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new NativeDaemonConnectorException("problem sending command", e);
}
}
}
NativeDaemonEvent event = null;
do {
event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd);
if (event == null) {
loge("timed-out waiting for response to " + logCmd);
throw new NativeDaemonTimeoutException(logCmd, event);
}
if (VDBG) log("RMV <- {" + event + "}");
events.add(event);
} while (event.isClassContinue());
final long endTime = SystemClock.elapsedRealtime();
if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");
}
if (event.isClassClientError()) {
throw new NativeDaemonArgumentException(logCmd, event);
}
if (event.isClassServerError()) {
throw new NativeDaemonFailureException(logCmd, event);
}
return events.toArray(new NativeDaemonEvent[events.size()]);
}
/**
* Append the given argument to {@link StringBuilder}, escaping as needed,
* and surrounding with quotes when it contains spaces.
*/
@VisibleForTesting
static void appendEscaped(StringBuilder builder, String arg) {
final boolean hasSpaces = arg.indexOf(' ') >= 0;
if (hasSpaces) {
builder.append('"');
}
final int length = arg.length();
for (int i = 0; i < length; i++) {
final char c = arg.charAt(i);
if (c == '"') {
builder.append("\\\"");
} else if (c == '\\') {
builder.append("\\\\");
} else {
builder.append(c);
}
}
if (hasSpaces) {
builder.append('"');
}
}
private static class NativeDaemonArgumentException extends NativeDaemonConnectorException {
public NativeDaemonArgumentException(String command, NativeDaemonEvent event) {
super(command, event);
}
@Override
public IllegalArgumentException rethrowAsParcelableException() {
throw new IllegalArgumentException(getMessage(), this);
}
}
private static class NativeDaemonFailureException extends NativeDaemonConnectorException {
public NativeDaemonFailureException(String command, NativeDaemonEvent event) {
super(command, event);
}
}
/**
* Command builder that handles argument list building. Any arguments must
* be separated from base command so they can be properly escaped.
*/
public static class Command {
private String mCmd;
private ArrayList<Object> mArguments = new ArrayList<>();
public Command(String cmd, Object... args) {
mCmd = cmd;
for (Object arg : args) {
appendArg(arg);
}
}
public Command appendArg(Object arg) {
mArguments.add(arg);
return this;
}
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mLocalLog.dump(fd, pw, args);
pw.println();
mResponseQueue.dump(fd, pw, args);
}
private void log(String logstring) {
if (mDebug) Log.d(TAG, logstring);
mLocalLog.log(logstring);
}
private void loge(String logstring) {
Log.e(TAG, logstring);
mLocalLog.log(logstring);
}
private static class ResponseQueue {
private static class PendingCmd {
public final int cmdNum;
public final String logCmd;
public BlockingQueue<NativeDaemonEvent> responses =
new ArrayBlockingQueue<NativeDaemonEvent>(10);
// The availableResponseCount member is used to track when we can remove this
// instance from the ResponseQueue.
// This is used under the protection of a sync of the mPendingCmds object.
// A positive value means we've had more writers retreive this object while
// a negative value means we've had more readers. When we've had an equal number
// (it goes to zero) we can remove this object from the mPendingCmds list.
// Note that we may have more responses for this command (and more readers
// coming), but that would result in a new PendingCmd instance being created
// and added with the same cmdNum.
// Also note that when this goes to zero it just means a parity of readers and
// writers have retrieved this object - not that they are done using it. The
// responses queue may well have more responses yet to be read or may get more
// responses added to it. But all those readers/writers have retreived and
// hold references to this instance already so it can be removed from
// mPendingCmds queue.
public int availableResponseCount;
public PendingCmd(int cmdNum, String logCmd) {
this.cmdNum = cmdNum;
this.logCmd = logCmd;
}
}
private final LinkedList<PendingCmd> mPendingCmds;
private int mMaxCount;
ResponseQueue(int maxCount) {
mPendingCmds = new LinkedList<PendingCmd>();
mMaxCount = maxCount;
}
public void add(int cmdNum, NativeDaemonEvent response) {
PendingCmd found = null;
synchronized (mPendingCmds) {
for (PendingCmd pendingCmd : mPendingCmds) {
if (pendingCmd.cmdNum == cmdNum) {
found = pendingCmd;
break;
}
}
if (found == null) {
// didn't find it - make sure our queue isn't too big before adding
while (mPendingCmds.size() >= mMaxCount) {
Log.e("NativeDaemonConnector.ResponseQueue",
"more buffered than allowed: " + mPendingCmds.size() +
" >= " + mMaxCount);
// let any waiter timeout waiting for this
PendingCmd pendingCmd = mPendingCmds.remove();
Log.e("NativeDaemonConnector.ResponseQueue",
"Removing request: " + pendingCmd.logCmd + " (" +
pendingCmd.cmdNum + ")");
}
found = new PendingCmd(cmdNum, null);
mPendingCmds.add(found);
}
found.availableResponseCount++;
// if a matching remove call has already retrieved this we can remove this
// instance from our list
if (found.availableResponseCount == 0) mPendingCmds.remove(found);
}
try {
found.responses.put(response);
} catch (InterruptedException e) { }
}
// note that the timeout does not count time in deep sleep. If you don't want
// the device to sleep, hold a wakelock
public NativeDaemonEvent remove(int cmdNum, long timeoutMs, String logCmd) {
PendingCmd found = null;
synchronized (mPendingCmds) {
for (PendingCmd pendingCmd : mPendingCmds) {
if (pendingCmd.cmdNum == cmdNum) {
found = pendingCmd;
break;
}
}
if (found == null) {
found = new PendingCmd(cmdNum, logCmd);
mPendingCmds.add(found);
}
found.availableResponseCount--;
// if a matching add call has already retrieved this we can remove this
// instance from our list
if (found.availableResponseCount == 0) mPendingCmds.remove(found);
}
NativeDaemonEvent result = null;
try {
result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {}
if (result == null) {
Log.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response");
}
return result;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Pending requests:");
synchronized (mPendingCmds) {
for (PendingCmd pendingCmd : mPendingCmds) {
pw.println(" Cmd " + pendingCmd.cmdNum + " - " + pendingCmd.logCmd);
}
}
}
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 2006 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;
import android.os.Parcel;
/**
* An exception that indicates there was an error with a
* {@link NativeDaemonConnector} operation.
*/
public class NativeDaemonConnectorException extends Exception {
private String mCmd;
private NativeDaemonEvent mEvent;
public NativeDaemonConnectorException(String detailMessage) {
super(detailMessage);
}
public NativeDaemonConnectorException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
public NativeDaemonConnectorException(String cmd, NativeDaemonEvent event) {
super("command '" + cmd + "' failed with '" + event + "'");
mCmd = cmd;
mEvent = event;
}
public int getCode() {
return mEvent != null ? mEvent.getCode() : -1;
}
public String getCmd() {
return mCmd;
}
/**
* Rethrow as a {@link RuntimeException} subclass that is handled by
* {@link Parcel#writeException(Exception)}.
*/
public IllegalArgumentException rethrowAsParcelableException() {
throw new IllegalStateException(getMessage(), this);
}
}

View File

@@ -1,267 +0,0 @@
/*
* 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;
import android.util.Log;
import java.io.FileDescriptor;
import java.util.ArrayList;
/**
* Parsed event from native side of {@link NativeDaemonConnector}.
*/
public class NativeDaemonEvent {
// TODO: keep class ranges in sync with ResponseCode.h
// TODO: swap client and server error ranges to roughly mirror HTTP spec
private final int mCmdNumber;
private final int mCode;
private final String mMessage;
private final String mRawEvent;
private final String mLogMessage;
private String[] mParsed;
private FileDescriptor[] mFdList;
private NativeDaemonEvent(int cmdNumber, int code, String message,
String rawEvent, String logMessage, FileDescriptor[] fdList) {
mCmdNumber = cmdNumber;
mCode = code;
mMessage = message;
mRawEvent = rawEvent;
mLogMessage = logMessage;
mParsed = null;
mFdList = fdList;
}
static public final String SENSITIVE_MARKER = "{{sensitive}}";
public int getCmdNumber() {
return mCmdNumber;
}
public int getCode() {
return mCode;
}
public String getMessage() {
return mMessage;
}
public FileDescriptor[] getFileDescriptors() {
return mFdList;
}
@Deprecated
public String getRawEvent() {
return mRawEvent;
}
@Override
public String toString() {
return mLogMessage;
}
/**
* Test if event represents a partial response which is continued in
* additional subsequent events.
*/
public boolean isClassContinue() {
return mCode >= 100 && mCode < 200;
}
/**
* Test if event represents a command success.
*/
public boolean isClassOk() {
return mCode >= 200 && mCode < 300;
}
/**
* Test if event represents a remote native daemon error.
*/
public boolean isClassServerError() {
return mCode >= 400 && mCode < 500;
}
/**
* Test if event represents a command syntax or argument error.
*/
public boolean isClassClientError() {
return mCode >= 500 && mCode < 600;
}
/**
* Test if event represents an unsolicited event from native daemon.
*/
public boolean isClassUnsolicited() {
return isClassUnsolicited(mCode);
}
private static boolean isClassUnsolicited(int code) {
return code >= 600 && code < 700;
}
/**
* Verify this event matches the given code.
*
* @throws IllegalStateException if {@link #getCode()} doesn't match.
*/
public void checkCode(int code) {
if (mCode != code) {
throw new IllegalStateException("Expected " + code + " but was: " + this);
}
}
/**
* Parse the given raw event into {@link NativeDaemonEvent} instance.
*
* @throws IllegalArgumentException when line doesn't match format expected
* from native side.
*/
public static NativeDaemonEvent parseRawEvent(String rawEvent, FileDescriptor[] fdList) {
final String[] parsed = rawEvent.split(" ");
if (parsed.length < 2) {
throw new IllegalArgumentException("Insufficient arguments");
}
int skiplength = 0;
final int code;
try {
code = Integer.parseInt(parsed[0]);
skiplength = parsed[0].length() + 1;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("problem parsing code", e);
}
int cmdNumber = -1;
if (isClassUnsolicited(code) == false) {
if (parsed.length < 3) {
throw new IllegalArgumentException("Insufficient arguemnts");
}
try {
cmdNumber = Integer.parseInt(parsed[1]);
skiplength += parsed[1].length() + 1;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("problem parsing cmdNumber", e);
}
}
String logMessage = rawEvent;
if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
skiplength += parsed[2].length() + 1;
logMessage = parsed[0] + " " + parsed[1] + " {}";
}
final String message = rawEvent.substring(skiplength);
return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage, fdList);
}
/**
* Filter the given {@link NativeDaemonEvent} list, returning
* {@link #getMessage()} for any events matching the requested code.
*/
public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
final ArrayList<String> result = new ArrayList<>();
for (NativeDaemonEvent event : events) {
if (event.getCode() == matchCode) {
result.add(event.getMessage());
}
}
return result.toArray(new String[result.size()]);
}
/**
* Find the Nth field of the event.
*
* This ignores and code or cmdNum, the first return value is given for N=0.
* Also understands "\"quoted\" multiword responses" and tries them as a single field
*/
public String getField(int n) {
if (mParsed == null) {
mParsed = unescapeArgs(mRawEvent);
}
n += 2; // skip code and command#
if (n > mParsed.length) return null;
return mParsed[n];
}
public static String[] unescapeArgs(String rawEvent) {
final boolean DEBUG_ROUTINE = false;
final String LOGTAG = "unescapeArgs";
final ArrayList<String> parsed = new ArrayList<String>();
final int length = rawEvent.length();
int current = 0;
int wordEnd = -1;
boolean quoted = false;
if (DEBUG_ROUTINE) Log.e(LOGTAG, "parsing '" + rawEvent + "'");
if (rawEvent.charAt(current) == '\"') {
quoted = true;
current++;
}
while (current < length) {
// find the end of the word
char terminator = quoted ? '\"' : ' ';
wordEnd = current;
while (wordEnd < length && rawEvent.charAt(wordEnd) != terminator) {
if (rawEvent.charAt(wordEnd) == '\\') {
// skip the escaped char
++wordEnd;
}
++wordEnd;
}
if (wordEnd > length) wordEnd = length;
String word = rawEvent.substring(current, wordEnd);
current += word.length();
if (!quoted) {
word = word.trim();
} else {
current++; // skip the trailing quote
}
// unescape stuff within the word
word = word.replace("\\\\", "\\");
word = word.replace("\\\"", "\"");
if (DEBUG_ROUTINE) Log.e(LOGTAG, "found '" + word + "'");
parsed.add(word);
// find the beginning of the next word - either of these options
int nextSpace = rawEvent.indexOf(' ', current);
int nextQuote = rawEvent.indexOf(" \"", current);
if (DEBUG_ROUTINE) {
Log.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
}
if (nextQuote > -1 && nextQuote <= nextSpace) {
quoted = true;
current = nextQuote + 2;
} else {
quoted = false;
if (nextSpace > -1) {
current = nextSpace + 1;
}
} // else we just start the next word after the current and read til the end
if (DEBUG_ROUTINE) {
Log.e(LOGTAG, "next loop - current=" + current
+ ", length=" + length + ", quoted=" + quoted);
}
}
return parsed.toArray(new String[parsed.size()]);
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
/**
* An exception that indicates there was a timeout with a
* {@link NativeDaemonConnector} operation.
*/
public class NativeDaemonTimeoutException extends NativeDaemonConnectorException {
public NativeDaemonTimeoutException(String command, NativeDaemonEvent event) {
super(command, event);
}
}

View File

@@ -1,139 +0,0 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.net.INetd;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.system.ErrnoException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.BpfMap;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.Struct.U32;
/**
* Monitor interface added (without removed) and right interface name and its index to bpf map.
*/
public class BpfInterfaceMapUpdater {
private static final String TAG = BpfInterfaceMapUpdater.class.getSimpleName();
// This is current path but may be changed soon.
private static final String IFACE_INDEX_NAME_MAP_PATH =
"/sys/fs/bpf/map_netd_iface_index_name_map";
private final IBpfMap<U32, InterfaceMapValue> mBpfMap;
private final INetd mNetd;
private final Handler mHandler;
private final Dependencies mDeps;
public BpfInterfaceMapUpdater(Context ctx, Handler handler) {
this(ctx, handler, new Dependencies());
}
@VisibleForTesting
public BpfInterfaceMapUpdater(Context ctx, Handler handler, Dependencies deps) {
mDeps = deps;
mBpfMap = deps.getInterfaceMap();
mNetd = deps.getINetd(ctx);
mHandler = handler;
}
/**
* Dependencies of BpfInerfaceMapUpdater, for injection in tests.
*/
@VisibleForTesting
public static class Dependencies {
/** Create BpfMap for updating interface and index mapping. */
public IBpfMap<U32, InterfaceMapValue> getInterfaceMap() {
try {
return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH, BpfMap.BPF_F_RDWR,
U32.class, InterfaceMapValue.class);
} catch (ErrnoException e) {
Log.e(TAG, "Cannot create interface map: " + e);
return null;
}
}
/** Get InterfaceParams for giving interface name. */
public InterfaceParams getInterfaceParams(String ifaceName) {
return InterfaceParams.getByName(ifaceName);
}
/** Get INetd binder object. */
public INetd getINetd(Context ctx) {
return INetd.Stub.asInterface((IBinder) ctx.getSystemService(Context.NETD_SERVICE));
}
}
/**
* Start listening interface update event.
* Query current interface names before listening.
*/
public void start() {
mHandler.post(() -> {
if (mBpfMap == null) {
Log.wtf(TAG, "Fail to start: Null bpf map");
return;
}
try {
// TODO: use a NetlinkMonitor and listen for RTM_NEWLINK messages instead.
mNetd.registerUnsolicitedEventListener(new InterfaceChangeObserver());
} catch (RemoteException e) {
Log.wtf(TAG, "Unable to register netd UnsolicitedEventListener, " + e);
}
final String[] ifaces;
try {
// TODO: use a netlink dump to get the current interface list.
ifaces = mNetd.interfaceGetList();
} catch (RemoteException | ServiceSpecificException e) {
Log.wtf(TAG, "Unable to query interface names by netd, " + e);
return;
}
for (String ifaceName : ifaces) {
addInterface(ifaceName);
}
});
}
private void addInterface(String ifaceName) {
final InterfaceParams iface = mDeps.getInterfaceParams(ifaceName);
if (iface == null) {
Log.e(TAG, "Unable to get InterfaceParams for " + ifaceName);
return;
}
try {
mBpfMap.updateEntry(new U32(iface.index), new InterfaceMapValue(ifaceName));
} catch (ErrnoException e) {
Log.e(TAG, "Unable to update entry for " + ifaceName + ", " + e);
}
}
private class InterfaceChangeObserver extends BaseNetdUnsolicitedEventListener {
@Override
public void onInterfaceAdded(String ifName) {
mHandler.post(() -> addInterface(ifName));
}
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) 2022 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.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;
/**
* Key for cookie tag map.
*/
public class CookieTagMapKey extends Struct {
@Field(order = 0, type = Type.S64)
public final long socketCookie;
public CookieTagMapKey(final long socketCookie) {
this.socketCookie = socketCookie;
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) 2022 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.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;
/**
* Value for cookie tag map.
*/
public class CookieTagMapValue extends Struct {
@Field(order = 0, type = Type.U32)
public final long uid;
@Field(order = 1, type = Type.U32)
public final long tag;
public CookieTagMapValue(final long uid, final long tag) {
this.uid = uid;
this.tag = tag;
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright (C) 2014 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 android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* This class provides APIs to do a delayed data write to a given {@link OutputStream}.
*/
public class DelayedDiskWrite {
private static final String TAG = "DelayedDiskWrite";
private HandlerThread mDiskWriteHandlerThread;
private Handler mDiskWriteHandler;
/* Tracks multiple writes on the same thread */
private int mWriteSequence = 0;
/**
* Used to do a delayed data write to a given {@link OutputStream}.
*/
public interface Writer {
/**
* write data to a given {@link OutputStream}.
*/
void onWriteCalled(DataOutputStream out) throws IOException;
}
/**
* Do a delayed data write to a given output stream opened from filePath.
*/
public void write(final String filePath, final Writer w) {
write(filePath, w, true);
}
/**
* Do a delayed data write to a given output stream opened from filePath.
*/
public void write(final String filePath, final Writer w, final boolean open) {
if (TextUtils.isEmpty(filePath)) {
throw new IllegalArgumentException("empty file path");
}
/* Do a delayed write to disk on a separate handler thread */
synchronized (this) {
if (++mWriteSequence == 1) {
mDiskWriteHandlerThread = new HandlerThread("DelayedDiskWriteThread");
mDiskWriteHandlerThread.start();
mDiskWriteHandler = new Handler(mDiskWriteHandlerThread.getLooper());
}
}
mDiskWriteHandler.post(new Runnable() {
@Override
public void run() {
doWrite(filePath, w, open);
}
});
}
private void doWrite(String filePath, Writer w, boolean open) {
DataOutputStream out = null;
try {
if (open) {
out = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream(filePath)));
}
w.onWriteCalled(out);
} catch (IOException e) {
loge("Error writing data file " + filePath);
} finally {
if (out != null) {
try {
out.close();
} catch (Exception e) { }
}
// Quit if no more writes sent
synchronized (this) {
if (--mWriteSequence == 0) {
mDiskWriteHandler.getLooper().quit();
mDiskWriteHandler = null;
mDiskWriteHandlerThread = null;
}
}
}
}
private void loge(String s) {
Log.e(TAG, s);
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) 2022 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.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;
/**
* The value of bpf interface index map which is used for NetworkStatsService.
*/
public class InterfaceMapValue extends Struct {
@Field(order = 0, type = Type.ByteArray, arraysize = 16)
public final byte[] interfaceName;
public InterfaceMapValue(String iface) {
final byte[] ifaceArray = iface.getBytes();
interfaceName = new byte[16];
// All array bytes after the interface name, if any, must be 0.
System.arraycopy(ifaceArray, 0, interfaceName, 0, ifaceArray.length);
}
}

View File

@@ -1,449 +0,0 @@
/*
* Copyright (C) 2014 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 android.net.InetAddresses;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.ProxyInfo;
import android.net.StaticIpConfiguration;
import android.net.Uri;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.ProxyUtils;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
/**
* This class provides an API to store and manage L3 network IP configuration.
*/
public class IpConfigStore {
private static final String TAG = "IpConfigStore";
private static final boolean DBG = false;
protected final DelayedDiskWrite mWriter;
/* IP and proxy configuration keys */
protected static final String ID_KEY = "id";
protected static final String IP_ASSIGNMENT_KEY = "ipAssignment";
protected static final String LINK_ADDRESS_KEY = "linkAddress";
protected static final String GATEWAY_KEY = "gateway";
protected static final String DNS_KEY = "dns";
protected static final String PROXY_SETTINGS_KEY = "proxySettings";
protected static final String PROXY_HOST_KEY = "proxyHost";
protected static final String PROXY_PORT_KEY = "proxyPort";
protected static final String PROXY_PAC_FILE = "proxyPac";
protected static final String EXCLUSION_LIST_KEY = "exclusionList";
protected static final String EOS = "eos";
protected static final int IPCONFIG_FILE_VERSION = 3;
public IpConfigStore(DelayedDiskWrite writer) {
mWriter = writer;
}
public IpConfigStore() {
this(new DelayedDiskWrite());
}
private static boolean writeConfig(DataOutputStream out, String configKey,
IpConfiguration config) throws IOException {
return writeConfig(out, configKey, config, IPCONFIG_FILE_VERSION);
}
/**
* Write the IP configuration with the given parameters to {@link DataOutputStream}.
*/
@VisibleForTesting
public static boolean writeConfig(DataOutputStream out, String configKey,
IpConfiguration config, int version) throws IOException {
boolean written = false;
try {
switch (config.getIpAssignment()) {
case STATIC:
out.writeUTF(IP_ASSIGNMENT_KEY);
out.writeUTF(config.getIpAssignment().toString());
StaticIpConfiguration staticIpConfiguration = config.getStaticIpConfiguration();
if (staticIpConfiguration != null) {
if (staticIpConfiguration.getIpAddress() != null) {
LinkAddress ipAddress = staticIpConfiguration.getIpAddress();
out.writeUTF(LINK_ADDRESS_KEY);
out.writeUTF(ipAddress.getAddress().getHostAddress());
out.writeInt(ipAddress.getPrefixLength());
}
if (staticIpConfiguration.getGateway() != null) {
out.writeUTF(GATEWAY_KEY);
out.writeInt(0); // Default route.
out.writeInt(1); // Have a gateway.
out.writeUTF(staticIpConfiguration.getGateway().getHostAddress());
}
for (InetAddress inetAddr : staticIpConfiguration.getDnsServers()) {
out.writeUTF(DNS_KEY);
out.writeUTF(inetAddr.getHostAddress());
}
}
written = true;
break;
case DHCP:
out.writeUTF(IP_ASSIGNMENT_KEY);
out.writeUTF(config.getIpAssignment().toString());
written = true;
break;
case UNASSIGNED:
/* Ignore */
break;
default:
loge("Ignore invalid ip assignment while writing");
break;
}
switch (config.getProxySettings()) {
case STATIC:
ProxyInfo proxyProperties = config.getHttpProxy();
String exclusionList = ProxyUtils.exclusionListAsString(
proxyProperties.getExclusionList());
out.writeUTF(PROXY_SETTINGS_KEY);
out.writeUTF(config.getProxySettings().toString());
out.writeUTF(PROXY_HOST_KEY);
out.writeUTF(proxyProperties.getHost());
out.writeUTF(PROXY_PORT_KEY);
out.writeInt(proxyProperties.getPort());
if (exclusionList != null) {
out.writeUTF(EXCLUSION_LIST_KEY);
out.writeUTF(exclusionList);
}
written = true;
break;
case PAC:
ProxyInfo proxyPacProperties = config.getHttpProxy();
out.writeUTF(PROXY_SETTINGS_KEY);
out.writeUTF(config.getProxySettings().toString());
out.writeUTF(PROXY_PAC_FILE);
out.writeUTF(proxyPacProperties.getPacFileUrl().toString());
written = true;
break;
case NONE:
out.writeUTF(PROXY_SETTINGS_KEY);
out.writeUTF(config.getProxySettings().toString());
written = true;
break;
case UNASSIGNED:
/* Ignore */
break;
default:
loge("Ignore invalid proxy settings while writing");
break;
}
if (written) {
out.writeUTF(ID_KEY);
if (version < 3) {
out.writeInt(Integer.valueOf(configKey));
} else {
out.writeUTF(configKey);
}
}
} catch (NullPointerException e) {
loge("Failure in writing " + config + e);
}
out.writeUTF(EOS);
return written;
}
/**
* @deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead.
* New method uses string as network identifier which could be interface name or MAC address or
* other token.
*/
@Deprecated
public void writeIpAndProxyConfigurationsToFile(String filePath,
final SparseArray<IpConfiguration> networks) {
mWriter.write(filePath, out -> {
out.writeInt(IPCONFIG_FILE_VERSION);
for (int i = 0; i < networks.size(); i++) {
writeConfig(out, String.valueOf(networks.keyAt(i)), networks.valueAt(i));
}
});
}
/**
* Write the IP configuration associated to the target networks to the destination path.
*/
public void writeIpConfigurations(String filePath,
ArrayMap<String, IpConfiguration> networks) {
mWriter.write(filePath, out -> {
out.writeInt(IPCONFIG_FILE_VERSION);
for (int i = 0; i < networks.size(); i++) {
writeConfig(out, networks.keyAt(i), networks.valueAt(i));
}
});
}
/**
* Read the IP configuration from the destination path to {@link BufferedInputStream}.
*/
public static ArrayMap<String, IpConfiguration> readIpConfigurations(String filePath) {
BufferedInputStream bufferedInputStream;
try {
bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
} catch (FileNotFoundException e) {
// Return an empty array here because callers expect an empty array when the file is
// not present.
loge("Error opening configuration file: " + e);
return new ArrayMap<>(0);
}
return readIpConfigurations(bufferedInputStream);
}
/** @deprecated use {@link #readIpConfigurations(String)} */
@Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
BufferedInputStream bufferedInputStream;
try {
bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
} catch (FileNotFoundException e) {
// Return an empty array here because callers expect an empty array when the file is
// not present.
loge("Error opening configuration file: " + e);
return new SparseArray<>();
}
return readIpAndProxyConfigurations(bufferedInputStream);
}
/** @deprecated use {@link #readIpConfigurations(InputStream)} */
@Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(
InputStream inputStream) {
ArrayMap<String, IpConfiguration> networks = readIpConfigurations(inputStream);
if (networks == null) {
return null;
}
SparseArray<IpConfiguration> networksById = new SparseArray<>();
for (int i = 0; i < networks.size(); i++) {
int id = Integer.valueOf(networks.keyAt(i));
networksById.put(id, networks.valueAt(i));
}
return networksById;
}
/** Returns a map of network identity token and {@link IpConfiguration}. */
public static ArrayMap<String, IpConfiguration> readIpConfigurations(
InputStream inputStream) {
ArrayMap<String, IpConfiguration> networks = new ArrayMap<>();
DataInputStream in = null;
try {
in = new DataInputStream(inputStream);
int version = in.readInt();
if (version != 3 && version != 2 && version != 1) {
loge("Bad version on IP configuration file, ignore read");
return null;
}
while (true) {
String uniqueToken = null;
// Default is DHCP with no proxy
IpAssignment ipAssignment = IpAssignment.DHCP;
ProxySettings proxySettings = ProxySettings.NONE;
StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
LinkAddress linkAddress = null;
InetAddress gatewayAddress = null;
String proxyHost = null;
String pacFileUrl = null;
int proxyPort = -1;
String exclusionList = null;
String key;
final List<InetAddress> dnsServers = new ArrayList<>();
do {
key = in.readUTF();
try {
if (key.equals(ID_KEY)) {
if (version < 3) {
int id = in.readInt();
uniqueToken = String.valueOf(id);
} else {
uniqueToken = in.readUTF();
}
} else if (key.equals(IP_ASSIGNMENT_KEY)) {
ipAssignment = IpAssignment.valueOf(in.readUTF());
} else if (key.equals(LINK_ADDRESS_KEY)) {
LinkAddress parsedLinkAddress =
new LinkAddress(
InetAddresses.parseNumericAddress(in.readUTF()),
in.readInt());
if (parsedLinkAddress.getAddress() instanceof Inet4Address
&& linkAddress == null) {
linkAddress = parsedLinkAddress;
} else {
loge("Non-IPv4 or duplicate address: " + parsedLinkAddress);
}
} else if (key.equals(GATEWAY_KEY)) {
LinkAddress dest = null;
InetAddress gateway = null;
if (version == 1) {
// only supported default gateways - leave the dest/prefix empty
gateway = InetAddresses.parseNumericAddress(in.readUTF());
if (gatewayAddress == null) {
gatewayAddress = gateway;
} else {
loge("Duplicate gateway: " + gateway.getHostAddress());
}
} else {
if (in.readInt() == 1) {
dest =
new LinkAddress(
InetAddresses.parseNumericAddress(in.readUTF()),
in.readInt());
}
if (in.readInt() == 1) {
gateway = InetAddresses.parseNumericAddress(in.readUTF());
}
// If the destination is a default IPv4 route, use the gateway
// address unless already set. If there is no destination, assume
// it is default route and use the gateway address in all cases.
if (dest == null) {
gatewayAddress = gateway;
} else if (dest.getAddress() instanceof Inet4Address
&& dest.getPrefixLength() == 0 && gatewayAddress == null) {
gatewayAddress = gateway;
} else {
loge("Non-IPv4 default or duplicate route: "
+ dest.getAddress());
}
}
} else if (key.equals(DNS_KEY)) {
dnsServers.add(InetAddresses.parseNumericAddress(in.readUTF()));
} else if (key.equals(PROXY_SETTINGS_KEY)) {
proxySettings = ProxySettings.valueOf(in.readUTF());
} else if (key.equals(PROXY_HOST_KEY)) {
proxyHost = in.readUTF();
} else if (key.equals(PROXY_PORT_KEY)) {
proxyPort = in.readInt();
} else if (key.equals(PROXY_PAC_FILE)) {
pacFileUrl = in.readUTF();
} else if (key.equals(EXCLUSION_LIST_KEY)) {
exclusionList = in.readUTF();
} else if (key.equals(EOS)) {
break;
} else {
loge("Ignore unknown key " + key + "while reading");
}
} catch (IllegalArgumentException e) {
loge("Ignore invalid address while reading" + e);
}
} while (true);
staticIpConfiguration = new StaticIpConfiguration.Builder()
.setIpAddress(linkAddress)
.setGateway(gatewayAddress)
.setDnsServers(dnsServers)
.build();
if (uniqueToken != null) {
IpConfiguration config = new IpConfiguration();
networks.put(uniqueToken, config);
switch (ipAssignment) {
case STATIC:
config.setStaticIpConfiguration(staticIpConfiguration);
config.setIpAssignment(ipAssignment);
break;
case DHCP:
config.setIpAssignment(ipAssignment);
break;
case UNASSIGNED:
loge("BUG: Found UNASSIGNED IP on file, use DHCP");
config.setIpAssignment(IpAssignment.DHCP);
break;
default:
loge("Ignore invalid ip assignment while reading.");
config.setIpAssignment(IpAssignment.UNASSIGNED);
break;
}
switch (proxySettings) {
case STATIC:
ProxyInfo proxyInfo = ProxyInfo.buildDirectProxy(proxyHost, proxyPort,
ProxyUtils.exclusionStringAsList(exclusionList));
config.setProxySettings(proxySettings);
config.setHttpProxy(proxyInfo);
break;
case PAC:
ProxyInfo proxyPacProperties =
ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
config.setProxySettings(proxySettings);
config.setHttpProxy(proxyPacProperties);
break;
case NONE:
config.setProxySettings(proxySettings);
break;
case UNASSIGNED:
loge("BUG: Found UNASSIGNED proxy on file, use NONE");
config.setProxySettings(ProxySettings.NONE);
break;
default:
loge("Ignore invalid proxy settings while reading");
config.setProxySettings(ProxySettings.UNASSIGNED);
break;
}
} else {
if (DBG) log("Missing id while parsing configuration");
}
}
} catch (EOFException ignore) {
} catch (IOException e) {
loge("Error parsing configuration: " + e);
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) { }
}
}
return networks;
}
protected static void loge(String s) {
Log.e(TAG, s);
}
protected static void log(String s) {
Log.d(TAG, s);
}
}

View File

@@ -1,505 +0,0 @@
/*
* 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.INTERFACES_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.UID_ALL;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.NetworkStats;
import android.net.UnderlyingNetworkInfo;
import android.os.ServiceSpecificException;
import android.os.StrictMode;
import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ProcFileReader;
import com.android.net.module.util.CollectionUtils;
import com.android.server.BpfNetMaps;
import libcore.io.IoUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Creates {@link NetworkStats} instances by parsing various {@code /proc/}
* files as needed.
*
* @hide
*/
public class NetworkStatsFactory {
static {
System.loadLibrary("service-connectivity");
}
private static final String TAG = "NetworkStatsFactory";
private static final boolean USE_NATIVE_PARSING = true;
private static final boolean VALIDATE_NATIVE_STATS = false;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
private final File mStatsXtIfaceAll;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
private final File mStatsXtIfaceFmt;
/** Path to {@code /proc/net/xt_qtaguid/stats}. */
private final File mStatsXtUid;
private final boolean mUseBpfStats;
private final Context mContext;
private final BpfNetMaps mBpfNetMaps;
/**
* 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 final Object mPersistentDataLock = new Object();
/** Set containing info about active VPNs and their underlying networks. */
private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0];
// A persistent snapshot of cumulative stats since device start
@GuardedBy("mPersistentDataLock")
private NetworkStats mPersistSnapshot;
// The persistent snapshot of tun and 464xlat adjusted stats since device start
@GuardedBy("mPersistentDataLock")
private NetworkStats mTunAnd464xlatAdjustedStats;
/**
* (Stacked interface) -> (base interface) association for all connected ifaces since boot.
*
* Because counters must never roll backwards, once a given interface is stacked on top of an
* underlying interface, the stacked interface can never be stacked on top of
* another interface. */
private final ConcurrentHashMap<String, String> mStackedIfaces
= new ConcurrentHashMap<>();
/** Informs the factory of a new stacked interface. */
public void noteStackedIface(String stackedIface, String baseIface) {
if (stackedIface != null && baseIface != null) {
mStackedIfaces.put(stackedIface, baseIface);
}
}
/**
* 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 void updateUnderlyingNetworkInfos(UnderlyingNetworkInfo[] vpnArray) {
mUnderlyingNetworkInfos = vpnArray.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
* on which the specified ones are stacked. Stacked interfaces are those noted with
* {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
* is called are guaranteed to be included.
*/
public String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
return null;
}
HashSet<String> relatedIfaces = new HashSet<>(Arrays.asList(requiredIfaces));
// ConcurrentHashMap's EntrySet iterators are "guaranteed to traverse
// elements as they existed upon construction exactly once, and may
// (but are not guaranteed to) reflect any modifications subsequent to construction".
// This is enough here.
for (Map.Entry<String, String> entry : mStackedIfaces.entrySet()) {
if (relatedIfaces.contains(entry.getKey())) {
relatedIfaces.add(entry.getValue());
} else if (relatedIfaces.contains(entry.getValue())) {
relatedIfaces.add(entry.getKey());
}
}
String[] outArray = new String[relatedIfaces.size()];
return relatedIfaces.toArray(outArray);
}
/**
* Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
* @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
*/
public void apply464xlatAdjustments(NetworkStats baseTraffic, NetworkStats stackedTraffic) {
NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, mStackedIfaces);
}
public NetworkStatsFactory(@NonNull Context ctx) {
this(ctx, new File("/proc/"), true);
}
@VisibleForTesting
public NetworkStatsFactory(@NonNull Context ctx, File procRoot, boolean useBpfStats) {
mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats;
mBpfNetMaps = new BpfNetMaps();
synchronized (mPersistentDataLock) {
mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
}
mContext = ctx;
}
public NetworkStats readBpfNetworkStatsDev() throws IOException {
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
if (nativeReadNetworkStatsDev(stats) != 0) {
throw new IOException("Failed to parse bpf iface stats");
}
return stats;
}
/**
* Parse and return interface-level summary {@link NetworkStats} measured
* using {@code /proc/net/dev} style hooks, which may include non IP layer
* traffic. Values monotonically increase since device boot, and may include
* details about inactive interfaces.
*
* @throws IllegalStateException when problem parsing stats.
*/
public NetworkStats readNetworkStatsSummaryDev() throws IOException {
// Return xt_bpf stats if switched to bpf module.
if (mUseBpfStats)
return readBpfNetworkStatsDev();
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
final NetworkStats.Entry entry = new NetworkStats.Entry();
ProcFileReader reader = null;
try {
reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceAll));
while (reader.hasMoreData()) {
entry.iface = reader.nextString();
entry.uid = UID_ALL;
entry.set = SET_ALL;
entry.tag = TAG_NONE;
final boolean active = reader.nextInt() != 0;
// always include snapshot values
entry.rxBytes = reader.nextLong();
entry.rxPackets = reader.nextLong();
entry.txBytes = reader.nextLong();
entry.txPackets = reader.nextLong();
// fold in active numbers, but only when active
if (active) {
entry.rxBytes += reader.nextLong();
entry.rxPackets += reader.nextLong();
entry.txBytes += reader.nextLong();
entry.txPackets += reader.nextLong();
}
stats.insertEntry(entry);
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
throw protocolExceptionWithCause("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
}
return stats;
}
/**
* Parse and return interface-level summary {@link NetworkStats}. Designed
* to return only IP layer traffic. Values monotonically increase since
* device boot, and may include details about inactive interfaces.
*
* @throws IllegalStateException when problem parsing stats.
*/
public NetworkStats readNetworkStatsSummaryXt() throws IOException {
// Return xt_bpf stats if qtaguid module is replaced.
if (mUseBpfStats)
return readBpfNetworkStatsDev();
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
// return null when kernel doesn't support
if (!mStatsXtIfaceFmt.exists()) return null;
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
final NetworkStats.Entry entry = new NetworkStats.Entry();
ProcFileReader reader = null;
try {
// open and consume header line
reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt));
reader.finishLine();
while (reader.hasMoreData()) {
entry.iface = reader.nextString();
entry.uid = UID_ALL;
entry.set = SET_ALL;
entry.tag = TAG_NONE;
entry.rxBytes = reader.nextLong();
entry.rxPackets = reader.nextLong();
entry.txBytes = reader.nextLong();
entry.txPackets = reader.nextLong();
stats.insertEntry(entry);
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
throw protocolExceptionWithCause("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
}
return stats;
}
public NetworkStats readNetworkStatsDetail() throws IOException {
return readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL);
}
@GuardedBy("mPersistentDataLock")
private void requestSwapActiveStatsMapLocked() throws IOException {
try {
// Do a active map stats swap. Once the swap completes, this code
// can read and clean the inactive map without races.
mBpfNetMaps.swapActiveStatsMap();
} catch (ServiceSpecificException e) {
throw new IOException(e);
}
}
/**
* 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, 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 (mPersistentDataLock) {
// Take a reference. If this gets swapped out, we still have the old reference.
final UnderlyingNetworkInfo[] vpnArray = mUnderlyingNetworkInfos;
// 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) {
requestSwapActiveStatsMapLocked();
// Stats are always read from the inactive map, so they must be read after the
// swap
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
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);
} else {
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL,
INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) {
throw new IOException("Failed to parse network stats");
}
if (VALIDATE_NATIVE_STATS) {
final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid,
UID_ALL, INTERFACES_ALL, TAG_ALL);
assertEquals(javaStats, stats);
}
mPersistSnapshot = stats;
}
} else {
mPersistSnapshot = javaReadNetworkStatsDetail(mStatsXtUid, UID_ALL, INTERFACES_ALL,
TAG_ALL);
}
NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
// Filter return values
adjustedStats.filter(limitUid, limitIfaces, limitTag);
return adjustedStats;
}
}
@GuardedBy("mPersistentDataLock")
private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats,
NetworkStats previousStats, UnderlyingNetworkInfo[] 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(mStackedIfaces);
// Migrate data usage over a VPN to the TUN network.
for (UnderlyingNetworkInfo info : vpnArray) {
delta.migrateTun(info.getOwnerUid(), info.getInterface(),
info.getUnderlyingInterfaces());
// 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.
*/
@VisibleForTesting
public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid,
String[] limitIfaces, int limitTag)
throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
final NetworkStats.Entry entry = new NetworkStats.Entry();
int idx = 1;
int lastIdx = 1;
ProcFileReader reader = null;
try {
// open and consume header line
reader = new ProcFileReader(new FileInputStream(detailPath));
reader.finishLine();
while (reader.hasMoreData()) {
idx = reader.nextInt();
if (idx != lastIdx + 1) {
throw new ProtocolException(
"inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
}
lastIdx = idx;
entry.iface = reader.nextString();
entry.tag = kernelToTag(reader.nextString());
entry.uid = reader.nextInt();
entry.set = reader.nextInt();
entry.rxBytes = reader.nextLong();
entry.rxPackets = reader.nextLong();
entry.txBytes = reader.nextLong();
entry.txPackets = reader.nextLong();
if ((limitIfaces == null || CollectionUtils.contains(limitIfaces, entry.iface))
&& (limitUid == UID_ALL || limitUid == entry.uid)
&& (limitTag == TAG_ALL || limitTag == entry.tag)) {
stats.insertEntry(entry);
}
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
throw protocolExceptionWithCause("problem parsing idx " + idx, e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
}
return stats;
}
public void assertEquals(NetworkStats expected, NetworkStats actual) {
if (expected.size() != actual.size()) {
throw new AssertionError(
"Expected size " + expected.size() + ", actual size " + actual.size());
}
NetworkStats.Entry expectedRow = null;
NetworkStats.Entry actualRow = null;
for (int i = 0; i < expected.size(); i++) {
expectedRow = expected.getValues(i, expectedRow);
actualRow = actual.getValues(i, actualRow);
if (!expectedRow.equals(actualRow)) {
throw new AssertionError(
"Expected row " + i + ": " + expectedRow + ", actual row " + actualRow);
}
}
}
/**
* Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
* format like {@code 0x7fffffff00000000}.
*/
public static int kernelToTag(String string) {
int length = string.length();
if (length > 10) {
return Long.decode(string.substring(0, length - 8)).intValue();
} else {
return 0;
}
}
/**
* Parse statistics from file into given {@link NetworkStats} object. Values
* are expected to monotonically increase since device boot.
*/
@VisibleForTesting
public static native int nativeReadNetworkStatsDetail(NetworkStats stats, String path,
int limitUid, String[] limitIfaces, int limitTag, boolean useBpfStats);
@VisibleForTesting
public static native int nativeReadNetworkStatsDev(NetworkStats stats);
private static ProtocolException protocolExceptionWithCause(String message, Throwable cause) {
ProtocolException pe = new ProtocolException(message);
pe.initCause(cause);
return pe;
}
}

View File

@@ -1,451 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.net;
import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.DataUsageRequest;
import android.net.NetworkIdentitySet;
import android.net.NetworkStack;
import android.net.NetworkStats;
import android.net.NetworkStatsAccess;
import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.netstats.IUsageCallback;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages observers of {@link NetworkStats}. Allows observers to be notified when
* data usage has been reported in {@link NetworkStatsService}. An observer can set
* a threshold of how much data it cares about to be notified.
*/
class NetworkStatsObservers {
private static final String TAG = "NetworkStatsObservers";
private static final boolean LOGV = false;
private static final int MSG_REGISTER = 1;
private static final int MSG_UNREGISTER = 2;
private static final int MSG_UPDATE_STATS = 3;
// All access to this map must be done from the handler thread.
// indexed by DataUsageRequest#requestId
private final SparseArray<RequestInfo> mDataUsageRequests = new SparseArray<>();
// Sequence number of DataUsageRequests
private final AtomicInteger mNextDataUsageRequestId = new AtomicInteger();
// Lazily instantiated when an observer is registered.
private volatile Handler mHandler;
/**
* Creates a wrapper that contains the caller context and a normalized request.
* The request should be returned to the caller app, and the wrapper should be sent to this
* object through #addObserver by the service handler.
*
* <p>It will register the observer asynchronously, so it is safe to call from any thread.
*
* @return the normalized request wrapped within {@link RequestInfo}.
*/
public DataUsageRequest register(Context context, DataUsageRequest inputRequest,
IUsageCallback callback, int callingUid, @NetworkStatsAccess.Level int accessLevel) {
DataUsageRequest request = buildRequest(context, inputRequest, callingUid);
RequestInfo requestInfo = buildRequestInfo(request, callback, callingUid,
accessLevel);
if (LOGV) Log.v(TAG, "Registering observer for " + request);
getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
return request;
}
/**
* Unregister a data usage observer.
*
* <p>It will unregister the observer asynchronously, so it is safe to call from any thread.
*/
public void unregister(DataUsageRequest request, int callingUid) {
getHandler().sendMessage(mHandler.obtainMessage(MSG_UNREGISTER, callingUid, 0 /* ignore */,
request));
}
/**
* Updates data usage statistics of registered observers and notifies if limits are reached.
*
* <p>It will update stats asynchronously, so it is safe to call from any thread.
*/
public void updateStats(NetworkStats xtSnapshot, NetworkStats uidSnapshot,
ArrayMap<String, NetworkIdentitySet> activeIfaces,
ArrayMap<String, NetworkIdentitySet> activeUidIfaces,
long currentTime) {
StatsContext statsContext = new StatsContext(xtSnapshot, uidSnapshot, activeIfaces,
activeUidIfaces, currentTime);
getHandler().sendMessage(mHandler.obtainMessage(MSG_UPDATE_STATS, statsContext));
}
private Handler getHandler() {
if (mHandler == null) {
synchronized (this) {
if (mHandler == null) {
if (LOGV) Log.v(TAG, "Creating handler");
mHandler = new Handler(getHandlerLooperLocked(), mHandlerCallback);
}
}
}
return mHandler;
}
@VisibleForTesting
protected Looper getHandlerLooperLocked() {
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
return handlerThread.getLooper();
}
private Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER: {
handleRegister((RequestInfo) msg.obj);
return true;
}
case MSG_UNREGISTER: {
handleUnregister((DataUsageRequest) msg.obj, msg.arg1 /* callingUid */);
return true;
}
case MSG_UPDATE_STATS: {
handleUpdateStats((StatsContext) msg.obj);
return true;
}
default: {
return false;
}
}
}
};
/**
* Adds a {@link RequestInfo} as an observer.
* Should only be called from the handler thread otherwise there will be a race condition
* on mDataUsageRequests.
*/
private void handleRegister(RequestInfo requestInfo) {
mDataUsageRequests.put(requestInfo.mRequest.requestId, requestInfo);
}
/**
* Removes a {@link DataUsageRequest} if the calling uid is authorized.
* Should only be called from the handler thread otherwise there will be a race condition
* on mDataUsageRequests.
*/
private void handleUnregister(DataUsageRequest request, int callingUid) {
RequestInfo requestInfo;
requestInfo = mDataUsageRequests.get(request.requestId);
if (requestInfo == null) {
if (LOGV) Log.v(TAG, "Trying to unregister unknown request " + request);
return;
}
if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) {
Log.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
return;
}
if (LOGV) Log.v(TAG, "Unregistering " + request);
mDataUsageRequests.remove(request.requestId);
requestInfo.unlinkDeathRecipient();
requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
}
private void handleUpdateStats(StatsContext statsContext) {
if (mDataUsageRequests.size() == 0) {
return;
}
for (int i = 0; i < mDataUsageRequests.size(); i++) {
RequestInfo requestInfo = mDataUsageRequests.valueAt(i);
requestInfo.updateStats(statsContext);
}
}
private DataUsageRequest buildRequest(Context context, DataUsageRequest request,
int callingUid) {
// For non-NETWORK_STACK permission uid, cap the minimum threshold to a safe default to
// avoid too many callbacks.
final long thresholdInBytes = (context.checkPermission(
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, Process.myPid(), callingUid)
== PackageManager.PERMISSION_GRANTED ? request.thresholdInBytes
: Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes));
if (thresholdInBytes > request.thresholdInBytes) {
Log.w(TAG, "Threshold was too low for " + request
+ ". Overriding to a safer default of " + thresholdInBytes + " bytes");
}
return new DataUsageRequest(mNextDataUsageRequestId.incrementAndGet(),
request.template, thresholdInBytes);
}
private RequestInfo buildRequestInfo(DataUsageRequest request, IUsageCallback callback,
int callingUid, @NetworkStatsAccess.Level int accessLevel) {
if (accessLevel <= NetworkStatsAccess.Level.USER) {
return new UserUsageRequestInfo(this, request, callback, callingUid,
accessLevel);
} else {
// Safety check in case a new access level is added and we forgot to update this
if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) {
throw new IllegalArgumentException(
"accessLevel " + accessLevel + " is less than DEVICESUMMARY.");
}
return new NetworkUsageRequestInfo(this, request, callback, callingUid,
accessLevel);
}
}
/**
* Tracks information relevant to a data usage observer.
* It will notice when the calling process dies so we can self-expire.
*/
private abstract static class RequestInfo implements IBinder.DeathRecipient {
private final NetworkStatsObservers mStatsObserver;
protected final DataUsageRequest mRequest;
private final IUsageCallback mCallback;
protected final int mCallingUid;
protected final @NetworkStatsAccess.Level int mAccessLevel;
protected NetworkStatsRecorder mRecorder;
protected NetworkStatsCollection mCollection;
RequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
mStatsObserver = statsObserver;
mRequest = request;
mCallback = callback;
mCallingUid = callingUid;
mAccessLevel = accessLevel;
try {
mCallback.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
binderDied();
}
}
@Override
public void binderDied() {
if (LOGV) {
Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mCallback + ")");
}
mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
callCallback(NetworkStatsManager.CALLBACK_RELEASED);
}
@Override
public String toString() {
return "RequestInfo from uid:" + mCallingUid
+ " for " + mRequest + " accessLevel:" + mAccessLevel;
}
private void unlinkDeathRecipient() {
mCallback.asBinder().unlinkToDeath(this, 0);
}
/**
* Update stats given the samples and interface to identity mappings.
*/
private void updateStats(StatsContext statsContext) {
if (mRecorder == null) {
// First run; establish baseline stats
resetRecorder();
recordSample(statsContext);
return;
}
recordSample(statsContext);
if (checkStats()) {
resetRecorder();
callCallback(NetworkStatsManager.CALLBACK_LIMIT_REACHED);
}
}
private void callCallback(int callbackType) {
try {
if (LOGV) {
Log.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+ " for " + mRequest);
}
switch (callbackType) {
case NetworkStatsManager.CALLBACK_LIMIT_REACHED:
mCallback.onThresholdReached(mRequest);
break;
case NetworkStatsManager.CALLBACK_RELEASED:
mCallback.onCallbackReleased(mRequest);
break;
}
} catch (RemoteException e) {
// May occur naturally in the race of binder death.
Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
}
}
private void resetRecorder() {
mRecorder = new NetworkStatsRecorder();
mCollection = mRecorder.getSinceBoot();
}
protected abstract boolean checkStats();
protected abstract void recordSample(StatsContext statsContext);
private String callbackTypeToName(int callbackType) {
switch (callbackType) {
case NetworkStatsManager.CALLBACK_LIMIT_REACHED:
return "LIMIT_REACHED";
case NetworkStatsManager.CALLBACK_RELEASED:
return "RELEASED";
default:
return "UNKNOWN";
}
}
}
private static class NetworkUsageRequestInfo extends RequestInfo {
NetworkUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
super(statsObserver, request, callback, callingUid, accessLevel);
}
@Override
protected boolean checkStats() {
long bytesSoFar = getTotalBytesForNetwork(mRequest.template);
if (LOGV) {
Log.v(TAG, bytesSoFar + " bytes so far since notification for "
+ mRequest.template);
}
if (bytesSoFar > mRequest.thresholdInBytes) {
return true;
}
return false;
}
@Override
protected void recordSample(StatsContext statsContext) {
// Recorder does not need to be locked in this context since only the handler
// 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,
statsContext.mCurrentTime);
}
/**
* Reads stats matching the given template. {@link NetworkStatsCollection} will aggregate
* over all buckets, which in this case should be only one since we built it big enough
* that it will outlive the caller. If it doesn't, then there will be multiple buckets.
*/
private long getTotalBytesForNetwork(NetworkTemplate template) {
NetworkStats stats = mCollection.getSummary(template,
Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */,
mAccessLevel, mCallingUid);
return stats.getTotalBytes();
}
}
private static class UserUsageRequestInfo extends RequestInfo {
UserUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
IUsageCallback callback, int callingUid,
@NetworkStatsAccess.Level int accessLevel) {
super(statsObserver, request, callback, callingUid, accessLevel);
}
@Override
protected boolean checkStats() {
int[] uidsToMonitor = mCollection.getRelevantUids(mAccessLevel, mCallingUid);
for (int i = 0; i < uidsToMonitor.length; i++) {
long bytesSoFar = getTotalBytesForNetworkUid(mRequest.template, uidsToMonitor[i]);
if (bytesSoFar > mRequest.thresholdInBytes) {
return true;
}
}
return false;
}
@Override
protected void recordSample(StatsContext statsContext) {
// Recorder does not need to be locked in this context since only the handler
// thread will update it. We pass the VPN info so VPN traffic is reattributed to
// responsible apps.
mRecorder.recordSnapshotLocked(statsContext.mUidSnapshot, statsContext.mActiveUidIfaces,
statsContext.mCurrentTime);
}
/**
* Reads all stats matching the given template and uid. Ther history will likely only
* contain one bucket per ident since we build it big enough that it will outlive the
* caller lifetime.
*/
private long getTotalBytesForNetworkUid(NetworkTemplate template, int uid) {
try {
NetworkStatsHistory history = mCollection.getHistory(template, null, uid,
NetworkStats.SET_ALL, NetworkStats.TAG_NONE,
NetworkStatsHistory.FIELD_ALL,
Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */,
mAccessLevel, mCallingUid);
return history.getTotalBytes();
} catch (SecurityException e) {
if (LOGV) {
Log.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
+ uid);
}
return 0;
}
}
}
private static class StatsContext {
NetworkStats mXtSnapshot;
NetworkStats mUidSnapshot;
ArrayMap<String, NetworkIdentitySet> mActiveIfaces;
ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces;
long mCurrentTime;
StatsContext(NetworkStats xtSnapshot, NetworkStats uidSnapshot,
ArrayMap<String, NetworkIdentitySet> activeIfaces,
ArrayMap<String, NetworkIdentitySet> activeUidIfaces,
long currentTime) {
mXtSnapshot = xtSnapshot;
mUidSnapshot = uidSnapshot;
mActiveIfaces = activeIfaces;
mActiveUidIfaces = activeUidIfaces;
mCurrentTime = currentTime;
}
}
}

View File

@@ -1,507 +0,0 @@
/*
* Copyright (C) 2012 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.TAG_NONE;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.YEAR_IN_MILLIS;
import android.net.NetworkIdentitySet;
import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsAccess;
import android.net.NetworkStatsCollection;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.Binder;
import android.os.DropBoxManager;
import android.service.NetworkStatsRecorderProto;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FileRotator;
import com.android.net.module.util.NetworkStatsUtils;
import libcore.io.IoUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
/**
* Logic to record deltas between periodic {@link NetworkStats} snapshots into
* {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}.
* Keeps pending changes in memory until they pass a specific threshold, in
* bytes. Uses {@link FileRotator} for persistence logic if present.
* <p>
* Not inherently thread safe.
*/
public class NetworkStatsRecorder {
private static final String TAG = "NetworkStatsRecorder";
private static final boolean LOGD = false;
private static final boolean LOGV = false;
private static final String TAG_NETSTATS_DUMP = "netstats_dump";
/** Dump before deleting in {@link #recoverFromWtf()}. */
private static final boolean DUMP_BEFORE_DELETE = true;
private final FileRotator mRotator;
private final NonMonotonicObserver<String> mObserver;
private final DropBoxManager mDropBox;
private final String mCookie;
private final long mBucketDuration;
private final boolean mOnlyTags;
private long mPersistThresholdBytes = 2 * MB_IN_BYTES;
private NetworkStats mLastSnapshot;
private final NetworkStatsCollection mPending;
private final NetworkStatsCollection mSinceBoot;
private final CombiningRewriter mPendingRewriter;
private WeakReference<NetworkStatsCollection> mComplete;
/**
* Non-persisted recorder, with only one bucket. Used by {@link NetworkStatsObservers}.
*/
public NetworkStatsRecorder() {
mRotator = null;
mObserver = null;
mDropBox = null;
mCookie = null;
// set the bucket big enough to have all data in one bucket, but allow some
// slack to avoid overflow
mBucketDuration = YEAR_IN_MILLIS;
mOnlyTags = false;
mPending = null;
mSinceBoot = new NetworkStatsCollection(mBucketDuration);
mPendingRewriter = null;
}
/**
* Persisted recorder.
*/
public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags) {
mRotator = Objects.requireNonNull(rotator, "missing FileRotator");
mObserver = Objects.requireNonNull(observer, "missing NonMonotonicObserver");
mDropBox = Objects.requireNonNull(dropBox, "missing DropBoxManager");
mCookie = cookie;
mBucketDuration = bucketDuration;
mOnlyTags = onlyTags;
mPending = new NetworkStatsCollection(bucketDuration);
mSinceBoot = new NetworkStatsCollection(bucketDuration);
mPendingRewriter = new CombiningRewriter(mPending);
}
public void setPersistThreshold(long thresholdBytes) {
if (LOGV) Log.v(TAG, "setPersistThreshold() with " + thresholdBytes);
mPersistThresholdBytes = NetworkStatsUtils.constrain(
thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
}
public void resetLocked() {
mLastSnapshot = null;
if (mPending != null) {
mPending.reset();
}
if (mSinceBoot != null) {
mSinceBoot.reset();
}
if (mComplete != null) {
mComplete.clear();
}
}
public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) {
return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE,
NetworkStatsAccess.Level.DEVICE, Binder.getCallingUid()).getTotal(null);
}
public NetworkStatsCollection getSinceBoot() {
return mSinceBoot;
}
/**
* Load complete history represented by {@link FileRotator}. Caches
* internally as a {@link WeakReference}, and updated with future
* {@link #recordSnapshotLocked(NetworkStats, Map, long)} snapshots as long
* as reference is valid.
*/
public NetworkStatsCollection getOrLoadCompleteLocked() {
Objects.requireNonNull(mRotator, "missing FileRotator");
NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
if (res == null) {
res = loadLocked(Long.MIN_VALUE, Long.MAX_VALUE);
mComplete = new WeakReference<NetworkStatsCollection>(res);
}
return res;
}
public NetworkStatsCollection getOrLoadPartialLocked(long start, long end) {
Objects.requireNonNull(mRotator, "missing FileRotator");
NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
if (res == null) {
res = loadLocked(start, end);
}
return res;
}
private NetworkStatsCollection loadLocked(long start, long end) {
if (LOGD) Log.d(TAG, "loadLocked() reading from disk for " + mCookie);
final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
try {
mRotator.readMatching(res, start, end);
res.recordCollection(mPending);
} catch (IOException e) {
Log.wtf(TAG, "problem completely reading network stats", e);
recoverFromWtf();
} catch (OutOfMemoryError e) {
Log.wtf(TAG, "problem completely reading network stats", e);
recoverFromWtf();
}
return res;
}
/**
* 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, long currentTimeMillis) {
final HashSet<String> unknownIfaces = new HashSet<>();
// skip recording when snapshot missing
if (snapshot == null) return;
// assume first snapshot is bootstrap and don't record
if (mLastSnapshot == null) {
mLastSnapshot = snapshot;
return;
}
final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
final NetworkStats delta = NetworkStats.subtract(
snapshot, mLastSnapshot, mObserver, mCookie);
final long end = currentTimeMillis;
final long start = end - delta.getElapsedRealtime();
NetworkStats.Entry entry = null;
for (int i = 0; i < delta.size(); i++) {
entry = delta.getValues(i, entry);
// As a last-ditch check, report any negative values and
// clamp them so recording below doesn't croak.
if (entry.isNegative()) {
if (mObserver != null) {
mObserver.foundNonMonotonic(delta, i, mCookie);
}
entry.rxBytes = Math.max(entry.rxBytes, 0);
entry.rxPackets = Math.max(entry.rxPackets, 0);
entry.txBytes = Math.max(entry.txBytes, 0);
entry.txPackets = Math.max(entry.txPackets, 0);
entry.operations = Math.max(entry.operations, 0);
}
final NetworkIdentitySet ident = ifaceIdent.get(entry.iface);
if (ident == null) {
unknownIfaces.add(entry.iface);
continue;
}
// skip when no delta occurred
if (entry.isEmpty()) continue;
// only record tag data when requested
if ((entry.tag == TAG_NONE) != mOnlyTags) {
if (mPending != null) {
mPending.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
}
// also record against boot stats when present
if (mSinceBoot != null) {
mSinceBoot.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
}
// also record against complete dataset when present
if (complete != null) {
complete.recordData(ident, entry.uid, entry.set, entry.tag, start, end, entry);
}
}
}
mLastSnapshot = snapshot;
if (LOGV && unknownIfaces.size() > 0) {
Log.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
}
}
/**
* Consider persisting any pending deltas, if they are beyond
* {@link #mPersistThresholdBytes}.
*/
public void maybePersistLocked(long currentTimeMillis) {
Objects.requireNonNull(mRotator, "missing FileRotator");
final long pendingBytes = mPending.getTotalBytes();
if (pendingBytes >= mPersistThresholdBytes) {
forcePersistLocked(currentTimeMillis);
} else {
mRotator.maybeRotate(currentTimeMillis);
}
}
/**
* Force persisting any pending deltas.
*/
public void forcePersistLocked(long currentTimeMillis) {
Objects.requireNonNull(mRotator, "missing FileRotator");
if (mPending.isDirty()) {
if (LOGD) Log.d(TAG, "forcePersistLocked() writing for " + mCookie);
try {
mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
mRotator.maybeRotate(currentTimeMillis);
mPending.reset();
} catch (IOException e) {
Log.wtf(TAG, "problem persisting pending stats", e);
recoverFromWtf();
} catch (OutOfMemoryError e) {
Log.wtf(TAG, "problem persisting pending stats", e);
recoverFromWtf();
}
}
}
/**
* Remove the given UID from all {@link FileRotator} history, migrating it
* to {@link TrafficStats#UID_REMOVED}.
*/
public void removeUidsLocked(int[] uids) {
if (mRotator != null) {
try {
// Rewrite all persisted data to migrate UID stats
mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uids));
} catch (IOException e) {
Log.wtf(TAG, "problem removing UIDs " + Arrays.toString(uids), e);
recoverFromWtf();
} catch (OutOfMemoryError e) {
Log.wtf(TAG, "problem removing UIDs " + Arrays.toString(uids), e);
recoverFromWtf();
}
}
// Remove any pending stats
if (mPending != null) {
mPending.removeUids(uids);
}
if (mSinceBoot != null) {
mSinceBoot.removeUids(uids);
}
// Clear UID from current stats snapshot
if (mLastSnapshot != null) {
mLastSnapshot.removeUids(uids);
}
final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
if (complete != null) {
complete.removeUids(uids);
}
}
/**
* Rewriter that will combine current {@link NetworkStatsCollection} values
* with anything read from disk, and write combined set to disk. Clears the
* original {@link NetworkStatsCollection} when finished writing.
*/
private static class CombiningRewriter implements FileRotator.Rewriter {
private final NetworkStatsCollection mCollection;
public CombiningRewriter(NetworkStatsCollection collection) {
mCollection = Objects.requireNonNull(collection, "missing NetworkStatsCollection");
}
@Override
public void reset() {
// ignored
}
@Override
public void read(InputStream in) throws IOException {
mCollection.read(in);
}
@Override
public boolean shouldWrite() {
return true;
}
@Override
public void write(OutputStream out) throws IOException {
mCollection.write(out);
mCollection.reset();
}
}
/**
* Rewriter that will remove any {@link NetworkStatsHistory} attributed to
* the requested UID, only writing data back when modified.
*/
public static class RemoveUidRewriter implements FileRotator.Rewriter {
private final NetworkStatsCollection mTemp;
private final int[] mUids;
public RemoveUidRewriter(long bucketDuration, int[] uids) {
mTemp = new NetworkStatsCollection(bucketDuration);
mUids = uids;
}
@Override
public void reset() {
mTemp.reset();
}
@Override
public void read(InputStream in) throws IOException {
mTemp.read(in);
mTemp.clearDirty();
mTemp.removeUids(mUids);
}
@Override
public boolean shouldWrite() {
return mTemp.isDirty();
}
@Override
public void write(OutputStream out) throws IOException {
mTemp.write(out);
}
}
public void importLegacyNetworkLocked(File file) throws IOException {
Objects.requireNonNull(mRotator, "missing FileRotator");
// legacy file still exists; start empty to avoid double importing
mRotator.deleteAll();
final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
collection.readLegacyNetwork(file);
final long startMillis = collection.getStartMillis();
final long endMillis = collection.getEndMillis();
if (!collection.isEmpty()) {
// process legacy data, creating active file at starting time, then
// using end time to possibly trigger rotation.
mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
mRotator.maybeRotate(endMillis);
}
}
public void importLegacyUidLocked(File file) throws IOException {
Objects.requireNonNull(mRotator, "missing FileRotator");
// legacy file still exists; start empty to avoid double importing
mRotator.deleteAll();
final NetworkStatsCollection collection = new NetworkStatsCollection(mBucketDuration);
collection.readLegacyUid(file, mOnlyTags);
final long startMillis = collection.getStartMillis();
final long endMillis = collection.getEndMillis();
if (!collection.isEmpty()) {
// process legacy data, creating active file at starting time, then
// using end time to possibly trigger rotation.
mRotator.rewriteActive(new CombiningRewriter(collection), startMillis);
mRotator.maybeRotate(endMillis);
}
}
public void dumpLocked(IndentingPrintWriter pw, boolean fullHistory) {
if (mPending != null) {
pw.print("Pending bytes: "); pw.println(mPending.getTotalBytes());
}
if (fullHistory) {
pw.println("Complete history:");
getOrLoadCompleteLocked().dump(pw);
} else {
pw.println("History since boot:");
mSinceBoot.dump(pw);
}
}
public void dumpDebugLocked(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
if (mPending != null) {
proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES,
mPending.getTotalBytes());
}
getOrLoadCompleteLocked().dumpDebug(proto,
NetworkStatsRecorderProto.COMPLETE_HISTORY);
proto.end(start);
}
public void dumpCheckin(PrintWriter pw, long start, long end) {
// Only load and dump stats from the requested window
getOrLoadPartialLocked(start, end).dumpCheckin(pw, start, end);
}
/**
* Recover from {@link FileRotator} failure by dumping state to
* {@link DropBoxManager} and deleting contents.
*/
private void recoverFromWtf() {
if (DUMP_BEFORE_DELETE) {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
mRotator.dumpAll(os);
} catch (IOException e) {
// ignore partial contents
os.reset();
} finally {
IoUtils.closeQuietly(os);
}
mDropBox.addData(TAG_NETSTATS_DUMP, os.toByteArray(), 0);
}
mRotator.deleteAll();
}
}

View File

@@ -1,246 +0,0 @@
/*
* Copyright (C) 2020 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.app.usage.NetworkStatsManager.NETWORK_TYPE_5G_NSA;
import static android.app.usage.NetworkStatsManager.getCollapsedRatType;
import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED;
import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA;
import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
import android.annotation.NonNull;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
/**
* Helper class that watches for events that are triggered per subscription.
*/
@TargetApi(Build.VERSION_CODES.TIRAMISU)
public class NetworkStatsSubscriptionsMonitor extends
SubscriptionManager.OnSubscriptionsChangedListener {
/**
* Interface that this monitor uses to delegate event handling to NetworkStatsService.
*/
public interface Delegate {
/**
* Notify that the collapsed RAT type has been changed for any subscription. The method
* will also be triggered for any existing sub when start and stop monitoring.
*
* @param subscriberId IMSI of the subscription.
* @param collapsedRatType collapsed RAT type.
* @see android.app.usage.NetworkStatsManager#getCollapsedRatType(int).
*/
void onCollapsedRatTypeChanged(@NonNull String subscriberId, int collapsedRatType);
}
private final Delegate mDelegate;
/**
* Receivers that watches for {@link TelephonyDisplayInfo} changes for each subscription, to
* monitor the transitioning between Radio Access Technology(RAT) types for each sub.
*/
@NonNull
private final CopyOnWriteArrayList<RatTypeListener> mRatListeners =
new CopyOnWriteArrayList<>();
@NonNull
private final SubscriptionManager mSubscriptionManager;
@NonNull
private final TelephonyManager mTeleManager;
@NonNull
private final Executor mExecutor;
NetworkStatsSubscriptionsMonitor(@NonNull Context context,
@NonNull Executor executor, @NonNull Delegate delegate) {
super();
mSubscriptionManager = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mExecutor = executor;
mDelegate = delegate;
}
@Override
public void onSubscriptionsChanged() {
// Collect active subId list, hidden subId such as opportunistic subscriptions are
// also needed to track CBRS.
final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
// IMSI is needed for every newly added sub. Listener stores subscriberId into it to
// prevent binder call to telephony when querying RAT. Keep listener registration with empty
// IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
// with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
final List<Pair<Integer, String>> filteredNewSubs = new ArrayList<>();
for (final int subId : newSubs) {
final String subscriberId =
mTeleManager.createForSubscriptionId(subId).getSubscriberId();
if (!TextUtils.isEmpty(subscriberId)) {
filteredNewSubs.add(new Pair(subId, subscriberId));
}
}
for (final Pair<Integer, String> sub : filteredNewSubs) {
// Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
// suddenly change regardless of subId, such as switch IMSI feature in modem side.
// If that happens, register new listener with new IMSI and remove old one later.
if (CollectionUtils.any(mRatListeners, it -> it.equalsKey(sub.first, sub.second))) {
continue;
}
final RatTypeListener listener = new RatTypeListener(this, sub.first, sub.second);
mRatListeners.add(listener);
// Register listener to the telephony manager that associated with specific sub.
mTeleManager.createForSubscriptionId(sub.first)
.registerTelephonyCallback(mExecutor, listener);
Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first);
}
for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
// If there is no subId and IMSI matched the listener, removes it.
if (!CollectionUtils.any(filteredNewSubs,
it -> listener.equalsKey(it.first, it.second))) {
handleRemoveRatTypeListener(listener);
}
}
}
@NonNull
private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) {
final ArrayList<Integer> ret = new ArrayList<>();
final int[] ids = subscriptionManager.getCompleteActiveSubscriptionIdList();
for (int id : ids) ret.add(id);
return ret;
}
/**
* Get a collapsed RatType for the given subscriberId.
*
* @param subscriberId the target subscriberId
* @return collapsed RatType for the given subscriberId
*/
public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
final int index = CollectionUtils.indexOf(mRatListeners,
it -> TextUtils.equals(subscriberId, it.mSubscriberId));
return index != -1 ? mRatListeners.get(index).mLastCollapsedRatType
: TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
/**
* Start monitoring events that triggered per subscription.
*/
public void start() {
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this);
}
/**
* Unregister subscription changes and all listeners for each subscription.
*/
public void stop() {
mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
handleRemoveRatTypeListener(listener);
}
}
private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
mTeleManager.createForSubscriptionId(listener.mSubId)
.unregisterTelephonyCallback(listener);
Log.d(NetworkStatsService.TAG, "RAT type listener unregistered for sub " + listener.mSubId);
mRatListeners.remove(listener);
// Removal of subscriptions doesn't generate RAT changed event, fire it for every
// RatTypeListener.
mDelegate.onCollapsedRatTypeChanged(
listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN);
}
static class RatTypeListener extends TelephonyCallback
implements TelephonyCallback.DisplayInfoListener {
// Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}.
@NonNull
private final int mSubId;
// IMSI to identifying the corresponding network from {@link NetworkState}.
// See {@link TelephonyManager#getSubscriberId}.
@NonNull
private final String mSubscriberId;
private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
@NonNull
private final NetworkStatsSubscriptionsMonitor mMonitor;
RatTypeListener(@NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
@NonNull String subscriberId) {
mSubId = subId;
mSubscriberId = subscriberId;
mMonitor = monitor;
}
@Override
public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfo) {
// In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony
// would report RAT = 5G_NR.
// However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and
// network allocates a secondary 5G cell so telephony reports RAT = LTE along with
// NR state as connected. In such case, attributes the data usage to NR.
// See b/160727498.
final boolean is5GNsa = displayInfo.getNetworkType() == NETWORK_TYPE_LTE
&& (displayInfo.getOverrideNetworkType() == OVERRIDE_NETWORK_TYPE_NR_NSA
|| displayInfo.getOverrideNetworkType() == OVERRIDE_NETWORK_TYPE_NR_ADVANCED);
final int networkType =
(is5GNsa ? NETWORK_TYPE_5G_NSA : displayInfo.getNetworkType());
final int collapsedRatType = getCollapsedRatType(networkType);
if (collapsedRatType == mLastCollapsedRatType) return;
if (NetworkStatsService.LOGD) {
Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): "
+ mLastCollapsedRatType + " -> " + collapsedRatType);
}
mLastCollapsedRatType = collapsedRatType;
mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType);
}
@VisibleForTesting
public int getSubId() {
return mSubId;
}
boolean equalsKey(int subId, @NonNull String subscriberId) {
return mSubId == subId && TextUtils.equals(mSubscriberId, subscriberId);
}
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) 2022 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.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;
/**
* Key for both stats maps.
*/
public class StatsMapKey extends Struct {
@Field(order = 0, type = Type.U32)
public final long uid;
@Field(order = 1, type = Type.U32)
public final long tag;
@Field(order = 2, type = Type.U32)
public final long counterSet;
@Field(order = 3, type = Type.U32)
public final long ifaceIndex;
public StatsMapKey(final long uid, final long tag, final long counterSet,
final long ifaceIndex) {
this.uid = uid;
this.tag = tag;
this.counterSet = counterSet;
this.ifaceIndex = ifaceIndex;
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) 2022 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.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;
/**
* Value used for both stats maps and uid stats map.
*/
public class StatsMapValue extends Struct {
@Field(order = 0, type = Type.U63)
public final long rxPackets;
@Field(order = 1, type = Type.U63)
public final long rxBytes;
@Field(order = 2, type = Type.U63)
public final long txPackets;
@Field(order = 3, type = Type.U63)
public final long txBytes;
public StatsMapValue(final long rxPackets, final long rxBytes, final long txPackets,
final long txBytes) {
this.rxPackets = rxPackets;
this.rxBytes = rxBytes;
this.txPackets = txPackets;
this.txBytes = txBytes;
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) 2022 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.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;
/**
* Key for uid stats map.
*/
public class UidStatsMapKey extends Struct {
@Field(order = 0, type = Type.U32)
public final long uid;
public UidStatsMapKey(final long uid) {
this.uid = uid;
}
}

View File

@@ -1,97 +0,0 @@
/*
* 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;
import static com.android.server.NativeDaemonConnector.appendEscaped;
import static com.android.server.NativeDaemonConnector.makeCommand;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.server.NativeDaemonConnector.SensitiveArg;
/**
* Tests for {@link NativeDaemonConnector}.
*/
@MediumTest
public class NativeDaemonConnectorTest extends AndroidTestCase {
private static final String TAG = "NativeDaemonConnectorTest";
public void testArgumentNormal() throws Exception {
final StringBuilder builder = new StringBuilder();
builder.setLength(0);
appendEscaped(builder, "");
assertEquals("", builder.toString());
builder.setLength(0);
appendEscaped(builder, "foo");
assertEquals("foo", builder.toString());
builder.setLength(0);
appendEscaped(builder, "foo\"bar");
assertEquals("foo\\\"bar", builder.toString());
builder.setLength(0);
appendEscaped(builder, "foo\\bar\\\"baz");
assertEquals("foo\\\\bar\\\\\\\"baz", builder.toString());
}
public void testArgumentWithSpaces() throws Exception {
final StringBuilder builder = new StringBuilder();
builder.setLength(0);
appendEscaped(builder, "foo bar");
assertEquals("\"foo bar\"", builder.toString());
builder.setLength(0);
appendEscaped(builder, "foo\"bar\\baz foo");
assertEquals("\"foo\\\"bar\\\\baz foo\"", builder.toString());
}
public void testArgumentWithUtf() throws Exception {
final StringBuilder builder = new StringBuilder();
builder.setLength(0);
appendEscaped(builder, "caf\u00E9 c\u00F6ffee");
assertEquals("\"caf\u00E9 c\u00F6ffee\"", builder.toString());
}
public void testSensitiveArgs() throws Exception {
final StringBuilder rawBuilder = new StringBuilder();
final StringBuilder logBuilder = new StringBuilder();
rawBuilder.setLength(0);
logBuilder.setLength(0);
makeCommand(rawBuilder, logBuilder, 1, "foo", "bar", "baz");
assertEquals("1 foo bar baz\0", rawBuilder.toString());
assertEquals("1 foo bar baz", logBuilder.toString());
rawBuilder.setLength(0);
logBuilder.setLength(0);
makeCommand(rawBuilder, logBuilder, 1, "foo", new SensitiveArg("bar"), "baz");
assertEquals("1 foo bar baz\0", rawBuilder.toString());
assertEquals("1 foo [scrubbed] baz", logBuilder.toString());
rawBuilder.setLength(0);
logBuilder.setLength(0);
makeCommand(rawBuilder, logBuilder, 1, "foo", new SensitiveArg("foo bar"), "baz baz",
new SensitiveArg("wat"));
assertEquals("1 foo \"foo bar\" \"baz baz\" wat\0", rawBuilder.toString());
assertEquals("1 foo [scrubbed] \"baz baz\" [scrubbed]", logBuilder.toString());
}
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright (C) 2018 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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import android.net.InetAddresses;
import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.ProxyInfo;
import android.net.StaticIpConfiguration;
import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Unit tests for {@link IpConfigStore}
*/
@RunWith(AndroidJUnit4.class)
public class IpConfigStoreTest {
private static final int KEY_CONFIG = 17;
private static final String IFACE_1 = "eth0";
private static final String IFACE_2 = "eth1";
private static final String IP_ADDR_1 = "192.168.1.10/24";
private static final String IP_ADDR_2 = "192.168.1.20/24";
private static final String DNS_IP_ADDR_1 = "1.2.3.4";
private static final String DNS_IP_ADDR_2 = "5.6.7.8";
@Test
public void backwardCompatibility2to3() throws IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
DataOutputStream outputStream = new DataOutputStream(byteStream);
final IpConfiguration expectedConfig =
newIpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null);
// Emulate writing to old format.
writeDhcpConfigV2(outputStream, KEY_CONFIG, expectedConfig);
InputStream in = new ByteArrayInputStream(byteStream.toByteArray());
ArrayMap<String, IpConfiguration> configurations = IpConfigStore.readIpConfigurations(in);
assertNotNull(configurations);
assertEquals(1, configurations.size());
IpConfiguration actualConfig = configurations.get(String.valueOf(KEY_CONFIG));
assertNotNull(actualConfig);
assertEquals(expectedConfig, actualConfig);
}
@Test
public void staticIpMultiNetworks() throws Exception {
final ArrayList<InetAddress> dnsServers = new ArrayList<>();
dnsServers.add(InetAddresses.parseNumericAddress(DNS_IP_ADDR_1));
dnsServers.add(InetAddresses.parseNumericAddress(DNS_IP_ADDR_2));
final StaticIpConfiguration staticIpConfiguration1 = new StaticIpConfiguration.Builder()
.setIpAddress(new LinkAddress(IP_ADDR_1))
.setDnsServers(dnsServers).build();
final StaticIpConfiguration staticIpConfiguration2 = new StaticIpConfiguration.Builder()
.setIpAddress(new LinkAddress(IP_ADDR_2))
.setDnsServers(dnsServers).build();
ProxyInfo proxyInfo =
ProxyInfo.buildDirectProxy("10.10.10.10", 88, Arrays.asList("host1", "host2"));
IpConfiguration expectedConfig1 = newIpConfiguration(IpAssignment.STATIC,
ProxySettings.STATIC, staticIpConfiguration1, proxyInfo);
IpConfiguration expectedConfig2 = newIpConfiguration(IpAssignment.STATIC,
ProxySettings.STATIC, staticIpConfiguration2, proxyInfo);
ArrayMap<String, IpConfiguration> expectedNetworks = new ArrayMap<>();
expectedNetworks.put(IFACE_1, expectedConfig1);
expectedNetworks.put(IFACE_2, expectedConfig2);
MockedDelayedDiskWrite writer = new MockedDelayedDiskWrite();
IpConfigStore store = new IpConfigStore(writer);
store.writeIpConfigurations("file/path/not/used/", expectedNetworks);
InputStream in = new ByteArrayInputStream(writer.byteStream.toByteArray());
ArrayMap<String, IpConfiguration> actualNetworks = IpConfigStore.readIpConfigurations(in);
assertNotNull(actualNetworks);
assertEquals(2, actualNetworks.size());
assertEquals(expectedNetworks.get(IFACE_1), actualNetworks.get(IFACE_1));
assertEquals(expectedNetworks.get(IFACE_2), actualNetworks.get(IFACE_2));
}
private IpConfiguration newIpConfiguration(IpAssignment ipAssignment,
ProxySettings proxySettings, StaticIpConfiguration staticIpConfig, ProxyInfo info) {
final IpConfiguration config = new IpConfiguration();
config.setIpAssignment(ipAssignment);
config.setProxySettings(proxySettings);
config.setStaticIpConfiguration(staticIpConfig);
config.setHttpProxy(info);
return config;
}
// This is simplified snapshot of code that was used to store values in V2 format (key as int).
private static void writeDhcpConfigV2(DataOutputStream out, int configKey,
IpConfiguration config) throws IOException {
out.writeInt(2); // VERSION 2
switch (config.getIpAssignment()) {
case DHCP:
out.writeUTF("ipAssignment");
out.writeUTF(config.getIpAssignment().toString());
break;
default:
fail("Not supported in test environment");
}
out.writeUTF("id");
out.writeInt(configKey);
out.writeUTF("eos");
}
/** Synchronously writes into given byte steam */
private static class MockedDelayedDiskWrite extends DelayedDiskWrite {
final ByteArrayOutputStream mByteStream = new ByteArrayOutputStream();
@Override
public void write(String filePath, Writer w) {
DataOutputStream outputStream = new DataOutputStream(mByteStream);
try {
w.onWriteCalled(outputStream);
} catch (IOException e) {
fail();
}
}
}
}