流量管理的位置在com.oneplus.security.network下面

包含
-
calibrate 校準(zhǔn)功能包
calibrate部分代碼結(jié)構(gòu)圖- AutoCalibrateBootReceiver
<receiver android:name="com.oneplus.security.network.calibrate.AutoCalibrateBootReceiver"
android:exported="true"
android:enabled="true" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.DATE_CHANGED"/>
</intent-filter>
</receiver>
接收日期變更和開機(jī)的廣播來自行流量自動校驗
String action = intent.getAction();
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
Log.d(TAG, "set up auto calibrate function according to preference config state");
Intent changeDateIntent = new Intent(context, AutoCalibrateService.class);
changeDateIntent.putExtra(AutoCalibrateUtil.KEY_AUTO_CALIBRATE_SERVICE_TASK,
AutoCalibrateUtil.INDEX_CONFIG_AUTO_CALIBRATE_TASK);
context.startService(changeDateIntent);
} else if (Intent.ACTION_DATE_CHANGED.equals(action)) {
Intent changeDateIntent = new Intent(context, AutoCalibrateService.class);
changeDateIntent.putExtra(AutoCalibrateUtil.KEY_AUTO_CALIBRATE_SERVICE_TASK,
AutoCalibrateUtil.INDEX_CHECK_CALIBRATE_DATE_TASK);
context.startService(changeDateIntent);
}
- AutoCalibrateService
這個文件是自動流量校準(zhǔn)的主服務(wù)
@Override
public int onStartCommand (Intent intent, int flags, int startId) {
Log.i(TAG, "called on start command ");
if (null != intent) {
int taskIndex = intent.getIntExtra(AutoCalibrateUtil.KEY_AUTO_CALIBRATE_SERVICE_TASK,
AutoCalibrateUtil.INDEX_INVALID_TASK);
mCurrentSlotId = mSimcardDataModel.getCurrentTrafficRunningSlotId();
if (AutoCalibrateUtil.INDEX_START_AUTO_CALIBRATE_TASK == taskIndex) {
//啟動一個異步任務(wù)來調(diào)用歐朋sdk的短信查詢接口,在回調(diào)中對本地保存的流量數(shù)據(jù)及上一次校準(zhǔn)信息做處理
} else if (AutoCalibrateUtil.INDEX_CHECK_CALIBRATE_DATE_TASK == taskIndex) {
// 響應(yīng)日期變化并且更新與月結(jié)日相關(guān)的功能邏輯
} else if (AutoCalibrateUtil.INDEX_CONFIG_AUTO_CALIBRATE_TASK == taskIndex) {
// 發(fā)生手機(jī)重啟時,檢查是否已經(jīng)配置了流量自動校準(zhǔn)服務(wù),并相應(yīng)選擇恢復(fù)或者退出
stopSelf();
} else {
stopSelf();
}
} else {
stopSelf();
}
return super.onStartCommand(intent, flags, startId);
}
- AutoCalibrateUtil
這個文件是為了保存和獲取雙卡場景下流量自動校準(zhǔn)配置信息所做的工具類,數(shù)據(jù)存儲使用SharedPreference
public static final int INDEX_CHECK_CALIBRATE_DATE_TASK = 0;//接收到日期變更后觸發(fā),任務(wù)發(fā)送給AutoCalibrateService處理
public static final int INDEX_START_AUTO_CALIBRATE_TASK = 1;//在設(shè)置中使用,打開自動校準(zhǔn)開關(guān)后生效
public static final int INDEX_CONFIG_AUTO_CALIBRATE_TASK = 2;//接收到開機(jī)廣播觸發(fā),用于重啟因手機(jī)掉電導(dǎo)致的Alarm丟失
// 以下為針對雙卡場景定義的用于保存自動校準(zhǔn)相關(guān)SharedPreference的key
private static final String KEY_AUTO_CALIBRATE_SWITCH_SIM_ONE = "key_auto_calibrate_switch_sim_one";
private static final String KEY_AUTO_CALIBRATE_SWITCH_SIM_TWO = "key_auto_calibrate_switch_sim_two";
private static final String KEY_AUTO_CALIBRATE_TIME_INTERVAL_INDEX_SIM_ONE =
"key_auto_calibrate_time_interval_index_sim_one";
private static final String KEY_AUTO_CALIBRATE_TIME_INTERVAL_INDEX_SIM_TWO =
"key_auto_calibrate_time_interval_index_sim_two";
private static final String KEY_AUTO_CALIBRATE_TIME_INTERVAL_SIM_ONE =
"key_auto_calibrate_time_interval_sim_one";
private static final String KEY_AUTO_CALIBRATE_TIME_INTERVAL_SIM_TWO =
"key_auto_calibrate_time_interval_sim_two";
-
operator 運(yùn)營商信息管理包
用于運(yùn)營商相關(guān)的流量信息讀取、配置和管理
operator包文件結(jié)構(gòu)-
OperatorModelInterface的定義和使用
OperatorModelInterface結(jié)構(gòu)圖- AbstractOperatorDataModel 抽象流量數(shù)據(jù)模型,定義了具體類里需要使用的回調(diào)集合類并且實現(xiàn)了注冊和釋放對應(yīng)接口的方法
-
protected List<OperatorProvinceUpdater> mProvinceUpdaterList;
protected List<OperatorBrandUpdater> mBrandUpdaterList;
protected List<OperatorPackageUsageUpdater> mOperatorQueryResultUpdaterList;
protected List<OperatorAccountDayUpdater> mOperatorAccountDayUpdaterList;
* OperatorProvinceUpdater 運(yùn)營商省份更新接口
* OperatorBrandUpdater 運(yùn)營商品牌更新接口

* OperatorPackageUsageUpdater 運(yùn)營商流量使用情況更新接口

* OperatorAccountDayUpdater 運(yùn)營商月結(jié)日更新接口


* NativeOperatorDataModel 僅使用建立在原生流量統(tǒng)計基礎(chǔ)上的流量模型進(jìn)行流量數(shù)據(jù)管理
@Override
public void requesetPkgMonthlyUsageAndTotalInByte (final int slotId) {
checkThreadPoolExistence();
mThreadPool.execute(new Runnable() {
@Override
public void run () {
long lastCalibrateTime = AutoCalibrateUtil.getLastCalibrateTime(mContext.get(),
slotId);
long used = NativeOperatorDataManager.getPkgUsedMonthlyInByte(mContext.get(), slotId);
final long start = (used == OperaConst.PKG_USAGE_INVALID_VALUE)
? TimeUtils.getTrafficStaticsMonthlyStartTime(getAccountDay(slotId))
: lastCalibrateTime;
final long end = TimeUtils.getTrafficStaticsMonthlyEndTime();
long extra = mTrafficDataModel.getDataUsageWithinSpecificTime
(slotId, start, end);
boolean isLastCalibrateTimeLongAgo = ((System.currentTimeMillis() -
lastCalibrateTime) > 1000 * 60 * 1);
notifyMonthlyUsageAndTotalChanged(slotId, getPkgTotalInByte(slotId),
isLastCalibrateTimeLongAgo ? used + extra : used);
}
});
}
get數(shù)據(jù)使用情況的幾個方法
@Override
public long getPkgTotalInByte (int slotId) {
return NativeOperatorDataManager.getPkgTotalInByte(mContext.get(), slotId);
}
@Override
public int getAccountDay (int slotId) {
return NativeOperatorDataManager.getAccountDay(mContext.get(), slotId);
}
@Override
public long getPkgUsedMonthlyInByte (int slotId) {
return NativeOperatorDataManager.getPkgUsedMonthlyInByte(mContext.get(), slotId);
}
NativeOperatorDataManager —— 一個用于保存本地流量值的SharedPreference管理類
* InvalidOperatorDataModel 空模式對應(yīng)的流量模型,無sim卡時得到一個空模型,關(guān)鍵的接口實現(xiàn)直接返回?zé)o效值,可以參考調(diào)用端代碼
@Override
public long getPkgTotalInByte (int slotId) {
return OperaConst.PKG_USAGE_INVALID_VALUE;
}
@Override
public int getAccountDay (int slotId) {
return OperaConst.ACCOUNT_DAY_INVALID_VALUE;
}
@Override
public long getPkgUsedMonthlyInByte (int slotId) {
return OperaConst.PKG_USAGE_INVALID_VALUE;
}
* OperaOperatorDataModel 歐朋流量服務(wù)對應(yīng)的流量模型,對于移動、聯(lián)通、電信三大運(yùn)營商生效
調(diào)用歐朋sdk服務(wù),進(jìn)行短信校準(zhǔn)回調(diào)注冊,PackageQueryServiceAgent是一個抽象了歐朋sdk接口調(diào)用的可實例化的類
@Override
public void addQueryResultListener (final int slotId) {
if (null != mPackageQueryServiceAgent && mPackageQueryServiceAgent.isServiceBound()) {
addQueryResultListenrToAgentService(slotId);
} else {
if (null != mPackageQueryServiceAgent) {
mPackageQueryServiceAgent.addConnectionListener(new PackageQueryServiceAgent.ServiceConnectionListener() {
@Override
public void onConnected (IPackageQueryService service) {
Log.d(TAG, "recall add sms query result listener after service bound");
addQueryResultListenrToAgentService(slotId);
}
@Override
public void onDisconnected () {
}
});
} else {
Log.e(TAG, "package service agent is null, impossible to fetch query result.");
}
}
}
private void addQueryResultListenrToAgentService (int slotId) {
mPackageQueryServiceAgent.addQueryResultListener(slotId, this);
}
獲取流量信息的請求方法
該方法直接使用本對象初始化時創(chuàng)建的線程池進(jìn)行異步操作,所以回調(diào)不在主線程中,更新數(shù)據(jù)時需要注意
@Override
public void requesetPkgMonthlyUsageAndTotalInByte (final int slotId) {
if (null != mPackageQueryServiceAgent && mPackageQueryServiceAgent.isServiceBound()) {
startQueryPackageTotalAndMonthlyUsage(slotId);
} else {
if (null != mPackageQueryServiceAgent) {
mPackageQueryServiceAgent.addConnectionListener(new PackageQueryServiceAgent.ServiceConnectionListener() {
@Override
public void onConnected (IPackageQueryService service) {
Log.d(TAG, "recall start query after service bound");
startQueryPackageTotalAndMonthlyUsage(slotId);
}
@Override
public void onDisconnected () {
}
});
} else {
Log.e(TAG, "package service agent is null, impossible to fetch query result.");
}
}
}
private void startQueryPackageTotalAndMonthlyUsage (final int slotId) {
if (null != mThreadPool && !mThreadPool.isShutdown()) {
mThreadPool.execute(new Runnable() {
@Override
public void run () {
int total = mPackageQueryServiceAgent.getPkgTotalInKb(slotId);
int used = mPackageQueryServiceAgent.getPkgUsedInKb(slotId);
long extra = 0L;
long lastCalibrateTime = AutoCalibrateUtil.getLastCalibrateTime(mContext,
slotId);
if (lastCalibrateTime != AutoCalibrateUtil.DEFAULT_INVALID_LAST_CALIBRATE_DAY) {
long currentTime = System.currentTimeMillis();
// if last calibrate time is more than 20 minutes ago, we would like to double check
// monthly usage rather than just show monthly usage result obtained from sms.
boolean isLastCalibrateTimeLongAgo = (currentTime - lastCalibrateTime) >
(1 * 60 * 1000);
if (isLastCalibrateTimeLongAgo) {
// add used value.
final long start = (used == OperaConst.PKG_USAGE_INVALID_VALUE)
? TimeUtils.getTrafficStaticsMonthlyStartTime(getAccountDay(slotId))
: AutoCalibrateUtil.getLastCalibrateTime(mContext, slotId);
final long end = TimeUtils.getTrafficStaticsMonthlyEndTime();
extra = mNativeTrafficDataModel.getDataUsageWithinSpecificTime
(slotId, start, end);
Log.i(TAG, "last calibrate time is 20 minutes long ago, we add " +
"calibrated result with native saved value from last " +
"calibration till now value is " + extra);
}
}
notifyMonthlyUsageAndTotalChanged(
slotId,
(total == OperaConst.PKG_USAGE_INVALID_VALUE)
? OperaConst.PKG_USAGE_INVALID_VALUE
: total * LONG_BYTE_FACTOR,
(used == OperaConst.PKG_USAGE_INVALID_VALUE)
? OperaConst.PKG_USAGE_INVALID_VALUE
: used * LONG_BYTE_FACTOR + extra);
}
});
}
}
短信校準(zhǔn)結(jié)果的回調(diào)處理
@Override
public void onQueryResult (JSONObject result) {
Log.d(TAG, "onQueryResult");
SmsQueryResult queryResult = SmsQueryResultUtils.analyzeAndSaveSmsQueryResult(mContext, result,
mSmsQueryStateManager);
if (queryResult.isQuerySuccessed) {
requesetPkgMonthlyUsageAndTotalInByte(queryResult.queriedSlotId);
}
for (SmsQueryResultListener listener : mOperatorSmsQueryResultListenerList) {
listener.onSmsQueryResultAnalyzed(queryResult.queriedSlotId, queryResult.queriedErrorCode);
}
}
各個get接口的實現(xiàn)方式
@Override
public long getPkgTotalInByte (int slotId) {
int total = mPackageQueryServiceAgent.getPkgTotalInKb(slotId);
return (total == OperaConst.PKG_USAGE_INVALID_VALUE)
? OperaConst.PKG_USAGE_INVALID_VALUE
: total * LONG_BYTE_FACTOR;
}
@Override
public int getAccountDay (int slotId) {
//int accountday = mPackageQueryServiceAgent.getAccountDay(slotId);
int accountday = AccountDayLocalCache.getAccountDay(mContext, slotId);
return accountday;
}
@Override
public long getPkgUsedMonthlyInByte (int slotId) {
int used = mPackageQueryServiceAgent.getPkgUsedInKb(slotId);
return (used == OperaConst.PKG_USAGE_INVALID_VALUE)
? OperaConst.PKG_USAGE_INVALID_VALUE
: used * LONG_BYTE_FACTOR;
}
- 本包中其他回調(diào)接口的定義
- OperatorDataModelFactory工廠類
根據(jù)sim卡是否為歐朋sdk支持的運(yùn)營商來決定生成哪一個流量模型
public class OperatorDataModelFactory {
private static final String TAG = "OperatorDataModelFactory";
public static OperatorModelInterface getOperatorDataModel (Context context, int slotId,
boolean isSdkSupported) {
OperatorModelInterface mOperatorDataModel;
if (isSdkSupported) {
Log.d(TAG, "build opera operator model.");
mOperatorDataModel = OperaOperatorDataModel.getInstance(context);
} else {
// use native operator data model.
Log.d(TAG, "build native operator model.");
mOperatorDataModel = NativeOperatorDataModel.getInstance(context);
}
return mOperatorDataModel;
}
}
-
settings 設(shè)置界面及功能管理包settings文件結(jié)構(gòu)圖
- 界面相關(guān)類
CarrierConfigFragment, CarrierConfigSettingsActivity, 用于使用歐朋sdk時進(jìn)行運(yùn)營商信息配置
TrafficMonthlyUsageConfigActivity, 用于配置月已用流量
TrafficTotalAndClosingDayConfigAcitivity, 用于配置月總流量和月結(jié)日
TrafficUsageSettingsActivity 用于展示整個設(shè)置界面,包含了一個PreferenceFragment內(nèi)部類TrafficUsageSettingsFragment - 設(shè)置數(shù)據(jù)模型類
- OpertorPrefModel
- 界面相關(guān)類
abstract String getProvinceAndBrandPrefKey();
abstract String getTrafficTotalAndClosingDayPrefKey();
abstract String getTrafficMonthlyUsagePrefKey();



* SimPreferenceValueUpdater
用于統(tǒng)一處理設(shè)置需要顯示的值如省份、品牌、流量值等變更的接口,接收OperatorPrefModel為參數(shù),使用其中定義的抽象方法動態(tài)獲取到具體的OperatorPrefModel實現(xiàn),并進(jìn)行數(shù)據(jù)更新

public interface SimPreferenceValueUpdater {
void updateProvinceAndBrand (OperatorPrefModel model);
void updateAccountDay(OperatorPrefModel model);
void updateTrafficTotal(OperatorPrefModel model);
void updateTrafficMonthlyUsed(OperatorPrefModel model);
void updateSmsQueryResult(int errorCode);
}
-
simcard 手機(jī)sim卡信息管理包
simcard包結(jié)構(gòu)圖- SimcardDataModelInterface及其實現(xiàn)類SimcardDataModel
可以說有一些多余,因為這個接口只有一個具體實現(xiàn),應(yīng)該是一個過度設(shè)計的問題
這個類主要負(fù)責(zé)獲取sim卡的相關(guān)信息,使用接口定義,看的更加清楚吧,避免陷入實現(xiàn)細(xì)節(jié)
- SimcardDataModelInterface及其實現(xiàn)類SimcardDataModel
public interface SimcardDataModelInterface {
int SIM_CARD_ONE_INDEX = 0;
int SIM_CARD_TWO_INDEX = 1;
int INVALID_SIM_SLOT_ID = -1;
int INVALID_CLOSING_DAY = -1;
String KEY_SIM_CARD_SLOT = "sim_card_slot";
String OPERATOR_CODE_CHINA_MOBILE = "46000";
String OPERATOR_CODE_CHINA_MOBILE_1 = "46002";
String OPERATOR_CODE_CHINA_UNICOM = "46001";
String OPERATOR_CODE_CHINA_TELECOM = "46003";
String DEFAULT_OPERATOR_NUMBER = "";
int getCurrentUsingSimNumber ();
int getPhoneCount ();
int getCurrentTrafficRunningSlotId();
void registerSimStateListener (SimStateListener listener);
void removeSimStateListener (SimStateListener listener);
boolean isSlotSimInserted (int slotId);
boolean isSlotOperatorSupportedBySdk(int slotId);
String getSlotOperatorName(int slotId);
String getSlotOperatorNumber(int slotId);
int getSubIdBySlotId(int slotId);
String getIMSIBySlotId(int slotId);
void setDataEnabled(boolean state);
}
- SimStateListener的用途
public interface SimStateListener {
String SS_ABSENT = "ABSENT";
String SS_READY = "READY";
// 監(jiān)聽sim卡狀態(tài)變化的廣播回調(diào),插拔卡是一種觸發(fā)場景
void onSimStateChanged (String simState);
// 需求中需要更新運(yùn)營商,但是單純依賴sim卡廣播回調(diào),不能獲取到運(yùn)營商碼,且運(yùn)營商碼的變更不會有sim卡狀態(tài)廣播發(fā)出,
// 所以自定義了一個運(yùn)營商碼變更事件
void onSimOperatorCodeChanged(int slotId, String simValue);
}
SimStateListener的調(diào)用端代碼,定義在SimcardDataModel中,動態(tài)注冊一個監(jiān)聽sim卡狀態(tài)的廣播,在廣播回調(diào)中被處理
private BroadcastReceiver mSimStateChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive (Context context, Intent intent) {
String action = intent.getAction();
String state = intent.getStringExtra(SIM_STATE_KEY);
if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
boolean hasSimStateReallyChanged = false;
for (int i = 0; i < mPhoneCount; i++) {
int originalState = mMSimState[i];
mMSimState[i] = TelephonyManager.getDefault().getSimState(i);
if (originalState != mMSimState[i]) {
hasSimStateReallyChanged = true;
}
Log.d(TAG, "original state " + originalState + " new state " + mMSimState[i]);
parseOperatorCode(i, getOperatorCode(i));
}
if (hasSimStateReallyChanged) {
notifySimStateChanged(state);
}
}
}
};
...
private void notifySimStateChanged (String simState) {
if (SimStateListener.SS_ABSENT.equals(simState) || SimStateListener.SS_READY.equals
(simState)) {
for (SimStateListener s : mSimStateListeners) {
s.onSimStateChanged(simState);
}
} else {
Log.e(TAG, "simState value " + simState + " not supported yet.");
}
}

- smsquery 短信查詢功能管理包
- SmsQueryStateManager 單例實現(xiàn),用于整個流量模塊獲取和更新當(dāng)前短信查詢的狀態(tài)
查詢同時對卡1和卡2的狀態(tài)做跟蹤,可能出現(xiàn)的值及初始化方式都定義在下面代碼中
- SmsQueryStateManager 單例實現(xiàn),用于整個流量模塊獲取和更新當(dāng)前短信查詢的狀態(tài)
public static final int SMS_QUERY_INITIALIZED = 100;
public static final int SMS_QUERY_PROCESSING = 101;
public static final int SMS_QUERY_FINISHED = 102;
public static final int SMS_QUERY_ERROR = 103;
private int slotOneSmsQueryState = SMS_QUERY_INITIALIZED;
private int slotTwoSmsQueryState = SMS_QUERY_INITIALIZED;
實現(xiàn)中發(fā)現(xiàn),完全只靠內(nèi)存中對象做記錄的話,會出現(xiàn)SmsQueryStateManager因為各種原因?qū)ο蟊恢亟▽?dǎo)致短信查詢裝填出錯,一直提示查詢中的狀況,所以存儲設(shè)計為使用SharedPreference進(jìn)行值的長時間保存
public synchronized int getSmsQueryStateBySlotId (int slotId) {
SharedPreferences sp = mAppContext.getSharedPreferences(KEY_QUERY_STATE_SAVER, Activity
.MODE_PRIVATE);
if (SimcardDataModelInterface.SIM_CARD_ONE_INDEX == slotId) {
slotOneSmsQueryState = getSavedQueryStateOnSlot(sp, KEY_QUERY_STATE_SLOT_ONE_SAVER);
return slotOneSmsQueryState;
} else if (SimcardDataModelInterface.SIM_CARD_TWO_INDEX == slotId) {
slotTwoSmsQueryState = getSavedQueryStateOnSlot(sp, KEY_QUERY_STATE_SLOT_TWO_SAVER);
return slotTwoSmsQueryState;
} else {
Log.e(TAG, "queried with invalid slotId");
return SMS_QUERY_ERROR;
}
}
public static synchronized void clearQueryState(Context mAppContext) {
SharedPreferences sp = mAppContext.getSharedPreferences(KEY_QUERY_STATE_SAVER, Activity
.MODE_PRIVATE);
SharedPreferences.Editor e = sp.edit();
e.putInt(KEY_QUERY_STATE_SLOT_ONE_SAVER, SMS_QUERY_INITIALIZED);
e.putInt(KEY_QUERY_STATE_SLOT_TWO_SAVER, SMS_QUERY_INITIALIZED);
e.apply();
}
public synchronized void setSmsQueryStateBySlotId (int slotId, int state) {
boolean isStateValueInvalid =
state != SMS_QUERY_PROCESSING
&& state != SMS_QUERY_FINISHED
&& state != SMS_QUERY_INITIALIZED;
if (isStateValueInvalid) {
Log.e(TAG, "set query state with invalid state value " + state);
return;
}
SharedPreferences sp = mAppContext.getSharedPreferences(KEY_QUERY_STATE_SAVER, Activity.MODE_PRIVATE);
SharedPreferences.Editor e = sp.edit();
slotOneSmsQueryState = getSavedQueryStateOnSlot(sp, KEY_QUERY_STATE_SLOT_ONE_SAVER);
slotTwoSmsQueryState = getSavedQueryStateOnSlot(sp, KEY_QUERY_STATE_SLOT_TWO_SAVER);
boolean changed = false;
if (SimcardDataModelInterface.SIM_CARD_ONE_INDEX == slotId) {
if (slotOneSmsQueryState != state) {
slotOneSmsQueryState = state;
changed = true;
e.putInt(KEY_QUERY_STATE_SLOT_ONE_SAVER, state);
e.apply();
}
} else if (SimcardDataModelInterface.SIM_CARD_TWO_INDEX == slotId) {
if (slotTwoSmsQueryState != state) {
slotTwoSmsQueryState = state;
changed = true;
e.putInt(KEY_QUERY_STATE_SLOT_TWO_SAVER, state);
e.apply();
}
} else {
Log.e(TAG, "set query state with invalid slotId");
}
Log.d(TAG, "notify smsQueryStateMananger state change " + changed + " on " + slotId);
if (changed) {
for (SmsQueryStateUpdater updater : mQueryStateList) {
updater.onSmsQueryStateValueUpdate(slotId, state);
}
} else {
// force update query finish state in case query is successfully but never change UI
// state.
if (slotOneSmsQueryState == SMS_QUERY_FINISHED) {
for (SmsQueryStateUpdater updater : mQueryStateList) {
updater.onSmsQueryStateValueUpdate(SimcardDataModelInterface
.SIM_CARD_ONE_INDEX, slotOneSmsQueryState);
}
} else if (slotTwoSmsQueryState == SMS_QUERY_FINISHED) {
for (SmsQueryStateUpdater updater : mQueryStateList) {
updater.onSmsQueryStateValueUpdate(SimcardDataModelInterface
.SIM_CARD_TWO_INDEX, slotTwoSmsQueryState);
}
}
Log.d(TAG, "state is " + state + "slot 1 " + slotOneSmsQueryState + " slot 2 " +
slotTwoSmsQueryState);
}
}
在主Activity,SecurityMainActivity退出時,也需要強(qiáng)制做一次數(shù)據(jù)重置,后續(xù)迭代更改了應(yīng)用架構(gòu)的話,要注意這一點
@Override
protected void onDestroy () {
super.onDestroy();
// when finish activity actively, reset sms query state as initialized.
if (hasRegisteredColorThemeChangeReceiver) {
unregisterReceiver(mChangeColorThemeReceiver);
hasRegisteredColorThemeChangeReceiver = false;
} else {
// do nothing.
}
SmsQueryStateManager sqm = SmsQueryStateManager.getInstance(getApplicationContext());
sqm.setSmsQueryStateBySlotId(
SimcardDataModelInterface.SIM_CARD_ONE_INDEX
, SmsQueryStateManager.SMS_QUERY_INITIALIZED);
sqm.setSmsQueryStateBySlotId(
SimcardDataModelInterface.SIM_CARD_TWO_INDEX
, SmsQueryStateManager.SMS_QUERY_INITIALIZED);
}

- statics 流量使用信息統(tǒng)計包
這個包是做第一個版本的流量詳情統(tǒng)計和流量權(quán)限開關(guān)時實現(xiàn)的,目前因需求調(diào)整不會上線,需要對無用代碼做清理,因為涉及到數(shù)據(jù)庫操作,需要避免不必要的影響 - trafficalarm 流量告警功能管理包
目前的基本思路是在app中注冊監(jiān)聽發(fā)生數(shù)據(jù)行為的廣播,收到廣播后根據(jù)當(dāng)前是否在使用數(shù)據(jù)流量而啟動流量使用狀況分析的服務(wù),在后臺定時的查詢流量使用狀況,觸發(fā)了異常條件后進(jìn)行對話框彈出提醒
該服務(wù)直接繼承于Service類,后續(xù)修改時要注意添加新分支后需要主動調(diào)用stop Service的相關(guān)方法流量告警功能結(jié)構(gòu)圖- TrafficUsageAlarmReceiver說明
<receiver android:name="com.oneplus.security.network.trafficalarm.TrafficUsageAlarmReceiver"
android:exported="false"
android:enabled="true" >
<intent-filter>
<action android:name="android.net.conn.DATA_ACTIVITY_CHANGE"/>
</intent-filter>
</receiver>
@Override
public void onReceive (Context context, Intent intent) {
if (!FunctionUtils.isNetworkEnabled) {
return;
}
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (!pm.isInteractive()) {
Log.i(TAG, "screen is off stop any data usage check process. buy action is " + intent
.getAction());
cancelAnyTrafficUsageService(context);
return;
}
String action = intent.getAction();
if (ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE.equals(action)) {
if (ConnectivityUtil.isMobileConnected(context)) {
boolean isDataNetworkActive = intent.getBooleanExtra(ConnectivityManager
.EXTRA_IS_ACTIVE, false);
Log.d(TAG, "state is " + isDataNetworkActive);
if (isDataNetworkActive) {
enableTrafficUsageAlertServices(context);
} else {
cancelAnyTrafficUsageService(context);
}
} else {
// is using wifi now, do nothing.
}
}
}
- TrafficUsageAlarmIntentService說明
在onCreate時進(jìn)行必要的資源初始化操作
@Override
public void onCreate () {
super.onCreate();
Log.d(TAG, "create usage alarm service");
mHandler = new Handler(this);
mSimcardDataModel = SimcardDataModel.getInstance(getApplicationContext());
mCurrentSlotId = mSimcardDataModel.getCurrentTrafficRunningSlotId();
if (mCurrentSlotId < 0) {
Log.e(TAG, "query data alert with invalid slotId.");
}
mOperatorDataModel = OperatorDataModelFactory.getOperatorDataModel(getApplicationContext
(), mCurrentSlotId, mSimcardDataModel.isSlotOperatorSupportedBySdk(mCurrentSlotId));
mOperatorDataModel.addTrafficUsageUpdater(this);
}
在onStartCommand時,調(diào)用OperatorModelInterface上的異步查詢流量使用情況的方法
@Override
public int onStartCommand (Intent intent, int flags, int startId) {
mOperatorDataModel.requesetPkgMonthlyUsageAndTotalInByte(mCurrentSlotId);
mServiceStartedAction = intent.getAction();
return super.onStartCommand(intent, flags, startId);
}
在流量總量和月用量的回調(diào)時,進(jìn)行是否需要發(fā)出流量使用警告的邏輯判斷,跟據(jù)當(dāng)前的slotId,對應(yīng)slotId上面的流量警告配置狀況做決定,如果需要進(jìn)行彈框提醒,則發(fā)消息給主線程的Handler處理
@Override
public void onTrafficTotalAndUsedUpdate (long totalByte, long usedbyte, int slotId) {
if (null == mHandler) {
stopSelf();
return;
}
Log.d(TAG, "total is " + totalByte + " used is " + usedbyte);
if (totalByte < 0) {
Log.d(TAG, "total pkg usage returned is invalid");
return;
}
boolean isPkgRunOut = usedbyte >= totalByte;
boolean hasLeftLessThanTenPercent = ((totalByte - usedbyte) * 10 - totalByte) < 0;
if (ACTION_CONFIRM_WHETHER_TO_START_DATA_ALARM.equals(mServiceStartedAction)) {
final Context c = TrafficUsageAlarmIntentService.this;
boolean hasLeftLessThanTwentyPercent = (totalByte - usedbyte) * 5 - totalByte < 0;
// less than 30 MB.
final long LOW_DATA_LIMIT = 30 * 1024 * 1024L;
boolean isRemainingDataLessThan30Mb = totalByte - usedbyte < LOW_DATA_LIMIT;
if (isRemainingDataLessThan30Mb) {
TrafficUsageAlarmUtils.startTrafficUsageRunningOutService(c);
} else {
TrafficUsageAlarmUtils.cancelTrafficUsageRunningOutService(c);
}
if (hasLeftLessThanTwentyPercent && !hasLeftLessThanTenPercent) {
TrafficUsageAlarmUtils.startAlertTenPercentLeftService(c);
}
if (isPkgRunOut) {
TrafficUsageAlarmUtils.cancelAnyTrafficRunningOutCheckService
(TrafficUsageAlarmIntentService.this);
}
}
if (isPkgRunOut) {
// pkg run off.
if (TrafficUsageAlarmUtils.shouldAlertTrafficRunningOut(TrafficUsageAlarmIntentService
.this, mCurrentSlotId)) {
Message message = mHandler.obtainMessage(MESSAGE_ALERT_PKG_RUN_OUT);
mHandler.sendMessage(message);
} else if (TrafficUsageAlarmUtils.shouldAlertTrafficRunningOutAutoClose
(TrafficUsageAlarmIntentService.this, mCurrentSlotId)) {
Message message = mHandler.obtainMessage(MESSAGE_ALERT_PKG_RUN_OUT);
mHandler.sendMessage(message);
} else {
stopSelf();
}
} else {
if (hasLeftLessThanTenPercent) {
if (TrafficUsageAlarmUtils.shouldAlertLessThanTenPercentPkgLeft
(TrafficUsageAlarmIntentService.this, mCurrentSlotId)) {
Message message = mHandler.obtainMessage(MESSAGE_ALERT_TEN_PERCENT_LOW);
mHandler.sendMessage(message);
}
} else {
stopSelf();
}
}
}
主線程中的Handler回調(diào),用于真正啟動警告窗口
@Override
public boolean handleMessage (Message msg) {
switch (msg.what) {
case MESSAGE_ALERT_PKG_RUN_OUT:
boolean isAutoCloseNetworkEnabled = TrafficUsageAlarmUtils
.getAutoCloseNetworkWhenDataRunningOut(this, false, mCurrentSlotId);
if (isAutoCloseNetworkEnabled) {
showPkgRunningOutAutoCloseAlertDialog();
} else {
showPkgRunningOutAlertDialog();
}
break;
case MESSAGE_ALERT_TEN_PERCENT_LOW:
showTenPercentLeftAlertDialog();
break;
default:
stopSelf();
break;
}
return false;
}
對于對話框提醒的部分,設(shè)計了提醒過之后就不再提醒的功能,以及發(fā)生過流量校準(zhǔn),及手工修改流量后,清空不再提醒標(biāo)志的功能,后續(xù)涉及到相關(guān)邏輯的修改,以及增加新功能時,需要關(guān)注到
目前我想到還沒做的 重新設(shè)置開關(guān)后,是否需要重置 已發(fā)出對話框提醒的標(biāo)志
public static boolean getTenPercentPkgLeftAlert (Context context, boolean defaultValue, int slotId) {
if (SimcardDataModelInterface.INVALID_SIM_SLOT_ID == slotId) {
return false;
}
return getTrafficUsagePreferenceBooleanValue(context,
getTenPercentDataLeftConfigKey(slotId), defaultValue);
}
public static void setHasAlertedTenPercentLeft (Context context, boolean state, int slotId) {
if (SimcardDataModelInterface.SIM_CARD_ONE_INDEX == slotId) {
setTrafficUsagePreferenceBooleanValue(context, KEY_HAS_SIM_1_ALERT_TEN_PERCENT_LEFT, state);
} else if (SimcardDataModelInterface.SIM_CARD_TWO_INDEX == slotId) {
setTrafficUsagePreferenceBooleanValue(context, KEY_HAS_SIM_2_ALERT_TEN_PERCENT_LEFT,
state);
} else {
logOutUsingInvalidSlotId();
}
}
- trafficinfo 基于原生流量統(tǒng)計的原生流量信息獲取包
public interface TrafficDataModelInterface {
long getDataUsageWithinSpecificTime (int slotId, long start, long end);
long getDailyUsageBySlotId (int slotId, long monthlyUsage, int accountDay);//不再需要日已用提醒,所以該接口實際已無用了
void clearTrafficData();
}






