android 设置sim卡流量上限,获取sim卡实时流量,清除所有流量数据
设置sim卡流量上限,获取sim卡实时流量在系统的设置里面是有的,首先来看下系统的设置是怎样实现功能的。关于这个功能的类DataUsageSummary.java,先看一下设置流量上限的方法private void setPolicyLimitBytes(long limitBytes) {if (LOGD) Log.d(TAG, "setPolicyLimitBytes...
设置sim卡流量上限,获取sim卡实时流量在系统的设置里面是有的,首先来看下系统的设置是怎样实现功能的。关于这个功能的类DataUsageSummary.java,先看一下设置流量上限的方法
private void setPolicyLimitBytes(long limitBytes) {
if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
/// M: for ALPS02270187 @{
if (mTemplate == null) {
Log.w(TAG, "template is null, just return");
return;
}
/// @}
/// M: for ALPS01857983 @{
int rule = mTemplate.getMatchRule();
Log.d(TAG,"rule = " + rule+" limitBytes "+limitBytes);
if (rule != NetworkTemplate.MATCH_WIFI_WILDCARD) {
/// @}
//这个就是核心了
mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
updatePolicy(false);
}
}
跟踪一下mPolicyEditor,mTemplate这两个变量是怎么来的,首先来看一下mTemplate
private void updateBody() {
...
if (isMobileTab(currentTab)) {
...
mTemplate = buildTemplateMobileAll(
getActiveSubscriberId(context, getSubId(currentTab)));
if (LOGD) Log.d(TAG, "updateBody() getSubId=" + getSubId(currentTab)+" getActiveSubscriberId "+getActiveSubscriberId(context, getSubId(currentTab)));
/// M: for C2K datasuage
CdmaUtils.fillTemplateForCdmaLte(mTemplate, getSubId(currentTab));
mTemplate = NetworkTemplate.normalize(mTemplate,
mTelephonyManager.getMergedSubscriberIds());
}
...
}
//获取sim卡的imsi号
private static String getActiveSubscriberId(Context context, int subId) {
final TelephonyManager tele = TelephonyManager.from(context);
String retVal = tele.getSubscriberId(subId);
if (LOGD) Log.d(TAG, "getActiveSubscriberId=" + retVal + " subId=" + subId);
return retVal;
}
buildTemplateMobileAll方法就是根据sim卡的imsi号来建立一个Template,
CdmaUtils.java对mTemplate进行了一些操作,
public static void fillTemplateForCdmaLte(NetworkTemplate template, int subId) {
if (CdmaFeatureOptionUtils.isCdmaLteDcSupport()) {
final TelephonyManagerEx teleEx = TelephonyManagerEx.getDefault();
final String svlteSubscriberId = teleEx.getSubscriberIdForLteDcPhone(subId);
if (!(TextUtils.isEmpty(svlteSubscriberId)) && svlteSubscriberId.length() > 0) {
Log.d(TAG, "bf:" + template);
template.addMatchSubscriberId(svlteSubscriberId);
Log.d(TAG, "af:" + template);
}
}
}
接下来是 NetworkTemplate的normalize方法
public static NetworkTemplate normalize(NetworkTemplate template, String[] merged) {
if (template.isMatchRuleMobile() && ArrayUtils.contains(merged, template.mSubscriberId)) {
// Requested template subscriber is part of the merge group; return
// a template that matches all merged subscribers.
return new NetworkTemplate(template.mMatchRule, merged[0], merged,
template.mNetworkId);
} else {
return template;
}
}
mTemplate的初始化完了,接下来研究mPolicyEditor,
根据PolicyManager直接new了一个PolicyEditor
mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
这个mPolicyEditor是从NetworkPolicyManager来的
mPolicyManager = NetworkPolicyManager.from(context);
根据这些我自己写了一个setPolicyLimitBytes方法:
private void setPolicyLimitBytes(long limitBytes) {
Log.d(TAG, "setPolicyLimitBytes()");
NetworkTemplate mTemplate = buildTemplateMobileAll(getActiveSubscriberId(mContext, 1));
mTemplate.addMatchSubscriberId(getActiveSubscriberId(mContext, 1));
TelephonyManager mTelephonyManager = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
mTemplate = NetworkTemplate.normalize(mTemplate,mTelephonyManager.getMergedSubscriberIds());
if (mTemplate == null) {
Log.w(TAG, "template is null, just return");
return;
}
int rule = mTemplate.getMatchRule();
Log.d(TAG,"rule = " + rule);
if (rule != NetworkTemplate.MATCH_WIFI_WILDCARD) {
NetworkPolicyManager mPolicyManager = NetworkPolicyManager.from(mContext);
NetworkPolicyEditor mPolicyEditor = new NetworkPolicyEditor(mPolicyManager);
mPolicyEditor.setPolicyLimitBytes(mTemplate, limitBytes);
}
}
经过测试,可以达到跟设置里面设置流量上限一样的功能。
接下来是获取sim卡使用流量的功能,这里的totalBytes就是显示的流量,
final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
mCycleSummary.setText(totalPhrase);
所有的流量数据都来自entry这个变量
//start是开始的时间,end是结束时间,now是当前时间
entry = mChartData.network.getValues(start, end, now, null);
这个mChartData是选择流量开始统计的view,可以看到选择不同的时间,所统计出来的流量是不一样的。Entry是NetworkStatsHistory的内部类,
//加载玩之后,data也被传进来了
@Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
...
mChartData = data;
...
}
是通过ChartDataLoader传过来的
@Override
public ChartData loadInBackground() {
final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
final AppItem app = mArgs.getParcelable(KEY_APP);
final int fields = mArgs.getInt(KEY_FIELDS);
Log.w("ChartDataLoader", "KEY_FIELDS "+fields);
try {
return loadInBackground(template, app, fields);
} catch (RemoteException e) {
// since we can't do much without history, and we don't want to
// leave with half-baked UI, we bail hard.
throw new RuntimeException("problem reading network stats", e);
}
}
private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
throws RemoteException {
final ChartData data = new ChartData();
data.network = mSession.getHistoryForNetwork(template, fields);
if (app != null) {
// load stats for current uid and template
final int size = app.uids.size();
for (int i = 0; i < size; i++) {
final int uid = app.uids.keyAt(i);
data.detailDefault = collectHistoryForUid(
template, uid, SET_DEFAULT, data.detailDefault);
data.detailForeground = collectHistoryForUid(
template, uid, SET_FOREGROUND, data.detailForeground);
}
if (size > 0) {
data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
data.detail.recordEntireHistory(data.detailDefault);
data.detail.recordEntireHistory(data.detailForeground);
} else {
data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
}
}
return data;
}
这里最重要的两个变量mSession和template,template已经知道了。mSession是DataUsageSummary传过来的,在onCreat的时候
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStatsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
mStatsSession = mStatsService.openSession();
}
根据这些我又自己写了一个获取当前使用流量的函数,
private String getTotalByte() {
NetworkTemplate mTemplate = buildTemplateMobileAll(getActiveSubscriberId(mContext, 1));
mTemplate.addMatchSubscriberId(getActiveSubscriberId(mContext, 1));
TelephonyManager mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mTemplate = NetworkTemplate.normalize(mTemplate, mTelephonyManager.getMergedSubscriberIds());
if (mTemplate == null) {
Log.w(TAG, "template is null, just return");
return "";
}
try {
INetworkStatsService mStatsService = INetworkStatsService.Stub.asInterface(ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
mStatsSession = mStatsService.openSession();
NetworkStatsHistory nsh = mStatsSession.getHistoryForNetwork(mTemplate, 10);
long cTime = System.currentTimeMillis();
NetworkStatsHistory.Entry entry = nsh.getValues(PreferencesUtils.getLong(mContext, "LIMITBYTESSTARTTIME", System.currentTimeMillis()), cTime, PreferencesUtils.getLong(mContext, "LIMITBYTESENDTIME", System.currentTimeMillis()), null);
String totalPhrase = Formatter.formatFileSize(mContext, entry.rxBytes + entry.txBytes);
Log.d(TAG, "getTotalByte, entry: rxBytes " + entry.rxBytes + " txBytes " + entry.txBytes + " totalPhrase " + totalPhrase);
return totalPhrase;
} catch (Exception e) {
Log.d(TAG, "getTotalByte, exception happen: " + e + " , so finish current activity");
return "";
}
}
在测试的过程中,我发现一个奇怪的问题,我这里获取到的流量总是不准确的,比系统设置里面的要少很多。当我停止使用流量的时候,这个值还会一直变,等一段时间之后就和系统设置里面的一样了。这个问题我研究了很久,没有找到问题所在,然后在看了一篇文章:https://blog.csdn.net/wdyshowtime/article/details/78532182,文章中提到的减小桶的体积可以解决这个问题,把桶的体积由1小时变成1分钟,这样每分钟都会把之前消耗的记录下来,这样虽然有一点误差,但是影响不大。
最后是清除流量数据,设置里面并没有提供这个方法。我首先所想到的就是找到数据保存的位置,然后把直接把数据删了,数据全部都保存在data/system/netstats文件夹下,
我试了一下直接把这几个文件删掉,重启一下机器,发现设置里面的流量数据完全被清除了,这样就可以达到我的目的了,接下来就是权限的问题了,怎样让app有权限去操作data/system/netstats里面的文件,需要添加selinux权限
--- a/device/mediatek/common/sepolicy/file.te
+++ b/device/mediatek/common/sepolicy/file.te
@@ -213,4 +213,8 @@ type ipoh_data_file, file_type, data_file_type;
# Operation : Migration
# Purpose : md_monitor data file
-type md_monitor_data_file, file_type, data_file_type;
\ No newline at end of file
+type md_monitor_data_file, file_type, data_file_type;
+
+type data_system_netstats_dev, fs_type;
+type data_system_netstats_uid, fs_type;
+type data_system_netstats_xt, fs_type;
--- a/device/mediatek/common/sepolicy/genfs_contexts
+++ b/device/mediatek/common/sepolicy/genfs_contexts
@@ -11,3 +11,7 @@ genfscon proc /driver/icusb u:object_r:proc_icusb:s0
genfscon iso9660 / u:object_r:iso9660:s0
genfscon rawfs / u:object_r:rawfs:s0
genfscon fuseblk / u:object_r:fuseblk:s0
+
+genfscon proc /data/system/netstats/dev. u:object_r:data_system_netstats_dev:s0
+genfscon proc /data/system/netstats/uid. u:object_r:data_system_netstats_uid:s0
+genfscon proc /data/system/netstats/xt. u:object_r:data_system_netstats_xt:s0
--- a/device/mediatek/common/sepolicy/system_app.te
+++ b/device/mediatek/common/sepolicy/system_app.te
@@ -213,3 +213,7 @@ allow system_app mtk_mwblacklist_service:service_manager find;
# Package: teei
allow system_app teei_fp_device:chr_file rw_file_perms;
allow system_app teei_client_device:chr_file r_file_perms;
+
+allow system_app data_system_netstats_dev:file rw_file_perms;
+allow system_app data_system_netstats_uid:file rw_file_perms;
+allow system_app data_system_netstats_xt:file rw_file_perms;
删除的方法很简单,
public boolean deleteDirectory(String filePath) {
//如果filePath不以文件分隔符结尾,自动添加文件分隔符
if (!filePath.endsWith(File.separator)) {
filePath = filePath + File.separator;
}
File dirFile = new File(filePath);
if (!dirFile.exists() || !dirFile.isDirectory()) {
Log.d("deleteFile ","!dirFile.exists ");
return false;
}
File[] files = dirFile.listFiles();
//遍历删除文件夹下的所有文件(包括子目录)
for (int i = 0; i < files.length; i++) {
if (files[i].isFile()) {
//删除子文件
File file1 = new File(files[i].getAbsolutePath());
if (file1.isFile() && file1.exists()) {
boolean flag = file1.delete();
Log.d("deleteFile ","getAbsolutePath "+files[i].getAbsolutePath()+" flag "+flag);
}
}
}
return true;
}
这个方法唯一不足的就是删除之后需要重启才会生效,但是基本上满足我现在的要求。
所有的功能到此结束。。。
更多推荐
所有评论(0)