首頁>技術>

概況

Android手機上安裝的很多應用都會頻繁喚醒手機(喚醒系統、喚醒螢幕),造成手機耗電等現象。良好的對齊喚醒管理方案,就是對後臺應用待機時不頻繁喚醒,智慧節省電量。

實現原理:APK作為該功能的入口,勾選應用後,將勾選的應用寫入黑名單,並通知framework黑名單內容變化;framework接收到通知後,自動獲取黑名單中的應用,儲存到列表中;在framework呼叫介面中檢測應用是否在黑名單中,如果在黑名單中則檢測鬧鐘型別,如果鬧鐘型別是0或2,對應修改為1或3。

應用層功能實現APK介面初始化

在ForbitAlarmLogic構造方法中初始化了陣列列表listPkgs、forbitPkgs、allowPkgs、showPkgs。

listPkgs:表示需要設定對齊喚醒的應用,如果這些應用已經安裝,就會顯示在對齊喚醒設定的介面上。初始資料從/data/data/com.***.android.security/app_bin/forbitapplist.xml中獲取,如果檔案不存在,則從本地資源陣列security_array_savepower_forbitalarms中獲取。

forbitPkgs:表示對齊喚醒名單,即禁止喚醒的名單,介面勾選的應用。初始資料從SharedPreference資料庫名ManagerUtil.PRE_NAME(com.***.android.savepowermanager_preferences)中獲取鍵值ManagerUtil.FORBIT_ALARM_APP_LIST_KEY中儲存的資料,將獲取的資料儲存到forbitPkgs陣列中,如果沒有資料則返回null。

allowPkgs:表示允許喚醒的名單,介面沒有勾選的應用。初始資料從SharedPreference資料庫ManagerUtil.PRE_NAME(com.***.android.savepowermanager_preferences)中獲取鍵值為ManagerUtil.ALLOW_ALARM_APP_LIST_KEY中儲存的資料,將獲取的資料儲存到allowPkgs陣列列表中;如果沒有資料則返回null。

showPkgs:表示要顯示在對齊喚醒設定介面的陣列應用列表,在資料初始化之前先將該陣列清空。對齊喚醒方案優化之前,該陣列儲存的是listPkgs列表與已安裝應用的交集。優化之後,同時還儲存了已安裝的第三方應用。

public ForbitAlarmLogic(Context ctx) { this.mCtx = ctx; pm = ctx.getPackageManager(); xmlAppList = Util.getDefaultDataPath(ctx) + "/app_bin/applist.xml"; String xmlFile = Util.getDefaultDataPath(ctx)+"/app_bin/forbitapplist.xml"; File f = new File(xmlFile); if (!f.exists()) { Log.e("forbitapplist not exist!"); String[] strs = mCtx.getResources().getStringArray(R.array.security_array_savepower_forbitalarms); for (String str : strs) { listPkgs.add(str); } } else { readFromXmlWithFilename(xmlFile, listPkgs); }// readFromXml(); Set<String> forbitset = (Set<String>)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_NAME, ManagerUtil.FORBIT_ALARM_APP_LIST_KEY, null, 4); if (forbitset != null) { Iterator<String> forbitir = forbitset.iterator(); while(forbitir.hasNext()) { String forbit = forbitir.next(); forbitPkgs.add(forbit); } } Set<String> allowset = (Set<String>)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_NAME, ManagerUtil.ALLOW_ALARM_APP_LIST_KEY, null, 4); if (allowset != null) { Iterator<String> allowir = allowset.iterator(); while(allowir.hasNext()) { String allow = allowir.next(); allowPkgs.add(allow); } }}
 public ArrayList<DroidApp> getListApps() { if (forbitPkgs.size() == 0) { Set<String> forbitset= (Set<String>)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_NAME,  ManagerUtil.FORBIT_ALARM_APP_LIST_KEY, null, 4); if (forbitset == null) { readFromXml(); HashSet<String> forbitPkgsSet = new HashSet<String>(); for (String pkg : forbitPkgs) {  forbitPkgsSet.add(pkg); } ManagerUtil.savePreferenceValue(mCtx, ManagerUtil.PRE_NAME,  ManagerUtil.FORBIT_ALARM_APP_LIST_KEY, forbitPkgsSet, 4); } else { Iterator<String> forbitir = forbitset.iterator(); while(forbitir.hasNext()) {  String forbit = forbitir.next();  forbitPkgs.add(forbit); } } } showPkgs.clear(); ArrayList<DroidApp> apps = new ArrayList<DroidApp>();  final List<PackageInfo> installed = pm.getInstalledPackages(0);  String name = null; for (final PackageInfo appInfo : installed){ String pkg = appInfo.packageName; if (listPkgs.contains(pkg)) { DroidApp app = new DroidApp(); name = pm.getApplicationLabel(appInfo.applicationInfo).toString(); app.name = name; app.icon = appInfo.applicationInfo.loadIcon(pm); if (forbitPkgs.contains(pkg)) {  app.online_switch = true; } else if (allowPkgs.contains(pkg)) {  app.online_switch = false; } else {  app.online_switch = true; } app.pkg = pkg; apps.add(app); showPkgs.add(pkg); Log.d("in white list and installed package is : "+pkg); } else {//  已經安裝的第三方應用 if ((appInfo.applicationInfo.uid > 10000) && (appInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 && (appInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {  String pkgName = appInfo.packageName;  DroidApp app = new DroidApp();  app.name = pm.getApplicationLabel(appInfo.applicationInfo).toString();  app.icon = appInfo.applicationInfo.loadIcon(pm);//  app.online_switch = true;  if (forbitPkgs.contains(pkg)) { app.online_switch = true;  } else if (allowPkgs.contains(pkg)) { app.online_switch = false;  } else { app.online_switch = true;  }  app.pkg = pkgName;  apps.add(app);  showPkgs.add(pkgName);  Log.d("not in white list and installed third package is : "+pkgName); } } } return apps; }
private class GetListDataThread implements Runnable { @Override public void run() { // TODO Auto-generated method stub appList = mFbAmLogic.getListApps(); resultList.clear(); for (DroidApp app : appList) { Log.d("getListApps appname = " + app.pkg); if (app.online_switch) { if (app.pkg != null && app.pkg.length() > 0) {  resultList.add(app.pkg);  saveList.add(app.pkg); } } } Message msg = Message.obtain(); msg.what = MSG_SHOWLIST; handler.sendMessage(msg); }}

ForbitAlarmLogic類的getListApps()方法中重新為forbitPkgs陣列賦值

如果forbitPkgs為空,即在構造方法中沒有獲取到資料,重新從上面資料庫中獲取資料;如果仍然是空,則從/data/data/com.***.android.security/app_bin/applist.xml檔案中獲取,儲存到forbitPkgs陣列中。

手機管家中顯示的對齊喚醒名單主要有: (1)、forbitapplist.xml檔案與已安裝應用的交集應用;

(2)、已安裝的第三方應用。

APK響應機制

APK在啟動之後,就已經設定好了黑白名單,初始化過程就是載入介面的過程。

響應點選事件

介面初始化完畢之後,將處於勾選狀態的應用儲存到兩個陣列列表:resultList、saveList。響應點選事件時,將應用移除resultList列表,或新增到resultList列表中。

介面退出機制

在onPause()方法中判斷resultList與saveList是否相同,如果不相同則重新儲存對齊喚醒名單,並通知AlarmManagerService。

 public void onPause() { // TODO Auto-generated method stub super.onPause(); new Thread(new Runnable() {  @Override public void run() { // TODO Auto-generated method stub boolean isSameContent = true; for (int i = 0; i < saveList.size(); i++) {  Log.d("saveList "+ i + " = "+saveList.get(i)); } for (int j = 0; j < resultList.size(); j++) {  Log.d("resultList "+ j + " = "+resultList.get(j)); }  if (saveList.size() == resultList.size()) {  Log.i("saveList == resultList");  for (String result : resultList) { String xmlAppList = "/data/data/com.***.android.security/app_bin/applist.xml"; ArrayList<String> forbitPkgs = new ArrayList<String>(); ForbitAlarmLogic.readFromXmlWithFilename(xmlAppList, forbitPkgs); if (!forbitPkgs.contains(result)) {  Log.i(result + "Not In applist.xml");  isSameContent = false;  break; }  if (!saveList.contains(result)) {  Log.i(result + "Not In SaveList");  isSameContent = false;  break; }  } } else {  Log.i("saveList Changed");  isSameContent = false; }  if (!isSameContent) {  Log.i("ForbitAlarmSetting save Data");  mFbAmLogic.saveAlarmAppMap(resultList); } } }).start(); }

(1)、如何重新儲存名單?

首先,清空allowPkgs和forbitPkgs,即先清空允許啟動的應用列表和禁止啟動的應用列表。

其次,將禁止喚醒的應用(即介面上處於勾選狀態的應用)新增到forbitPkgs中,並寫入/data/data/com.***.android.security/app_bin/applist.xml檔案中。同時寫入對應鍵值為ManagerUtil.FORBIT_ALARM_APP_LIST_KEY資料庫中。

再次,將允許喚醒的應用(介面上沒有勾選的應用)新增到allowPkgs中,並寫入對應鍵值為ManagerUtil.ALLOW_ALARM_APP_LIST_KEY資料庫中。

最後,通知AlarmManagerService。

(2)、如何通知AlarmManagerService?

上面資料儲存完畢後,傳送廣播:com.***.android.savepower.forbitalarmapplistchanged,通知AlarmManagerService。

public static void notifyFramework(final Context ctx) { new Thread(){ public void run() { try { Thread.sleep(200); Intent intent = new Intent(); intent.setAction(ManagerUtil.INTENT_FORBITALARM_LIST_CHANGED); ctx.sendBroadcast(intent); } catch (InterruptedException e) { Log.e("applist.xml send broadcast error"); } }; }.start();}

流程圖如下:

安裝第三方應用

在PackageReceiver類中接收到包安裝的廣播後,將第三方應用新增到白名單,重新獲取對齊喚醒資料。

  new Thread(new Runnable() {  @Override  public void run() { // TODO Auto-generated method stub Log.d("automatically add newly installed applications into blacklist."  + " packageName = " + packageName);  synchronized (PackageReceiver.this) {  mForbitAlarmLogic = ForbitAlarmLogic  .getInstance(mCtx);  mForbitAlarmLogic  .packageReceiverApkAdded(packageName); }  } }).start();
AlarmManagerService實現機制接收廣播

當對齊喚醒名單發生變化時,會發送forbitalarmapplistchanged 廣播。AlarmManagerService定義了該廣播的接收器,用來接收APK傳送的廣播。從applist.xml(/data/data/com.***.android.security/app_bin/applist.xml)檔案中讀取應用儲存到全域性變數mHashtable中。

class UpdateXmlReceiver extends BroadcastReceiver { public UpdateXmlReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_SAVEPOWER_UPDATEXML); getContext().registerReceiver(this, filter); } @Override public void onReceive(Context context, Intent intent) { synchronized (mLock) { // TODO Auto-generated method stub if(YulongFeature.FEATURE_REDUCE_RTC_WAKEUP){ mHashtable.clear(); Slog.d(TAG, "Receive savepower broadcast, read xml again."); getPackageNameFromXml(); } } }}
private void getPackageNameFromXml() { FileReader permReader = null; try { permReader = new FileReader(xmlNewFile); Slog.d(TAG, "getPackageNameFromXml : read xmlNewFile "); } catch (FileNotFoundException e) { try { permReader = new FileReader(xmlFile); Slog.d(TAG, "getPackageNameFromXml : read xmlFile "); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block Slog.d(TAG, "getPackageNameFromXml, can not find config xml "); return; } } try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(permReader); XmlUtils.beginDocument(parser, "channel"); while (true) { XmlUtils.nextElement(parser); if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { break; } String name = parser.getName(); if ("item".equals(name)) { int id = Integer.parseInt(parser.getAttributeValue(null, "id")); if (id <= 0) { Slog.w(TAG, "<item> without name at "  + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue;  } String packagename = parser.getAttributeValue(null, "name"); if (packagename == null) {  Slog.w(TAG, "<item> without name at "  + parser.getPositionDescription());  XmlUtils.skipCurrentTag(parser);  continue; } Slog.d(TAG, "getPackageNameFromXml : id is " + id + " name is " + packagename); mHashtable.put(id, packagename); XmlUtils.skipCurrentTag(parser); } else { XmlUtils.skipCurrentTag(parser); continue; } } permReader.close(); } catch (XmlPullParserException e) { Slog.w(TAG, "Got execption parsing permissions.", e); } catch (IOException e) { Slog.w(TAG, "Got execption parsing permissions.", e); }}
修改鬧鐘型別

在呼叫setImpl方法設定鬧鐘時,我們通過修改鬧鐘的型別來實現對齊喚醒功能。

if (type == AlarmManager.RTC_WAKEUP || type == AlarmManager.ELAPSED_REALTIME_WAKEUP) { if(mHashtable.containsValue(callingPackage)){ if (AlarmManager.RTC_WAKEUP == type) {  type = AlarmManager.RTC;  Slog.v(TAG, "change alarm type RTC_WAKEUP to RTC for " + callingPackage); } if (AlarmManager.ELAPSED_REALTIME_WAKEUP == type) {  type = AlarmManager.ELAPSED_REALTIME;  Slog.v(TAG, "change alarm type ELAPSED_REALTIME_WAKEUP to ELAPSED_REALTIME for " + callingPackage); } } }
對齊喚醒新增機制

(1)、第三方應用全部新增到對齊喚醒名單;

(2)、禁止系統應用驗證前新增到對齊喚醒名單,避免導致系統異常。

A. 系統核心應用不允許加入對齊喚醒名單,即位於system/priv-app目錄下的應用不可以加入對齊喚醒名單;

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Vue入門系列:一 平緩起步