设置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;
    }

这个方法唯一不足的就是删除之后需要重启才会生效,但是基本上满足我现在的要求。

所有的功能到此结束。。。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐