Merge history of ConnectivityT
Renamed files/directories: ConnectivityT/service/Android.bp --> service-t/Sources.bp ConnectivityT/framework-t/Android.bp --> framework-t/Sources.bp ConnectivityT/framework-t/aidl-export --> framework/aidl-export ConnectivityT/service --> service-t ConnectivityT/framework-t --> framework-t ConnectivityT/tests --> tests ConnectivityT/OWNERS --> (removed) BUG: 222234190 TEST: TH Ignore-AOSP-First: Move with history done per-branch Merged-In: I81893df9f327abb84f1561b2b33027a2d23a4d65 Merged-In: I67c703e3f7aa9d5787f032a79ed62e45412baf4f Change-Id: I27a91f1a94f9d807f92762436f533c4b0d0114d5
This commit is contained in:
205
framework-t/Sources.bp
Normal file
205
framework-t/Sources.bp
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
//
|
||||||
|
// 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",
|
||||||
|
],
|
||||||
|
}
|
||||||
46
framework-t/jni/android_net_TrafficStats.cpp
Normal file
46
framework-t/jni/android_net_TrafficStats.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
39
framework-t/jni/onload.cpp
Normal file
39
framework-t/jni/onload.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
742
framework-t/src/android/app/usage/NetworkStats.java
Normal file
742
framework-t/src/android/app/usage/NetworkStats.java
Normal file
@@ -0,0 +1,742 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1181
framework-t/src/android/app/usage/NetworkStatsManager.java
Normal file
1181
framework-t/src/android/app/usage/NetworkStatsManager.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
framework-t/src/android/net/DataUsageRequest.aidl
Normal file
19
framework-t/src/android/net/DataUsageRequest.aidl
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
112
framework-t/src/android/net/DataUsageRequest.java
Normal file
112
framework-t/src/android/net/DataUsageRequest.java
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
729
framework-t/src/android/net/EthernetManager.java
Normal file
729
framework-t/src/android/net/EthernetManager.java
Normal file
@@ -0,0 +1,729 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
102
framework-t/src/android/net/EthernetNetworkSpecifier.java
Normal file
102
framework-t/src/android/net/EthernetNetworkSpecifier.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* 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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
185
framework-t/src/android/net/EthernetNetworkUpdateRequest.java
Normal file
185
framework-t/src/android/net/EthernetNetworkUpdateRequest.java
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
50
framework-t/src/android/net/IEthernetManager.aidl
Normal file
50
framework-t/src/android/net/IEthernetManager.aidl
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
27
framework-t/src/android/net/IEthernetServiceListener.aidl
Normal file
27
framework-t/src/android/net/IEthernetServiceListener.aidl
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
78
framework-t/src/android/net/IIpSecService.aidl
Normal file
78
framework-t/src/android/net/IIpSecService.aidl
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
** 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
104
framework-t/src/android/net/INetworkStatsService.aidl
Normal file
104
framework-t/src/android/net/INetworkStatsService.aidl
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
70
framework-t/src/android/net/INetworkStatsSession.aidl
Normal file
70
framework-t/src/android/net/INetworkStatsSession.aidl
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
}
|
||||||
23
framework-t/src/android/net/ITetheredInterfaceCallback.aidl
Normal file
23
framework-t/src/android/net/ITetheredInterfaceCallback.aidl
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
491
framework-t/src/android/net/IpSecAlgorithm.java
Normal file
491
framework-t/src/android/net/IpSecAlgorithm.java
Normal file
@@ -0,0 +1,491 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
20
framework-t/src/android/net/IpSecConfig.aidl
Normal file
20
framework-t/src/android/net/IpSecConfig.aidl
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
358
framework-t/src/android/net/IpSecConfig.java
Normal file
358
framework-t/src/android/net/IpSecConfig.java
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
1065
framework-t/src/android/net/IpSecManager.java
Normal file
1065
framework-t/src/android/net/IpSecManager.java
Normal file
File diff suppressed because it is too large
Load Diff
20
framework-t/src/android/net/IpSecSpiResponse.aidl
Normal file
20
framework-t/src/android/net/IpSecSpiResponse.aidl
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
78
framework-t/src/android/net/IpSecSpiResponse.java
Normal file
78
framework-t/src/android/net/IpSecSpiResponse.java
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* 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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
405
framework-t/src/android/net/IpSecTransform.java
Normal file
405
framework-t/src/android/net/IpSecTransform.java
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
framework-t/src/android/net/IpSecTransformResponse.aidl
Normal file
20
framework-t/src/android/net/IpSecTransformResponse.aidl
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
74
framework-t/src/android/net/IpSecTransformResponse.java
Normal file
74
framework-t/src/android/net/IpSecTransformResponse.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package android.net;
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
20
framework-t/src/android/net/IpSecUdpEncapResponse.aidl
Normal file
20
framework-t/src/android/net/IpSecUdpEncapResponse.aidl
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
98
framework-t/src/android/net/IpSecUdpEncapResponse.java
Normal file
98
framework-t/src/android/net/IpSecUdpEncapResponse.java
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
594
framework-t/src/android/net/NetworkIdentity.java
Normal file
594
framework-t/src/android/net/NetworkIdentity.java
Normal file
@@ -0,0 +1,594 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
230
framework-t/src/android/net/NetworkIdentitySet.java
Normal file
230
framework-t/src/android/net/NetworkIdentitySet.java
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
192
framework-t/src/android/net/NetworkStateSnapshot.java
Normal file
192
framework-t/src/android/net/NetworkStateSnapshot.java
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
1834
framework-t/src/android/net/NetworkStats.java
Normal file
1834
framework-t/src/android/net/NetworkStats.java
Normal file
File diff suppressed because it is too large
Load Diff
208
framework-t/src/android/net/NetworkStatsAccess.java
Normal file
208
framework-t/src/android/net/NetworkStatsAccess.java
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
956
framework-t/src/android/net/NetworkStatsCollection.java
Normal file
956
framework-t/src/android/net/NetworkStatsCollection.java
Normal file
@@ -0,0 +1,956 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
framework-t/src/android/net/NetworkStatsHistory.aidl
Normal file
19
framework-t/src/android/net/NetworkStatsHistory.aidl
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
1162
framework-t/src/android/net/NetworkStatsHistory.java
Normal file
1162
framework-t/src/android/net/NetworkStatsHistory.java
Normal file
File diff suppressed because it is too large
Load Diff
1119
framework-t/src/android/net/NetworkTemplate.java
Normal file
1119
framework-t/src/android/net/NetworkTemplate.java
Normal file
File diff suppressed because it is too large
Load Diff
1148
framework-t/src/android/net/TrafficStats.java
Normal file
1148
framework-t/src/android/net/TrafficStats.java
Normal file
File diff suppressed because it is too large
Load Diff
19
framework-t/src/android/net/UnderlyingNetworkInfo.aidl
Normal file
19
framework-t/src/android/net/UnderlyingNetworkInfo.aidl
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
135
framework-t/src/android/net/UnderlyingNetworkInfo.java
Normal file
135
framework-t/src/android/net/UnderlyingNetworkInfo.java
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
framework-t/src/android/net/netstats/IUsageCallback.aidl
Normal file
29
framework-t/src/android/net/netstats/IUsageCallback.aidl
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
30
framework-t/src/android/net/nsd/INsdManager.aidl
Normal file
30
framework-t/src/android/net/nsd/INsdManager.aidl
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
39
framework-t/src/android/net/nsd/INsdManagerCallback.aidl
Normal file
39
framework-t/src/android/net/nsd/INsdManagerCallback.aidl
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
35
framework-t/src/android/net/nsd/INsdServiceConnector.aidl
Normal file
35
framework-t/src/android/net/nsd/INsdServiceConnector.aidl
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
1083
framework-t/src/android/net/nsd/NsdManager.java
Normal file
1083
framework-t/src/android/net/nsd/NsdManager.java
Normal file
File diff suppressed because it is too large
Load Diff
418
framework-t/src/android/net/nsd/NsdServiceInfo.java
Normal file
418
framework-t/src/android/net/nsd/NsdServiceInfo.java
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
/*
|
||||||
|
* 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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
19
framework/aidl-export/android/net/NetworkStats.aidl
Normal file
19
framework/aidl-export/android/net/NetworkStats.aidl
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
19
framework/aidl-export/android/net/NetworkTemplate.aidl
Normal file
19
framework/aidl-export/android/net/NetworkTemplate.aidl
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
19
framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl
Normal file
19
framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
156
service-t/Sources.bp
Normal file
156
service-t/Sources.bp
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
//
|
||||||
|
// 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",
|
||||||
|
],
|
||||||
|
}
|
||||||
362
service-t/jni/com_android_server_net_NetworkStatsFactory.cpp
Normal file
362
service-t/jni/com_android_server_net_NetworkStatsFactory.cpp
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
116
service-t/jni/com_android_server_net_NetworkStatsService.cpp
Normal file
116
service-t/jni/com_android_server_net_NetworkStatsService.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
38
service-t/jni/onload.cpp
Normal file
38
service-t/jni/onload.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
1878
service-t/src/com/android/server/IpSecService.java
Normal file
1878
service-t/src/com/android/server/IpSecService.java
Normal file
File diff suppressed because it is too large
Load Diff
704
service-t/src/com/android/server/NativeDaemonConnector.java
Normal file
704
service-t/src/com/android/server/NativeDaemonConnector.java
Normal file
@@ -0,0 +1,704 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
267
service-t/src/com/android/server/NativeDaemonEvent.java
Normal file
267
service-t/src/com/android/server/NativeDaemonEvent.java
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* 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()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1146
service-t/src/com/android/server/NsdService.java
Normal file
1146
service-t/src/com/android/server/NsdService.java
Normal file
File diff suppressed because it is too large
Load Diff
139
service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
Normal file
139
service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
service-t/src/com/android/server/net/CookieTagMapKey.java
Normal file
33
service-t/src/com/android/server/net/CookieTagMapKey.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
service-t/src/com/android/server/net/CookieTagMapValue.java
Normal file
37
service-t/src/com/android/server/net/CookieTagMapValue.java
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
114
service-t/src/com/android/server/net/DelayedDiskWrite.java
Normal file
114
service-t/src/com/android/server/net/DelayedDiskWrite.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
35
service-t/src/com/android/server/net/InterfaceMapValue.java
Normal file
35
service-t/src/com/android/server/net/InterfaceMapValue.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
449
service-t/src/com/android/server/net/IpConfigStore.java
Normal file
449
service-t/src/com/android/server/net/IpConfigStore.java
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
505
service-t/src/com/android/server/net/NetworkStatsFactory.java
Normal file
505
service-t/src/com/android/server/net/NetworkStatsFactory.java
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
451
service-t/src/com/android/server/net/NetworkStatsObservers.java
Normal file
451
service-t/src/com/android/server/net/NetworkStatsObservers.java
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
507
service-t/src/com/android/server/net/NetworkStatsRecorder.java
Normal file
507
service-t/src/com/android/server/net/NetworkStatsRecorder.java
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
2528
service-t/src/com/android/server/net/NetworkStatsService.java
Normal file
2528
service-t/src/com/android/server/net/NetworkStatsService.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,246 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
service-t/src/com/android/server/net/StatsMapKey.java
Normal file
46
service-t/src/com/android/server/net/StatsMapKey.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
service-t/src/com/android/server/net/StatsMapValue.java
Normal file
46
service-t/src/com/android/server/net/StatsMapValue.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
service-t/src/com/android/server/net/UidStatsMapKey.java
Normal file
33
service-t/src/com/android/server/net/UidStatsMapKey.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
159
tests/unit/java/com/android/server/net/IpConfigStoreTest.java
Normal file
159
tests/unit/java/com/android/server/net/IpConfigStoreTest.java
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user