前言:最近在Android8.0的项目中遇到一个新的需求,客户在MR版本中要求我们更改APN参数,然后通过OTA升级的方式来更新APN参数,但是Android系统的设计是只有第一次刷机后开机才会走APN的初始化,并将APN添加到数据库中,OTA升级是不会触发系统去更新数据库的。
1、首先定义了一个BroadcastReceiver用于接收系统开机的广播,代码如下,注释见代码
public class ResetApnReceiver extends BroadcastReceiver {
private static final String TAG = "Felix ResetApnReceiver";
private static final String ACTION_BOOT = "android.intent.action.BOOT_COMPLETED";
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive");
if (ACTION_BOOT.equals(intent.getAction())) {
Log.i(TAG, "receive boot completed");
SharedPreferences sp = context.getSharedPreferences("updateApn",
Context.MODE_PRIVATE);
//获取当前的系统版本号
String version = sp.getString("version", "");
//判断系统是否发生了升级 避免每次开机后遍历xml导致系统性能降低
if (!version.equals(SystemProperties.get("ro.internal.build.version"))) {
context.startService(new Intent(context, ResetApnService.class));
Log.i(TAG, "service start, version:" + version);
//保存系统版本号
sp.edit().putString("version", SystemProperties.get("ro.internal.build.version")).commit();
} else {
Log.i(TAG, "not start service, version:" + version);
}
}
}
}
由于上面的代码很简单,就不做过多的解释了,主要就是判断系统版本是否发生了升级,若发生了升级,就开启服务去遍历xml并更新到数据库,若未升级则不遍历。
2、接下来我们来看看Service的实现,思路很简单,遍历新的apns-conf.xml文件,然后去数据库做对比,查询数据是否发生了更改,若更改则将最新的参数同步到数据库保存,否则就不管。
public class ResetApnService extends Service {
private static final String APN_PATH = "/system/etc/apns-conf.xml";
private static final String TAG = "Felix ResetApnService";
public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers");
private FileInputStream fis;
private int updateId;
public ResetApnService() {
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
writeApn();
}
private void writeApn() {
Log.i(TAG, "writeApn()");
new Thread() {
@Override
public void run() {
try {
Log.i(TAG, "start parser");
//parser apns-conf.xml
XmlPullParser parser = Xml.newPullParser();
fis = new FileInputStream(APN_PATH);
Log.i(TAG, "file to stream");
parser.setInput(fis, "utf-8");
Log.i(TAG, "set utf-8");
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
String name = parser.getName();
if ("apn".equalsIgnoreCase(name)) {
//mcc_number:客户发生更改的mcc值
if ("mcc_number".equals(parser.getAttributeValue(null, "mcc"))) {
int count = parser.getAttributeCount();
String[] names = new String[count];
String[] values = new String[count];
for (int i = 0; i < count; i++) {
names[i] = parser.getAttributeName(i);
values[i] = parser.getAttributeValue(i);
Log.i(TAG, names[i] + " : " + values[i]);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < names.length; i++) {
if (names[i].equals("carrier")) {
sb.append("name");
} else {
sb.append(names[i]);
}
sb.append("=\"");
sb.append(values[i]);
sb.append("\"");
if (i != names.length - 1) {
sb.append(" and ");
}
}
if (!checkApn(sb.toString())) {
boolean result = updateApn(CONTENT_URI,
"_id = \"" + updateId + "\"", names, values);
Log.i(TAG, "update:" + result);
updateId++;
}
}
}
break;
case XmlPullParser.END_TAG:
break;
}
eventType = parser.next();
}
Log.i(TAG, "parser end");
fis.close();
Log.i(TAG, "file close");
stopSelf();
} catch (FileNotFoundException e) {
Log.i(TAG, "sorry, file not found!");
if (fis != null)
try {
fis.close();
stopSelf();
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} catch (XmlPullParserException e) {
Log.i(TAG, "sorry, xml parser fail!");
if (fis != null)
try {
fis.close();
stopSelf();
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} catch (IOException e) {
Log.i(TAG, "sorry, IO exception!");
if (fis != null)
try {
fis.close();
stopSelf();
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
}.start();
}
//检查apn参数是否发生了变化
private boolean checkApn(String where) {
Log.i(TAG, "start check apn");
Log.i(TAG, "check where:" + where);
Cursor cursor = getContentResolver().query(CONTENT_URI, new String[]{"_id"}, where, null, null);
if (cursor != null) {
boolean moveToNext = cursor.moveToNext();
if (moveToNext) {
updateId = Integer.valueOf(cursor.getString(0)) + 1;
}
Log.i(TAG, "check result:" + moveToNext);
cursor.close();
return moveToNext;
}
return true;
}
//更新数据
private boolean updateApn(Uri updateURI, String where, String[] name, String[] values) {
Log.i(TAG, "start update apn");
Log.i(TAG, "where:" + where);
ContentValues contentValues = new ContentValues();
for (int i = 0; i < name.length; i++) {
if (name[i].equals("carrier")) {
contentValues.put("name", values[i]);
} else {
contentValues.put(name[i], values[i]);
}
}
int update = getContentResolver().update(updateURI, contentValues, where, null);
Log.i(TAG, "update result :" + update);
if (update > 0) {
return true;
} else {
return false;
}
}
}
由于代码逻辑很简单,就不做过多的赘述了,可能有些人想说,干嘛不调用系统的重置APN参数的方法,这样系统会重新去xml里面读取参数来初始化数据库,但是这样若用户自定义了APN数据则会丢失,影响用户体验。
全文完 ~