Android power optimization (2) — alignment wake up

Time:2020-11-21

survey

Many applications installed on Android phones will frequently wake up the phone (wake-up system, wake-up screen), resulting in the phenomenon of mobile phone power consumption. A good alignment wake-up management scheme is to wake up the background application infrequently when it is standby, so as to save power intelligently.

Implementation principle: APK is the entrance of this function. After checking the application, the checked application will be written into the blacklist and the framework will be informed of the change of the blacklist content. After receiving the notice, the framework will automatically obtain the application in the blacklist and save it to the list. In the framework calling interface, it will detect whether the application is in the blacklist. If it is in the blacklist, the alarm type will be detected The type is 0 or 2, and the corresponding change is 1 or 3.

Implementation of application layer function

Apk interface initialization

The array lists listpkgs, forbitpkgs, allowpkgs and showpkgs are initialized in the forbitalarmlogic construction method.

Listpkgs: indicates the applications that need to be set with wake-up alignment. If these applications have been installed, they will be displayed on the interface of alignment wake-up settings. Initial data from / data / data / com android.security/app_ bin/ forbitapplist.xml If the file does not exist, from the local resource array security_ array_ savepower_ For vital arms.

Forbitpkgs: refers to the aligned wake-up list, that is, the list of forbidden wake-up, and the application checked in the interface. Initial data from sharedpreference database name ManagerUtil.PRE_ NAME(com.***. android.savepowermanager_ Preferences) ManagerUtil.FORBIT_ ALARM_ APP_ LIST_ The data saved in key will be saved to the forbitpkgs array. If there is no data, null will be returned.

Allowpkgs: indicates the list of wake-up allowed, and the application not checked in the interface. Initial data from shared preference database ManagerUtil.PRE_ NAME(com.***. android.savepowermanager_ The key value in preferences is ManagerUtil.ALLOW_ ALARM_ APP_ LIST_ For the data saved in key, the obtained data is saved to the allowpkgs array list; if there is no data, null is returned.

Showpkgs: indicates the array application list to be displayed in the alignment wake-up settings interface. Clear the array before data initialization. Before alignment wake-up scheme optimization, the array stores the intersection of listpkgs list and installed applications. After optimization, it also saves the installed third-party applications.

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 forbitset = (Set)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_NAME,
            ManagerUtil.FORBIT_ALARM_APP_LIST_KEY, null, 4);
    if (forbitset != null) {
        Iterator forbitir = forbitset.iterator();
        while(forbitir.hasNext()) {
            String forbit = forbitir.next();
            forbitPkgs.add(forbit);
        }
    }

    Set allowset = (Set)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_NAME,
            ManagerUtil.ALLOW_ALARM_APP_LIST_KEY, null, 4);
    if (allowset != null) {
        Iteratorallowir = allowset.iterator();
        while(allowir.hasNext()) {
            String allow = allowir.next();
            allowPkgs.add(allow);
        }
    }
}
    public ArrayList getListApps() {
        if (forbitPkgs.size() == 0) {
            Set forbitset= (Set)ManagerUtil.getPreferenceValue(mCtx, ManagerUtil.PRE_NAME,
                    ManagerUtil.FORBIT_ALARM_APP_LIST_KEY, null, 4);
            if (forbitset == null) {
                readFromXml();
                HashSet forbitPkgsSet = new HashSet();
                for (String pkg : forbitPkgs) {
                    forbitPkgsSet.add(pkg);
                }
                ManagerUtil.savePreferenceValue(mCtx, ManagerUtil.PRE_NAME,
                    ManagerUtil.FORBIT_ALARM_APP_LIST_KEY, forbitPkgsSet, 4);
            } else {
                Iterator forbitir = forbitset.iterator();
                while(forbitir.hasNext()) {
                    String forbit = forbitir.next();
                    forbitPkgs.add(forbit);
                }
            }
        }
        showPkgs.clear();
        ArrayList apps = new ArrayList();
        
        final Listinstalled =  pm.getInstalledPackages (0);
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);
    }

}

In the getlistapps() method of the forbitalarmlogic class, reassign the forbitpkgs array

If forbitpkgs is empty, that is to say, if no data is obtained in the constructor, the data is retrieved from the above database again; if it is still empty, the data is retrieved from / data / data / com. * * android.security/app_ bin/ applist.xml File and save it to the forbitpkgs array.

The aligned wake-up list displayed in the mobile phone manager mainly includes:

(1)、 forbitapplist.xml Application of intersection between file and installed application;

(2) , installed third-party applications.

Apk response mechanism

After APK is started, the black and white list has been set. The initialization process is the process of loading the interface.

Response to click events

After the initialization of the interface, save the checked application to two array lists: resultlist and savelist. In response to a click event, the application is removed from the resultlist list list or added to the resultlist list list.

Interface exit mechanism

In the onpause() method, judge whether the resultlist is the same as the savelist. If not, save the aligned wake-up list again and notify the 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 forbitPkgs = new ArrayList();
                        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) How to save the list again?

First of all, clear the allowpkgs and forbitpkgs, that is, clear the list of allowed applications and the list of prohibited applications.

Secondly, add the application that forbids wake-up (i.e. the application in the checked state on the interface) to forbitpkgs and write it to / data / data / com. * * android.security/app_ bin/ applist.xml File. At the same time, write the corresponding key value as ManagerUtil.FORBIT_ ALARM_ APP_ LIST_ Key database.

Thirdly, add the wake-up enabled applications (those not checked on the interface) to allowpkgs, and write the corresponding key value as ManagerUtil.ALLOW_ ALARM_ APP_ LIST_ Key database.

Finally, the alarm manager service is notified.

(2) How to notify alarm manager service?

After the above data is saved, the broadcast will be sent: com android.savepower.forbitalarmapplistchanged , notify the 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();
}

The flow chart is as follows:

Install third party applications

After receiving the broadcast of package installation in the packagereceiver class, add the third-party application to the whitelist to obtain the alignment wake-up data again.

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();

Implementation mechanism of alarm manager service

Receive broadcast

When the alignment wake-up list changes, the broadcast for live arm applistchanged is sent. The alarm manager service defines the receiver of the broadcast to receive the broadcast sent by APK. from applist.xml (/data/data/com.***. android.security/app_ bin/ applist.xml )The read application in the file is saved to the global variable 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, " without name at "
                             + parser.getPositionDescription());
                     XmlUtils.skipCurrentTag(parser);
                     continue;
                 }
                String packagename = parser.getAttributeValue(null, "name");
                if (packagename == null) {
                    Slog.w(TAG, "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);
    }
}

Modify alarm type

When the setimpl method is called to set the alarm clock, we modify the type of alarm clock to realize the function of alignment wake-up.

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);
                }
            }
        }

Alignment wake up add mechanism

(1) All third-party applications are added to the alignment wake-up list;

(2) . prohibit the system application from adding to the alignment wake-up list before verification to avoid system exceptions.

A. The system core applications are not allowed to join the aligned wake-up list, that is, the applications located in the system / priv app directory can not be added to the aligned wake-up list;